('service', 'Service', errors);\r\n *\r\n * ...\r\n * throw error.create(Err.GENERIC);\r\n * ...\r\n * throw error.create(Err.FILE_NOT_FOUND, {'file': fileName});\r\n * ...\r\n * // Service: Could not file file: foo.txt (service/file-not-found).\r\n *\r\n * catch (e) {\r\n * assert(e.message === \"Could not find file: foo.txt.\");\r\n * if (e.code === 'service/file-not-found') {\r\n * console.log(\"Could not read file: \" + e['file']);\r\n * }\r\n * }\r\n */\r\nconst ERROR_NAME = 'FirebaseError';\r\n// Based on code from:\r\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types\r\nclass FirebaseError extends Error {\r\n constructor(\r\n /** The error code for this error. */\r\n code, message, \r\n /** Custom data for this error. */\r\n customData) {\r\n super(message);\r\n this.code = code;\r\n this.customData = customData;\r\n /** The custom name for all FirebaseErrors. */\r\n this.name = ERROR_NAME;\r\n // Fix For ES5\r\n // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work\r\n Object.setPrototypeOf(this, FirebaseError.prototype);\r\n // Maintains proper stack trace for where our error was thrown.\r\n // Only available on V8.\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, ErrorFactory.prototype.create);\r\n }\r\n }\r\n}\r\nclass ErrorFactory {\r\n constructor(service, serviceName, errors) {\r\n this.service = service;\r\n this.serviceName = serviceName;\r\n this.errors = errors;\r\n }\r\n create(code, ...data) {\r\n const customData = data[0] || {};\r\n const fullCode = `${this.service}/${code}`;\r\n const template = this.errors[code];\r\n const message = template ? replaceTemplate(template, customData) : 'Error';\r\n // Service Name: Error message (service/code).\r\n const fullMessage = `${this.serviceName}: ${message} (${fullCode}).`;\r\n const error = new FirebaseError(fullCode, fullMessage, customData);\r\n return error;\r\n }\r\n}\r\nfunction replaceTemplate(template, data) {\r\n return template.replace(PATTERN, (_, key) => {\r\n const value = data[key];\r\n return value != null ? String(value) : `<${key}?>`;\r\n });\r\n}\r\nconst PATTERN = /\\{\\$([^}]+)}/g;\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Evaluates a JSON string into a javascript object.\r\n *\r\n * @param {string} str A string containing JSON.\r\n * @return {*} The javascript object representing the specified JSON.\r\n */\r\nfunction jsonEval(str) {\r\n return JSON.parse(str);\r\n}\r\n/**\r\n * Returns JSON representing a javascript object.\r\n * @param {*} data Javascript object to be stringified.\r\n * @return {string} The JSON contents of the object.\r\n */\r\nfunction stringify(data) {\r\n return JSON.stringify(data);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Decodes a Firebase auth. token into constituent parts.\r\n *\r\n * Notes:\r\n * - May return with invalid / incomplete claims if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst decode = function (token) {\r\n let header = {}, claims = {}, data = {}, signature = '';\r\n try {\r\n const parts = token.split('.');\r\n header = jsonEval(base64Decode(parts[0]) || '');\r\n claims = jsonEval(base64Decode(parts[1]) || '');\r\n signature = parts[2];\r\n data = claims['d'] || {};\r\n delete claims['d'];\r\n }\r\n catch (e) { }\r\n return {\r\n header,\r\n claims,\r\n data,\r\n signature\r\n };\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and checks the validity of its time-based claims. Will return true if the\r\n * token is within the time window authorized by the 'nbf' (not-before) and 'iat' (issued-at) claims.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isValidTimestamp = function (token) {\r\n const claims = decode(token).claims;\r\n const now = Math.floor(new Date().getTime() / 1000);\r\n let validSince = 0, validUntil = 0;\r\n if (typeof claims === 'object') {\r\n if (claims.hasOwnProperty('nbf')) {\r\n validSince = claims['nbf'];\r\n }\r\n else if (claims.hasOwnProperty('iat')) {\r\n validSince = claims['iat'];\r\n }\r\n if (claims.hasOwnProperty('exp')) {\r\n validUntil = claims['exp'];\r\n }\r\n else {\r\n // token will expire after 24h by default\r\n validUntil = validSince + 86400;\r\n }\r\n }\r\n return (!!now &&\r\n !!validSince &&\r\n !!validUntil &&\r\n now >= validSince &&\r\n now <= validUntil);\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and returns its issued at time if valid, null otherwise.\r\n *\r\n * Notes:\r\n * - May return null if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst issuedAtTime = function (token) {\r\n const claims = decode(token).claims;\r\n if (typeof claims === 'object' && claims.hasOwnProperty('iat')) {\r\n return claims['iat'];\r\n }\r\n return null;\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and checks the validity of its format. Expects a valid issued-at time.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isValidFormat = function (token) {\r\n const decoded = decode(token), claims = decoded.claims;\r\n return !!claims && typeof claims === 'object' && claims.hasOwnProperty('iat');\r\n};\r\n/**\r\n * Attempts to peer into an auth token and determine if it's an admin auth token by looking at the claims portion.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isAdmin = function (token) {\r\n const claims = decode(token).claims;\r\n return typeof claims === 'object' && claims['admin'] === true;\r\n};\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction contains(obj, key) {\r\n return Object.prototype.hasOwnProperty.call(obj, key);\r\n}\r\nfunction safeGet(obj, key) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n return obj[key];\r\n }\r\n else {\r\n return undefined;\r\n }\r\n}\r\nfunction isEmpty(obj) {\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\nfunction map(obj, fn, contextObj) {\r\n const res = {};\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n res[key] = fn.call(contextObj, obj[key], key, obj);\r\n }\r\n }\r\n return res;\r\n}\r\n/**\r\n * Deep equal two objects. Support Arrays and Objects.\r\n */\r\nfunction deepEqual(a, b) {\r\n if (a === b) {\r\n return true;\r\n }\r\n const aKeys = Object.keys(a);\r\n const bKeys = Object.keys(b);\r\n for (const k of aKeys) {\r\n if (!bKeys.includes(k)) {\r\n return false;\r\n }\r\n const aProp = a[k];\r\n const bProp = b[k];\r\n if (isObject(aProp) && isObject(bProp)) {\r\n if (!deepEqual(aProp, bProp)) {\r\n return false;\r\n }\r\n }\r\n else if (aProp !== bProp) {\r\n return false;\r\n }\r\n }\r\n for (const k of bKeys) {\r\n if (!aKeys.includes(k)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\nfunction isObject(thing) {\r\n return thing !== null && typeof thing === 'object';\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a querystring-formatted string (e.g. &arg=val&arg2=val2) from a\r\n * params object (e.g. {arg: 'val', arg2: 'val2'})\r\n * Note: You must prepend it with ? when adding it to a URL.\r\n */\r\nfunction querystring(querystringParams) {\r\n const params = [];\r\n for (const [key, value] of Object.entries(querystringParams)) {\r\n if (Array.isArray(value)) {\r\n value.forEach(arrayVal => {\r\n params.push(encodeURIComponent(key) + '=' + encodeURIComponent(arrayVal));\r\n });\r\n }\r\n else {\r\n params.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));\r\n }\r\n }\r\n return params.length ? '&' + params.join('&') : '';\r\n}\r\n/**\r\n * Decodes a querystring (e.g. ?arg=val&arg2=val2) into a params object\r\n * (e.g. {arg: 'val', arg2: 'val2'})\r\n */\r\nfunction querystringDecode(querystring) {\r\n const obj = {};\r\n const tokens = querystring.replace(/^\\?/, '').split('&');\r\n tokens.forEach(token => {\r\n if (token) {\r\n const [key, value] = token.split('=');\r\n obj[decodeURIComponent(key)] = decodeURIComponent(value);\r\n }\r\n });\r\n return obj;\r\n}\r\n/**\r\n * Extract the query string part of a URL, including the leading question mark (if present).\r\n */\r\nfunction extractQuerystring(url) {\r\n const queryStart = url.indexOf('?');\r\n if (!queryStart) {\r\n return '';\r\n }\r\n const fragmentStart = url.indexOf('#', queryStart);\r\n return url.substring(queryStart, fragmentStart > 0 ? fragmentStart : undefined);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @fileoverview SHA-1 cryptographic hash.\r\n * Variable names follow the notation in FIPS PUB 180-3:\r\n * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.\r\n *\r\n * Usage:\r\n * var sha1 = new sha1();\r\n * sha1.update(bytes);\r\n * var hash = sha1.digest();\r\n *\r\n * Performance:\r\n * Chrome 23: ~400 Mbit/s\r\n * Firefox 16: ~250 Mbit/s\r\n *\r\n */\r\n/**\r\n * SHA-1 cryptographic hash constructor.\r\n *\r\n * The properties declared here are discussed in the above algorithm document.\r\n * @constructor\r\n * @final\r\n * @struct\r\n */\r\nclass Sha1 {\r\n constructor() {\r\n /**\r\n * Holds the previous values of accumulated variables a-e in the compress_\r\n * function.\r\n * @private\r\n */\r\n this.chain_ = [];\r\n /**\r\n * A buffer holding the partially computed hash result.\r\n * @private\r\n */\r\n this.buf_ = [];\r\n /**\r\n * An array of 80 bytes, each a part of the message to be hashed. Referred to\r\n * as the message schedule in the docs.\r\n * @private\r\n */\r\n this.W_ = [];\r\n /**\r\n * Contains data needed to pad messages less than 64 bytes.\r\n * @private\r\n */\r\n this.pad_ = [];\r\n /**\r\n * @private {number}\r\n */\r\n this.inbuf_ = 0;\r\n /**\r\n * @private {number}\r\n */\r\n this.total_ = 0;\r\n this.blockSize = 512 / 8;\r\n this.pad_[0] = 128;\r\n for (let i = 1; i < this.blockSize; ++i) {\r\n this.pad_[i] = 0;\r\n }\r\n this.reset();\r\n }\r\n reset() {\r\n this.chain_[0] = 0x67452301;\r\n this.chain_[1] = 0xefcdab89;\r\n this.chain_[2] = 0x98badcfe;\r\n this.chain_[3] = 0x10325476;\r\n this.chain_[4] = 0xc3d2e1f0;\r\n this.inbuf_ = 0;\r\n this.total_ = 0;\r\n }\r\n /**\r\n * Internal compress helper function.\r\n * @param buf Block to compress.\r\n * @param offset Offset of the block in the buffer.\r\n * @private\r\n */\r\n compress_(buf, offset) {\r\n if (!offset) {\r\n offset = 0;\r\n }\r\n const W = this.W_;\r\n // get 16 big endian words\r\n if (typeof buf === 'string') {\r\n for (let i = 0; i < 16; i++) {\r\n // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS\r\n // have a bug that turns the post-increment ++ operator into pre-increment\r\n // during JIT compilation. We have code that depends heavily on SHA-1 for\r\n // correctness and which is affected by this bug, so I've removed all uses\r\n // of post-increment ++ in which the result value is used. We can revert\r\n // this change once the Safari bug\r\n // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and\r\n // most clients have been updated.\r\n W[i] =\r\n (buf.charCodeAt(offset) << 24) |\r\n (buf.charCodeAt(offset + 1) << 16) |\r\n (buf.charCodeAt(offset + 2) << 8) |\r\n buf.charCodeAt(offset + 3);\r\n offset += 4;\r\n }\r\n }\r\n else {\r\n for (let i = 0; i < 16; i++) {\r\n W[i] =\r\n (buf[offset] << 24) |\r\n (buf[offset + 1] << 16) |\r\n (buf[offset + 2] << 8) |\r\n buf[offset + 3];\r\n offset += 4;\r\n }\r\n }\r\n // expand to 80 words\r\n for (let i = 16; i < 80; i++) {\r\n const t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];\r\n W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;\r\n }\r\n let a = this.chain_[0];\r\n let b = this.chain_[1];\r\n let c = this.chain_[2];\r\n let d = this.chain_[3];\r\n let e = this.chain_[4];\r\n let f, k;\r\n // TODO(user): Try to unroll this loop to speed up the computation.\r\n for (let i = 0; i < 80; i++) {\r\n if (i < 40) {\r\n if (i < 20) {\r\n f = d ^ (b & (c ^ d));\r\n k = 0x5a827999;\r\n }\r\n else {\r\n f = b ^ c ^ d;\r\n k = 0x6ed9eba1;\r\n }\r\n }\r\n else {\r\n if (i < 60) {\r\n f = (b & c) | (d & (b | c));\r\n k = 0x8f1bbcdc;\r\n }\r\n else {\r\n f = b ^ c ^ d;\r\n k = 0xca62c1d6;\r\n }\r\n }\r\n const t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;\r\n e = d;\r\n d = c;\r\n c = ((b << 30) | (b >>> 2)) & 0xffffffff;\r\n b = a;\r\n a = t;\r\n }\r\n this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;\r\n this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;\r\n this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;\r\n this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;\r\n this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;\r\n }\r\n update(bytes, length) {\r\n // TODO(johnlenz): tighten the function signature and remove this check\r\n if (bytes == null) {\r\n return;\r\n }\r\n if (length === undefined) {\r\n length = bytes.length;\r\n }\r\n const lengthMinusBlock = length - this.blockSize;\r\n let n = 0;\r\n // Using local instead of member variables gives ~5% speedup on Firefox 16.\r\n const buf = this.buf_;\r\n let inbuf = this.inbuf_;\r\n // The outer while loop should execute at most twice.\r\n while (n < length) {\r\n // When we have no data in the block to top up, we can directly process the\r\n // input buffer (assuming it contains sufficient data). This gives ~25%\r\n // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that\r\n // the data is provided in large chunks (or in multiples of 64 bytes).\r\n if (inbuf === 0) {\r\n while (n <= lengthMinusBlock) {\r\n this.compress_(bytes, n);\r\n n += this.blockSize;\r\n }\r\n }\r\n if (typeof bytes === 'string') {\r\n while (n < length) {\r\n buf[inbuf] = bytes.charCodeAt(n);\r\n ++inbuf;\r\n ++n;\r\n if (inbuf === this.blockSize) {\r\n this.compress_(buf);\r\n inbuf = 0;\r\n // Jump to the outer loop so we use the full-block optimization.\r\n break;\r\n }\r\n }\r\n }\r\n else {\r\n while (n < length) {\r\n buf[inbuf] = bytes[n];\r\n ++inbuf;\r\n ++n;\r\n if (inbuf === this.blockSize) {\r\n this.compress_(buf);\r\n inbuf = 0;\r\n // Jump to the outer loop so we use the full-block optimization.\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n this.inbuf_ = inbuf;\r\n this.total_ += length;\r\n }\r\n /** @override */\r\n digest() {\r\n const digest = [];\r\n let totalBits = this.total_ * 8;\r\n // Add pad 0x80 0x00*.\r\n if (this.inbuf_ < 56) {\r\n this.update(this.pad_, 56 - this.inbuf_);\r\n }\r\n else {\r\n this.update(this.pad_, this.blockSize - (this.inbuf_ - 56));\r\n }\r\n // Add # bits.\r\n for (let i = this.blockSize - 1; i >= 56; i--) {\r\n this.buf_[i] = totalBits & 255;\r\n totalBits /= 256; // Don't use bit-shifting here!\r\n }\r\n this.compress_(this.buf_);\r\n let n = 0;\r\n for (let i = 0; i < 5; i++) {\r\n for (let j = 24; j >= 0; j -= 8) {\r\n digest[n] = (this.chain_[i] >> j) & 255;\r\n ++n;\r\n }\r\n }\r\n return digest;\r\n }\r\n}\n\n/**\r\n * Helper to make a Subscribe function (just like Promise helps make a\r\n * Thenable).\r\n *\r\n * @param executor Function which can make calls to a single Observer\r\n * as a proxy.\r\n * @param onNoObservers Callback when count of Observers goes to zero.\r\n */\r\nfunction createSubscribe(executor, onNoObservers) {\r\n const proxy = new ObserverProxy(executor, onNoObservers);\r\n return proxy.subscribe.bind(proxy);\r\n}\r\n/**\r\n * Implement fan-out for any number of Observers attached via a subscribe\r\n * function.\r\n */\r\nclass ObserverProxy {\r\n /**\r\n * @param executor Function which can make calls to a single Observer\r\n * as a proxy.\r\n * @param onNoObservers Callback when count of Observers goes to zero.\r\n */\r\n constructor(executor, onNoObservers) {\r\n this.observers = [];\r\n this.unsubscribes = [];\r\n this.observerCount = 0;\r\n // Micro-task scheduling by calling task.then().\r\n this.task = Promise.resolve();\r\n this.finalized = false;\r\n this.onNoObservers = onNoObservers;\r\n // Call the executor asynchronously so subscribers that are called\r\n // synchronously after the creation of the subscribe function\r\n // can still receive the very first value generated in the executor.\r\n this.task\r\n .then(() => {\r\n executor(this);\r\n })\r\n .catch(e => {\r\n this.error(e);\r\n });\r\n }\r\n next(value) {\r\n this.forEachObserver((observer) => {\r\n observer.next(value);\r\n });\r\n }\r\n error(error) {\r\n this.forEachObserver((observer) => {\r\n observer.error(error);\r\n });\r\n this.close(error);\r\n }\r\n complete() {\r\n this.forEachObserver((observer) => {\r\n observer.complete();\r\n });\r\n this.close();\r\n }\r\n /**\r\n * Subscribe function that can be used to add an Observer to the fan-out list.\r\n *\r\n * - We require that no event is sent to a subscriber sychronously to their\r\n * call to subscribe().\r\n */\r\n subscribe(nextOrObserver, error, complete) {\r\n let observer;\r\n if (nextOrObserver === undefined &&\r\n error === undefined &&\r\n complete === undefined) {\r\n throw new Error('Missing Observer.');\r\n }\r\n // Assemble an Observer object when passed as callback functions.\r\n if (implementsAnyMethods(nextOrObserver, [\r\n 'next',\r\n 'error',\r\n 'complete'\r\n ])) {\r\n observer = nextOrObserver;\r\n }\r\n else {\r\n observer = {\r\n next: nextOrObserver,\r\n error,\r\n complete\r\n };\r\n }\r\n if (observer.next === undefined) {\r\n observer.next = noop;\r\n }\r\n if (observer.error === undefined) {\r\n observer.error = noop;\r\n }\r\n if (observer.complete === undefined) {\r\n observer.complete = noop;\r\n }\r\n const unsub = this.unsubscribeOne.bind(this, this.observers.length);\r\n // Attempt to subscribe to a terminated Observable - we\r\n // just respond to the Observer with the final error or complete\r\n // event.\r\n if (this.finalized) {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n try {\r\n if (this.finalError) {\r\n observer.error(this.finalError);\r\n }\r\n else {\r\n observer.complete();\r\n }\r\n }\r\n catch (e) {\r\n // nothing\r\n }\r\n return;\r\n });\r\n }\r\n this.observers.push(observer);\r\n return unsub;\r\n }\r\n // Unsubscribe is synchronous - we guarantee that no events are sent to\r\n // any unsubscribed Observer.\r\n unsubscribeOne(i) {\r\n if (this.observers === undefined || this.observers[i] === undefined) {\r\n return;\r\n }\r\n delete this.observers[i];\r\n this.observerCount -= 1;\r\n if (this.observerCount === 0 && this.onNoObservers !== undefined) {\r\n this.onNoObservers(this);\r\n }\r\n }\r\n forEachObserver(fn) {\r\n if (this.finalized) {\r\n // Already closed by previous event....just eat the additional values.\r\n return;\r\n }\r\n // Since sendOne calls asynchronously - there is no chance that\r\n // this.observers will become undefined.\r\n for (let i = 0; i < this.observers.length; i++) {\r\n this.sendOne(i, fn);\r\n }\r\n }\r\n // Call the Observer via one of it's callback function. We are careful to\r\n // confirm that the observe has not been unsubscribed since this asynchronous\r\n // function had been queued.\r\n sendOne(i, fn) {\r\n // Execute the callback asynchronously\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n if (this.observers !== undefined && this.observers[i] !== undefined) {\r\n try {\r\n fn(this.observers[i]);\r\n }\r\n catch (e) {\r\n // Ignore exceptions raised in Observers or missing methods of an\r\n // Observer.\r\n // Log error to console. b/31404806\r\n if (typeof console !== 'undefined' && console.error) {\r\n console.error(e);\r\n }\r\n }\r\n }\r\n });\r\n }\r\n close(err) {\r\n if (this.finalized) {\r\n return;\r\n }\r\n this.finalized = true;\r\n if (err !== undefined) {\r\n this.finalError = err;\r\n }\r\n // Proxy is no longer needed - garbage collect references\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n this.observers = undefined;\r\n this.onNoObservers = undefined;\r\n });\r\n }\r\n}\r\n/** Turn synchronous function into one called asynchronously. */\r\n// eslint-disable-next-line @typescript-eslint/ban-types\r\nfunction async(fn, onError) {\r\n return (...args) => {\r\n Promise.resolve(true)\r\n .then(() => {\r\n fn(...args);\r\n })\r\n .catch((error) => {\r\n if (onError) {\r\n onError(error);\r\n }\r\n });\r\n };\r\n}\r\n/**\r\n * Return true if the object passed in implements any of the named methods.\r\n */\r\nfunction implementsAnyMethods(obj, methods) {\r\n if (typeof obj !== 'object' || obj === null) {\r\n return false;\r\n }\r\n for (const method of methods) {\r\n if (method in obj && typeof obj[method] === 'function') {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\nfunction noop() {\r\n // do nothing\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Check to make sure the appropriate number of arguments are provided for a public function.\r\n * Throws an error if it fails.\r\n *\r\n * @param fnName The function name\r\n * @param minCount The minimum number of arguments to allow for the function call\r\n * @param maxCount The maximum number of argument to allow for the function call\r\n * @param argCount The actual number of arguments provided.\r\n */\r\nconst validateArgCount = function (fnName, minCount, maxCount, argCount) {\r\n let argError;\r\n if (argCount < minCount) {\r\n argError = 'at least ' + minCount;\r\n }\r\n else if (argCount > maxCount) {\r\n argError = maxCount === 0 ? 'none' : 'no more than ' + maxCount;\r\n }\r\n if (argError) {\r\n const error = fnName +\r\n ' failed: Was called with ' +\r\n argCount +\r\n (argCount === 1 ? ' argument.' : ' arguments.') +\r\n ' Expects ' +\r\n argError +\r\n '.';\r\n throw new Error(error);\r\n }\r\n};\r\n/**\r\n * Generates a string to prefix an error message about failed argument validation\r\n *\r\n * @param fnName The function name\r\n * @param argName The name of the argument\r\n * @return The prefix to add to the error thrown for validation.\r\n */\r\nfunction errorPrefix(fnName, argName) {\r\n return `${fnName} failed: ${argName} argument `;\r\n}\r\n/**\r\n * @param fnName\r\n * @param argumentNumber\r\n * @param namespace\r\n * @param optional\r\n */\r\nfunction validateNamespace(fnName, namespace, optional) {\r\n if (optional && !namespace) {\r\n return;\r\n }\r\n if (typeof namespace !== 'string') {\r\n //TODO: I should do more validation here. We only allow certain chars in namespaces.\r\n throw new Error(errorPrefix(fnName, 'namespace') + 'must be a valid firebase namespace.');\r\n }\r\n}\r\nfunction validateCallback(fnName, argumentName, \r\n// eslint-disable-next-line @typescript-eslint/ban-types\r\ncallback, optional) {\r\n if (optional && !callback) {\r\n return;\r\n }\r\n if (typeof callback !== 'function') {\r\n throw new Error(errorPrefix(fnName, argumentName) + 'must be a valid function.');\r\n }\r\n}\r\nfunction validateContextObject(fnName, argumentName, context, optional) {\r\n if (optional && !context) {\r\n return;\r\n }\r\n if (typeof context !== 'object' || context === null) {\r\n throw new Error(errorPrefix(fnName, argumentName) + 'must be a valid context object.');\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// Code originally came from goog.crypt.stringToUtf8ByteArray, but for some reason they\r\n// automatically replaced '\\r\\n' with '\\n', and they didn't handle surrogate pairs,\r\n// so it's been modified.\r\n// Note that not all Unicode characters appear as single characters in JavaScript strings.\r\n// fromCharCode returns the UTF-16 encoding of a character - so some Unicode characters\r\n// use 2 characters in Javascript. All 4-byte UTF-8 characters begin with a first\r\n// character in the range 0xD800 - 0xDBFF (the first character of a so-called surrogate\r\n// pair).\r\n// See http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3\r\n/**\r\n * @param {string} str\r\n * @return {Array}\r\n */\r\nconst stringToByteArray = function (str) {\r\n const out = [];\r\n let p = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n let c = str.charCodeAt(i);\r\n // Is this the lead surrogate in a surrogate pair?\r\n if (c >= 0xd800 && c <= 0xdbff) {\r\n const high = c - 0xd800; // the high 10 bits.\r\n i++;\r\n assert(i < str.length, 'Surrogate pair missing trail surrogate.');\r\n const low = str.charCodeAt(i) - 0xdc00; // the low 10 bits.\r\n c = 0x10000 + (high << 10) + low;\r\n }\r\n if (c < 128) {\r\n out[p++] = c;\r\n }\r\n else if (c < 2048) {\r\n out[p++] = (c >> 6) | 192;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else if (c < 65536) {\r\n out[p++] = (c >> 12) | 224;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else {\r\n out[p++] = (c >> 18) | 240;\r\n out[p++] = ((c >> 12) & 63) | 128;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n }\r\n return out;\r\n};\r\n/**\r\n * Calculate length without actually converting; useful for doing cheaper validation.\r\n * @param {string} str\r\n * @return {number}\r\n */\r\nconst stringLength = function (str) {\r\n let p = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const c = str.charCodeAt(i);\r\n if (c < 128) {\r\n p++;\r\n }\r\n else if (c < 2048) {\r\n p += 2;\r\n }\r\n else if (c >= 0xd800 && c <= 0xdbff) {\r\n // Lead surrogate of a surrogate pair. The pair together will take 4 bytes to represent.\r\n p += 4;\r\n i++; // skip trail surrogate.\r\n }\r\n else {\r\n p += 3;\r\n }\r\n }\r\n return p;\r\n};\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The amount of milliseconds to exponentially increase.\r\n */\r\nconst DEFAULT_INTERVAL_MILLIS = 1000;\r\n/**\r\n * The factor to backoff by.\r\n * Should be a number greater than 1.\r\n */\r\nconst DEFAULT_BACKOFF_FACTOR = 2;\r\n/**\r\n * The maximum milliseconds to increase to.\r\n *\r\n * Visible for testing\r\n */\r\nconst MAX_VALUE_MILLIS = 4 * 60 * 60 * 1000; // Four hours, like iOS and Android.\r\n/**\r\n * The percentage of backoff time to randomize by.\r\n * See\r\n * http://go/safe-client-behavior#step-1-determine-the-appropriate-retry-interval-to-handle-spike-traffic\r\n * for context.\r\n *\r\n *
Visible for testing\r\n */\r\nconst RANDOM_FACTOR = 0.5;\r\n/**\r\n * Based on the backoff method from\r\n * https://github.com/google/closure-library/blob/master/closure/goog/math/exponentialbackoff.js.\r\n * Extracted here so we don't need to pass metadata and a stateful ExponentialBackoff object around.\r\n */\r\nfunction calculateBackoffMillis(backoffCount, intervalMillis = DEFAULT_INTERVAL_MILLIS, backoffFactor = DEFAULT_BACKOFF_FACTOR) {\r\n // Calculates an exponentially increasing value.\r\n // Deviation: calculates value from count and a constant interval, so we only need to save value\r\n // and count to restore state.\r\n const currBaseValue = intervalMillis * Math.pow(backoffFactor, backoffCount);\r\n // A random \"fuzz\" to avoid waves of retries.\r\n // Deviation: randomFactor is required.\r\n const randomWait = Math.round(\r\n // A fraction of the backoff value to add/subtract.\r\n // Deviation: changes multiplication order to improve readability.\r\n RANDOM_FACTOR *\r\n currBaseValue *\r\n // A random float (rounded to int by Math.round above) in the range [-1, 1]. Determines\r\n // if we add or subtract.\r\n (Math.random() - 0.5) *\r\n 2);\r\n // Limits backoff to max to avoid effectively permanent backoff.\r\n return Math.min(MAX_VALUE_MILLIS, currBaseValue + randomWait);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Provide English ordinal letters after a number\r\n */\r\nfunction ordinal(i) {\r\n if (!Number.isFinite(i)) {\r\n return `${i}`;\r\n }\r\n return i + indicator(i);\r\n}\r\nfunction indicator(i) {\r\n i = Math.abs(i);\r\n const cent = i % 100;\r\n if (cent >= 10 && cent <= 20) {\r\n return 'th';\r\n }\r\n const dec = i % 10;\r\n if (dec === 1) {\r\n return 'st';\r\n }\r\n if (dec === 2) {\r\n return 'nd';\r\n }\r\n if (dec === 3) {\r\n return 'rd';\r\n }\r\n return 'th';\r\n}\n\n/**\r\n * @license\r\n * Copyright 2021 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction getModularInstance(service) {\r\n if (service && service._delegate) {\r\n return service._delegate;\r\n }\r\n else {\r\n return service;\r\n }\r\n}\n\nexport { CONSTANTS, Deferred, ErrorFactory, FirebaseError, MAX_VALUE_MILLIS, RANDOM_FACTOR, Sha1, areCookiesEnabled, assert, assertionError, async, base64, base64Decode, base64Encode, base64urlEncodeWithoutPadding, calculateBackoffMillis, contains, createMockUserToken, createSubscribe, decode, deepCopy, deepEqual, deepExtend, errorPrefix, extractQuerystring, getGlobal, getModularInstance, getUA, isAdmin, isBrowser, isBrowserExtension, isElectron, isEmpty, isIE, isIndexedDBAvailable, isMobileCordova, isNode, isNodeSdk, isReactNative, isSafari, isUWP, isValidFormat, isValidTimestamp, issuedAtTime, jsonEval, map, ordinal, querystring, querystringDecode, safeGet, stringLength, stringToByteArray, stringify, validateArgCount, validateCallback, validateContextObject, validateIndexedDBOpenable, validateNamespace };\n//# sourceMappingURL=index.esm2017.js.map\n","import { Deferred } from '@firebase/util';\n\n/**\r\n * Component for service name T, e.g. `auth`, `auth-internal`\r\n */\r\nclass Component {\r\n /**\r\n *\r\n * @param name The public service name, e.g. app, auth, firestore, database\r\n * @param instanceFactory Service factory responsible for creating the public interface\r\n * @param type whether the service provided by the component is public or private\r\n */\r\n constructor(name, instanceFactory, type) {\r\n this.name = name;\r\n this.instanceFactory = instanceFactory;\r\n this.type = type;\r\n this.multipleInstances = false;\r\n /**\r\n * Properties to be added to the service namespace\r\n */\r\n this.serviceProps = {};\r\n this.instantiationMode = \"LAZY\" /* LAZY */;\r\n this.onInstanceCreated = null;\r\n }\r\n setInstantiationMode(mode) {\r\n this.instantiationMode = mode;\r\n return this;\r\n }\r\n setMultipleInstances(multipleInstances) {\r\n this.multipleInstances = multipleInstances;\r\n return this;\r\n }\r\n setServiceProps(props) {\r\n this.serviceProps = props;\r\n return this;\r\n }\r\n setInstanceCreatedCallback(callback) {\r\n this.onInstanceCreated = callback;\r\n return this;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DEFAULT_ENTRY_NAME = '[DEFAULT]';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Provider for instance for service name T, e.g. 'auth', 'auth-internal'\r\n * NameServiceMapping[T] is an alias for the type of the instance\r\n */\r\nclass Provider {\r\n constructor(name, container) {\r\n this.name = name;\r\n this.container = container;\r\n this.component = null;\r\n this.instances = new Map();\r\n this.instancesDeferred = new Map();\r\n this.instancesOptions = new Map();\r\n this.onInitCallbacks = new Map();\r\n }\r\n /**\r\n * @param identifier A provider can provide mulitple instances of a service\r\n * if this.component.multipleInstances is true.\r\n */\r\n get(identifier) {\r\n // if multipleInstances is not supported, use the default name\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);\r\n if (!this.instancesDeferred.has(normalizedIdentifier)) {\r\n const deferred = new Deferred();\r\n this.instancesDeferred.set(normalizedIdentifier, deferred);\r\n if (this.isInitialized(normalizedIdentifier) ||\r\n this.shouldAutoInitialize()) {\r\n // initialize the service if it can be auto-initialized\r\n try {\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n if (instance) {\r\n deferred.resolve(instance);\r\n }\r\n }\r\n catch (e) {\r\n // when the instance factory throws an exception during get(), it should not cause\r\n // a fatal error. We just return the unresolved promise in this case.\r\n }\r\n }\r\n }\r\n return this.instancesDeferred.get(normalizedIdentifier).promise;\r\n }\r\n getImmediate(options) {\r\n var _a;\r\n // if multipleInstances is not supported, use the default name\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(options === null || options === void 0 ? void 0 : options.identifier);\r\n const optional = (_a = options === null || options === void 0 ? void 0 : options.optional) !== null && _a !== void 0 ? _a : false;\r\n if (this.isInitialized(normalizedIdentifier) ||\r\n this.shouldAutoInitialize()) {\r\n try {\r\n return this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n }\r\n catch (e) {\r\n if (optional) {\r\n return null;\r\n }\r\n else {\r\n throw e;\r\n }\r\n }\r\n }\r\n else {\r\n // In case a component is not initialized and should/can not be auto-initialized at the moment, return null if the optional flag is set, or throw\r\n if (optional) {\r\n return null;\r\n }\r\n else {\r\n throw Error(`Service ${this.name} is not available`);\r\n }\r\n }\r\n }\r\n getComponent() {\r\n return this.component;\r\n }\r\n setComponent(component) {\r\n if (component.name !== this.name) {\r\n throw Error(`Mismatching Component ${component.name} for Provider ${this.name}.`);\r\n }\r\n if (this.component) {\r\n throw Error(`Component for ${this.name} has already been provided`);\r\n }\r\n this.component = component;\r\n // return early without attempting to initialize the component if the component requires explicit initialization (calling `Provider.initialize()`)\r\n if (!this.shouldAutoInitialize()) {\r\n return;\r\n }\r\n // if the service is eager, initialize the default instance\r\n if (isComponentEager(component)) {\r\n try {\r\n this.getOrInitializeService({ instanceIdentifier: DEFAULT_ENTRY_NAME });\r\n }\r\n catch (e) {\r\n // when the instance factory for an eager Component throws an exception during the eager\r\n // initialization, it should not cause a fatal error.\r\n // TODO: Investigate if we need to make it configurable, because some component may want to cause\r\n // a fatal error in this case?\r\n }\r\n }\r\n // Create service instances for the pending promises and resolve them\r\n // NOTE: if this.multipleInstances is false, only the default instance will be created\r\n // and all promises with resolve with it regardless of the identifier.\r\n for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) {\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier);\r\n try {\r\n // `getOrInitializeService()` should always return a valid instance since a component is guaranteed. use ! to make typescript happy.\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n instanceDeferred.resolve(instance);\r\n }\r\n catch (e) {\r\n // when the instance factory throws an exception, it should not cause\r\n // a fatal error. We just leave the promise unresolved.\r\n }\r\n }\r\n }\r\n clearInstance(identifier = DEFAULT_ENTRY_NAME) {\r\n this.instancesDeferred.delete(identifier);\r\n this.instancesOptions.delete(identifier);\r\n this.instances.delete(identifier);\r\n }\r\n // app.delete() will call this method on every provider to delete the services\r\n // TODO: should we mark the provider as deleted?\r\n async delete() {\r\n const services = Array.from(this.instances.values());\r\n await Promise.all([\r\n ...services\r\n .filter(service => 'INTERNAL' in service) // legacy services\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n .map(service => service.INTERNAL.delete()),\r\n ...services\r\n .filter(service => '_delete' in service) // modularized services\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n .map(service => service._delete())\r\n ]);\r\n }\r\n isComponentSet() {\r\n return this.component != null;\r\n }\r\n isInitialized(identifier = DEFAULT_ENTRY_NAME) {\r\n return this.instances.has(identifier);\r\n }\r\n getOptions(identifier = DEFAULT_ENTRY_NAME) {\r\n return this.instancesOptions.get(identifier) || {};\r\n }\r\n initialize(opts = {}) {\r\n const { options = {} } = opts;\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(opts.instanceIdentifier);\r\n if (this.isInitialized(normalizedIdentifier)) {\r\n throw Error(`${this.name}(${normalizedIdentifier}) has already been initialized`);\r\n }\r\n if (!this.isComponentSet()) {\r\n throw Error(`Component ${this.name} has not been registered yet`);\r\n }\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier,\r\n options\r\n });\r\n // resolve any pending promise waiting for the service instance\r\n for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) {\r\n const normalizedDeferredIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier);\r\n if (normalizedIdentifier === normalizedDeferredIdentifier) {\r\n instanceDeferred.resolve(instance);\r\n }\r\n }\r\n return instance;\r\n }\r\n /**\r\n *\r\n * @param callback - a function that will be invoked after the provider has been initialized by calling provider.initialize().\r\n * The function is invoked SYNCHRONOUSLY, so it should not execute any longrunning tasks in order to not block the program.\r\n *\r\n * @param identifier An optional instance identifier\r\n * @returns a function to unregister the callback\r\n */\r\n onInit(callback, identifier) {\r\n var _a;\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);\r\n const existingCallbacks = (_a = this.onInitCallbacks.get(normalizedIdentifier)) !== null && _a !== void 0 ? _a : new Set();\r\n existingCallbacks.add(callback);\r\n this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks);\r\n const existingInstance = this.instances.get(normalizedIdentifier);\r\n if (existingInstance) {\r\n callback(existingInstance, normalizedIdentifier);\r\n }\r\n return () => {\r\n existingCallbacks.delete(callback);\r\n };\r\n }\r\n /**\r\n * Invoke onInit callbacks synchronously\r\n * @param instance the service instance`\r\n */\r\n invokeOnInitCallbacks(instance, identifier) {\r\n const callbacks = this.onInitCallbacks.get(identifier);\r\n if (!callbacks) {\r\n return;\r\n }\r\n for (const callback of callbacks) {\r\n try {\r\n callback(instance, identifier);\r\n }\r\n catch (_a) {\r\n // ignore errors in the onInit callback\r\n }\r\n }\r\n }\r\n getOrInitializeService({ instanceIdentifier, options = {} }) {\r\n let instance = this.instances.get(instanceIdentifier);\r\n if (!instance && this.component) {\r\n instance = this.component.instanceFactory(this.container, {\r\n instanceIdentifier: normalizeIdentifierForFactory(instanceIdentifier),\r\n options\r\n });\r\n this.instances.set(instanceIdentifier, instance);\r\n this.instancesOptions.set(instanceIdentifier, options);\r\n /**\r\n * Invoke onInit listeners.\r\n * Note this.component.onInstanceCreated is different, which is used by the component creator,\r\n * while onInit listeners are registered by consumers of the provider.\r\n */\r\n this.invokeOnInitCallbacks(instance, instanceIdentifier);\r\n /**\r\n * Order is important\r\n * onInstanceCreated() should be called after this.instances.set(instanceIdentifier, instance); which\r\n * makes `isInitialized()` return true.\r\n */\r\n if (this.component.onInstanceCreated) {\r\n try {\r\n this.component.onInstanceCreated(this.container, instanceIdentifier, instance);\r\n }\r\n catch (_a) {\r\n // ignore errors in the onInstanceCreatedCallback\r\n }\r\n }\r\n }\r\n return instance || null;\r\n }\r\n normalizeInstanceIdentifier(identifier = DEFAULT_ENTRY_NAME) {\r\n if (this.component) {\r\n return this.component.multipleInstances ? identifier : DEFAULT_ENTRY_NAME;\r\n }\r\n else {\r\n return identifier; // assume multiple instances are supported before the component is provided.\r\n }\r\n }\r\n shouldAutoInitialize() {\r\n return (!!this.component &&\r\n this.component.instantiationMode !== \"EXPLICIT\" /* EXPLICIT */);\r\n }\r\n}\r\n// undefined should be passed to the service factory for the default instance\r\nfunction normalizeIdentifierForFactory(identifier) {\r\n return identifier === DEFAULT_ENTRY_NAME ? undefined : identifier;\r\n}\r\nfunction isComponentEager(component) {\r\n return component.instantiationMode === \"EAGER\" /* EAGER */;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * ComponentContainer that provides Providers for service name T, e.g. `auth`, `auth-internal`\r\n */\r\nclass ComponentContainer {\r\n constructor(name) {\r\n this.name = name;\r\n this.providers = new Map();\r\n }\r\n /**\r\n *\r\n * @param component Component being added\r\n * @param overwrite When a component with the same name has already been registered,\r\n * if overwrite is true: overwrite the existing component with the new component and create a new\r\n * provider with the new component. It can be useful in tests where you want to use different mocks\r\n * for different tests.\r\n * if overwrite is false: throw an exception\r\n */\r\n addComponent(component) {\r\n const provider = this.getProvider(component.name);\r\n if (provider.isComponentSet()) {\r\n throw new Error(`Component ${component.name} has already been registered with ${this.name}`);\r\n }\r\n provider.setComponent(component);\r\n }\r\n addOrOverwriteComponent(component) {\r\n const provider = this.getProvider(component.name);\r\n if (provider.isComponentSet()) {\r\n // delete the existing provider from the container, so we can register the new component\r\n this.providers.delete(component.name);\r\n }\r\n this.addComponent(component);\r\n }\r\n /**\r\n * getProvider provides a type safe interface where it can only be called with a field name\r\n * present in NameServiceMapping interface.\r\n *\r\n * Firebase SDKs providing services should extend NameServiceMapping interface to register\r\n * themselves.\r\n */\r\n getProvider(name) {\r\n if (this.providers.has(name)) {\r\n return this.providers.get(name);\r\n }\r\n // create a Provider for a service that hasn't registered with Firebase\r\n const provider = new Provider(name, this);\r\n this.providers.set(name, provider);\r\n return provider;\r\n }\r\n getProviders() {\r\n return Array.from(this.providers.values());\r\n }\r\n}\n\nexport { Component, ComponentContainer, Provider };\n//# sourceMappingURL=index.esm2017.js.map\n","/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * A container for all of the Logger instances\r\n */\r\nconst instances = [];\r\n/**\r\n * The JS SDK supports 5 log levels and also allows a user the ability to\r\n * silence the logs altogether.\r\n *\r\n * The order is a follows:\r\n * DEBUG < VERBOSE < INFO < WARN < ERROR\r\n *\r\n * All of the log types above the current log level will be captured (i.e. if\r\n * you set the log level to `INFO`, errors will still be logged, but `DEBUG` and\r\n * `VERBOSE` logs will not)\r\n */\r\nvar LogLevel;\r\n(function (LogLevel) {\r\n LogLevel[LogLevel[\"DEBUG\"] = 0] = \"DEBUG\";\r\n LogLevel[LogLevel[\"VERBOSE\"] = 1] = \"VERBOSE\";\r\n LogLevel[LogLevel[\"INFO\"] = 2] = \"INFO\";\r\n LogLevel[LogLevel[\"WARN\"] = 3] = \"WARN\";\r\n LogLevel[LogLevel[\"ERROR\"] = 4] = \"ERROR\";\r\n LogLevel[LogLevel[\"SILENT\"] = 5] = \"SILENT\";\r\n})(LogLevel || (LogLevel = {}));\r\nconst levelStringToEnum = {\r\n 'debug': LogLevel.DEBUG,\r\n 'verbose': LogLevel.VERBOSE,\r\n 'info': LogLevel.INFO,\r\n 'warn': LogLevel.WARN,\r\n 'error': LogLevel.ERROR,\r\n 'silent': LogLevel.SILENT\r\n};\r\n/**\r\n * The default log level\r\n */\r\nconst defaultLogLevel = LogLevel.INFO;\r\n/**\r\n * By default, `console.debug` is not displayed in the developer console (in\r\n * chrome). To avoid forcing users to have to opt-in to these logs twice\r\n * (i.e. once for firebase, and once in the console), we are sending `DEBUG`\r\n * logs to the `console.log` function.\r\n */\r\nconst ConsoleMethod = {\r\n [LogLevel.DEBUG]: 'log',\r\n [LogLevel.VERBOSE]: 'log',\r\n [LogLevel.INFO]: 'info',\r\n [LogLevel.WARN]: 'warn',\r\n [LogLevel.ERROR]: 'error'\r\n};\r\n/**\r\n * The default log handler will forward DEBUG, VERBOSE, INFO, WARN, and ERROR\r\n * messages on to their corresponding console counterparts (if the log method\r\n * is supported by the current log level)\r\n */\r\nconst defaultLogHandler = (instance, logType, ...args) => {\r\n if (logType < instance.logLevel) {\r\n return;\r\n }\r\n const now = new Date().toISOString();\r\n const method = ConsoleMethod[logType];\r\n if (method) {\r\n console[method](`[${now}] ${instance.name}:`, ...args);\r\n }\r\n else {\r\n throw new Error(`Attempted to log a message with an invalid logType (value: ${logType})`);\r\n }\r\n};\r\nclass Logger {\r\n /**\r\n * Gives you an instance of a Logger to capture messages according to\r\n * Firebase's logging scheme.\r\n *\r\n * @param name The name that the logs will be associated with\r\n */\r\n constructor(name) {\r\n this.name = name;\r\n /**\r\n * The log level of the given Logger instance.\r\n */\r\n this._logLevel = defaultLogLevel;\r\n /**\r\n * The main (internal) log handler for the Logger instance.\r\n * Can be set to a new function in internal package code but not by user.\r\n */\r\n this._logHandler = defaultLogHandler;\r\n /**\r\n * The optional, additional, user-defined log handler for the Logger instance.\r\n */\r\n this._userLogHandler = null;\r\n /**\r\n * Capture the current instance for later use\r\n */\r\n instances.push(this);\r\n }\r\n get logLevel() {\r\n return this._logLevel;\r\n }\r\n set logLevel(val) {\r\n if (!(val in LogLevel)) {\r\n throw new TypeError(`Invalid value \"${val}\" assigned to \\`logLevel\\``);\r\n }\r\n this._logLevel = val;\r\n }\r\n // Workaround for setter/getter having to be the same type.\r\n setLogLevel(val) {\r\n this._logLevel = typeof val === 'string' ? levelStringToEnum[val] : val;\r\n }\r\n get logHandler() {\r\n return this._logHandler;\r\n }\r\n set logHandler(val) {\r\n if (typeof val !== 'function') {\r\n throw new TypeError('Value assigned to `logHandler` must be a function');\r\n }\r\n this._logHandler = val;\r\n }\r\n get userLogHandler() {\r\n return this._userLogHandler;\r\n }\r\n set userLogHandler(val) {\r\n this._userLogHandler = val;\r\n }\r\n /**\r\n * The functions below are all based on the `console` interface\r\n */\r\n debug(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.DEBUG, ...args);\r\n this._logHandler(this, LogLevel.DEBUG, ...args);\r\n }\r\n log(...args) {\r\n this._userLogHandler &&\r\n this._userLogHandler(this, LogLevel.VERBOSE, ...args);\r\n this._logHandler(this, LogLevel.VERBOSE, ...args);\r\n }\r\n info(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.INFO, ...args);\r\n this._logHandler(this, LogLevel.INFO, ...args);\r\n }\r\n warn(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.WARN, ...args);\r\n this._logHandler(this, LogLevel.WARN, ...args);\r\n }\r\n error(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.ERROR, ...args);\r\n this._logHandler(this, LogLevel.ERROR, ...args);\r\n }\r\n}\r\nfunction setLogLevel(level) {\r\n instances.forEach(inst => {\r\n inst.setLogLevel(level);\r\n });\r\n}\r\nfunction setUserLogHandler(logCallback, options) {\r\n for (const instance of instances) {\r\n let customLogLevel = null;\r\n if (options && options.level) {\r\n customLogLevel = levelStringToEnum[options.level];\r\n }\r\n if (logCallback === null) {\r\n instance.userLogHandler = null;\r\n }\r\n else {\r\n instance.userLogHandler = (instance, level, ...args) => {\r\n const message = args\r\n .map(arg => {\r\n if (arg == null) {\r\n return null;\r\n }\r\n else if (typeof arg === 'string') {\r\n return arg;\r\n }\r\n else if (typeof arg === 'number' || typeof arg === 'boolean') {\r\n return arg.toString();\r\n }\r\n else if (arg instanceof Error) {\r\n return arg.message;\r\n }\r\n else {\r\n try {\r\n return JSON.stringify(arg);\r\n }\r\n catch (ignored) {\r\n return null;\r\n }\r\n }\r\n })\r\n .filter(arg => arg)\r\n .join(' ');\r\n if (level >= (customLogLevel !== null && customLogLevel !== void 0 ? customLogLevel : instance.logLevel)) {\r\n logCallback({\r\n level: LogLevel[level].toLowerCase(),\r\n message,\r\n args,\r\n type: instance.name\r\n });\r\n }\r\n };\r\n }\r\n }\r\n}\n\nexport { LogLevel, Logger, setLogLevel, setUserLogHandler };\n//# sourceMappingURL=index.esm2017.js.map\n","import { Component, ComponentContainer } from '@firebase/component';\nimport { Logger, setUserLogHandler, setLogLevel as setLogLevel$1 } from '@firebase/logger';\nimport { ErrorFactory, deepEqual } from '@firebase/util';\nexport { FirebaseError } from '@firebase/util';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass PlatformLoggerServiceImpl {\r\n constructor(container) {\r\n this.container = container;\r\n }\r\n // In initial implementation, this will be called by installations on\r\n // auth token refresh, and installations will send this string.\r\n getPlatformInfoString() {\r\n const providers = this.container.getProviders();\r\n // Loop through providers and get library/version pairs from any that are\r\n // version components.\r\n return providers\r\n .map(provider => {\r\n if (isVersionServiceProvider(provider)) {\r\n const service = provider.getImmediate();\r\n return `${service.library}/${service.version}`;\r\n }\r\n else {\r\n return null;\r\n }\r\n })\r\n .filter(logString => logString)\r\n .join(' ');\r\n }\r\n}\r\n/**\r\n *\r\n * @param provider check if this provider provides a VersionService\r\n *\r\n * NOTE: Using Provider<'app-version'> is a hack to indicate that the provider\r\n * provides VersionService. The provider is not necessarily a 'app-version'\r\n * provider.\r\n */\r\nfunction isVersionServiceProvider(provider) {\r\n const component = provider.getComponent();\r\n return (component === null || component === void 0 ? void 0 : component.type) === \"VERSION\" /* VERSION */;\r\n}\n\nconst name$o = \"@firebase/app\";\nconst version$1 = \"0.7.17\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst logger = new Logger('@firebase/app');\n\nconst name$n = \"@firebase/app-compat\";\n\nconst name$m = \"@firebase/analytics-compat\";\n\nconst name$l = \"@firebase/analytics\";\n\nconst name$k = \"@firebase/app-check-compat\";\n\nconst name$j = \"@firebase/app-check\";\n\nconst name$i = \"@firebase/auth\";\n\nconst name$h = \"@firebase/auth-compat\";\n\nconst name$g = \"@firebase/database\";\n\nconst name$f = \"@firebase/database-compat\";\n\nconst name$e = \"@firebase/functions\";\n\nconst name$d = \"@firebase/functions-compat\";\n\nconst name$c = \"@firebase/installations\";\n\nconst name$b = \"@firebase/installations-compat\";\n\nconst name$a = \"@firebase/messaging\";\n\nconst name$9 = \"@firebase/messaging-compat\";\n\nconst name$8 = \"@firebase/performance\";\n\nconst name$7 = \"@firebase/performance-compat\";\n\nconst name$6 = \"@firebase/remote-config\";\n\nconst name$5 = \"@firebase/remote-config-compat\";\n\nconst name$4 = \"@firebase/storage\";\n\nconst name$3 = \"@firebase/storage-compat\";\n\nconst name$2 = \"@firebase/firestore\";\n\nconst name$1 = \"@firebase/firestore-compat\";\n\nconst name = \"firebase\";\nconst version = \"9.6.7\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The default app name\r\n *\r\n * @internal\r\n */\r\nconst DEFAULT_ENTRY_NAME = '[DEFAULT]';\r\nconst PLATFORM_LOG_STRING = {\r\n [name$o]: 'fire-core',\r\n [name$n]: 'fire-core-compat',\r\n [name$l]: 'fire-analytics',\r\n [name$m]: 'fire-analytics-compat',\r\n [name$j]: 'fire-app-check',\r\n [name$k]: 'fire-app-check-compat',\r\n [name$i]: 'fire-auth',\r\n [name$h]: 'fire-auth-compat',\r\n [name$g]: 'fire-rtdb',\r\n [name$f]: 'fire-rtdb-compat',\r\n [name$e]: 'fire-fn',\r\n [name$d]: 'fire-fn-compat',\r\n [name$c]: 'fire-iid',\r\n [name$b]: 'fire-iid-compat',\r\n [name$a]: 'fire-fcm',\r\n [name$9]: 'fire-fcm-compat',\r\n [name$8]: 'fire-perf',\r\n [name$7]: 'fire-perf-compat',\r\n [name$6]: 'fire-rc',\r\n [name$5]: 'fire-rc-compat',\r\n [name$4]: 'fire-gcs',\r\n [name$3]: 'fire-gcs-compat',\r\n [name$2]: 'fire-fst',\r\n [name$1]: 'fire-fst-compat',\r\n 'fire-js': 'fire-js',\r\n [name]: 'fire-js-all'\r\n};\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @internal\r\n */\r\nconst _apps = new Map();\r\n/**\r\n * Registered components.\r\n *\r\n * @internal\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nconst _components = new Map();\r\n/**\r\n * @param component - the component being added to this app's container\r\n *\r\n * @internal\r\n */\r\nfunction _addComponent(app, component) {\r\n try {\r\n app.container.addComponent(component);\r\n }\r\n catch (e) {\r\n logger.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e);\r\n }\r\n}\r\n/**\r\n *\r\n * @internal\r\n */\r\nfunction _addOrOverwriteComponent(app, component) {\r\n app.container.addOrOverwriteComponent(component);\r\n}\r\n/**\r\n *\r\n * @param component - the component to register\r\n * @returns whether or not the component is registered successfully\r\n *\r\n * @internal\r\n */\r\nfunction _registerComponent(component) {\r\n const componentName = component.name;\r\n if (_components.has(componentName)) {\r\n logger.debug(`There were multiple attempts to register component ${componentName}.`);\r\n return false;\r\n }\r\n _components.set(componentName, component);\r\n // add the component to existing app instances\r\n for (const app of _apps.values()) {\r\n _addComponent(app, component);\r\n }\r\n return true;\r\n}\r\n/**\r\n *\r\n * @param app - FirebaseApp instance\r\n * @param name - service name\r\n *\r\n * @returns the provider for the service with the matching name\r\n *\r\n * @internal\r\n */\r\nfunction _getProvider(app, name) {\r\n return app.container.getProvider(name);\r\n}\r\n/**\r\n *\r\n * @param app - FirebaseApp instance\r\n * @param name - service name\r\n * @param instanceIdentifier - service instance identifier in case the service supports multiple instances\r\n *\r\n * @internal\r\n */\r\nfunction _removeServiceInstance(app, name, instanceIdentifier = DEFAULT_ENTRY_NAME) {\r\n _getProvider(app, name).clearInstance(instanceIdentifier);\r\n}\r\n/**\r\n * Test only\r\n *\r\n * @internal\r\n */\r\nfunction _clearComponents() {\r\n _components.clear();\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERRORS = {\r\n [\"no-app\" /* NO_APP */]: \"No Firebase App '{$appName}' has been created - \" +\r\n 'call Firebase App.initializeApp()',\r\n [\"bad-app-name\" /* BAD_APP_NAME */]: \"Illegal App name: '{$appName}\",\r\n [\"duplicate-app\" /* DUPLICATE_APP */]: \"Firebase App named '{$appName}' already exists with different options or config\",\r\n [\"app-deleted\" /* APP_DELETED */]: \"Firebase App named '{$appName}' already deleted\",\r\n [\"invalid-app-argument\" /* INVALID_APP_ARGUMENT */]: 'firebase.{$appName}() takes either no argument or a ' +\r\n 'Firebase App instance.',\r\n [\"invalid-log-argument\" /* INVALID_LOG_ARGUMENT */]: 'First argument to `onLog` must be null or a function.'\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory('app', 'Firebase', ERRORS);\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass FirebaseAppImpl {\r\n constructor(options, config, container) {\r\n this._isDeleted = false;\r\n this._options = Object.assign({}, options);\r\n this._config = Object.assign({}, config);\r\n this._name = config.name;\r\n this._automaticDataCollectionEnabled =\r\n config.automaticDataCollectionEnabled;\r\n this._container = container;\r\n this.container.addComponent(new Component('app', () => this, \"PUBLIC\" /* PUBLIC */));\r\n }\r\n get automaticDataCollectionEnabled() {\r\n this.checkDestroyed();\r\n return this._automaticDataCollectionEnabled;\r\n }\r\n set automaticDataCollectionEnabled(val) {\r\n this.checkDestroyed();\r\n this._automaticDataCollectionEnabled = val;\r\n }\r\n get name() {\r\n this.checkDestroyed();\r\n return this._name;\r\n }\r\n get options() {\r\n this.checkDestroyed();\r\n return this._options;\r\n }\r\n get config() {\r\n this.checkDestroyed();\r\n return this._config;\r\n }\r\n get container() {\r\n return this._container;\r\n }\r\n get isDeleted() {\r\n return this._isDeleted;\r\n }\r\n set isDeleted(val) {\r\n this._isDeleted = val;\r\n }\r\n /**\r\n * This function will throw an Error if the App has already been deleted -\r\n * use before performing API actions on the App.\r\n */\r\n checkDestroyed() {\r\n if (this.isDeleted) {\r\n throw ERROR_FACTORY.create(\"app-deleted\" /* APP_DELETED */, { appName: this._name });\r\n }\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The current SDK version.\r\n *\r\n * @public\r\n */\r\nconst SDK_VERSION = version;\r\nfunction initializeApp(options, rawConfig = {}) {\r\n if (typeof rawConfig !== 'object') {\r\n const name = rawConfig;\r\n rawConfig = { name };\r\n }\r\n const config = Object.assign({ name: DEFAULT_ENTRY_NAME, automaticDataCollectionEnabled: false }, rawConfig);\r\n const name = config.name;\r\n if (typeof name !== 'string' || !name) {\r\n throw ERROR_FACTORY.create(\"bad-app-name\" /* BAD_APP_NAME */, {\r\n appName: String(name)\r\n });\r\n }\r\n const existingApp = _apps.get(name);\r\n if (existingApp) {\r\n // return the existing app if options and config deep equal the ones in the existing app.\r\n if (deepEqual(options, existingApp.options) &&\r\n deepEqual(config, existingApp.config)) {\r\n return existingApp;\r\n }\r\n else {\r\n throw ERROR_FACTORY.create(\"duplicate-app\" /* DUPLICATE_APP */, { appName: name });\r\n }\r\n }\r\n const container = new ComponentContainer(name);\r\n for (const component of _components.values()) {\r\n container.addComponent(component);\r\n }\r\n const newApp = new FirebaseAppImpl(options, config, container);\r\n _apps.set(name, newApp);\r\n return newApp;\r\n}\r\n/**\r\n * Retrieves a {@link @firebase/app#FirebaseApp} instance.\r\n *\r\n * When called with no arguments, the default app is returned. When an app name\r\n * is provided, the app corresponding to that name is returned.\r\n *\r\n * An exception is thrown if the app being retrieved has not yet been\r\n * initialized.\r\n *\r\n * @example\r\n * ```javascript\r\n * // Return the default app\r\n * const app = getApp();\r\n * ```\r\n *\r\n * @example\r\n * ```javascript\r\n * // Return a named app\r\n * const otherApp = getApp(\"otherApp\");\r\n * ```\r\n *\r\n * @param name - Optional name of the app to return. If no name is\r\n * provided, the default is `\"[DEFAULT]\"`.\r\n *\r\n * @returns The app corresponding to the provided app name.\r\n * If no app name is provided, the default app is returned.\r\n *\r\n * @public\r\n */\r\nfunction getApp(name = DEFAULT_ENTRY_NAME) {\r\n const app = _apps.get(name);\r\n if (!app) {\r\n throw ERROR_FACTORY.create(\"no-app\" /* NO_APP */, { appName: name });\r\n }\r\n return app;\r\n}\r\n/**\r\n * A (read-only) array of all initialized apps.\r\n * @public\r\n */\r\nfunction getApps() {\r\n return Array.from(_apps.values());\r\n}\r\n/**\r\n * Renders this app unusable and frees the resources of all associated\r\n * services.\r\n *\r\n * @example\r\n * ```javascript\r\n * deleteApp(app)\r\n * .then(function() {\r\n * console.log(\"App deleted successfully\");\r\n * })\r\n * .catch(function(error) {\r\n * console.log(\"Error deleting app:\", error);\r\n * });\r\n * ```\r\n *\r\n * @public\r\n */\r\nasync function deleteApp(app) {\r\n const name = app.name;\r\n if (_apps.has(name)) {\r\n _apps.delete(name);\r\n await Promise.all(app.container\r\n .getProviders()\r\n .map(provider => provider.delete()));\r\n app.isDeleted = true;\r\n }\r\n}\r\n/**\r\n * Registers a library's name and version for platform logging purposes.\r\n * @param library - Name of 1p or 3p library (e.g. firestore, angularfire)\r\n * @param version - Current version of that library.\r\n * @param variant - Bundle variant, e.g., node, rn, etc.\r\n *\r\n * @public\r\n */\r\nfunction registerVersion(libraryKeyOrName, version, variant) {\r\n var _a;\r\n // TODO: We can use this check to whitelist strings when/if we set up\r\n // a good whitelist system.\r\n let library = (_a = PLATFORM_LOG_STRING[libraryKeyOrName]) !== null && _a !== void 0 ? _a : libraryKeyOrName;\r\n if (variant) {\r\n library += `-${variant}`;\r\n }\r\n const libraryMismatch = library.match(/\\s|\\//);\r\n const versionMismatch = version.match(/\\s|\\//);\r\n if (libraryMismatch || versionMismatch) {\r\n const warning = [\r\n `Unable to register library \"${library}\" with version \"${version}\":`\r\n ];\r\n if (libraryMismatch) {\r\n warning.push(`library name \"${library}\" contains illegal characters (whitespace or \"/\")`);\r\n }\r\n if (libraryMismatch && versionMismatch) {\r\n warning.push('and');\r\n }\r\n if (versionMismatch) {\r\n warning.push(`version name \"${version}\" contains illegal characters (whitespace or \"/\")`);\r\n }\r\n logger.warn(warning.join(' '));\r\n return;\r\n }\r\n _registerComponent(new Component(`${library}-version`, () => ({ library, version }), \"VERSION\" /* VERSION */));\r\n}\r\n/**\r\n * Sets log handler for all Firebase SDKs.\r\n * @param logCallback - An optional custom log handler that executes user code whenever\r\n * the Firebase SDK makes a logging call.\r\n *\r\n * @public\r\n */\r\nfunction onLog(logCallback, options) {\r\n if (logCallback !== null && typeof logCallback !== 'function') {\r\n throw ERROR_FACTORY.create(\"invalid-log-argument\" /* INVALID_LOG_ARGUMENT */);\r\n }\r\n setUserLogHandler(logCallback, options);\r\n}\r\n/**\r\n * Sets log level for all Firebase SDKs.\r\n *\r\n * All of the log types above the current log level are captured (i.e. if\r\n * you set the log level to `info`, errors are logged, but `debug` and\r\n * `verbose` logs are not).\r\n *\r\n * @public\r\n */\r\nfunction setLogLevel(logLevel) {\r\n setLogLevel$1(logLevel);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction registerCoreComponents(variant) {\r\n _registerComponent(new Component('platform-logger', container => new PlatformLoggerServiceImpl(container), \"PRIVATE\" /* PRIVATE */));\r\n // Register `app` package.\r\n registerVersion(name$o, version$1, variant);\r\n // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\n registerVersion(name$o, version$1, 'esm2017');\r\n // Register platform SDK identifier (no version).\r\n registerVersion('fire-js', '');\r\n}\n\n/**\r\n * Firebase App\r\n *\r\n * @remarks This package coordinates the communication between the different Firebase components\r\n * @packageDocumentation\r\n */\r\nregisterCoreComponents('');\n\nexport { SDK_VERSION, DEFAULT_ENTRY_NAME as _DEFAULT_ENTRY_NAME, _addComponent, _addOrOverwriteComponent, _apps, _clearComponents, _components, _getProvider, _registerComponent, _removeServiceInstance, deleteApp, getApp, getApps, initializeApp, onLog, registerVersion, setLogLevel };\n//# sourceMappingURL=index.esm2017.js.map\n","import { registerVersion } from '@firebase/app';\nexport * from '@firebase/app';\n\nvar name = \"firebase\";\nvar version = \"9.6.7\";\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nregisterVersion(name, version, 'app');\n//# sourceMappingURL=index.esm.js.map\n","import { getApp, _getProvider, _registerComponent, registerVersion } from '@firebase/app';\nimport { Component } from '@firebase/component';\nimport { ErrorFactory, FirebaseError } from '@firebase/util';\nimport { openDb } from 'idb';\n\nconst name = \"@firebase/installations\";\nconst version = \"0.5.5\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst PENDING_TIMEOUT_MS = 10000;\r\nconst PACKAGE_VERSION = `w:${version}`;\r\nconst INTERNAL_AUTH_VERSION = 'FIS_v2';\r\nconst INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1';\r\nconst TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour\r\nconst SERVICE = 'installations';\r\nconst SERVICE_NAME = 'Installations';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERROR_DESCRIPTION_MAP = {\r\n [\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: \"{$valueName}\"',\r\n [\"not-registered\" /* NOT_REGISTERED */]: 'Firebase Installation is not registered.',\r\n [\"installation-not-found\" /* INSTALLATION_NOT_FOUND */]: 'Firebase Installation not found.',\r\n [\"request-failed\" /* REQUEST_FAILED */]: '{$requestName} request failed with error \"{$serverCode} {$serverStatus}: {$serverMessage}\"',\r\n [\"app-offline\" /* APP_OFFLINE */]: 'Could not process request. Application offline.',\r\n [\"delete-pending-registration\" /* DELETE_PENDING_REGISTRATION */]: \"Can't delete installation while there is a pending registration request.\"\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory(SERVICE, SERVICE_NAME, ERROR_DESCRIPTION_MAP);\r\n/** Returns true if error is a FirebaseError that is based on an error from the server. */\r\nfunction isServerError(error) {\r\n return (error instanceof FirebaseError &&\r\n error.code.includes(\"request-failed\" /* REQUEST_FAILED */));\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction getInstallationsEndpoint({ projectId }) {\r\n return `${INSTALLATIONS_API_URL}/projects/${projectId}/installations`;\r\n}\r\nfunction extractAuthTokenInfoFromResponse(response) {\r\n return {\r\n token: response.token,\r\n requestStatus: 2 /* COMPLETED */,\r\n expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn),\r\n creationTime: Date.now()\r\n };\r\n}\r\nasync function getErrorFromResponse(requestName, response) {\r\n const responseJson = await response.json();\r\n const errorData = responseJson.error;\r\n return ERROR_FACTORY.create(\"request-failed\" /* REQUEST_FAILED */, {\r\n requestName,\r\n serverCode: errorData.code,\r\n serverMessage: errorData.message,\r\n serverStatus: errorData.status\r\n });\r\n}\r\nfunction getHeaders({ apiKey }) {\r\n return new Headers({\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'x-goog-api-key': apiKey\r\n });\r\n}\r\nfunction getHeadersWithAuth(appConfig, { refreshToken }) {\r\n const headers = getHeaders(appConfig);\r\n headers.append('Authorization', getAuthorizationHeader(refreshToken));\r\n return headers;\r\n}\r\n/**\r\n * Calls the passed in fetch wrapper and returns the response.\r\n * If the returned response has a status of 5xx, re-runs the function once and\r\n * returns the response.\r\n */\r\nasync function retryIfServerError(fn) {\r\n const result = await fn();\r\n if (result.status >= 500 && result.status < 600) {\r\n // Internal Server Error. Retry request.\r\n return fn();\r\n }\r\n return result;\r\n}\r\nfunction getExpiresInFromResponseExpiresIn(responseExpiresIn) {\r\n // This works because the server will never respond with fractions of a second.\r\n return Number(responseExpiresIn.replace('s', '000'));\r\n}\r\nfunction getAuthorizationHeader(refreshToken) {\r\n return `${INTERNAL_AUTH_VERSION} ${refreshToken}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function createInstallationRequest(appConfig, { fid }) {\r\n const endpoint = getInstallationsEndpoint(appConfig);\r\n const headers = getHeaders(appConfig);\r\n const body = {\r\n fid,\r\n authVersion: INTERNAL_AUTH_VERSION,\r\n appId: appConfig.appId,\r\n sdkVersion: PACKAGE_VERSION\r\n };\r\n const request = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (response.ok) {\r\n const responseValue = await response.json();\r\n const registeredInstallationEntry = {\r\n fid: responseValue.fid || fid,\r\n registrationStatus: 2 /* COMPLETED */,\r\n refreshToken: responseValue.refreshToken,\r\n authToken: extractAuthTokenInfoFromResponse(responseValue.authToken)\r\n };\r\n return registeredInstallationEntry;\r\n }\r\n else {\r\n throw await getErrorFromResponse('Create Installation', response);\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/** Returns a promise that resolves after given time passes. */\r\nfunction sleep(ms) {\r\n return new Promise(resolve => {\r\n setTimeout(resolve, ms);\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction bufferToBase64UrlSafe(array) {\r\n const b64 = btoa(String.fromCharCode(...array));\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst VALID_FID_PATTERN = /^[cdef][\\w-]{21}$/;\r\nconst INVALID_FID = '';\r\n/**\r\n * Generates a new FID using random values from Web Crypto API.\r\n * Returns an empty string if FID generation fails for any reason.\r\n */\r\nfunction generateFid() {\r\n try {\r\n // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5\r\n // bytes. our implementation generates a 17 byte array instead.\r\n const fidByteArray = new Uint8Array(17);\r\n const crypto = self.crypto || self.msCrypto;\r\n crypto.getRandomValues(fidByteArray);\r\n // Replace the first 4 random bits with the constant FID header of 0b0111.\r\n fidByteArray[0] = 0b01110000 + (fidByteArray[0] % 0b00010000);\r\n const fid = encode(fidByteArray);\r\n return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;\r\n }\r\n catch (_a) {\r\n // FID generation errored\r\n return INVALID_FID;\r\n }\r\n}\r\n/** Converts a FID Uint8Array to a base64 string representation. */\r\nfunction encode(fidByteArray) {\r\n const b64String = bufferToBase64UrlSafe(fidByteArray);\r\n // Remove the 23rd character that was added because of the extra 4 bits at the\r\n // end of our 17 byte array, and the '=' padding.\r\n return b64String.substr(0, 22);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/** Returns a string key that can be used to identify the app. */\r\nfunction getKey(appConfig) {\r\n return `${appConfig.appName}!${appConfig.appId}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst fidChangeCallbacks = new Map();\r\n/**\r\n * Calls the onIdChange callbacks with the new FID value, and broadcasts the\r\n * change to other tabs.\r\n */\r\nfunction fidChanged(appConfig, fid) {\r\n const key = getKey(appConfig);\r\n callFidChangeCallbacks(key, fid);\r\n broadcastFidChange(key, fid);\r\n}\r\nfunction addCallback(appConfig, callback) {\r\n // Open the broadcast channel if it's not already open,\r\n // to be able to listen to change events from other tabs.\r\n getBroadcastChannel();\r\n const key = getKey(appConfig);\r\n let callbackSet = fidChangeCallbacks.get(key);\r\n if (!callbackSet) {\r\n callbackSet = new Set();\r\n fidChangeCallbacks.set(key, callbackSet);\r\n }\r\n callbackSet.add(callback);\r\n}\r\nfunction removeCallback(appConfig, callback) {\r\n const key = getKey(appConfig);\r\n const callbackSet = fidChangeCallbacks.get(key);\r\n if (!callbackSet) {\r\n return;\r\n }\r\n callbackSet.delete(callback);\r\n if (callbackSet.size === 0) {\r\n fidChangeCallbacks.delete(key);\r\n }\r\n // Close broadcast channel if there are no more callbacks.\r\n closeBroadcastChannel();\r\n}\r\nfunction callFidChangeCallbacks(key, fid) {\r\n const callbacks = fidChangeCallbacks.get(key);\r\n if (!callbacks) {\r\n return;\r\n }\r\n for (const callback of callbacks) {\r\n callback(fid);\r\n }\r\n}\r\nfunction broadcastFidChange(key, fid) {\r\n const channel = getBroadcastChannel();\r\n if (channel) {\r\n channel.postMessage({ key, fid });\r\n }\r\n closeBroadcastChannel();\r\n}\r\nlet broadcastChannel = null;\r\n/** Opens and returns a BroadcastChannel if it is supported by the browser. */\r\nfunction getBroadcastChannel() {\r\n if (!broadcastChannel && 'BroadcastChannel' in self) {\r\n broadcastChannel = new BroadcastChannel('[Firebase] FID Change');\r\n broadcastChannel.onmessage = e => {\r\n callFidChangeCallbacks(e.data.key, e.data.fid);\r\n };\r\n }\r\n return broadcastChannel;\r\n}\r\nfunction closeBroadcastChannel() {\r\n if (fidChangeCallbacks.size === 0 && broadcastChannel) {\r\n broadcastChannel.close();\r\n broadcastChannel = null;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DATABASE_NAME = 'firebase-installations-database';\r\nconst DATABASE_VERSION = 1;\r\nconst OBJECT_STORE_NAME = 'firebase-installations-store';\r\nlet dbPromise = null;\r\nfunction getDbPromise() {\r\n if (!dbPromise) {\r\n dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, upgradeDB => {\r\n // We don't use 'break' in this switch statement, the fall-through\r\n // behavior is what we want, because if there are multiple versions between\r\n // the old version and the current version, we want ALL the migrations\r\n // that correspond to those versions to run, not only the last one.\r\n // eslint-disable-next-line default-case\r\n switch (upgradeDB.oldVersion) {\r\n case 0:\r\n upgradeDB.createObjectStore(OBJECT_STORE_NAME);\r\n }\r\n });\r\n }\r\n return dbPromise;\r\n}\r\n/** Assigns or overwrites the record for the given key with the given value. */\r\nasync function set(appConfig, value) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n const objectStore = tx.objectStore(OBJECT_STORE_NAME);\r\n const oldValue = await objectStore.get(key);\r\n await objectStore.put(value, key);\r\n await tx.complete;\r\n if (!oldValue || oldValue.fid !== value.fid) {\r\n fidChanged(appConfig, value.fid);\r\n }\r\n return value;\r\n}\r\n/** Removes record(s) from the objectStore that match the given key. */\r\nasync function remove(appConfig) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).delete(key);\r\n await tx.complete;\r\n}\r\n/**\r\n * Atomically updates a record with the result of updateFn, which gets\r\n * called with the current value. If newValue is undefined, the record is\r\n * deleted instead.\r\n * @return Updated value\r\n */\r\nasync function update(appConfig, updateFn) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n const store = tx.objectStore(OBJECT_STORE_NAME);\r\n const oldValue = await store.get(key);\r\n const newValue = updateFn(oldValue);\r\n if (newValue === undefined) {\r\n await store.delete(key);\r\n }\r\n else {\r\n await store.put(newValue, key);\r\n }\r\n await tx.complete;\r\n if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) {\r\n fidChanged(appConfig, newValue.fid);\r\n }\r\n return newValue;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Updates and returns the InstallationEntry from the database.\r\n * Also triggers a registration request if it is necessary and possible.\r\n */\r\nasync function getInstallationEntry(appConfig) {\r\n let registrationPromise;\r\n const installationEntry = await update(appConfig, oldEntry => {\r\n const installationEntry = updateOrCreateInstallationEntry(oldEntry);\r\n const entryWithPromise = triggerRegistrationIfNecessary(appConfig, installationEntry);\r\n registrationPromise = entryWithPromise.registrationPromise;\r\n return entryWithPromise.installationEntry;\r\n });\r\n if (installationEntry.fid === INVALID_FID) {\r\n // FID generation failed. Waiting for the FID from the server.\r\n return { installationEntry: await registrationPromise };\r\n }\r\n return {\r\n installationEntry,\r\n registrationPromise\r\n };\r\n}\r\n/**\r\n * Creates a new Installation Entry if one does not exist.\r\n * Also clears timed out pending requests.\r\n */\r\nfunction updateOrCreateInstallationEntry(oldEntry) {\r\n const entry = oldEntry || {\r\n fid: generateFid(),\r\n registrationStatus: 0 /* NOT_STARTED */\r\n };\r\n return clearTimedOutRequest(entry);\r\n}\r\n/**\r\n * If the Firebase Installation is not registered yet, this will trigger the\r\n * registration and return an InProgressInstallationEntry.\r\n *\r\n * If registrationPromise does not exist, the installationEntry is guaranteed\r\n * to be registered.\r\n */\r\nfunction triggerRegistrationIfNecessary(appConfig, installationEntry) {\r\n if (installationEntry.registrationStatus === 0 /* NOT_STARTED */) {\r\n if (!navigator.onLine) {\r\n // Registration required but app is offline.\r\n const registrationPromiseWithError = Promise.reject(ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */));\r\n return {\r\n installationEntry,\r\n registrationPromise: registrationPromiseWithError\r\n };\r\n }\r\n // Try registering. Change status to IN_PROGRESS.\r\n const inProgressEntry = {\r\n fid: installationEntry.fid,\r\n registrationStatus: 1 /* IN_PROGRESS */,\r\n registrationTime: Date.now()\r\n };\r\n const registrationPromise = registerInstallation(appConfig, inProgressEntry);\r\n return { installationEntry: inProgressEntry, registrationPromise };\r\n }\r\n else if (installationEntry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n return {\r\n installationEntry,\r\n registrationPromise: waitUntilFidRegistration(appConfig)\r\n };\r\n }\r\n else {\r\n return { installationEntry };\r\n }\r\n}\r\n/** This will be executed only once for each new Firebase Installation. */\r\nasync function registerInstallation(appConfig, installationEntry) {\r\n try {\r\n const registeredInstallationEntry = await createInstallationRequest(appConfig, installationEntry);\r\n return set(appConfig, registeredInstallationEntry);\r\n }\r\n catch (e) {\r\n if (isServerError(e) && e.customData.serverCode === 409) {\r\n // Server returned a \"FID can not be used\" error.\r\n // Generate a new ID next time.\r\n await remove(appConfig);\r\n }\r\n else {\r\n // Registration failed. Set FID as not registered.\r\n await set(appConfig, {\r\n fid: installationEntry.fid,\r\n registrationStatus: 0 /* NOT_STARTED */\r\n });\r\n }\r\n throw e;\r\n }\r\n}\r\n/** Call if FID registration is pending in another request. */\r\nasync function waitUntilFidRegistration(appConfig) {\r\n // Unfortunately, there is no way of reliably observing when a value in\r\n // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),\r\n // so we need to poll.\r\n let entry = await updateInstallationRequest(appConfig);\r\n while (entry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n // createInstallation request still in progress.\r\n await sleep(100);\r\n entry = await updateInstallationRequest(appConfig);\r\n }\r\n if (entry.registrationStatus === 0 /* NOT_STARTED */) {\r\n // The request timed out or failed in a different call. Try again.\r\n const { installationEntry, registrationPromise } = await getInstallationEntry(appConfig);\r\n if (registrationPromise) {\r\n return registrationPromise;\r\n }\r\n else {\r\n // if there is no registrationPromise, entry is registered.\r\n return installationEntry;\r\n }\r\n }\r\n return entry;\r\n}\r\n/**\r\n * Called only if there is a CreateInstallation request in progress.\r\n *\r\n * Updates the InstallationEntry in the DB based on the status of the\r\n * CreateInstallation request.\r\n *\r\n * Returns the updated InstallationEntry.\r\n */\r\nfunction updateInstallationRequest(appConfig) {\r\n return update(appConfig, oldEntry => {\r\n if (!oldEntry) {\r\n throw ERROR_FACTORY.create(\"installation-not-found\" /* INSTALLATION_NOT_FOUND */);\r\n }\r\n return clearTimedOutRequest(oldEntry);\r\n });\r\n}\r\nfunction clearTimedOutRequest(entry) {\r\n if (hasInstallationRequestTimedOut(entry)) {\r\n return {\r\n fid: entry.fid,\r\n registrationStatus: 0 /* NOT_STARTED */\r\n };\r\n }\r\n return entry;\r\n}\r\nfunction hasInstallationRequestTimedOut(installationEntry) {\r\n return (installationEntry.registrationStatus === 1 /* IN_PROGRESS */ &&\r\n installationEntry.registrationTime + PENDING_TIMEOUT_MS < Date.now());\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function generateAuthTokenRequest({ appConfig, platformLoggerProvider }, installationEntry) {\r\n const endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry);\r\n const headers = getHeadersWithAuth(appConfig, installationEntry);\r\n // If platform logger exists, add the platform info string to the header.\r\n const platformLogger = platformLoggerProvider.getImmediate({\r\n optional: true\r\n });\r\n if (platformLogger) {\r\n headers.append('x-firebase-client', platformLogger.getPlatformInfoString());\r\n }\r\n const body = {\r\n installation: {\r\n sdkVersion: PACKAGE_VERSION\r\n }\r\n };\r\n const request = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (response.ok) {\r\n const responseValue = await response.json();\r\n const completedAuthToken = extractAuthTokenInfoFromResponse(responseValue);\r\n return completedAuthToken;\r\n }\r\n else {\r\n throw await getErrorFromResponse('Generate Auth Token', response);\r\n }\r\n}\r\nfunction getGenerateAuthTokenEndpoint(appConfig, { fid }) {\r\n return `${getInstallationsEndpoint(appConfig)}/${fid}/authTokens:generate`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a valid authentication token for the installation. Generates a new\r\n * token if one doesn't exist, is expired or about to expire.\r\n *\r\n * Should only be called if the Firebase Installation is registered.\r\n */\r\nasync function refreshAuthToken(installations, forceRefresh = false) {\r\n let tokenPromise;\r\n const entry = await update(installations.appConfig, oldEntry => {\r\n if (!isEntryRegistered(oldEntry)) {\r\n throw ERROR_FACTORY.create(\"not-registered\" /* NOT_REGISTERED */);\r\n }\r\n const oldAuthToken = oldEntry.authToken;\r\n if (!forceRefresh && isAuthTokenValid(oldAuthToken)) {\r\n // There is a valid token in the DB.\r\n return oldEntry;\r\n }\r\n else if (oldAuthToken.requestStatus === 1 /* IN_PROGRESS */) {\r\n // There already is a token request in progress.\r\n tokenPromise = waitUntilAuthTokenRequest(installations, forceRefresh);\r\n return oldEntry;\r\n }\r\n else {\r\n // No token or token expired.\r\n if (!navigator.onLine) {\r\n throw ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */);\r\n }\r\n const inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry);\r\n tokenPromise = fetchAuthTokenFromServer(installations, inProgressEntry);\r\n return inProgressEntry;\r\n }\r\n });\r\n const authToken = tokenPromise\r\n ? await tokenPromise\r\n : entry.authToken;\r\n return authToken;\r\n}\r\n/**\r\n * Call only if FID is registered and Auth Token request is in progress.\r\n *\r\n * Waits until the current pending request finishes. If the request times out,\r\n * tries once in this thread as well.\r\n */\r\nasync function waitUntilAuthTokenRequest(installations, forceRefresh) {\r\n // Unfortunately, there is no way of reliably observing when a value in\r\n // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),\r\n // so we need to poll.\r\n let entry = await updateAuthTokenRequest(installations.appConfig);\r\n while (entry.authToken.requestStatus === 1 /* IN_PROGRESS */) {\r\n // generateAuthToken still in progress.\r\n await sleep(100);\r\n entry = await updateAuthTokenRequest(installations.appConfig);\r\n }\r\n const authToken = entry.authToken;\r\n if (authToken.requestStatus === 0 /* NOT_STARTED */) {\r\n // The request timed out or failed in a different call. Try again.\r\n return refreshAuthToken(installations, forceRefresh);\r\n }\r\n else {\r\n return authToken;\r\n }\r\n}\r\n/**\r\n * Called only if there is a GenerateAuthToken request in progress.\r\n *\r\n * Updates the InstallationEntry in the DB based on the status of the\r\n * GenerateAuthToken request.\r\n *\r\n * Returns the updated InstallationEntry.\r\n */\r\nfunction updateAuthTokenRequest(appConfig) {\r\n return update(appConfig, oldEntry => {\r\n if (!isEntryRegistered(oldEntry)) {\r\n throw ERROR_FACTORY.create(\"not-registered\" /* NOT_REGISTERED */);\r\n }\r\n const oldAuthToken = oldEntry.authToken;\r\n if (hasAuthTokenRequestTimedOut(oldAuthToken)) {\r\n return Object.assign(Object.assign({}, oldEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });\r\n }\r\n return oldEntry;\r\n });\r\n}\r\nasync function fetchAuthTokenFromServer(installations, installationEntry) {\r\n try {\r\n const authToken = await generateAuthTokenRequest(installations, installationEntry);\r\n const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken });\r\n await set(installations.appConfig, updatedInstallationEntry);\r\n return authToken;\r\n }\r\n catch (e) {\r\n if (isServerError(e) &&\r\n (e.customData.serverCode === 401 || e.customData.serverCode === 404)) {\r\n // Server returned a \"FID not found\" or a \"Invalid authentication\" error.\r\n // Generate a new ID next time.\r\n await remove(installations.appConfig);\r\n }\r\n else {\r\n const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });\r\n await set(installations.appConfig, updatedInstallationEntry);\r\n }\r\n throw e;\r\n }\r\n}\r\nfunction isEntryRegistered(installationEntry) {\r\n return (installationEntry !== undefined &&\r\n installationEntry.registrationStatus === 2 /* COMPLETED */);\r\n}\r\nfunction isAuthTokenValid(authToken) {\r\n return (authToken.requestStatus === 2 /* COMPLETED */ &&\r\n !isAuthTokenExpired(authToken));\r\n}\r\nfunction isAuthTokenExpired(authToken) {\r\n const now = Date.now();\r\n return (now < authToken.creationTime ||\r\n authToken.creationTime + authToken.expiresIn < now + TOKEN_EXPIRATION_BUFFER);\r\n}\r\n/** Returns an updated InstallationEntry with an InProgressAuthToken. */\r\nfunction makeAuthTokenRequestInProgressEntry(oldEntry) {\r\n const inProgressAuthToken = {\r\n requestStatus: 1 /* IN_PROGRESS */,\r\n requestTime: Date.now()\r\n };\r\n return Object.assign(Object.assign({}, oldEntry), { authToken: inProgressAuthToken });\r\n}\r\nfunction hasAuthTokenRequestTimedOut(authToken) {\r\n return (authToken.requestStatus === 1 /* IN_PROGRESS */ &&\r\n authToken.requestTime + PENDING_TIMEOUT_MS < Date.now());\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Creates a Firebase Installation if there isn't one for the app and\r\n * returns the Installation ID.\r\n * @param installations - The `Installations` instance.\r\n *\r\n * @public\r\n */\r\nasync function getId(installations) {\r\n const installationsImpl = installations;\r\n const { installationEntry, registrationPromise } = await getInstallationEntry(installationsImpl.appConfig);\r\n if (registrationPromise) {\r\n registrationPromise.catch(console.error);\r\n }\r\n else {\r\n // If the installation is already registered, update the authentication\r\n // token if needed.\r\n refreshAuthToken(installationsImpl).catch(console.error);\r\n }\r\n return installationEntry.fid;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a Firebase Installations auth token, identifying the current\r\n * Firebase Installation.\r\n * @param installations - The `Installations` instance.\r\n * @param forceRefresh - Force refresh regardless of token expiration.\r\n *\r\n * @public\r\n */\r\nasync function getToken(installations, forceRefresh = false) {\r\n const installationsImpl = installations;\r\n await completeInstallationRegistration(installationsImpl.appConfig);\r\n // At this point we either have a Registered Installation in the DB, or we've\r\n // already thrown an error.\r\n const authToken = await refreshAuthToken(installationsImpl, forceRefresh);\r\n return authToken.token;\r\n}\r\nasync function completeInstallationRegistration(appConfig) {\r\n const { registrationPromise } = await getInstallationEntry(appConfig);\r\n if (registrationPromise) {\r\n // A createInstallation request is in progress. Wait until it finishes.\r\n await registrationPromise;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function deleteInstallationRequest(appConfig, installationEntry) {\r\n const endpoint = getDeleteEndpoint(appConfig, installationEntry);\r\n const headers = getHeadersWithAuth(appConfig, installationEntry);\r\n const request = {\r\n method: 'DELETE',\r\n headers\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (!response.ok) {\r\n throw await getErrorFromResponse('Delete Installation', response);\r\n }\r\n}\r\nfunction getDeleteEndpoint(appConfig, { fid }) {\r\n return `${getInstallationsEndpoint(appConfig)}/${fid}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Deletes the Firebase Installation and all associated data.\r\n * @param installations - The `Installations` instance.\r\n *\r\n * @public\r\n */\r\nasync function deleteInstallations(installations) {\r\n const { appConfig } = installations;\r\n const entry = await update(appConfig, oldEntry => {\r\n if (oldEntry && oldEntry.registrationStatus === 0 /* NOT_STARTED */) {\r\n // Delete the unregistered entry without sending a deleteInstallation request.\r\n return undefined;\r\n }\r\n return oldEntry;\r\n });\r\n if (entry) {\r\n if (entry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n // Can't delete while trying to register.\r\n throw ERROR_FACTORY.create(\"delete-pending-registration\" /* DELETE_PENDING_REGISTRATION */);\r\n }\r\n else if (entry.registrationStatus === 2 /* COMPLETED */) {\r\n if (!navigator.onLine) {\r\n throw ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */);\r\n }\r\n else {\r\n await deleteInstallationRequest(appConfig, entry);\r\n await remove(appConfig);\r\n }\r\n }\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Sets a new callback that will get called when Installation ID changes.\r\n * Returns an unsubscribe function that will remove the callback when called.\r\n * @param installations - The `Installations` instance.\r\n * @param callback - The callback function that is invoked when FID changes.\r\n * @returns A function that can be called to unsubscribe.\r\n *\r\n * @public\r\n */\r\nfunction onIdChange(installations, callback) {\r\n const { appConfig } = installations;\r\n addCallback(appConfig, callback);\r\n return () => {\r\n removeCallback(appConfig, callback);\r\n };\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns an instance of {@link Installations} associated with the given\r\n * {@link @firebase/app#FirebaseApp} instance.\r\n * @param app - The {@link @firebase/app#FirebaseApp} instance.\r\n *\r\n * @public\r\n */\r\nfunction getInstallations(app = getApp()) {\r\n const installationsImpl = _getProvider(app, 'installations').getImmediate();\r\n return installationsImpl;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction extractAppConfig(app) {\r\n if (!app || !app.options) {\r\n throw getMissingValueError('App Configuration');\r\n }\r\n if (!app.name) {\r\n throw getMissingValueError('App Name');\r\n }\r\n // Required app config keys\r\n const configKeys = [\r\n 'projectId',\r\n 'apiKey',\r\n 'appId'\r\n ];\r\n for (const keyName of configKeys) {\r\n if (!app.options[keyName]) {\r\n throw getMissingValueError(keyName);\r\n }\r\n }\r\n return {\r\n appName: app.name,\r\n projectId: app.options.projectId,\r\n apiKey: app.options.apiKey,\r\n appId: app.options.appId\r\n };\r\n}\r\nfunction getMissingValueError(valueName) {\r\n return ERROR_FACTORY.create(\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */, {\r\n valueName\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst INSTALLATIONS_NAME = 'installations';\r\nconst INSTALLATIONS_NAME_INTERNAL = 'installations-internal';\r\nconst publicFactory = (container) => {\r\n const app = container.getProvider('app').getImmediate();\r\n // Throws if app isn't configured properly.\r\n const appConfig = extractAppConfig(app);\r\n const platformLoggerProvider = _getProvider(app, 'platform-logger');\r\n const installationsImpl = {\r\n app,\r\n appConfig,\r\n platformLoggerProvider,\r\n _delete: () => Promise.resolve()\r\n };\r\n return installationsImpl;\r\n};\r\nconst internalFactory = (container) => {\r\n const app = container.getProvider('app').getImmediate();\r\n // Internal FIS instance relies on public FIS instance.\r\n const installations = _getProvider(app, INSTALLATIONS_NAME).getImmediate();\r\n const installationsInternal = {\r\n getId: () => getId(installations),\r\n getToken: (forceRefresh) => getToken(installations, forceRefresh)\r\n };\r\n return installationsInternal;\r\n};\r\nfunction registerInstallations() {\r\n _registerComponent(new Component(INSTALLATIONS_NAME, publicFactory, \"PUBLIC\" /* PUBLIC */));\r\n _registerComponent(new Component(INSTALLATIONS_NAME_INTERNAL, internalFactory, \"PRIVATE\" /* PRIVATE */));\r\n}\n\n/**\r\n * Firebase Installations\r\n *\r\n * @packageDocumentation\r\n */\r\nregisterInstallations();\r\nregisterVersion(name, version);\r\n// BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\nregisterVersion(name, version, 'esm2017');\n\nexport { deleteInstallations, getId, getInstallations, getToken, onIdChange };\n//# sourceMappingURL=index.esm2017.js.map\n","import '@firebase/installations';\nimport { Component } from '@firebase/component';\nimport { openDb, deleteDb } from 'idb';\nimport { ErrorFactory, validateIndexedDBOpenable, isIndexedDBAvailable, areCookiesEnabled, getModularInstance } from '@firebase/util';\nimport { _registerComponent, registerVersion, getApp, _getProvider } from '@firebase/app';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DEFAULT_SW_PATH = '/firebase-messaging-sw.js';\r\nconst DEFAULT_SW_SCOPE = '/firebase-cloud-messaging-push-scope';\r\nconst DEFAULT_VAPID_KEY = 'BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoMj8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4';\r\nconst ENDPOINT = 'https://fcmregistrations.googleapis.com/v1';\r\nconst CONSOLE_CAMPAIGN_ID = 'google.c.a.c_id';\r\nconst CONSOLE_CAMPAIGN_NAME = 'google.c.a.c_l';\r\nconst CONSOLE_CAMPAIGN_TIME = 'google.c.a.ts';\r\n/** Set to '1' if Analytics is enabled for the campaign */\r\nconst CONSOLE_CAMPAIGN_ANALYTICS_ENABLED = 'google.c.a.e';\r\nvar MessageType$1;\r\n(function (MessageType) {\r\n MessageType[MessageType[\"DATA_MESSAGE\"] = 1] = \"DATA_MESSAGE\";\r\n MessageType[MessageType[\"DISPLAY_NOTIFICATION\"] = 3] = \"DISPLAY_NOTIFICATION\";\r\n})(MessageType$1 || (MessageType$1 = {}));\n\n/**\r\n * @license\r\n * Copyright 2018 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\r\n * in compliance with the License. You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software distributed under the License\r\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r\n * or implied. See the License for the specific language governing permissions and limitations under\r\n * the License.\r\n */\r\nvar MessageType;\r\n(function (MessageType) {\r\n MessageType[\"PUSH_RECEIVED\"] = \"push-received\";\r\n MessageType[\"NOTIFICATION_CLICKED\"] = \"notification-clicked\";\r\n})(MessageType || (MessageType = {}));\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction arrayToBase64(array) {\r\n const uint8Array = new Uint8Array(array);\r\n const base64String = btoa(String.fromCharCode(...uint8Array));\r\n return base64String.replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\nfunction base64ToArray(base64String) {\r\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\r\n const base64 = (base64String + padding)\r\n .replace(/\\-/g, '+')\r\n .replace(/_/g, '/');\r\n const rawData = atob(base64);\r\n const outputArray = new Uint8Array(rawData.length);\r\n for (let i = 0; i < rawData.length; ++i) {\r\n outputArray[i] = rawData.charCodeAt(i);\r\n }\r\n return outputArray;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst OLD_DB_NAME = 'fcm_token_details_db';\r\n/**\r\n * The last DB version of 'fcm_token_details_db' was 4. This is one higher, so that the upgrade\r\n * callback is called for all versions of the old DB.\r\n */\r\nconst OLD_DB_VERSION = 5;\r\nconst OLD_OBJECT_STORE_NAME = 'fcm_token_object_Store';\r\nasync function migrateOldDatabase(senderId) {\r\n if ('databases' in indexedDB) {\r\n // indexedDb.databases() is an IndexedDB v3 API and does not exist in all browsers. TODO: Remove\r\n // typecast when it lands in TS types.\r\n const databases = await indexedDB.databases();\r\n const dbNames = databases.map(db => db.name);\r\n if (!dbNames.includes(OLD_DB_NAME)) {\r\n // old DB didn't exist, no need to open.\r\n return null;\r\n }\r\n }\r\n let tokenDetails = null;\r\n const db = await openDb(OLD_DB_NAME, OLD_DB_VERSION, async (db) => {\r\n var _a;\r\n if (db.oldVersion < 2) {\r\n // Database too old, skip migration.\r\n return;\r\n }\r\n if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {\r\n // Database did not exist. Nothing to do.\r\n return;\r\n }\r\n const objectStore = db.transaction.objectStore(OLD_OBJECT_STORE_NAME);\r\n const value = await objectStore.index('fcmSenderId').get(senderId);\r\n await objectStore.clear();\r\n if (!value) {\r\n // No entry in the database, nothing to migrate.\r\n return;\r\n }\r\n if (db.oldVersion === 2) {\r\n const oldDetails = value;\r\n if (!oldDetails.auth || !oldDetails.p256dh || !oldDetails.endpoint) {\r\n return;\r\n }\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: (_a = oldDetails.createTime) !== null && _a !== void 0 ? _a : Date.now(),\r\n subscriptionOptions: {\r\n auth: oldDetails.auth,\r\n p256dh: oldDetails.p256dh,\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: typeof oldDetails.vapidKey === 'string'\r\n ? oldDetails.vapidKey\r\n : arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n else if (db.oldVersion === 3) {\r\n const oldDetails = value;\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: oldDetails.createTime,\r\n subscriptionOptions: {\r\n auth: arrayToBase64(oldDetails.auth),\r\n p256dh: arrayToBase64(oldDetails.p256dh),\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n else if (db.oldVersion === 4) {\r\n const oldDetails = value;\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: oldDetails.createTime,\r\n subscriptionOptions: {\r\n auth: arrayToBase64(oldDetails.auth),\r\n p256dh: arrayToBase64(oldDetails.p256dh),\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n });\r\n db.close();\r\n // Delete all old databases.\r\n await deleteDb(OLD_DB_NAME);\r\n await deleteDb('fcm_vapid_details_db');\r\n await deleteDb('undefined');\r\n return checkTokenDetails(tokenDetails) ? tokenDetails : null;\r\n}\r\nfunction checkTokenDetails(tokenDetails) {\r\n if (!tokenDetails || !tokenDetails.subscriptionOptions) {\r\n return false;\r\n }\r\n const { subscriptionOptions } = tokenDetails;\r\n return (typeof tokenDetails.createTime === 'number' &&\r\n tokenDetails.createTime > 0 &&\r\n typeof tokenDetails.token === 'string' &&\r\n tokenDetails.token.length > 0 &&\r\n typeof subscriptionOptions.auth === 'string' &&\r\n subscriptionOptions.auth.length > 0 &&\r\n typeof subscriptionOptions.p256dh === 'string' &&\r\n subscriptionOptions.p256dh.length > 0 &&\r\n typeof subscriptionOptions.endpoint === 'string' &&\r\n subscriptionOptions.endpoint.length > 0 &&\r\n typeof subscriptionOptions.swScope === 'string' &&\r\n subscriptionOptions.swScope.length > 0 &&\r\n typeof subscriptionOptions.vapidKey === 'string' &&\r\n subscriptionOptions.vapidKey.length > 0);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// Exported for tests.\r\nconst DATABASE_NAME = 'firebase-messaging-database';\r\nconst DATABASE_VERSION = 1;\r\nconst OBJECT_STORE_NAME = 'firebase-messaging-store';\r\nlet dbPromise = null;\r\nfunction getDbPromise() {\r\n if (!dbPromise) {\r\n dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, upgradeDb => {\r\n // We don't use 'break' in this switch statement, the fall-through behavior is what we want,\r\n // because if there are multiple versions between the old version and the current version, we\r\n // want ALL the migrations that correspond to those versions to run, not only the last one.\r\n // eslint-disable-next-line default-case\r\n switch (upgradeDb.oldVersion) {\r\n case 0:\r\n upgradeDb.createObjectStore(OBJECT_STORE_NAME);\r\n }\r\n });\r\n }\r\n return dbPromise;\r\n}\r\n/** Gets record(s) from the objectStore that match the given key. */\r\nasync function dbGet(firebaseDependencies) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tokenDetails = await db\r\n .transaction(OBJECT_STORE_NAME)\r\n .objectStore(OBJECT_STORE_NAME)\r\n .get(key);\r\n if (tokenDetails) {\r\n return tokenDetails;\r\n }\r\n else {\r\n // Check if there is a tokenDetails object in the old DB.\r\n const oldTokenDetails = await migrateOldDatabase(firebaseDependencies.appConfig.senderId);\r\n if (oldTokenDetails) {\r\n await dbSet(firebaseDependencies, oldTokenDetails);\r\n return oldTokenDetails;\r\n }\r\n }\r\n}\r\n/** Assigns or overwrites the record for the given key with the given value. */\r\nasync function dbSet(firebaseDependencies, tokenDetails) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).put(tokenDetails, key);\r\n await tx.complete;\r\n return tokenDetails;\r\n}\r\n/** Removes record(s) from the objectStore that match the given key. */\r\nasync function dbRemove(firebaseDependencies) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).delete(key);\r\n await tx.complete;\r\n}\r\nfunction getKey({ appConfig }) {\r\n return appConfig.appId;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERROR_MAP = {\r\n [\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: \"{$valueName}\"',\r\n [\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */]: 'This method is available in a Window context.',\r\n [\"only-available-in-sw\" /* AVAILABLE_IN_SW */]: 'This method is available in a service worker context.',\r\n [\"permission-default\" /* PERMISSION_DEFAULT */]: 'The notification permission was not granted and dismissed instead.',\r\n [\"permission-blocked\" /* PERMISSION_BLOCKED */]: 'The notification permission was not granted and blocked instead.',\r\n [\"unsupported-browser\" /* UNSUPPORTED_BROWSER */]: \"This browser doesn't support the API's required to use the Firebase SDK.\",\r\n [\"indexed-db-unsupported\" /* INDEXED_DB_UNSUPPORTED */]: \"This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)\",\r\n [\"failed-service-worker-registration\" /* FAILED_DEFAULT_REGISTRATION */]: 'We are unable to register the default service worker. {$browserErrorMessage}',\r\n [\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */]: 'A problem occurred while subscribing the user to FCM: {$errorInfo}',\r\n [\"token-subscribe-no-token\" /* TOKEN_SUBSCRIBE_NO_TOKEN */]: 'FCM returned no token when subscribing the user to push.',\r\n [\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */]: 'A problem occurred while unsubscribing the ' +\r\n 'user from FCM: {$errorInfo}',\r\n [\"token-update-failed\" /* TOKEN_UPDATE_FAILED */]: 'A problem occurred while updating the user from FCM: {$errorInfo}',\r\n [\"token-update-no-token\" /* TOKEN_UPDATE_NO_TOKEN */]: 'FCM returned no token when updating the user to push.',\r\n [\"use-sw-after-get-token\" /* USE_SW_AFTER_GET_TOKEN */]: 'The useServiceWorker() method may only be called once and must be ' +\r\n 'called before calling getToken() to ensure your service worker is used.',\r\n [\"invalid-sw-registration\" /* INVALID_SW_REGISTRATION */]: 'The input to useServiceWorker() must be a ServiceWorkerRegistration.',\r\n [\"invalid-bg-handler\" /* INVALID_BG_HANDLER */]: 'The input to setBackgroundMessageHandler() must be a function.',\r\n [\"invalid-vapid-key\" /* INVALID_VAPID_KEY */]: 'The public VAPID key must be a string.',\r\n [\"use-vapid-key-after-get-token\" /* USE_VAPID_KEY_AFTER_GET_TOKEN */]: 'The usePublicVapidKey() method may only be called once and must be ' +\r\n 'called before calling getToken() to ensure your VAPID key is used.'\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP);\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function requestGetToken(firebaseDependencies, subscriptionOptions) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const body = getBody(subscriptionOptions);\r\n const subscribeOptions = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n let responseData;\r\n try {\r\n const response = await fetch(getEndpoint(firebaseDependencies.appConfig), subscribeOptions);\r\n responseData = await response.json();\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n if (!responseData.token) {\r\n throw ERROR_FACTORY.create(\"token-subscribe-no-token\" /* TOKEN_SUBSCRIBE_NO_TOKEN */);\r\n }\r\n return responseData.token;\r\n}\r\nasync function requestUpdateToken(firebaseDependencies, tokenDetails) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const body = getBody(tokenDetails.subscriptionOptions);\r\n const updateOptions = {\r\n method: 'PATCH',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n let responseData;\r\n try {\r\n const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${tokenDetails.token}`, updateOptions);\r\n responseData = await response.json();\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-update-failed\" /* TOKEN_UPDATE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-update-failed\" /* TOKEN_UPDATE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n if (!responseData.token) {\r\n throw ERROR_FACTORY.create(\"token-update-no-token\" /* TOKEN_UPDATE_NO_TOKEN */);\r\n }\r\n return responseData.token;\r\n}\r\nasync function requestDeleteToken(firebaseDependencies, token) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const unsubscribeOptions = {\r\n method: 'DELETE',\r\n headers\r\n };\r\n try {\r\n const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${token}`, unsubscribeOptions);\r\n const responseData = await response.json();\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n}\r\nfunction getEndpoint({ projectId }) {\r\n return `${ENDPOINT}/projects/${projectId}/registrations`;\r\n}\r\nasync function getHeaders({ appConfig, installations }) {\r\n const authToken = await installations.getToken();\r\n return new Headers({\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'x-goog-api-key': appConfig.apiKey,\r\n 'x-goog-firebase-installations-auth': `FIS ${authToken}`\r\n });\r\n}\r\nfunction getBody({ p256dh, auth, endpoint, vapidKey }) {\r\n const body = {\r\n web: {\r\n endpoint,\r\n auth,\r\n p256dh\r\n }\r\n };\r\n if (vapidKey !== DEFAULT_VAPID_KEY) {\r\n body.web.applicationPubKey = vapidKey;\r\n }\r\n return body;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// UpdateRegistration will be called once every week.\r\nconst TOKEN_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\r\nasync function getTokenInternal(messaging) {\r\n const pushSubscription = await getPushSubscription(messaging.swRegistration, messaging.vapidKey);\r\n const subscriptionOptions = {\r\n vapidKey: messaging.vapidKey,\r\n swScope: messaging.swRegistration.scope,\r\n endpoint: pushSubscription.endpoint,\r\n auth: arrayToBase64(pushSubscription.getKey('auth')),\r\n p256dh: arrayToBase64(pushSubscription.getKey('p256dh'))\r\n };\r\n const tokenDetails = await dbGet(messaging.firebaseDependencies);\r\n if (!tokenDetails) {\r\n // No token, get a new one.\r\n return getNewToken(messaging.firebaseDependencies, subscriptionOptions);\r\n }\r\n else if (!isTokenValid(tokenDetails.subscriptionOptions, subscriptionOptions)) {\r\n // Invalid token, get a new one.\r\n try {\r\n await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);\r\n }\r\n catch (e) {\r\n // Suppress errors because of #2364\r\n console.warn(e);\r\n }\r\n return getNewToken(messaging.firebaseDependencies, subscriptionOptions);\r\n }\r\n else if (Date.now() >= tokenDetails.createTime + TOKEN_EXPIRATION_MS) {\r\n // Weekly token refresh\r\n return updateToken(messaging, {\r\n token: tokenDetails.token,\r\n createTime: Date.now(),\r\n subscriptionOptions\r\n });\r\n }\r\n else {\r\n // Valid token, nothing to do.\r\n return tokenDetails.token;\r\n }\r\n}\r\n/**\r\n * This method deletes the token from the database, unsubscribes the token from FCM, and unregisters\r\n * the push subscription if it exists.\r\n */\r\nasync function deleteTokenInternal(messaging) {\r\n const tokenDetails = await dbGet(messaging.firebaseDependencies);\r\n if (tokenDetails) {\r\n await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);\r\n await dbRemove(messaging.firebaseDependencies);\r\n }\r\n // Unsubscribe from the push subscription.\r\n const pushSubscription = await messaging.swRegistration.pushManager.getSubscription();\r\n if (pushSubscription) {\r\n return pushSubscription.unsubscribe();\r\n }\r\n // If there's no SW, consider it a success.\r\n return true;\r\n}\r\nasync function updateToken(messaging, tokenDetails) {\r\n try {\r\n const updatedToken = await requestUpdateToken(messaging.firebaseDependencies, tokenDetails);\r\n const updatedTokenDetails = Object.assign(Object.assign({}, tokenDetails), { token: updatedToken, createTime: Date.now() });\r\n await dbSet(messaging.firebaseDependencies, updatedTokenDetails);\r\n return updatedToken;\r\n }\r\n catch (e) {\r\n await deleteTokenInternal(messaging);\r\n throw e;\r\n }\r\n}\r\nasync function getNewToken(firebaseDependencies, subscriptionOptions) {\r\n const token = await requestGetToken(firebaseDependencies, subscriptionOptions);\r\n const tokenDetails = {\r\n token,\r\n createTime: Date.now(),\r\n subscriptionOptions\r\n };\r\n await dbSet(firebaseDependencies, tokenDetails);\r\n return tokenDetails.token;\r\n}\r\n/**\r\n * Gets a PushSubscription for the current user.\r\n */\r\nasync function getPushSubscription(swRegistration, vapidKey) {\r\n const subscription = await swRegistration.pushManager.getSubscription();\r\n if (subscription) {\r\n return subscription;\r\n }\r\n return swRegistration.pushManager.subscribe({\r\n userVisibleOnly: true,\r\n // Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key\r\n // submitted to pushManager#subscribe must be of type Uint8Array.\r\n applicationServerKey: base64ToArray(vapidKey)\r\n });\r\n}\r\n/**\r\n * Checks if the saved tokenDetails object matches the configuration provided.\r\n */\r\nfunction isTokenValid(dbOptions, currentOptions) {\r\n const isVapidKeyEqual = currentOptions.vapidKey === dbOptions.vapidKey;\r\n const isEndpointEqual = currentOptions.endpoint === dbOptions.endpoint;\r\n const isAuthEqual = currentOptions.auth === dbOptions.auth;\r\n const isP256dhEqual = currentOptions.p256dh === dbOptions.p256dh;\r\n return isVapidKeyEqual && isEndpointEqual && isAuthEqual && isP256dhEqual;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction externalizePayload(internalPayload) {\r\n const payload = {\r\n from: internalPayload.from,\r\n // eslint-disable-next-line camelcase\r\n collapseKey: internalPayload.collapse_key,\r\n // eslint-disable-next-line camelcase\r\n messageId: internalPayload.fcmMessageId\r\n };\r\n propagateNotificationPayload(payload, internalPayload);\r\n propagateDataPayload(payload, internalPayload);\r\n propagateFcmOptions(payload, internalPayload);\r\n return payload;\r\n}\r\nfunction propagateNotificationPayload(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.notification) {\r\n return;\r\n }\r\n payload.notification = {};\r\n const title = messagePayloadInternal.notification.title;\r\n if (!!title) {\r\n payload.notification.title = title;\r\n }\r\n const body = messagePayloadInternal.notification.body;\r\n if (!!body) {\r\n payload.notification.body = body;\r\n }\r\n const image = messagePayloadInternal.notification.image;\r\n if (!!image) {\r\n payload.notification.image = image;\r\n }\r\n}\r\nfunction propagateDataPayload(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.data) {\r\n return;\r\n }\r\n payload.data = messagePayloadInternal.data;\r\n}\r\nfunction propagateFcmOptions(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.fcmOptions) {\r\n return;\r\n }\r\n payload.fcmOptions = {};\r\n const link = messagePayloadInternal.fcmOptions.link;\r\n if (!!link) {\r\n payload.fcmOptions.link = link;\r\n }\r\n // eslint-disable-next-line camelcase\r\n const analyticsLabel = messagePayloadInternal.fcmOptions.analytics_label;\r\n if (!!analyticsLabel) {\r\n payload.fcmOptions.analyticsLabel = analyticsLabel;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction isConsoleMessage(data) {\r\n // This message has a campaign ID, meaning it was sent using the Firebase Console.\r\n return typeof data === 'object' && !!data && CONSOLE_CAMPAIGN_ID in data;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n_mergeStrings('hts/frbslgigp.ogepscmv/ieo/eaylg', 'tp:/ieaeogn-agolai.o/1frlglgc/o');\r\n_mergeStrings('AzSCbw63g1R0nCw85jG8', 'Iaya3yLKwmgvh7cF0q4');\r\nfunction _mergeStrings(s1, s2) {\r\n const resultArray = [];\r\n for (let i = 0; i < s1.length; i++) {\r\n resultArray.push(s1.charAt(i));\r\n if (i < s2.length) {\r\n resultArray.push(s2.charAt(i));\r\n }\r\n }\r\n return resultArray.join('');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction extractAppConfig(app) {\r\n if (!app || !app.options) {\r\n throw getMissingValueError('App Configuration Object');\r\n }\r\n if (!app.name) {\r\n throw getMissingValueError('App Name');\r\n }\r\n // Required app config keys\r\n const configKeys = [\r\n 'projectId',\r\n 'apiKey',\r\n 'appId',\r\n 'messagingSenderId'\r\n ];\r\n const { options } = app;\r\n for (const keyName of configKeys) {\r\n if (!options[keyName]) {\r\n throw getMissingValueError(keyName);\r\n }\r\n }\r\n return {\r\n appName: app.name,\r\n projectId: options.projectId,\r\n apiKey: options.apiKey,\r\n appId: options.appId,\r\n senderId: options.messagingSenderId\r\n };\r\n}\r\nfunction getMissingValueError(valueName) {\r\n return ERROR_FACTORY.create(\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */, {\r\n valueName\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass MessagingService {\r\n constructor(app, installations, analyticsProvider) {\r\n // logging is only done with end user consent. Default to false.\r\n this.deliveryMetricsExportedToBigQueryEnabled = false;\r\n this.onBackgroundMessageHandler = null;\r\n this.onMessageHandler = null;\r\n this.logEvents = [];\r\n this.isLogServiceStarted = false;\r\n const appConfig = extractAppConfig(app);\r\n this.firebaseDependencies = {\r\n app,\r\n appConfig,\r\n installations,\r\n analyticsProvider\r\n };\r\n }\r\n _delete() {\r\n return Promise.resolve();\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function registerDefaultSw(messaging) {\r\n try {\r\n messaging.swRegistration = await navigator.serviceWorker.register(DEFAULT_SW_PATH, {\r\n scope: DEFAULT_SW_SCOPE\r\n });\r\n // The timing when browser updates sw when sw has an update is unreliable from experiment. It\r\n // leads to version conflict when the SDK upgrades to a newer version in the main page, but sw\r\n // is stuck with the old version. For example,\r\n // https://github.com/firebase/firebase-js-sdk/issues/2590 The following line reliably updates\r\n // sw if there was an update.\r\n messaging.swRegistration.update().catch(() => {\r\n /* it is non blocking and we don't care if it failed */\r\n });\r\n }\r\n catch (e) {\r\n throw ERROR_FACTORY.create(\"failed-service-worker-registration\" /* FAILED_DEFAULT_REGISTRATION */, {\r\n browserErrorMessage: e.message\r\n });\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function updateSwReg(messaging, swRegistration) {\r\n if (!swRegistration && !messaging.swRegistration) {\r\n await registerDefaultSw(messaging);\r\n }\r\n if (!swRegistration && !!messaging.swRegistration) {\r\n return;\r\n }\r\n if (!(swRegistration instanceof ServiceWorkerRegistration)) {\r\n throw ERROR_FACTORY.create(\"invalid-sw-registration\" /* INVALID_SW_REGISTRATION */);\r\n }\r\n messaging.swRegistration = swRegistration;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function updateVapidKey(messaging, vapidKey) {\r\n if (!!vapidKey) {\r\n messaging.vapidKey = vapidKey;\r\n }\r\n else if (!messaging.vapidKey) {\r\n messaging.vapidKey = DEFAULT_VAPID_KEY;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function getToken$1(messaging, options) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n if (Notification.permission === 'default') {\r\n await Notification.requestPermission();\r\n }\r\n if (Notification.permission !== 'granted') {\r\n throw ERROR_FACTORY.create(\"permission-blocked\" /* PERMISSION_BLOCKED */);\r\n }\r\n await updateVapidKey(messaging, options === null || options === void 0 ? void 0 : options.vapidKey);\r\n await updateSwReg(messaging, options === null || options === void 0 ? void 0 : options.serviceWorkerRegistration);\r\n return getTokenInternal(messaging);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function logToScion(messaging, messageType, data) {\r\n const eventType = getEventType(messageType);\r\n const analytics = await messaging.firebaseDependencies.analyticsProvider.get();\r\n analytics.logEvent(eventType, {\r\n /* eslint-disable camelcase */\r\n message_id: data[CONSOLE_CAMPAIGN_ID],\r\n message_name: data[CONSOLE_CAMPAIGN_NAME],\r\n message_time: data[CONSOLE_CAMPAIGN_TIME],\r\n message_device_time: Math.floor(Date.now() / 1000)\r\n /* eslint-enable camelcase */\r\n });\r\n}\r\nfunction getEventType(messageType) {\r\n switch (messageType) {\r\n case MessageType.NOTIFICATION_CLICKED:\r\n return 'notification_open';\r\n case MessageType.PUSH_RECEIVED:\r\n return 'notification_foreground';\r\n default:\r\n throw new Error();\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function messageEventListener(messaging, event) {\r\n const internalPayload = event.data;\r\n if (!internalPayload.isFirebaseMessaging) {\r\n return;\r\n }\r\n if (messaging.onMessageHandler &&\r\n internalPayload.messageType === MessageType.PUSH_RECEIVED) {\r\n if (typeof messaging.onMessageHandler === 'function') {\r\n messaging.onMessageHandler(externalizePayload(internalPayload));\r\n }\r\n else {\r\n messaging.onMessageHandler.next(externalizePayload(internalPayload));\r\n }\r\n }\r\n // Log to Scion if applicable\r\n const dataPayload = internalPayload.data;\r\n if (isConsoleMessage(dataPayload) &&\r\n dataPayload[CONSOLE_CAMPAIGN_ANALYTICS_ENABLED] === '1') {\r\n await logToScion(messaging, internalPayload.messageType, dataPayload);\r\n }\r\n}\n\nconst name = \"@firebase/messaging\";\nconst version = \"0.9.8\";\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst WindowMessagingFactory = (container) => {\r\n const messaging = new MessagingService(container.getProvider('app').getImmediate(), container.getProvider('installations-internal').getImmediate(), container.getProvider('analytics-internal'));\r\n navigator.serviceWorker.addEventListener('message', e => messageEventListener(messaging, e));\r\n return messaging;\r\n};\r\nconst WindowMessagingInternalFactory = (container) => {\r\n const messaging = container\r\n .getProvider('messaging')\r\n .getImmediate();\r\n const messagingInternal = {\r\n getToken: (options) => getToken$1(messaging, options)\r\n };\r\n return messagingInternal;\r\n};\r\nfunction registerMessagingInWindow() {\r\n _registerComponent(new Component('messaging', WindowMessagingFactory, \"PUBLIC\" /* PUBLIC */));\r\n _registerComponent(new Component('messaging-internal', WindowMessagingInternalFactory, \"PRIVATE\" /* PRIVATE */));\r\n registerVersion(name, version);\r\n // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\n registerVersion(name, version, 'esm2017');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Checks if all required APIs exist in the browser.\r\n * @returns a Promise that resolves to a boolean.\r\n *\r\n * @public\r\n */\r\nasync function isWindowSupported() {\r\n try {\r\n // This throws if open() is unsupported, so adding it to the conditional\r\n // statement below can cause an uncaught error.\r\n await validateIndexedDBOpenable();\r\n }\r\n catch (e) {\r\n return false;\r\n }\r\n // firebase-js-sdk/issues/2393 reveals that idb#open in Safari iframe and Firefox private browsing\r\n // might be prohibited to run. In these contexts, an error would be thrown during the messaging\r\n // instantiating phase, informing the developers to import/call isSupported for special handling.\r\n return (typeof window !== 'undefined' &&\r\n isIndexedDBAvailable() &&\r\n areCookiesEnabled() &&\r\n 'serviceWorker' in navigator &&\r\n 'PushManager' in window &&\r\n 'Notification' in window &&\r\n 'fetch' in window &&\r\n ServiceWorkerRegistration.prototype.hasOwnProperty('showNotification') &&\r\n PushSubscription.prototype.hasOwnProperty('getKey'));\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function deleteToken$1(messaging) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n if (!messaging.swRegistration) {\r\n await registerDefaultSw(messaging);\r\n }\r\n return deleteTokenInternal(messaging);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction onMessage$1(messaging, nextOrObserver) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n messaging.onMessageHandler = nextOrObserver;\r\n return () => {\r\n messaging.onMessageHandler = null;\r\n };\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Retrieves a Firebase Cloud Messaging instance.\r\n *\r\n * @returns The Firebase Cloud Messaging instance associated with the provided firebase app.\r\n *\r\n * @public\r\n */\r\nfunction getMessagingInWindow(app = getApp()) {\r\n // Conscious decision to make this async check non-blocking during the messaging instance\r\n // initialization phase for performance consideration. An error would be thrown latter for\r\n // developer's information. Developers can then choose to import and call `isSupported` for\r\n // special handling.\r\n isWindowSupported().then(isSupported => {\r\n // If `isWindowSupported()` resolved, but returned false.\r\n if (!isSupported) {\r\n throw ERROR_FACTORY.create(\"unsupported-browser\" /* UNSUPPORTED_BROWSER */);\r\n }\r\n }, _ => {\r\n // If `isWindowSupported()` rejected.\r\n throw ERROR_FACTORY.create(\"indexed-db-unsupported\" /* INDEXED_DB_UNSUPPORTED */);\r\n });\r\n return _getProvider(getModularInstance(app), 'messaging').getImmediate();\r\n}\r\n/**\r\n * Subscribes the {@link Messaging} instance to push notifications. Returns an Firebase Cloud\r\n * Messaging registration token that can be used to send push messages to that {@link Messaging}\r\n * instance.\r\n *\r\n * If a notification permission isn't already granted, this method asks the user for permission. The\r\n * returned promise rejects if the user does not allow the app to show notifications.\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n * @param options - Provides an optional vapid key and an optinoal service worker registration\r\n *\r\n * @returns The promise resolves with an FCM registration token.\r\n *\r\n * @public\r\n */\r\nasync function getToken(messaging, options) {\r\n messaging = getModularInstance(messaging);\r\n return getToken$1(messaging, options);\r\n}\r\n/**\r\n * Deletes the registration token associated with this {@link Messaging} instance and unsubscribes\r\n * the {@link Messaging} instance from the push subscription.\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n *\r\n * @returns The promise resolves when the token has been successfully deleted.\r\n *\r\n * @public\r\n */\r\nfunction deleteToken(messaging) {\r\n messaging = getModularInstance(messaging);\r\n return deleteToken$1(messaging);\r\n}\r\n/**\r\n * When a push message is received and the user is currently on a page for your origin, the\r\n * message is passed to the page and an `onMessage()` event is dispatched with the payload of\r\n * the push message.\r\n *\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n * @param nextOrObserver - This function, or observer object with `next` defined,\r\n * is called when a message is received and the user is currently viewing your page.\r\n * @returns To stop listening for messages execute this returned function.\r\n *\r\n * @public\r\n */\r\nfunction onMessage(messaging, nextOrObserver) {\r\n messaging = getModularInstance(messaging);\r\n return onMessage$1(messaging, nextOrObserver);\r\n}\n\n/**\r\n * Firebase Cloud Messaging\r\n *\r\n * @packageDocumentation\r\n */\r\nregisterMessagingInWindow();\n\nexport { deleteToken, getMessagingInWindow as getMessaging, getToken, isWindowSupported as isSupported, onMessage };\n//# sourceMappingURL=index.esm2017.js.map\n","const __WEBPACK_NAMESPACE_OBJECT__ = Tinode;","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Alert extends React.PureComponent {\n render() {\n return this.props.visible ?\n
\n
\n
{this.props.title}
\n
{this.props.content}
\n
\n {this.props.onReject ?\n \n :\n null\n }\n \n
\n
\n
\n :\n null;\n }\n};\n","// This is a generated file. Don't edit.\n\nexport const PACKAGE_VERSION = \"0.19.0-alpha1\";\n","import { PACKAGE_VERSION } from './version.js';\n\n// Name of this application, used in the User-Agent.\nexport const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.17');\n\n// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own\nexport const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';\n\n// The array of possible hosts to connect to.\nexport const KNOWN_HOSTS = {hosted: 'web.tinode.co', local: 'localhost:6060'};\n\n// Default host name and port to connect to.\nexport const DEFAULT_HOST = KNOWN_HOSTS.hosted;\n\n// Enable console logging.\nexport const LOGGING_ENABLED = true;\n\n// Minimum time between two keypress notifications, milliseconds.\nexport const KEYPRESS_DELAY = 3*1000;\n// Delay before sending a {note} for reciving a message, milliseconds.\nexport const RECEIVED_DELAY = 500;\n// Delay before sending a read notification, milliseconds.\nexport const READ_DELAY = 1000;\n\n// The default shortest allowed tag length. Matches the value on the server.\nexport const MIN_TAG_LENGTH = 2;\n// The default greatest allowed tag length. Matches the value on the server.\nexport const MAX_TAG_LENGTH = 96;\n// The default maximum number of tags allowed. Matches the value on the server.\nexport const MAX_TAG_COUNT = 16;\n\n// Access mode for P2P subscriptions initiated by the current user.\nexport const DEFAULT_P2P_ACCESS_MODE = 'JRWPS';\n// Access mode for new group topics created by the current user.\nexport const NEW_GRP_ACCESS_MODE = 'JRWPSAO';\n// Access mode for a channel.\nexport const CHANNEL_ACCESS_MODE = 'JR';\n\n// Access mode for no access.\nexport const NO_ACCESS_MODE = 'N';\n\n// Mediaquery breakpoint between desktop and mobile, in px. Should match the value\n// in @media (max-size: 640px) in base.css\nexport const MEDIA_BREAKPOINT = 640;\n// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px.\nexport const REM_SIZE = 13;\n\n// Size of the avatar image: when image dimensions are greater or the image is not square, it's resized to\n// a square of this size or less.\nexport const AVATAR_SIZE = 384;\n\n// Maximum size of an avatar in bytes for sending in-band. Bigger avatars will be sent out of band (as uploads).\nexport const MAX_AVATAR_BYTES = 4096;\n\n// Size of the broken_image shown in MessagesView\nexport const BROKEN_IMAGE_SIZE = 32;\n\n// Number of chat messages to fetch in one call.\nexport const MESSAGES_PAGE = 24;\n\n// Default maximum in-band (included directly into the message) attachment size which fits into\n// a message of 256K in size. Used when the server-provided value is unavailable. The actual\n// binary size of the attachment should be smaller due to base64 encoding expansion and some overhead,\n// for instance 1024 bytes.\n// Increase this limit to a greater value in production, if desired. Also increase\n// max_message_size in the server config.\nexport const MAX_INBAND_ATTACHMENT_SIZE = 262144;\n\n// Default absolute maximum attachment size to be used with the server = 8MB.\n// Used when the server-provided value is unavailable. Increase to something like 100MB in production.\nexport const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23;\n\n// Maximum allowed linear dimension (pixels) of an image sent inline. Larger images will be shrunk\n// to make the larger dimension fit under this size. You may want to adjust it to 1600 or\n// 2400 for production.\nexport const MAX_IMAGE_DIM = 1024;\n\n// Linear dimensions of image preview: shrink image under this size for previews.\nexport const IMAGE_PREVIEW_DIM = 64;\n\n// Linear dimensions of image thumbnail: shrink image under this size for thumbnails in reply quote previews.\nexport const IMAGE_THUMBNAIL_DIM = 36;\n\n// Maximum number of online users to be shown in a topic title bar. Others will be\n// hidden under \"+X more\"\nexport const MAX_ONLINE_IN_TOPIC = 4;\n\n// Maximum length of user name, topic title, and private comment.\nexport const MAX_TITLE_LENGTH = 60;\n// Maximum length of topic description.\nexport const MAX_TOPIC_DESCRIPTION_LENGTH = 360;\n\n// Length of message previews in chat list, in characters.\nexport const MESSAGE_PREVIEW_LENGTH = 80;\n\n// Length of a quote in quoted reply (for outgoing messages).\nexport const QUOTED_REPLY_LENGTH = 30;\n// Length of a preview of a forwarded message.\nexport const FORWARDED_PREVIEW_LENGTH = 84;\n\n// Link for \"Contact Us\".\nexport const LINK_CONTACT_US = 'email:support@tinode.co';\n\n// Link to Privacy Policy.\nexport const LINK_PRIVACY_POLICY = 'https://tinode.co/privacy.html';\n\n// Link to Terms of Service.\nexport const LINK_TERMS_OF_SERVICE = 'https://tinode.co/terms.html';\n","// Context Menu: popup/dropdown menu.\nimport React from 'react';\nimport { injectIntl, defineMessages } from 'react-intl';\n\nimport { REM_SIZE } from '../config.js';\n\nconst messages = defineMessages({\n info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n },\n clear_messages: {\n id: 'menu_item_clear_messages',\n defaultMessage: 'Clear messages',\n description: 'Delete all messages'\n },\n clear_for_all: {\n id: 'menu_item_clear_messages_for_all',\n defaultMessage: 'Clear for All',\n description: 'Delete all message(s) for all members'\n },\n delete: {\n id: 'menu_item_delete',\n defaultMessage: 'Delete',\n description: 'Delete selected messages'\n },\n delete_for_all: {\n id: 'menu_item_delete_for_all',\n defaultMessage: 'Delete for All',\n description: 'Delete selected message(s) for all members'\n },\n send_retry: {\n id: 'menu_item_send_retry',\n defaultMessage: 'Retry',\n description: 'Retry sending message'\n },\n mute: {\n id: 'menu_item_mute',\n defaultMessage: 'Mute',\n description: 'Turn off notifications'\n },\n unmute: {\n id: 'menu_item_unmute',\n defaultMessage: 'Unmute',\n description: 'Turn notifications on'\n },\n reply: {\n id: 'menu_item_reply',\n defaultMessage: 'Reply',\n description: 'Reply to message'\n },\n forward: {\n id: 'menu_item_forward',\n defaultMessage: 'Forward',\n description: 'Forward message'\n },\n topic_delete: {\n id: 'menu_item_delete_topic',\n defaultMessage: 'Delete',\n description: 'Delete entire topic'\n },\n topic_delete_warning: {\n id: 'topic_delete_warning',\n defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',\n description: 'Alert warning when deleting entire topic'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n unblock: {\n id: 'menu_item_unblock',\n defaultMessage: 'Unblock',\n description: 'Unblock topic or user'\n },\n // Generic string suitable for either topic or user.\n block: {\n id: 'menu_item_block',\n defaultMessage: 'Block',\n description: 'Block topic or user'\n },\n topic_block_warning: {\n id: 'topic_block_warning',\n defaultMessage: 'Are you sure you want to block this conversation?',\n description: 'Alert warning when blocking a topic.'\n },\n member_delete: {\n id: 'menu_item_member_delete',\n defaultMessage: 'Remove',\n description: 'Remove user from topic'\n },\n archive: {\n id: 'menu_item_archive_topic',\n defaultMessage: 'Archive',\n description: 'Move topic from the list of active chats to archive'\n },\n unarchive: {\n id: 'menu_item_restore_topic',\n defaultMessage: 'Restore',\n description: 'Restore topic from archive'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n});\n\nclass ContextMenu extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n const {formatMessage} = props.intl;\n\n this.handlePageClick = this.handlePageClick.bind(this);\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this.handleClick = this.handleClick.bind(this);\n\n // Preconfigured menu items.\n this.MenuItems = {\n 'topic_info': {\n id: 'topic_info',\n title: formatMessage(messages.info),\n handler: null\n },\n 'messages_clear': {\n id: 'messages_clear',\n title: formatMessage(messages.clear_messages),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_messages), // title\n formatMessage(messages.clear_messages_warning), // content\n (() => { this.deleteMessages(true, false, params, errorHandler); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'messages_clear_hard': {\n id: 'messages_clear_hard',\n title: formatMessage(messages.clear_for_all),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_for_all), // title\n formatMessage(message.delete_messages_warning), // content\n (() => { return this.deleteMessages(true, true, params, errorHandler); }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'message_delete': {\n id: 'message_delete',\n title: formatMessage(messages.delete),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, false, params, errorHandler);\n }\n },\n 'message_delete_hard': {\n id: 'message_delete_hard',\n title: formatMessage(messages.delete_for_all),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, true, params, errorHandler);\n }\n },\n 'menu_item_send_retry': {\n id: 'menu_item_send_retry',\n title: formatMessage(messages.send_retry),\n handler: (params, errorHandler) => {\n return this.retryMessage(params, errorHandler);\n }\n },\n 'menu_item_reply': {\n id: 'menu_item_reply',\n title: formatMessage(messages.reply),\n handler: (params, errorHandler) => {\n return this.replyToMessage(params, errorHandler);\n }\n },\n 'menu_item_forward': {\n id: 'menu_item_forward',\n title: formatMessage(messages.forward),\n handler: () => {} /* the action is taken directly in tinode-web */\n },\n 'topic_unmute': {\n id: 'topic_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'topic_mute': {\n id: 'topic_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'topic_unblock': {\n id: 'topic_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n 'topic_block': {\n id: 'topic_block',\n title: formatMessage(messages.block),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.block), // title\n formatMessage(messages.topic_block_warning), // content\n (() => {\n return this.topicPermissionSetter('-JP', params, errorHandler).then((ctrl) => {\n this.props.onTopicRemoved(params.topicName);\n return ctrl;\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_delete': {\n id: 'topic_delete',\n title: formatMessage(messages.topic_delete),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.topic_delete), // title\n formatMessage(messages.topic_delete_warning), // content\n (() => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.delTopic(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_archive': {\n id: 'topic_archive',\n title: formatMessage(messages.archive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'topic_restore': {\n id: 'topic_restore',\n title: formatMessage(messages.unarchive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(false).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n // menu_item_edit_permissions is defined elsewhere.\n 'permissions': {\n id: 'permissions',\n title: formatMessage(messages.edit_permissions),\n handler: null\n },\n 'member_delete': {\n id: 'member_delete',\n title: formatMessage(messages.member_delete),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic || !params.user) {\n console.log(\"Topic or user not found: '\" + params.topicName + \"', '\" + params.user + \"'\");\n return;\n }\n return topic.delSubscription(params.user).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'member_mute': {\n id: 'member_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'member_unmute': {\n id: 'member_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'member_block': {\n id: 'member_block',\n title: formatMessage(messages.block),\n handler: this.topicPermissionSetter.bind(this, '-JP')\n },\n 'member_unblock': {\n id: 'member_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n };\n }\n\n componentDidMount() {\n document.addEventListener('mousedown', this.handlePageClick, false);\n document.addEventListener('keyup', this.handleEscapeKey, false);\n }\n\n componentWillUnmount() {\n document.removeEventListener('mousedown', this.handlePageClick, false);\n document.removeEventListener('keyup', this.handleEscapeKey, false);\n }\n\n handlePageClick(e) {\n if (this.selfRef.current.contains(e.target)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n }\n\n handleEscapeKey(e) {\n if (e.keyCode === 27) {\n this.props.hide();\n }\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n let item = this.props.items[e.currentTarget.dataset.id];\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n\n if (!item) {\n console.error(\"Invalid menu item ID\", e.currentTarget.dataset.id);\n } else {\n this.props.onAction(\n item.id,\n item.handler(this.props.params, this.props.onError),\n this.props.params);\n }\n }\n\n // Menu Actions\n\n deleteMessages(all, hard, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n\n // We don't know if the message is still pending (e.g. attachment is being uploaded),\n // so try cancelling first. No harm if we can't cancel.\n // The message can be cancelled if transmission to the server has not\n // started yet or if the message send has failed.\n if (!all && topic.cancelSend(params.seq)) {\n return;\n }\n // Can't cancel. Delete instead.\n const promise = all ?\n topic.delMessagesAll(hard) :\n topic.delMessagesList([params.seq], hard);\n\n return promise.catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Retries sending failed message.\n retryMessage(params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n // Remove the existing message entry.\n if (!topic || !topic.flushMessage(params.seq)) {\n return;\n }\n const msg = topic.createMessage(params.content, false);\n return topic.publishDraft(msg).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Function is used by context menu to set permissions.\n topicPermissionSetter(mode, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found\", params.topicName);\n return;\n }\n\n let result = topic.updateMode(params.user, mode);\n if (errorHandler) {\n result = result.catch((err) => {\n errorHandler(err.message, 'err');\n });\n }\n return result;\n }\n\n replyToMessage(params, errorHandler) {\n params.pickReply(params.seq, params.content, params.userFrom, params.userName, errorHandler);\n }\n\n render() {\n const menu = [];\n let count = 0;\n this.props.items.map((item) => {\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n if (item && item.title) {\n menu.push(\n item.title == '-' ?\n \n :\n {item.title}\n );\n }\n count++;\n });\n\n // Ensure that menu is inside the app-container.\n const hSize = 12 * REM_SIZE;\n const vSize = REM_SIZE * (0.7 + menu.length * 2.5);\n const left = (this.props.bounds.right - this.props.clickAt.x < hSize) ?\n (this.props.clickAt.x - this.props.bounds.left - hSize) :\n (this.props.clickAt.x - this.props.bounds.left);\n const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ?\n (this.props.clickAt.y - this.props.bounds.top - vSize) :\n (this.props.clickAt.y - this.props.bounds.top);\n\n const position = {\n left: left + 'px',\n top: top + 'px'\n };\n\n return (\n \n );\n }\n}\n\nexport default injectIntl(ContextMenu);\n","import React from 'react';\n\nconst icon_mapping = {'muted': 'notifications_off', 'banned': 'block', 'staff': 'verified_user'};\n\n/* Contact's labels: [you], or icons [muted], [blocked] */\nexport default class ContactBadges extends React.PureComponent {\n render() {\n let badges = null;\n if (this.props.badges && this.props.badges.length > 0) {\n badges = [];\n this.props.badges.map((b) => {\n const color = (b.color ? ' ' + b.color : '');\n if (b.icon) {\n badges.push({icon_mapping[b.icon] || b.icon});\n } else {\n badges.push({b.name});\n }\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n","// Short representation of time in the past.\nexport function shortDateFormat(then, locale) {\n locale = locale || window.navigator.userLanguage || window.navigator.language;\n const now = new Date();\n if (then.getFullYear() == now.getFullYear()) {\n // Same year.\n if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) {\n // Same month and day, show time only.\n return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'});\n }\n // Different month and/or day, show month day, time.\n return then.toLocaleDateString(locale,\n {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'});\n }\n // Different year: just show the date.\n return then.toLocaleDateString(locale, {year: 'numeric', month: 'short', day: 'numeric'});\n}\n\n// Convert seconds to minutes:seconds, i.e. 156 sec -> 2:36.\n// If fixedMins is true, then minutes are represented by at least two digits.\nexport function secondsToTime(seconds, fixedMin) {\n let min = Math.floor(seconds / 60);\n if (fixedMin) {\n min = min < 10 ? `0${min}` : min;\n }\n let sec = (seconds % 60) | 0;\n sec = sec < 10 ? `0${sec}` : sec;\n return `${min}:${sec}`;\n}\n\n// Convert a number of bytes to human-readable format.\nexport function bytesToHumanSize(bytes) {\n if (!bytes || bytes == 0) {\n return '0 Bytes';\n }\n\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const bucket = Math.min(Math.floor(Math.log2(bytes) / 10) | 0, sizes.length-1);\n const count = bytes / Math.pow(1024, bucket);\n const round = bucket > 0 ? (count < 3 ? 2 : (count < 30 ? 1 : 0)) : 0;\n return count.toFixed(round) + ' ' + sizes[bucket];\n}\n\n// Shorten a file name to be under maxLength by clipping out the middle.\nexport function shortenFileName(filename, maxLength) {\n if (typeof filename != 'string') {\n return filename;\n }\n return filename.length > maxLength ?\n filename.slice(0, maxLength/2 - 1) + '…' + filename.slice(1 - maxLength/2) : filename;\n}\n\n// Get 32 bit integer hash value for a string. Ideally it should produce the same value\n// as Java's String#hash().\nfunction stringToColorHash(value) {\n let hash = 0;\n value = '' + value;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) - hash) + value.charCodeAt(i);\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash;\n}\n\n// User avatar letter color id.\nexport function letterTileColorId(userId) {\n return Math.abs(stringToColorHash(userId)) % 16;\n}\n\n// Converts user or topic ID to a CSS color class. Ideally it should produce the same color value as Java version.\n// params:\n// - {string} id: user or topic ID\n// - {boolean} light: light or dark version of the color.\n// - {boolean} fg: foreground (text) or background color.\nexport function idToColorClass(id, light, fg) {\n return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + letterTileColorId(id);\n}\n","// Odds and ends\n\nimport Tinode from 'tinode-sdk';\n\n// Make shortcut icon appear with a green dot + show unread count in title.\nexport function updateFavicon(count) {\n const oldIcon = document.getElementById('shortcut-icon');\n const head = document.head || document.getElementsByTagName('head')[0];\n const newIcon = document.createElement('link');\n newIcon.type = 'image/png';\n newIcon.id = 'shortcut-icon';\n newIcon.rel = 'shortcut icon';\n newIcon.href = 'img/logo32x32' + (count > 0 ? 'a' : '') + '.png';\n if (oldIcon) {\n head.removeChild(oldIcon);\n }\n head.appendChild(newIcon);\n\n document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode';\n}\n\n// Create theCard which represents user's or topic's \"public\" info.\nexport function theCard(fn, imageUrl, imageMimeType, note) {\n let card = null;\n fn = fn && fn.trim();\n note = note && note.trim();\n\n if (fn) {\n card = {\n fn: fn\n };\n }\n\n if (typeof note == 'string') {\n card = card || {};\n card.note = note ? note : Tinode.DEL_CHAR;\n }\n\n if (imageUrl) {\n card = card || {};\n let mimeType = imageMimeType;\n // Is this a data URL \"data:[][;base64],\"?\n const matches = /^data:(image\\/[-a-z0-9+.]+)?(;base64)?,/i.exec(imageUrl);\n if (matches) {\n mimeType = matches[1];\n card.photo = {\n data: imageUrl.substring(imageUrl.indexOf(',') + 1),\n ref: Tinode.DEL_CHAR\n };\n } else {\n card.photo = {\n data: Tinode.DEL_CHAR,\n ref: imageUrl\n };\n }\n card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);\n }\n\n return card;\n}\n\n// Deep-shallow compare two arrays: shallow compare each element.\nexport function arrayEqual(a, b) {\n if (a === b) {\n return true;\n }\n\n if (!Array.isArray(a) || !Array.isArray(b)) {\n return false;\n }\n\n // Compare lengths first.\n if (a.length != b.length) {\n return false;\n }\n // Order of elements is ignored.\n a.sort();\n b.sort();\n for (let i = 0, l = a.length; i < l; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n// Checks (loosely) if the given string is a phone. If so, returns the phone number in a format\n// as close to E.164 as possible.\nexport function asPhone(val) {\n val = val.trim();\n if (/^(?:\\+?(\\d{1,3}))?[- (.]*(\\d{3})[- ).]*(\\d{3})[- .]*(\\d{2})[- .]*(\\d{2})?$/.test(val)) {\n return val.replace(/[- ().]*/, '');\n }\n return null;\n}\n\n// Checks (loosely) if the given string is an email. If so returns the email.\nexport function asEmail(val) {\n val = val.trim();\n if (/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$/.test(val)) {\n return val;\n }\n return null;\n}\n\n// Checks if URL is a relative url, i.e. has no 'scheme://', including the case of missing scheme '//'.\n// The scheme is expected to be RFC-compliant, e.g. [a-z][a-z0-9+.-]*\n// example.html - ok\n// https:example.com - not ok.\n// http:/example.com - not ok.\n// ' ↲ https://example.com' - not ok. (↲ means carriage return)\nexport function isUrlRelative(url) {\n return url && !/^\\s*([a-z][a-z0-9+.-]*:|\\/\\/)/im.test(url);\n}\n\n// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of\n// strings with permitted URL schemes, such as ['ftp', 'ftps']; otherwise accept http and https only.\nexport function sanitizeUrl(url, allowedSchemes) {\n if (typeof url != 'string') {\n return url;\n }\n\n // Strip control characters and whitespace. They are not valid URL characters anyway.\n url = url.replace(/[^\\x20-\\x7E]/gmi, '').trim();\n\n // Relative URLs are safe.\n // Relative URL does not start with ':', abcd123: or '//'.\n if (!/^([a-z][a-z0-9+.-]*:|\\/\\/)/i.test(url)) {\n return url;\n }\n\n // Blob URLs are safe.\n if (/^blob:http/.test(url)) {\n return url;\n }\n\n // Absolute URL. Accept only safe schemes, or no scheme.\n const schemes = Array.isArray(allowedSchemes) ? allowedSchemes.join('|') : 'http|https';\n const re = new RegExp('^((' + schemes + '):|//)', 'i');\n if (!re.test(url)) {\n return null;\n }\n\n return url;\n}\n\n// Ensure URL is suitable for
field: the URL must be a relative URL or\n// have http:, https:, blob: or data: scheme.\n// In case of data: scheme, the URL must start with a 'data:image/XXXX;base64,'.\nexport function sanitizeImageUrl(url) {\n if (!url) {\n return null;\n }\n\n const sanitizedUrl = sanitizeUrl(url);\n if (sanitizedUrl) {\n return sanitizedUrl;\n }\n\n // Is this a data: URL of an image?\n if (/data:image\\/[a-z0-9.-]+;base64,/i.test(url.trim())) {\n return url;\n }\n\n return null;\n}\n\n// Given message's received status, return name and color of a delivery indicator icon.\nexport function deliveryMarker(received) {\n switch (received) {\n case Tinode.MESSAGE_STATUS_SENDING:\n return { name: 'access_time' }; // watch face\n case Tinode.MESSAGE_STATUS_FAILED:\n return { name: 'warning', color: 'danger-color' }; // yellow icon /!\\\n case Tinode.MESSAGE_STATUS_SENT:\n return { name: 'done' }; // checkmark\n case Tinode.MESSAGE_STATUS_RECEIVED:\n return { name: 'done_all' }; // double checkmark\n case Tinode.MESSAGE_STATUS_READ:\n return { name: 'done_all', color: 'blue' }; // blue double checkmark\n }\n return null;\n}\n\n// Wraps a promise to make it cancelable.\n// The parameter can be either a promise or an error. If it's an error, the wrapped promise is\n// created in a rejected state.\nexport function cancelablePromise(promise) {\n let hasCanceled = false;\n\n const wrappedPromise = promise instanceof Error ?\n Promise.reject(promise) :\n new Promise((resolve, reject) => {\n promise.then(\n result => hasCanceled ? reject({isCanceled: true}) : resolve(result),\n error => hasCanceled ? reject({isCanceled: true}) : reject(error)\n );\n });\n\n return {\n promise: wrappedPromise,\n cancel() {\n hasCanceled = true;\n },\n };\n};\n","import React from 'react';\n\nimport Tinode from 'tinode-sdk';\n\nimport { idToColorClass } from '../lib/strformat.js';\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nexport default class LetterTile extends React.PureComponent {\n render() {\n let avatar;\n if (this.props.avatar === true) {\n const isGroup = Tinode.isGroupTopicName(this.props.topic);\n const iconColor = idToColorClass(this.props.topic, isGroup);\n if (this.props.topic && this.props.title && this.props.title.trim()) {\n const letter = this.props.title.trim().charAt(0);\n const className = 'lettertile ' + iconColor + (this.props.deleted ? ' disabled' : '');\n avatar = ()\n } else {\n const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');\n avatar = isGroup ? group : person;\n }\n } else if (this.props.avatar) {\n const url = this.props.tinode.authorizeURL(sanitizeImageUrl(this.props.avatar));\n // If avatar image is invalid, show a placeholder.\n const className = 'avatar' + (this.props.deleted ? ' deleted' : '');\n avatar =
{e.target.onerror = null; e.target.src=\"../img/broken_image.png\"}} />;\n } else {\n avatar = null;\n }\n return avatar;\n }\n}\n","// The counter of unread messages in the topic.\nimport React from 'react';\n\nexport default class UnreadBadge extends React.PureComponent {\n render() {\n return (this.props.count > 0 ?\n {this.props.count > 9 ? \"9+\" : this.props.count}\n : null);\n }\n};\n","// Image with a placeholder which is replaced when the promise is resolved.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class LazyImage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n src: 'img/placeholder.png',\n style: Object.assign({padding: '4px'}, this.props.style),\n className: this.props.className,\n alt: this.props.alt,\n onClick: this.props.onClick,\n };\n }\n\n componentDidMount() {\n // whenDone is a wrapper around an actual promise to be able to cancel it.\n this.props.whenDone\n .promise\n .then(data => this.setState({src: data.src, style: {...this.state.style, padding: 0}}))\n .catch(() => this.setState({src: 'img/broken_image.png'}));\n }\n\n componentWillUnmount() {\n this.props.whenDone.cancel();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.whenDone != this.props.whenDone) {\n this.setState({src: 'img/placeholder.png', style: {...this.state.style, padding: '4px'}});\n this.props.whenDone\n .promise\n .then(data => this.setState({src: data.src, style: {...this.state.style, padding: 0}}))\n .catch(() => this.setState({src: 'img/broken_image.png'}));\n }\n }\n\n render() {\n return React.createElement('img', this.state);\n }\n};\n","// File uload/download progress indicator with a cancel inside.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class FileProgress extends React.PureComponent {\n render() {\n return (\n \n );\n }\n}\n","// Image view with progress bar and a cancel button.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\n\nexport default class UploadingImage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n return (\n \n {React.createElement('img', this.props)}\n
\n \n
\n
\n );\n }\n};\n","// File and image helper functions.\n\n// Supported image MIME types and corresponding file extensions.\nexport const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];\nexport const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];\n\n// Get an URL from a theCard photo: either create a data: URL or return reference URL.\nexport function makeImageUrl(photo) {\n if (photo && typeof photo == 'object') {\n if (photo.ref) {\n return photo.ref\n }\n if (photo.data && photo.type) {\n const mime = photo.type.startsWith('image/') ? photo.type : ('image/' + photo.type);\n return 'data:' + mime + ';base64,' + photo.data;\n }\n }\n return null;\n}\n\n// Calculate linear dimensions for scaling image down to fit under a certain size.\n// Returns an object which contains destination sizes, source sizes, and offsets\n// into source (when making square images).\nexport function fitImageSize(width, height, maxWidth, maxHeight, forceSquare) {\n // Sanitize input\n width = width | 0;\n height = height | 0;\n maxWidth = maxWidth | 0;\n maxHeight = maxHeight | 0;\n\n if (width <= 0 || height <= 0 || maxWidth <= 0 || maxHeight <= 0) {\n return null;\n }\n\n if (forceSquare) {\n maxWidth = maxHeight = Math.min(maxWidth, maxHeight);\n }\n\n const scale = Math.min(\n Math.min(width, maxWidth) / width,\n Math.min(height, maxHeight) / height\n );\n\n const size = {\n dstWidth: (width * scale) | 0,\n dstHeight: (height * scale) | 0,\n };\n\n if (forceSquare) {\n // Also calculate parameters for making the image square.\n size.dstWidth = size.dstHeight = Math.min(size.dstWidth, size.dstHeight);\n size.srcWidth = size.srcHeight = Math.min(width, height);\n size.xoffset = ((width - size.srcWidth) / 2) | 0;\n size.yoffset = ((height - size.srcWidth) / 2) | 0;\n } else {\n size.xoffset = size.yoffset = 0;\n size.srcWidth = width;\n size.srcHeight = height;\n }\n return size;\n}\n\n// Ensure file's extension matches mime content type\nexport function fileNameForMime(fname, mime) {\n const idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime);\n if (idx < 0 || !fname) {\n // Unknown mime or empty name.\n return fname;\n }\n const ext = MIME_EXTENSIONS[idx];\n\n const at = fname.lastIndexOf('.');\n if (at >= 0) {\n fname = fname.substring(0, at);\n }\n return fname + '.' + ext;\n}\n\n// Scale uploaded image to fit under certain dimensions and byte size, optionally constraining to a square.\n// Returns a promise which is resolven on success or rejected on failure.\nexport function imageScaled(fileOrBlob, maxWidth, maxHeight, maxSize, forceSquare) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = function(err) {\n reject(new Error(\"Image format unrecognized\"));\n }\n img.onload = async function() {\n // Once the image is loaded, the URL is no longer needed.\n URL.revokeObjectURL(img.src);\n\n // Calculate the desired image dimensions.\n const dim = fitImageSize(img.width, img.height, maxWidth, maxHeight, forceSquare);\n if (!dim) {\n reject(new Error(\"Invalid image\"));\n return;\n }\n let canvas = document.createElement('canvas');\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n let ctx = canvas.getContext('2d');\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(img, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n\n const mime = SUPPORTED_IMAGE_FORMATS.includes(fileOrBlob.type) ? fileOrBlob.type : 'image/jpeg';\n // Generate blob to check size of the image.\n let blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n if (!blob) {\n reject(new Error(\"Unsupported image format\"));\n return;\n }\n // Ensure the image is not too large. Shrink the image keeping the aspect ratio.\n // Do nothing if maxsize is <= 0.\n while (maxSize > 0 && blob.length > maxSize) {\n dim.dstWidth = (dim.dstWidth * 0.70710678118) | 0;\n dim.dstHeight = (dim.dstHeight * 0.70710678118) | 0;\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n ctx = canvas.getContext('2d');\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(img, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n }\n\n canvas = null;\n resolve({mime: mime, blob: blob, width: dim.dstWidth, height: dim.dstHeight, name: fileNameForMime(fileOrBlob.name, mime)});\n };\n img.src = URL.createObjectURL(fileOrBlob);\n });\n}\n\n// Scale and crop image according to specified dimensions.\n// The coordinates are in unscaled image pixels, i.e. cut the rectangle first then scale it.\n// returns a promise.\nexport function imageCrop(mime, objURL, left, top, width, height, scale) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = (err) => {\n reject(new Error(\"Image format unrecognized\"));\n };\n img.onload = () => {\n // Once the image is loaded, the URL is no longer needed.\n URL.revokeObjectURL(img.src);\n\n let canvas = document.createElement('canvas');\n canvas.width = width * scale;\n canvas.height = height * scale;\n let ctx = canvas.getContext('2d');\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(img, left, top, width, height, 0, 0, canvas.width, canvas.height);\n\n mime = SUPPORTED_IMAGE_FORMATS.includes(mime) ? mime : 'image/jpeg';\n // Generate blob to check size of the image.\n canvas.toBlob((blob) => {\n // Allow GC.\n canvas = null;\n if (blob) {\n resolve({mime: mime, blob: blob, width: width, height: height});\n } else {\n reject(new Error(\"Unsupported image format\"));\n }\n }, mime);\n };\n img.src = objURL;\n });\n}\n\n// Convert file to base64 string.\nexport function fileToBase64(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = (evt) => {\n reject(reader.error);\n };\n reader.onload = () => {\n resolve({mime: file.type, bits: reader.result.split(',')[1], name: file.name});\n };\n reader.readAsDataURL(file);\n });\n}\n\n// Convert Blob to base64 string. Returns a promise resolved with the base64 string and mime.\nexport function blobToBase64(blob) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = (evt) => {\n reject(reader.error);\n };\n reader.onload = () => {\n resolve({mime: blob.type, bits: reader.result.split(',')[1]});\n };\n reader.readAsDataURL(blob);\n });\n}\n\n// File pasted from the clipboard. It's either an inline image or a file attachment.\nexport function filePasted(event, onImageSuccess, onAttachmentSuccess, onError) {\n const items = (event.clipboardData || event.originalEvent.clipboardData || {}).items;\n if (!Array.isArray(items)) {\n return false;\n }\n\n for (let i in items) {\n const item = items[i];\n if (item.kind === 'file') {\n const file = item.getAsFile();\n if (!file) {\n console.error(\"Failed to get file object from pasted file item\", item.kind, item.type);\n onError(\"Failed to get file object from pasted file item\");\n continue;\n }\n if (file.type && file.type.split('/')[0] == 'image') {\n onImageSuccess(file);\n } else {\n onAttachmentSuccess(file);\n }\n // Indicate that the pasted data contains a file.\n return true;\n }\n }\n // No file found.\n return false;\n}\n\n// Get mime type from data URL header.\nexport function getMimeType(header) {\n var mime = /^data:(image\\/[-+a-z0-9.]+);base64/.exec(header);\n return (mime && mime.length > 1) ? mime[1] : null;\n}\n\n// Given length of a binary object in bytes, calculate the length after\n// base64 encoding.\nexport function base64EncodedLen(n) {\n return Math.floor((n + 2) / 3) * 4;\n}\n\n// Given length of a base64-encoded object, calculate decoded size of the\n// pbject in bytes.\nexport function base64DecodedLen(n) {\n return Math.floor(n / 4) * 3;\n}\n\n// Re-encode string to standard base64 encoding with padding.\n// The string may be base64-URL encoded without the padding.\nexport function base64ReEncode(str) {\n if (str) {\n str = str.replace(/-/g, '+').replace(/_/g, '/');\n try {\n str = btoa(atob(str));\n } catch(err) {\n console.error(\"Failed to base64 re-encode string.\", err);\n str = null;\n }\n }\n return str;\n}\n\n// Convert a base64 encoded string with the provided mime type into a Blob.\nexport function base64ToBlob(str, mime) {\n if (!str) {\n return null;\n }\n\n try {\n // Make blob.\n const bin = atob(str);\n const length = bin.length;\n const buf = new ArrayBuffer(length);\n const arr = new Uint8Array(buf);\n for (let i = 0; i < length; i++) {\n arr[i] = bin.charCodeAt(i);\n }\n\n return new Blob([buf], { type: mime });\n } catch(err) {\n console.error(\"Failed to convert base64 to blob: \", err);\n }\n\n return null;\n}\n","import React from 'react';\nimport { defineMessages } from 'react-intl';\n\nimport { Drafty } from 'tinode-sdk';\n\nimport LazyImage from '../widgets/lazy-image.jsx'\nimport UploadingImage from '../widgets/uploading-image.jsx'\n\nimport { IMAGE_THUMBNAIL_DIM, BROKEN_IMAGE_SIZE, REM_SIZE } from '../config.js';\nimport { base64ToBlob, blobToBase64, fitImageSize, imageScaled } from './blob-helpers.js';\nimport { idToColorClass, shortenFileName } from './strformat.js';\nimport { cancelablePromise, sanitizeImageUrl } from './utils.js';\n\nconst messages = defineMessages({\n drafty_form: {\n id: 'drafty_form',\n defaultMessage: 'Form: ',\n description: 'Comment for form in drafty preview'\n },\n drafty_attachment: {\n id: 'drafty_attachment',\n defaultMessage: 'Attachment',\n description: 'Comment for attachment in drafty preview'\n },\n drafty_image: {\n id: 'drafty_image',\n defaultMessage: 'Picture',\n description: 'Comment for embedded images in drafty preview'\n }\n});\n\n// Size the already scaled image.\nfunction handleImageData(el, data, attr) {\n if (!data) {\n attr.src = 'img/broken_image.png';\n attr.style = {\n width: IMAGE_THUMBNAIL_DIM + 'px',\n height: IMAGE_THUMBNAIL_DIM + 'px',\n };\n return el;\n }\n\n attr.className = 'inline-image';\n const dim = fitImageSize(data.width, data.height,\n this.viewportWidth > 0 ? Math.min(this.viewportWidth - REM_SIZE * 6.5, REM_SIZE * 34.5) :\n REM_SIZE * 34.5, REM_SIZE * 24, false) ||\n {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = {\n width: dim.dstWidth + 'px',\n height: dim.dstHeight + 'px',\n // Looks like a Chrome bug: broken image does not respect 'width' and 'height'.\n minWidth: dim.dstWidth + 'px',\n minHeight: dim.dstHeight + 'px'\n };\n if (!Drafty.isProcessing(data)) {\n attr.src = this.authorizeURL(sanitizeImageUrl(attr.src));\n attr.alt = data.name;\n if (attr.src) {\n if (Math.max(data.width || 0, data.height || 0) > IMAGE_THUMBNAIL_DIM) {\n // Allow previews for large enough images.\n attr.onClick = this.onImagePreview;\n attr.className += ' image-clickable';\n }\n attr.loading = 'lazy';\n } else {\n attr.src = 'img/broken_image.png';\n }\n } else {\n // Use custom element instead of
.\n el = UploadingImage;\n }\n\n return el;\n}\n\nfunction quotedImage(data) {\n let promise;\n // Get the blob from the image data.\n if (data.val) {\n const blob = base64ToBlob(data.val, data.mime);\n promise = blob ? Promise.resolve(blob) : Prmise.reject(new Error(\"Invalid image\"));\n } else {\n promise = fetch(this.authorizeURL(sanitizeImageUrl(data.ref))).then(evt => {\n if (evt.ok) {\n return evt.blob();\n } else {\n throw new Error(`Image fetch unsuccessful: ${evt.status} ${evt.statusText}`);\n }\n });\n }\n\n // Scale the blob.\n return promise\n .then(blob => {\n return imageScaled(blob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, true)\n }).then(scaled => {\n data.mime = scaled.mime;\n data.size = scaled.blob.size;\n data.width = scaled.width;\n data.height = scaled.height;\n delete data.ref;\n // Keeping the original file name, if provided: ex.data.name;\n\n return blobToBase64(scaled.blob);\n }).then(b64 => {\n data.val = b64.bits;\n return data;\n }).catch(err => {\n delete data.val;\n delete data.name;\n data.width = IMAGE_THUMBNAIL_DIM;\n data.height = IMAGE_THUMBNAIL_DIM;\n data.maxWidth = IMAGE_THUMBNAIL_DIM;\n data.maxHeight = IMAGE_THUMBNAIL_DIM;\n // Rethrow.\n throw err;\n });\n}\n\n// The main Drafty formatter: converts Drafty elements into React classes. 'this' is set by the caller.\n// 'this' must contain:\n// viewportWidth: this.props.viewportWidth;\n// authorizeURL: this.props.tinode.authorizeURL\n// onImagePreview: this.handleImagePreview\n// onFormButtonClick: this.handleFormButtonClick\n// onQuoteClick: this.handleQuoteClick (optional)\nexport function fullFormatter(style, data, values, key, stack) {\n if (stack.includes('QQ')) {\n return quoteFormatter.call(this, style, data, values, key);\n }\n\n if (!style) {\n // Unformatted.\n return values;\n }\n\n let el = Drafty.tagName(style);\n const attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'BR':\n values = null;\n break;\n case 'HL':\n // Highlighted text. Assign class name.\n attr.className = 'highlight';\n break;\n case 'HD':\n el = null;\n values = null;\n break;\n case 'IM':\n // Additional processing for images\n el = handleImageData.call(this, el, data, attr);\n // Image element cannot have content.\n values = null;\n break;\n case 'BN':\n // Button\n attr.onClick = this.onFormButtonClick;\n let inner = React.Children.map(values, (child) => {\n return typeof child == 'string' ? child : undefined;\n });\n if (!inner || inner.length == 0) {\n inner = [attr.name]\n }\n // Get text which will be sent back when the button is clicked.\n attr['data-title'] = inner.join('');\n break;\n case 'MN':\n // Mention\n attr.className = 'mention'\n if (data) {\n attr.className += ' ' + idToColorClass(data.val, false, true);\n }\n break;\n case 'FM':\n // Form\n attr.className = 'bot-form';\n break;\n case 'RW':\n // Form element formatting is dependent on element content.\n break;\n case 'QQ':\n // Quote/citation.\n attr.className = 'reply-quote'\n attr.onClick = this.onQuoteClick;\n break;\n default:\n if (el == '_UNKN') {\n // Unknown element.\n el = React.Fragment;\n values = [extension, ' '].concat(values || []);\n }\n break;\n }\n if (!el) {\n return values;\n }\n return React.createElement(el, attr, values);\n}\n\n// Converts Drafty object into a one-line preview. 'this' is set by the caller.\n// 'this' must contain:\n// formatMessage: this.props.intl.formatMessage\n// messages: formatjs messages defined with defineMessages.\nexport function previewFormatter(style, data, values, key) {\n if (!style) {\n // Unformatted.\n return values;\n }\n\n let el = Drafty.tagName(style);\n const attr = { key: key };\n switch (style) {\n case 'BR':\n // Replace new line with a space.\n el = React.Fragment;\n values = [' '];\n break;\n case 'HL':\n // Make highlight less prominent in preview.\n attr.className = 'highlight preview';\n break;\n case 'LN':\n case 'MN':\n // Disable links in previews.\n el = 'span';\n break;\n case 'IM':\n // Replace image with '[icon] Image'.\n el = React.Fragment;\n values = [photo, this.formatMessage(messages.drafty_image)];\n break;\n case 'BN':\n el = 'span';\n attr.className = 'flat-button faux';\n break;\n case 'FM':\n el = React.Fragment;\n values = [dashboard,\n this.formatMessage(messages.drafty_form)].concat(' ', values || []);\n break;\n case 'RW':\n el = React.Fragment;\n break;\n case 'EX':\n if (data) {\n if (data.mime == 'application/json') {\n // Ignore JSON attachments: they are form response payloads.\n return null;\n }\n // Clear payload.\n delete data.val;\n delete data.ref;\n }\n el = React.Fragment;\n values = [attachment, this.formatMessage(messages.drafty_attachment)];\n break;\n case 'QQ':\n case 'HD':\n el = null;\n values = null;\n break;\n default:\n if (el == '_UNKN') {\n el = React.Fragment;\n values = [extension, ' '].concat(values || []);\n }\n break;\n }\n if (!el) {\n return values;\n }\n return React.createElement(el, attr, values);\n};\n\n// Converts Drafty object into a quoted reply. 'this' is set by the caller.\nfunction inlineImageAttr(attr, data) {\n attr.style = {\n width: IMAGE_THUMBNAIL_DIM + 'px',\n height: IMAGE_THUMBNAIL_DIM + 'px',\n maxWidth: IMAGE_THUMBNAIL_DIM + 'px',\n maxHeight: IMAGE_THUMBNAIL_DIM + 'px',\n }\n attr.className = 'inline-image';\n attr.alt = this.formatMessage(messages.drafty_image);\n if (!data) {\n attr.src = 'img/broken_image.png';\n }\n attr.title = attr.alt;\n return attr;\n}\n\n// Displays a portion of Drafty within 'QQ' quotes. 'this' is set by the caller.\n// 'this' must contain:\n// formatMessage: this.props.intl.formatMessage\n// messages: formatjs messages defined with defineMessages.\n// authorizeURL: this.props.tinode.authorizeURL\n// onQuoteClick: this.handleQuoteClick (optional)\nfunction quoteFormatter(style, data, values, key) {\n if (['BR', 'EX', 'IM', 'MN'].includes(style)) {\n let el = Drafty.tagName(style);\n let attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch(style) {\n case 'BR':\n values = null;\n break;\n case 'IM':\n attr = inlineImageAttr.call(this, attr, data);\n values = [React.createElement('img', attr, null), ' ', attr.alt];\n el = React.Fragment;\n // Fragment attributes.\n attr = {key: key};\n break;\n case 'MN':\n el = 'span';\n attr.className = 'mention'\n if (data) {\n attr.className += ' ' + idToColorClass(data.val, false, true);\n }\n break;\n case 'EX':\n let fname;\n if (data) {\n if (data.mime == 'application/json') {\n // Ignore JSON attachments: they are form response payloads.\n return null;\n }\n fname = data.name;\n // Clear payload.\n delete data.val;\n delete data.ref;\n }\n el = React.Fragment;\n values = [attachment,\n shortenFileName(fname, 16) || this.formatMessage(messages.drafty_attachment)];\n break;\n }\n return React.createElement(el, attr, values);\n }\n return previewFormatter.call(this, style, data, values, key);\n}\n\n// Create image thumbnail suitable for inclusion in a quote.\nfunction quoteImage(data) {\n let promise;\n // Get the blob from the image data.\n if (data.val) {\n const blob = base64ToBlob(data.val, data.mime);\n if (!blob) {\n throw new Error(\"Invalid image\");\n }\n promise = Promise.resolve(blob);\n } else if (data.ref) {\n promise = fetch(this.authorizeURL(sanitizeImageUrl(data.ref)))\n .then(evt => {\n if (evt.ok) {\n return evt.blob();\n } else {\n throw new Error(`Image fetch unsuccessful: ${evt.status} ${evt.statusText}`);\n }\n });\n } else {\n throw new Error(\"Missing image data\");\n }\n\n // Scale the blob.\n return promise\n .then(blob => {\n // Cut the square from the center of the image and shrink it.\n return imageScaled(blob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, true)\n }).then(scaled => {\n data.mime = scaled.mime;\n data.size = scaled.blob.size;\n data.width = scaled.width;\n data.height = scaled.height;\n delete data.ref;\n // Keeping the original file name, if provided: ex.data.name;\n\n data.src = URL.createObjectURL(scaled.blob);\n return blobToBase64(scaled.blob);\n }).then(b64 => {\n data.val = b64.bits;\n return data;\n }).catch(err => {\n delete data.val;\n delete data.src;\n data.width = IMAGE_THUMBNAIL_DIM;\n data.height = IMAGE_THUMBNAIL_DIM;\n // Rethrow.\n throw err;\n });\n}\n\n// Create a preview of a reply.\nexport function replyFormatter(style, data, values, key, stack) {\n if (style == 'IM') {\n const attr = inlineImageAttr.call(this, {key: key}, data);\n let loadedPromise;\n try {\n loadedPromise = cancelablePromise(quoteImage.call(this, data));\n } catch (error) {\n loadedPromise = cancelablePromise(error);\n }\n attr.whenDone = loadedPromise;\n values = [React.createElement(LazyImage, attr, null), ' ', attr.alt];\n return React.createElement(React.Fragment, {key: key}, values);\n } else if (style == 'QQ') {\n if (stack.includes('QQ')) {\n // Quote inside quote when forwarding a message.\n return React.createElement('span', {key: key},\n [format_quote, ' ']);\n }\n\n const attr = Drafty.attrValue('QQ', data) || {};\n attr.key = key;\n attr.className = 'reply-quote'\n return React.createElement(Drafty.tagName('QQ'), attr, values);\n }\n return quoteFormatter.call(this, style, data, values, key);\n}\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage, injectIntl } from 'react-intl';\n\nimport ContactBadges from './contact-badges.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport UnreadBadge from './unread-badge.jsx';\n\nimport { Drafty } from 'tinode-sdk';\n\nimport { previewFormatter } from '../lib/formatters.js';\nimport { deliveryMarker } from '../lib/utils.js';\n\nclass Contact extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n if (this.props.onSelected) {\n // Index this.props.index is used by GroupManager.\n this.props.onSelected(this.props.item, this.props.index);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.props.item, y: e.pageY, x: e.pageX });\n }\n\n render() {\n let title = this.props.title;\n if (!title) {\n title = ;\n } else if (title.length > 30) {\n // FIXME: this is probably wrong for RTL languages.\n title = title.substring(0, 28) + '…';\n }\n const online = this.props.now ? 'online' : 'offline';\n const avatar = this.props.avatar ? this.props.avatar : true;\n const badges = this.props.badges ? this.props.badges.slice() : [];\n const icon_badges = [];\n if (this.props.isVerified) {\n icon_badges.push({icon: 'verified', color: 'verified-color'});\n }\n if (this.props.isStaff) {\n icon_badges.push({icon: 'staff', color: 'staff-color'});\n }\n if (this.props.isDangerous) {\n icon_badges.push({icon: 'dangerous', color: 'danger-color'});\n }\n if (this.props.acs) {\n if (this.props.showMode) {\n badges.push({name: this.props.acs.getMode(), key: 'mode'});\n }\n if (this.props.acs.isMuted()) {\n icon_badges.push({icon: 'muted'});\n }\n if (!this.props.acs.isJoiner()) {\n icon_badges.push({icon: 'banned'});\n }\n }\n\n // The this.props.preview contains alreay shortened Drafty or string.\n let preview;\n if (typeof this.props.preview == 'string') {\n preview = this.props.preview;\n } else if (Drafty.isValid(this.props.preview)) {\n preview = React.createElement(React.Fragment, null, Drafty.format(this.props.preview, previewFormatter,\n {formatMessage: this.props.intl.formatMessage}));\n } else if (this.props.preview) {\n preview =\n <>warning_amber \n \n >;\n }\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n const titleClass = 'contact-title' + (this.props.deleted ? ' deleted' : '');\n\n return (\n \n \n \n {this.props.deleted ? cancel :\n this.props.showOnline ? :\n (this.props.showCheckmark && this.props.selected) ?\n check_circle\n : null}\n
\n \n
{title}\n {this.props.isChannel ?

: null}\n
\n {!this.props.deleted ?
: null}\n
\n {this.props.showMode ?\n
:\n this.props.small ? null :
{marker}{preview || this.props.comment || '\\u00A0'}
}\n
\n {this.props.showContextMenu ?\n \n \n expand_more\n \n : null}\n \n );\n }\n};\n\nexport default injectIntl(Contact);\n","// A panel included into a list of contacts with an action text.\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nclass ContactAction extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.onAction(this.props.action);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n {formatMessage(this.props.title, this.props.values)}
\n \n );\n }\n};\n\nexport default injectIntl(ContactAction);\n","// ContactList: component for showing a list of contacts,\n// such as a list of group members in a group chat.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport { Drafty } from 'tinode-sdk';\nimport Tinode from 'tinode-sdk';\n\nimport Contact from './contact.jsx';\nimport ContactAction from './contact-action.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\nimport { MESSAGE_PREVIEW_LENGTH } from '../config.js';\n\nconst messages = defineMessages({\n badge_you: {\n id: 'badge_you',\n defaultMessage: 'you',\n description: 'Badge for indicating the current user'\n },\n badge_owner: {\n id: 'badge_owner',\n defaultMessage: 'owner',\n description: 'Badge for indicating the owner'\n }\n});\n\nclass ContactList extends React.Component {\n render() {\n const { formatMessage } = this.props.intl;\n const showCheckmark = Array.isArray(this.props.topicSelected);\n const contactNodes = [];\n let contactsCount = 0;\n if (this.props.contacts && this.props.contacts.length > 0) {\n this.props.contacts.map((c) => {\n if (c.action) {\n // Action item\n contactNodes.push(\n );\n } else {\n // Normal contact\n const key = this.props.showMode ? c.user : (c.topic || c.user);\n // If filter function is provided, filter out the items\n // which don't satisfy the condition.\n if (this.props.filterFunc && this.props.filter) {\n const filterOn = [key];\n if (c.private && c.private.comment) {\n filterOn.push(('' + c.private.comment).toLowerCase());\n }\n if (c.public && c.public.fn) {\n filterOn.push(('' + c.public.fn).toLowerCase());\n }\n if (!this.props.filterFunc(this.props.filter, filterOn)) {\n return;\n }\n }\n\n const isChannel = Tinode.isChannelTopicName(key);\n const selected = showCheckmark ?\n (this.props.topicSelected.indexOf(key) > -1) :\n (this.props.topicSelected === key);\n const badges = [];\n if (this.props.showMode) {\n if (key == this.props.myUserId) {\n badges.push({name: formatMessage(messages.badge_you), color: 'green'});\n }\n if (c.acs && c.acs.isOwner()) {\n badges.push({name: formatMessage(messages.badge_owner), color: 'blue'});\n }\n }\n\n const comment = Array.isArray(c.private) ?\n c.private.join(',') : (c.private ? c.private.comment : null);\n let preview;\n let forwarded;\n let deliveryStatus;\n if (!this.props.showMode && c.latestMessage) {\n const msg = c.latestMessage(true);\n if (msg) {\n forwarded = msg.head ? msg.head.forwarded : null;\n deliveryStatus = msg._status || c.msgStatus(msg, true);\n if (msg.content) {\n preview = typeof msg.content == 'string' ?\n msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :\n Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH);\n }\n }\n }\n\n contactNodes.push(\n \n );\n contactsCount ++;\n }\n }, this);\n }\n\n return (\n \n {contactsCount == 0 ?\n
\n :\n null\n }\n {contactNodes.length > 0 ?\n
\n :\n null\n }\n
\n );\n }\n};\n\nexport default injectIntl(ContactList);\n","import React from 'react';\n\nimport Tinode from 'tinode-sdk';\n\nexport default class SearchContacts extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n edited: false,\n search: ''\n };\n\n this.handleSearchChange = this.handleSearchChange.bind(this);\n this.handleSearch = this.handleSearch.bind(this);\n this.handleClear = this.handleClear.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n componentWillUnmount() {\n if (this.state.edited) {\n this.setState({search: '', edited: false});\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n }\n\n handleSearchChange(e) {\n this.setState({search: e.target.value});\n }\n\n handleSearch(e) {\n e.preventDefault();\n var query = this.state.search.trim();\n this.setState({edited: (query.length > 0)});\n this.props.onSearchContacts(query.length > 0 ? query : Tinode.DEL_CHAR);\n }\n\n handleClear(e) {\n e.preventDefault();\n if (this.state.edited) {\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n this.setState({search: '', edited: false});\n }\n\n handleKeyDown(e) {\n if (e.key === 'Enter') {\n this.handleSearch(e);\n } else if (e.key === 'Escape') {\n this.handleClear();\n }\n }\n\n render() {\n return (\n \n );\n }\n};\n","// Forward Menu: message forwarding popup/dropdown menu.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from './contact-list.jsx';\nimport SearchContacts from './search-contacts.jsx';\n\nexport default class ForwardDialog extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n query: null\n };\n\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this.handleClose = this.handleClose.bind(this);\n\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleContactSelected = this.handleContactSelected.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleEscapeKey(e) {\n if (e.keyCode === 27) {\n // Not forwarding the message.\n this.props.hide(false);\n }\n }\n\n handleClose(e) {\n e.preventDefault();\n this.props.hide(false);\n }\n\n handleSearchContacts(query) {\n this.setState({ query: Tinode.isNullValue(query) ? null : query });\n this.props.onSearchContacts(query);\n }\n\n handleContactSelected(uid) {\n this.props.onTopicSelected(uid);\n this.props.hide(true);\n }\n\n render() {\n let contacts = this.state.query != null ? this.props.searchResults : this.props.contacts;\n // Filter out contacts without a 'W' or 'J' permissions as well as the current topic.\n contacts = contacts.filter((c) => {\n return (c.name != this.props.topicSelected) && c.acs.isJoiner() && c.acs.isWriter();\n });\n\n return (\n \n
\n
\n
{\n (search_placeholder) => \n }\n
{\n (not_found_placeholder) => \n }\n
\n
\n );\n }\n}\n","import React from 'react';\n\nexport default class LoadSpinner extends React.PureComponent {\n render() {\n const className = 'load-spinner-box' + (this.props.large ? ' large' : '')\n + (this.props.clear ? ' clear' : '') + (this.props.centered ? ' centered' : '');\n return (this.props.show ?\n : null);\n }\n}\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\nimport LoadSpinner from './load-spinner.jsx';\n\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nimport { AVATAR_SIZE } from '../config.js';\n\nexport default class AvatarUpload extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n source: props.avatar\n };\n\n this.handleFileReceived = this.handleFileReceived.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (this.props.avatar != prevProps.avatar) {\n this.setState({source: this.props.avatar});\n }\n }\n\n handleFileReceived(e) {\n const image = e.target.files[0];\n this.props.onImageUpdated(image.type, URL.createObjectURL(image), image.name);\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n render() {\n // Randomize id value in case more than one AvatarUpload is shown\n // at the same time.\n const randId = 'file-input-avatar-' + ('' + Math.random()).substring(0, 4);\n const className = 'avatar-upload' + (this.props.readOnly ? ' read-only' : '');\n return (\n \n {this.props.readOnly || !this.state.source ?\n null :\n
{e.preventDefault(); this.props.onImageUpdated();}}>\n clear\n }\n {this.state.source ?\n
)})
:\n this.props.readOnly && this.props.uid ?\n
\n \n
\n :\n
{AVATAR_SIZE}×{AVATAR_SIZE}
}\n {this.props.readOnly ? null :\n
}\n {this.props.readOnly ? null :\n
}\n
\n
\n );\n }\n};\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nconst icon_mapping = {'staff': 'verified_user'};\n\nconst messages = defineMessages({\n badge_verified: {\n id: 'badge_verified',\n defaultMessage: 'Verified/official',\n description: 'Explanation of a verified account or topic badge'\n },\n badge_staff: {\n id: 'badge_staff',\n defaultMessage: 'Staff-managed',\n description: 'Explanation of a staff-managed account or topic badge'\n },\n badge_danger: {\n id: 'badge_danger',\n defaultMessage: 'Untrustworthy',\n description: 'Suspicious or untrustworthy account or topic badge'\n },\n});\n\n\n/* Trusted badges with labels: verified, staff, danger */\nclass BadgeList extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let badges = null;\n if (this.props.trustedBadges && this.props.trustedBadges.length > 0) {\n badges = [];\n this.props.trustedBadges.map((b) => {\n const comment = formatMessage(messages['badge_' + b]);\n const style = 'material-icons ' + b + '-color';\n badges.push({icon_mapping[b] || b} {comment}
);\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n\nexport default injectIntl(BadgeList);\n","import React from 'react';\n\n/* BEGIN CheckBox: styled checkbox */\nexport default class CheckBox extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleChange = this.handleChange.bind(this);\n }\n\n handleChange() {\n this.props.onChange(this.props.name, !this.props.checked);\n }\n\n render() {\n return (\n this.props.onChange ? (\n this.props.checked === true ?\n check_box :\n this.props.checked === false ?\n check_box_outline_blank :\n indeterminate_check_box\n ) : (\n this.props.checked ?\n check_box :\n check_box_outline_blank\n )\n );\n }\n}\n/* END CheckBox */\n","// The X menu to be displayed in title bars.\nimport React from 'react';\n\nexport default class MenuCancel extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onCancel();}}>\n close\n \n );\n }\n}\n","import React from 'react';\n\nimport MenuCancel from './menu-cancel.jsx';\n\nexport default class ErrorPanel extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n show: false,\n };\n\n this.hide = this.hide.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.level !== this.props.level) {\n this.setState({\n show: !(!this.props.level)\n });\n }\n }\n\n hide() {\n this.setState({show: false});\n if (this.props.onClearError) {\n this.props.onClearError();\n }\n }\n\n render() {\n const icons = {err: 'error', warn: 'warning', info: 'info'}\n const level = icons[this.props.level] || '';\n const className = 'info-box ' + level;\n return (\n \n );\n }\n};\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\n\nexport default class Chip extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel(this.props.topic, this.props.index);\n }\n\n render() {\n const title = this.props.title || this.props.topic;\n const className = this.props.invalid ? 'chip invalid' : 'chip';\n return (\n \n {this.props.noAvatar ?\n
:\n
\n \n
\n }\n
{title}\n {this.props.onCancel && !this.props.required ?\n
×\n :
}\n
\n );\n }\n};\n","import React from 'react';\n\nimport Chip from './chip.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\n/* BEGIN ChipInput: group membership widget */\nexport default class ChipInput extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = ChipInput.deriveStateFromProps(props);\n this.state.input = '';\n this.state.focused = false;\n\n this.handleTextInput = this.handleTextInput.bind(this);\n this.removeChipAt = this.removeChipAt.bind(this);\n this.handleChipCancel = this.handleChipCancel.bind(this);\n this.handleFocusGained = this.handleFocusGained.bind(this);\n this.handleFocusLost = this.handleFocusLost.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n static deriveStateFromProps(props) {\n return {\n placeholder: props.chips ? '' : props.prompt,\n sortedChips: ChipInput.sortChips(props.chips, props.staticMembers),\n chipIndex: ChipInput.indexChips(props.chips)\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chips != this.props.chips ||\n prevProps.staticMembers != this.props.staticMembers ||\n prevProps.prompt != this.props.prompt) {\n this.setState(ChipInput.deriveStateFromProps(this.props));\n }\n if (!prevState || this.props.chips.length > prevState.sortedChips.length) {\n this.setState({input: ''});\n }\n }\n\n // Map chip index to user name\n static indexChips(chips) {\n const index = {};\n let count = 0;\n chips.map((item) => {\n index[item.user] = count;\n count ++;\n });\n return index;\n }\n\n // Have non-removable chips appear before all other chips.\n static sortChips(chips, keep) {\n const required = [];\n const normal = [];\n chips.map((item) => {\n if (keep && keep.includes(item.user)) {\n required.push(item);\n } else {\n normal.push(item);\n }\n });\n return required.concat(normal);\n }\n\n handleTextInput(e) {\n this.setState({input: e.target.value});\n if (this.props.filterFunc) {\n this.props.filterFunc(e.target.value);\n }\n }\n\n removeChipAt(idx) {\n const removed = this.state.sortedChips[idx];\n this.props.onChipRemoved(removed.user, this.state.chipIndex[removed.user]);\n }\n\n handleChipCancel(item, idx) {\n this.removeChipAt(idx);\n }\n\n handleFocusGained() {\n this.setState({focused: true});\n }\n\n handleFocusLost() {\n this.setState({focused: false});\n if (this.props.onFocusLost) {\n this.props.onFocusLost(this.state.input);\n }\n }\n\n handleKeyDown(e) {\n if (e.key === 'Backspace') {\n if (this.state.input.length == 0 && this.state.sortedChips.length > 0) {\n const at = this.state.sortedChips.length - 1;\n if (this.state.sortedChips[at].user !== this.props.staticMembers) {\n this.removeChipAt(at);\n }\n }\n } else if (e.key === 'Enter') {\n if (this.props.onEnter) {\n this.props.onEnter(this.state.input);\n }\n } else if (e.key === 'Escape') {\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n }\n\n render() {\n const chips = [];\n let count = 0;\n const staticMembers = this.props.staticMembers || [];\n this.state.sortedChips.map((item) => {\n chips.push(\n \n );\n count++;\n });\n const className = \"chip-input\" + (this.state.focused ? ' focused' : '');\n const autoFocus = !(this.props.tabIndex > 0);\n return (\n \n {chips}\n \n
\n );\n }\n};\n","// GroupMembers: control for managing a list of group members.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\nimport ContactList from './contact-list.jsx';\n\nconst messages = defineMessages({\n no_contacts: {\n id: 'no_contacts',\n defaultMessage: 'You have no contacts :-(',\n description: 'Shown in ContactsView when the user has no contacts'\n },\n contacts_not_found_short: {\n id: 'contacts_not_found_short',\n defaultMessage: \"No contacts match ''{query}''\",\n description: 'Shown in ContactsView when search returned no results'\n }\n});\n\nclass GroupManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Array of topic members\n members: props.members,\n index: GroupManager.indexMembers(props.members),\n staticMembers: GroupManager.staticMembers(props.members, props.keepInitialMembers, props.requiredMember),\n contactFilter: '',\n noContactsMessage: props.intl.formatMessage(messages.no_contacts),\n selectedContacts: GroupManager.selectedContacts(props.members)\n };\n\n this.handleContactSelected = this.handleContactSelected.bind(this);\n this.handleMemberRemoved = this.handleMemberRemoved.bind(this);\n this.handleContactFilter = this.handleContactFilter.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static indexMembers(members) {\n let index = {};\n members.map((m) => {\n index[m.user] = {delta: 0, present: true}; // Delta: 0 unchanged, +1 added, -1 removed\n });\n return index;\n }\n\n static staticMembers(members, keepInitial, requiredMember) {\n let stat = [];\n members.map((m) => {\n if (keepInitial || m.user == requiredMember) {\n stat.push(m.user);\n }\n });\n return stat;\n }\n\n static selectedContacts(members) {\n let sel = [];\n members.map((m) => {\n sel.push(m.user);\n });\n return sel;\n }\n\n handleContactSelected(userId, index) {\n let status = this.state.index[userId];\n if (status) {\n if (status.present) {\n // Prevent duplicate members\n return;\n }\n status.delta += 1;\n status.present = true;\n } else {\n status = {delta: 1, present: true};\n }\n\n let m = this.state.members.slice();\n m.push(this.props.contacts[index]);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleMemberRemoved(userId, index) {\n const status = this.state.index[userId];\n if (!status || !status.present) {\n return;\n }\n status.present = false;\n status.delta -= 1;\n\n let m = this.state.members.slice();\n m.splice(index, 1);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleContactFilter(val) {\n const {formatMessage} = this.props.intl;\n const msg = !val ?\n formatMessage(messages.no_contacts) :\n formatMessage(messages.contacts_not_found_short, {query: val});\n\n this.setState({contactFilter: val, noContactsMessage: msg});\n }\n\n static doContactFiltering(filter, values) {\n if (filter) {\n for (let i=0; i= 0) {\n return true;\n }\n }\n return false;\n }\n return true;\n }\n\n handleSubmit() {\n var instance = this;\n var members = [];\n var added = [];\n var removed = [];\n\n var keys = Object.keys(this.state.index);\n keys.map(function(k) {\n if (instance.state.index[k].present) {\n members.push(k);\n }\n\n if (instance.state.index[k].delta > 0) {\n added.push(k);\n } else if (instance.state.index[k].delta < 0) {\n removed.push(k);\n }\n });\n this.props.onSubmit(members, added, removed);\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(GroupManager);\n","// PermissionsEditor: Component for editing permissions\n// \nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport CheckBox from './checkbox.jsx';\nimport Contact from './contact.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\n// Translatable permission names.\nconst messages = defineMessages({\n joiner: {\n id: 'permission_join',\n defaultMessage: \"Join ({val})\",\n description: 'Name of J permission'\n },\n reader: {\n id: 'permission_read',\n defaultMessage: \"Read ({val})\",\n description: 'Name of R permission'\n },\n writer: {\n id: 'permission_write',\n defaultMessage: \"Write ({val})\",\n description: 'Name of W permission'\n },\n preser: {\n id: 'permission_pres',\n defaultMessage: \"Get notified ({val})\",\n description: 'Name of P permission'\n },\n approver: {\n id: 'permission_admin',\n defaultMessage: \"Approve ({val})\",\n description: 'Name of A permission'\n },\n sharer: {\n id: 'permission_share',\n defaultMessage: \"Share ({val})\",\n description: 'Name of S permission'\n },\n deleter: {\n id: 'permission_delete',\n defaultMessage: \"Delete ({val})\",\n description: 'Name of D permission'\n },\n owner: {\n id: 'permission_owner',\n defaultMessage: \"Owner ({val})\",\n description: 'Name of O permission'\n }\n});\n\nclass PermissionsEditor extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n mode: (props.mode || '').replace('N', '')\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(val) {\n let mode = this.state.mode;\n const idx = mode.indexOf(val);\n if (idx == -1) {\n mode += val;\n } else {\n mode = mode.replace(val, '');\n }\n this.setState({mode: mode});\n }\n\n handleSubmit() {\n // Normalize string, otherwise cannot check if mode has changed.\n const mode = (this.state.mode || 'N').split('').sort().join('');\n const before = (this.props.mode || 'N').split('').sort().join('')\n if (mode !== before) {\n this.props.onSubmit(mode);\n } else {\n this.props.onCancel();\n }\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const all = 'JRWPASDO';\n const names = {\n 'J': formatMessage(messages.joiner, {val: 'J'}),\n 'R': formatMessage(messages.reader, {val: 'R'}),\n 'W': formatMessage(messages.writer, {val: 'W'}),\n 'P': formatMessage(messages.preser, {val: 'P'}),\n 'A': formatMessage(messages.approver, {val: 'A'}),\n 'S': formatMessage(messages.sharer, {val: 'S'}),\n 'D': formatMessage(messages.deleter, {val: 'D'}),\n 'O': formatMessage(messages.owner, {val: 'O'})\n };\n\n let skip = this.props.skip || '';\n let mode = this.state.mode;\n let compare = (this.props.compare || '').replace('N', '');\n let items = [];\n for (let i=0; i= 0 && mode.indexOf(c) < 0) {\n // Permission is marked as inactive: hide unchecked permissions, disable checked permissions\n continue;\n }\n items.push(\n \n | {names[c]} | \n {skip.indexOf(c) < 0 ?\n = 0)} onChange={this.handleChange}/>\n :\n = 0)} />\n } | {this.props.compare ? \n = 0)}/>\n | : null}\n
\n );\n }\n\n return (\n \n {this.props.userTitle ?\n
: null}\n
\n
\n {this.props.compare ?\n \n | {this.props.modeTitle} | \n {this.props.compareTitle} | \n
:\n null}\n \n {items}\n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(PermissionsEditor);\n","// Image zoom and crop widget.\n\nimport React from 'react';\n\nconst DEFAULT_MAX_ZOOM = 2.5;\n\nexport default class Cropper extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Coordinates of the top left corner of the image in container coordinates.\n panX: 0,\n panY: 0,\n // Origin of the zoom in image coordinates.\n originX: 0,\n originY: 0,\n // Zoom scale.\n zoom: 1,\n // Zoom min and max.\n minZoom: 0,\n maxZoom: DEFAULT_MAX_ZOOM,\n };\n\n // References to DOM elements for hooking up events and getting screen dimensions.\n // The transparent element which captures mouse moves (moves with the image).\n this.overlay = React.createRef();\n // The image cutout window or view port (static).\n this.cutout = React.createRef();\n // Scaled and translated image preview.\n this.preview = React.createRef();\n // The main container (static).\n this.boundingBox = React.createRef();\n\n // The original image width and height before any scaling.\n this.imageWidth = 0;\n this.imageHeight = 0;\n\n // Mouse position when dragging.\n this.mouseX = 0\n this.mouseY = 0;\n // Length of the previous mouse move when dragging.\n this.prevDistance = 0;\n\n // Bounding rectangles of static elements.\n this.cutoutRect = {};\n this.bBoxRect = {};\n // Center of the bounding box.\n this.originX = 0;\n this.originY = 0;\n\n this.initScaling = this.initScaling.bind(this);\n this.onZoom = this.onZoom.bind(this);\n this.handleZoom = this.handleZoom.bind(this);\n this.mouseDown = this.mouseDown.bind(this);\n this.mouseUp = this.mouseUp.bind(this);\n this.mouseMove = this.mouseMove.bind(this);\n this.mouseTouch = this.mouseTouch.bind(this);\n this.positionAll = this.positionAll.bind(this);\n this.translate = this.translate.bind(this);\n }\n\n componentDidMount() {\n this.overlay.current.addEventListener('mousedown', this.mouseDown, { passive: true });\n this.overlay.current.addEventListener('touchstart', this.mouseDown, { passive: true });\n\n // The rectangle position is in viewport coordinates.\n this.bBoxRect = this.boundingBox.current.getBoundingClientRect();\n // Center of the bounding box in container coordinates.\n this.originX = this.bBoxRect.width / 2;\n this.originY = this.bBoxRect.height / 2;\n\n // The rectangle position is in viewport coordinates.\n this.cutoutRect = this.cutout.current.getBoundingClientRect();\n }\n\n componentWillUnmount() {\n this.overlay.current.removeEventListener('mousedown', this.mouseDown);\n this.overlay.current.removeEventListener('touchstart', this.mouseDown);\n }\n\n // Position all elements.\n positionAll(panX, panY, zoom) {\n // Zoom origin in image preview coordinates.\n this.setState({\n panX: panX,\n panY: panY,\n zoom: zoom,\n originX: this.originX - panX,\n originY: this.originY - panY,\n });\n\n // Pass cut out coordinates to caller.\n const left = (this.originX - panX) * zoom - this.originX;\n const top = (this.originY - panY) * zoom - this.originY;\n this.props.onChange(\n (left + this.cutoutRect.left - this.bBoxRect.left) / zoom,\n (top + this.cutoutRect.top - this.bBoxRect.top) / zoom,\n this.cutoutRect.width / zoom,\n this.cutoutRect.height / zoom,\n zoom);\n }\n\n // Check if new location is within the limits.\n static checkBound(currPan, img, cutout, delta) {\n let nextDiff = Math.min(0, cutout[0] - img[0] - delta, img[1] - cutout[1] + delta);\n if (nextDiff == 0) {\n // Cutout is completely within the image.\n currPan += delta;\n } else if (Math.min(0, cutout[0] - img[0], img[1] - cutout[1]) < nextDiff) {\n // Cutout is outside of the range but closer to the bound.\n currPan += delta;\n }\n return currPan;\n }\n\n initScaling() {\n // Initialize components.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n this.imageWidth = imgRect.width;\n this.imageHeight = imgRect.height;\n\n // Set zoom slider's min and max values. Make sure the scaled image cannot be smaller than the cutout.\n const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);\n this.setState({\n minZoom: minZoom,\n maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)\n });\n\n // Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. \"fill\" not \"fit\".\n const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);\n // Converting from viewport coordinates to container, then panning.\n const panX = this.cutoutRect.left - this.bBoxRect.left -\n (imgRect.width - this.cutoutRect.width) / 2;\n const panY = this.cutoutRect.top - this.bBoxRect.top -\n (imgRect.height - this.cutoutRect.height) / 2;\n\n this.positionAll(panX, panY, zoom);\n }\n\n onZoom(e) {\n this.handleZoom(e.target.value);\n }\n\n handleZoom(zoom) {\n let panX = this.state.panX;\n let panY = this.state.panY;\n\n // Ensure that the image at the new zoom is not outside of the cutout boundaries.\n // Calculate image coordinates at the new zoom.\n const imgLeft = this.originX - (this.originX - panX) * zoom;\n const imgRight = imgLeft + this.imageWidth * zoom;\n // Cutout in the same coordinates as the image.\n const coLeft = this.cutoutRect.left - this.bBoxRect.left;\n const coRight = coLeft + this.cutoutRect.width;\n if (coLeft < imgLeft) {\n panX -= imgLeft - coLeft;\n } else if (coRight > imgRight) {\n panX += coRight - imgRight;\n }\n const imgTop = this.originY - (this.originY - panY) * zoom;\n const imgBottom = imgTop + this.imageHeight * zoom;\n const coTop = this.cutoutRect.top - this.bBoxRect.top;\n const coBottom = coTop + this.cutoutRect.height;\n if (coTop < imgTop) {\n panY -= imgTop - coTop;\n } else if (coBottom > imgBottom) {\n panY += coBottom - imgBottom;\n }\n\n this.positionAll(panX, panY, zoom);\n }\n\n mouseDown(e) {\n if (e.touches) {\n this.mouseX = e.touches[0].pageX;\n this.mouseY = e.touches[0].pageY;\n } else {\n this.mouseX = e.pageX;\n this.mouseY = e.pageY;\n }\n\n window.addEventListener('mousemove', this.mouseMove, { passive: false });\n window.addEventListener('touchmove', this.mouseTouch, { passive: false });\n window.addEventListener('mouseup', this.mouseUp, { passive: true });\n window.addEventListener('touchend', this.mouseUp, { passive: true });\n // Disable text selection in the entire document.\n document.body.style['userSelect'] = 'none';\n }\n\n // Perform image panning.\n translate(pageX, pageY) {\n const dX = pageX - this.mouseX;\n const dY = pageY - this.mouseY;\n\n this.mouseX = pageX;\n this.mouseY = pageY;\n\n // Make sure the image is within the cutout window.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n // Check if the new position is within the boundaries, and if not if it's closer to them.\n let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],\n [this.cutoutRect.left, this.cutoutRect.right], dX);\n let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],\n [this.cutoutRect.top, this.cutoutRect.bottom], dY);\n\n this.positionAll(panX, panY, this.state.zoom);\n }\n\n // Image panning.\n mouseMove(e) {\n e.preventDefault();\n this.translate(e.pageX, e.pageY);\n }\n\n mouseTouch(e) {\n e.preventDefault();\n\n if (e.touches.length == 1) {\n this.translate(e.touches[0].pageX, e.touches[0].pageY);\n return;\n }\n\n // Image zooming by pinching.\n const [touch0, touch1] = e.touches;\n const distance = Math.sqrt((touch0.pageX - touch1.pageX) * (touch0.pageX - touch1.pageX) +\n (touch0.pageY - touch1.pageY) * (touch0.pageY - touch1.pageY));\n\n if (!this.prevDistance) {\n this.prevDistance = distance / this.state.zoom;\n }\n\n let scale = (distance / this.prevDistance);\n this.handleZoom(Math.max(this.minZoom, Math.min(this.maxZoom, scale)));\n }\n\n mouseUp(e) {\n window.removeEventListener('mousemove', this.mouseMove);\n window.removeEventListener('touchmove', this.mouseTouch);\n window.removeEventListener('mouseup', this.mouseUp);\n window.removeEventListener('touchend', this.mouseUp);\n // Re-enable text selection.\n document.body.style['userSelect'] = '';\n\n this.positionAll(this.state.panX, this.state.panY, this.state.zoom);\n }\n\n render() {\n // transform3d: position and scale.\n const t3d = `translate3d(${this.state.panX}px, ${this.state.panY}px, 0) scale(${this.state.zoom})`;\n // transformOrigin: zoom origin.\n const orig = `${this.state.originX}px ${this.state.originY}px`;\n\n // Overlay position and size are exactly equal to the position and size of the transformed image\n // except it uses different coordinates.\n const overlay = {\n top: `${this.originY - this.state.originY * this.state.zoom}px`,\n left: `${this.originX - this.state.originX * this.state.zoom}px`,\n width: `${this.imageWidth * this.state.zoom}px`,\n height: `${this.imageHeight * this.state.zoom}px`\n };\n return (\n \n
\n

\n
\n
\n
\n
\n \n
\n
\n );\n }\n}\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Cropper from './cropper.jsx';\nimport { imageCrop } from '../lib/blob-helpers.js';\n\nexport default class AvatarCrop extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n top: 0,\n left: 0,\n width: 0,\n height: 0,\n scale: 1\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleChange = this.handleChange.bind(this);\n }\n\n // Cut out in image coordinates and desired scale factor.\n handleChange(left, top, width, height, scale) {\n this.setState({\n left: left,\n top: top,\n width: width,\n height: height,\n scale: scale\n });\n }\n\n handleSubmit() {\n imageCrop(this.props.mime, this.props.avatar,\n this.state.left, this.state.top, this.state.width, this.state.height,\n this.state.scale)\n .then(img => {\n this.props.onSubmit(img.mime, img.blob, img.width, img.height);\n })\n .catch(err => {\n this.props.onError(err);\n });\n }\n\n render() {\n return (\n \n
\n \n
\n
\n {this.props.onCancel ?\n \n : null}\n \n
\n
\n );\n }\n};\n","// Password with a visiblity toggle.\nimport React from 'react';\n\nexport default class VisiblePassword extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n value: this.props.value,\n visible: false\n };\n\n this.handleVisibility = this.handleVisibility.bind(this);\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value});\n if (this.props.onChange) {\n this.props.onChange(e);\n }\n }\n\n handleVisibility(e) {\n e.preventDefault();\n this.setState({visible: !this.state.visible});\n }\n\n handleKeyDown(e) {\n if (e.keyCode == 27) {\n // Escape pressed\n this.setState({value: this.props.value, visible: false});\n if (this.props.onFinished) {\n this.props.onFinished();\n }\n } else if (e.keyCode == 13) {\n // Enter pressed\n this.handleEditingFinished();\n }\n }\n\n handleEditingFinished(e) {\n if (e) {\n let currentTarget = e.currentTarget;\n setTimeout(() => {\n if (!currentTarget.contains(document.activeElement)) {\n if (this.props.onFinished) {\n this.props.onFinished(this.state.value);\n }\n }\n }, 0);\n } else if (this.props.onFinished) {\n this.props.onFinished(this.state.value.trim());\n }\n }\n\n render() {\n return (\n \n \n \n \n {this.state.visible ? 'visibility' : 'visibility_off'}\n \n \n
\n );\n }\n}\n","// In-place text editor. Shows text with an icon which toggles it into an input field.\nimport React from 'react';\n\nimport VisiblePassword from './visible-password.jsx';\n\nexport default class InPlaceEdit extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n this.state = {\n active: props.active,\n initialValue: props.value || '',\n value: props.value || ''\n };\n\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleStartEditing = this.handleStartEditing.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n this.handlePasswordFinished = this.handlePasswordFinished.bind(this);\n }\n\n componentDidUpdate(prevProps, prevState) {\n // If text has changed while in read mode, update text and discard changes.\n // Ignore update if in edit mode.\n const newValue = this.props.value || '';\n if (prevState.initialValue != newValue && !prevState.active) {\n this.setState({\n initialValue: newValue,\n value: newValue\n });\n }\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value || ''});\n }\n\n handleKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({value: this.props.value, active: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleEditingFinished(e);\n }\n }\n\n handleStartEditing() {\n if (!this.props.readOnly) {\n this.setState({active: true}, () => {\n if (this.selfRef.current) {\n this.selfRef.current.focus();\n }\n });\n }\n }\n\n handleEditingFinished(event) {\n const value = this.state.value.trim();\n if (this.props.required && (!event.target.checkValidity() || !value)) {\n // Empty input\n this.setState({value: this.props.value, active: false});\n return;\n }\n this.setState({active: false});\n if ((value || this.props.value) && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n handlePasswordFinished(value) {\n this.setState({active: false});\n if (value && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n render() {\n if (!this.state.active) {\n let spanText = this.props.type == 'password' ? '••••••••' : this.state.value;\n let spanClass = 'in-place-edit' + (this.props.readOnly ? ' disabled' : '');\n if (!spanText) {\n spanText = this.props.placeholder;\n spanClass += ' placeholder';\n }\n if (!this.props.multiline || this.props.multiline == 1) {\n spanClass += ' short';\n }\n\n return (\n {spanText}\n );\n }\n\n let element;\n const attr = {};\n if (this.props.type == 'password') {\n element = VisiblePassword;\n attr.onFinished = this.handlePasswordFinished;\n } else {\n if (this.props.multiline > 1) {\n element = 'textarea';\n attr.rows = this.props.multiline;\n attr.className = 'inplace-edit';\n } else {\n element = 'input';\n attr.type = this.props.type || 'text';\n }\n attr.value = this.state.value;\n attr.onChange = this.handeTextChange;\n attr.onKeyDown = this.handleKeyDown;\n attr.onBlur = this.handleEditingFinished;\n }\n attr.placeholder = this.props.placeholder;\n attr.required = this.props.required ? 'required' : '';\n attr.autoComplete = this.props.autoComplete;\n attr.autoFocus = true;\n attr.ref = this.selfRef;\n\n return React.createElement(element, attr, null);\n }\n};\n","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { MAX_TAG_COUNT, MAX_TAG_LENGTH, MIN_TAG_LENGTH } from '../config.js';\nimport { arrayEqual } from '../lib/utils.js';\n\nexport default class TagManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tags: this.props.tags || [],\n tagInput: '',\n activated: this.props.activated\n };\n\n this.handleTagInput = this.handleTagInput.bind(this);\n this.handleAddTag = this.handleAddTag.bind(this);\n this.handleRemoveTag = this.handleRemoveTag.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n const tags = nextProps.tags || [];\n if (!arrayEqual(tags, prevState.tags) && !prevState.activated) {\n return {tags: tags};\n }\n return null;\n }\n\n handleTagInput(text) {\n this.setState({tagInput: text});\n if (text.length > 0) {\n const last = text[text.length-1];\n if (text[0] == '\"') {\n // This is a quoted string.\n if (text.length > 1 && last == '\"') {\n this.handleAddTag(text.substring(1, text.length-1));\n }\n } else if (last == ',' || last == ' ' || last == ';' || last == '\"') {\n // User entered ',', ' ' or ';'\n this.handleAddTag(text.substring(0, text.length-1).trim());\n }\n }\n }\n\n handleAddTag(tag) {\n const maxTagCount = this.props.tinode.getServerLimit('maxTagCount', MAX_TAG_COUNT);\n\n if (tag.length > 0 && this.state.tags.length < maxTagCount) {\n const tags = this.state.tags.slice(0);\n tags.push(tag);\n\n this.setState({tags: tags, tagInput: ''});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n return tags;\n }\n return this.state.tags;\n }\n\n handleRemoveTag(tag, index) {\n const tags = this.state.tags.slice(0);\n tags.splice(index, 1);\n this.setState({tags: tags});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n }\n\n handleSubmit() {\n // Add unprocessed input to tags, submit the list.\n this.props.onSubmit(this.handleAddTag(this.state.tagInput.trim()));\n this.setState({activated: false, tags: this.props.tags || []});\n }\n\n handleCancel() {\n this.setState({activated: false, tagInput: '', tags: this.props.tags || []});\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n\n render() {\n const minTagLength = this.props.tinode.getServerLimit('minTagLength', MIN_TAG_LENGTH);\n const maxTagLength = this.props.tinode.getServerLimit('maxTagLength', MAX_TAG_LENGTH);\n\n let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < minTagLength || tag.length > maxTagLength)});\n });\n } else {\n this.state.tags.map((tag) => {\n tags.push({tag});\n });\n if (tags.length == 0) {\n tags = (\n \n \n \n );\n }\n }\n return (\n \n
\n \n
\n {this.state.activated ?\n
\n
{\n (add_tags_prompt) => \n }\n {this.props.onSubmit || this.props.onCancel ?\n
\n \n \n
\n : null}\n
\n :\n
\n }\n
\n );\n }\n};\n","// Widget for editing topic description.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarCrop from './avatar-crop.jsx';\nimport AvatarUpload from './avatar-upload.jsx';\nimport InPlaceEdit from './in-place-edit.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,\n MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\nimport { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nexport default class TopicDescEdit extends React.Component {\n constructor(props) {\n super(props);\n\n const topic = this.props.tinode.getTopic(this.props.topic);\n const acs = topic.getAccessMode();\n this.state = {\n isMe: Tinode.isMeTopicName(this.props.topic),\n owner: acs && acs.isOwner(),\n fullName: topic.public ? topic.public.fn : undefined,\n private: topic.private ? topic.private.comment : null,\n description: topic.public ? topic.public.note : undefined,\n avatar: makeImageUrl(topic.public ? topic.public.photo : null),\n tags: topic.tags() || [],\n newAvatar: null,\n newAvatarMime: null\n };\n\n this.previousOnTags = null;\n\n this.tnNewTags = this.tnNewTags.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handleImageUpdated = this.handleImageUpdated.bind(this);\n this.handleAvatarCropped = this.handleAvatarCropped.bind(this);\n this.handlePrivateUpdate = this.handlePrivateUpdate.bind(this);\n this.handleDescriptionUpdate = this.handleDescriptionUpdate.bind(this);\n this.uploadAvatar = this.uploadAvatar.bind(this);\n this.handleAvatarCropCancel = this.handleAvatarCropCancel.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n }\n\n componentDidMount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n this.previousOnTags = topic.onTagsUpdated;\n topic.onTagsUpdated = this.tnNewTags;\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n topic.onTagsUpdated = this.previousOnTags;\n }\n\n tnNewTags(tags) {\n this.setState({tags: tags});\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn && this.state.fullName !== fn) {\n this.setState({fullName: fn});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(fn, null));\n }\n }\n\n handlePrivateUpdate(comment) {\n comment = comment.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.private !== comment) {\n this.setState({private: comment});\n this.props.onUpdateTopicDesc(this.props.topic, null, comment || Tinode.DEL_CHAR);\n }\n }\n\n handleDescriptionUpdate(desc) {\n desc = desc.trim().substring(0, MAX_TOPIC_DESCRIPTION_LENGTH);\n if (desc) {\n this.setState({description: desc});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, null, null, desc));\n }\n }\n\n // AvatarUpload widget has changed the avatar: received a new file or deleted the avatar.\n handleImageUpdated(mime, img) {\n // This will show the AvatarCropView is img is not null.\n this.setState({newAvatar: img, newAvatarMime: mime});\n if (!img) {\n // Deleting the avatar.\n this.setState({avatar: null});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, Tinode.DEL_CHAR));\n }\n }\n\n // AvatarCropView calls this method when the user has cropped the image.\n handleAvatarCropped(mime, blob, width, height) {\n const url = blob ? URL.createObjectURL(blob) : null;\n this.setState({avatar: url, newAvatar: null, newAvatarMime: null});\n if (blob) {\n this.uploadAvatar(mime, blob, width, height);\n }\n }\n\n // Utility method for converting cropped avatar blob to bytes for sending inband or\n // for uploading it to the server out of band.\n uploadAvatar(mime, blob, width, height) {\n const readyToUpload = (image) => {\n let {mime, blob} = image;\n if (blob.size > MAX_AVATAR_BYTES) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n\n this.setState({uploading: true});\n uploader.upload(blob)\n .then((url) => {\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, url));\n })\n .catch((err) => {\n this.props.onError(err, 'err');\n })\n .finally(() => {\n this.setState({uploading: false});\n });\n } else {\n this.setState({uploading: true});\n // Convert blob to base64-encoded bits.\n blobToBase64(blob)\n .then(b64 => {\n const du = makeImageUrl({data: b64.bits, type: mime});\n this.setState({source: du});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, du));\n this.setState({uploading: false});\n });\n }\n };\n\n if (width > AVATAR_SIZE || height > AVATAR_SIZE || width != height) {\n // Avatar is not square or too large even after cropping. Shrink it and make square.\n imageScaled(blob, AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true)\n .then(scaled => readyToUpload(scaled))\n .catch(err => this.props.onError(err, 'err'));\n } else {\n readyToUpload({mime: mime, blob: blob, width: width, height: height});\n }\n }\n\n handleAvatarCropCancel(img) {\n this.setState({newAvatar: null, newAvatarMime: null});\n }\n\n handleTagsUpdated(tags) {\n // Check if tags have actually changed.\n if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n return;\n }\n this.props.onUpdateTags(tags);\n }\n\n render() {\n if (this.state.newAvatar) {\n return (\n \n );\n }\n\n const editable = this.state.isMe || this.state.owner;\n\n return (\n <>\n \n
\n \n \n {this.state.isMe ?\n
\n
\n
\n {\n (full_name_placeholder) => \n }\n
\n
\n :\n <>\n
\n
\n
\n {\n (group_name_placeholder) => \n }\n
\n
\n
\n
\n \n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n >\n }\n {editable || this.state.description ?\n
\n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n : null\n }\n
\n {editable ?\n <>\n \n {\n (title_tag_manager) => \n }\n >\n : null\n }\n >\n );\n }\n};\n","// View for editing topic (including 'me' topic) parameters.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport TopicDescEdit from '../widgets/topic-desc-edit.jsx';\n\nimport { arrayEqual, asEmail, asPhone } from '../lib/utils.js';\n\nexport default class TopicCommon extends React.Component {\n constructor(props) {\n super(props);\n\n const topic = this.props.tinode.getTopic(this.props.topic);\n const acs = topic.getAccessMode();\n this.state = {\n isMe: this.props.topic == 'me',\n owner: acs && acs.isOwner(),\n credentials: (topic.getCredentials ? topic.getCredentials() : null) || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n tags: []\n };\n\n this.previousTagsUpdated = undefined;\n\n this.onTagsUpdated = this.onTagsUpdated.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.tnCredsUpdated = this.tnCredsUpdated.bind(this);\n this.handleCredChange = this.handleCredChange.bind(this);\n this.handleCredKeyDown = this.handleCredKeyDown.bind(this);\n this.handleCredEntered = this.handleCredEntered.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n topic.onCredsUpdated = this.tnCredsUpdated;\n\n if (topic.onTagsUpdated != this.onTagsUpdated) {\n if (topic.getType() == 'grp') {\n this.previousTagsUpdated = topic.onTagsUpdated;\n topic.onTagsUpdated = this.onTagsUpdated;\n } else {\n this.previousTagsUpdated = undefined;\n }\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n topic.onCredsUpdated = undefined;\n topic.onTagsUpdated = this.previousTagsUpdated;\n }\n\n tnCredsUpdated(creds) {\n this.setState({credentials: creds || []});\n }\n\n handleCredChange(e) {\n this.setState({newCred: e.target.value, addCredInvalid: false});\n }\n\n handleCredKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({newCred: '', addCredActive: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleCredEntered(e);\n }\n }\n\n handleCredEntered(e) {\n const value = this.state.newCred.trim();\n if (!value) {\n this.setState({addCredActive: false, addCredInvalid: false});\n return;\n }\n\n let val = asPhone(value);\n let method;\n if (val) {\n method = 'tel';\n } else {\n val = asEmail(value);\n if (val) {\n method = 'email';\n }\n }\n if (method) {\n this.props.onCredAdd(method, val);\n this.setState({addCredActive: false, newCred: ''});\n } else {\n this.setState({addCredInvalid: true});\n }\n }\n\n // Server informs that the tags have been updated.\n onTagsUpdated(tags) {\n this.setState({tags: tags});\n\n if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {\n this.previousTagsUpdated(tags);\n }\n }\n\n // Request server to change tags.\n handleTagsUpdated(tags) {\n if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n this.props.onTopicTagsUpdateRequest(this.props.topic, tags);\n }\n }\n\n render() {\n const credentials = [];\n if (this.state.isMe) {\n this.state.credentials.map((cred) => {\n credentials.push(\n \n );\n });\n }\n\n return (\n \n
\n {this.state.isMe ?\n <>\n
\n
\n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n >\n : null\n }\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from './in-place-edit.jsx';\n\nimport { NO_ACCESS_MODE } from '../config.js';\n\nconst messages = defineMessages({\n clear_messages: {\n id: 'action_clear_messages',\n defaultMessage: 'Clear Messages',\n description: 'Flat button [Clear Messages] (soft-delete messages)'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n delete_messages: {\n id: 'action_delete_messages',\n defaultMessage: 'Clear Messages for All',\n description: 'Flat button [Clear for All] (hard-delete all messages)'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n topic_delete: {\n id: 'topic_delete',\n defaultMessage: 'Delete Conversation',\n description: 'Alert title when deleting the topic.'\n },\n topic_delete_warning: {\n id: 'topic_delete_warning',\n defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',\n description: 'Alert warning when deleting entire topic'\n },\n leave_chat: {\n id: 'action_leave_chat',\n defaultMessage: 'Leave Conversation',\n description: 'Flat button [Leave Conversation]'\n },\n leave_chat_warning: {\n id: 'leave_chat_warning',\n defaultMessage: 'Are you sure you want to leave this conversation?',\n description: 'Alert dialog warning when unsubscribing from a chat.'\n },\n block_contact: {\n id: 'action_block_contact',\n defaultMessage: \"Block Contact\",\n description: \"Flat button [Block Contact]\"\n },\n block_contact_warning: {\n id: 'block_contact_warning',\n defaultMessage: 'Are you sure you want to block this contact?',\n description: 'Alert dialog warning when blocking a contact.'\n },\n report_chat: {\n id: 'action_report_chat',\n defaultMessage: 'Report Conversation',\n description: 'Flat button [Report Group]'\n },\n report_chat_warning: {\n id: 'report_chat_warning',\n defaultMessage: 'Are you sure you want to block and report this conversation?',\n description: 'Alert dialog warning when reporting a conversation for abuse'\n },\n other_user: {\n id: 'label_other_user',\n defaultMessage: 'Other',\n description: 'Label for the other user when the user is unnamed'\n },\n});\n\nclass TopicSecurity extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleDeleteTopic = this.handleDeleteTopic.bind(this);\n this.handleDeleteMessages = this.handleDeleteMessages.bind(this);\n this.handleLeave = this.handleLeave.bind(this);\n this.handleBlock = this.handleBlock.bind(this);\n this.handleReport = this.handleReport.bind(this);\n }\n\n handleDeleteTopic(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.topic_delete), // title\n formatMessage(messages.topic_delete_warning), // content\n (() => { this.props.onDeleteTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleDeleteMessages(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(this.props.deleter ? messages.delete_messages : messages.clear_messages), // title\n formatMessage(this.props.deleter ? messages.delete_messages_warning : messages.clear_messages_warning), // content\n (() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleLeave(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.leave_chat), // title\n formatMessage(messages.leave_chat_warning), // content\n (() => { this.props.onLeaveTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleBlock(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.block_contact), // title\n formatMessage(messages.block_contact_warning), // content\n (() => { this.props.onBlockTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleReport(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.report_chat), // title\n formatMessage(messages.report_chat_warning), // content\n (() => { this.props.onReportTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n
\n
\n
\n {this.props.groupTopic ?\n <>\n
\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('want')}}>\n {this.props.access}\n \n
\n {!this.props.channel ?\n
\n
\n \n
\n
\n
Auth: {\n e.preventDefault();\n if (this.props.owner) {\n this.props.onLaunchPermissionsEditor('auth');\n }\n }}>{this.props.auth}\n
\n
Anon: {\n e.preventDefault();\n if (this.props.owner) {\n this.props.onLaunchPermissionsEditor('anon');\n }\n }}>{this.props.anon}\n
\n
\n
\n :\n null\n }\n >\n :\n
\n
\n \n
\n
\n
\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('want')}}>\n {this.props.access}\n \n
\n
{this.props.fullName ? this.props.fullName : formatMessage(messages.other_user)}:\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('given')}}>\n {this.props.modeGiven2}\n \n
\n
\n
\n }\n
\n
\n );\n }\n};\n\nexport default injectIntl(TopicSecurity);\n","// InfoView: panel with topic/user info.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport ContactList from '../widgets/contact-list.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupManager from '../widgets/group-manager.jsx';\nimport MenuCancel from '../widgets/menu-cancel.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TopicCommon from '../widgets/topic-common.jsx';\nimport TopicSecurity from '../widgets/topic-security.jsx';\n\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH, NO_ACCESS_MODE } from '../config.js';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n info: {\n id: 'panel_title_info',\n description: 'Title for InfoView',\n defaultMessage: 'Info'\n },\n general: {\n id: 'panel_title_general',\n description: 'Title for TopicCommon.',\n defaultMessage: 'General'\n },\n security: {\n id: 'panel_title_security',\n description: 'Title for TopicSecirity and AccSecurity.',\n defaultMessage: 'Security'\n },\n members: {\n id: 'panel_title_members',\n description: 'Title for managing group members view.',\n defaultMessage: 'Members'\n },\n crop: {\n id: 'panel_title_crop',\n description: 'Title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n perm_want: {\n id: 'requested_permissions',\n defaultMessage: 'Requested',\n description: 'Title for permissions'\n },\n perm_given: {\n id: 'granted_permissions',\n defaultMessage: 'Granted',\n description: 'Title for permissions'\n },\n perm_auth: {\n id: 'permissions_authenticated',\n defaultMessage: 'Authenticated',\n description: 'Title for editing default authenticated permissions'\n },\n perm_anon: {\n id: 'permissions_anonymous',\n defaultMessage: 'Anonymous',\n description: 'Title for editing default anonymous permissions'\n },\n perm_user: {\n id: 'permissions_user',\n defaultMessage: 'User\\'s Permissions',\n description: 'Title for editing user\\'s permissions'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n});\n\nfunction _clip(str, length) {\n return str && str.substring(0, length);\n}\n\nclass InfoView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n topic: null,\n owner: false,\n admin: false,\n sharer: false,\n deleter: false,\n muted: false,\n address: null,\n groupTopic: undefined,\n channel: undefined,\n fullName: undefined,\n description: undefined,\n avatar: null,\n private: null,\n selectedContact: null,\n access: null,\n modeGiven: null,\n modeWant: null,\n modeGiven2: null, // P2P topic, the other user mode given\n modeWant2: null, // P2P topic, the other user mode want\n auth: null,\n anon: null,\n contactList: [],\n trustedBadges: [],\n previousMetaDesc: undefined,\n previousSubsUpdated: undefined,\n };\n\n this.resetSubs = this.resetSubs.bind(this);\n this.resetDesc = this.resetDesc.bind(this);\n this.resetTags = this.resetTags.bind(this);\n this.onMetaDesc = this.onMetaDesc.bind(this);\n this.onSubsUpdated = this.onSubsUpdated.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleMuted = this.handleMuted.bind(this);\n this.handleUnarchive = this.handleUnarchive.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleShowAddMembers = this.handleShowAddMembers.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleMemberSelected = this.handleMemberSelected.bind(this);\n this.handleContextMenu = this.handleContextMenu.bind(this);\n this.handleBackNavigate = this.handleBackNavigate.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n if (this.onMetaDesc != topic.onMetaDesc) {\n this.previousMetaDesc = topic.onMetaDesc;\n topic.onMetaDesc = this.onMetaDesc;\n\n this.previousSubsUpdated = topic.onSubsUpdated;\n topic.onSubsUpdated = this.onSubsUpdated;\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n this.resetDesc(topic, props);\n this.resetSubs(topic, props);\n this.resetTags(topic);\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.setState({topic: null});\n topic.onMetaDesc = this.previousMetaDesc;\n topic.onSubsUpdated = this.previousSubsUpdated;\n }\n\n resetSubs(topic, props) {\n const newState = {contactList: []};\n if (topic.getType() == 'p2p') {\n // Fetch the other party in the p2p conversation.\n // Topic may not be ready yet, so check if user is found.\n const user2 = topic.subscriber(props.topic);\n if (user2) {\n newState.modeGiven2 = user2.acs.getGiven();\n newState.modeWant2 = user2.acs.getWant();\n } else {\n newState.modeGiven2 = NO_ACCESS_MODE;\n newState.modeWant2 = NO_ACCESS_MODE;\n }\n } else {\n topic.subscribers((sub) => {\n newState.contactList.push(sub);\n }, this);\n }\n\n this.setState(newState);\n }\n\n resetDesc(topic, props) {\n const defacs = topic.getDefaultAccess() || {};\n const acs = topic.getAccessMode();\n\n const badges = [];\n if (topic.trusted) {\n for (const [key, val] of Object.entries(topic.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n }\n\n this.setState({\n owner: acs && acs.isOwner(),\n admin: acs && acs.isAdmin(),\n sharer: acs && acs.isSharer(),\n deleter: acs && acs.isDeleter(),\n muted: acs && acs.isMuted(),\n\n fullName: _clip(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),\n description: _clip(topic.public ? topic.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),\n avatar: makeImageUrl(topic.public ? topic.public.photo : null),\n trustedBadges: badges,\n private: _clip(topic.private ? topic.private.comment : null, MAX_TITLE_LENGTH),\n archived: topic.isArchived(),\n address: topic.name,\n groupTopic: topic.isGroupType(),\n channel: topic.isChannelType() || topic.chan,\n access: acs ? acs.getMode() : undefined,\n modeGiven: acs ? acs.getGiven() : undefined,\n modeWant: acs ? acs.getWant() : undefined,\n auth: defacs.auth,\n anon: defacs.anon\n });\n }\n\n resetTags(topic) {\n if (topic.getType() != 'grp') {\n return;\n }\n\n const acs = topic.getAccessMode();\n if (acs && acs.isOwner()) {\n // Requesting tags: owner is editing the topic.\n topic.getMeta(topic.startMetaQuery().withTags().build());\n }\n }\n\n onMetaDesc(desc) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetDesc(topic, this.props);\n\n if (this.previousMetaDesc && this.previousMetaDesc != this.onMetaDesc) {\n this.previousMetaDesc(desc);\n }\n }\n\n onSubsUpdated(subs) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetSubs(topic, this.props);\n\n if (this.previousSubsUpdated && this.previousSubsUpdated != this.onSubsUpdated) {\n this.previousSubsUpdated(subs);\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onTopicDescUpdate(this.props.topic, theCard(null, img || Tinode.DEL_CHAR), null);\n }\n\n handleMuted(ignored, checked) {\n this.setState({muted: checked});\n this.props.onChangePermissions(this.props.topic, checked ? '-P' : '+P');\n }\n\n handleUnarchive(ignored, ignored2) {\n this.props.onTopicUnArchive(this.props.topic);\n }\n\n handlePermissionsChanged(which, perm) {\n switch (which) {\n case 'auth':\n this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {auth: perm});\n break;\n case 'anon':\n this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {anon: perm});\n break;\n case 'mode':\n case 'want':\n this.props.onChangePermissions(this.props.topic, perm);\n break;\n case 'given':\n this.props.onChangePermissions(this.props.topic, perm, this.props.topic);\n break;\n case 'user':\n this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited);\n break;\n }\n\n this.handleBackNavigate();\n }\n\n handleLaunchPermissionsEditor(which, uid) {\n const {formatMessage} = this.props.intl;\n let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar;\n switch (which) {\n case 'mode':\n toEdit = this.state.access;\n break;\n case 'want':\n toEdit = this.state.modeWant;\n toCompare = this.state.modeGiven;\n if (this.state.owner) {\n // Don't allow owner to unset 'O' permission. All other permissions are OK.\n toSkip = 'O';\n } else {\n // Allow accepting any of 'ASDO' permissions but don't allow asking for them.\n toSkip = Tinode.AccessMode.encode(Tinode.AccessMode.diff('ASDO', this.state.modeGiven));\n if (this.state.channel) {\n // Channels are read-only.\n toSkip += 'W';\n }\n }\n titleEdit = formatMessage(messages.perm_want);\n titleCompare = formatMessage(messages.perm_given);\n break;\n case 'given':\n toEdit = this.state.modeGiven2;\n toCompare = this.state.modeWant2;\n toSkip = this.state.groupTopic ? (this.state.owner ? '' : 'O') : 'ASDO';\n titleEdit = formatMessage(messages.perm_given);\n titleCompare = formatMessage(messages.perm_want);\n break;\n case 'auth':\n toEdit = this.state.auth;\n toSkip = 'O';\n break;\n case 'anon':\n toEdit = this.state.anon;\n toSkip = 'O';\n break;\n case 'user': {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(uid);\n if (!user || !user.acs) {\n return;\n }\n toEdit = user.acs.getGiven();\n toCompare = user.acs.getWant();\n toSkip = this.state.owner ? '' : 'O';\n titleEdit = formatMessage(messages.perm_given);\n titleCompare = formatMessage(messages.perm_want);\n if (user.public) {\n userTitle = user.public.fn;\n userAvatar = user.public.photo;\n }\n break;\n }\n default:\n console.error(\"Unknown permission editing mode '\" + which + \"'\");\n return;\n }\n this.setState({\n userPermissionsEdited: uid,\n userPermissionsTitle: userTitle,\n userPermissionsAvatar: userAvatar,\n editedPermissions: toEdit,\n immutablePermissions: toCompare,\n editedPermissionsTitle: titleEdit,\n immutablePermissionsTitle: titleCompare,\n editedPermissionsSkipped: toSkip,\n });\n this.props.onNavigate(`perm/${which}`);\n }\n\n handleShowAddMembers(e) {\n e.preventDefault();\n this.props.onInitFind();\n this.props.onNavigate('members');\n }\n\n handleMemberUpdateRequest(members, added, removed) {\n this.props.onMemberUpdateRequest(this.props.topic, added, removed);\n this.props.onNavigate('info');\n }\n\n handleMemberSelected(uid) {\n this.setState({selectedContact: uid});\n }\n\n handleBackNavigate() {\n const args = (this.props.panel || 'info').split('/');\n if (args[0] == 'info') {\n this.props.onNavigate(null);\n } else if (args[0] == 'perm') {\n if (args[1] == 'user') {\n this.props.onNavigate('info');\n } else {\n this.props.onNavigate('security');\n }\n } else {\n this.props.onNavigate('info');\n }\n }\n\n handleContextMenu(params) {\n const {formatMessage} = this.props.intl;\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(params.topicName);\n if (!user || !user.acs) {\n return;\n }\n\n const isMe = this.props.tinode.isMe(params.topicName);\n const menuItems = [\n {title: formatMessage(messages.edit_permissions), handler: () => {\n this.handleLaunchPermissionsEditor(isMe ? 'want' : 'user', params.topicName);\n }}\n ];\n if (!isMe) {\n menuItems.push('member_delete');\n }\n menuItems.push(user.acs.isMuted() ? 'member_unmute' : 'member_mute');\n if (!isMe) {\n menuItems.push(user.acs.isJoiner() ? 'member_block' : 'member_unblock');\n }\n this.props.showContextMenu({\n topicName: this.props.topic,\n x: params.x,\n y: params.y,\n user: params.topicName}, menuItems);\n }\n\n render() {\n const args = (this.props.panel || 'info').split('/');\n const view = args[0];\n args.shift();\n\n const {formatMessage} = this.props.intl;\n const panelTitle = formatMessage((view == 'perm' ? messages['perm_' + args[0]] : messages[view])\n || messages['info']);\n\n return (\n \n
\n
{panelTitle}
\n
\n \n
\n
\n {this.props.displayMobile ?\n
: null}\n {view == 'members' ?\n
\n :\n view == 'perm' && args.length > 0 ?\n
this.handlePermissionsChanged(args[0], mode)}\n onCancel={this.handleBackNavigate} />\n :\n view == 'general' ?\n \n :\n view == 'security' ?\n \n :\n \n
\n
\n
\n \n \n
\n {this.state.archived ?\n
\n \n \n
\n :\n null\n }\n
\n
\n {this.state.groupTopic && this.state.sharer ?\n <>\n
\n
\n \n
\n
\n
{\n (no_members) => \n }\n >\n :\n null\n }\n
\n }\n \n );\n }\n};\n\nexport default injectIntl(InfoView);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\nimport { bytesToHumanSize } from '../lib/strformat.js';\nimport { isUrlRelative, sanitizeUrl } from '../lib/utils.js';\n\nexport default class Attachment extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n downloader: null,\n progress: 0\n };\n\n this.downloadFile = this.downloadFile.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n downloadFile(url, filename, mimetype) {\n if (!url) {\n this.props.onError(\"Invalid download URL '\" + url + \"'\");\n return;\n }\n\n const downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype,\n (loaded) => {\n this.setState({progress: loaded / this.props.size});\n },\n (err) => {\n this.props.onError(err, 'err');\n }\n ).then(() => {\n this.setState({downloader: null, progress: 0});\n }).catch((err) => {\n if (err) {\n this.props.onError(\"Error downloading file: \" + err.message, 'err');\n }\n this.setState({downloader: null, progress: 0});\n });\n }\n\n handleCancel() {\n if (this.props.uploading) {\n this.props.onCancelUpload();\n } else if (this.state.downloader) {\n this.state.downloader.cancel();\n }\n }\n\n render() {\n let filename = this.props.filename || 'file_attachment';\n if (filename.length > 36) {\n filename = filename.substr(0, 16) + '...' + filename.substr(-16);\n }\n let size = this.props.size > 0 ?\n ({bytesToHumanSize(this.props.size)}) :\n null;\n\n // Detect if the download URL is relative or absolute.\n // If the URL is relative use LargeFileHelper to attach authentication\n // credentials to the request.\n let url, helperFunc;\n if (!this.props.uploading && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) {\n // Relative URL. Use download helper.\n url = '#';\n helperFunc = (e) => {\n e.preventDefault();\n this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);\n };\n } else {\n url = sanitizeUrl(this.props.downloadUrl);\n helperFunc = null;\n }\n const downloadWidget = <>file_download >;\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploading || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\n};\n","// Received/read indicator.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { deliveryMarker } from '../lib/utils.js';\n\nconst messages = defineMessages({\n message_sending: {\n id: 'message_sending',\n defaultMessage: 'sending...',\n description: 'Message being sent, in place of time stamp'\n },\n message_sending_failed: {\n id: 'message_sending_failed',\n defaultMessage: 'failed',\n description: 'Failed to send message, in place of time stamp'\n }\n});\n\nclass ReceivedMarker extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let timestamp;\n if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {\n timestamp = formatMessage(messages.message_sending);\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n timestamp = formatMessage(messages.message_sending_failed);\n } else {\n timestamp = this.props.timestamp.toLocaleTimeString(this.props.intl.locale, { timeStyle: 'short' });\n }\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n\n return (\n \n {timestamp}{'\\u00a0'}{marker}\n \n );\n }\n};\n\nexport default injectIntl(ReceivedMarker);\n","// Single chat message bubble, sent or received.\n\nimport React from 'react';\nimport { FormattedMessage, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport Attachment from './attachment.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport ReceivedMarker from './received-marker.jsx'\n\nimport { fullFormatter } from '../lib/formatters.js';\nimport { sanitizeUrl } from '../lib/utils.js';\n\nclass BaseChatMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n progress: 0\n };\n\n if (props.uploader) {\n props.uploader.onProgress = this.handleProgress.bind(this);\n }\n\n this.handleImagePreview = this.handleImagePreview.bind(this);\n this.handleFormButtonClick = this.handleFormButtonClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.formatterContext = {\n formatMessage: props.intl.formatMessage.bind(props.intl),\n viewportWidth: props.viewportWidth,\n authorizeURL: props.tinode.authorizeURL.bind(props.tinode),\n onImagePreview: this.handleImagePreview,\n onFormButtonClick: this.handleFormButtonClick,\n onQuoteClick: this.handleQuoteClick\n };\n }\n\n handleImagePreview(e) {\n e.preventDefault();\n this.props.onImagePreview({\n url: e.target.src,\n filename: e.target.title,\n width: e.target.dataset.width,\n height: e.target.dataset.height,\n size: e.target.dataset.size,\n type: e.target.dataset.mime\n });\n }\n\n handleFormButtonClick(e) {\n e.preventDefault();\n const data = {\n seq: this.props.seq\n };\n data.resp = {};\n if (e.target.dataset.name) {\n data.resp[e.target.dataset.name] = e.target.dataset.val ? e.target.dataset.val :\n e.target.dataset.val === undefined ? 1 : '' + e.target.dataset.val;\n }\n if (e.target.dataset.act == 'url') {\n data.ref = sanitizeUrl(e.target.dataset.ref) || 'about:blank';\n }\n const text = e.target.dataset.title || 'unknown';\n this.props.onFormResponse(e.target.dataset.act, text, data);\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const menuItems = [];\n if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n menuItems.push('menu_item_send_retry');\n }\n if (this.props.userIsWriter &&\n this.props.received > Tinode.MESSAGE_STATUS_FAILED &&\n this.props.received < Tinode.MESSAGE_STATUS_DEL_RANGE) {\n menuItems.push('menu_item_reply');\n }\n menuItems.push('menu_item_forward');\n\n this.props.showContextMenu({\n seq: this.props.seq,\n content: this.props.content,\n userFrom: this.props.userFrom,\n userName: this.props.userName,\n y: e.pageY,\n x: e.pageX,\n pickReply: this.props.pickReply\n }, menuItems);\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.onCancelUpload(this.props.seq, this.props.uploader);\n }\n\n handleQuoteClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const replyToSeq = this.props.replyToSeq;\n if (replyToSeq) {\n this.props.onQuoteClick(replyToSeq);\n }\n }\n\n render() {\n const sideClass = this.props.sequence + ' ' + (this.props.response ? 'left' : 'right');\n const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';\n const avatar = this.props.userAvatar || true;\n const fullDisplay = (this.props.isGroup && this.props.response &&\n (this.props.sequence == 'single' || this.props.sequence == 'last'));\n\n let content = this.props.content;\n const attachments = [];\n if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) {\n Drafty.attachments(content, (att, i) => {\n if (att.mime == 'application/json') {\n // Don't show json objects as attachments.\n // They are not meant for users.\n return;\n }\n attachments.push();\n }, this);\n const tree = Drafty.format(content, fullFormatter, this.formatterContext);\n content = React.createElement(React.Fragment, null, tree);\n } else if (typeof content != 'string') {\n content = <>warning_amber \n >\n }\n\n return (\n \n {this.props.isGroup && this.props.response ?\n \n {fullDisplay ?\n :\n null}\n
:\n null}\n \n
\n
\n
\n {content}\n {attachments}\n
\n {this.props.timestamp ?\n
\n : null}\n
\n {this.props.showContextMenu ?\n
\n \n expand_more\n \n : null\n }\n
\n {fullDisplay ?\n
\n {this.props.userName ||\n \n }\n
\n : null\n }\n
\n \n );\n }\n};\n\nconst IntlChatMessage = injectIntl(BaseChatMessage);\nconst ChatMessage = React.forwardRef((props, ref) => );\n\nexport default ChatMessage;\n","// Send message form.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport { KEYPRESS_DELAY } from '../config.js';\nimport { filePasted } from '../lib/blob-helpers.js';\nimport { replyFormatter } from '../lib/formatters.js';\n\nconst messages = defineMessages({\n messaging_disabled: {\n id: 'messaging_disabled_prompt',\n defaultMessage: 'Messaging disabled',\n description: 'Prompt in SendMessage in read-only topic'\n },\n type_new_message: {\n id: 'new_message_prompt',\n defaultMessage: 'New message',\n description: 'Prompt in send message field'\n },\n add_image_caption: {\n id: 'image_caption_prompt',\n defaultMessage: 'Image caption',\n description: 'Prompt in SendMessage for attached image'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n cannot_initiate_upload: {\n id: 'cannot_initiate_file_upload',\n defaultMessage: 'Cannot initiate file upload.',\n description: 'Generic error messagewhen attachment fails'\n },\n});\n\nclass SendMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n quote: null,\n message: '',\n // Make initial keypress time as if it happened 5001 milliseconds in the past.\n keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1\n };\n\n this.handlePasteEvent = this.handlePasteEvent.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleSend = this.handleSend.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleMessageTyping = this.handleMessageTyping.bind(this);\n\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.formatReply = this.formatReply.bind(this);\n }\n\n componentDidMount() {\n if (this.messageEditArea) {\n this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);\n }\n\n this.setState({quote: this.formatReply()});\n }\n\n componentWillUnmount() {\n if (this.messageEditArea) {\n this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentDidUpdate(prevProps) {\n if (this.messageEditArea) {\n this.messageEditArea.focus();\n }\n\n if (prevProps.reply != this.props.reply) {\n this.setState({quote: this.formatReply()});\n }\n }\n\n formatReply() {\n return this.props.reply ?\n Drafty.format(this.props.reply.content, replyFormatter, {\n formatMessage: this.props.intl.formatMessage.bind(this.props.intl),\n authorizeURL: this.props.tinode.authorizeURL.bind(this.props.tinode)\n }) : null;\n }\n\n handlePasteEvent(e) {\n if (this.props.disabled) {\n return;\n }\n // FIXME: handle large files too.\n if (filePasted(e,\n (bits, mime, width, height, fname) => {\n this.props.onAttachImage(mime, bits, width, height, fname);\n },\n (mime, bits, fname) => {\n this.props.onAttachFile(mime, bits, fname);\n },\n this.props.onError)) {\n\n // If a file was pasted, don't paste base64 data into input field.\n e.preventDefault();\n }\n }\n\n handleAttachImage(e) {\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachImage(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleAttachFile(e) {\n const {formatMessage} = this.props.intl;\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachFile(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleSend(e) {\n e.preventDefault();\n const message = this.state.message.trim();\n if (message || this.props.acceptBlank || this.props.noInput) {\n this.props.onSendMessage(message);\n this.setState({message: ''});\n }\n }\n\n /* Send on Enter key */\n handleKeyPress(e) {\n // Remove this if you don't want Enter to trigger send\n if (e.key === 'Enter') {\n // Have Shift-Enter insert a line break instead\n if (!e.shiftKey) {\n e.preventDefault();\n e.stopPropagation();\n\n this.handleSend(e);\n }\n }\n }\n\n handleMessageTyping(e) {\n const newState = {message: e.target.value};\n if (this.props.onKeyPress) {\n const now = new Date().getTime();\n if (now - this.state.keypressTimestamp > KEYPRESS_DELAY) {\n this.props.onKeyPress();\n newState.keypressTimestamp = now;\n }\n }\n this.setState(newState);\n }\n\n handleQuoteClick(e) {\n e.preventDefault();\n e.stopPropagation();\n if (this.props.reply && this.props.onQuoteClick) {\n const replyToSeq = this.props.reply.seq;\n this.props.onQuoteClick(replyToSeq);\n }\n }\n\n render() {\n const { formatMessage } = this.props.intl;\n const prompt = this.props.disabled ?\n formatMessage(messages.messaging_disabled) :\n (this.props.messagePrompt ?\n formatMessage(messages[this.props.messagePrompt]) :\n formatMessage(messages.type_new_message));\n\n const quote =\n (\n
\n {this.state.quote}\n
);\n\n return (\n \n {this.state.quote && !this.props.noInput ? quote : null}\n
\n
\n );\n }\n};\n\nexport default injectIntl(SendMessage);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { bytesToHumanSize, shortenFileName } from '../lib/strformat.js';\n\n// Get material icon name from mime type.\nfunction iconFromMime(mime) {\n // If more icons become available in material icons, add them to this mime-to-icon mapping.\n const mimeToIcon = {default: 'insert_drive_file', image: 'image', text: 'description', video: 'theaters'};\n\n return mimeToIcon[mime] || mimeToIcon[(mime || '').split('/')[0]] || mimeToIcon['default'];\n}\n\nexport default class DocPreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSendDoc = this.handleSendDoc.bind(this);\n }\n\n handleSendDoc(caption) {\n this.props.onClose();\n this.props.onSendMessage(this.props.content.file);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n return (\n \n
\n
\n
\n
{iconFromMime(this.props.content.type)}\n
{shortenFileName(this.props.content.name, 24) || '-'}
\n
{this.props.content.type || 'application/octet-stream'}
\n
{bytesToHumanSize(this.props.content.size)}
\n
\n
\n
\n
\n );\n }\n};\n","// GroupSubs: a list of group subscribers currently online.\n// Show in the topic title bar\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { MAX_ONLINE_IN_TOPIC } from '../config.js'\n\nimport LetterTile from './letter-tile.jsx';\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\nexport default class GroupSubs extends React.Component {\n constructor(props) {\n super(props);\n }\n\n render() {\n const usersOnline = [];\n const totalCount = (this.props.subscribers || []).length;\n const countToShow = Math.min(MAX_ONLINE_IN_TOPIC, totalCount);\n\n (this.props.subscribers || []).some((sub) => {\n usersOnline.push(\n \n \n
\n );\n return usersOnline.length == countToShow;\n });\n\n return (\n {usersOnline} {totalCount > countToShow ?\n \n \n : null}\n
\n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize, shortenFileName } from '../lib/strformat.js';\n\nexport default class ImagePreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n width: 0,\n height: 0\n };\n\n this.handleSendImage = this.handleSendImage.bind(this);\n }\n\n assignWidth(node) {\n if (node && !this.state.width) {\n const bounds = node.getBoundingClientRect();\n this.setState({\n width: bounds.width | 0,\n height: bounds.height | 0\n });\n }\n }\n\n handleSendImage(caption) {\n this.props.onClose();\n this.props.onSendMessage(caption, this.props.content.blob);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n\n const dim = fitImageSize(this.props.content.width, this.props.content.height,\n this.state.width, this.state.height, false);\n const size = dim ? { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' } :\n ((this.props.content.width > this.props.content.height) ? {width: '100%'} : {height: '100%'});\n size.maxWidth = '100%';\n size.maxHeight = '100%';\n\n // Average font aspect ratio is ~0.5; File name takes 1/3 of the viewport width.\n const maxlength = Math.max(((this.state.width / REM_SIZE / 1.5) | 0) - 2, 12);\n const fname = shortenFileName(this.props.content.name, maxlength) || '-';\n\n const width = this.props.content.width || '-';\n const height = this.props.content.height || '-';\n return (\n \n
\n
this.assignWidth(node)}>\n

\n
\n {this.props.onSendMessage ?\n
\n :\n }\n
\n );\n }\n};\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Invitation extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleButtonAction = this.handleButtonAction.bind(this);\n }\n\n handleButtonAction(evt, data) {\n evt.preventDefault();\n this.props.onAction(data);\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n \n \n
\n
\n );\n }\n};\n","// Audio recorder widget.\n\nimport React from 'react';\n\nimport { secondsToTime } from '../lib/strformat';\n\n// FFT resolution.\nconst BUFFER_SIZE = 256;\n// Thickness of a visualization bar.\nconst LINE_WIDTH = 5;\n// Spacing between two visualization bars.\nconst SPACING = 2;\n// Duration represented by one visualization bar.\nconst MILLIS_PER_BAR = 100;\n// Color of histogram bars\nconst BAR_COLOR = '#8fbed6';\n// Background color\nconst BKG_COLOR = '#eeeeee';\n\nexport default class AudioRecorder extends React.PureComponent {\n constructor(props) {\n super(props);\n\n const enabled = !!navigator.mediaDevices.getUserMedia;\n this.state = {\n enabled: enabled,\n audioRecord: null,\n recording: false,\n paused: false,\n duration: '0:00'\n };\n\n this.visualize = this.visualize.bind(this);\n this.initMediaRecording = this.initMediaRecording.bind(this);\n this.handleStart = this.handleStart.bind(this);\n this.handleStop = this.handleStop.bind(this);\n this.handleDelete = this.handleDelete.bind(this);\n\n this.durationMillis = 0;\n this.startedOn = null;\n this.canvasRef = React.createRef();\n }\n\n componentDidMount() {\n if (!this.state.enabled) {\n return;\n }\n\n this.canvasContext = this.canvasRef.current.getContext('2d');\n this.canvasContext.lineCap = 'round';\n // To reduce line blurring.\n this.canvasContext.translate(0.5, 0.5);\n\n this.stream = null;\n this.mediaRecorder = null;\n this.audioContext = null;\n this.sampleRate = null;\n this.audioInput = null;\n this.analyser = null;\n\n this.audioChunks = [];\n }\n\n // Draw amplitude of sound.\n visualize() {\n const pcmData = new Uint8Array(this.analyser.frequencyBinCount);\n const width = this.canvasRef.current.width;\n const height = this.canvasRef.current.height;\n const viewBuffer = [];\n const viewLength = (width / (LINE_WIDTH + SPACING)) | 0;\n const viewDuration = MILLIS_PER_BAR * viewLength;\n\n // Take each N-th sample:\n const samples = (0.1 * this.sampleRate) | 0;\n\n this.canvasContext.fillStyle = BKG_COLOR;\n this.canvasContext.lineWidth = LINE_WIDTH;\n this.canvasContext.strokeStyle = BAR_COLOR;\n\n let prevBarCount = 0;\n const drawFrame = () => {\n const duration = this.durationMillis + Date.now() - this.startedOn;\n // Update record length timer.\n this.setState({duration: secondsToTime(duration / 1000)});\n\n if (this.state.recording) {\n window.requestAnimationFrame(drawFrame);\n }\n\n // Draw histogram.\n\n // Get current waveform and calculate its amplitude.\n this.analyser.getByteTimeDomainData(pcmData);\n let volume = 0.0;\n for (const amplitude of pcmData) {\n volume += (amplitude - 127) ** 2;\n }\n volume = Math.sqrt(volume/pcmData.length);\n\n let barCount = (duration / MILLIS_PER_BAR) | 0;\n // Shift of the histogram along x-axis to make scrolling smooth. No need to shift if recording is too short.\n const dx = viewDuration > duration ? 0 :\n (duration - MILLIS_PER_BAR * barCount) / MILLIS_PER_BAR * (LINE_WIDTH + SPACING);\n\n if (prevBarCount != barCount) {\n prevBarCount = barCount;\n // Add new amplitude visualization bar.\n viewBuffer.push(volume);\n if (viewBuffer.length > viewLength) {\n // Keep at most 'viewLength' amplitude bars.\n viewBuffer.shift();\n }\n }\n\n // Clear canvas.\n this.canvasContext.fillRect(0, 0, width, height);\n\n // Draw amplitude bars.\n this.canvasContext.beginPath();\n for (let i = 0; i < viewBuffer.length; i++) {\n let x = i * (LINE_WIDTH + SPACING) - dx;\n let y = viewBuffer[i] / 64 * height;\n\n this.canvasContext.moveTo(x, (height - y) * 0.5);\n this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);\n }\n // Actually draw the bars on canvas.\n this.canvasContext.stroke();\n }\n\n drawFrame();\n }\n\n handleStop() {\n this.mediaRecorder.pause();\n this.durationMillis += Date.now() - this.startedOn;\n this.setState({recording: false, duration: secondsToTime(this.durationMillis / 1000)});\n }\n\n handleStart() {\n if (this.mediaRecorder) {\n this.startedOn = Date.now();\n this.mediaRecorder.resume();\n this.setState({recording: true}, this.visualize);\n } else {\n try {\n navigator.mediaDevices.getUserMedia({audio: true, video: false}).then(this.initMediaRecording, this.props.onError);\n } catch (err) {\n this.props.onError(err);\n }\n }\n }\n\n handleDelete() {\n // Delete current recording.\n console.log(\"Delete current cached recording\");\n }\n\n initMediaRecording(stream) {\n this.stream = stream;\n this.mediaRecorder = new MediaRecorder(stream);\n\n // The following code is needed for visualization.\n this.audioContext = new AudioContext();\n this.sampleRate = this.audioContext.sampleRate;\n this.audioInput = this.audioContext.createMediaStreamSource(stream);\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = BUFFER_SIZE;\n this.audioInput.connect(this.analyser);\n\n this.mediaRecorder.onstop = _ => {\n console.log(\"Data available after MediaRecorder.stop() called.\");\n const blob = new Blob(this.audioChunks, { 'type' : 'audio/mp3; codecs=mp3' });\n this.audioChunks = [];\n const url = window.URL.createObjectURL(blob);\n this.setState({audioRecord: url});\n console.log(\"Audio URL:\", url);\n }\n\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n this.audioChunks.push(e.data);\n }\n }\n\n this.startedOn = Date.now();\n this.mediaRecorder.start();\n this.setState({recording: true}, this.visualize);\n }\n\n render() {\n return (this.state.enabled ?\n :\n Audio not available
);\n }\n}\n","/* This is just a static page to display when no conversation is selected. */\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AudioRecorder from '../widgets/audio-recorder.jsx';\n\nimport { APP_NAME } from '../config.js';\n\nexport default class LogoView extends React.PureComponent {\n render() {\n return \n\n const version = APP_NAME + ' (' + Tinode.getLibrary() + ')';\n return (\n \n
\n
\n
\n Tinode Web
\n \n
{version}
\n
{this.props.serverVersion} ({this.props.serverAddress})
\n
\n
\n );\n }\n};\n","// Message bubble with non-payload content, such as an indicator of deleted content or a date stamp.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class MetaMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n let content = null;\n let bubbleClass = 'bubble';\n if (this.props.deleted) {\n // Message represents a range of deleted messages.\n content = <>block \n \n >\n bubbleClass += ' deleted';\n } else if (this.props.date) {\n // A date badge.\n content = <>{this.props.date}>\n bubbleClass += ' date';\n }\n\n if (!content) {\n return <>{null}>\n }\n\n return (\n \n \n \n );\n }\n};\n","// Utility class for hash navigation.\n\n// Parse hash as in http://www.example.com/path#hash as if it were\n// path and arguments.\nexport default class HashNavigation {\n static parseUrlHash(hash) {\n // Split path from args, path -> parts[0], args->path[1]\n const parts = hash.split('?', 2);\n const params = {};\n let path = [];\n if (parts[0]) {\n path = parts[0].replace('#', '').split('/');\n }\n if (parts[1]) {\n parts[1].split('&').forEach(function(part) {\n const item = part.split('=');\n if (item[0]) {\n params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);\n }\n });\n }\n return {path: path, params: params};\n }\n\n static navigateTo(url) {\n window.location.hash = url;\n }\n\n static composeUrlHash(path, params) {\n let url = path.join('/');\n const args = [];\n for (const key in params) {\n if (params.hasOwnProperty(key)) {\n args.push(key + '=' + params[key]);\n }\n }\n if (args.length > 0) {\n url += '?' + args.join('&');\n }\n return url;\n }\n\n static addUrlParam(hash, key, value) {\n const parsed = this.parseUrlHash(hash);\n parsed.params[key] = value;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static removeUrlParam(hash, key) {\n const parsed = this.parseUrlHash(hash);\n delete parsed.params[key];\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlSidePanel(hash, sidepanel) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[0] = sidepanel;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlInfoPanel(hash, infopanel) {\n const parsed = this.parseUrlHash(hash);\n if (infopanel) {\n parsed.params.info = infopanel;\n } else {\n delete parsed.params.info;\n }\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlTopic(hash, topic) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[1] = topic;\n // Close InfoView on topic change.\n delete parsed.params.info;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n}\n","// Panel with a chat.\n\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\nconst Drafty = Tinode.Drafty;\n\nimport ChatMessage from '../widgets/chat-message.jsx';\nimport ContactBadges from '../widgets/contact-badges.jsx';\nimport DocPreview from '../widgets/doc-preview.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupSubs from '../widgets/group-subs.jsx';\nimport ImagePreview from '../widgets/image-preview.jsx';\nimport Invitation from '../widgets/invitation.jsx';\nimport LetterTile from '../widgets/letter-tile.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport LogoView from './logo-view.jsx';\nimport MetaMessage from '../widgets/meta-message.jsx';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY,\n MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE,\n READ_DELAY, QUOTED_REPLY_LENGTH } from '../config.js';\nimport { blobToBase64, fileToBase64, imageScaled, makeImageUrl } from '../lib/blob-helpers.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\n\n// Run timer with this frequency (ms) for checking notification queue.\nconst NOTIFICATION_EXEC_INTERVAL = 300;\n// Scroll distance before [go to latest message] button is shown.\nconst SHOW_GO_TO_LAST_DIST = 100;\n\nconst messages = defineMessages({\n online_now: {\n id: 'online_now',\n defaultMessage: 'online now',\n description: 'Indicator that the user or topic is currently online',\n },\n last_seen: {\n id: 'last_seen_timestamp',\n defaultMessage: 'Last seen',\n description: 'Label for the timestamp of when the user or topic was last online'\n },\n not_found: {\n id: 'title_not_found',\n defaultMessage: 'Not found',\n description: 'Title shown when topic is not found'\n },\n channel: {\n id: 'channel',\n defaultMessage: 'channel',\n description: 'Subtitle shown for channels in MessagesView instead of last seen'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n invalid_content: {\n id: 'invalid_content',\n defaultMessage: 'invalid content',\n description: 'Shown when the message is unreadable'\n },\n});\n\n// Checks if the access permissions are granted but not yet accepted.\nfunction isUnconfirmed(acs) {\n if (acs) {\n const ex = acs.getExcessive() || '';\n return acs.isJoiner('given') && (ex.includes('R') || ex.includes('W'));\n }\n return false;\n}\n\nfunction isPeerRestricted(acs) {\n if (acs) {\n const ms = acs.getMissing() || '';\n return acs.isJoiner('want') && (ms.includes('R') || ms.includes('W'));\n }\n return false;\n}\n\nclass MessagesView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = MessagesView.getDerivedStateFromProps(props, {});\n\n this.leave = this.leave.bind(this);\n this.sendMessage = this.sendMessage.bind(this);\n this.retrySend = this.retrySend.bind(this);\n this.sendImageAttachment = this.sendImageAttachment.bind(this);\n this.sendFileAttachment = this.sendFileAttachment.bind(this);\n this.sendKeyPress = this.sendKeyPress.bind(this);\n this.subscribe = this.subscribe.bind(this);\n this.handleScrollReference = this.handleScrollReference.bind(this);\n this.handleScrollEvent = this.handleScrollEvent.bind(this);\n this.handleDescChange = this.handleDescChange.bind(this);\n this.handleSubsUpdated = this.handleSubsUpdated.bind(this);\n this.handleMessageUpdate = this.handleMessageUpdate.bind(this);\n this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);\n this.handleInfoReceipt = this.handleInfoReceipt.bind(this);\n this.handleImagePostview = this.handleImagePostview.bind(this);\n this.handleClosePreview = this.handleClosePreview.bind(this);\n this.handleFormResponse = this.handleFormResponse.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleShowMessageContextMenu = this.handleShowMessageContextMenu.bind(this);\n this.handleNewChatAcceptance = this.handleNewChatAcceptance.bind(this);\n this.handleEnablePeer = this.handleEnablePeer.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.postReadNotification = this.postReadNotification.bind(this);\n this.clearNotificationQueue = this.clearNotificationQueue.bind(this);\n this.goToLatestMessage = this.goToLatestMessage.bind(this);\n\n this.handlePickReply = this.handlePickReply.bind(this);\n this.handleCancelReply = this.handleCancelReply.bind(this);\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.chatMessageRefs = {};\n this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);\n\n this.readNotificationQueue = [];\n this.readNotificationTimer = null;\n }\n\n getOrCreateMessageRef(seqId) {\n if (this.chatMessageRefs.hasOwnProperty(seqId)) {\n return this.chatMessageRefs[seqId];\n }\n const ref = React.createRef();\n this.chatMessageRefs[seqId] = ref;\n return ref;\n }\n\n componentDidMount() {\n if (this.messagesScroller) {\n this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);\n }\n }\n\n componentWillUnmount() {\n if (this.messagesScroller) {\n this.messagesScroller.removeEventListener('scroll', this.handleScrollEvent);\n }\n\n // Flush all notifications.\n this.clearNotificationQueue();\n }\n\n // Scroll last message into view on component update e.g. on message received\n // or vertical shrinking.\n componentDidUpdate(prevProps, prevState) {\n if (this.messagesScroller &&\n (prevState.topic != this.state.topic || prevState.messageCount != this.state.messageCount)) {\n // New message.\n if (this.state.scrollPosition < SHOW_GO_TO_LAST_DIST) {\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight -\n this.state.scrollPosition -\n this.messagesScroller.offsetHeight;\n }\n }\n\n const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n }\n\n if (topic) {\n topic.onData = this.handleMessageUpdate;\n topic.onAllMessagesReceived = this.handleAllMessagesReceived;\n topic.onInfo = this.handleInfoReceipt;\n topic.onMetaDesc = this.handleDescChange;\n topic.onSubsUpdated = this.handleSubsUpdated;\n topic.onPres = this.handleSubsUpdated;\n }\n }\n\n if (!this.props.applicationVisible) {\n // If application is not visible, flush all unsent 'read' notifications.\n this.clearNotificationQueue();\n } else {\n // Otherwise assume there are unread messages.\n this.postReadNotification(0);\n }\n\n if (topic && ((this.state.topic != prevState.topic) || !prevProps.ready)) {\n if (topic._new) {\n console.log('Fetching new topic description');\n topic.getMeta(topic.startMetaQuery().withDesc().build());\n } else {\n this.subscribe(topic);\n }\n }\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n let nextState = {};\n if (!nextProps.topic) {\n // Default state: no topic.\n nextState = {\n messageCount: 0,\n latestClearId: -1,\n onlineSubs: [],\n topic: null,\n title: '',\n avatar: null,\n isVerified: false,\n isStaff: false,\n isDangerous: false,\n deleted: false,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: false,\n channel: false,\n reply: null,\n showGoToLastButton: false\n };\n } else if (nextProps.topic != prevState.topic) {\n const topic = nextProps.tinode.getTopic(nextProps.topic);\n\n let reply = null;\n if (nextProps.forwardMessage) {\n // We are forwarding a message. Show preview.\n const preview = nextProps.forwardMessage.preview;\n reply = {\n content: preview,\n seq: null\n };\n }\n\n nextState = {\n topic: nextProps.topic,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n reply: reply,\n showGoToLastButton: false,\n deleted: topic._deleted\n };\n\n if (topic) {\n // Topic exists.\n const subs = [];\n\n if (nextProps.connected) {\n topic.subscribers((sub) => {\n if (sub.online && sub.user != nextProps.myUserId) {\n subs.push(sub);\n }\n });\n }\n\n Object.assign(nextState, {\n onlineSubs: subs\n });\n\n if (topic.public) {\n Object.assign(nextState, {\n title: topic.public.fn,\n avatar: makeImageUrl(topic.public.photo)\n });\n } else {\n Object.assign(nextState, {\n title: '',\n avatar: null\n });\n }\n\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(nextState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (prevState.peerMessagingDisabled) {\n Object.assign(nextState, {\n peerMessagingDisabled: false\n });\n }\n Object.assign(nextState, {\n messageCount: topic.messageCount(),\n latestClearId: topic.maxClearId(),\n channel: topic.isChannelType()\n });\n } else {\n // Invalid topic.\n Object.assign(nextState, {\n messageCount: 0,\n latestClearId: -1,\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: false,\n channel: false\n });\n }\n }\n\n if (nextProps.acs) {\n if (nextProps.acs.isWriter() != prevState.isWriter) {\n nextState.isWriter = !prevState.isWriter;\n }\n if (nextProps.acs.isReader() != prevState.isReader) {\n nextState.isReader = !prevState.isReader;\n }\n if (!nextProps.acs.isReader('given') != prevState.readingBlocked) {\n nextState.readingBlocked = !prevState.readingBlocked;\n }\n if (nextProps.acs.isSharer() != prevState.isSharer) {\n nextState.isSharer = !prevState.isSharer;\n }\n } else {\n if (prevState.isWriter) {\n nextState.isWriter = false;\n }\n if (prevState.isReader) {\n nextState.isReader = false;\n }\n if (!prevState.readingBlocked) {\n prevState.readingBlocked = true;\n }\n if (prevState.isSharer) {\n nextState.isSharer = false;\n }\n }\n\n if (isUnconfirmed(nextProps.acs) == !prevState.unconformed) {\n nextState.unconfirmed = !prevState.unconformed;\n }\n\n // Clear subscribers online when there is no connection.\n if (!nextProps.connected && prevState.onlineSubs && prevState.onlineSubs.length > 0) {\n nextState.onlineSubs = [];\n }\n\n return nextState;\n }\n\n subscribe(topic) {\n if (topic.isSubscribed() || !this.props.ready) {\n return;\n }\n\n // Is this a new topic?\n const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);\n\n // Don't request the tags. They are useless unless the user\n // is the owner and is editing the topic.\n let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();\n if (this.state.isReader || newTopic) {\n // Reading is either permitted or we don't know because it's a new topic. Ask for messages.\n getQuery = getQuery.withLaterData(MESSAGES_PAGE);\n if (this.state.isReader) {\n getQuery = getQuery.withLaterDel();\n }\n // And show \"loading\" spinner.\n this.setState({ fetchingMessages: true });\n }\n\n const setQuery = newTopic ? this.props.newTopicParams : undefined;\n topic.subscribe(getQuery.build(), setQuery)\n .then((ctrl) => {\n if (ctrl.code == 303) {\n // Redirect to another topic requested.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));\n return;\n }\n if (this.state.topic != ctrl.topic) {\n this.setState({topic: ctrl.topic});\n }\n this.props.onNewTopicCreated(this.props.topic, ctrl.topic);\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n this.retrySend(pub);\n }\n });\n })\n .catch((err) => {\n console.error(\"Failed subscription to\", this.state.topic);\n this.props.onError(err.message, 'err');\n const blankState = MessagesView.getDerivedStateFromProps({}, {});\n blankState.title = this.props.intl.formatMessage(messages.not_found);\n this.setState(blankState);\n });\n }\n\n leave(oldTopicName) {\n if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {\n return;\n }\n const oldTopic = this.props.tinode.getTopic(oldTopicName);\n if (oldTopic && oldTopic.isSubscribed()) {\n oldTopic.leave(false)\n .catch(() => { /* do nothing here */ })\n .finally(() => {\n // We don't care if the request succeeded or failed.\n // The topic is dead regardless.\n this.setState({fetchingMessages: false});\n oldTopic.onData = undefined;\n oldTopic.onAllMessagesReceived = undefined;\n oldTopic.onInfo = undefined;\n oldTopic.onMetaDesc = undefined;\n oldTopic.onSubsUpdated = undefined;\n oldTopic.onPres = undefined;\n });\n }\n }\n\n // Don't use React.createRef as the ref.current is not available in componentDidMount in this component.\n handleScrollReference(node) {\n if (node) {\n node.addEventListener('scroll', this.handleScrollEvent);\n this.messagesScroller = node;\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n }\n }\n\n // Get older messages and show/hide [go to latest message] button.\n handleScrollEvent(event) {\n const pos = event.target.scrollHeight - event.target.scrollTop - event.target.offsetHeight;\n this.setState({\n scrollPosition: pos,\n showGoToLastButton: pos > SHOW_GO_TO_LAST_DIST,\n });\n\n if (this.state.fetchingMessages) {\n return;\n }\n\n if (event.target.scrollTop <= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {\n this.setState({fetchingMessages: true}, () => {\n topic.getMessagesPage(MESSAGES_PAGE)\n .catch((err) => this.props.onError(err.message, 'err'))\n .finally(() => this.setState({fetchingMessages: false}));\n });\n }\n }\n }\n\n goToLatestMessage() {\n this.setState({scrollPosition: 0});\n if (this.messagesScroller) {\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight;\n }\n }\n\n handleDescChange(desc) {\n if (desc.public) {\n this.setState({\n title: desc.public.fn,\n avatar: makeImageUrl(desc.public.photo)\n });\n } else {\n this.setState({\n title: '',\n avatar: null\n });\n }\n\n if (desc.acs) {\n this.setState({\n isWriter: desc.acs.isWriter(),\n isReader: desc.acs.isReader(),\n readingBlocked: !desc.acs.isReader('given'),\n unconfirmed: isUnconfirmed(desc.acs),\n });\n }\n }\n\n postReadNotification(seq) {\n // Ignore notifications if the app is invisible.\n if (!this.props.applicationVisible) {\n return;\n }\n\n // Set up the timer if it's not running already.\n if (!this.readNotificationTimer) {\n this.readNotificationTimer = setInterval(() => {\n if (this.readNotificationQueue.length == 0) {\n // Shut down the timer if the queue is empty.\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n return;\n }\n\n let seq = -1;\n while (this.readNotificationQueue.length > 0) {\n const n = this.readNotificationQueue[0];\n if (n.topicName != this.state.topic) {\n // Topic has changed. Drop the notification.\n this.readNotificationQueue.shift();\n continue;\n }\n\n const now = new Date();\n if (n.sendAt <= now) {\n // Remove expired notification from queue.\n this.readNotificationQueue.shift();\n seq = Math.max(seq, n.seq);\n } else {\n break;\n }\n }\n\n // Send only one notification for the whole batch of messages.\n if (seq >= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic) {\n topic.noteRead(seq);\n }\n }\n }, NOTIFICATION_EXEC_INTERVAL);\n }\n\n const now = new Date();\n this.readNotificationQueue.push({\n topicName: this.state.topic,\n seq: seq,\n sendAt: now.setMilliseconds(now.getMilliseconds() + READ_DELAY)\n });\n }\n\n // Clear notification queue and timer.\n clearNotificationQueue() {\n this.readNotificationQueue = [];\n if (this.readNotificationTimer) {\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n }\n }\n\n handleSubsUpdated() {\n if (this.state.topic) {\n const subs = [];\n const topic = this.props.tinode.getTopic(this.state.topic);\n topic.subscribers((sub) => {\n if (sub.online && sub.user != this.props.myUserId) {\n subs.push(sub);\n }\n });\n const newState = {onlineSubs: subs};\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(newState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (this.state.peerMessagingDisabled) {\n Object.assign(newState, {\n peerMessagingDisabled: false\n });\n }\n this.setState(newState);\n }\n }\n\n // The 'msg' could be false-ish if some message ranges were deleted.\n handleMessageUpdate(msg) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (!msg) {\n // msg could be null if one or more messages were deleted.\n // Updating state to force redraw.\n this.setState({latestClearId: topic.maxClearId()});\n return;\n }\n\n clearTimeout(this.keyPressTimer)\n this.setState({messageCount: topic.messageCount(), typingIndicator: false});\n\n // Scroll to the bottom if the message is added to the end of the message list.\n // TODO: This should be replaced by showing a \"scroll to bottom\" button.\n if (topic.isNewMessage(msg.seq)) {\n if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {\n this.setState({showGoToLastButton: true});\n } else {\n this.setState({scrollPosition: 0});\n }\n }\n\n // Aknowledge messages except own messages. They are\n // automatically assumed to be read and recived.\n const status = topic.msgStatus(msg, true);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.postReadNotification(msg.seq);\n }\n // This will send \"received\" notifications right away.\n this.props.onData(msg);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\n if (count > 0) {\n // 0 means \"latest\".\n this.postReadNotification(0);\n }\n }\n\n handleInfoReceipt(info) {\n switch (info.what) {\n case 'kp': {\n clearTimeout(this.keyPressTimer);\n this.keyPressTimer = setTimeout(() => {\n this.setState({typingIndicator: false});\n }, KEYPRESS_DELAY + 1000);\n if (!this.state.typingIndicator) {\n this.setState({typingIndicator: true});\n }\n break;\n }\n case 'read':\n case 'recv':\n // Redraw due to changed recv/read status.\n this.forceUpdate();\n break;\n default:\n console.info(\"Other change in topic: \", info.what);\n }\n }\n\n handleImagePostview(content) {\n this.setState({ imagePostview: content });\n }\n\n handleClosePreview() {\n if (this.state.imagePreview && this.state.imagePreview.url) {\n URL.revokeObjectURL(this.state.imagePreview.url);\n }\n this.setState({ imagePostview: null, imagePreview: null, docPreview: null });\n }\n\n handleFormResponse(action, text, data) {\n if (action == 'pub') {\n this.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));\n } else if (action == 'url') {\n const url = new URL(data.ref);\n const params = url.searchParams;\n for (let key in data.resp) {\n if (data.resp.hasOwnProperty(key)) {\n params.set(key, data.resp[key]);\n }\n }\n ['name', 'seq'].map((key) => {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\n params.set('topic', this.state.topic);\n url.search = params;\n window.open(url, '_blank');\n } else {\n console.info(\"Unknown action in form\", action);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.state.topic, y: e.pageY, x: e.pageX });\n }\n\n handleShowMessageContextMenu(params, messageSpecificMenuItems) {\n if (params.userFrom == 'chan') {\n params.userFrom = this.state.topic;\n params.userName = this.state.title;\n }\n params.topicName = this.state.topic;\n const menuItems = messageSpecificMenuItems || [];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\n if (!topic.isChannelType()) {\n menuItems.push('message_delete');\n }\n const acs = topic.getAccessMode();\n if (acs && acs.isDeleter()) {\n menuItems.push('message_delete_hard');\n }\n }\n this.props.showContextMenu(params, menuItems);\n }\n\n handleNewChatAcceptance(action) {\n this.props.onNewChat(this.state.topic, action);\n }\n\n handleEnablePeer(e) {\n e.preventDefault();\n this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);\n }\n\n sendKeyPress() {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic.isSubscribed()) {\n topic.noteKeyPress();\n }\n }\n\n // sendMessage sends the message with an optional subscription to topic first.\n sendMessage(msg, uploadCompletionPromise, uploader) {\n let head;\n if (this.props.forwardMessage) {\n // We are forwarding a message.\n msg = this.props.forwardMessage.msg;\n head = this.props.forwardMessage.head;\n this.handleCancelReply();\n } else if (this.state.reply && this.state.reply.content) {\n // We are replying to a message in this topic.\n head = {reply: '' + this.state.reply.seq};\n // Turn it into Drafty so we can make a quoted Drafty object later.\n if (typeof msg == 'string') {\n msg = Drafty.parse(msg);\n }\n msg = Drafty.append(Drafty.sanitizeEntities(this.state.reply.content), msg);\n this.handleCancelReply();\n }\n this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);\n }\n\n // Retry sending a message.\n retrySend(pub) {\n this.props.sendMessage(pub.content, undefined, undefined, pub.head);\n }\n\n // Send attachment as Drafty message:\n // - if file is too large, upload it and send a s link.\n // - if file is small enough, just send it in-band.\n sendFileAttachment(file) {\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (file.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, {\n mime: file.type,\n filename: file.name,\n size: file.size,\n urlPromise: uploadCompletionPromise\n });\n // Pass data and the uploader to the TinodeWeb.\n this.sendMessage(msg, uploadCompletionPromise, uploader);\n } else {\n // Small enough to send inband.\n fileToBase64(file)\n .then(b64 => this.sendMessage(Drafty.attachFile(null, {mime: b64.mime, data: b64.bits, filename: b64.name})))\n .catch(err => this.props.onError(err));\n }\n }\n\n // handleAttachFile method is called when [Attach file] button is clicked.\n handleAttachFile(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n if (file.size > maxExternAttachmentSize) {\n // Too large.\n this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');\n } else {\n this.setState({\n docPreview: {\n file: file,\n name: file.name,\n size: file.size,\n type: file.type\n }\n });\n }\n }\n\n // sendImageAttachment sends the image bits inband as Drafty message.\n sendImageAttachment(caption, blob) {\n const mime = this.state.imagePreview.mime;\n const width = this.state.imagePreview.width;\n const height = this.state.imagePreview.height;\n const fname = this.state.imagePreview.name;\n\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (blob.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(blob);\n\n // Make small preview to show while uploading.\n imageScaled(blob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false)\n // Convert tiny image into base64 for serialization and previewing.\n .then(scaled => blobToBase64(scaled.blob))\n .then(b64 => {\n let msg = Drafty.insertImage(null, 0, {\n mime: mime,\n _tempPreview: b64.bits, // This preview will not be serialized.\n width: width,\n height: height,\n filename: fname,\n size: blob.size,\n urlPromise: uploadCompletionPromise\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.parse(caption));\n }\n // Pass data and the uploader to the TinodeWeb.\n this.sendMessage(msg, uploadCompletionPromise, uploader);\n }).catch((err) => {\n this.props.onError(err, 'err');\n });\n return;\n }\n\n // Upload the image if it's too big to be send inband.\n blobToBase64(blob)\n .then(b64 => {\n let msg = Drafty.insertImage(null, 0, {\n mime: b64.mime,\n preview: b64.bits, // Serializable preview\n width: width,\n height: height,\n filename: fname,\n size: blob.size\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.parse(caption));\n }\n this.sendMessage(msg);\n });\n }\n\n // handleAttachImage method is called when [Attach image] button is clicked.\n handleAttachImage(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n // Get image dimensions and size, optionally scale it down.\n imageScaled(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, maxExternAttachmentSize, false)\n .then(scaled => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(scaled.blob),\n blob: scaled.blob,\n name: scaled.name,\n width: scaled.width,\n height: scaled.height,\n size: scaled.blob.size,\n mime: scaled.mime\n }});\n }).catch(err => {\n this.props.onError(err, 'err');\n });\n }\n\n handleCancelUpload(seq, uploader) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const found = topic.findMessage(seq);\n if (found) {\n found._cancelled = true;\n }\n uploader.cancel();\n }\n\n // seq: seq ID of the source message\n // context: message content.\n // forwarded: true if the source message is also a forwarded message.\n // senderId: UID of the sender of the source message.\n // senderName: full name of the sender of the original message.\n handlePickReply(seq, content, senderId, senderName) {\n this.setState({reply: null});\n\n if (!seq || !content) {\n return;\n }\n\n content = typeof content == 'string' ? Drafty.init(content) : content;\n if (Drafty.isValid(content)) {\n content = Drafty.replyContent(content, QUOTED_REPLY_LENGTH);\n } else {\n // /!\\ invalid content.\n content = Drafty.append(Drafty.init('\\u26A0 '),\n Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));\n }\n\n this.setState({\n reply: {\n content: Drafty.quote(senderName, senderId, content),\n seq: seq\n }\n });\n this.props.onCancelForwardMessage();\n }\n\n handleCancelReply() {\n this.setState({reply: null});\n this.props.onCancelForwardMessage();\n }\n\n handleQuoteClick(replyToSeq) {\n const ref = this.getOrCreateMessageRef(replyToSeq);\n if (ref && ref.current) {\n ref.current.scrollIntoView({block: \"center\", behavior: \"smooth\"});\n ref.current.classList.add('flash');\n setTimeout(() => { ref.current.classList.remove('flash') } , 1000);\n } else {\n console.error(\"Unresolved message ref\", replyToSeq);\n }\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n let component;\n if (this.props.hideSelf) {\n component = null;\n } else if (!this.state.topic) {\n component = (\n \n );\n } else {\n let component2;\n if (this.state.imagePreview) {\n // Preview image before sending.\n component2 = (\n \n );\n } else if (this.state.imagePostview) {\n // Expand received image.\n component2 = (\n \n );\n } else if (this.state.docPreview) {\n // Preview attachment before sending.\n component2 = (\n \n );\n } else {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const isChannel = topic.isChannelType();\n const groupTopic = topic.isGroupType() && !isChannel;\n const icon_badges = [];\n if (topic.trusted) {\n if (topic.trusted.verified) {\n icon_badges.push({icon: 'verified', color: 'badge-inv'});\n }\n if (topic.trusted.staff) {\n icon_badges.push({icon: 'staff', color: 'badge-inv'});\n }\n if (topic.trusted.danger) {\n icon_badges.push({icon: 'dangerous', color: 'badge-inv'});\n }\n }\n\n const messageNodes = [];\n let previousFrom = null;\n let chatBoxClass = null;\n const dateFmt = new Intl.DateTimeFormat(this.props.intl.locale);\n topic.messages((msg, prev, next, i) => {\n let nextFrom = next ? (next.from || 'chan') : null;\n\n let sequence = 'single';\n let thisFrom = msg.from || 'chan';\n if (thisFrom == previousFrom) {\n if (thisFrom == nextFrom) {\n sequence = 'middle';\n } else {\n sequence = 'last';\n }\n } else if (thisFrom == nextFrom) {\n sequence = 'first';\n }\n previousFrom = thisFrom;\n\n const isReply = !(thisFrom == this.props.myUserId);\n const deliveryStatus = topic.msgStatus(msg, true);\n\n let userFrom = thisFrom, userName, userAvatar;\n const user = topic.userDesc(thisFrom);\n if (user && user.public) {\n userName = user.public.fn;\n userAvatar = makeImageUrl(user.public.photo);\n }\n chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';\n\n // Ref for this chat message.\n const ref = this.getOrCreateMessageRef(msg.seq);\n let replyToSeq = msg.head ? parseInt(msg.head.reply) : null;\n if (!replyToSeq || isNaN(replyToSeq)) {\n replyToSeq = null;\n }\n\n // This message was sent on a different date than the previous.\n if (!prev || new Date(prev.ts).toDateString() != new Date(msg.ts).toDateString()) {\n messageNodes.push(\n \n );\n }\n\n if (msg.hi) {\n // Deleted message.\n messageNodes.push(\n \n );\n } else {\n messageNodes.push(\n \n );\n }\n });\n\n let lastSeen = null;\n if (isChannel) {\n lastSeen = formatMessage(messages.channel);\n } else {\n const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.isP2PTopicName(cont.topic)) {\n if (cont.online) {\n lastSeen = formatMessage(messages.online_now);\n } else if (cont.seen) {\n lastSeen = formatMessage(messages.last_seen) + \": \" +\n shortDateFormat(cont.seen.when, this.props.intl.locale);\n // TODO: also handle user agent in c.seen.ua\n }\n }\n }\n const avatar = this.state.avatar || true;\n const online = this.state.deleted ? null :\n this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';\n\n const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');\n\n component2 = (\n <>\n \n {this.props.displayMobile ?\n \n : null}\n \n \n
\n
\n {!this.state.isReader ?\n
\n {this.state.readingBlocked ?\n
\n \n
\n : null }\n
\n : null }\n
\n {this.state.peerMessagingDisabled && !this.state.unconfirmed ?\n : null}\n {this.state.unconfirmed ?\n \n :\n }\n >\n );\n }\n\n component = {component2}
\n }\n return component;\n }\n};\n\nexport default injectIntl(MessagesView);\n","// The <- button to be displayed in title bars.\nimport React from 'react';\n\nexport default class ButtonBack extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onBack();}}>\n arrow_back\n \n );\n }\n}\n","import React from 'react';\n\nexport default class MenuContacts extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nexport default class MenuStart extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\nimport ButtonBack from './button-back.jsx';\nimport ContactBadges from './contact-badges.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n const icon_badges = [];\n if (this.props.trustedBadges) {\n this.props.trustedBadges.map((b) => {\n icon_badges.push({icon: b, color: 'badge-inv'});\n });\n }\n let avatar = null;\n if (this.props.tinode) {\n avatar = this.props.tinode.authorizeURL(sanitizeImageUrl(this.props.avatar));\n }\n return (\n \n {this.props.onCancel ?
: null}\n {avatar ?\n
\n \n
\n :\n null}\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\n\nfunction _clip(str, length) {\n return str && str.substring(0, length);\n}\n\nexport default class AccountSettingsView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: _clip(me.public ? me.public.fn : undefined, MAX_TITLE_LENGTH),\n description: _clip(me.public ? me.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),\n avatar: makeImageUrl(me.public ? me.public.photo : null)\n };\n }\n\n render() {\n return (\n \n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedMessage, defineMessages } from 'react-intl';\n\nimport ContactList from '../widgets/contact-list.jsx';\n\nimport { updateFavicon } from '../lib/utils.js';\n\nconst messages = defineMessages({\n archived_contacts_title: {\n id: \"archived_contacts\",\n defaultMessage: \"Archived contacts ({count})\",\n description: \"Label for archived chats\"\n }\n});\n\nexport default class ContactsView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleAction = this.handleAction.bind(this);\n\n this.state = ContactsView.deriveStateFromProps(props);\n }\n\n static deriveStateFromProps(props) {\n const contacts = [];\n let unreadThreads = 0;\n let archivedCount = 0;\n props.chatList.map((c) => {\n const blocked = c.acs && !c.acs.isJoiner();\n // Show only blocked contacts only when props.blocked == true.\n if (blocked && props.blocked) {\n contacts.push(c);\n }\n if (blocked || props.blocked) {\n return;\n }\n\n if (c.private && c.private.arch) {\n if (props.archive) {\n contacts.push(c);\n } else {\n archivedCount ++;\n }\n } else if (!props.archive) {\n contacts.push(c);\n unreadThreads += c.unread > 0 ? 1 : 0;\n }\n });\n\n contacts.sort((a, b) => {\n return (b.touched || 0) - (a.touched || 0);\n });\n\n if (archivedCount > 0) {\n contacts.push({\n action: 'archive',\n title: messages.archived_contacts_title,\n values: {count: archivedCount}\n });\n }\n\n return {\n contactList: contacts,\n unreadThreads: unreadThreads\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chatList != this.props.chatList ||\n prevProps.archive != this.props.archive ||\n prevProps.blocked != this.props.blocked) {\n const newState = ContactsView.deriveStateFromProps(this.props);\n this.setState(newState);\n if (newState.unreadThreads != prevState.unreadThreads) {\n updateFavicon(newState.unreadThreads);\n }\n }\n }\n\n handleAction(action_ignored) {\n this.props.onShowArchive();\n }\n\n render() {\n return (\n ¯∖_(ツ)_/¯\"\n description=\"HTML message shown in ContactList when no contacts are found\">{\n (no_contacts) => \n }\n );\n }\n};\n","// Helper functions for storing values in localStorage.\n// By default localStorage can store only strings, not objects or other types.\n\nexport default class LocalStorageUtil {\n // Replace old object with the new one.\n static setObject(key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n }\n\n // Get stored object.\n static getObject(key) {\n const value = localStorage.getItem(key);\n return value && JSON.parse(value);\n }\n\n // Partially or wholly update stored object.\n static updateObject(key, value) {\n const oldVal = this.getObject(key);\n this.setObject(key, Object.assign(oldVal || {}, value));\n }\n\n // Just a wrapper.\n static removeItem(key) {\n localStorage.removeItem(key);\n }\n}\n","// Account registration.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport { theCard } from '../lib/utils.js';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class CreateAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n login: '',\n password: '',\n email: '',\n fn: '', // full/formatted name\n imageDataUrl: null,\n errorCleared: false,\n saveToken: LocalStorageUtil.getObject('keep-logged-in')\n };\n\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(password) {\n this.setState({password: password});\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value})\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleToggleSaveToken() {\n LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.setState({errorCleared: false});\n this.props.onCreateAccount(\n this.state.login.trim(),\n this.state.password.trim(),\n theCard(this.state.fn.trim().substring(0, MAX_TITLE_LENGTH), this.state.imageDataUrl),\n {'meth': 'email', 'val': this.state.email});\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\n\nexport default class EditAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCheckboxClick = this.handleCheckboxClick.bind(this);\n }\n\n handleCheckboxClick(what, checked) {\n if (what == 'sound') {\n this.props.onToggleMessageSounds(checked);\n } else if (what == 'alert') {\n this.props.onTogglePushNotifications(checked);\n } else if (what == 'incognito') {\n this.props.onToggleIncognitoMode(checked);\n }\n }\n\n render() {\n return (\n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\n\nconst messages = defineMessages({\n delete_account: {\n id: 'delete_account',\n defaultMessage: 'Delete account',\n description: 'Title for delete account warning'\n },\n delete_account_warning: {\n id: 'delete_account_warning',\n defaultMessage: 'Are you sure you want to delete your account? It cannot be undone.',\n description: 'Warning message when deleting an account'\n }\n});\n\nclass AccSecurityView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n let blockedCount = 0;\n me.contacts((c) => {\n if (c.acs && !c.acs.isJoiner()) {\n blockedCount ++;\n }\n });\n const defacs = me.getDefaultAccess();\n this.state = {\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n showPermissionEditorFor: undefined,\n blockedCount: blockedCount,\n };\n\n this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdatePassword(pwd);\n }\n\n handleLaunchPermissionsEditor(which) {\n this.setState({\n showPermissionEditorFor: which,\n editedPermissions: this.state[which]\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handlePermissionsChanged(perm) {\n let defacs = {};\n defacs[this.state.showPermissionEditorFor] = perm;\n this.props.onUpdateAccountDesc('me', undefined, undefined, defacs);\n\n let newState = {showPermissionEditorFor: undefined};\n newState[this.state.showPermissionEditorFor] = perm;\n this.setState(newState);\n }\n\n handleDeleteAccount(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.delete_account), // title\n formatMessage(messages.delete_account_warning), // content\n (() => { this.props.onDeleteAccount(); }), // onConfirm\n null, // use default text \"OK\"\n true, // Show Reject button\n null // use default text \"Cancel\"\n );\n }\n\n render() {\n return (\n <>{this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n {this.state.blockedCount > 0 ?\n <>\n
\n
\n >\n : null }\n
\n }>\n );\n }\n};\n\nexport default injectIntl(AccSecurityView);\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME, LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE } from '../config.js';\n\nexport default class AccSupportView extends React.PureComponent {\n render() {\n return (\n \n
\n
\n
\n
\n \n {APP_NAME}\n
\n
\n \n {Tinode.getLibrary()}\n
\n
\n \n {this.props.serverVersion}\n
\n
\n \n {this.props.serverAddress}\n
\n
\n
\n );\n }\n};\n","// Login form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\n\nexport default class LoginView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: props.persist\n };\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n handleToggleSaveToken() {\n this.props.onPersistenceChange(!this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n/* END Login */\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n invalid_id: {\n id: 'error_invalid_id',\n defaultMessage: 'Invalid ID',\n description: 'Error message'\n }\n});\n\nclass NewTopicById extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n groupId: '',\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleChange(e) {\n this.setState({groupId: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.groupId) {\n const name = this.state.groupId.trim();\n const prefix = name.substr(0, 3);\n if (name.length > 3 && ['usr', 'grp', 'chn'].includes(prefix)) {\n this.props.onSubmit(name);\n } else {\n this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');\n }\n }\n }\n\n render() {\n return (\n \n
\n {\n (prompt) => \n }\n
\n
\n \n
\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicById);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from './avatar-upload.jsx';\nimport CheckBox from './checkbox.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\n\nexport default class NewTopicGroup extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.fullName = React.createRef();\n\n this.state = {\n fullName: '', // full/formatted name\n private: '',\n description: '',\n imageDataUrl: null,\n tags: [],\n isChannel: false\n };\n\n this.handleFieldEdit = this.handleFieldEdit.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleTagsChanged = this.handleTagsChanged.bind(this);\n this.handleChannelToggle = this.handleChannelToggle.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n componentDidMount() {\n // this.fullName.current.focus();\n }\n\n handleFieldEdit(name, e) {\n this.setState({[name]: e.target.value || ''});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleTagsChanged(tags) {\n this.setState({tags: tags});\n }\n\n handleChannelToggle() {\n this.setState({isChannel: !this.state.isChannel});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n\n const fn = this.state.fullName.trim().substring(0, MAX_TITLE_LENGTH);\n const comment = this.state.private.trim().substring(0, MAX_TITLE_LENGTH);\n const description = this.state.description.trim().substring(0, MAX_TOPIC_DESCRIPTION_LENGTH);\n if (fn) {\n this.props.onSubmit(fn, description, this.state.imageDataUrl, comment, this.state.tags, this.state.isChannel);\n }\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n return (\n \n );\n }\n};\n","// Create new topic and invite users or send an invite.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from '../widgets/contact-list.jsx';\nimport NewTopicById from '../widgets/new-topic-by-id.jsx';\nimport NewTopicGroup from '../widgets/new-topic-group.jsx';\nimport SearchContacts from '../widgets/search-contacts.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\nimport { theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n search_for_contacts: {\n id: \"search_for_contacts\",\n defaultMessage: \"Use search to find contacts\",\n description: \"Text shown in contacts view when user entered no search query.\"\n },\n search_no_results: {\n id: \"search_no_results\",\n defaultMessage: \"Search returned no results\",\n description: \"Text shown in contacts view when query returned no results.\"\n },\n search_placeholder: {\n id: \"search_placeholder\",\n defaultMessage: \"List like email:alice@example.com, tel:17025550003...\",\n description: \"Placeholder in contacts search field\"\n },\n});\n\nclass NewTopicView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tabSelected: 'find',\n searchQuery: null\n };\n\n this.handleTabClick = this.handleTabClick.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleSearchResultSelected = this.handleSearchResultSelected.bind(this);\n this.handleNewGroupSubmit = this.handleNewGroupSubmit.bind(this);\n this.handleGroupByID = this.handleGroupByID.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleTabClick(e) {\n e.preventDefault();\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));\n this.setState({tabSelected: e.currentTarget.dataset.id});\n }\n\n handleSearchContacts(query) {\n this.props.onSearchContacts(query);\n this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});\n }\n\n handleSearchResultSelected(name) {\n if (this.state.tabSelected == 'find') {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(name);\n }\n }\n\n handleNewGroupSubmit(name, description, dataUrl, priv, tags, isChannel) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined,\n {public: theCard(name, dataUrl, null, description), private: priv, tags: tags}, isChannel);\n }\n\n handleGroupByID(topicName) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(topicName);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const no_contacts_placeholder = formatMessage(this.state.searchQuery ?\n messages.search_no_results : messages.search_for_contacts);\n const search_placeholder = formatMessage(messages.search_placeholder);\n return (\n \n
\n {this.state.tabSelected === 'grp' ?\n
:\n this.state.tabSelected === 'byid' ?\n
:\n
\n \n \n
}\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicView);\n","// A password reset form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\n\nexport default class PasswordResetView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n email: '',\n password: '',\n sent: false\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n }\n\n componentDidMount() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n this.setState({token: parsed.params.token, scheme: parsed.params.scheme});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.token) {\n this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);\n } else if (this.state.sent) {\n this.props.onCancel();\n } else {\n const email = this.state.email.trim();\n this.setState({email: email});\n this.props.onRequest('email', email).then(() => {\n this.setState({sent: true});\n });\n }\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n render() {\n return (\n \n );\n }\n}\n","import React from 'react';\n\nimport { KNOWN_HOSTS } from '../config.js';\n\n/* Combobox for selecting host name */\n\nexport default class HostSelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n hostName: props.serverAddress,\n changed: false\n };\n\n this.handleHostNameChange = this.handleHostNameChange.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handleHostNameChange(e) {\n this.setState({hostName: e.target.value, changed: true});\n }\n\n handleEditingFinished() {\n if (this.state.changed) {\n this.setState({changed: false});\n this.props.onServerAddressChange(this.state.hostName.trim());\n }\n }\n\n render() {\n var hostOptions = [];\n for (let key in KNOWN_HOSTS) {\n let item = KNOWN_HOSTS[key];\n hostOptions.push(\n \n );\n }\n return (\n \n \n \n
\n );\n }\n}\n","// Tinode config panel.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport HostSelector from '../widgets/host-selector.jsx';\n\nexport default class SettingsView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n transport: props.transport || 'def',\n serverAddress: props.serverAddress,\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleTransportSelected = this.handleTransportSelected.bind(this);\n this.handleServerAddressChange = this.handleServerAddressChange.bind(this);\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onUpdate({\n transport: this.state.transport,\n serverAddress: this.state.serverAddress\n });\n }\n\n handleTransportSelected(e) {\n this.setState({transport: e.currentTarget.value});\n }\n\n handleServerAddressChange(name) {\n this.setState({serverAddress: name});\n }\n\n render() {\n const names = {def: \"default\", ws: \"websocket\", lp: \"long polling\"};\n const transportOptions = [];\n ['def', 'ws', 'lp'].map((item) => {\n const id = 'transport-' + item;\n const name = names[item];\n transportOptions.push(\n \n \n \n \n );\n });\n\n return (\n \n );\n }\n};\n","// ValidationView: panel for confirming credentials, like email or phone.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n phone: {\n id: 'phone_dative',\n defaultMessage: 'phone',\n description: \"Dative case of 'phone', i.e. 'phone' in 'by phone'\",\n },\n email: {\n id: 'email_dative',\n defaultMessage: 'email',\n description: \"Dative case of 'email', i.e. 'email' in 'by email'\",\n }\n});\n\nclass ValidationView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n code: props.credCode || ''\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(e) {\n this.setState({code: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n } else if (e.key == 'Escape') {\n this.handleCancel(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.code && this.state.code.trim()) {\n this.props.onSubmit(this.props.credMethod, this.state.code.trim());\n }\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel();\n }\n\n\n render() {\n const { formatMessage } = this.props.intl;\n const methods = {'email': formatMessage(messages.email), 'tel': formatMessage(messages.phone)};\n let method = methods[this.props.credMethod] || this.props.credMethod;\n return (\n \n
\n \n
\n
\n {\n (numbers_only) => \n }\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(ValidationView);\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport SideNavbar from '../widgets/side-navbar.jsx';\nimport TopicCommon from '../widgets/topic-common.jsx';\n\nimport AccountSettingsView from './account-settings-view.jsx';\nimport ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport AccNotificationsView from './acc-notifications-view.jsx';\nimport AccSecurityView from './acc-security-view.jsx';\nimport AccSupportView from './acc-support-view.jsx';\nimport LoginView from './login-view.jsx';\nimport NewTopicView from './new-topic-view.jsx';\nimport PasswordResetView from './password-reset-view.jsx';\nimport SettingsView from './settings-view.jsx';\nimport ValidationView from './validation-view.jsx';\n\n// Panel titles for translation.\nconst messages = defineMessages({\n login: {\n id: 'sidepanel_title_login',\n description: 'Sidepanel title for LoginView.',\n defaultMessage: 'Sign In'\n },\n register: {\n id: 'sidepanel_title_register',\n description: 'Sidepanel title for CreateAccountView.',\n defaultMessage: 'Create Account'\n },\n settings: {\n id: 'sidepanel_title_settings',\n description: 'Sidepanel title for SettingsView.',\n defaultMessage: 'Settings'\n },\n edit: {\n id: 'sidepanel_title_account_settings',\n description: 'Sidepanel title for AccountSettingsView.',\n defaultMessage: 'Account Settings'\n },\n general: {\n id: 'panel_title_general',\n description: 'Title for TopicCommon.',\n defaultMessage: 'General'\n },\n security: {\n id: 'panel_title_security',\n description: 'Title for TopicSecirity and AccSecurity.',\n defaultMessage: 'Security'\n },\n crop: {\n id: 'panel_title_crop',\n description: 'Title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n notif: {\n id: 'sidepanel_title_acc_notifications',\n description: 'Sidepanel title for AccNotificationsView.',\n defaultMessage: 'Notifications'\n },\n support: {\n id: 'sidepanel_title_acc_support',\n description: 'Sidepanel title for AccSupportView.',\n defaultMessage: 'Support'\n },\n newtpk: {\n id: 'sidepanel_title_newtpk',\n description: 'Sidepanel title for NewTopicView.',\n defaultMessage: 'Start New Chat'\n },\n cred: {\n id: 'sidepanel_title_cred',\n description: 'Sidepanel title for ValidationView.',\n defaultMessage: 'Confirm Credentials'\n },\n reset: {\n id: 'sidepanel_title_reset',\n description: 'Sidepanel title for PasswordResetView.',\n defaultMessage: 'Reset Password'\n },\n archive: {\n id: 'sidepanel_title_archive',\n description: 'Sidepanel title for ContactsView-Archive.',\n defaultMessage: 'Archived Chats'\n },\n blocked: {\n id: 'sidepanel_title_blocked',\n description: 'Sidepanel title for ContactsView-Blocked.',\n defaultMessage: 'Blocked Chats'\n }\n});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleNewTopic = this.handleNewTopic.bind(this);\n }\n\n handleNewTopic() {\n this.props.onNavigate('newtpk');\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');\n\n let title, avatar, badges;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n badges = this.props.trustedBadges;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\n badges = null;\n }\n\n let onCancel;\n if (['login', 'contacts'].indexOf(view) == -1) {\n onCancel = this.props.onCancel;\n }\n\n return (\n \n
\n\n
\n\n
\n\n {view === 'login' ?\n
:\n\n view === 'register' ?\n
:\n\n view === 'settings' ?\n
:\n\n view === 'edit' ?\n
:\n\n (view === 'general' || view === 'crop') ?\n
:\n\n view === 'notif' ?\n
:\n\n view === 'security' ?\n
:\n\n view === 'support' ?\n
:\n\n (view === 'contacts' || view == 'archive' || view == 'blocked') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\n
:\n null}\n
\n );\n }\n};\n\nexport default injectIntl(SidepanelView);\n","import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js';\n\n// Detect server address from the URL\nexport function detectServerAddress() {\n let host = DEFAULT_HOST;\n if (typeof window.location == 'object') {\n if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {\n host = KNOWN_HOSTS.local;\n } else if (window.location.hostname) {\n host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');\n }\n }\n return host;\n}\n\n// Detect if the page is served over HTTPS.\nexport function isSecureConnection() {\n if (typeof window.location == 'object') {\n return window.location.protocol == 'https:';\n }\n return false;\n}\n\nexport function isLocalHost() {\n if (typeof window.location == 'object') {\n return window.location.hostname == 'localhost';\n }\n return false;\n}\n","// The top-level class to hold all functionality together.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport { initializeApp as firebaseInitApp } from 'firebase/app';\nimport { getMessaging as firebaseGetMessaging, getToken as firebaseGetToken,\n deleteToken as firebaseDelToken, onMessage as firebaseOnMessage } from 'firebase/messaging';\n\nimport Tinode from 'tinode-sdk';\n\nimport Alert from '../widgets/alert.jsx';\nimport ContextMenu from '../widgets/context-menu.jsx';\nimport ForwardDialog from '../widgets/forward-dialog.jsx';\n\nimport InfoView from './info-view.jsx';\nimport MessagesView from './messages-view.jsx';\nimport SidepanelView from './sidepanel-view.jsx';\n\nimport { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, FORWARDED_PREVIEW_LENGTH, LOGGING_ENABLED,\n MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';\nimport { PACKAGE_VERSION } from '../version.js';\nimport { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';\nimport { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { secondsToTime } from '../lib/strformat.js'\nimport { updateFavicon } from '../lib/utils.js';\n\n// Sound to play on message received.\nconst POP_SOUND = new Audio('audio/msg.mp3');\n\nconst messages = defineMessages({\n reconnect_countdown: {\n id: 'reconnect_countdown',\n defaultMessage: 'Disconnected. Reconnecting in {seconds}…',\n description: 'Message shown when an app update is available.'\n },\n reconnect_now: {\n id: 'reconnect_now',\n defaultMessage: 'Try now',\n description: 'Prompt for reconnecting now'\n },\n push_init_failed: {\n id: 'push_init_failed',\n defaultMessage: 'Failed to initialize push notifications',\n description: 'Error message when push notifications have failed to initialize.'\n },\n invalid_security_token: {\n id: 'invalid_security_token',\n defaultMessage: 'Invalid security token',\n description: 'Error message when resetting password.'\n },\n no_connection: {\n id: 'no_connection',\n defaultMessage: 'No connection',\n description: 'Warning that the user is offline.'\n },\n code_doesnot_match: {\n id: 'code_doesnot_match',\n defaultMessage: 'Code does not match',\n description: 'Error message when the credential validation code is incorrect.'\n },\n menu_item_info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n }\n});\n\nclass TinodeWeb extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n this.state = this.getBlankState();\n\n this.handleResize = this.handleResize.bind(this);\n this.handleHashRoute = this.handleHashRoute.bind(this);\n this.handleOnline = this.handleOnline.bind(this);\n this.checkForAppUpdate = this.checkForAppUpdate.bind(this);\n this.handleVisibilityEvent = this.handleVisibilityEvent.bind(this);\n this.handleError = this.handleError.bind(this);\n this.handleLoginRequest = this.handleLoginRequest.bind(this);\n this.handlePersistenceChange = this.handlePersistenceChange.bind(this);\n this.handleConnected = this.handleConnected.bind(this);\n this.handleAutoreconnectIteration = this.handleAutoreconnectIteration.bind(this);\n this.doLogin = this.doLogin.bind(this);\n this.handleCredentialsRequest = this.handleCredentialsRequest.bind(this);\n this.handleLoginSuccessful = this.handleLoginSuccessful.bind(this);\n this.handleDisconnect = this.handleDisconnect.bind(this);\n this.tnMeMetaDesc = this.tnMeMetaDesc.bind(this);\n this.tnMeContactUpdate = this.tnMeContactUpdate.bind(this);\n this.tnMeSubsUpdated = this.tnMeSubsUpdated.bind(this);\n this.resetContactList = this.resetContactList.bind(this);\n this.tnData = this.tnData.bind(this);\n this.tnInitFind = this.tnInitFind.bind(this);\n this.tnFndSubsUpdated = this.tnFndSubsUpdated.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleTopicSelected = this.handleTopicSelected.bind(this);\n this.handleHideMessagesView = this.handleHideMessagesView.bind(this);\n this.handleSendMessage = this.handleSendMessage.bind(this);\n this.handleNewChatInvitation = this.handleNewChatInvitation.bind(this);\n this.handleNewAccount = this.handleNewAccount.bind(this);\n this.handleNewAccountRequest = this.handleNewAccountRequest.bind(this);\n this.handleUpdatePasswordRequest = this.handleUpdatePasswordRequest.bind(this);\n this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);\n this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);\n this.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.bind(this);\n this.handleShowBlocked = this.handleShowBlocked.bind(this);\n this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this);\n this.handleCredAdd = this.handleCredAdd.bind(this);\n this.handleCredDelete = this.handleCredDelete.bind(this);\n this.handleCredConfirm = this.handleCredConfirm.bind(this);\n this.initFCMessaging = this.initFCMessaging.bind(this);\n this.toggleFCMToken = this.toggleFCMToken.bind(this);\n this.handlePushMessage = this.handlePushMessage.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);\n this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);\n this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);\n this.handleUnarchive = this.handleUnarchive.bind(this);\n this.handleChangePermissions = this.handleChangePermissions.bind(this);\n this.handleTagsUpdateRequest = this.handleTagsUpdateRequest.bind(this);\n this.handleLogout = this.handleLogout.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n this.handleDeleteTopicRequest = this.handleDeleteTopicRequest.bind(this);\n this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);\n this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);\n this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);\n this.handleReportTopic = this.handleReportTopic.bind(this);\n this.handleShowContextMenu = this.handleShowContextMenu.bind(this);\n this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this);\n this.handleHideContextMenu = this.handleHideContextMenu.bind(this);\n this.handleShowAlert = this.handleShowAlert.bind(this);\n this.handleShowInfoView = this.handleShowInfoView.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);\n this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);\n this.handleResetPassword = this.handleResetPassword.bind(this);\n this.handleContextMenuAction = this.handleContextMenuAction.bind(this);\n\n this.handleShowForwardDialog = this.handleShowForwardDialog.bind(this);\n this.handleHideForwardDialog = this.handleHideForwardDialog.bind(this);\n\n this.sendMessageToTopic = this.sendMessageToTopic.bind(this);\n }\n\n getBlankState() {\n const settings = LocalStorageUtil.getObject('settings') || {};\n const persist = !!LocalStorageUtil.getObject('keep-logged-in');\n\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n // Try to re-login on new connection.\n autoLogin: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n incognitoMode: false,\n // Persistent request to enable alerts.\n desktopAlerts: persist && !!settings.desktopAlerts,\n // Enable / disable checkbox.\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebaseInitApp != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: persist ? LocalStorageUtil.getObject('firebase-token') : null,\n\n applicationVisible: !document.hidden,\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n myTrustedBadges: [],\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\n persist: persist,\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newTopicParams: null,\n loginDisabled: false,\n displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),\n infoPanel: undefined,\n mobilePanel: 'sidepanel',\n\n contextMenuVisible: false,\n contextMenuBounds: null,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuItems: [],\n\n forwardDialogVisible: false,\n forwardMessage: null,\n\n // Modal alert dialog.\n alertVisible: false,\n alertParams: {},\n\n // Chats as shown in the ContactsView\n chatList: [],\n // Contacts returned by a search query.\n searchResults: [],\n // Merged results of a search query and p2p chats.\n searchableContacts: [],\n credMethod: undefined,\n credCode: undefined,\n // Topic to go to after login.\n requestedTopic: undefined\n };\n }\n\n componentDidMount() {\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', (e) => { this.handleOnline(true); });\n window.addEventListener('offline', (e) => { this.handleOnline(false); });\n window.addEventListener('hashchange', this.handleHashRoute);\n\n // Process background notifications from the service worker.\n if (typeof BroadcastChannel == 'function') {\n const serviceWorkerChannel = new BroadcastChannel('tinode-sw');\n serviceWorkerChannel.addEventListener('message', this.handlePushMessage);\n } else {\n // Safari is broken by design.\n console.warn(\"Your browser does not support BroadcastChannel. Some features will not be available\");\n }\n\n // Window/tab visible or invisible for pausing timers.\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n\n new Promise((resolve, reject) => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport,\n this.props.intl.locale, this.state.persist, resolve);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n }).then(() => {\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n this.initFCMessaging().then(() => {\n if (this.state.desktopAlerts) {\n this.tinode.setDeviceToken(this.state.firebaseToken);\n }\n }).catch(() => {\n // do nothing: handled earlier.\n // catch needed to pervent unnecessary logging of error.\n });\n }\n\n // Parse and save the hash navigation params.\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n\n // Read contacts from cache.\n this.resetContactList();\n\n const token = this.state.persist ? LocalStorageUtil.getObject('auth-token') : undefined;\n if (token) {\n this.setState({autoLogin: true});\n\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n // Maybe navigate to home screen.\n if (!['cred', 'reset', 'register'].includes(parsedNav.path[0])) {\n // Save possible topic name.\n this.setState({requestedTopic: parsedNav.path[1]});\n HashNavigation.navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\n });\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n }\n\n // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n static tnSetup(serverAddress, transport, locale, persistentCache, onSetupCompleted) {\n const tinode = new Tinode({appName: APP_NAME, host: serverAddress, apiKey: API_KEY, transport: transport,\n secure: isSecureConnection(), persist: persistentCache}, onSetupCompleted);\n tinode.setHumanLanguage(locale);\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n // Tinode received a push notification from the server.\n handlePushMessage(payload) {\n const data = payload.data || {};\n switch (data.what) {\n case 'msg':\n if (Tinode.isChannelTopicName(data.topic)) {\n // The last argument is a fake user Id: otherwise the update is seen as one from the current user.\n this.tinode.oobNotification('msg', data.topic, data.seq, 'fake-uid');\n }\n break;\n case 'read':\n // Message read on another device, clear it here.\n this.tinode.oobNotification('read', data.topic, data.seq);\n break;\n default:\n console.warn(\"Unknown push type ignored\", data.what, data);\n }\n }\n\n initFCMessaging() {\n const {formatMessage, locale} = this.props.intl;\n const onError = (msg, err) => {\n console.error(msg, err);\n this.handleError(formatMessage(messages.push_init_failed), 'err');\n this.setState({desktopAlertsEnabled: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n\n try {\n this.fcm = firebaseGetMessaging(firebaseInitApp(FIREBASE_INIT, APP_NAME));\n return navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n reg.active.postMessage(JSON.stringify({locale: locale, version: PACKAGE_VERSION}));\n return reg;\n }).then((reg) => {\n // Request token.\n return TinodeWeb.requestFCMToken(this.fcm, reg);\n }).then((token) => {\n const persist = LocalStorageUtil.getObject('keep-logged-in');\n if (token != this.state.firebaseToken) {\n this.tinode.setDeviceToken(token);\n if (persist) {\n LocalStorageUtil.setObject('firebase-token', token);\n }\n }\n this.setState({firebaseToken: token, desktopAlerts: true});\n if (persist) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n\n // Handhe FCM pushes\n // (a) for channels always,\n // (b) pushes when the app is in foreground but has no focus.\n firebaseOnMessage(this.fcm, (payload) => { this.handlePushMessage(payload); });\n }).catch((err) => {\n // SW registration or FCM has failed :(\n onError(err);\n throw err;\n });\n } catch (err) {\n onError(err);\n return Promise.reject(err);\n }\n }\n\n // Google's FCM API is idiotic.\n static requestFCMToken(fcm, sw) {\n return firebaseGetToken(fcm, {\n serviceWorkerRegistration: sw,\n vapidKey: FIREBASE_INIT.messagingVapidKey\n }).then((token) => {\n if (token) {\n return token;\n } else {\n // Try to request permissions.\n return Notification.requestPermission().then((permission) => {\n if (permission === 'granted') {\n return firebaseGetToken(fcm, {\n serviceWorkerRegistration: reg,\n vapidKey: FIREBASE_INIT.messagingVapidKey\n }).then((token) => {\n if (token) {\n return token;\n } else {\n throw new Error(\"Failed to initialize notifications\");\n }\n });\n } else {\n throw new Error(\"No permission to send notifications: \" + permission);\n }\n });\n }\n });\n }\n\n handleResize() {\n const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n if (this.state.displayMobile != mobile) {\n this.setState({displayMobile: mobile});\n }\n }\n\n // Check if a newer version of TinodeWeb app is available at the server.\n checkForAppUpdate(reg) {\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n const msg = <>\n \n \n .>;\n this.handleError(msg, 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n const hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','notif','security','support','general','crop',\n 'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {\n this.setState({sidePanelSelected: hash.path[0]});\n } else {\n console.warn(\"Unknown sidepanel view\", hash.path[0]);\n }\n\n // Topic for MessagesView selector.\n let topicName = hash.path[1] || null;\n if (topicName != this.state.topicSelected) {\n if (!Tinode.topicType(topicName)) {\n // Clear invalid topic name.\n topicName = null;\n }\n this.setState({\n topicSelected: topicName,\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)\n });\n }\n } else {\n // Empty hashpath\n this.setState({sidePanelSelected: '', topicSelected: null});\n }\n\n // Save validation credentials, if available.\n if (hash.params.method) {\n this.setState({ credMethod: hash.params.method });\n }\n if (hash.params.code) {\n this.setState({ credCode: hash.params.code });\n }\n\n // Additional parameters of panels.\n this.setState({\n infoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n\n handleOnline(online) {\n if (online) {\n this.handleError();\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n } else {\n this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n handleVisibilityEvent() {\n this.setState({applicationVisible: !document.hidden});\n }\n\n handleError(err, level, action, actionText) {\n this.setState({errorText: err, errorLevel: level, errorAction: action, errorActionText: actionText});\n }\n\n // User clicked Login button in the side panel.\n handleLoginRequest(login, password) {\n this.setState({\n loginDisabled: true,\n login: login,\n password: password,\n loadSpinnerVisible: true,\n autoLogin: true\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});\n } else {\n this.tinode.connect().catch((err) => {\n // Socket error\n this.setState({loginDisabled: false, autoLogin: false, loadSpinnerVisible: false});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Enable or disable saving the password and IndexedDB.\n handlePersistenceChange(persist) {\n if (persist) {\n this.tinode.initStorage().then(() => {\n LocalStorageUtil.setObject('keep-logged-in', true);\n this.setState({persist: true});\n });\n } else {\n this.tinode.clearStorage().then(() => {\n LocalStorageUtil.setObject('keep-logged-in', false);\n this.setState({persist: false});\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n // Just to be sure.\n clearInterval(this.reconnectCountdown);\n this.handleError();\n\n const params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none')\n });\n\n if (this.state.autoLogin) {\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\n }\n }\n\n // Called for each auto-reconnect iteration.\n handleAutoreconnectIteration(sec, prom) {\n clearInterval(this.reconnectCountdown);\n\n if (sec < 0) {\n // Clear error\n this.handleError();\n return;\n }\n\n if (prom) {\n // Reconnecting now\n prom.then(() => {\n // Reconnected: clear error\n this.handleError();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n return;\n }\n\n const {formatMessage} = this.props.intl;\n let count = sec / 1000;\n count = count | count;\n this.reconnectCountdown = setInterval(() => {\n const timeLeft = (count > 99) ? secondsToTime(count) : count;\n this.handleError(\n formatMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatMessage(messages.reconnect_now)\n );\n count -= 1;\n }, 1000);\n }\n\n // Connection lost\n handleDisconnect(err) {\n this.setState({\n connected: false,\n ready: false,\n topicSelectedOnline: false,\n errorText: err && err.message ? err.message : \"Disconnected\",\n errorLevel: err && err.message ? 'err' : 'warn',\n loginDisabled: false,\n contextMenuVisible: false,\n forwardDialogVisible: false,\n serverVersion: \"no connection\"\n });\n }\n\n doLogin(login, password, cred) {\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n HashNavigation.navigateTo('');\n return;\n }\n // Sanitize and package credentail.\n cred = Tinode.credential(cred);\n // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n let promise = null;\n let token = this.tinode.getAuthToken();\n if (login && password) {\n token = null;\n this.setState({password: null});\n promise = this.tinode.loginBasic(login, password, cred);\n } else if (token) {\n promise = this.tinode.loginToken(token.token, cred);\n }\n\n if (promise) {\n promise.then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n this.setState({loadSpinnerVisible: false});\n if (cred) {\n this.handleError(this.props.intl.formatMessage(messages.code_doesnot_match), 'warn');\n }\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful();\n }\n }).catch((err) => {\n // Login failed, report error.\n this.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined,\n loadSpinnerVisible: false,\n autoLogin: false\n });\n this.handleError(err.message, 'err');\n if (token) {\n this.handleLogout();\n }\n HashNavigation.navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n HashNavigation.navigateTo('');\n this.setState({loginDisabled: false});\n }\n }\n\n handleCredentialsRequest(params) {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n }\n\n handleLoginSuccessful() {\n this.handleError();\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n\n const goToTopic = this.state.requestedTopic;\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n const me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID(),\n autoLogin: true,\n requestedTopic: undefined,\n });\n // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n me.subscribe(\n me.startMetaQuery().\n withLaterSub().\n withDesc().\n withTags().\n withCred().\n build()\n ).catch((err) => {\n this.tinode.disconnect();\n localStorage.removeItem('auth-token');\n this.handleError(err.message, 'err');\n HashNavigation.navigateTo('');\n }).finally(() => {\n this.setState({loadSpinnerVisible: false});\n });\n let urlHash = HashNavigation.setUrlSidePanel(window.location.hash, 'contacts');\n if (goToTopic) {\n urlHash = HashNavigation.setUrlTopic(urlHash, goToTopic);\n }\n HashNavigation.navigateTo(urlHash);\n }\n\n tnMeMetaDesc(desc) {\n if (desc) {\n if (desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\n });\n }\n if (desc.trusted) {\n const badges = [];\n for (const [key, val] of Object.entries(desc.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n this.setState({\n myTrustedBadges: badges,\n });\n }\n if (desc.acs) {\n this.setState({\n incognitoMode: !desc.acs.isPresencer()\n });\n }\n }\n }\n\n // Reactions to updates to the contact list.\n tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedOnline: (what == 'on')});\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // Check if the topic is archived. If so, don't play a sound.\n const topic = this.tinode.getTopic(cont.topic);\n const archived = topic && topic.isArchived();\n\n // New message received. Maybe the message is from the current user, then unread is 0.\n if (cont.unread > 0 && this.state.messageSounds && !archived) {\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (document.hidden || this.state.topicSelected != cont.topic) {\n POP_SOUND.play();\n }\n }\n // Reorder contact list to use possibly updated 'touched'.\n this.resetContactList();\n } else if (what == 'recv') {\n // Explicitly ignoring \"recv\" -- it causes no visible updates to contact list.\n } else if (what == 'gone' || what == 'unsub') {\n // Topic deleted or user unsubscribed. Remove topic from view.\n // If the currently selected topic is gone, clear the selection.\n if (this.state.topicSelected == cont.topic) {\n this.handleTopicSelected(null);\n }\n // Redraw without the deleted topic.\n this.resetContactList();\n } else if (what == 'acs') {\n // Permissions changed. If it's for the currently selected topic,\n // update the views.\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedAcs: cont.acs});\n }\n } else if (what == 'del') {\n // TODO: messages deleted (hard or soft) -- update pill counter.\n } else if (what == 'upd') {\n // upd - handled by the SDK. Explicitly ignoring here.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n console.info(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n\n tnMeSubsUpdated(unused) {\n this.resetContactList();\n }\n\n // Merge search results and contact list to create a single flat\n // list of known contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n const merged = {};\n\n // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n for (const c of chatList) {\n if (Tinode.isP2PTopicName(c.topic)) {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n }\n\n // Add all foundCountacts if they have not been added already.\n for (const c of foundContacts) {\n if (!merged[c.user]) {\n merged[c.user] = c;\n }\n }\n\n return Object.values(merged);\n }\n\n resetContactList() {\n const newState = {\n chatList: []\n };\n\n if (!this.state.ready) {\n newState.ready = true;\n }\n\n this.tinode.getMeTopic().contacts((c) => {\n if (!c.topic && !c.user) {\n // Contacts expect c.topic to be set.\n c.topic = c.name;\n }\n\n newState.chatList.push(c);\n if (this.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n });\n\n const past = new Date(0);\n newState.chatList.sort((a, b) => {\n return (a.touched || past).getTime() - (b.touched || past).getTime();\n });\n\n // Merge search results and chat list.\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n }\n\n // Sending \"received\" notifications\n tnData(data) {\n const topic = this.tinode.getTopic(data.topic);\n if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(() => {\n this.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, RECEIVED_DELAY);\n }\n }\n\n /* Fnd topic: find contacts by tokens */\n tnInitFind() {\n const fnd = this.tinode.getFndTopic();\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.subscribe(fnd.startMetaQuery().withSub().build()).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n tnFndSubsUpdated() {\n const foundContacts = [];\n // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n this.tinode.getFndTopic().contacts((s) => {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\n }\n\n /** Called when the user enters a contact into the contact search field in the NewAccount panel\n @param query {Array} is an array of contacts to search for\n */\n handleSearchContacts(query) {\n const fnd = this.tinode.getFndTopic();\n fnd.setMeta({desc: {public: query}}).then((ctrl) => {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User clicked on a topic in the side panel or deleted a topic.\n handleTopicSelected(topicName) {\n // Clear newTopicParams after use.\n if (this.state.newTopicParams && this.state.newTopicParams._topicName != topicName) {\n this.setState({\n newTopicParams: null\n });\n }\n\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n infoPanel: undefined\n });\n // Different topic selected.\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: this.tinode.isTopicOnline(topicName),\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName),\n forwardMessage: null\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName));\n }\n } else {\n // Currently selected contact deleted\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'sidepanel',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n infoPanel: undefined,\n forwardMessage: null\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));\n }\n }\n\n // In mobile view user requested to show sidepanel\n handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null));\n }\n\n // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n // - msg - Drafty message with content\n // - promise - Promise to be resolved when the upload is completed\n // - uploader - for tracking progress\n // - head - head dictionary to be attached to the message\n handleSendMessage(msg, promise, uploader, head) {\n const topic = this.tinode.getTopic(this.state.topicSelected);\n this.sendMessageToTopic(topic, msg, promise, uploader, head);\n }\n\n sendMessageToTopic(topic, msg, promise, uploader, head) {\n msg = topic.createMessage(msg, false);\n // The uploader is used to show progress.\n msg._uploader = uploader;\n\n if (head) {\n msg.head = Object.assign(msg.head || {}, head);\n }\n\n if (!topic.isSubscribed()) {\n // Topic is not subscribed yet. Subscribe.\n if (!promise) {\n promise = Promise.resolve();\n }\n promise = promise\n .then(() => topic.subscribe())\n .then(() => {\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n this.sendMessage(pub);\n }\n });\n });\n }\n\n if (promise) {\n promise = promise.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise)\n .then((ctrl) => {\n if (topic.isArchived()) {\n return topic.archive(false);\n }\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleNewChatInvitation(topicName, action) {\n const topic = this.tinode.getTopic(topicName);\n let response = null;\n switch (action) {\n case 'accept':\n // Accept given permissions.\n const mode = topic.getAccessMode().getGiven();\n response = topic.setMeta({sub: {mode: mode}});\n if (topic.isP2PType()) {\n // For P2P topics change 'given' permission of the peer too.\n // In p2p topics the other user has the same name as the topic.\n response = response.then((ctrl) => {\n topic.setMeta({sub: {user: topicName, mode: mode}});\n });\n }\n break;\n case 'delete':\n // Ignore invitation by deleting it.\n response = topic.delTopic(true);\n break;\n case 'block':\n // Ban the topic making futher invites impossible.\n // Just self-ban.\n const am = topic.getAccessMode().updateWant('-JP').getWant();\n response = topic.setMeta({sub: {mode: am}}).then((ctrl) => {\n return this.handleTopicSelected(null);\n });\n break;\n default:\n console.warn(\"Unknown invitation action\", '\"' + action + '\"\"');\n }\n\n if (response != null) {\n response.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // User chose a Sign Up menu item.\n handleNewAccount() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register'));\n }\n\n // Actual registration of a new account.\n handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n // Clear old error, if any.\n this.handleError();\n\n this.tinode.connect(this.state.serverAddress)\n .then(() => {\n return this.tinode.createAccountBasic(login_, password_,\n {public: public_, tags: tags_, cred: Tinode.credential(cred_)});\n }).then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful(this);\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleToggleIncognitoMode(on) {\n // Make state undefined.\n this.setState({incognitoMode: null});\n\n const me = this.tinode.getMeTopic();\n const am = me.getAccessMode().updateWant(on ? '-P' : '+P').getWant();\n me.setMeta({sub: {mode: am}}).catch((err) => {\n // Request failed, keep existing state.\n this.setState({incognitoMode: !on});\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountTagsRequest(tags) {\n this.tinode.getMeTopic().setMeta({tags: tags})\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User chose Settings menu item.\n handleSettings() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n const serverAddress = settings.serverAddress || this.state.serverAddress;\n const transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport, this.props.intl.locale,\n LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n LocalStorageUtil.setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, ''));\n }\n\n // User chose 'Archived chats'.\n handleShowArchive() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'archive' : ''));\n }\n\n // User viewes 'Blocked chats'.\n handleShowBlocked() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'blocked' : ''));\n }\n\n toggleFCMToken(enabled) {\n if (enabled) {\n this.setState({desktopAlerts: null});\n if (!this.state.firebaseToken) {\n this.initFCMessaging();\n } else {\n this.setState({desktopAlerts: true});\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }\n } else if (this.state.firebaseToken && this.fcm) {\n firebaseDelToken(this.fcm).catch((err) => {\n console.error(\"Unable to delete token.\", err);\n }).finally(() => {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n localStorage.removeItem('firebase-token');\n this.setState({desktopAlerts: false, firebaseToken: null});\n // Inform the server that the token was deleted.\n this.tinode.setDeviceToken(null);\n });\n } else {\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n }\n\n handleToggleMessageSounds(enabled) {\n this.setState({messageSounds: enabled});\n LocalStorageUtil.updateObject('settings', {\n messageSoundsOff: !enabled\n });\n }\n\n handleCredAdd(method, value) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: method, val: value}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredDelete(method, value) {\n const me = this.tinode.getMeTopic();\n me.delCredential(method, value).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredConfirm(method, response) {\n this.handleCredentialsRequest({cred: [method]});\n }\n\n // User clicked Cancel button in Setting or Sign Up panel.\n handleSidepanelCancel() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n let path = '';\n if (['security','support','general','notif'].includes(parsed.path[0])) {\n path = 'edit';\n } else if ('crop' == parsed.path[0]) {\n path = 'general';\n } else if ('blocked' == parsed.path[0]) {\n path = 'security';\n } else if (this.state.myUserId) {\n path = 'contacts';\n }\n parsed.path[0] = path;\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n delete parsed.params.scheme;\n delete parsed.params.token;\n }\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n this.setState({errorText: '', errorLevel: null});\n }\n\n // Sidepanel navigator. No need to bind to 'this'.\n basicNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));\n }\n\n // Topic info navigator. No need to bind to 'this'.\n infoNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlInfoPanel(window.location.hash, hash));\n }\n\n // Request to start a topic, new or selected from search results, or \"by ID\".\n handleStartTopicRequest(topicName, newTopicParams, isChannel) {\n // Check if topic is indeed new. If not, launch it.\n if (topicName && this.tinode.isTopicCached(topicName)) {\n this.handleTopicSelected(topicName);\n return;\n }\n\n const params = {};\n if (Tinode.isP2PTopicName(topicName)) {\n // Because we are initialing the subscription, set 'want' to all permissions.\n params.sub = {mode: DEFAULT_P2P_ACCESS_MODE};\n // Give the other user all permissions too.\n params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};\n } else {\n topicName = topicName || this.tinode.newGroupTopicName(isChannel);\n if (newTopicParams) {\n params.desc = {public: newTopicParams.public, private: {comment: newTopicParams.private}};\n params.tags = newTopicParams.tags;\n }\n }\n params._topicName = topicName;\n this.setState({newTopicParams: params}, () => {this.handleTopicSelected(topicName)});\n }\n\n // New topic was created, here is the new topic name.\n handleNewTopicCreated(oldName, newName) {\n if (this.state.topicSelected == oldName && oldName != newName) {\n // If the current URl contains the old topic name, replace it with new.\n // Update the name of the selected topic first so the navigator doen't clear\n // the state.\n this.setState({topicSelected: newName}, () => {\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName));\n });\n }\n }\n\n handleTopicUpdateRequest(topicName, pub, priv, defacs) {\n this.handleError();\n\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const params = {};\n let attachments;\n if (pub) {\n if (pub.photo) {\n if (pub.photo.ref && pub.photo.ref != Tinode.DEL_CHAR) {\n attachments = [pub.photo.ref];\n } else if (!pub.photo.data || pub.photo.data == Tinode.DEL_CHAR) {\n pub.photo = Tinode.DEL_CHAR;\n }\n }\n params.public = pub;\n }\n\n if (typeof priv == 'string') {\n params.private = (priv === Tinode.DEL_CHAR) ?\n Tinode.DEL_CHAR : {comment: priv};\n }\n if (defacs) {\n params.defacs = defacs;\n }\n topic.setMeta({desc: params, attachments: attachments}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleUnarchive(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.archive(false);\n }\n }\n\n handleUpdatePasswordRequest(password) {\n this.handleError();\n\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleChangePermissions(topicName, mode, uid) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const am = topic.getAccessMode();\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n topic.setMeta({sub: {user: uid, mode: mode}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleTagsUpdateRequest(topicName, tags) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.setMeta({tags: tags}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleLogout() {\n updateFavicon(0);\n\n // Remove stored data.\n localStorage.removeItem('auth-token');\n localStorage.removeItem('firebase-token');\n localStorage.removeItem('settings');\n if (this.state.firebaseToken) {\n firebaseDelToken(this.fcm);\n }\n\n clearInterval(this.reconnectCountdown);\n\n let cleared;\n if (this.tinode) {\n cleared = this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n } else {\n cleared = Promose.resolve();\n }\n this.setState(this.getBlankState());\n\n cleared.then(() => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress,\n this.state.transport, this.props.intl.locale, LocalStorageUtil.getObject('keep-logged-in'), () => {\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n HashNavigation.navigateTo('');\n })\n });\n }\n\n handleDeleteAccount() {\n this.tinode.delCurrentUser(true).then((ctrl) => {\n this.handleLogout();\n });\n }\n\n handleDeleteTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request to hard-delete topic.\n topic.delTopic(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleDeleteMessagesRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request hard-delete all messages.\n topic.delMessagesAll(true).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleLeaveUnsubRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.leave(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleBlockTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleReportTopic(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Publish spam report.\n this.tinode.publish(Tinode.TOPIC_SYS, Tinode.Drafty.attachJSON(null, {\n 'action': 'report',\n 'target': topicName\n }));\n\n // Remove J and P permissions.\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {x: params.x, y: params.y},\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: this.selfRef.current.getBoundingClientRect()\n });\n }\n\n //\n handleShowForwardDialog(params) {\n if (this.state.sidePanelSelected == 'newtpk') {\n // If the Find panel is active, close it.\n this.handleSidepanelCancel();\n }\n const header = '➦ ' + params.userName;\n const content = typeof params.content == 'string' ?\n Tinode.Drafty.init(params.content) : Tinode.Drafty.forwardedContent(params.content);\n const preview = Tinode.Drafty.preview(content, FORWARDED_PREVIEW_LENGTH,\n undefined, params.forwarded != null);\n const msg = Tinode.Drafty.append(\n Tinode.Drafty.appendLineBreak(\n Tinode.Drafty.mention(header, params.userFrom)),\n content);\n\n const msgPreview = Tinode.Drafty.quote(header, params.userFrom, preview);\n const head = {\n forwarded: params.topicName + ':' + params.seq\n };\n this.setState({\n forwardDialogVisible: true,\n forwardMessage: { head: head, msg: msg, preview: msgPreview }\n });\n }\n\n defaultTopicContextMenu(topicName) {\n const topic = this.tinode.getTopic(topicName);\n\n let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false, archived = false;\n if (topic) {\n subscribed = topic.isSubscribed();\n archived = topic.isArchived();\n\n const acs = topic.getAccessMode();\n if (acs) {\n muted = acs.isMuted();\n blocked = !acs.isJoiner();\n self_blocked = !acs.isJoiner('want');\n deleter = acs.isDeleter();\n }\n }\n\n return [\n subscribed ? {\n title: this.props.intl.formatMessage(messages.menu_item_info),\n handler: this.handleShowInfoView\n } : null,\n subscribed ? 'messages_clear' : null,\n subscribed && deleter ? 'messages_clear_hard' : null,\n muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',\n self_blocked ? 'topic_unblock' : 'topic_block',\n archived ? 'topic_restore' : 'topic_archive',\n 'topic_delete'\n ];\n }\n\n handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n\n handleHideForwardDialog(keepForwardedMessage) {\n this.setState({\n forwardDialogVisible: false,\n forwardMessage: keepForwardedMessage ? this.state.forwardMessage : null\n });\n }\n\n handleContextMenuAction(action, promise, params) {\n if (action == 'topic_archive') {\n if (promise && params.topicName && params.topicName == this.state.topicSelected) {\n promise.then(() => {\n this.handleTopicSelected(null);\n });\n }\n } else if (action == 'menu_item_forward') {\n this.handleShowForwardDialog(params);\n }\n }\n\n handleShowAlert(title, content, onConfirm, confirmText, onReject, rejectText) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirmText,\n onReject: onReject,\n reject: rejectText\n }\n });\n }\n\n handleShowInfoView() {\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', 'info'));\n this.setState({infoPanel: 'info'});\n }\n\n handleMemberUpdateRequest(topicName, added, removed) {\n if (!topicName) {\n return;\n }\n\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map((uid) => {\n topic.invite(uid, null).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map((uid) => {\n topic.delSubscription(uid).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n }\n\n handleValidateCredentialsRequest(cred, code) {\n if (this.tinode.isAuthenticated()) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: cred, resp: code}})\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n } else {\n this.setState({credMethod: cred, credCode: code});\n this.doLogin(null, null, {meth: cred, resp: code});\n }\n }\n\n handlePasswordResetRequest(method, value) {\n // If already connected, connnect() will return a resolved promise.\n return this.tinode.connect()\n .then(() => {\n return this.tinode.requestResetAuthSecret('basic', method, value);\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n handleResetPassword(scheme, newPassword, token) {\n token = base64ReEncode(token);\n if (!token) {\n this.handleError(this.props.intl.formatMessage(messages.invalid_security_token), 'err');\n } else {\n this.tinode.connect()\n .then(() => {\n return this.tinode.updateAccountBasic(null, null, newPassword, {token: token});\n })\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n }\n\n render() {\n return (\n \n {this.state.contextMenuVisible ?\n
{\n if (topicName == this.state.topicSelected) {\n this.handleTopicSelected(null);\n }\n }}\n onError={this.handleError} />\n :\n null\n }\n {this.state.forwardDialogVisible ?\n \n :\n null\n }\n { this.setState({alertVisible: false}); }) : null}\n reject={this.state.alertParams.reject}\n onConfirm={() => { this.setState({alertVisible: false}); this.state.alertParams.onConfirm(); }}\n confirm={this.state.alertParams.confirm}\n />\n \n\n \n\n {this.state.infoPanel ?\n \n :\n null\n }\n \n );\n }\n};\n\nexport default injectIntl(TinodeWeb);\n","// Put all packages together.\n// Used to generate umd/index.prod.js\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport { IntlProvider } from 'react-intl';\n\nimport allMessages from './messages.json';\nimport TinodeWeb from './views/tinode-web.jsx';\nimport HashNavigation from './lib/navigation.js';\n\n// Detect human language to use in the UI:\n// Check parameters from URL hash #?hl=ru, then browser, then use 'en' as a fallback.\nconst { params } = HashNavigation.parseUrlHash(window.location.hash);\nconst language = (params && params.hl) ||\n (navigator.languages && navigator.languages[0]) ||\n navigator.language ||\n navigator.userLanguage ||\n 'en';\n\n// Get the base language 'en' from a more specific 'en_GB' or 'en-US' as a partial fallback.\nconst baseLanguage = language.toLowerCase().split(/[-_]/)[0];\n\n// Try the full locale first, then the locale without the region code, fallback to 'en'.\nconst htmlLang = allMessages[language] ? langauge : allMessages[baseLanguage] ? baseLanguage : 'en';\nconst messages = allMessages[htmlLang];\n\n// Set lang attribute of the HTML element: \ndocument.getElementsByTagName('html')[0].setAttribute('lang', htmlLang);\n\nReactDOM.render(\n \n \n \n \n ,\n document.getElementById('mountPoint')\n);\n"],"names":["exports","toArray","arr","Array","prototype","slice","call","promisifyRequest","request","Promise","resolve","reject","onsuccess","result","onerror","error","promisifyRequestCall","obj","method","args","p","apply","then","promisifyCursorRequestCall","value","Cursor","proxyProperties","ProxyClass","targetProp","properties","forEach","prop","Object","defineProperty","get","this","set","val","proxyRequestMethods","Constructor","arguments","proxyMethods","proxyCursorRequestMethods","Index","index","_index","cursor","_cursor","_request","ObjectStore","store","_store","Transaction","idbTransaction","_tx","complete","oncomplete","onabort","UpgradeDB","db","oldVersion","transaction","_db","DB","openDb","name","version","upgradeCallback","indexedDB","onupgradeneeded","event","deleteDb","IDBIndex","IDBCursor","methodName","createIndex","IDBObjectStore","objectStore","IDBTransaction","createObjectStore","IDBDatabase","funcName","replace","callback","length","nativeObject","getAll","query","count","instance","items","iterateCursor","push","undefined","continue","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","module","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","enumerable","g","globalThis","Function","e","window","hasOwnProperty","React","ReactDOM","ReactIntl","stringToByteArray$1","str","out","i","c","charCodeAt","Deferred","constructor","promise","wrapCallback","catch","FirebaseError","Error","code","message","customData","super","setPrototypeOf","captureStackTrace","ErrorFactory","create","service","serviceName","errors","data","fullCode","template","PATTERN","_","String","replaceTemplate","fullMessage","deepEqual","b","aKeys","keys","bKeys","k","includes","aProp","bProp","isObject","thing","getModularInstance","_delegate","Component","instanceFactory","type","multipleInstances","serviceProps","instantiationMode","onInstanceCreated","setInstantiationMode","mode","setMultipleInstances","setServiceProps","props","setInstanceCreatedCallback","DEFAULT_ENTRY_NAME","Provider","container","component","instances","Map","instancesDeferred","instancesOptions","onInitCallbacks","identifier","normalizedIdentifier","normalizeInstanceIdentifier","has","deferred","isInitialized","shouldAutoInitialize","getOrInitializeService","instanceIdentifier","getImmediate","options","_a","optional","getComponent","setComponent","isComponentEager","instanceDeferred","entries","clearInstance","delete","async","services","from","values","all","filter","map","INTERNAL","_delete","isComponentSet","getOptions","initialize","opts","onInit","existingCallbacks","Set","add","existingInstance","invokeOnInitCallbacks","callbacks","ComponentContainer","providers","addComponent","provider","getProvider","addOrOverwriteComponent","getProviders","LogLevel","levelStringToEnum","DEBUG","VERBOSE","INFO","WARN","ERROR","SILENT","defaultLogLevel","ConsoleMethod","defaultLogHandler","logType","logLevel","now","Date","toISOString","console","PlatformLoggerServiceImpl","getPlatformInfoString","isVersionServiceProvider","library","logString","join","name$o","version$1","logger","_logLevel","_logHandler","_userLogHandler","TypeError","setLogLevel","logHandler","userLogHandler","debug","log","info","warn","PLATFORM_LOG_STRING","_apps","_components","_addComponent","app","_registerComponent","componentName","ERROR_FACTORY","FirebaseAppImpl","config","_isDeleted","_options","assign","_config","_name","_automaticDataCollectionEnabled","automaticDataCollectionEnabled","_container","checkDestroyed","isDeleted","appName","initializeApp","rawConfig","existingApp","newApp","registerVersion","libraryKeyOrName","variant","libraryMismatch","match","versionMismatch","warning","PENDING_TIMEOUT_MS","PACKAGE_VERSION","INTERNAL_AUTH_VERSION","TOKEN_EXPIRATION_BUFFER","isServerError","getInstallationsEndpoint","projectId","extractAuthTokenInfoFromResponse","response","token","requestStatus","expiresIn","responseExpiresIn","Number","creationTime","getErrorFromResponse","requestName","errorData","json","serverCode","serverMessage","serverStatus","status","getHeaders","apiKey","Headers","Accept","getHeadersWithAuth","appConfig","refreshToken","headers","append","getAuthorizationHeader","retryIfServerError","fn","sleep","ms","setTimeout","VALID_FID_PATTERN","generateFid","fidByteArray","Uint8Array","self","crypto","msCrypto","getRandomValues","fid","array","btoa","fromCharCode","substr","encode","test","getKey","appId","fidChangeCallbacks","fidChanged","callFidChangeCallbacks","channel","getBroadcastChannel","postMessage","closeBroadcastChannel","broadcastFidChange","broadcastChannel","BroadcastChannel","onmessage","size","close","OBJECT_STORE_NAME","dbPromise","getDbPromise","upgradeDB","tx","oldValue","put","remove","update","updateFn","newValue","getInstallationEntry","registrationPromise","installationEntry","oldEntry","clearTimedOutRequest","registrationStatus","updateOrCreateInstallationEntry","entryWithPromise","navigator","onLine","inProgressEntry","registrationTime","registeredInstallationEntry","endpoint","body","authVersion","sdkVersion","JSON","stringify","fetch","ok","responseValue","authToken","createInstallationRequest","registerInstallation","waitUntilFidRegistration","triggerRegistrationIfNecessary","entry","updateInstallationRequest","generateAuthTokenRequest","platformLoggerProvider","getGenerateAuthTokenEndpoint","platformLogger","installation","refreshAuthToken","installations","forceRefresh","tokenPromise","isEntryRegistered","oldAuthToken","isAuthTokenExpired","isAuthTokenValid","updateAuthTokenRequest","waitUntilAuthTokenRequest","inProgressAuthToken","requestTime","makeAuthTokenRequestInProgressEntry","updatedInstallationEntry","fetchAuthTokenFromServer","getToken","installationsImpl","completeInstallationRegistration","getMissingValueError","valueName","INSTALLATIONS_NAME","internalFactory","getId","configKeys","keyName","extractAppConfig","DEFAULT_VAPID_KEY","CONSOLE_CAMPAIGN_ID","MessageType$1","MessageType","arrayToBase64","uint8Array","base64ToArray","base64String","base64","repeat","rawData","atob","outputArray","OLD_DB_NAME","OLD_OBJECT_STORE_NAME","upgradeDb","dbGet","firebaseDependencies","tokenDetails","oldTokenDetails","senderId","dbNames","databases","objectStoreNames","contains","clear","oldDetails","auth","p256dh","fcmToken","createTime","subscriptionOptions","swScope","vapidKey","checkTokenDetails","migrateOldDatabase","dbSet","requestDeleteToken","unsubscribeOptions","getEndpoint","responseData","errorInfo","err","getBody","web","applicationPubKey","getTokenInternal","messaging","pushSubscription","swRegistration","subscription","pushManager","getSubscription","subscribe","userVisibleOnly","applicationServerKey","getPushSubscription","scope","dbOptions","currentOptions","isVapidKeyEqual","isEndpointEqual","isAuthEqual","isP256dhEqual","isTokenValid","updatedToken","updateOptions","requestUpdateToken","updatedTokenDetails","deleteTokenInternal","updateToken","getNewToken","dbRemove","unsubscribe","subscribeOptions","requestGetToken","externalizePayload","internalPayload","payload","collapseKey","collapse_key","messageId","fcmMessageId","messagePayloadInternal","notification","title","image","propagateNotificationPayload","propagateDataPayload","fcmOptions","link","analyticsLabel","analytics_label","propagateFcmOptions","_mergeStrings","s1","s2","resultArray","charAt","MessagingService","analyticsProvider","deliveryMetricsExportedToBigQueryEnabled","onBackgroundMessageHandler","onMessageHandler","logEvents","isLogServiceStarted","messagingSenderId","registerDefaultSw","serviceWorker","register","browserErrorMessage","getToken$1","Notification","permission","requestPermission","updateVapidKey","ServiceWorkerRegistration","updateSwReg","serviceWorkerRegistration","logToScion","messageType","eventType","NOTIFICATION_CLICKED","PUSH_RECEIVED","getEventType","logEvent","message_id","message_name","message_time","message_device_time","Math","floor","messageEventListener","isFirebaseMessaging","next","dataPayload","WindowMessagingInternalFactory","isWindowSupported","preExist","DB_CHECK_NAME","open","deleteDatabase","cookieEnabled","PushSubscription","getMessagingInWindow","isSupported","deleteToken","deleteToken$1","onMessage","nextOrObserver","onMessage$1","addEventListener","Tinode","Alert","render","visible","className","content","onReject","onClick","FormattedMessage","id","defaultMessage","onConfirm","confirm","APP_NAME","KNOWN_HOSTS","hosted","local","DEFAULT_HOST","KEYPRESS_DELAY","DEFAULT_P2P_ACCESS_MODE","REM_SIZE","AVATAR_SIZE","MAX_INBAND_ATTACHMENT_SIZE","MAX_EXTERN_ATTACHMENT_SIZE","IMAGE_THUMBNAIL_DIM","MAX_TITLE_LENGTH","MAX_TOPIC_DESCRIPTION_LENGTH","messages","defineMessages","clear_messages","clear_for_all","delete_for_all","send_retry","mute","unmute","reply","forward","topic_delete","topic_delete_warning","delete_messages_warning","unblock","block","topic_block_warning","member_delete","archive","unarchive","edit_permissions","clear_messages_warning","ContextMenu","selfRef","formatMessage","intl","handlePageClick","bind","handleEscapeKey","handleClick","MenuItems","handler","params","errorHandler","onShowAlert","deleteMessages","retryMessage","replyToMessage","topicPermissionSetter","ctrl","onTopicRemoved","topicName","topic","tinode","getTopic","delTopic","user","delSubscription","componentDidMount","document","componentWillUnmount","removeEventListener","current","target","preventDefault","stopPropagation","hide","keyCode","item","currentTarget","dataset","onAction","onError","hard","cancelSend","seq","delMessagesAll","delMessagesList","flushMessage","msg","createMessage","publishDraft","updateMode","pickReply","userFrom","userName","menu","vSize","position","left","bounds","right","clickAt","x","top","bottom","y","style","ref","injectIntl","icon_mapping","ContactBadges","badges","color","icon","secondsToTime","seconds","fixedMin","min","sec","bytesToHumanSize","bytes","sizes","bucket","log2","pow","round","toFixed","shortenFileName","filename","maxLength","idToColorClass","light","fg","userId","abs","hash","stringToColorHash","updateFavicon","oldIcon","getElementById","head","getElementsByTagName","newIcon","createElement","rel","href","removeChild","appendChild","theCard","imageUrl","imageMimeType","note","card","trim","mimeType","matches","exec","photo","substring","indexOf","arrayEqual","isArray","sort","l","sanitizeUrl","url","allowedSchemes","schemes","RegExp","sanitizeImageUrl","sanitizedUrl","deliveryMarker","received","cancelablePromise","hasCanceled","isCanceled","cancel","LetterTile","avatar","isGroup","iconColor","letter","deleted","authorizeURL","alt","src","UnreadBadge","LazyImage","state","padding","whenDone","setState","componentDidUpdate","prevProps","FileProgress","width","progress","onCancel","UploadingImage","onCancelUpload","SUPPORTED_IMAGE_FORMATS","MIME_EXTENSIONS","makeImageUrl","startsWith","fitImageSize","height","maxWidth","maxHeight","forceSquare","scale","dstWidth","dstHeight","srcWidth","srcHeight","xoffset","yoffset","fileNameForMime","fname","mime","idx","ext","at","lastIndexOf","imageScaled","fileOrBlob","maxSize","img","Image","crossOrigin","onload","URL","revokeObjectURL","dim","canvas","ctx","getContext","imageSmoothingEnabled","drawImage","blob","toBlob","clearRect","createObjectURL","blobToBase64","reader","FileReader","evt","bits","split","readAsDataURL","drafty_form","drafty_attachment","drafty_image","handleImageData","el","attr","viewportWidth","minWidth","minHeight","Drafty","max","onImagePreview","loading","fullFormatter","stack","quoteFormatter","onFormButtonClick","inner","child","onQuoteClick","concat","previewFormatter","inlineImageAttr","quoteImage","bin","buf","ArrayBuffer","Blob","base64ToBlob","statusText","scaled","b64","replyFormatter","loadedPromise","Contact","handleContextClick","onSelected","showContextMenu","pageY","pageX","online","icon_badges","preview","isVerified","isStaff","isDangerous","acs","showMode","getMode","isMuted","isJoiner","marker","titleClass","showCheckmark","selected","showOnline","isChannel","unread","small","comment","ContactAction","action","badge_you","badge_owner","ContactList","topicSelected","contactNodes","contactsCount","contacts","filterFunc","filterOn","private","toLowerCase","public","myUserId","isOwner","forwarded","deliveryStatus","latestMessage","_status","msgStatus","showUnread","connected","onTopicSelected","trusted","verified","staff","danger","_deleted","noScroll","dangerouslySetInnerHTML","__html","emptyListMessage","SearchContacts","edited","search","handleSearchChange","handleSearch","handleClear","handleKeyDown","onSearchContacts","placeholder","onChange","onKeyDown","required","autoFocus","ForwardDialog","handleClose","handleSearchContacts","handleContactSelected","onInitFind","uid","searchResults","isWriter","desription","search_placeholder","not_found_placeholder","LoadSpinner","large","centered","show","AvatarUpload","source","handleFileReceived","files","onImageUpdated","randId","random","readOnly","accept","htmlFor","uploading","badge_verified","badge_staff","badge_danger","BadgeList","trustedBadges","CheckBox","handleChange","checked","MenuCancel","ErrorPanel","level","onClearError","text","whiteSpace","actionText","Chip","handleCancel","invalid","noAvatar","ChipInput","deriveStateFromProps","input","focused","handleTextInput","removeChipAt","handleChipCancel","handleFocusGained","handleFocusLost","chips","prompt","sortedChips","sortChips","staticMembers","chipIndex","indexChips","prevState","keep","normal","removed","onChipRemoved","onFocusLost","onEnter","avatarDisabled","tabIndex","onFocus","onBlur","no_contacts","contacts_not_found_short","GroupManager","members","indexMembers","keepInitialMembers","requiredMember","contactFilter","noContactsMessage","selectedContacts","handleMemberRemoved","handleContactFilter","handleSubmit","m","delta","present","keepInitial","stat","sel","splice","added","onSubmit","doContactFiltering","joiner","writer","preser","approver","sharer","deleter","owner","PermissionsEditor","names","skip","compare","userTitle","userAvatar","modeTitle","compareTitle","Cropper","panX","panY","originX","originY","zoom","minZoom","maxZoom","overlay","cutout","boundingBox","imageWidth","imageHeight","mouseX","mouseY","prevDistance","cutoutRect","bBoxRect","initScaling","onZoom","handleZoom","mouseDown","mouseUp","mouseMove","mouseTouch","positionAll","translate","passive","getBoundingClientRect","currPan","nextDiff","imgRect","imgLeft","imgRight","coLeft","coRight","imgTop","imgBottom","coTop","coBottom","touches","dX","dY","checkBound","touch0","touch1","distance","sqrt","t3d","orig","transform","transformOrigin","onLoad","step","AvatarCrop","objURL","VisiblePassword","handleVisibility","handeTextChange","handleEditingFinished","onFinished","activeElement","autoComplete","InPlaceEdit","active","initialValue","handleStartEditing","handlePasswordFinished","focus","checkValidity","spanText","spanClass","multiline","element","rows","TagManager","tags","tagInput","activated","handleTagInput","handleAddTag","handleRemoveTag","nextProps","last","tag","maxTagCount","getServerLimit","onTagsChanged","minTagLength","maxTagLength","add_tags_prompt","TopicDescEdit","getAccessMode","isMe","fullName","description","newAvatar","newAvatarMime","previousOnTags","tnNewTags","handleFullNameUpdate","handleImageUpdated","handleAvatarCropped","handlePrivateUpdate","handleDescriptionUpdate","uploadAvatar","handleAvatarCropCancel","handleTagsUpdated","onTagsUpdated","onUpdateTopicDesc","desc","readyToUpload","uploader","getLargeFileHelper","upload","finally","du","onUpdateTags","editable","full_name_placeholder","group_name_placeholder","private_placeholder","title_tag_manager","TopicCommon","credentials","getCredentials","addCredActive","addCredInvalid","newCred","previousTagsUpdated","tnCredsUpdated","handleCredChange","handleCredKeyDown","handleCredEntered","onCredsUpdated","getType","creds","asPhone","asEmail","onCredAdd","onTopicTagsUpdateRequest","cred","meth","done","onCredConfirm","onCredDelete","delete_messages","leave_chat","leave_chat_warning","block_contact","block_contact_warning","report_chat","report_chat_warning","other_user","TopicSecurity","handleDeleteTopic","handleDeleteMessages","handleLeave","handleBlock","handleReport","onDeleteTopic","onDeleteMessages","onLeaveTopic","onBlockTopic","onReportTopic","groupTopic","onLaunchPermissionsEditor","access","anon","modeGiven2","general","security","crop","perm_want","perm_given","perm_auth","perm_anon","perm_user","_clip","InfoView","admin","muted","address","selectedContact","modeGiven","modeWant","modeWant2","contactList","previousMetaDesc","previousSubsUpdated","resetSubs","resetDesc","resetTags","onMetaDesc","onSubsUpdated","handleImageChanged","handleMuted","handleUnarchive","handlePermissionsChanged","handleLaunchPermissionsEditor","handleShowAddMembers","handleMemberUpdateRequest","handleMemberSelected","handleContextMenu","handleBackNavigate","newState","user2","subscriber","getGiven","getWant","subscribers","sub","defacs","getDefaultAccess","isAdmin","isSharer","isDeleter","archived","isArchived","isGroupType","isChannelType","chan","getMeta","startMetaQuery","withTags","build","subs","onTopicDescUpdate","ignored","onChangePermissions","ignored2","onTopicUnArchive","which","perm","onTopicDescUpdateRequest","userPermissionsEdited","toEdit","toCompare","toSkip","titleEdit","titleCompare","userPermissionsTitle","userPermissionsAvatar","editedPermissions","immutablePermissions","editedPermissionsTitle","immutablePermissionsTitle","editedPermissionsSkipped","onNavigate","onMemberUpdateRequest","panel","menuItems","view","shift","panelTitle","displayMobile","errorLevel","errorText","searchableContacts","no_members","Attachment","downloader","downloadFile","mimetype","download","loaded","helperFunc","isUrlRelative","downloadUrl","downloadWidget","message_sending","message_sending_failed","ReceivedMarker","timestamp","toLocaleTimeString","locale","timeStyle","BaseChatMessage","onProgress","handleProgress","handleImagePreview","handleFormButtonClick","handleCancelUpload","handleQuoteClick","formatterContext","resp","act","onFormResponse","MESSAGE_STATUS_FAILED","userIsWriter","MESSAGE_STATUS_DEL_RANGE","ratio","replyToSeq","sideClass","sequence","bubbleClass","fullDisplay","attachments","att","tree","innerRef","IntlChatMessage","messaging_disabled","type_new_message","add_image_caption","file_attachment_too_large","cannot_initiate_upload","SendMessage","quote","keypressTimestamp","getTime","handlePasteEvent","handleAttachImage","handleAttachFile","handleSend","handleKeyPress","handleMessageTyping","formatReply","messageEditArea","disabled","onImageSuccess","onAttachmentSuccess","clipboardData","originalEvent","kind","file","getAsFile","filePasted","onAttachImage","onAttachFile","acceptBlank","noInput","onSendMessage","shiftKey","onKeyPress","messagePrompt","onCancelReply","attachImage","click","attachFile","display","DocPreview","handleSendDoc","caption","onClose","mimeToIcon","default","video","iconFromMime","GroupSubs","usersOnline","totalCount","countToShow","some","overflow","ImagePreview","handleSendImage","assignWidth","node","maxlength","Invitation","handleButtonAction","MILLIS_PER_BAR","AudioRecorder","enabled","mediaDevices","getUserMedia","audioRecord","recording","paused","duration","visualize","initMediaRecording","handleStart","handleStop","handleDelete","durationMillis","startedOn","canvasRef","canvasContext","lineCap","stream","mediaRecorder","audioContext","sampleRate","audioInput","analyser","audioChunks","pcmData","frequencyBinCount","viewBuffer","viewLength","viewDuration","fillStyle","lineWidth","strokeStyle","prevBarCount","drawFrame","requestAnimationFrame","getByteTimeDomainData","volume","amplitude","barCount","dx","fillRect","beginPath","moveTo","lineTo","stroke","pause","resume","audio","MediaRecorder","AudioContext","createMediaStreamSource","createAnalyser","fftSize","connect","onstop","ondataavailable","start","LogoView","MetaMessage","date","HashNavigation","parts","path","part","decodeURIComponent","location","parsed","parseUrlHash","composeUrlHash","sidepanel","infopanel","online_now","last_seen","not_found","invalid_content","isUnconfirmed","ex","getExcessive","isPeerRestricted","getMissing","MessagesView","getDerivedStateFromProps","leave","sendMessage","retrySend","sendImageAttachment","sendFileAttachment","sendKeyPress","handleScrollReference","handleScrollEvent","handleDescChange","handleSubsUpdated","handleMessageUpdate","handleAllMessagesReceived","handleInfoReceipt","handleImagePostview","handleClosePreview","handleFormResponse","handleShowMessageContextMenu","handleNewChatAcceptance","handleEnablePeer","postReadNotification","clearNotificationQueue","goToLatestMessage","handlePickReply","handleCancelReply","chatMessageRefs","getOrCreateMessageRef","readNotificationQueue","readNotificationTimer","seqId","messagesScroller","messageCount","scrollPosition","scrollTop","scrollHeight","offsetHeight","onData","onAllMessagesReceived","onInfo","onPres","applicationVisible","ready","_new","withDesc","nextState","forwardMessage","docPreview","imagePreview","imagePostview","typingIndicator","fetchingMessages","showGoToLastButton","onlineSubs","peer","p2pPeerDesc","peerMessagingDisabled","latestClearId","maxClearId","isReader","readingBlocked","unconformed","unconfirmed","isSubscribed","newTopic","newTopicParams","_topicName","getQuery","withLaterDesc","withLaterSub","withLaterData","withLaterDel","setQuery","onNewTopicCreated","queuedMessages","pub","_sending","navigateTo","setUrlTopic","blankState","oldTopicName","isTopicCached","oldTopic","pos","msgHasMoreMessages","getMessagesPage","setInterval","clearInterval","sendAt","noteRead","setMilliseconds","getMilliseconds","clearTimeout","keyPressTimer","isNewMessage","what","forceUpdate","attachJSON","parse","searchParams","messageSpecificMenuItems","onNewChat","noteKeyPress","uploadCompletionPromise","sanitizeEntities","maxInbandAttachmentSize","urlPromise","fileToBase64","maxExternAttachmentSize","limit","insertImage","_tempPreview","appendLineBreak","found","findMessage","_cancelled","senderName","init","isValid","replyContent","wrapInto","onCancelForwardMessage","scrollIntoView","behavior","classList","hideSelf","component2","messageNodes","previousFrom","chatBoxClass","dateFmt","Intl","DateTimeFormat","prev","nextFrom","thisFrom","isReply","userDesc","parseInt","isNaN","ts","toDateString","format","hi","isChan","_uploader","lastSeen","cont","getMeTopic","getContact","seen","userLanguage","language","getFullYear","getMonth","getDate","hour12","hour","minute","toLocaleDateString","month","day","year","shortDateFormat","when","onHideMessagesView","serverVersion","serverAddress","ButtonBack","onBack","MenuContacts","onNewTopic","onSettings","MenuStart","onSignUp","SideNavbar","AccountSettingsView","me","archived_contacts_title","ContactsView","handleAction","unreadThreads","archivedCount","chatList","blocked","arch","touched","action_ignored","onShowArchive","LocalStorageUtil","localStorage","setItem","getItem","oldVal","getObject","setObject","removeItem","CreateAccountView","login","password","email","imageDataUrl","errorCleared","saveToken","handleLoginChange","handlePasswordChange","handleEmailChange","handleFnChange","handleToggleSaveToken","onCreateAccount","submitClasses","login_prompt","password_prompt","onImageChanged","full_name_prompt","email_prompt","stay_logged_in","EditAccountView","handleCheckboxClick","onToggleMessageSounds","onTogglePushNotifications","onToggleIncognitoMode","messageSounds","desktopAlertsEnabled","desktopAlerts","incognitoMode","delete_account","delete_account_warning","AccSecurityView","blockedCount","showPermissionEditorFor","handlePasswordUpdate","handleHidePermissionsEditor","handleDeleteAccount","pwd","onUpdatePassword","onUpdateAccountDesc","onDeleteAccount","password_unchanged","onLogout","onShowBlocked","AccSupportView","LoginView","hostName","persist","onPersistenceChange","onLogin","autoCorrect","autoCapitalize","invalid_id","NewTopicById","groupId","prefix","NewTopicGroup","handleFieldEdit","handleTagsChanged","handleChannelToggle","search_for_contacts","search_no_results","NewTopicView","tabSelected","searchQuery","handleTabClick","handleSearchResultSelected","handleNewGroupSubmit","handleGroupByID","addUrlParam","removeUrlParam","onCreateTopic","dataUrl","priv","no_contacts_placeholder","PasswordResetView","sent","scheme","onReset","onRequest","HostSelector","changed","handleHostNameChange","onServerAddressChange","hostOptions","list","SettingsView","transport","handleTransportSelected","handleServerAddressChange","onUpdate","def","ws","lp","transportOptions","phone","ValidationView","credCode","credMethod","numbers_only","settings","edit","notif","support","newtpk","reset","SidepanelView","handleNewTopic","errorAction","errorActionText","loadSpinnerVisible","loginDisabled","onLoginRequest","onGlobalSettings","onUpdateAccountTags","onValidateCredentials","onPasswordResetRequest","onResetPassword","detectServerAddress","host","protocol","hostname","port","isSecureConnection","POP_SOUND","Audio","reconnect_countdown","reconnect_now","push_init_failed","invalid_security_token","no_connection","code_doesnot_match","menu_item_info","TinodeWeb","getBlankState","handleResize","handleHashRoute","handleOnline","checkForAppUpdate","handleVisibilityEvent","handleError","handleLoginRequest","handlePersistenceChange","handleConnected","handleAutoreconnectIteration","doLogin","handleCredentialsRequest","handleLoginSuccessful","handleDisconnect","tnMeMetaDesc","tnMeContactUpdate","tnMeSubsUpdated","resetContactList","tnData","tnInitFind","tnFndSubsUpdated","handleTopicSelected","handleHideMessagesView","handleSendMessage","handleNewChatInvitation","handleNewAccount","handleNewAccountRequest","handleUpdatePasswordRequest","handleUpdateAccountTagsRequest","handleToggleIncognitoMode","handleSettings","handleGlobalSettings","handleShowArchive","handleShowBlocked","handleToggleMessageSounds","handleCredAdd","handleCredDelete","handleCredConfirm","initFCMessaging","toggleFCMToken","handlePushMessage","handleSidepanelCancel","handleStartTopicRequest","handleNewTopicCreated","handleTopicUpdateRequest","handleChangePermissions","handleTagsUpdateRequest","handleLogout","handleDeleteTopicRequest","handleDeleteMessagesRequest","handleLeaveUnsubRequest","handleBlockTopicRequest","handleReportTopic","handleShowContextMenu","defaultTopicContextMenu","handleHideContextMenu","handleShowAlert","handleShowInfoView","handleValidateCredentialsRequest","handlePasswordResetRequest","handleResetPassword","handleContextMenuAction","handleShowForwardDialog","handleHideForwardDialog","sendMessageToTopic","autoLogin","messageSoundsOff","FIREBASE_INIT","firebaseToken","hidden","sidePanelSelected","sidePanelTitle","sidePanelAvatar","myTrustedBadges","liveConnection","topicSelectedOnline","topicSelectedAcs","innerWidth","infoPanel","mobilePanel","contextMenuVisible","contextMenuBounds","contextMenuClickAt","contextMenuParams","contextMenuItems","forwardDialogVisible","alertVisible","alertParams","requestedTopic","documentElement","clientWidth","viewportHeight","clientHeight","tnSetup","onConnect","onDisconnect","onAutoreconnectIteration","setDeviceToken","parsedNav","expires","setAuthToken","readTimer","readTimerCallback","persistentCache","onSetupCompleted","secure","setHumanLanguage","enableLogging","oobNotification","updateObject","fcm","firebaseGetMessaging","firebaseInitApp","reg","requestFCMToken","firebaseOnMessage","sw","firebaseGetToken","messagingVapidKey","mobile","onupdatefound","installingWorker","installing","onstatechange","controller","getTopicAccessMode","newTopicTabSelected","tab","reconnectCountdown","reconnect","isConnected","initStorage","clearStorage","getServerInfo","ver","prom","timeLeft","isAuthenticated","getAuthToken","loginBasic","loginToken","goToTopic","onContactUpdate","getCurrentUserID","withCred","disconnect","urlHash","setUrlSidePanel","isPresencer","play","unused","foundContacts","merged","updated","past","prepareSearchableContacts","receivedTimer","noteRecv","fnd","getFndTopic","withSub","s","setMeta","isTopicOnline","isP2PType","am","updateWant","login_","password_","public_","cred_","tags_","createAccountBasic","on","firebaseDelToken","delCredential","basicNavigator","infoNavigator","setUrlInfoPanel","newGroupTopicName","oldName","newName","updateAccountBasic","getCurrentLogin","updateGiven","cleared","Promose","delCurrentUser","publish","header","msgPreview","self_blocked","subscribed","keepForwardedMessage","confirmText","rejectText","invite","requestResetAuthSecret","newPassword","base64ReEncode","myUserName","onAddMember","handleManageGroupMembers","hl","languages","baseLanguage","htmlLang","allMessages","langauge","setAttribute","IntlProvider","textComponent"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"file":"index.prod.js","mappings":"gCAIQ,SAAUA,GAAW,aAE3B,SAASC,EAAQC,GACf,OAAOC,MAAMC,UAAUC,MAAMC,KAAKJ,GAGpC,SAASK,EAAiBC,GACxB,OAAO,IAAIC,SAAQ,SAASC,EAASC,GACnCH,EAAQI,UAAY,WAClBF,EAAQF,EAAQK,SAGlBL,EAAQM,QAAU,WAChBH,EAAOH,EAAQO,WAKrB,SAASC,EAAqBC,EAAKC,EAAQC,GACzC,IAAIX,EACAY,EAAI,IAAIX,SAAQ,SAASC,EAASC,GAEpCJ,EADAC,EAAUS,EAAIC,GAAQG,MAAMJ,EAAKE,IACPG,KAAKZ,EAASC,MAI1C,OADAS,EAAEZ,QAAUA,EACLY,EAGT,SAASG,EAA2BN,EAAKC,EAAQC,GAC/C,IAAIC,EAAIJ,EAAqBC,EAAKC,EAAQC,GAC1C,OAAOC,EAAEE,MAAK,SAASE,GACrB,GAAKA,EACL,OAAO,IAAIC,EAAOD,EAAOJ,EAAEZ,YAI/B,SAASkB,EAAgBC,EAAYC,EAAYC,GAC/CA,EAAWC,SAAQ,SAASC,GAC1BC,OAAOC,eAAeN,EAAWvB,UAAW2B,EAAM,CAChDG,IAAK,WACH,OAAOC,KAAKP,GAAYG,IAE1BK,IAAK,SAASC,GACZF,KAAKP,GAAYG,GAAQM,QAMjC,SAASC,EAAoBX,EAAYC,EAAYW,EAAaV,GAChEA,EAAWC,SAAQ,SAASC,GACpBA,KAAQQ,EAAYnC,YAC1BuB,EAAWvB,UAAU2B,GAAQ,WAC3B,OAAOf,EAAqBmB,KAAKP,GAAaG,EAAMS,gBAK1D,SAASC,EAAad,EAAYC,EAAYW,EAAaV,GACzDA,EAAWC,SAAQ,SAASC,GACpBA,KAAQQ,EAAYnC,YAC1BuB,EAAWvB,UAAU2B,GAAQ,WAC3B,OAAOI,KAAKP,GAAYG,GAAMV,MAAMc,KAAKP,GAAaY,gBAK5D,SAASE,EAA0Bf,EAAYC,EAAYW,EAAaV,GACtEA,EAAWC,SAAQ,SAASC,GACpBA,KAAQQ,EAAYnC,YAC1BuB,EAAWvB,UAAU2B,GAAQ,WAC3B,OAAOR,EAA2BY,KAAKP,GAAaG,EAAMS,gBAKhE,SAASG,EAAMC,GACbT,KAAKU,OAASD,EAuBhB,SAASnB,EAAOqB,EAAQtC,GACtB2B,KAAKY,QAAUD,EACfX,KAAKa,SAAWxC,EA+BlB,SAASyC,EAAYC,GACnBf,KAAKgB,OAASD,EAuChB,SAASE,EAAYC,GACnBlB,KAAKmB,IAAMD,EACXlB,KAAKoB,SAAW,IAAI9C,SAAQ,SAASC,EAASC,GAC5C0C,EAAeG,WAAa,WAC1B9C,KAEF2C,EAAevC,QAAU,WACvBH,EAAO0C,EAAetC,QAExBsC,EAAeI,QAAU,WACvB9C,EAAO0C,EAAetC,WAkB5B,SAAS2C,EAAUC,EAAIC,EAAYC,GACjC1B,KAAK2B,IAAMH,EACXxB,KAAKyB,WAAaA,EAClBzB,KAAK0B,YAAc,IAAIT,EAAYS,GAkBrC,SAASE,EAAGJ,GACVxB,KAAK2B,IAAMH,EA6Db,SAASK,EAAOC,EAAMC,EAASC,GAC7B,IAAI/C,EAAIJ,EAAqBoD,UAAW,OAAQ,CAACH,EAAMC,IACnD1D,EAAUY,EAAEZ,QAUhB,OARIA,IACFA,EAAQ6D,gBAAkB,SAASC,GAC7BH,GACFA,EAAgB,IAAIT,EAAUlD,EAAQK,OAAQyD,EAAMV,WAAYpD,EAAQqD,gBAKvEzC,EAAEE,MAAK,SAASqC,GACrB,OAAO,IAAII,EAAGJ,MAIlB,SAASY,EAASN,GAChB,OAAOjD,EAAqBoD,UAAW,iBAAkB,CAACH,IA9N5DvC,EAAgBiB,EAAO,SAAU,CAC/B,OACA,UACA,aACA,WAGFL,EAAoBK,EAAO,SAAU6B,SAAU,CAC7C,MACA,SACA,SACA,aACA,UAGF9B,EAA0BC,EAAO,SAAU6B,SAAU,CACnD,aACA,kBAQF9C,EAAgBD,EAAQ,UAAW,CACjC,YACA,MACA,aACA,UAGFa,EAAoBb,EAAQ,UAAWgD,UAAW,CAChD,SACA,WAIF,CAAC,UAAW,WAAY,sBAAsB3C,SAAQ,SAAS4C,GACvDA,KAAcD,UAAUrE,YAC9BqB,EAAOrB,UAAUsE,GAAc,WAC7B,IAAI5B,EAASX,KACThB,EAAOqB,UACX,OAAO/B,QAAQC,UAAUY,MAAK,WAE5B,OADAwB,EAAOC,QAAQ2B,GAAYrD,MAAMyB,EAAOC,QAAS5B,GAC1CZ,EAAiBuC,EAAOE,UAAU1B,MAAK,SAASE,GACrD,GAAKA,EACL,OAAO,IAAIC,EAAOD,EAAOsB,EAAOE,qBAUxCC,EAAY7C,UAAUuE,YAAc,WAClC,OAAO,IAAIhC,EAAMR,KAAKgB,OAAOwB,YAAYtD,MAAMc,KAAKgB,OAAQX,aAG9DS,EAAY7C,UAAUwC,MAAQ,WAC5B,OAAO,IAAID,EAAMR,KAAKgB,OAAOP,MAAMvB,MAAMc,KAAKgB,OAAQX,aAGxDd,EAAgBuB,EAAa,SAAU,CACrC,OACA,UACA,aACA,kBAGFX,EAAoBW,EAAa,SAAU2B,eAAgB,CACzD,MACA,MACA,SACA,QACA,MACA,SACA,SACA,aACA,UAGFlC,EAA0BO,EAAa,SAAU2B,eAAgB,CAC/D,aACA,kBAGFnC,EAAaQ,EAAa,SAAU2B,eAAgB,CAClD,gBAkBFxB,EAAYhD,UAAUyE,YAAc,WAClC,OAAO,IAAI5B,EAAYd,KAAKmB,IAAIuB,YAAYxD,MAAMc,KAAKmB,IAAKd,aAG9Dd,EAAgB0B,EAAa,MAAO,CAClC,mBACA,SAGFX,EAAaW,EAAa,MAAO0B,eAAgB,CAC/C,UASFpB,EAAUtD,UAAU2E,kBAAoB,WACtC,OAAO,IAAI9B,EAAYd,KAAK2B,IAAIiB,kBAAkB1D,MAAMc,KAAK2B,IAAKtB,aAGpEd,EAAgBgC,EAAW,MAAO,CAChC,OACA,UACA,qBAGFjB,EAAaiB,EAAW,MAAOsB,YAAa,CAC1C,oBACA,UAOFjB,EAAG3D,UAAUyD,YAAc,WACzB,OAAO,IAAIT,EAAYjB,KAAK2B,IAAID,YAAYxC,MAAMc,KAAK2B,IAAKtB,aAG9Dd,EAAgBqC,EAAI,MAAO,CACzB,OACA,UACA,qBAGFtB,EAAasB,EAAI,MAAOiB,YAAa,CACnC,UAKF,CAAC,aAAc,iBAAiBlD,SAAQ,SAASmD,GAC/C,CAAChC,EAAaN,GAAOb,SAAQ,SAASS,GAE9B0C,KAAY1C,EAAYnC,YAE9BmC,EAAYnC,UAAU6E,EAASC,QAAQ,OAAQ,YAAc,WAC3D,IAAI/D,EAAOlB,EAAQuC,WACf2C,EAAWhE,EAAKA,EAAKiE,OAAS,GAC9BC,EAAelD,KAAKgB,QAAUhB,KAAKU,OACnCrC,EAAU6E,EAAaJ,GAAU5D,MAAMgE,EAAclE,EAAKd,MAAM,GAAI,IACxEG,EAAQI,UAAY,WAClBuE,EAAS3E,EAAQK,iBAOzB,CAAC8B,EAAOM,GAAanB,SAAQ,SAASS,GAChCA,EAAYnC,UAAUkF,SAC1B/C,EAAYnC,UAAUkF,OAAS,SAASC,EAAOC,GAC7C,IAAIC,EAAWtD,KACXuD,EAAQ,GAEZ,OAAO,IAAIjF,SAAQ,SAASC,GAC1B+E,EAASE,cAAcJ,GAAO,SAASzC,GAChCA,GAIL4C,EAAME,KAAK9C,EAAOtB,YAEJqE,IAAVL,GAAuBE,EAAMN,QAAUI,EAI3C1C,EAAOgD,WAHLpF,EAAQgF,IANRhF,EAAQgF,cAoClB1F,EAAQgE,OAASA,EACjBhE,EAAQuE,SAAWA,EAEnBvC,OAAOC,eAAejC,EAAS,aAAc,CAAEwB,OAAO,IAxTSuE,CAAQ/F,KCArEgG,EAA2B,GAG/B,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBL,IAAjBM,EACH,OAAOA,EAAanG,QAGrB,IAAIoG,EAASJ,EAAyBE,GAAY,CAGjDlG,QAAS,IAOV,OAHAqG,EAAoBH,GAAU5F,KAAK8F,EAAOpG,QAASoG,EAAQA,EAAOpG,QAASiG,GAGpEG,EAAOpG,QCpBfiG,EAAoBK,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAH,EAAoBQ,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,GCLRN,EAAoBQ,EAAI,CAACzG,EAAS2G,KACjC,IAAI,IAAIC,KAAOD,EACXV,EAAoBY,EAAEF,EAAYC,KAASX,EAAoBY,EAAE7G,EAAS4G,IAC5E5E,OAAOC,eAAejC,EAAS4G,EAAK,CAAEE,YAAY,EAAM5E,IAAKyE,EAAWC,MCJ3EX,EAAoBc,EAAI,WACvB,GAA0B,iBAAfC,WAAyB,OAAOA,WAC3C,IACC,OAAO7E,MAAQ,IAAI8E,SAAS,cAAb,GACd,MAAOC,GACR,GAAsB,iBAAXC,OAAqB,OAAOA,QALjB,GCAxBlB,EAAoBY,EAAI,CAAC5F,EAAKc,IAAUC,OAAO5B,UAAUgH,eAAe9G,KAAKW,EAAKc,G,mBCAlF,MAAM,EAA+BsF,M,aCArC,MAAM,EAA+BC,S,aCArC,MAAM,EAA+BC,U,spzDCoF/BC,EAAsB,SAAUC,GAElC,MAAMC,EAAM,GACZ,IAAItG,EAAI,EACR,IAAK,IAAIuG,EAAI,EAAGA,EAAIF,EAAIrC,OAAQuC,IAAK,CACjC,IAAIC,EAAIH,EAAII,WAAWF,GACnBC,EAAI,IACJF,EAAItG,KAAOwG,EAENA,EAAI,MACTF,EAAItG,KAAQwG,GAAK,EAAK,IACtBF,EAAItG,KAAY,GAAJwG,EAAU,KAEA,QAAZ,MAAJA,IACND,EAAI,EAAIF,EAAIrC,QACyB,QAAZ,MAAxBqC,EAAII,WAAWF,EAAI,KAEpBC,EAAI,QAAgB,KAAJA,IAAe,KAA6B,KAAtBH,EAAII,aAAaF,IACvDD,EAAItG,KAAQwG,GAAK,GAAM,IACvBF,EAAItG,KAASwG,GAAK,GAAM,GAAM,IAC9BF,EAAItG,KAASwG,GAAK,EAAK,GAAM,IAC7BF,EAAItG,KAAY,GAAJwG,EAAU,MAGtBF,EAAItG,KAAQwG,GAAK,GAAM,IACvBF,EAAItG,KAASwG,GAAK,EAAK,GAAM,IAC7BF,EAAItG,KAAY,GAAJwG,EAAU,KAG9B,OAAOF,GAgWX,MAAMI,EACFC,cACI5F,KAAKxB,OAAS,OACdwB,KAAKzB,QAAU,OACfyB,KAAK6F,QAAU,IAAIvH,SAAQ,CAACC,EAASC,KACjCwB,KAAKzB,QAAUA,EACfyB,KAAKxB,OAASA,KAQtBsH,aAAa9C,GACT,MAAO,CAACpE,EAAOS,KACPT,EACAoB,KAAKxB,OAAOI,GAGZoB,KAAKzB,QAAQc,GAEO,mBAAb2D,IAGPhD,KAAK6F,QAAQE,OAAM,SAGK,IAApB/C,EAASC,OACTD,EAASpE,GAGToE,EAASpE,EAAOS,MA+RpC,MAAM2G,UAAsBC,MACxBL,YAEAM,EAAMC,EAENC,GACIC,MAAMF,GACNnG,KAAKkG,KAAOA,EACZlG,KAAKoG,WAAaA,EAElBpG,KAAK8B,KAbM,gBAgBXjC,OAAOyG,eAAetG,KAAMgG,EAAc/H,WAGtCgI,MAAMM,mBACNN,MAAMM,kBAAkBvG,KAAMwG,EAAavI,UAAUwI,SAIjE,MAAMD,EACFZ,YAAYc,EAASC,EAAaC,GAC9B5G,KAAK0G,QAAUA,EACf1G,KAAK2G,YAAcA,EACnB3G,KAAK4G,OAASA,EAElBH,OAAOP,KAASW,GACZ,MAAMT,EAAaS,EAAK,IAAM,GACxBC,EAAW,GAAG9G,KAAK0G,WAAWR,IAC9Ba,EAAW/G,KAAK4G,OAAOV,GACvBC,EAAUY,EAOxB,SAAyBA,EAAUF,GAC/B,OAAOE,EAAShE,QAAQiE,GAAS,CAACC,EAAGxC,KACjC,MAAMpF,EAAQwH,EAAKpC,GACnB,OAAgB,MAATpF,EAAgB6H,OAAO7H,GAAS,IAAIoF,SAVhB0C,CAAgBJ,EAAUX,GAAc,QAE7DgB,EAAc,GAAGpH,KAAK2G,gBAAgBR,MAAYW,MAExD,OADc,IAAId,EAAcc,EAAUM,EAAahB,IAU/D,MAAMY,EAAU,gBAkMhB,SAASK,EAAU9C,EAAG+C,GAClB,GAAI/C,IAAM+C,EACN,OAAO,EAEX,MAAMC,EAAQ1H,OAAO2H,KAAKjD,GACpBkD,EAAQ5H,OAAO2H,KAAKF,GAC1B,IAAK,MAAMI,KAAKH,EAAO,CACnB,IAAKE,EAAME,SAASD,GAChB,OAAO,EAEX,MAAME,EAAQrD,EAAEmD,GACVG,EAAQP,EAAEI,GAChB,GAAII,EAASF,IAAUE,EAASD,IAC5B,IAAKR,EAAUO,EAAOC,GAClB,OAAO,OAGV,GAAID,IAAUC,EACf,OAAO,EAGf,IAAK,MAAMH,KAAKD,EACZ,IAAKF,EAAMI,SAASD,GAChB,OAAO,EAGf,OAAO,EAEX,SAASI,EAASC,GACd,OAAiB,OAAVA,GAAmC,iBAAVA,EAq0BpC,SAASC,EAAmBtB,GACxB,OAAIA,GAAWA,EAAQuB,UACZvB,EAAQuB,UAGRvB,ECh2Df,MAAMwB,EAOFtC,YAAY9D,EAAMqG,EAAiBC,GAC/BpI,KAAK8B,KAAOA,EACZ9B,KAAKmI,gBAAkBA,EACvBnI,KAAKoI,KAAOA,EACZpI,KAAKqI,mBAAoB,EAIzBrI,KAAKsI,aAAe,GACpBtI,KAAKuI,kBAAoB,OACzBvI,KAAKwI,kBAAoB,KAE7BC,qBAAqBC,GAEjB,OADA1I,KAAKuI,kBAAoBG,EAClB1I,KAEX2I,qBAAqBN,GAEjB,OADArI,KAAKqI,kBAAoBA,EAClBrI,KAEX4I,gBAAgBC,GAEZ,OADA7I,KAAKsI,aAAeO,EACb7I,KAEX8I,2BAA2B9F,GAEvB,OADAhD,KAAKwI,kBAAoBxF,EAClBhD,MAoBf,MAAM+I,EAAqB,YAsB3B,MAAMC,EACFpD,YAAY9D,EAAMmH,GACdjJ,KAAK8B,KAAOA,EACZ9B,KAAKiJ,UAAYA,EACjBjJ,KAAKkJ,UAAY,KACjBlJ,KAAKmJ,UAAY,IAAIC,IACrBpJ,KAAKqJ,kBAAoB,IAAID,IAC7BpJ,KAAKsJ,iBAAmB,IAAIF,IAC5BpJ,KAAKuJ,gBAAkB,IAAIH,IAM/BrJ,IAAIyJ,GAEA,MAAMC,EAAuBzJ,KAAK0J,4BAA4BF,GAC9D,IAAKxJ,KAAKqJ,kBAAkBM,IAAIF,GAAuB,CACnD,MAAMG,EAAW,IAAIjE,EAErB,GADA3F,KAAKqJ,kBAAkBpJ,IAAIwJ,EAAsBG,GAC7C5J,KAAK6J,cAAcJ,IACnBzJ,KAAK8J,uBAEL,IACI,MAAMxG,EAAWtD,KAAK+J,uBAAuB,CACzCC,mBAAoBP,IAEpBnG,GACAsG,EAASrL,QAAQ+E,GAGzB,MAAOyB,KAMf,OAAO/E,KAAKqJ,kBAAkBtJ,IAAI0J,GAAsB5D,QAE5DoE,aAAaC,GACT,IAAIC,EAEJ,MAAMV,EAAuBzJ,KAAK0J,4BAA4BQ,MAAAA,OAAyC,EAASA,EAAQV,YAClHY,EAAyF,QAA7ED,EAAKD,MAAAA,OAAyC,EAASA,EAAQE,gBAA6B,IAAPD,GAAgBA,EACvH,IAAInK,KAAK6J,cAAcJ,KACnBzJ,KAAK8J,uBAeJ,CAED,GAAIM,EACA,OAAO,KAGP,MAAMnE,MAAM,WAAWjG,KAAK8B,yBApBhC,IACI,OAAO9B,KAAK+J,uBAAuB,CAC/BC,mBAAoBP,IAG5B,MAAO1E,GACH,GAAIqF,EACA,OAAO,KAGP,MAAMrF,GActBsF,eACI,OAAOrK,KAAKkJ,UAEhBoB,aAAapB,GACT,GAAIA,EAAUpH,OAAS9B,KAAK8B,KACxB,MAAMmE,MAAM,yBAAyBiD,EAAUpH,qBAAqB9B,KAAK8B,SAE7E,GAAI9B,KAAKkJ,UACL,MAAMjD,MAAM,iBAAiBjG,KAAK8B,kCAItC,GAFA9B,KAAKkJ,UAAYA,EAEZlJ,KAAK8J,uBAAV,CAIA,GAwKR,SAA0BZ,GACtB,MAAuC,UAAhCA,EAAUX,kBAzKTgC,CAAiBrB,GACjB,IACIlJ,KAAK+J,uBAAuB,CAAEC,mBAAoBjB,IAEtD,MAAOhE,IAUX,IAAK,MAAOiF,EAAoBQ,KAAqBxK,KAAKqJ,kBAAkBoB,UAAW,CACnF,MAAMhB,EAAuBzJ,KAAK0J,4BAA4BM,GAC9D,IAEI,MAAM1G,EAAWtD,KAAK+J,uBAAuB,CACzCC,mBAAoBP,IAExBe,EAAiBjM,QAAQ+E,GAE7B,MAAOyB,OAMf2F,cAAclB,EAAaT,aACvB/I,KAAKqJ,kBAAkBsB,OAAOnB,GAC9BxJ,KAAKsJ,iBAAiBqB,OAAOnB,GAC7BxJ,KAAKmJ,UAAUwB,OAAOnB,GAI1BoB,eACI,MAAMC,EAAW7M,MAAM8M,KAAK9K,KAAKmJ,UAAU4B,gBACrCzM,QAAQ0M,IAAI,IACXH,EACEI,QAAOvE,GAAW,aAAcA,IAEhCwE,KAAIxE,GAAWA,EAAQyE,SAASR,cAClCE,EACEI,QAAOvE,GAAW,YAAaA,IAE/BwE,KAAIxE,GAAWA,EAAQ0E,cAGpCC,iBACI,OAAyB,MAAlBrL,KAAKkJ,UAEhBW,cAAcL,EAAaT,aACvB,OAAO/I,KAAKmJ,UAAUQ,IAAIH,GAE9B8B,WAAW9B,EAAaT,aACpB,OAAO/I,KAAKsJ,iBAAiBvJ,IAAIyJ,IAAe,GAEpD+B,WAAWC,EAAO,IACd,MAAM,QAAEtB,EAAU,IAAOsB,EACnB/B,EAAuBzJ,KAAK0J,4BAA4B8B,EAAKxB,oBACnE,GAAIhK,KAAK6J,cAAcJ,GACnB,MAAMxD,MAAM,GAAGjG,KAAK8B,QAAQ2H,mCAEhC,IAAKzJ,KAAKqL,iBACN,MAAMpF,MAAM,aAAajG,KAAK8B,oCAElC,MAAMwB,EAAWtD,KAAK+J,uBAAuB,CACzCC,mBAAoBP,EACpBS,QAAAA,IAGJ,IAAK,MAAOF,EAAoBQ,KAAqBxK,KAAKqJ,kBAAkBoB,UAAW,CAE/EhB,IADiCzJ,KAAK0J,4BAA4BM,IAElEQ,EAAiBjM,QAAQ+E,GAGjC,OAAOA,EAUXmI,OAAOzI,EAAUwG,GACb,IAAIW,EACJ,MAAMV,EAAuBzJ,KAAK0J,4BAA4BF,GACxDkC,EAA8E,QAAzDvB,EAAKnK,KAAKuJ,gBAAgBxJ,IAAI0J,UAA0C,IAAPU,EAAgBA,EAAK,IAAIwB,IACrHD,EAAkBE,IAAI5I,GACtBhD,KAAKuJ,gBAAgBtJ,IAAIwJ,EAAsBiC,GAC/C,MAAMG,EAAmB7L,KAAKmJ,UAAUpJ,IAAI0J,GAI5C,OAHIoC,GACA7I,EAAS6I,EAAkBpC,GAExB,KACHiC,EAAkBf,OAAO3H,IAOjC8I,sBAAsBxI,EAAUkG,GAC5B,MAAMuC,EAAY/L,KAAKuJ,gBAAgBxJ,IAAIyJ,GAC3C,GAAKuC,EAGL,IAAK,MAAM/I,KAAY+I,EACnB,IACI/I,EAASM,EAAUkG,GAEvB,MAAOW,KAKfJ,wBAAuB,mBAAEC,EAAkB,QAAEE,EAAU,KACnD,IAAI5G,EAAWtD,KAAKmJ,UAAUpJ,IAAIiK,GAClC,IAAK1G,GAAYtD,KAAKkJ,YAClB5F,EAAWtD,KAAKkJ,UAAUf,gBAAgBnI,KAAKiJ,UAAW,CACtDe,oBAyCuBR,EAzC2BQ,EA0CvDR,IAAeT,OAAqBrF,EAAY8F,GAzC3CU,QAAAA,IAEJlK,KAAKmJ,UAAUlJ,IAAI+J,EAAoB1G,GACvCtD,KAAKsJ,iBAAiBrJ,IAAI+J,EAAoBE,GAM9ClK,KAAK8L,sBAAsBxI,EAAU0G,GAMjChK,KAAKkJ,UAAUV,mBACf,IACIxI,KAAKkJ,UAAUV,kBAAkBxI,KAAKiJ,UAAWe,EAAoB1G,GAEzE,MAAO6G,IAqBvB,IAAuCX,EAhB/B,OAAOlG,GAAY,KAEvBoG,4BAA4BF,EAAaT,aACrC,OAAI/I,KAAKkJ,UACElJ,KAAKkJ,UAAUb,kBAAoBmB,EAAaT,EAGhDS,EAGfM,uBACI,QAAU9J,KAAKkJ,WAC0B,aAArClJ,KAAKkJ,UAAUX,mBA8B3B,MAAMyD,EACFpG,YAAY9D,GACR9B,KAAK8B,KAAOA,EACZ9B,KAAKiM,UAAY,IAAI7C,IAWzB8C,aAAahD,GACT,MAAMiD,EAAWnM,KAAKoM,YAAYlD,EAAUpH,MAC5C,GAAIqK,EAASd,iBACT,MAAM,IAAIpF,MAAM,aAAaiD,EAAUpH,yCAAyC9B,KAAK8B,QAEzFqK,EAAS7B,aAAapB,GAE1BmD,wBAAwBnD,GACHlJ,KAAKoM,YAAYlD,EAAUpH,MAC/BuJ,kBAETrL,KAAKiM,UAAUtB,OAAOzB,EAAUpH,MAEpC9B,KAAKkM,aAAahD,GAStBkD,YAAYtK,GACR,GAAI9B,KAAKiM,UAAUtC,IAAI7H,GACnB,OAAO9B,KAAKiM,UAAUlM,IAAI+B,GAG9B,MAAMqK,EAAW,IAAInD,EAASlH,EAAM9B,MAEpC,OADAA,KAAKiM,UAAUhM,IAAI6B,EAAMqK,GAClBA,EAEXG,eACI,OAAOtO,MAAM8M,KAAK9K,KAAKiM,UAAUlB,WChYzC,MAAM5B,EAAY,GAYlB,IAAIoD,GACJ,SAAWA,GACPA,EAASA,EAAgB,MAAI,GAAK,QAClCA,EAASA,EAAkB,QAAI,GAAK,UACpCA,EAASA,EAAe,KAAI,GAAK,OACjCA,EAASA,EAAe,KAAI,GAAK,OACjCA,EAASA,EAAgB,MAAI,GAAK,QAClCA,EAASA,EAAiB,OAAI,GAAK,SANvC,CAOGA,IAAaA,EAAW,KAC3B,MAAMC,EAAoB,CACtB,MAASD,EAASE,MAClB,QAAWF,EAASG,QACpB,KAAQH,EAASI,KACjB,KAAQJ,EAASK,KACjB,MAASL,EAASM,MAClB,OAAUN,EAASO,QAKjBC,EAAkBR,EAASI,KAO3BK,EAAgB,CAClB,CAACT,EAASE,OAAQ,MAClB,CAACF,EAASG,SAAU,MACpB,CAACH,EAASI,MAAO,OACjB,CAACJ,EAASK,MAAO,OACjB,CAACL,EAASM,OAAQ,SAOhBI,EAAoB,CAAC3J,EAAU4J,KAAYlO,KAC7C,GAAIkO,EAAU5J,EAAS6J,SACnB,OAEJ,MAAMC,GAAM,IAAIC,MAAOC,cACjBvO,EAASiO,EAAcE,GAC7B,IAAInO,EAIA,MAAM,IAAIkH,MAAM,8DAA8DiH,MAH9EK,QAAQxO,GAAQ,IAAIqO,OAAS9J,EAASxB,WAAY9C,ICxD1D,MAAMwO,EACF5H,YAAYqD,GACRjJ,KAAKiJ,UAAYA,EAIrBwE,wBAII,OAHkBzN,KAAKiJ,UAAUqD,eAI5BpB,KAAIiB,IACL,GAoBZ,SAAkCA,GAC9B,MAAMjD,EAAYiD,EAAS9B,eAC3B,MAAkF,aAA1EnB,MAAAA,OAA6C,EAASA,EAAUd,MAtB5DsF,CAAyBvB,GAAW,CACpC,MAAMzF,EAAUyF,EAASlC,eACzB,MAAO,GAAGvD,EAAQiH,WAAWjH,EAAQ3E,UAGrC,OAAO,QAGVkJ,QAAO2C,GAAaA,IACpBC,KAAK,MAgBlB,MAAMC,EAAS,gBACTC,EAAY,SAkBZC,EAAS,IDMf,MAOIpI,YAAY9D,GACR9B,KAAK8B,KAAOA,EAIZ9B,KAAKiO,UAAYlB,EAKjB/M,KAAKkO,YAAcjB,EAInBjN,KAAKmO,gBAAkB,KAIvBhF,EAAU1F,KAAKzD,MAEfmN,eACA,OAAOnN,KAAKiO,UAEZd,aAASjN,GACT,KAAMA,KAAOqM,GACT,MAAM,IAAI6B,UAAU,kBAAkBlO,+BAE1CF,KAAKiO,UAAY/N,EAGrBmO,YAAYnO,GACRF,KAAKiO,UAA2B,iBAAR/N,EAAmBsM,EAAkBtM,GAAOA,EAEpEoO,iBACA,OAAOtO,KAAKkO,YAEZI,eAAWpO,GACX,GAAmB,mBAARA,EACP,MAAM,IAAIkO,UAAU,qDAExBpO,KAAKkO,YAAchO,EAEnBqO,qBACA,OAAOvO,KAAKmO,gBAEZI,mBAAerO,GACfF,KAAKmO,gBAAkBjO,EAK3BsO,SAASxP,GACLgB,KAAKmO,iBAAmBnO,KAAKmO,gBAAgBnO,KAAMuM,EAASE,SAAUzN,GACtEgB,KAAKkO,YAAYlO,KAAMuM,EAASE,SAAUzN,GAE9CyP,OAAOzP,GACHgB,KAAKmO,iBACDnO,KAAKmO,gBAAgBnO,KAAMuM,EAASG,WAAY1N,GACpDgB,KAAKkO,YAAYlO,KAAMuM,EAASG,WAAY1N,GAEhD0P,QAAQ1P,GACJgB,KAAKmO,iBAAmBnO,KAAKmO,gBAAgBnO,KAAMuM,EAASI,QAAS3N,GACrEgB,KAAKkO,YAAYlO,KAAMuM,EAASI,QAAS3N,GAE7C2P,QAAQ3P,GACJgB,KAAKmO,iBAAmBnO,KAAKmO,gBAAgBnO,KAAMuM,EAASK,QAAS5N,GACrEgB,KAAKkO,YAAYlO,KAAMuM,EAASK,QAAS5N,GAE7CJ,SAASI,GACLgB,KAAKmO,iBAAmBnO,KAAKmO,gBAAgBnO,KAAMuM,EAASM,SAAU7N,GACtEgB,KAAKkO,YAAYlO,KAAMuM,EAASM,SAAU7N,KCnFxB,iBAwEpB,EAAqB,YACrB4P,EAAsB,CACxB,CAACd,GAAS,YACV,uBAAU,mBACV,sBAAU,iBACV,6BAAU,wBACV,sBAAU,iBACV,6BAAU,wBACV,iBAAU,YACV,wBAAU,mBACV,qBAAU,YACV,4BAAU,mBACV,sBAAU,UACV,6BAAU,iBACV,0BAAU,WACV,iCAAU,kBACV,sBAAU,WACV,6BAAU,kBACV,wBAAU,YACV,+BAAU,mBACV,0BAAU,UACV,iCAAU,iBACV,oBAAU,WACV,2BAAU,kBACV,sBAAU,WACV,6BAAU,kBACV,UAAW,UACX,SAAQ,eAsBNe,EAAQ,IAAIzF,IAOZ0F,EAAc,IAAI1F,IAMxB,SAAS2F,EAAcC,EAAK9F,GACxB,IACI8F,EAAI/F,UAAUiD,aAAahD,GAE/B,MAAOnE,GACHiJ,EAAOQ,MAAM,aAAatF,EAAUpH,4CAA4CkN,EAAIlN,OAAQiD,IAiBpG,SAASkK,EAAmB/F,GACxB,MAAMgG,EAAgBhG,EAAUpH,KAChC,GAAIgN,EAAYnF,IAAIuF,GAEhB,OADAlB,EAAOQ,MAAM,sDAAsDU,OAC5D,EAEXJ,EAAY7O,IAAIiP,EAAehG,GAE/B,IAAK,MAAM8F,KAAOH,EAAM9D,SACpBgE,EAAcC,EAAK9F,GAEvB,OAAO,EAWX,SAAS,EAAa8F,EAAKlN,GACvB,OAAOkN,EAAI/F,UAAUmD,YAAYtK,GAsCrC,MAUMqN,EAAgB,IAAI3I,EAAa,MAAO,WAV/B,CACX,SAAyB,oFAEzB,eAAqC,gCACrC,gBAAuC,kFACvC,cAAmC,kDACnC,uBAAqD,6EAErD,uBAAqD,0DAoBzD,MAAM4I,EACFxJ,YAAYsE,EAASmF,EAAQpG,GACzBjJ,KAAKsP,YAAa,EAClBtP,KAAKuP,SAAW1P,OAAO2P,OAAO,GAAItF,GAClClK,KAAKyP,QAAU5P,OAAO2P,OAAO,GAAIH,GACjCrP,KAAK0P,MAAQL,EAAOvN,KACpB9B,KAAK2P,gCACDN,EAAOO,+BACX5P,KAAK6P,WAAa5G,EAClBjJ,KAAKiJ,UAAUiD,aAAa,IAAIhE,EAAU,OAAO,IAAMlI,MAAM,WAE7D4P,qCAEA,OADA5P,KAAK8P,iBACE9P,KAAK2P,gCAEZC,mCAA+B1P,GAC/BF,KAAK8P,iBACL9P,KAAK2P,gCAAkCzP,EAEvC4B,WAEA,OADA9B,KAAK8P,iBACE9P,KAAK0P,MAEZxF,cAEA,OADAlK,KAAK8P,iBACE9P,KAAKuP,SAEZF,aAEA,OADArP,KAAK8P,iBACE9P,KAAKyP,QAEZxG,gBACA,OAAOjJ,KAAK6P,WAEZE,gBACA,OAAO/P,KAAKsP,WAEZS,cAAU7P,GACVF,KAAKsP,WAAapP,EAMtB4P,iBACI,GAAI9P,KAAK+P,UACL,MAAMZ,EAAc1I,OAAO,cAAiC,CAAEuJ,QAAShQ,KAAK0P,SA2BxF,SAASO,EAAc/F,EAASgG,EAAY,IACxC,GAAyB,iBAAdA,EAAwB,CAE/BA,EAAY,CAAEpO,KADDoO,GAGjB,MAAMb,EAASxP,OAAO2P,OAAO,CAAE1N,KAAM,EAAoB8N,gCAAgC,GAASM,GAC5FpO,EAAOuN,EAAOvN,KACpB,GAAoB,iBAATA,IAAsBA,EAC7B,MAAMqN,EAAc1I,OAAO,eAAmC,CAC1DuJ,QAAS9I,OAAOpF,KAGxB,MAAMqO,EAActB,EAAM9O,IAAI+B,GAC9B,GAAIqO,EAAa,CAEb,GAAI9I,EAAU6C,EAASiG,EAAYjG,UAC/B7C,EAAUgI,EAAQc,EAAYd,QAC9B,OAAOc,EAGP,MAAMhB,EAAc1I,OAAO,gBAAqC,CAAEuJ,QAASlO,IAGnF,MAAMmH,EAAY,IAAI+C,EAAmBlK,GACzC,IAAK,MAAMoH,KAAa4F,EAAY/D,SAChC9B,EAAUiD,aAAahD,GAE3B,MAAMkH,EAAS,IAAIhB,EAAgBlF,EAASmF,EAAQpG,GAEpD,OADA4F,EAAM5O,IAAI6B,EAAMsO,GACTA,EAgFX,SAASC,EAAgBC,EAAkBvO,EAASwO,GAChD,IAAIpG,EAGJ,IAAIwD,EAA2D,QAAhDxD,EAAKyE,EAAoB0B,UAAsC,IAAPnG,EAAgBA,EAAKmG,EACxFC,IACA5C,GAAW,IAAI4C,KAEnB,MAAMC,EAAkB7C,EAAQ8C,MAAM,SAChCC,EAAkB3O,EAAQ0O,MAAM,SACtC,GAAID,GAAmBE,EAAiB,CACpC,MAAMC,EAAU,CACZ,+BAA+BhD,oBAA0B5L,OAY7D,OAVIyO,GACAG,EAAQlN,KAAK,iBAAiBkK,sDAE9B6C,GAAmBE,GACnBC,EAAQlN,KAAK,OAEbiN,GACAC,EAAQlN,KAAK,iBAAiB1B,2DAElCiM,EAAOW,KAAKgC,EAAQ9C,KAAK,MAG7BoB,EAAmB,IAAI/G,EAAU,GAAGyF,aAAmB,KAAM,CAAGA,QAAAA,EAAS5L,QAAAA,KAAY,YA4CzF,IAAgCwO,EAAAA,EAgBT,GAfnBtB,EAAmB,IAAI/G,EAAU,mBAAmBe,GAAa,IAAIuE,EAA0BvE,IAAY,YAE3GoH,EAAgBvC,EAAQC,EAAWwC,GAEnCF,EAAgBvC,EAAQC,EAAW,WAEnCsC,EAAgB,UAAW,IC/iB/BA,EAnBW,WACG,QAkBiB,O,aCjB/B,MAAM,EAAO,0BACP,EAAU,QAkBVO,EAAqB,IACrBC,EAAkB,UAClBC,EAAwB,SAExBC,EAA0B,KA4B1B,EAAgB,IAAIvK,EA3BV,gBACK,gBAkBS,CAC1B,4BAA+D,kDAC/D,iBAAyC,2CACzC,yBAAyD,mCACzD,iBAAyC,6FACzC,cAAmC,kDACnC,8BAAmE,6EAIvE,SAASwK,EAAcpS,GACnB,OAAQA,aAAiBoH,GACrBpH,EAAMsH,KAAKyB,SAAS,kBAmB5B,SAASsJ,GAAyB,UAAEC,IAChC,MAAO,4DAAqCA,kBAEhD,SAASC,EAAiCC,GACtC,MAAO,CACHC,MAAOD,EAASC,MAChBC,cAAe,EACfC,WAuCmCC,EAvCUJ,EAASG,UAyCnDE,OAAOD,EAAkBzO,QAAQ,IAAK,SAxCzC2O,aAAcrE,KAAKD,OAsC3B,IAA2CoE,EAnC3C5G,eAAe+G,EAAqBC,EAAaR,GAC7C,MACMS,SADqBT,EAASU,QACLlT,MAC/B,OAAO,EAAc6H,OAAO,iBAAuC,CAC/DmL,YAAAA,EACAG,WAAYF,EAAU3L,KACtB8L,cAAeH,EAAU1L,QACzB8L,aAAcJ,EAAUK,SAGhC,SAASC,GAAW,OAAEC,IAClB,OAAO,IAAIC,QAAQ,CACf,eAAgB,mBAChBC,OAAQ,mBACR,iBAAkBF,IAG1B,SAASG,GAAmBC,GAAW,aAAEC,IACrC,MAAMC,EAAUP,EAAWK,GAE3B,OADAE,EAAQC,OAAO,gBAoBnB,SAAgCF,GAC5B,MAAO,UAA4BA,IArBHG,CAAuBH,IAChDC,EAOX9H,eAAeiI,GAAmBC,GAC9B,MAAMpU,QAAeoU,IACrB,OAAIpU,EAAOwT,QAAU,KAAOxT,EAAOwT,OAAS,IAEjCY,IAEJpU,EAyEX,SAASqU,GAAMC,GACX,OAAO,IAAI1U,SAAQC,IACf0U,WAAW1U,EAASyU,MAyC5B,MAAME,GAAoB,oBAM1B,SAASC,KACL,IAGI,MAAMC,EAAe,IAAIC,WAAW,KACrBC,KAAKC,QAAUD,KAAKE,UAC5BC,gBAAgBL,GAEvBA,EAAa,GAAK,IAAcA,EAAa,GAAK,GAClD,MAAMM,EASd,SAAgBN,GAIZ,OAjD2BO,EA8CaP,EA7C5BQ,KAAK1M,OAAO2M,gBAAgBF,IAC7B5Q,QAAQ,MAAO,KAAKA,QAAQ,MAAO,MA+C7B+Q,OAAO,EAAG,IAjD/B,IAA+BH,EAoCXI,CAAOX,GACnB,OAAOF,GAAkBc,KAAKN,GAAOA,EAfzB,GAiBhB,MAAOvJ,GAEH,MAnBY,IA+CpB,SAAS8J,GAAOzB,GACZ,MAAO,GAAGA,EAAUxC,WAAWwC,EAAU0B,QAmB7C,MAAMC,GAAqB,IAAI/K,IAK/B,SAASgL,GAAW5B,EAAWkB,GAC3B,MAAMjP,EAAMwP,GAAOzB,GACnB6B,GAAuB5P,EAAKiP,GAqChC,SAA4BjP,EAAKiP,GAC7B,MAAMY,EAAUC,KACZD,GACAA,EAAQE,YAAY,CAAE/P,IAAAA,EAAKiP,IAAAA,IAE/Be,KAzCAC,CAAmBjQ,EAAKiP,GA2B5B,SAASW,GAAuB5P,EAAKiP,GACjC,MAAM3H,EAAYoI,GAAmBpU,IAAI0E,GACzC,GAAKsH,EAGL,IAAK,MAAM/I,KAAY+I,EACnB/I,EAAS0Q,GAUjB,IAAIiB,GAAmB,KAEvB,SAASJ,KAOL,OANKI,IAAoB,qBAAsBrB,OAC3CqB,GAAmB,IAAIC,iBAAiB,yBACxCD,GAAiBE,UAAY9P,IACzBsP,GAAuBtP,EAAE8B,KAAKpC,IAAKM,EAAE8B,KAAK6M,OAG3CiB,GAEX,SAASF,KAC2B,IAA5BN,GAAmBW,MAAcH,KACjCA,GAAiBI,QACjBJ,GAAmB,MAoB3B,MAEMK,GAAoB,+BAC1B,IAAIC,GAAY,KAChB,SAASC,KAcL,OAbKD,KACDA,IAAY,IAAApT,QANE,kCACG,GAKmCsT,IAMhD,GACS,IADDA,EAAU1T,WAEV0T,EAAUvS,kBAAkBoS,QAIrCC,GAGXrK,eAAe3K,GAAIuS,EAAWnT,GAC1B,MAAMoF,EAAMwP,GAAOzB,GAEb4C,SADWF,MACHxT,YAAYsT,GAAmB,aACvCtS,EAAc0S,EAAG1S,YAAYsS,IAC7BK,QAAiB3S,EAAY3C,IAAI0E,GAMvC,aALM/B,EAAY4S,IAAIjW,EAAOoF,SACvB2Q,EAAGhU,SACJiU,GAAYA,EAAS3B,MAAQrU,EAAMqU,KACpCU,GAAW5B,EAAWnT,EAAMqU,KAEzBrU,EAGXuL,eAAe2K,GAAO/C,GAClB,MAAM/N,EAAMwP,GAAOzB,GAEb4C,SADWF,MACHxT,YAAYsT,GAAmB,mBACvCI,EAAG1S,YAAYsS,IAAmBrK,OAAOlG,SACzC2Q,EAAGhU,SAQbwJ,eAAe4K,GAAOhD,EAAWiD,GAC7B,MAAMhR,EAAMwP,GAAOzB,GAEb4C,SADWF,MACHxT,YAAYsT,GAAmB,aACvCjU,EAAQqU,EAAG1S,YAAYsS,IACvBK,QAAiBtU,EAAMhB,IAAI0E,GAC3BiR,EAAWD,EAASJ,GAW1B,YAViB3R,IAAbgS,QACM3U,EAAM4J,OAAOlG,SAGb1D,EAAMuU,IAAII,EAAUjR,SAExB2Q,EAAGhU,UACLsU,GAAcL,GAAYA,EAAS3B,MAAQgC,EAAShC,KACpDU,GAAW5B,EAAWkD,EAAShC,KAE5BgC,EAuBX9K,eAAe+K,GAAqBnD,GAChC,IAAIoD,EACJ,MAAMC,QAA0BL,GAAOhD,GAAWsD,IAC9C,MAAMD,EAkBd,SAAyCC,GAKrC,OAAOC,GAJOD,GAAY,CACtBpC,IAAKP,KACL6C,mBAAoB,IArBMC,CAAgCH,GACpDI,EA+Bd,SAAwC1D,EAAWqD,GAC/C,GAA6C,IAAzCA,EAAkBG,mBAA4C,CAC9D,IAAKG,UAAUC,OAAQ,CAGnB,MAAO,CACHP,kBAAAA,EACAD,oBAHiCtX,QAAQE,OAAO,EAAciI,OAAO,iBAO7E,MAAM4P,EAAkB,CACpB3C,IAAKmC,EAAkBnC,IACvBsC,mBAAoB,EACpBM,iBAAkBjJ,KAAKD,OAErBwI,EAcdhL,eAAoC4H,EAAWqD,GAC3C,IACI,MAAMU,QA9Yd3L,eAAyC4H,GAAW,IAAEkB,IAClD,MAAM8C,EAAWvF,EAAyBuB,GACpCE,EAAUP,EAAWK,GACrBiE,EAAO,CACT/C,IAAAA,EACAgD,YAAa5F,EACboD,MAAO1B,EAAU0B,MACjByC,WAAY9F,GAEVxS,EAAU,CACZU,OAAQ,OACR2T,QAAAA,EACA+D,KAAMG,KAAKC,UAAUJ,IAEnBrF,QAAiByB,IAAmB,IAAMiE,MAAMN,EAAUnY,KAChE,GAAI+S,EAAS2F,GAAI,CACb,MAAMC,QAAsB5F,EAASU,OAOrC,MANoC,CAChC4B,IAAKsD,EAActD,KAAOA,EAC1BsC,mBAAoB,EACpBvD,aAAcuE,EAAcvE,aAC5BwE,UAAW9F,EAAiC6F,EAAcC,YAK9D,YAAYtF,EAAqB,sBAAuBP,GAoXd8F,CAA0B1E,EAAWqD,GAC/E,OAAO5V,GAAIuS,EAAW+D,GAE1B,MAAOxR,GAaH,MAZIiM,EAAcjM,IAAkC,MAA5BA,EAAEqB,WAAW2L,iBAG3BwD,GAAO/C,SAIPvS,GAAIuS,EAAW,CACjBkB,IAAKmC,EAAkBnC,IACvBsC,mBAAoB,IAGtBjR,GAhCsBoS,CAAqB3E,EAAW6D,GAC5D,MAAO,CAAER,kBAAmBQ,EAAiBT,oBAAAA,GAE5C,OAA6C,IAAzCC,EAAkBG,mBAChB,CACHH,kBAAAA,EACAD,oBAAqBwB,GAAyB5E,IAI3C,CAAEqD,kBAAAA,GAzDgBwB,CAA+B7E,EAAWqD,GAEnE,OADAD,EAAsBM,EAAiBN,oBAChCM,EAAiBL,qBAE5B,MAxPgB,KAwPZA,EAAkBnC,IAEX,CAAEmC,wBAAyBD,GAE/B,CACHC,kBAAAA,EACAD,oBAAAA,GAyERhL,eAAewM,GAAyB5E,GAIpC,IAAI8E,QAAcC,GAA0B/E,GAC5C,KAAoC,IAA7B8E,EAAMtB,0BAEHjD,GAAM,KACZuE,QAAcC,GAA0B/E,GAE5C,GAAiC,IAA7B8E,EAAMtB,mBAA4C,CAElD,MAAM,kBAAEH,EAAiB,oBAAED,SAA8BD,GAAqBnD,GAC9E,OAAIoD,GAKOC,EAGf,OAAOyB,EAUX,SAASC,GAA0B/E,GAC/B,OAAOgD,GAAOhD,GAAWsD,IACrB,IAAKA,EACD,MAAM,EAAcrP,OAAO,0BAE/B,OAAOsP,GAAqBD,MAGpC,SAASC,GAAqBuB,GAC1B,OASiD,KADbzB,EARDyB,GASTtB,oBACtBH,EAAkBS,iBAAmB1F,EAAqBvD,KAAKD,MATxD,CACHsG,IAAK4D,EAAM5D,IACXsC,mBAAoB,GAGrBsB,EAEX,IAAwCzB,EAqBxCjL,eAAe4M,IAAyB,UAAEhF,EAAS,uBAAEiF,GAA0B5B,GAC3E,MAAMW,EA6BV,SAAsChE,GAAW,IAAEkB,IAC/C,MAAO,GAAGzC,EAAyBuB,MAAckB,wBA9BhCgE,CAA6BlF,EAAWqD,GACnDnD,EAAUH,GAAmBC,EAAWqD,GAExC8B,EAAiBF,EAAuBxN,aAAa,CACvDG,UAAU,IAEVuN,GACAjF,EAAQC,OAAO,oBAAqBgF,EAAelK,yBAEvD,MAAMgJ,EAAO,CACTmB,aAAc,CACVjB,WAAY9F,IAGdxS,EAAU,CACZU,OAAQ,OACR2T,QAAAA,EACA+D,KAAMG,KAAKC,UAAUJ,IAEnBrF,QAAiByB,IAAmB,IAAMiE,MAAMN,EAAUnY,KAChE,GAAI+S,EAAS2F,GAAI,CAGb,OAD2B5F,QADCC,EAASU,QAKrC,YAAYH,EAAqB,sBAAuBP,GA6BhExG,eAAeiN,GAAiBC,EAAeC,GAAe,GAC1D,IAAIC,EACJ,MAAMV,QAAc9B,GAAOsC,EAActF,WAAWsD,IAChD,IAAKmC,GAAkBnC,GACnB,MAAM,EAAcrP,OAAO,kBAE/B,MAAMyR,EAAepC,EAASmB,UAC9B,IAAKc,GA8Fb,SAA0Bd,GACtB,OAAoC,IAA5BA,EAAU3F,gBAGtB,SAA4B2F,GACxB,MAAM7J,EAAMC,KAAKD,MACjB,OAAQA,EAAM6J,EAAUvF,cACpBuF,EAAUvF,aAAeuF,EAAU1F,UAAYnE,EAAM2D,EALpDoH,CAAmBlB,GAhGCmB,CAAiBF,GAElC,OAAOpC,EAEN,GAAmC,IAA/BoC,EAAa5G,cAGlB,OADA0G,EAwBZpN,eAAyCkN,EAAeC,GAIpD,IAAIT,QAAce,GAAuBP,EAActF,WACvD,KAAyC,IAAlC8E,EAAML,UAAU3F,qBAEbyB,GAAM,KACZuE,QAAce,GAAuBP,EAActF,WAEvD,MAAMyE,EAAYK,EAAML,UACxB,OAAgC,IAA5BA,EAAU3F,cAEHuG,GAAiBC,EAAeC,GAGhCd,EAxCYqB,CAA0BR,EAAeC,GACjDjC,EAEN,CAED,IAAKK,UAAUC,OACX,MAAM,EAAc3P,OAAO,eAE/B,MAAM4P,EA0FlB,SAA6CP,GACzC,MAAMyC,EAAsB,CACxBjH,cAAe,EACfkH,YAAanL,KAAKD,OAEtB,OAAOvN,OAAO2P,OAAO3P,OAAO2P,OAAO,GAAIsG,GAAW,CAAEmB,UAAWsB,IA/F/BE,CAAoC3C,GAE5D,OADAkC,EAsDZpN,eAAwCkN,EAAejC,GACnD,IACI,MAAMoB,QAAkBO,GAAyBM,EAAejC,GAC1D6C,EAA2B7Y,OAAO2P,OAAO3P,OAAO2P,OAAO,GAAIqG,GAAoB,CAAEoB,UAAAA,IAEvF,aADMhX,GAAI6X,EAActF,UAAWkG,GAC5BzB,EAEX,MAAOlS,GACH,IAAIiM,EAAcjM,IACe,MAA5BA,EAAEqB,WAAW2L,YAAkD,MAA5BhN,EAAEqB,WAAW2L,WAKhD,CACD,MAAM2G,EAA2B7Y,OAAO2P,OAAO3P,OAAO2P,OAAO,GAAIqG,GAAoB,CAAEoB,UAAW,CAAE3F,cAAe,WAC7GrR,GAAI6X,EAActF,UAAWkG,cAJ7BnD,GAAOuC,EAActF,WAM/B,MAAMzN,GAxEa4T,CAAyBb,EAAezB,GAChDA,MAMf,OAHkB2B,QACNA,EACNV,EAAML,UAoChB,SAASoB,GAAuB7F,GAC5B,OAAOgD,GAAOhD,GAAWsD,IACrB,IAAKmC,GAAkBnC,GACnB,MAAM,EAAcrP,OAAO,kBAE/B,MAAMyR,EAAepC,EAASmB,UAC9B,OAiDgC,KADHA,EAhDGiB,GAiDlB5G,eACd2F,EAAUuB,YAAc5H,EAAqBvD,KAAKD,MAjDvCvN,OAAO2P,OAAO3P,OAAO2P,OAAO,GAAIsG,GAAW,CAAEmB,UAAW,CAAE3F,cAAe,KAE7EwE,EA6Cf,IAAqCmB,KArBrC,SAASgB,GAAkBpC,GACvB,YAA8BnS,IAAtBmS,GACqC,IAAzCA,EAAkBG,mBAqF1BpL,eAAegO,GAASd,EAAeC,GAAe,GAClD,MAAMc,EAAoBf,QAO9BlN,eAAgD4H,GAC5C,MAAM,oBAAEoD,SAA8BD,GAAqBnD,GACvDoD,SAEMA,EAVJkD,CAAiCD,EAAkBrG,WAIzD,aADwBqF,GAAiBgB,EAAmBd,IAC3C1G,MAgMrB,SAAS0H,GAAqBC,GAC1B,OAAO,EAAcvS,OAAO,4BAA6D,CACrFuS,UAAAA,IAoBR,MAAMC,GAAqB,gBAerBC,GAAmBjQ,IACrB,MAEM6O,EAAgB,EAFV7O,EAAUmD,YAAY,OAAOnC,eAEDgP,IAAoBhP,eAK5D,MAJ8B,CAC1BkP,MAAO,IAtRfvO,eAAqBkN,GACjB,MAAMe,EAAoBf,GACpB,kBAAEjC,EAAiB,oBAAED,SAA8BD,GAAqBkD,EAAkBrG,WAShG,OARIoD,EACAA,EAAoB7P,MAAMwH,QAAQ3O,OAKlCiZ,GAAiBgB,GAAmB9S,MAAMwH,QAAQ3O,OAE/CiX,EAAkBnC,IA2QRyF,CAAMrB,GACnBc,SAAWb,GAAiBa,GAASd,EAAeC,KAKxD9I,EAAmB,IAAI/G,EAAU+Q,IAxBdhQ,IACnB,MAAM+F,EAAM/F,EAAUmD,YAAY,OAAOnC,eAEnCuI,EApDV,SAA0BxD,GACtB,IAAKA,IAAQA,EAAI9E,QACb,MAAM6O,GAAqB,qBAE/B,IAAK/J,EAAIlN,KACL,MAAMiX,GAAqB,YAG/B,MAAMK,EAAa,CACf,YACA,SACA,SAEJ,IAAK,MAAMC,KAAWD,EAClB,IAAKpK,EAAI9E,QAAQmP,GACb,MAAMN,GAAqBM,GAGnC,MAAO,CACHrJ,QAAShB,EAAIlN,KACboP,UAAWlC,EAAI9E,QAAQgH,UACvBkB,OAAQpD,EAAI9E,QAAQkI,OACpB8B,MAAOlF,EAAI9E,QAAQgK,OA8BLoF,CAAiBtK,GAQnC,MAN0B,CACtBA,IAAAA,EACAwD,UAAAA,EACAiF,uBAJ2B,EAAazI,EAAK,mBAK7C5D,QAAS,IAAM9M,QAAQC,aAeyC,WACpE0Q,EAAmB,IAAI/G,EA1BS,yBA0B8BgR,GAAiB,YASnF7I,EAAgB,EAAM,GAEtBA,EAAgB,EAAM,EAAS,WCxlC/B,MAEMkJ,GAAoB,0FAEpBC,GAAsB,kBAK5B,IAAIC,GAoBAC,GAsBJ,SAASC,GAAchG,GACnB,MAAMiG,EAAa,IAAIvG,WAAWM,GAElC,OADqBC,KAAK1M,OAAO2M,gBAAgB+F,IAC7B7W,QAAQ,KAAM,IAAIA,QAAQ,MAAO,KAAKA,QAAQ,MAAO,KAE7E,SAAS8W,GAAcC,GACnB,MACMC,GAAUD,EADA,IAAIE,QAAQ,EAAKF,EAAa7W,OAAS,GAAM,IAExDF,QAAQ,MAAO,KACfA,QAAQ,KAAM,KACbkX,EAAUC,KAAKH,GACfI,EAAc,IAAI9G,WAAW4G,EAAQhX,QAC3C,IAAK,IAAIuC,EAAI,EAAGA,EAAIyU,EAAQhX,SAAUuC,EAClC2U,EAAY3U,GAAKyU,EAAQvU,WAAWF,GAExC,OAAO2U,GAxDX,SAAWT,GACPA,EAAYA,EAA0B,aAAI,GAAK,eAC/CA,EAAYA,EAAkC,qBAAI,GAAK,uBAF3D,CAGGD,KAAkBA,GAAgB,KAiBrC,SAAWC,GACPA,EAA2B,cAAI,gBAC/BA,EAAkC,qBAAI,uBAF1C,CAGGA,KAAgBA,GAAc,KAoDjC,MAAMU,GAAc,uBAMdC,GAAwB,yBA2H9B,MAEM,GAAoB,2BAC1B,IAAI,GAAY,KAChB,SAAS,KAaL,OAZK,KACD,IAAY,IAAAxY,QANE,8BACG,GAKmCyY,IAKhD,GACS,IADDA,EAAU7Y,WAEV6Y,EAAU1X,kBAAkB,QAIrC,GAGXgI,eAAe2P,GAAMC,GACjB,MAAM/V,EAAM,GAAO+V,GACbhZ,QAAW,KACXiZ,QAAqBjZ,EACtBE,YAAY,IACZgB,YAAY,IACZ3C,IAAI0E,GACT,GAAIgW,EACA,OAAOA,EAEN,CAED,MAAMC,QA1Jd9P,eAAkC+P,GAC9B,GAAI,cAAe1Y,UAAW,CAG1B,MACM2Y,SADkB3Y,UAAU4Y,aACR3P,KAAI1J,GAAMA,EAAGM,OACvC,IAAK8Y,EAAQjT,SAASyS,IAElB,OAAO,KAGf,IAAIK,EAAe,KAuEnB,aAtEiB,IAAA5Y,QAAOuY,GAdL,GAckCxP,MAAOpJ,IACxD,IAAI2I,EACJ,GAAI3I,EAAGC,WAAa,EAEhB,OAEJ,IAAKD,EAAGsZ,iBAAiBC,SAASV,IAE9B,OAEJ,MAAM3X,EAAclB,EAAGE,YAAYgB,YAAY2X,IACzChb,QAAcqD,EAAYjC,MAAM,eAAeV,IAAI4a,GAEzD,SADMjY,EAAYsY,QACb3b,EAIL,GAAsB,IAAlBmC,EAAGC,WAAkB,CACrB,MAAMwZ,EAAa5b,EACnB,IAAK4b,EAAWC,OAASD,EAAWE,SAAWF,EAAWzE,SACtD,OAEJiE,EAAe,CACXpJ,MAAO4J,EAAWG,SAClBC,WAA6C,QAAhClR,EAAK8Q,EAAWI,kBAA+B,IAAPlR,EAAgBA,EAAKkD,KAAKD,MAC/EkO,oBAAqB,CACjBJ,KAAMD,EAAWC,KACjBC,OAAQF,EAAWE,OACnB3E,SAAUyE,EAAWzE,SACrB+E,QAASN,EAAWM,QACpBC,SAAyC,iBAAxBP,EAAWO,SACtBP,EAAWO,SACX7B,GAAcsB,EAAWO,iBAItC,GAAsB,IAAlBha,EAAGC,WAAkB,CAC1B,MAAMwZ,EAAa5b,EACnBob,EAAe,CACXpJ,MAAO4J,EAAWG,SAClBC,WAAYJ,EAAWI,WACvBC,oBAAqB,CACjBJ,KAAMvB,GAAcsB,EAAWC,MAC/BC,OAAQxB,GAAcsB,EAAWE,QACjC3E,SAAUyE,EAAWzE,SACrB+E,QAASN,EAAWM,QACpBC,SAAU7B,GAAcsB,EAAWO,iBAI1C,GAAsB,IAAlBha,EAAGC,WAAkB,CAC1B,MAAMwZ,EAAa5b,EACnBob,EAAe,CACXpJ,MAAO4J,EAAWG,SAClBC,WAAYJ,EAAWI,WACvBC,oBAAqB,CACjBJ,KAAMvB,GAAcsB,EAAWC,MAC/BC,OAAQxB,GAAcsB,EAAWE,QACjC3E,SAAUyE,EAAWzE,SACrB+E,QAASN,EAAWM,QACpBC,SAAU7B,GAAcsB,EAAWO,iBAKhDzG,cAEG,IAAA3S,UAASgY,UACT,IAAAhY,UAAS,8BACT,IAAAA,UAAS,aAGnB,SAA2BqY,GACvB,IAAKA,IAAiBA,EAAaa,oBAC/B,OAAO,EAEX,MAAM,oBAAEA,GAAwBb,EAChC,MAA2C,iBAA5BA,EAAaY,YACxBZ,EAAaY,WAAa,GACI,iBAAvBZ,EAAapJ,OACpBoJ,EAAapJ,MAAMpO,OAAS,GACQ,iBAA7BqY,EAAoBJ,MAC3BI,EAAoBJ,KAAKjY,OAAS,GACI,iBAA/BqY,EAAoBH,QAC3BG,EAAoBH,OAAOlY,OAAS,GACI,iBAAjCqY,EAAoB9E,UAC3B8E,EAAoB9E,SAASvT,OAAS,GACC,iBAAhCqY,EAAoBC,SAC3BD,EAAoBC,QAAQtY,OAAS,GACG,iBAAjCqY,EAAoBE,UAC3BF,EAAoBE,SAASvY,OAAS,EApBnCwY,CAAkBhB,GAAgBA,EAAe,KAwEtBiB,CAAmBlB,EAAqBhI,UAAUmI,UAChF,GAAID,EAEA,aADMiB,GAAMnB,EAAsBE,GAC3BA,GAKnB9P,eAAe+Q,GAAMnB,EAAsBC,GACvC,MAAMhW,EAAM,GAAO+V,GAEbpF,SADW,MACH1T,YAAY,GAAmB,aAG7C,aAFM0T,EAAG1S,YAAY,IAAmB4S,IAAImF,EAAchW,SACpD2Q,EAAGhU,SACFqZ,EAUX,SAAS,IAAO,UAAEjI,IACd,OAAOA,EAAU0B,MAmBrB,MAuBM,GAAgB,IAAI1N,EAAa,YAAa,YAvBlC,CACd,4BAA+D,kDAC/D,2BAAwD,gDACxD,uBAAgD,wDAChD,qBAAiD,qEACjD,qBAAiD,mEACjD,sBAAmD,2EACnD,yBAAyD,mGACzD,qCAA0E,+EAC1E,yBAAyD,qEACzD,2BAA6D,2DAC7D,2BAA6D,yEAE7D,sBAAmD,oEACnD,wBAAuD,wDACvD,yBAAyD,4IAEzD,0BAA2D,uEAC3D,qBAAiD,iEACjD,oBAA+C,yCAC/C,gCAAuE,0IA+E3EoE,eAAegR,GAAmBpB,EAAsBnJ,GACpD,MACMwK,EAAqB,CACvB9c,OAAQ,SACR2T,cAHkB,GAAW8H,IAKjC,IACI,MAAMpJ,QAAiB0F,MAAM,GAAGgF,GAAYtB,EAAqBhI,cAAcnB,IAASwK,GAClFE,QAAqB3K,EAASU,OACpC,GAAIiK,EAAand,MAAO,CACpB,MAAMuH,EAAU4V,EAAand,MAAMuH,QACnC,MAAM,GAAcM,OAAO,2BAA2D,CAClFuV,UAAW7V,KAIvB,MAAO8V,GACH,MAAM,GAAcxV,OAAO,2BAA2D,CAClFuV,UAAWC,KAIvB,SAASH,IAAY,UAAE5K,IACnB,MAAO,uDAAwBA,kBAEnCtG,eAAe,IAAW,UAAE4H,EAAS,cAAEsF,IACnC,MAAMb,QAAkBa,EAAcc,WACtC,OAAO,IAAIvG,QAAQ,CACf,eAAgB,mBAChBC,OAAQ,mBACR,iBAAkBE,EAAUJ,OAC5B,qCAAsC,OAAO6E,MAGrD,SAASiF,IAAQ,OAAEf,EAAM,KAAED,EAAI,SAAE1E,EAAQ,SAAEgF,IACvC,MAAM/E,EAAO,CACT0F,IAAK,CACD3F,SAAAA,EACA0E,KAAAA,EACAC,OAAAA,IAMR,OAHIK,IAAajC,KACb9C,EAAK0F,IAAIC,kBAAoBZ,GAE1B/E,EAqBX7L,eAAeyR,GAAiBC,GAC5B,MAAMC,QAgFV3R,eAAmC4R,EAAgBhB,GAC/C,MAAMiB,QAAqBD,EAAeE,YAAYC,kBACtD,GAAIF,EACA,OAAOA,EAEX,OAAOD,EAAeE,YAAYE,UAAU,CACxCC,iBAAiB,EAGjBC,qBAAsBjD,GAAc2B,KAzFTuB,CAAoBT,EAAUE,eAAgBF,EAAUd,UACjFF,EAAsB,CACxBE,SAAUc,EAAUd,SACpBD,QAASe,EAAUE,eAAeQ,MAClCxG,SAAU+F,EAAiB/F,SAC3B0E,KAAMvB,GAAc4C,EAAiBtI,OAAO,SAC5CkH,OAAQxB,GAAc4C,EAAiBtI,OAAO,YAE5CwG,QAAqBF,GAAM+B,EAAU9B,sBAC3C,GAAKC,EAIA,IAkFT,SAAsBwC,EAAWC,GAC7B,MAAMC,EAAkBD,EAAe1B,WAAayB,EAAUzB,SACxD4B,EAAkBF,EAAe1G,WAAayG,EAAUzG,SACxD6G,EAAcH,EAAehC,OAAS+B,EAAU/B,KAChDoC,EAAgBJ,EAAe/B,SAAW8B,EAAU9B,OAC1D,OAAOgC,GAAmBC,GAAmBC,GAAeC,EAvFlDC,CAAa9C,EAAaa,oBAAqBA,GAWpD,OAAIjO,KAAKD,OAASqN,EAAaY,WA1BZ,OAyD5BzQ,eAA2B0R,EAAW7B,GAClC,IACI,MAAM+C,QAzJd5S,eAAkC4P,EAAsBC,GACpD,MAAM/H,QAAgB,GAAW8H,GAC3B/D,EAAOyF,GAAQzB,EAAaa,qBAC5BmC,EAAgB,CAClB1e,OAAQ,QACR2T,QAAAA,EACA+D,KAAMG,KAAKC,UAAUJ,IAEzB,IAAIsF,EACJ,IACI,MAAM3K,QAAiB0F,MAAM,GAAGgF,GAAYtB,EAAqBhI,cAAciI,EAAapJ,QAASoM,GACrG1B,QAAqB3K,EAASU,OAElC,MAAOmK,GACH,MAAM,GAAcxV,OAAO,sBAAiD,CACxEuV,UAAWC,IAGnB,GAAIF,EAAand,MAAO,CACpB,MAAMuH,EAAU4V,EAAand,MAAMuH,QACnC,MAAM,GAAcM,OAAO,sBAAiD,CACxEuV,UAAW7V,IAGnB,IAAK4V,EAAa1K,MACd,MAAM,GAAc5K,OAAO,yBAE/B,OAAOsV,EAAa1K,MA8HWqM,CAAmBpB,EAAU9B,qBAAsBC,GACxEkD,EAAsB9d,OAAO2P,OAAO3P,OAAO2P,OAAO,GAAIiL,GAAe,CAAEpJ,MAAOmM,EAAcnC,WAAYhO,KAAKD,QAEnH,aADMuO,GAAMW,EAAU9B,qBAAsBmD,GACrCH,EAEX,MAAOzY,GAEH,YADM6Y,GAAoBtB,GACpBvX,GAtCC8Y,CAAYvB,EAAW,CAC1BjL,MAAOoJ,EAAapJ,MACpBgK,WAAYhO,KAAKD,MACjBkO,oBAAAA,IAKGb,EAAapJ,MAnBpB,UACUuK,GAAmBU,EAAU9B,qBAAsBC,EAAapJ,OAE1E,MAAOtM,GAEHwI,QAAQoB,KAAK5J,GAEjB,OAAO+Y,GAAYxB,EAAU9B,qBAAsBc,GAXnD,OAAOwC,GAAYxB,EAAU9B,qBAAsBc,GA8B3D1Q,eAAegT,GAAoBtB,GAC/B,MAAM7B,QAAqBF,GAAM+B,EAAU9B,sBACvCC,UACMmB,GAAmBU,EAAU9B,qBAAsBC,EAAapJ,aA7O9EzG,eAAwB4P,GACpB,MAAM/V,EAAM,GAAO+V,GAEbpF,SADW,MACH1T,YAAY,GAAmB,mBACvC0T,EAAG1S,YAAY,IAAmBiI,OAAOlG,SACzC2Q,EAAGhU,SAyOC2c,CAASzB,EAAU9B,uBAG7B,MAAM+B,QAAyBD,EAAUE,eAAeE,YAAYC,kBACpE,OAAIJ,GACOA,EAAiByB,cAiBhCpT,eAAekT,GAAYtD,EAAsBc,GAC7C,MAAMjK,QAjMVzG,eAA+B4P,EAAsBc,GACjD,MAAM5I,QAAgB,GAAW8H,GAC3B/D,EAAOyF,GAAQZ,GACf2C,EAAmB,CACrBlf,OAAQ,OACR2T,QAAAA,EACA+D,KAAMG,KAAKC,UAAUJ,IAEzB,IAAIsF,EACJ,IACI,MAAM3K,QAAiB0F,MAAMgF,GAAYtB,EAAqBhI,WAAYyL,GAC1ElC,QAAqB3K,EAASU,OAElC,MAAOmK,GACH,MAAM,GAAcxV,OAAO,yBAAuD,CAC9EuV,UAAWC,IAGnB,GAAIF,EAAand,MAAO,CACpB,MAAMuH,EAAU4V,EAAand,MAAMuH,QACnC,MAAM,GAAcM,OAAO,yBAAuD,CAC9EuV,UAAW7V,IAGnB,IAAK4V,EAAa1K,MACd,MAAM,GAAc5K,OAAO,4BAE/B,OAAOsV,EAAa1K,MAsKA6M,CAAgB1D,EAAsBc,GACpDb,EAAe,CACjBpJ,MAAAA,EACAgK,WAAYhO,KAAKD,MACjBkO,oBAAAA,GAGJ,aADMK,GAAMnB,EAAsBC,GAC3BA,EAAapJ,MA4CxB,SAAS8M,GAAmBC,GACxB,MAAMC,EAAU,CACZvT,KAAMsT,EAAgBtT,KAEtBwT,YAAaF,EAAgBG,aAE7BC,UAAWJ,EAAgBK,cAK/B,OAEJ,SAAsCJ,EAASK,GAC3C,IAAKA,EAAuBC,aACxB,OAEJN,EAAQM,aAAe,GACvB,MAAMC,EAAQF,EAAuBC,aAAaC,MAC5CA,IACFP,EAAQM,aAAaC,MAAQA,GAEjC,MAAMnI,EAAOiI,EAAuBC,aAAalI,KAC3CA,IACF4H,EAAQM,aAAalI,KAAOA,GAEhC,MAAMoI,EAAQH,EAAuBC,aAAaE,MAC5CA,IACFR,EAAQM,aAAaE,MAAQA,GApBjCC,CAA6BT,EAASD,GAuB1C,SAA8BC,EAASK,GACnC,IAAKA,EAAuB7X,KACxB,OAEJwX,EAAQxX,KAAO6X,EAAuB7X,KA1BtCkY,CAAqBV,EAASD,GA4BlC,SAA6BC,EAASK,GAClC,IAAKA,EAAuBM,WACxB,OAEJX,EAAQW,WAAa,GACrB,MAAMC,EAAOP,EAAuBM,WAAWC,KACzCA,IACFZ,EAAQW,WAAWC,KAAOA,GAG9B,MAAMC,EAAiBR,EAAuBM,WAAWG,gBACnDD,IACFb,EAAQW,WAAWE,eAAiBA,GAvCxCE,CAAoBf,EAASD,GACtBC,EAiFX,SAASgB,GAAcC,EAAIC,GACvB,MAAMC,EAAc,GACpB,IAAK,IAAIha,EAAI,EAAGA,EAAI8Z,EAAGrc,OAAQuC,IAC3Bga,EAAY/b,KAAK6b,EAAGG,OAAOja,IACvBA,EAAI+Z,EAAGtc,QACPuc,EAAY/b,KAAK8b,EAAGE,OAAOja,IAGnC,OAAOga,EAAY3R,KAAK,IA+C5B,SAAS,GAAqBmL,GAC1B,OAAO,GAAcvS,OAAO,4BAA6D,CACrFuS,UAAAA,IA3DRqG,GAAc,mCAAoC,mCAClDA,GAAc,uBAAwB,uBA8EtC,MAAMK,GACF9Z,YAAYoJ,EAAK8I,EAAe6H,GAE5B3f,KAAK4f,0CAA2C,EAChD5f,KAAK6f,2BAA6B,KAClC7f,KAAK8f,iBAAmB,KACxB9f,KAAK+f,UAAY,GACjB/f,KAAKggB,qBAAsB,EAC3B,MAAMxN,EA1Dd,SAA0BxD,GACtB,IAAKA,IAAQA,EAAI9E,QACb,MAAM,GAAqB,4BAE/B,IAAK8E,EAAIlN,KACL,MAAM,GAAqB,YAG/B,MAAMsX,EAAa,CACf,YACA,SACA,QACA,sBAEE,QAAElP,GAAY8E,EACpB,IAAK,MAAMqK,KAAWD,EAClB,IAAKlP,EAAQmP,GACT,MAAM,GAAqBA,GAGnC,MAAO,CACHrJ,QAAShB,EAAIlN,KACboP,UAAWhH,EAAQgH,UACnBkB,OAAQlI,EAAQkI,OAChB8B,MAAOhK,EAAQgK,MACfyG,SAAUzQ,EAAQ+V,mBAiCA,CAAiBjR,GACnChP,KAAKwa,qBAAuB,CACxBxL,IAAAA,EACAwD,UAAAA,EACAsF,cAAAA,EACA6H,kBAAAA,GAGRvU,UACI,OAAO9M,QAAQC,WAoBvBqM,eAAesV,GAAkB5D,GAC7B,IACIA,EAAUE,qBAAuBrG,UAAUgK,cAAcC,SA/wBzC,4BA+wBmE,CAC/EpD,MA/wBa,yCAsxBjBV,EAAUE,eAAehH,SAASzP,OAAM,SAI5C,MAAOhB,GACH,MAAM,GAAc0B,OAAO,qCAAwE,CAC/F4Z,oBAAqBtb,EAAEoB,WA2EnCyE,eAAe0V,GAAWhE,EAAWpS,GACjC,IAAKiM,UACD,MAAM,GAAc1P,OAAO,4BAK/B,GAHgC,YAA5B8Z,aAAaC,kBACPD,aAAaE,oBAES,YAA5BF,aAAaC,WACb,MAAM,GAAc/Z,OAAO,sBAI/B,aArCJmE,eAA8B0R,EAAWd,GAC/BA,EACFc,EAAUd,SAAWA,EAEfc,EAAUd,WAChBc,EAAUd,SAAWjC,IA8BnBmH,CAAepE,EAAWpS,MAAAA,OAAyC,EAASA,EAAQsR,gBAhE9F5Q,eAA2B0R,EAAWE,GAIlC,GAHKA,GAAmBF,EAAUE,sBACxB0D,GAAkB5D,GAEvBE,IAAoBF,EAAUE,eAAnC,CAGA,KAAMA,aAA0BmE,2BAC5B,MAAM,GAAcla,OAAO,2BAE/B6V,EAAUE,eAAiBA,GAuDrBoE,CAAYtE,EAAWpS,MAAAA,OAAyC,EAASA,EAAQ2W,2BAChFxE,GAAiBC,GAmB5B1R,eAAekW,GAAWxE,EAAWyE,EAAala,GAC9C,MAAMma,EAWV,SAAsBD,GAClB,OAAQA,GACJ,KAAKrH,GAAYuH,qBACb,MAAO,oBACX,KAAKvH,GAAYwH,cACb,MAAO,0BACX,QACI,MAAM,IAAIjb,OAlBAkb,CAAaJ,UACPzE,EAAU9B,qBAAqBmF,kBAAkB5f,OAC/DqhB,SAASJ,EAAW,CAE1BK,WAAYxa,EAAK2S,mBACjB8H,aAAcza,EAx4BQ,kBAy4BtB0a,aAAc1a,EAx4BQ,iBAy4BtB2a,oBAAqBC,KAAKC,MAAMrU,KAAKD,MAAQ,OA+BrDxC,eAAe+W,GAAqBrF,EAAWna,GAC3C,MAAMic,EAAkBjc,EAAM0E,KAC9B,IAAKuX,EAAgBwD,oBACjB,OAEAtF,EAAUwD,kBACV1B,EAAgB2C,cAAgBrH,GAAYwH,gBACF,mBAA/B5E,EAAUwD,iBACjBxD,EAAUwD,iBAAiB3B,GAAmBC,IAG9C9B,EAAUwD,iBAAiB+B,KAAK1D,GAAmBC,KAI3D,MAAM0D,EAAc1D,EAAgBvX,KAzTxC,IAA0BA,EAEC,iBAFDA,EA0TDib,IAxTgBjb,GAAQ2S,MAAuB3S,GAyTZ,MAApDib,EAv7BmC,uBAw7B7BhB,GAAWxE,EAAW8B,EAAgB2C,YAAae,GAIjE,MAAM,GAAO,sBACP,GAAU,QAuBVC,GAAkC9Y,IACpC,MAAMqT,EAAYrT,EACbmD,YAAY,aACZnC,eAIL,MAH0B,CACtB2O,SAAW1O,GAAYoW,GAAWhE,EAAWpS,KAkCrDU,eAAeoX,KACX,UN3XO,IAAI1jB,SAAQ,CAACC,EAASC,KACzB,IACI,IAAIyjB,GAAW,EACf,MAAMC,EAAgB,0DAChB7jB,EAAUiV,KAAKrR,UAAUkgB,KAAKD,GACpC7jB,EAAQI,UAAY,KAChBJ,EAAQK,OAAOqW,QAEVkN,GACD3O,KAAKrR,UAAUmgB,eAAeF,GAElC3jB,GAAQ,IAEZF,EAAQ6D,gBAAkB,KACtB+f,GAAW,GAEf5jB,EAAQM,QAAU,KACd,IAAIwL,EACJ3L,GAAiC,QAAxB2L,EAAK9L,EAAQO,aAA0B,IAAPuL,OAAgB,EAASA,EAAGhE,UAAY,KAGzF,MAAOvH,GACHJ,EAAOI,OM0Wf,MAAOmG,GACH,OAAO,EAKX,MAA0B,oBAAXC,QNhZa,iBAAd/C,aA0CW,oBAAdkU,YAA8BA,UAAUkM,gBMyW/C,kBAAmBlM,WACnB,gBAAiBnR,QACjB,iBAAkBA,QAClB,UAAWA,QACX2b,0BAA0B1iB,UAAUgH,eAAe,qBACnDqd,iBAAiBrkB,UAAUgH,eAAe,UA8ElD,SAASsd,GAAqBvT,EHprB9B,SAAgBlN,EAAO,aACnB,MAAMkN,EAAMH,EAAM9O,IAAI+B,GACtB,IAAKkN,EACD,MAAMG,EAAc1I,OAAO,SAAuB,CAAEuJ,QAASlO,IAEjE,OAAOkN,EG+qByB,IAchC,OATAgT,KAAoB7iB,MAAKqjB,IAErB,IAAKA,EACD,MAAM,GAAc/b,OAAO,0BAEhCQ,IAEC,MAAM,GAAcR,OAAO,6BAExB,EAAauB,EAAmBgH,GAAM,aAAa/E,eAiB9DW,eAAe,GAAS0R,EAAWpS,GAE/B,OAAOoW,GADPhE,EAAYtU,EAAmBsU,GACFpS,GAYjC,SAASuY,GAAYnG,GAEjB,OA1GJ1R,eAA6B0R,GACzB,IAAKnG,UACD,MAAM,GAAc1P,OAAO,4BAK/B,OAHK6V,EAAUE,sBACL0D,GAAkB5D,GAErBsB,GAAoBtB,GAmGpBoG,CADPpG,EAAYtU,EAAmBsU,IAgBnC,SAASqG,GAAUrG,EAAWsG,GAE1B,OAjGJ,SAAqBtG,EAAWsG,GAC5B,IAAKzM,UACD,MAAM,GAAc1P,OAAO,4BAG/B,OADA6V,EAAUwD,iBAAmB8C,EACtB,KACHtG,EAAUwD,iBAAmB,MA2F1B+C,CADPvG,EAAYtU,EAAmBsU,GACDsG,GA/L9B3T,EAAmB,IAAI/G,EAAU,aAfLe,IAC5B,MAAMqT,EAAY,IAAIoD,GAAiBzW,EAAUmD,YAAY,OAAOnC,eAAgBhB,EAAUmD,YAAY,0BAA0BnC,eAAgBhB,EAAUmD,YAAY,uBAE1K,OADA+J,UAAUgK,cAAc2C,iBAAiB,WAAW/d,GAAK4c,GAAqBrF,EAAWvX,KAClFuX,IAY+D,WACtErN,EAAmB,IAAI/G,EAAU,qBAAsB6Z,GAAgC,YACvF1R,EAAgB,GAAM,IAEtBA,EAAgB,GAAM,GAAS,WChgCnC,MAAM,GAA+B0S,O,eCGtB,MAAMC,WAAc9d,IAAAA,cACjC+d,SACE,OAAOjjB,KAAK6I,MAAMqa,QAChB,yBAAKC,UAAU,mBACb,yBAAKA,UAAU,SACb,yBAAKA,UAAU,SAASnjB,KAAK6I,MAAM+V,OACnC,yBAAKuE,UAAU,WAAWnjB,KAAK6I,MAAMua,SACrC,yBAAKD,UAAU,kBACZnjB,KAAK6I,MAAMwa,SACV,4BAAQF,UAAU,UAAUG,QAAStjB,KAAK6I,MAAMwa,UAC7CrjB,KAAK6I,MAAMrK,QAAU,kBAAC,EAAA+kB,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAG3E,KAEF,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6I,MAAM6a,WAC7C1jB,KAAK6I,MAAM8a,SAAW,kBAAC,EAAAJ,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,4BAMhF,MCvBG,MAAM5S,GAAkB,gBCClB+S,GAAW,0BAMXC,GAAc,CAACC,OAAQ,gBAAiBC,MAAO,kBAG/CC,GAAeH,GAAYC,OAM3BG,GAAiB,IAcjBC,GAA0B,QAa1BC,GAAW,GAIXC,GAAc,IAiBdC,GAA6B,OAI7BC,GAA6B,GAAK,GAWlCC,GAAsB,GAOtBC,GAAmB,GAEnBC,GAA+B,ICpFtCC,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BjW,KAAM,CACJ8U,GAAI,iBACJC,eAAgB,CAAF,wBAGhBmB,eAAgB,CACdpB,GAAI,2BACJC,eAAgB,CAAF,kCAGhBoB,cAAe,CACbrB,GAAI,mCACJC,eAAgB,CAAF,iCAGhB9Y,OAAQ,CACN6Y,GAAI,mBACJC,eAAgB,CAAF,0BAGhBqB,eAAgB,CACdtB,GAAI,2BACJC,eAAgB,CAAF,kCAGhBsB,WAAY,CACVvB,GAAI,uBACJC,eAAgB,CAAF,yBAGhBuB,KAAM,CACJxB,GAAI,iBACJC,eAAgB,CAAF,wBAGhBwB,OAAQ,CACNzB,GAAI,mBACJC,eAAgB,CAAF,0BAGhByB,MAAO,CACL1B,GAAI,kBACJC,eAAgB,CAAF,yBAGhB0B,QAAS,CACP3B,GAAI,oBACJC,eAAgB,CAAF,2BAGhB2B,aAAc,CACZ5B,GAAI,yBACJC,eAAgB,CAAF,0BAGhB4B,qBAAsB,CACpB7B,GAAI,uBACJC,eAAgB,CAAF,2FAGhB6B,wBAAyB,CACvB9B,GAAI,0BACJC,eAAgB,CAAF,mGAGhB8B,QAAS,CACP/B,GAAI,oBACJC,eAAgB,CAAF,2BAIhB+B,MAAO,CACLhC,GAAI,kBACJC,eAAgB,CAAF,yBAGhBgC,oBAAqB,CACnBjC,GAAI,sBACJC,eAAgB,CAAF,qEAGhBiC,cAAe,CACblC,GAAI,0BACJC,eAAgB,CAAF,0BAGhBkC,QAAS,CACPnC,GAAI,0BACJC,eAAgB,CAAF,2BAGhBmC,UAAW,CACTpC,GAAI,0BACJC,eAAgB,CAAF,2BAGhBoC,iBAAkB,CAChBrC,GAAI,6BACJC,eAAgB,CAAF,oCAGhBqC,uBAAwB,CACtBtC,GAAI,yBACJC,eAAgB,CAAF,uFAKlB,MAAMsC,WAAoB7gB,IAAAA,UACxBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKgmB,QAAU9gB,IAAAA,YAEf,MAAM,cAAC+gB,GAAiBpd,EAAMqd,KAE9BlmB,KAAKmmB,gBAAkBnmB,KAAKmmB,gBAAgBC,KAAKpmB,MACjDA,KAAKqmB,gBAAkBrmB,KAAKqmB,gBAAgBD,KAAKpmB,MACjDA,KAAKsmB,YAActmB,KAAKsmB,YAAYF,KAAKpmB,MAGzCA,KAAKumB,UAAY,CACf,WAAc,CACZ/C,GAAI,aACJ5E,MAAOqH,EAAcvB,GAAShW,MAC9B8X,QAAS,MAEX,eAAkB,CAChBhD,GAAI,iBACJ5E,MAAOqH,EAAcvB,GAASE,gBAC9B4B,QAAS,CAACC,EAAQC,IACT7d,EAAM8d,YACXV,EAAcvB,GAASE,gBACvBqB,EAAcvB,GAASoB,yBACtB,KAAQ9lB,KAAK4mB,gBAAe,GAAM,EAAOH,EAAQC,KAClD,MACA,EACA,OAIN,oBAAuB,CACrBlD,GAAI,sBACJ5E,MAAOqH,EAAcvB,GAASG,eAC9B2B,QAAS,CAACC,EAAQC,IACT7d,EAAM8d,YACXV,EAAcvB,GAASG,eACvBoB,EAAc9f,QAAQmf,0BACrB,IAAetlB,KAAK4mB,gBAAe,GAAM,EAAMH,EAAQC,IACxD,MACA,EACA,OAIN,eAAkB,CAChBlD,GAAI,iBACJ5E,MAAOqH,EAAcvB,GAAS/Z,QAC9B6b,QAAS,CAACC,EAAQC,IACT1mB,KAAK4mB,gBAAe,GAAO,EAAOH,EAAQC,IAGrD,oBAAuB,CACrBlD,GAAI,sBACJ5E,MAAOqH,EAAcvB,GAASI,gBAC9B0B,QAAS,CAACC,EAAQC,IACT1mB,KAAK4mB,gBAAe,GAAO,EAAMH,EAAQC,IAGpD,qBAAwB,CACtBlD,GAAI,uBACJ5E,MAAOqH,EAAcvB,GAASK,YAC9ByB,QAAS,CAACC,EAAQC,IACT1mB,KAAK6mB,aAAaJ,EAAQC,IAGrC,gBAAmB,CACjBlD,GAAI,kBACJ5E,MAAOqH,EAAcvB,GAASQ,OAC9BsB,QAAS,CAACC,EAAQC,IACT1mB,KAAK8mB,eAAeL,EAAQC,IAGvC,kBAAqB,CACnBlD,GAAI,oBACJ5E,MAAOqH,EAAcvB,GAASS,SAC9BqB,QAAS,QAEX,aAAgB,CACdhD,GAAI,eACJ5E,MAAOqH,EAAcvB,GAASO,QAC9BuB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,OAEjD,WAAc,CACZwjB,GAAI,aACJ5E,MAAOqH,EAAcvB,GAASM,MAC9BwB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,OAEjD,cAAiB,CACfwjB,GAAI,gBACJ5E,MAAOqH,EAAcvB,GAASa,SAC9BiB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,QAEjD,YAAe,CACbwjB,GAAI,cACJ5E,MAAOqH,EAAcvB,GAASc,OAC9BgB,QAAS,CAACC,EAAQC,IACT7d,EAAM8d,YACXV,EAAcvB,GAASc,OACvBS,EAAcvB,GAASe,sBACtB,IACQzlB,KAAK+mB,sBAAsB,MAAON,EAAQC,GAAcvnB,MAAM6nB,IACnEhnB,KAAK6I,MAAMoe,eAAeR,EAAOS,WAC1BF,MAGX,MACA,EACA,OAIN,aAAgB,CACdxD,GAAI,eACJ5E,MAAOqH,EAAcvB,GAASU,cAC9BoB,QAAS,CAACC,EAAQC,IACT7d,EAAM8d,YACXV,EAAcvB,GAASU,cACvBa,EAAcvB,GAASW,uBACtB,KACC,MAAM8B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,GAAKC,EAIL,OAAOA,EAAMG,UAAS,GAAMvhB,OAAOkW,IAC7ByK,GACFA,EAAazK,EAAI9V,QAAS,UAL5BoH,QAAQkB,IAAI,oBAAqBgY,EAAOS,aAS5C,MACA,EACA,OAIN,cAAiB,CACf1D,GAAI,gBACJ5E,MAAOqH,EAAcvB,GAASiB,SAC9Ba,QAAS,CAACC,EAAQC,KAChB,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,GAAKC,EAIL,OAAOA,EAAMxB,SAAQ,GAAM5f,OAAOkW,IAC5ByK,GACFA,EAAazK,EAAI9V,QAAS,UAL5BoH,QAAQkB,IAAI,oBAAqBgY,EAAOS,aAU9C,cAAiB,CACf1D,GAAI,gBACJ5E,MAAOqH,EAAcvB,GAASkB,WAC9BY,QAAS,CAACC,EAAQC,KAChB,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,GAAKC,EAIL,OAAOA,EAAMxB,SAAQ,GAAO5f,OAAOkW,IAC7ByK,GACFA,EAAazK,EAAI9V,QAAS,UAL5BoH,QAAQkB,IAAI,oBAAqBgY,EAAOS,aAW9C,YAAe,CACb1D,GAAI,cACJ5E,MAAOqH,EAAcvB,GAASmB,kBAC9BW,QAAS,MAEX,cAAiB,CACfhD,GAAI,gBACJ5E,MAAOqH,EAAcvB,GAASgB,eAC9Bc,QAAS,CAACC,EAAQC,KAChB,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,GAAKC,GAAUV,EAAOc,KAItB,OAAOJ,EAAMK,gBAAgBf,EAAOc,MAAMxhB,OAAOkW,IAC3CyK,GACFA,EAAazK,EAAI9V,QAAS,UAL5BoH,QAAQkB,IAAI,6BAA+BgY,EAAOS,UAAY,OAAST,EAAOc,KAAO,OAU3F,YAAe,CACb/D,GAAI,cACJ5E,MAAOqH,EAAcvB,GAASM,MAC9BwB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,OAEjD,cAAiB,CACfwjB,GAAI,gBACJ5E,MAAOqH,EAAcvB,GAASO,QAC9BuB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,OAEjD,aAAgB,CACdwjB,GAAI,eACJ5E,MAAOqH,EAAcvB,GAASc,OAC9BgB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,QAEjD,eAAkB,CAChBwjB,GAAI,iBACJ5E,MAAOqH,EAAcvB,GAASa,SAC9BiB,QAASxmB,KAAK+mB,sBAAsBX,KAAKpmB,KAAM,SAKrDynB,oBACEC,SAAS5E,iBAAiB,YAAa9iB,KAAKmmB,iBAAiB,GAC7DuB,SAAS5E,iBAAiB,QAAS9iB,KAAKqmB,iBAAiB,GAG3DsB,uBACED,SAASE,oBAAoB,YAAa5nB,KAAKmmB,iBAAiB,GAChEuB,SAASE,oBAAoB,QAAS5nB,KAAKqmB,iBAAiB,GAG9DF,gBAAgBphB,GACV/E,KAAKgmB,QAAQ6B,QAAQ9M,SAAShW,EAAE+iB,UAGpC/iB,EAAEgjB,iBACFhjB,EAAEijB,kBACFhoB,KAAK6I,MAAMof,QAGb5B,gBAAgBthB,GACI,KAAdA,EAAEmjB,SACJloB,KAAK6I,MAAMof,OAIf3B,YAAYvhB,GACVA,EAAEgjB,iBACFhjB,EAAEijB,kBACFhoB,KAAK6I,MAAMof,OACX,IAAIE,EAAOnoB,KAAK6I,MAAMtF,MAAMwB,EAAEqjB,cAAcC,QAAQ7E,IACjC,iBAAR2E,IACTA,EAAOnoB,KAAKumB,UAAU4B,IAGnBA,EAGHnoB,KAAK6I,MAAMyf,SACTH,EAAK3E,GACL2E,EAAK3B,QAAQxmB,KAAK6I,MAAM4d,OAAQzmB,KAAK6I,MAAM0f,SAC3CvoB,KAAK6I,MAAM4d,QALblZ,QAAQ3O,MAAM,uBAAwBmG,EAAEqjB,cAAcC,QAAQ7E,IAWlEoD,eAAe5b,EAAKwd,EAAM/B,EAAQC,GAChC,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,IAAKC,EAEH,YADA5Z,QAAQkB,IAAI,oBAAqBgY,EAAOS,WAQ1C,IAAKlc,GAAOmc,EAAMsB,WAAWhC,EAAOiC,KAClC,OAOF,OAJgB1d,EACdmc,EAAMwB,eAAeH,GACrBrB,EAAMyB,gBAAgB,CAACnC,EAAOiC,KAAMF,IAEvBziB,OAAOkW,IAChByK,GACFA,EAAazK,EAAI9V,QAAS,UAMhC0gB,aAAaJ,EAAQC,GACnB,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAEhD,IAAKC,IAAUA,EAAM0B,aAAapC,EAAOiC,KACvC,OAEF,MAAMI,EAAM3B,EAAM4B,cAActC,EAAOrD,SAAS,GAChD,OAAO+D,EAAM6B,aAAaF,GAAK/iB,OAAOkW,IAChCyK,GACFA,EAAazK,EAAI9V,QAAS,UAMhC4gB,sBAAsBre,EAAM+d,EAAQC,GAClC,MAAMS,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,IAAKC,EAEH,YADA5Z,QAAQkB,IAAI,kBAAmBgY,EAAOS,WAIxC,IAAIxoB,EAASyoB,EAAM8B,WAAWxC,EAAOc,KAAM7e,GAM3C,OALIge,IACFhoB,EAASA,EAAOqH,OAAOkW,IACrByK,EAAazK,EAAI9V,QAAS,WAGvBzH,EAGTooB,eAAeL,EAAQC,GACrBD,EAAOyC,UAAUzC,EAAOiC,IAAKjC,EAAOrD,QAASqD,EAAO0C,SAAU1C,EAAO2C,SAAU1C,GAGjFzD,SACE,MAAMoG,EAAO,GACb,IAAIhmB,EAAQ,EACZrD,KAAK6I,MAAMtF,MAAM2H,KAAKid,IACD,iBAARA,IACTA,EAAOnoB,KAAKumB,UAAU4B,IAEpBA,GAAQA,EAAKvJ,OACfyK,EAAK5lB,KACW,KAAd0kB,EAAKvJ,MACH,wBAAIuE,UAAU,YAAY1e,IAAKpB,IAE/B,wBAAIigB,QAAStjB,KAAKsmB,YAAa,UAASjjB,EAAOoB,IAAKpB,GAAQ8kB,EAAKvJ,QAGvEvb,OAIF,MACMimB,EAAQnF,IAAY,GAAoB,IAAdkF,EAAKpmB,QAQ/BsmB,EAAW,CACfC,MARYxpB,KAAK6I,MAAM4gB,OAAOC,MAAQ1pB,KAAK6I,MAAM8gB,QAAQC,EAF7C,IAGT5pB,KAAK6I,MAAM8gB,QAAQC,EAAI5pB,KAAK6I,MAAM4gB,OAAOD,KAHhC,IAITxpB,KAAK6I,MAAM8gB,QAAQC,EAAI5pB,KAAK6I,MAAM4gB,OAAOD,MAM/B,KACbK,KANW7pB,KAAK6I,MAAM4gB,OAAOK,OAAS9pB,KAAK6I,MAAM8gB,QAAQI,EAAIT,EAC1DtpB,KAAK6I,MAAM8gB,QAAQI,EAAI/pB,KAAK6I,MAAM4gB,OAAOI,IAAMP,EAC/CtpB,KAAK6I,MAAM8gB,QAAQI,EAAI/pB,KAAK6I,MAAM4gB,OAAOI,KAIjC,MAGb,OACE,wBAAI1G,UAAU,OAAO6G,MAAOT,EAAUU,IAAKjqB,KAAKgmB,SAC7CqD,IAMT,UAAea,EAAAA,EAAAA,YAAWnE,IC9dpBoE,GAAe,CAAC,MAAS,oBAAqB,OAAU,QAAS,MAAS,iBAGjE,MAAMC,WAAsBllB,IAAAA,cACzC+d,SACE,IAAIoH,EAAS,KACb,OAAIrqB,KAAK6I,MAAMwhB,QAAUrqB,KAAK6I,MAAMwhB,OAAOpnB,OAAS,GAClDonB,EAAS,GACTrqB,KAAK6I,MAAMwhB,OAAOnf,KAAK5D,IACrB,MAAMgjB,EAAShjB,EAAEgjB,MAAQ,IAAMhjB,EAAEgjB,MAAQ,GACrChjB,EAAEijB,KACJF,EAAO5mB,KAAK,uBAAG0f,UAAW,0BAA4BmH,EAAO7lB,IAAK6C,EAAE7C,KAAO6C,EAAEijB,MAAOJ,GAAa7iB,EAAEijB,OAASjjB,EAAEijB,OAE9GF,EAAO5mB,KAAK,0BAAM0f,UAAW,QAAUmH,EAAO7lB,IAAK6C,EAAE7C,KAAO6C,EAAExF,MAAOwF,EAAExF,UAGpE,oCAAGuoB,IAEL,MCAJ,SAASG,GAAcC,EAASC,GACrC,IAAIC,EAAMlJ,KAAKC,MAAM+I,EAAU,IAC3BC,IACFC,EAAMA,EAAM,GAAN,WAAeA,GAAQA,GAE/B,IAAIC,EAAOH,EAAU,GAAM,EAE3B,OADAG,EAAMA,EAAM,GAAN,WAAeA,GAAQA,EAC7B,UAAUD,EAAV,YAAiBC,GAIZ,SAASC,GAAiBC,GAC/B,IAAKA,GAAkB,GAATA,EACZ,MAAO,UAGT,MAAMC,EAAQ,CAAC,QAAS,KAAM,KAAM,KAAM,KAAM,MAC1CC,EAASvJ,KAAKkJ,IAAwC,EAApClJ,KAAKC,MAAMD,KAAKwJ,KAAKH,GAAS,IAASC,EAAM9nB,OAAO,GACtEI,EAAQynB,EAAQrJ,KAAKyJ,IAAI,KAAMF,GAC/BG,EAAQH,EAAS,EAAK3nB,EAAQ,EAAI,EAAKA,EAAQ,GAAK,EAAI,EAAM,EACpE,OAAOA,EAAM+nB,QAAQD,GAAS,IAAMJ,EAAMC,GAIrC,SAASK,GAAgBC,EAAUC,GACxC,MAAuB,iBAAZD,EACFA,EAEFA,EAASroB,OAASsoB,EACvBD,EAASptB,MAAM,EAAGqtB,EAAU,EAAI,GAAK,IAAMD,EAASptB,MAAM,EAAIqtB,EAAU,GAAKD,EAyB1E,SAASE,GAAehI,EAAIiI,EAAOC,GACxC,OAAQD,EAAQ,MAAQ,QAAUC,EAAK,MAAQ,QAVfC,EAU0CnI,EATnE/B,KAAKmK,IAZd,SAA2BvsB,GACzB,IAAIwsB,EAAO,EACXxsB,EAAQ,GAAKA,EACb,IAAK,IAAImG,EAAI,EAAGA,EAAInG,EAAM4D,OAAQuC,IAChCqmB,GAASA,GAAQ,GAAKA,EAAQxsB,EAAMqG,WAAWF,GAC/CqmB,GAAcA,EAEhB,OAAOA,EAKSC,CAAkBH,IAAW,IADxC,IAA2BA,EC5D3B,SAASI,GAAc1oB,GAC5B,MAAM2oB,EAAUtE,SAASuE,eAAe,iBAClCC,EAAOxE,SAASwE,MAAQxE,SAASyE,qBAAqB,QAAQ,GAC9DC,EAAU1E,SAAS2E,cAAc,QACvCD,EAAQhkB,KAAO,YACfgkB,EAAQ5I,GAAK,gBACb4I,EAAQE,IAAM,gBACdF,EAAQG,KAAO,iBAAmBlpB,EAAQ,EAAI,IAAM,IAAM,OACtD2oB,GACFE,EAAKM,YAAYR,GAEnBE,EAAKO,YAAYL,GAEjB1E,SAAS9I,OAASvb,EAAQ,EAAI,IAAIA,EAAM,KAAO,IAAM,SAIhD,SAASqpB,GAAQ5Z,EAAI6Z,EAAUC,EAAeC,GACnD,IAAIC,EAAO,KAeX,IAdAha,EAAKA,GAAMA,EAAGia,UAIZD,EAAO,CACLha,GAAIA,IAIW,iBARnB+Z,EAAOA,GAAQA,EAAKE,UASlBD,EAAOA,GAAQ,GACfA,EAAKD,KAAOA,GAAc9J,KAAAA,UAGxB4J,EAAU,CACZG,EAAOA,GAAQ,GACf,IAAIE,EAAWJ,EAEf,MAAMK,EAAU,2CAA2CC,KAAKP,GAC5DM,GACFD,EAAWC,EAAQ,GACnBH,EAAKK,MAAQ,CACXtmB,KAAM8lB,EAASS,UAAUT,EAASU,QAAQ,KAAO,GACjDpD,IAAKlH,KAAAA,WAGP+J,EAAKK,MAAQ,CACXtmB,KAAMkc,KAAAA,SACNkH,IAAK0C,GAGTG,EAAKK,MAAM/kB,MAAQ4kB,GAAY,cAAcI,UAAU,SAASnqB,QAGlE,OAAO6pB,EAIF,SAASQ,GAAW/oB,EAAG+C,GAC5B,GAAI/C,IAAM+C,EACR,OAAO,EAGT,IAAKtJ,MAAMuvB,QAAQhpB,KAAOvG,MAAMuvB,QAAQjmB,GACtC,OAAO,EAIT,GAAI/C,EAAEtB,QAAUqE,EAAErE,OAChB,OAAO,EAGTsB,EAAEipB,OACFlmB,EAAEkmB,OACF,IAAK,IAAIhoB,EAAI,EAAGioB,EAAIlpB,EAAEtB,OAAQuC,EAAIioB,EAAGjoB,IACnC,GAAIjB,EAAEiB,KAAO8B,EAAE9B,GACb,OAAO,EAGX,OAAO,EAkCF,SAASkoB,GAAYC,EAAKC,GAC/B,GAAkB,iBAAPD,EACT,OAAOA,EAQT,GAJAA,EAAMA,EAAI5qB,QAAQ,kBAAmB,IAAIgqB,QAIpC,8BAA8B/Y,KAAK2Z,GACtC,OAAOA,EAIT,GAAI,aAAa3Z,KAAK2Z,GACpB,OAAOA,EAIT,MAAME,EAAU7vB,MAAMuvB,QAAQK,GAAkBA,EAAe/f,KAAK,KAAO,aAE3E,OADW,IAAIigB,OAAO,MAAQD,EAAU,SAAU,KAC1C7Z,KAAK2Z,GAINA,EAHE,KASJ,SAASI,GAAiBJ,GAC/B,IAAKA,EACH,OAAO,KAGT,MAAMK,EAAeN,GAAYC,GACjC,OAAIK,IAKA,mCAAmCha,KAAK2Z,EAAIZ,QACvCY,EAGF,MAIF,SAASM,GAAeC,GAC7B,OAAQA,GACN,KAAKnL,KAAAA,uBACH,MAAO,CAAEjhB,KAAM,eACjB,KAAKihB,KAAAA,sBACH,MAAO,CAAEjhB,KAAM,UAAWwoB,MAAO,gBACnC,KAAKvH,KAAAA,oBACH,MAAO,CAAEjhB,KAAM,QACjB,KAAKihB,KAAAA,wBACH,MAAO,CAAEjhB,KAAM,YACjB,KAAKihB,KAAAA,oBACH,MAAO,CAAEjhB,KAAM,WAAYwoB,MAAO,QAEtC,OAAO,KAMF,SAAS6D,GAAkBtoB,GAChC,IAAIuoB,GAAc,EAWlB,MAAO,CACLvoB,QAVqBA,aAAmBI,MACxC3H,QAAQE,OAAOqH,GACf,IAAIvH,SAAQ,CAACC,EAASC,KACpBqH,EAAQ1G,MACNT,GAAU0vB,EAAc5vB,EAAO,CAAC6vB,YAAY,IAAS9vB,EAAQG,KAC7DE,GAAuBJ,EAAd4vB,EAAqB,CAACC,YAAY,GAAgBzvB,QAM/D0vB,SACEF,GAAc,ICnML,MAAMG,WAAmBrpB,IAAAA,cACtC+d,SACE,IAAIuL,EACJ,IAA0B,IAAtBxuB,KAAK6I,MAAM2lB,OAAiB,CAC9B,MAAMC,EAAU1L,KAAAA,iBAAwB/iB,KAAK6I,MAAMse,OAC7CuH,EAAYlD,GAAexrB,KAAK6I,MAAMse,MAAOsH,GACnD,GAAIzuB,KAAK6I,MAAMse,OAASnnB,KAAK6I,MAAM+V,OAAS5e,KAAK6I,MAAM+V,MAAMmO,OAAQ,CACnE,MAAM4B,EAAS3uB,KAAK6I,MAAM+V,MAAMmO,OAAOtN,OAAO,GACxC0D,EAAY,cAAgBuL,GAAa1uB,KAAK6I,MAAM+lB,QAAU,YAAc,IAClFJ,EAAU,yBAAKrL,UAAWA,GAAW,6BAAMwL,QACtC,CACL,MAAMxL,EAAY,kBAAoBuL,GAAa1uB,KAAK6I,MAAM+lB,QAAU,YAAc,IACtFJ,EAASC,EAAU,uBAAGtL,UAAWA,GAAd,SAAqC,uBAAGA,UAAWA,GAAd,gBAErD,GAAInjB,KAAK6I,MAAM2lB,OAAQ,CAC5B,MAAMb,EAAM3tB,KAAK6I,MAAMue,OAAOyH,aAAad,GAAiB/tB,KAAK6I,MAAM2lB,SAEjErL,EAAY,UAAYnjB,KAAK6I,MAAM+lB,QAAU,WAAa,IAChEJ,EAAS,yBAAKrL,UAAWA,EAAW2L,IAAI,SAASC,IAAKpB,EACpDpF,QAAUxjB,IAAKA,EAAE+iB,OAAOnpB,QAAU,KAAMoG,EAAE+iB,OAAOiH,IAAI,kCAEvDP,EAAS,KAEX,OAAOA,GC3BI,MAAMQ,WAAoB9pB,IAAAA,cACvC+d,SACE,OAAQjjB,KAAK6I,MAAMxF,MAAQ,EACzB,0BAAM8f,UAAU,UAAUnjB,KAAK6I,MAAMxF,MAAQ,EAAI,KAAOrD,KAAK6I,MAAMxF,OACjE,MCHO,MAAM4rB,WAAkB/pB,IAAAA,cACrCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXH,IAAK,sBACL/E,MAAOnqB,OAAO2P,OAAO,CAAC2f,QAAS,OAAQnvB,KAAK6I,MAAMmhB,OAClD7G,UAAWnjB,KAAK6I,MAAMsa,UACtB2L,IAAK9uB,KAAK6I,MAAMimB,IAChBxL,QAAStjB,KAAK6I,MAAMya,SAIxBmE,oBAEEznB,KAAK6I,MAAMumB,SACRvpB,QACA1G,MAAK0H,GAAQ7G,KAAKqvB,SAAS,CAACN,IAAKloB,EAAKkoB,IAAK/E,MAAO,IAAIhqB,KAAKkvB,MAAMlF,MAAOmF,QAAS,OACjFppB,OAAM,IAAM/F,KAAKqvB,SAAS,CAACN,IAAK,2BAGrCpH,uBACE3nB,KAAK6I,MAAMumB,SAASd,SAGtBgB,mBAAmBC,GACbA,EAAUH,UAAYpvB,KAAK6I,MAAMumB,WACnCpvB,KAAKqvB,SAAS,CAACN,IAAK,sBAAuB/E,MAAO,IAAIhqB,KAAKkvB,MAAMlF,MAAOmF,QAAS,SACjFnvB,KAAK6I,MAAMumB,SACRvpB,QACA1G,MAAK0H,GAAQ7G,KAAKqvB,SAAS,CAACN,IAAKloB,EAAKkoB,IAAK/E,MAAO,IAAIhqB,KAAKkvB,MAAMlF,MAAOmF,QAAS,OACjFppB,OAAM,IAAM/F,KAAKqvB,SAAS,CAACN,IAAK,4BAIvC9L,SACE,OAAO/d,IAAAA,cAAoB,MAAOlF,KAAKkvB,QCpC5B,MAAMM,WAAqBtqB,IAAAA,cACxC+d,SACE,OACE,yBAAKE,UAAU,YACb,6BAAK,0BAAM6G,MAAO,CAACyF,MAA8B,IAAtBzvB,KAAK6I,MAAM6mB,SAAkB,QACvD1vB,KAAK6I,MAAM6mB,SAAW,KACrB,uBAAGnD,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM8mB,aAC1D,uBAAGxM,UAAU,kBAAb,SADF,IAC0C,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAC1DC,eAAc,6BAGlB,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,oCCTjD,MAAMmM,WAAuB1qB,IAAAA,cAC1CU,YAAYiD,GACVxC,MAAMwC,GAGRoa,SACE,OACE,yBAAKE,UAAU,gBACZje,IAAAA,cAAoB,MAAOlF,KAAK6I,OACjC,yBAAKsa,UAAU,qBACb,kBAACqM,GAAD,CAAcE,SAAU1vB,KAAK6I,MAAM6mB,SAAUC,SAAU3vB,KAAK6I,MAAMgnB,oBCbrE,MAAMC,GAA0B,CAAC,aAAc,YAAa,YAAa,YAAa,iBAChFC,GAA0B,CAAC,MAAc,MAAa,MAAa,MAAa,OAGtF,SAASC,GAAa7C,GAC3B,GAAIA,GAAyB,iBAATA,EAAmB,CACrC,GAAIA,EAAMlD,IACR,OAAOkD,EAAMlD,IAEf,GAAIkD,EAAMtmB,MAAQsmB,EAAM/kB,KAAM,CAE5B,MAAO,SADM+kB,EAAM/kB,KAAK6nB,WAAW,UAAY9C,EAAM/kB,KAAQ,SAAW+kB,EAAM/kB,MACtD,WAAa+kB,EAAMtmB,MAG/C,OAAO,KAMF,SAASqpB,GAAaT,EAAOU,EAAQC,EAAUC,EAAWC,GAO/D,GAJAH,GAAkB,EAClBC,GAAsB,EACtBC,GAAwB,GAHxBZ,GAAgB,IAKH,GAAKU,GAAU,GAAKC,GAAY,GAAKC,GAAa,EAC7D,OAAO,KAGLC,IACFF,EAAWC,EAAY5O,KAAKkJ,IAAIyF,EAAUC,IAG5C,MAAME,EAAQ9O,KAAKkJ,IACjBlJ,KAAKkJ,IAAI8E,EAAOW,GAAYX,EAC5BhO,KAAKkJ,IAAIwF,EAAQE,GAAaF,GAG1Brb,EAAO,CACX0b,SAAWf,EAAQc,EAAS,EAC5BE,UAAYN,EAASI,EAAS,GAchC,OAXID,GAEFxb,EAAK0b,SAAW1b,EAAK2b,UAAYhP,KAAKkJ,IAAI7V,EAAK0b,SAAU1b,EAAK2b,WAC9D3b,EAAK4b,SAAW5b,EAAK6b,UAAYlP,KAAKkJ,IAAI8E,EAAOU,GACjDrb,EAAK8b,SAAYnB,EAAQ3a,EAAK4b,UAAY,EAAK,EAC/C5b,EAAK+b,SAAYV,EAASrb,EAAK4b,UAAY,EAAK,IAEhD5b,EAAK8b,QAAU9b,EAAK+b,QAAU,EAC9B/b,EAAK4b,SAAWjB,EAChB3a,EAAK6b,UAAYR,GAEZrb,EAIF,SAASgc,GAAgBC,EAAOC,GACrC,MAAMC,EAAMnB,GAAwBzC,QAAQ2D,GAC5C,GAAIC,EAAM,IAAMF,EAEd,OAAOA,EAET,MAAMG,EAAMnB,GAAgBkB,GAEtBE,EAAKJ,EAAMK,YAAY,KAI7B,OAHID,GAAM,IACRJ,EAAQA,EAAM3D,UAAU,EAAG+D,IAEtBJ,EAAQ,IAAMG,EAKhB,SAASG,GAAYC,EAAYlB,EAAUC,EAAWkB,EAASjB,GACpE,OAAO,IAAIhyB,SAAQ,CAACC,EAASC,KAC3B,MAAMgzB,EAAM,IAAIC,MAChBD,EAAIE,YAAc,YAClBF,EAAI7yB,QAAU,SAASsd,GACrBzd,EAAO,IAAIyH,MAAM,+BAEnBurB,EAAIG,OAAS/mB,iBAEXgnB,IAAIC,gBAAgBL,EAAIzC,KAGxB,MAAM+C,EAAM5B,GAAasB,EAAI/B,MAAO+B,EAAIrB,OAAQC,EAAUC,EAAWC,GACrE,IAAKwB,EAEH,YADAtzB,EAAO,IAAIyH,MAAM,kBAGnB,IAAI8rB,EAASrK,SAAS2E,cAAc,UACpC0F,EAAOtC,MAAQqC,EAAItB,SACnBuB,EAAO5B,OAAS2B,EAAIrB,UACpB,IAAIuB,EAAMD,EAAOE,WAAW,MAC5BD,EAAIE,uBAAwB,EAC5BF,EAAIG,UAAUX,EAAKM,EAAIlB,QAASkB,EAAIjB,QAASiB,EAAIpB,SAAUoB,EAAInB,UAC7D,EAAG,EAAGmB,EAAItB,SAAUsB,EAAIrB,WAE1B,MAAMO,EAAOlB,GAAwBnoB,SAAS2pB,EAAWlpB,MAAQkpB,EAAWlpB,KAAO,aAEnF,IAAIgqB,QAAa,IAAI9zB,SAAQC,GAAWwzB,EAAOM,OAAO9zB,EAASyyB,KAC/D,GAAKoB,EAAL,CAMA,KAAOb,EAAU,GAAKa,EAAKnvB,OAASsuB,GAClCO,EAAItB,SAA2B,aAAfsB,EAAItB,SAA4B,EAChDsB,EAAIrB,UAA6B,aAAhBqB,EAAIrB,UAA6B,EAClDsB,EAAOtC,MAAQqC,EAAItB,SACnBuB,EAAO5B,OAAS2B,EAAIrB,UACpBuB,EAAMD,EAAOE,WAAW,MACxBD,EAAIM,UAAU,EAAG,EAAGP,EAAOtC,MAAOsC,EAAO5B,QACzC6B,EAAIG,UAAUX,EAAKM,EAAIlB,QAASkB,EAAIjB,QAASiB,EAAIpB,SAAUoB,EAAInB,UAC7D,EAAG,EAAGmB,EAAItB,SAAUsB,EAAIrB,WAC1B2B,QAAa,IAAI9zB,SAAQC,GAAWwzB,EAAOM,OAAO9zB,EAASyyB,KAG7De,EAAS,KACTxzB,EAAQ,CAACyyB,KAAMA,EAAMoB,KAAMA,EAAM3C,MAAOqC,EAAItB,SAAUL,OAAQ2B,EAAIrB,UAAW3uB,KAAMgvB,GAAgBQ,EAAWxvB,KAAMkvB,UAlBlHxyB,EAAO,IAAIyH,MAAM,8BAoBrBurB,EAAIzC,IAAM6C,IAAIW,gBAAgBjB,MAwD3B,SAASkB,GAAaJ,GAC3B,OAAO,IAAI9zB,SAAQ,CAACC,EAASC,KAC3B,MAAMi0B,EAAS,IAAIC,WACnBD,EAAO9zB,QAAWg0B,IAChBn0B,EAAOi0B,EAAO7zB,QAEhB6zB,EAAOd,OAAS,KACdpzB,EAAQ,CAACyyB,KAAMoB,EAAKhqB,KAAMwqB,KAAMH,EAAO/zB,OAAOm0B,MAAM,KAAK,MAE3DJ,EAAOK,cAAcV,MCrLzB,MAAM1N,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BoO,YAAa,CACXvP,GAAI,cACJC,eAAgB,CAAF,yBAGhBuP,kBAAmB,CACjBxP,GAAI,oBACJC,eAAgB,CAAF,8BAGhBwP,aAAc,CACZzP,GAAI,eACJC,eAAgB,CAAF,6BAMlB,SAASyP,GAAgBC,EAAItsB,EAAMusB,GACjC,IAAKvsB,EAMH,OALAusB,EAAKrE,IAAM,uBACXqE,EAAKpJ,MAAQ,CACXyF,MAAOlL,OACP4L,OAAQ5L,QAEH4O,EAGTC,EAAKjQ,UAAY,eACjB,MAAM2O,EAAM5B,GAAarpB,EAAK4oB,MAAO5oB,EAAKspB,OACxCnwB,KAAKqzB,cAAgB,EAAI5R,KAAKkJ,IAAI3qB,KAAKqzB,cAAgBlP,KAAgBA,OACrEA,MAAiBA,KAAe,IAChC,CAACqM,SXS0B,GWTGC,UXSH,IWiB/B,OAzBA2C,EAAKpJ,MAAQ,CACXyF,MAAOqC,EAAItB,SAAW,KACtBL,OAAQ2B,EAAIrB,UAAY,KAExB6C,SAAUxB,EAAItB,SAAW,KACzB+C,UAAWzB,EAAIrB,UAAY,MAExB+C,GAAAA,OAAAA,aAAoB3sB,GAevBssB,EAAKvD,IAdLwD,EAAKrE,IAAM/uB,KAAK6uB,aAAad,GAAiBqF,EAAKrE,MACnDqE,EAAKtE,IAAMjoB,EAAK/E,KACZsxB,EAAKrE,KACHtN,KAAKgS,IAAI5sB,EAAK4oB,OAAS,EAAG5oB,EAAKspB,QAAU,GAAK5L,KAEhD6O,EAAK9P,QAAUtjB,KAAK0zB,eACpBN,EAAKjQ,WAAa,oBAEpBiQ,EAAKO,QAAU,QAEfP,EAAKrE,IAAM,wBAORoE,EAsDF,SAASS,GAAc5J,EAAOnjB,EAAMkE,EAAQtG,EAAKovB,GACtD,GAAIA,EAAMlsB,SAAS,MACjB,OAAOmsB,GAAe31B,KAAK6B,KAAMgqB,EAAOnjB,EAAMkE,EAAQtG,GAGxD,IAAKulB,EAEH,OAAOjf,EAGT,IAAIooB,EAAKK,GAAAA,OAAAA,QAAexJ,GACxB,MAAMoJ,EAAOI,GAAAA,OAAAA,UAAiBxJ,EAAOnjB,IAAS,GAE9C,OADAusB,EAAK3uB,IAAMA,EACHulB,GACN,IAAK,KACHjf,EAAS,KACT,MACF,IAAK,KAEHqoB,EAAKjQ,UAAY,YACjB,MACF,IAAK,KACHgQ,EAAK,KACLpoB,EAAS,KACT,MACF,IAAK,KAEHooB,EAAKD,GAAgB/0B,KAAK6B,KAAMmzB,EAAItsB,EAAMusB,GAE1CroB,EAAS,KACT,MACF,IAAK,KAEHqoB,EAAK9P,QAAUtjB,KAAK+zB,kBACpB,IAAIC,EAAQ9uB,IAAAA,SAAAA,IAAmB6F,GAASkpB,GACf,iBAATA,EAAoBA,OAAQvwB,IAEvCswB,GAAyB,GAAhBA,EAAM/wB,SAClB+wB,EAAQ,CAACZ,EAAKtxB,OAGhBsxB,EAAK,cAAgBY,EAAMnmB,KAAK,IAChC,MACF,IAAK,KAEHulB,EAAKjQ,UAAY,UACbtc,IACFusB,EAAKjQ,WAAa,IAAMqI,GAAe3kB,EAAK3G,KAAK,GAAO,IAE1D,MACF,IAAK,KAEHkzB,EAAKjQ,UAAY,WACjB,MACF,IAAK,KAEH,MACF,IAAK,KAEHiQ,EAAKjQ,UAAY,cACjBiQ,EAAK9P,QAAUtjB,KAAKk0B,aACpB,MACF,QACY,SAANf,IAEFA,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGoY,UAAU,uBAAb,aAAkD,KAAKgR,OAAOppB,GAAU,KAIxF,OAAKooB,EAGEjuB,IAAAA,cAAoBiuB,EAAIC,EAAMroB,GAF5BA,EASJ,SAASqpB,GAAiBpK,EAAOnjB,EAAMkE,EAAQtG,GACpD,IAAKulB,EAEH,OAAOjf,EAGT,IAAIooB,EAAKK,GAAAA,OAAAA,QAAexJ,GACxB,MAAMoJ,EAAO,CAAE3uB,IAAKA,GACpB,OAAQulB,GACN,IAAK,KAEHmJ,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,KACV,MACF,IAAK,KAEHqoB,EAAKjQ,UAAY,oBACjB,MACF,IAAK,KACL,IAAK,KAEHgQ,EAAK,OACL,MACF,IAAK,KAEHA,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGtG,IAAI,KAAK0e,UAAU,kBAAtB,SAAkDnjB,KAAKimB,cAAcvB,GAASuO,eACxF,MACF,IAAK,KACHE,EAAK,OACLC,EAAKjQ,UAAY,mBACjB,MACF,IAAK,KACHgQ,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGtG,IAAI,KAAK0e,UAAU,kBAAtB,aACRnjB,KAAKimB,cAAcvB,GAASqO,cAAcoB,OAAO,IAAKppB,GAAU,IAClE,MACF,IAAK,KACHooB,EAAKjuB,IAAAA,SACL,MACF,IAAK,KACH,GAAI2B,EAAM,CACR,GAAiB,oBAAbA,EAAKmqB,KAEP,OAAO,YAGFnqB,EAAK3G,WACL2G,EAAKojB,IAEdkJ,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGtG,IAAI,KAAK0e,UAAU,kBAAtB,cAAuDnjB,KAAKimB,cAAcvB,GAASsO,oBAC7F,MACF,IAAK,KACL,IAAK,KACHG,EAAK,KACLpoB,EAAS,KACT,MACF,QACY,SAANooB,IACFA,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGtG,IAAI,OAAO0e,UAAU,kBAAxB,aAAwD,KAAKgR,OAAOppB,GAAU,KAI9F,OAAKooB,EAGEjuB,IAAAA,cAAoBiuB,EAAIC,EAAMroB,GAF5BA,EAMX,SAASspB,GAAgBjB,EAAMvsB,GAa7B,OAZAusB,EAAKpJ,MAAQ,CACXyF,MAAOlL,OACP4L,OAAQ5L,OACR6L,SAAU7L,OACV8L,UAAW9L,QAEb6O,EAAKjQ,UAAY,eACjBiQ,EAAKtE,IAAM9uB,KAAKimB,cAAcvB,GAASuO,cAClCpsB,IACHusB,EAAKrE,IAAM,wBAEbqE,EAAKxU,MAAQwU,EAAKtE,IACXsE,EAST,SAASU,GAAe9J,EAAOnjB,EAAMkE,EAAQtG,GAC3C,GAAI,CAAC,KAAM,KAAM,KAAM,MAAMkD,SAASqiB,GAAQ,CAC5C,IAAImJ,EAAKK,GAAAA,OAAAA,QAAexJ,GACpBoJ,EAAOI,GAAAA,OAAAA,UAAiBxJ,EAAOnjB,IAAS,GAE5C,OADAusB,EAAK3uB,IAAMA,EACJulB,GACL,IAAK,KACHjf,EAAS,KACT,MACF,IAAK,KACHqoB,EAAOiB,GAAgBl2B,KAAK6B,KAAMozB,EAAMvsB,GACxCkE,EAAS,CAAC7F,IAAAA,cAAoB,MAAOkuB,EAAM,MAAO,IAAKA,EAAKtE,KAC5DqE,EAAKjuB,IAAAA,SAELkuB,EAAO,CAAC3uB,IAAKA,GACb,MACF,IAAK,KACH0uB,EAAK,OACLC,EAAKjQ,UAAY,UACbtc,IACFusB,EAAKjQ,WAAa,IAAMqI,GAAe3kB,EAAK3G,KAAK,GAAO,IAE1D,MACF,IAAK,KACH,IAAI6wB,EACJ,GAAIlqB,EAAM,CACR,GAAiB,oBAAbA,EAAKmqB,KAEP,OAAO,KAETD,EAAQlqB,EAAK/E,YAEN+E,EAAK3G,WACL2G,EAAKojB,IAEdkJ,EAAKjuB,IAAAA,SACL6F,EAAS,CAAC,uBAAGtG,IAAI,KAAK0e,UAAU,kBAAtB,cACRkI,GAAgB0F,EAAO,KAAO/wB,KAAKimB,cAAcvB,GAASsO,oBAGhE,OAAO9tB,IAAAA,cAAoBiuB,EAAIC,EAAMroB,GAEvC,OAAOqpB,GAAiBj2B,KAAK6B,KAAMgqB,EAAOnjB,EAAMkE,EAAQtG,GAI1D,SAAS6vB,GAAWztB,GAClB,IAAIhB,EAEJ,GAAIgB,EAAK3G,IAAK,CACZ,MAAMkyB,EDzFH,SAAsB9sB,EAAK0rB,GAChC,IAAK1rB,EACH,OAAO,KAGT,IAEE,MAAMivB,EAAMra,KAAK5U,GACXrC,EAASsxB,EAAItxB,OACbuxB,EAAM,IAAIC,YAAYxxB,GACtBlF,EAAM,IAAIsV,WAAWmhB,GAC3B,IAAK,IAAIhvB,EAAI,EAAGA,EAAIvC,EAAQuC,IAC1BzH,EAAIyH,GAAK+uB,EAAI7uB,WAAWF,GAG1B,OAAO,IAAIkvB,KAAK,CAACF,GAAM,CAAEpsB,KAAM4oB,IAC/B,MAAM/U,GACN1O,QAAQ3O,MAAM,qCAAsCqd,GAGtD,OAAO,KCqEQ0Y,CAAa9tB,EAAK3G,IAAK2G,EAAKmqB,MACzC,IAAKoB,EACH,MAAM,IAAInsB,MAAM,iBAElBJ,EAAUvH,QAAQC,QAAQ6zB,OACrB,KAAIvrB,EAAKojB,IAUd,MAAM,IAAIhkB,MAAM,sBAThBJ,EAAUiR,MAAM9W,KAAK6uB,aAAad,GAAiBlnB,EAAKojB,OACrD9qB,MAAKwzB,IACJ,GAAIA,EAAI5b,GACN,OAAO4b,EAAIP,OAEX,MAAM,IAAInsB,MAAJ,oCAAuC0sB,EAAIzgB,OAA3C,YAAqDygB,EAAIiC,gBAQvE,OAAO/uB,EACJ1G,MAAKizB,GAEGf,GAAYe,EAAM7N,GAAqBA,IAAsB,GAAG,KACtEplB,MAAK01B,IACNhuB,EAAKmqB,KAAO6D,EAAO7D,KACnBnqB,EAAKiO,KAAO+f,EAAOzC,KAAKtd,KACxBjO,EAAK4oB,MAAQoF,EAAOpF,MACpB5oB,EAAKspB,OAAS0E,EAAO1E,cACdtpB,EAAKojB,IAGZpjB,EAAKkoB,IAAM6C,IAAIW,gBAAgBsC,EAAOzC,MAC/BI,GAAaqC,EAAOzC,SAC1BjzB,MAAK21B,IACNjuB,EAAK3G,IAAM40B,EAAIlC,KACR/rB,KACNd,OAAMkW,IAMP,aALOpV,EAAK3G,WACL2G,EAAKkoB,IACZloB,EAAK4oB,MAAQlL,GACb1d,EAAKspB,OAAS5L,GAERtI,KAKL,SAAS8Y,GAAe/K,EAAOnjB,EAAMkE,EAAQtG,EAAKovB,GACvD,GAAa,MAAT7J,EAAe,CACjB,MAAMoJ,EAAOiB,GAAgBl2B,KAAK6B,KAAM,CAACyE,IAAKA,GAAMoC,GACpD,IAAImuB,EACJ,IACEA,EAAgB7G,GAAkBmG,GAAWn2B,KAAK6B,KAAM6G,IACxD,MAAOjI,GACPo2B,EAAgB7G,GAAkBvvB,GAIpC,OAFAw0B,EAAKhE,SAAW4F,EAChBjqB,EAAS,CAAC7F,IAAAA,cAAoB+pB,GAAWmE,EAAM,MAAO,IAAKA,EAAKtE,KACzD5pB,IAAAA,cAAoBA,IAAAA,SAAgB,CAACT,IAAKA,GAAMsG,GAClD,GAAa,MAATif,EAAe,CACxB,GAAI6J,EAAMlsB,SAAS,MAEjB,OAAOzC,IAAAA,cAAoB,OAAQ,CAACT,IAAKA,GACvC,CAAC,uBAAGA,IAAI,KAAK0e,UAAU,kBAAtB,gBAAyD,MAG9D,MAAMiQ,EAAOI,GAAAA,OAAAA,UAAiB,KAAM3sB,IAAS,GAG7C,OAFAusB,EAAK3uB,IAAMA,EACX2uB,EAAKjQ,UAAY,cACVje,IAAAA,cAAoBsuB,GAAAA,OAAAA,QAAe,MAAOJ,EAAMroB,GAEzD,OAAO+oB,GAAe31B,KAAK6B,KAAMgqB,EAAOnjB,EAAMkE,EAAQtG,GCxZxD,MAAMwwB,WAAgB/vB,IAAAA,UACpBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKsmB,YAActmB,KAAKsmB,YAAYF,KAAKpmB,MACzCA,KAAKk1B,mBAAqBl1B,KAAKk1B,mBAAmB9O,KAAKpmB,MAGzDsmB,YAAYvhB,GACVA,EAAEgjB,iBACFhjB,EAAEijB,kBACEhoB,KAAK6I,MAAMssB,YAEbn1B,KAAK6I,MAAMssB,WAAWn1B,KAAK6I,MAAMsf,KAAMnoB,KAAK6I,MAAMpI,OAItDy0B,mBAAmBnwB,GACjBA,EAAEgjB,iBACFhjB,EAAEijB,kBACFhoB,KAAK6I,MAAMusB,gBAAgB,CAAElO,UAAWlnB,KAAK6I,MAAMsf,KAAM4B,EAAGhlB,EAAEswB,MAAOzL,EAAG7kB,EAAEuwB,QAG5ErS,SACE,IAAIrE,EAAQ5e,KAAK6I,MAAM+V,MAClBA,EAGMA,EAAM3b,OAAS,KAExB2b,EAAQA,EAAMwO,UAAU,EAAG,IAAM,KAJjCxO,EAAQ,2BAAG,kBAAC,EAAA2E,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,8BAMhE,MAAM8R,EAASv1B,KAAK6I,MAAMuE,IAAM,SAAW,UACrCohB,GAASxuB,KAAK6I,MAAM2lB,QAASxuB,KAAK6I,MAAM2lB,OACxCnE,EAASrqB,KAAK6I,MAAMwhB,OAASrqB,KAAK6I,MAAMwhB,OAAOnsB,QAAU,GACzDs3B,EAAc,GAuBpB,IAAIC,EAtBAz1B,KAAK6I,MAAM6sB,YACbF,EAAY/xB,KAAK,CAAC8mB,KAAM,WAAYD,MAAO,mBAEzCtqB,KAAK6I,MAAM8sB,SACbH,EAAY/xB,KAAK,CAAC8mB,KAAM,QAASD,MAAO,gBAEtCtqB,KAAK6I,MAAM+sB,aACbJ,EAAY/xB,KAAK,CAAC8mB,KAAM,YAAaD,MAAO,iBAE1CtqB,KAAK6I,MAAMgtB,MACT71B,KAAK6I,MAAMitB,UACbzL,EAAO5mB,KAAK,CAAC3B,KAAM9B,KAAK6I,MAAMgtB,IAAIE,UAAWtxB,IAAK,SAEhDzE,KAAK6I,MAAMgtB,IAAIG,WACjBR,EAAY/xB,KAAK,CAAC8mB,KAAM,UAErBvqB,KAAK6I,MAAMgtB,IAAII,YAClBT,EAAY/xB,KAAK,CAAC8mB,KAAM,YAMK,iBAAtBvqB,KAAK6I,MAAM4sB,QACpBA,EAAUz1B,KAAK6I,MAAM4sB,QACZjC,GAAAA,OAAAA,QAAexzB,KAAK6I,MAAM4sB,SACnCA,EAAUvwB,IAAAA,cAAoBA,IAAAA,SAAgB,KAAMsuB,GAAAA,OAAAA,OAAcxzB,KAAK6I,MAAM4sB,QAASrB,GACpF,CAACnO,cAAejmB,KAAK6I,MAAMqd,KAAKD,iBACzBjmB,KAAK6I,MAAM4sB,UACpBA,EACE,oCAAE,uBAAGtS,UAAU,uBAAb,iBAAF,IAAuD,uBAAGA,UAAU,QAClE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClBC,eAAc,wCAItB,MAAM8G,EAAO0D,GAAejuB,KAAK6I,MAAMqlB,UACjCgI,EAAS3L,EAAO,uBAAGpH,UAAW,oCACjCoH,EAAKD,MAAQ,IAAMC,EAAKD,MAAQ,KAAMC,EAAKzoB,MAAY,KACpDq0B,EAAa,iBAAmBn2B,KAAK6I,MAAM+lB,QAAU,WAAa,IAExE,OACE,wBAAIzL,WAAYnjB,KAAK6I,MAAMutB,eAAiBp2B,KAAK6I,MAAMwtB,SAAW,WAAa,KAAM/S,QAAStjB,KAAKsmB,aACjG,yBAAKnD,UAAU,cACb,kBAACoL,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQA,EACR5P,MAAO5e,KAAK6I,MAAM+V,MAClBuI,MAAOnnB,KAAK6I,MAAMsf,KAClByG,QAAS5uB,KAAK6I,MAAM+lB,UACrB5uB,KAAK6I,MAAM+lB,QAAU,uBAAGzL,UAAU,0BAAb,UACpBnjB,KAAK6I,MAAMytB,WAAa,0BAAMnT,UAAWoS,IACxCv1B,KAAK6I,MAAMutB,eAAiBp2B,KAAK6I,MAAMwtB,SACxC,uBAAGlT,UAAU,4BAAb,gBACE,MAEN,yBAAKA,UAAU,YACb,6BAAK,0BAAMA,UAAWgT,GAAavX,GAChC5e,KAAK6I,MAAM0tB,UAAY,yBAAKxH,IAAI,mBAAmB5L,UAAU,UAAU2L,IAAI,YAAe,KAC3F,kBAAC1E,GAAD,CAAeC,OAAQmL,IACrBx1B,KAAK6I,MAAM+lB,QAAsD,KAA5C,kBAACI,GAAD,CAAa3rB,MAAOrD,KAAK6I,MAAM2tB,UAEvDx2B,KAAK6I,MAAMitB,SACV,8BAAM,kBAAC1L,GAAD,CAAeC,OAAQA,KAC7BrqB,KAAK6I,MAAM4tB,MAAQ,KAAO,yBAAKtT,UAAU,mBAAmB+S,EAAO,8BAAOT,GAAWz1B,KAAK6I,MAAM6tB,SAAW,OAE9G12B,KAAK6I,MAAMusB,gBACV,0BAAMjS,UAAU,eACd,uBAAGoJ,KAAK,IAAIjJ,QAAStjB,KAAKk1B,oBACxB,uBAAG/R,UAAU,kBAAb,iBAEM,OAMpB,UAAe+G,EAAAA,EAAAA,YAAW+K,IC1H1B,MAAM0B,WAAsBzxB,IAAAA,cAC1BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKsmB,YAActmB,KAAKsmB,YAAYF,KAAKpmB,MAG3CsmB,YAAYvhB,GACVA,EAAEgjB,iBACFhjB,EAAEijB,kBACFhoB,KAAK6I,MAAMyf,SAAStoB,KAAK6I,MAAM+tB,QAGjC3T,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KACnC,OACE,wBAAI5C,QAAStjB,KAAKsmB,YAAanD,UAAU,UACvC,yBAAKA,UAAU,eAAe8C,EAAcjmB,KAAK6I,MAAM+V,MAAO5e,KAAK6I,MAAMkC,WAMjF,UAAemf,EAAAA,EAAAA,YAAWyM,ICZpBjS,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BkS,UAAW,CACTrT,GAAI,YACJC,eAAgB,CAAF,uBAGhBqT,YAAa,CACXtT,GAAI,cACJC,eAAgB,CAAF,2BAKlB,MAAMsT,WAAoB7xB,IAAAA,UACxB+d,SACE,MAAM,cAAEgD,GAAkBjmB,KAAK6I,MAAMqd,KAC/BkQ,EAAgBp4B,MAAMuvB,QAAQvtB,KAAK6I,MAAMmuB,eACzCC,EAAe,GACrB,IAAIC,EAAgB,EA8FpB,OA7FIl3B,KAAK6I,MAAMsuB,UAAYn3B,KAAK6I,MAAMsuB,SAASl0B,OAAS,GACtDjD,KAAK6I,MAAMsuB,SAASjsB,KAAKzF,IACvB,GAAIA,EAAEmxB,OAEJK,EAAaxzB,KACX,kBAAC,GAAD,CACEmb,MAAOnZ,EAAEmZ,MAAOgY,OAAQnxB,EAAEmxB,OAAQ7rB,OAAQtF,EAAEsF,OAC5CtG,IAAKgB,EAAEmxB,OACPtO,SAAUtoB,KAAK6I,MAAMyf,gBAEpB,CAEL,MAAM7jB,EAAMzE,KAAK6I,MAAMitB,SAAWrwB,EAAE8hB,KAAQ9hB,EAAE0hB,OAAS1hB,EAAE8hB,KAGzD,GAAIvnB,KAAK6I,MAAMuuB,YAAcp3B,KAAK6I,MAAMoC,OAAQ,CAC9C,MAAMosB,EAAW,CAAC5yB,GAOlB,GANIgB,EAAE6xB,SAAW7xB,EAAE6xB,QAAQZ,SACzBW,EAAS5zB,MAAM,GAAKgC,EAAE6xB,QAAQZ,SAASa,eAErC9xB,EAAE+xB,QAAU/xB,EAAE+xB,OAAO1kB,IACvBukB,EAAS5zB,MAAM,GAAKgC,EAAE+xB,OAAO1kB,IAAIykB,gBAE9Bv3B,KAAK6I,MAAMuuB,WAAWp3B,KAAK6I,MAAMoC,OAAQosB,GAC5C,OAIJ,MAAMd,EAAYxT,KAAAA,mBAA0Bte,GACtC4xB,EAAWD,EACdp2B,KAAK6I,MAAMmuB,cAAc3J,QAAQ5oB,IAAQ,EACzCzE,KAAK6I,MAAMmuB,gBAAkBvyB,EAC1B4lB,EAAS,GACXrqB,KAAK6I,MAAMitB,WACTrxB,GAAOzE,KAAK6I,MAAM4uB,UACpBpN,EAAO5mB,KAAK,CAAC3B,KAAMmkB,EAAcvB,GAASmS,WAAYvM,MAAO,UAE3D7kB,EAAEowB,KAAOpwB,EAAEowB,IAAI6B,WACjBrN,EAAO5mB,KAAK,CAAC3B,KAAMmkB,EAAcvB,GAASoS,aAAcxM,MAAO,UAInE,MAAMoM,EAAU14B,MAAMuvB,QAAQ9nB,EAAE6xB,SAC9B7xB,EAAE6xB,QAAQzpB,KAAK,KAAQpI,EAAE6xB,QAAU7xB,EAAE6xB,QAAQZ,QAAU,KACzD,IAAIjB,EACAkC,EACAC,EACJ,IAAK53B,KAAK6I,MAAMitB,UAAYrwB,EAAEoyB,cAAe,CAC3C,MAAM/O,EAAMrjB,EAAEoyB,eAAc,GACxB/O,IACF6O,EAAY7O,EAAIoD,KAAOpD,EAAIoD,KAAKyL,UAAY,KAC5CC,EAAiB9O,EAAIgP,SAAWryB,EAAEsyB,UAAUjP,GAAK,GAC7CA,EAAI1F,UACNqS,EAAgC,iBAAf3M,EAAI1F,QACnB0F,EAAI1F,QAAQtP,OAAO,EdKC,IcJpB0f,GAAAA,OAAAA,QAAe1K,EAAI1F,QdIC,McC5B6T,EAAaxzB,KACX,kBAAC,GAAD,CACE2jB,OAAQpnB,KAAK6I,MAAMue,OACnBxI,MAAOnZ,EAAE+xB,OAAS/xB,EAAE+xB,OAAO1kB,GAAK,KAChC0b,OAAQwB,GAAavqB,EAAE+xB,OAAS/xB,EAAE+xB,OAAOrK,MAAQ,MACjDuJ,QAASA,EACTjB,QAASA,EACTkC,UAAWA,EACXzJ,SAAU0J,EACVpB,OAAQx2B,KAAK6I,MAAMmvB,WAAavyB,EAAE+wB,OAAS,EAC3CppB,IAAK3H,EAAE8vB,QAAUv1B,KAAK6I,MAAMovB,UAC5BpC,IAAKpwB,EAAEowB,IACPC,SAAU91B,KAAK6I,MAAMitB,SACrBzL,OAAQA,EACR+L,cAAeA,EACfC,SAAUA,EACVC,WAAYt2B,KAAK6I,MAAMytB,aAAeC,EACtCA,UAAWA,EACXpB,WAAYn1B,KAAK6I,MAAMqvB,gBACvB9C,gBAAiBp1B,KAAK6I,MAAMusB,gBAC5BM,WAAYjwB,EAAE0yB,SAAW1yB,EAAE0yB,QAAQC,SACnCzC,QAASlwB,EAAE0yB,SAAW1yB,EAAE0yB,QAAQE,MAChCzC,YAAanwB,EAAE0yB,SAAW1yB,EAAE0yB,QAAQG,OACpC1J,QAASnpB,EAAE8yB,SACXpQ,KAAM1jB,EACNhE,MAAOw2B,EAAah0B,OACpBwB,IAAKA,KAETyyB,OAEDl3B,MAIH,yBAAKmjB,UAAWnjB,KAAK6I,MAAM2vB,SAAW,KAAO,oBACzB,GAAjBtB,EACC,yBAAK/T,UAAU,qBACbsV,wBAAyB,CAACC,OAAQ14B,KAAK6I,MAAM8vB,oBAE/C,KAED1B,EAAah0B,OAAS,EACrB,wBAAIkgB,UAAU,eACX8T,GAGH,OAOV,UAAe/M,EAAAA,EAAAA,YAAW6M,IC/IX,MAAM6B,WAAuB1zB,IAAAA,cAC1CU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX2J,QAAQ,EACRC,OAAQ,IAGV94B,KAAK+4B,mBAAqB/4B,KAAK+4B,mBAAmB3S,KAAKpmB,MACvDA,KAAKg5B,aAAeh5B,KAAKg5B,aAAa5S,KAAKpmB,MAC3CA,KAAKi5B,YAAcj5B,KAAKi5B,YAAY7S,KAAKpmB,MACzCA,KAAKk5B,cAAgBl5B,KAAKk5B,cAAc9S,KAAKpmB,MAG/C2nB,uBACM3nB,KAAKkvB,MAAM2J,SACb74B,KAAKqvB,SAAS,CAACyJ,OAAQ,GAAID,QAAQ,IACnC74B,KAAK6I,MAAMswB,iBAAiBpW,KAAAA,WAIhCgW,mBAAmBh0B,GACjB/E,KAAKqvB,SAAS,CAACyJ,OAAQ/zB,EAAE+iB,OAAOzoB,QAGlC25B,aAAaj0B,GACXA,EAAEgjB,iBACF,IAAI3kB,EAAQpD,KAAKkvB,MAAM4J,OAAO/L,OAC9B/sB,KAAKqvB,SAAS,CAACwJ,OAASz1B,EAAMH,OAAS,IACvCjD,KAAK6I,MAAMswB,iBAAiB/1B,EAAMH,OAAS,EAAIG,EAAQ2f,KAAAA,UAGzDkW,YAAYl0B,GACVA,EAAEgjB,iBACE/nB,KAAKkvB,MAAM2J,QACb74B,KAAK6I,MAAMswB,iBAAiBpW,KAAAA,UAE9B/iB,KAAKqvB,SAAS,CAACyJ,OAAQ,GAAID,QAAQ,IAGrCK,cAAcn0B,GACE,UAAVA,EAAEN,IACJzE,KAAKg5B,aAAaj0B,GACC,WAAVA,EAAEN,KACXzE,KAAKi5B,cAIThW,SACE,OACE,yBAAKE,UAAU,cACb,yBAAKA,UAAU,kBACb,uBAAGA,UAAU,yBAAb,UACA,2BAAOA,UAAU,SAAS/a,KAAK,OAC3BgxB,YAAap5B,KAAK6I,MAAMuwB,YACxB/5B,MAAOW,KAAKkvB,MAAM4J,OAAQO,SAAUr5B,KAAK+4B,mBACzCO,UAAWt5B,KAAKk5B,cAAeK,UAAQ,EAACC,WAAS,IACpDx5B,KAAKkvB,MAAM4J,OACV,uBAAGvM,KAAK,IAAIjJ,QAAStjB,KAAKi5B,aAAa,uBAAG9V,UAAU,kBAAb,kBAEvC,8BAAM,uBAAGA,UAAU,kBAAb,SCxDH,MAAMsW,WAAsBv0B,IAAAA,UACzCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX9rB,MAAO,MAGTpD,KAAKqmB,gBAAkBrmB,KAAKqmB,gBAAgBD,KAAKpmB,MACjDA,KAAK05B,YAAc15B,KAAK05B,YAAYtT,KAAKpmB,MAEzCA,KAAK25B,qBAAuB35B,KAAK25B,qBAAqBvT,KAAKpmB,MAC3DA,KAAK45B,sBAAwB55B,KAAK45B,sBAAsBxT,KAAKpmB,MAG/DynB,oBACEznB,KAAK6I,MAAMgxB,aAGbxT,gBAAgBthB,GACI,KAAdA,EAAEmjB,SAEJloB,KAAK6I,MAAMof,MAAK,GAIpByR,YAAY30B,GACVA,EAAEgjB,iBACF/nB,KAAK6I,MAAMof,MAAK,GAGlB0R,qBAAqBv2B,GACnBpD,KAAKqvB,SAAS,CAAEjsB,MAAO2f,KAAAA,YAAmB3f,GAAS,KAAOA,IAC1DpD,KAAK6I,MAAMswB,iBAAiB/1B,GAG9Bw2B,sBAAsBE,GACpB95B,KAAK6I,MAAMqvB,gBAAgB4B,GAC3B95B,KAAK6I,MAAMof,MAAK,GAGlBhF,SACE,IAAIkU,EAA+B,MAApBn3B,KAAKkvB,MAAM9rB,MAAgBpD,KAAK6I,MAAMkxB,cAAgB/5B,KAAK6I,MAAMsuB,SAMhF,OAJAA,EAAWA,EAASlsB,QAAQxF,GAClBA,EAAE3D,MAAQ9B,KAAK6I,MAAMmuB,eAAkBvxB,EAAEowB,IAAII,YAAcxwB,EAAEowB,IAAImE,aAIzE,yBAAK7W,UAAU,mBACb,yBAAKA,UAAU,kBACb,yBAAKA,UAAU,sBACb,6BAAK,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,aAAcC,eAAc,8BACnDwW,WAAW,oEACb,6BAAK,uBAAG1N,KAAK,IAAIjJ,QAAStjB,KAAK05B,aAAa,uBAAGvW,UAAU,kBAAb,YAE9C,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gCAAiCC,eAAc,qCAEhEyW,GAAuB,kBAACtB,GAAD,CACtBQ,YAAac,EACbf,iBAAkBn5B,KAAK25B,yBAE3B,kBAAC,EAAApW,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,gDAEpD0W,GAA0B,kBAAC,GAAD,CACzB/S,OAAQpnB,KAAK6I,MAAMue,OACnB+P,SAAUA,EACVM,SAAUz3B,KAAK6I,MAAM4uB,SACrBkB,iBAAkBwB,EAClB7D,YAAY,EACZ0B,YAAY,EACZ5C,iBAAiB,EACjB8C,gBAAiBl4B,KAAK45B,6BC/ErB,MAAMQ,WAAoBl1B,IAAAA,cACvC+d,SACE,MAAME,EAAY,oBAAsBnjB,KAAK6I,MAAMwxB,MAAQ,SAAW,KACjEr6B,KAAK6I,MAAMmS,MAAQ,SAAW,KAAOhb,KAAK6I,MAAMyxB,SAAW,YAAc,IAC9E,OAAQt6B,KAAK6I,MAAM0xB,KACjB,yBAAKpX,UAAWA,GAAW,yBAAKA,UAAU,oBAAgC,MCEjE,MAAMqX,WAAqBt1B,IAAAA,UACxCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXuL,OAAQ5xB,EAAM2lB,QAGhBxuB,KAAK06B,mBAAqB16B,KAAK06B,mBAAmBtU,KAAKpmB,MAGzDsvB,mBAAmBC,GACbvvB,KAAK6I,MAAM2lB,QAAUe,EAAUf,QACjCxuB,KAAKqvB,SAAS,CAACoL,OAAQz6B,KAAK6I,MAAM2lB,SAItCkM,mBAAmB31B,GACjB,MAAM8Z,EAAQ9Z,EAAE+iB,OAAO6S,MAAM,GAC7B36B,KAAK6I,MAAM+xB,eAAe/b,EAAMzW,KAAMwpB,IAAIW,gBAAgB1T,GAAQA,EAAM/c,MAExEiD,EAAE+iB,OAAOzoB,MAAQ,GAGnB4jB,SAGE,MAAM4X,EAAS,sBAAwB,GAAKpZ,KAAKqZ,UAAU1N,UAAU,EAAG,GAClEjK,EAAY,iBAAmBnjB,KAAK6I,MAAMkyB,SAAW,aAAe,IAC1E,OACE,yBAAK5X,UAAWA,GACbnjB,KAAK6I,MAAMkyB,WAAa/6B,KAAKkvB,MAAMuL,OAClC,KACA,uBAAGlO,KAAK,IAAIpJ,UAAU,eAAeG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM+xB,mBACnF,uBAAGzX,UAAU,kBAAb,UAEHnjB,KAAKkvB,MAAMuL,OACV,yBAAK1L,IAAK/uB,KAAK6I,MAAMue,OAAOyH,aAAad,GAAiB/tB,KAAKkvB,MAAMuL,SAAUtX,UAAU,YACzFnjB,KAAK6I,MAAMkyB,UAAY/6B,KAAK6I,MAAMixB,IAChC,yBAAK3W,UAAU,cACb,kBAACoL,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBoH,QAAQ,EACRrH,MAAOnnB,KAAK6I,MAAMixB,IAClBlb,MAAO5e,KAAK6I,MAAM+V,SAGtB,yBAAKuE,UAAU,SAASiB,GAAxB,IAA4CA,IAC/CpkB,KAAK6I,MAAMkyB,SAAW,KACrB,2BAAO3yB,KAAK,OAAOob,GAAIqX,EAAQ1X,UAAU,mBACvC6X,OAAO,UAAU3B,SAAUr5B,KAAK06B,qBACnC16B,KAAK6I,MAAMkyB,SAAW,KACvB,2BAAOE,QAASJ,EAAQ1X,UAAU,SAChC,uBAAGA,UAAU,kBAAb,gBAEF,kBAACiX,GAAD,CAAaG,KAAMv6B,KAAK6I,MAAMqyB,UAAWb,OAAO,EAAMrf,OAAO,EAAMsf,UAAU,MC7DrF,MAAMnQ,GAAe,CAAC,MAAS,iBAEzBzF,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BwW,eAAgB,CACd3X,GAAI,iBACJC,eAAgB,CAAF,qCAGhB2X,YAAa,CACX5X,GAAI,cACJC,eAAgB,CAAF,iCAGhB4X,aAAc,CACZ7X,GAAI,eACJC,eAAgB,CAAF,mCAOlB,MAAM6X,WAAkBp2B,IAAAA,cACtB+d,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KACnC,IAAImE,EAAS,KACb,OAAIrqB,KAAK6I,MAAM0yB,eAAiBv7B,KAAK6I,MAAM0yB,cAAct4B,OAAS,GAChEonB,EAAS,GACTrqB,KAAK6I,MAAM0yB,cAAcrwB,KAAK5D,IAC5B,MAAMovB,EAAUzQ,EAAcvB,GAAS,SAAWpd,IAC5C0iB,EAAQ,kBAAoB1iB,EAAI,SACtC+iB,EAAO5mB,KAAK,yBAAK0f,UAAU,gBAAgB1e,IAAK6C,GAAG,uBAAG6b,UAAW6G,GAAQG,GAAa7iB,IAAMA,GAAhF,IAAwFovB,OAE/F,oCAAGrM,IAEL,MAIX,UAAeH,EAAAA,EAAAA,YAAWoR,ICvCX,MAAME,WAAiBt2B,IAAAA,cACpCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKy7B,aAAez7B,KAAKy7B,aAAarV,KAAKpmB,MAG7Cy7B,eACEz7B,KAAK6I,MAAMwwB,SAASr5B,KAAK6I,MAAM/G,MAAO9B,KAAK6I,MAAM6yB,SAGnDzY,SACE,OACEjjB,KAAK6I,MAAMwwB,UACc,IAAvBr5B,KAAK6I,MAAM6yB,QACT,uBAAGvY,UAAU,gCAAgCG,QAAStjB,KAAKy7B,cAA3D,cACqB,IAAvBz7B,KAAK6I,MAAM6yB,QACT,uBAAGvY,UAAU,gCAAgCG,QAAStjB,KAAKy7B,cAA3D,2BACA,uBAAGtY,UAAU,0BAAb,2BAEAnjB,KAAK6I,MAAM6yB,QACT,uBAAGvY,UAAU,kBAAb,aACA,uBAAGA,UAAU,kBAAb,4BCtBG,MAAMwY,WAAmBz2B,IAAAA,cACtC+d,SACE,OACE,uBAAGsJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM8mB,aAC1D,uBAAGxM,UAAU,kBAAb,WCHO,MAAMyY,WAAmB12B,IAAAA,cACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXqL,MAAM,GAGRv6B,KAAKioB,KAAOjoB,KAAKioB,KAAK7B,KAAKpmB,MAG7BsvB,mBAAmBC,GACbA,EAAUsM,QAAU77B,KAAK6I,MAAMgzB,OACjC77B,KAAKqvB,SAAS,CACZkL,OAASv6B,KAAK6I,MAAMgzB,QAK1B5T,OACEjoB,KAAKqvB,SAAS,CAACkL,MAAM,IACjBv6B,KAAK6I,MAAMizB,cACb97B,KAAK6I,MAAMizB,eAIf7Y,SACE,MACM4Y,EADQ,CAAC5f,IAAK,QAAStN,KAAM,UAAWD,KAAM,QAChC1O,KAAK6I,MAAMgzB,QAAU,GACnC1Y,EAAY,YAAc0Y,EAChC,OACE,yBAAK1Y,UAAWA,GACd,yBAAKA,UAAU,QAAO,uBAAGA,UAAU,kBAAkB0Y,IACrD,8BACG77B,KAAK6I,MAAMkzB,KACX/7B,KAAK6I,MAAM+tB,OACV,oCACO,uBAAGrK,KAAK,IACXvC,MAAO,CAAEgS,WAAY,UACrB1Y,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM+tB,WAC/C52B,KAAK6I,MAAMozB,aAGhB,MAEJ,yBAAK9Y,UAAU,UAAS,kBAACwY,GAAD,CAAYhM,SAAU3vB,KAAKioB,UC7C5C,MAAMiU,WAAah3B,IAAAA,cAChCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAG7Cm8B,aAAap3B,GACXA,EAAEgjB,iBACF/nB,KAAK6I,MAAM8mB,SAAS3vB,KAAK6I,MAAMse,MAAOnnB,KAAK6I,MAAMpI,OAGnDwiB,SACE,MAAMrE,EAAQ5e,KAAK6I,MAAM+V,OAAS5e,KAAK6I,MAAMse,MACvChE,EAAYnjB,KAAK6I,MAAMuzB,QAAU,eAAiB,OACxD,OACE,yBAAKjZ,UAAWA,GACbnjB,KAAK6I,MAAMwzB,SACV,0BAAMlZ,UAAU,WAChB,yBAAKA,UAAU,cACb,kBAACoL,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQxuB,KAAK6I,MAAM2lB,SAAU,EAC7BrH,MAAOnnB,KAAK6I,MAAMse,MAClBvI,MAAO5e,KAAK6I,MAAM+V,SAGxB,8BAAOA,GACN5e,KAAK6I,MAAM8mB,WAAa3vB,KAAK6I,MAAM0wB,SAClC,uBAAGhN,KAAK,IAAIjJ,QAAStjB,KAAKm8B,cAA1B,KACE,0BAAMhZ,UAAU,aC3Bb,MAAMmZ,WAAkBp3B,IAAAA,UACrCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQoN,GAAUC,qBAAqB1zB,GAC5C7I,KAAKkvB,MAAMsN,MAAQ,GACnBx8B,KAAKkvB,MAAMuN,SAAU,EAErBz8B,KAAK08B,gBAAkB18B,KAAK08B,gBAAgBtW,KAAKpmB,MACjDA,KAAK28B,aAAe38B,KAAK28B,aAAavW,KAAKpmB,MAC3CA,KAAK48B,iBAAmB58B,KAAK48B,iBAAiBxW,KAAKpmB,MACnDA,KAAK68B,kBAAoB78B,KAAK68B,kBAAkBzW,KAAKpmB,MACrDA,KAAK88B,gBAAkB98B,KAAK88B,gBAAgB1W,KAAKpmB,MACjDA,KAAKk5B,cAAgBl5B,KAAKk5B,cAAc9S,KAAKpmB,MAGpB,4BAAC6I,GAC1B,MAAO,CACLuwB,YAAavwB,EAAMk0B,MAAQ,GAAKl0B,EAAMm0B,OACtCC,YAAaX,GAAUY,UAAUr0B,EAAMk0B,MAAOl0B,EAAMs0B,eACpDC,UAAWd,GAAUe,WAAWx0B,EAAMk0B,QAI1CzN,mBAAmBC,EAAW+N,GACxB/N,EAAUwN,OAAS/8B,KAAK6I,MAAMk0B,OAChCxN,EAAU4N,eAAiBn9B,KAAK6I,MAAMs0B,eACtC5N,EAAUyN,QAAUh9B,KAAK6I,MAAMm0B,QAC/Bh9B,KAAKqvB,SAASiN,GAAUC,qBAAqBv8B,KAAK6I,UAE/Cy0B,GAAat9B,KAAK6I,MAAMk0B,MAAM95B,OAASq6B,EAAUL,YAAYh6B,SAChEjD,KAAKqvB,SAAS,CAACmN,MAAO,KAKT,kBAACO,GAChB,MAAMt8B,EAAQ,GACd,IAAI4C,EAAQ,EAKZ,OAJA05B,EAAM7xB,KAAKid,IACT1nB,EAAM0nB,EAAKZ,MAAQlkB,EACnBA,OAEK5C,EAIO,iBAACs8B,EAAOQ,GACtB,MAAMhE,EAAW,GACXiE,EAAS,GAQf,OAPAT,EAAM7xB,KAAKid,IACLoV,GAAQA,EAAK51B,SAASwgB,EAAKZ,MAC7BgS,EAAS91B,KAAK0kB,GAEdqV,EAAO/5B,KAAK0kB,MAGToR,EAASpF,OAAOqJ,GAGzBd,gBAAgB33B,GACd/E,KAAKqvB,SAAS,CAACmN,MAAOz3B,EAAE+iB,OAAOzoB,QAC3BW,KAAK6I,MAAMuuB,YACbp3B,KAAK6I,MAAMuuB,WAAWryB,EAAE+iB,OAAOzoB,OAInCs9B,aAAa1L,GACX,MAAMwM,EAAUz9B,KAAKkvB,MAAM+N,YAAYhM,GACvCjxB,KAAK6I,MAAM60B,cAAcD,EAAQlW,KAAMvnB,KAAKkvB,MAAMkO,UAAUK,EAAQlW,OAGtEqV,iBAAiBzU,EAAM8I,GACrBjxB,KAAK28B,aAAa1L,GAGpB4L,oBACE78B,KAAKqvB,SAAS,CAACoN,SAAS,IAG1BK,kBACE98B,KAAKqvB,SAAS,CAACoN,SAAS,IACpBz8B,KAAK6I,MAAM80B,aACb39B,KAAK6I,MAAM80B,YAAY39B,KAAKkvB,MAAMsN,OAItCtD,cAAcn0B,GACZ,GAAc,cAAVA,EAAEN,KACJ,GAA+B,GAA3BzE,KAAKkvB,MAAMsN,MAAMv5B,QAAejD,KAAKkvB,MAAM+N,YAAYh6B,OAAS,EAAG,CACrE,MAAMkuB,EAAKnxB,KAAKkvB,MAAM+N,YAAYh6B,OAAS,EACvCjD,KAAKkvB,MAAM+N,YAAY9L,GAAI5J,OAASvnB,KAAK6I,MAAMs0B,eACjDn9B,KAAK28B,aAAaxL,QAGH,UAAVpsB,EAAEN,IACPzE,KAAK6I,MAAM+0B,SACb59B,KAAK6I,MAAM+0B,QAAQ59B,KAAKkvB,MAAMsN,OAEb,WAAVz3B,EAAEN,KACPzE,KAAK6I,MAAM8mB,UACb3vB,KAAK6I,MAAM8mB,WAKjB1M,SACE,MAAM8Z,EAAQ,GACd,IAAI15B,EAAQ,EACZ,MAAM85B,EAAgBn9B,KAAK6I,MAAMs0B,eAAiB,GAClDn9B,KAAKkvB,MAAM+N,YAAY/xB,KAAKid,IAC1B4U,EAAMt5B,KACJ,kBAACy4B,GAAD,CACE9U,OAAQpnB,KAAK6I,MAAMue,OACnBuI,SAAU3vB,KAAK48B,iBACfpO,OAAQwB,GAAa7H,EAAKqP,OAASrP,EAAKqP,OAAOrK,MAAQ,MACvDvO,MAAOuJ,EAAKqP,OAASrP,EAAKqP,OAAO1kB,QAAKpP,EACtC24B,SAAUr8B,KAAK6I,MAAMg1B,eACrB1W,MAAOgB,EAAKZ,KACZgS,SAAU4D,EAAcx1B,SAASwgB,EAAKZ,MACtC6U,QAASjU,EAAKiU,QACd37B,MAAO4C,EACPoB,IAAK0jB,EAAKZ,QAEdlkB,OAEF,MAAM8f,EAAY,cAAgBnjB,KAAKkvB,MAAMuN,QAAU,WAAa,IAC9DjD,IAAcx5B,KAAK6I,MAAMi1B,SAAW,GAC1C,OACE,yBAAK3a,UAAWA,GACb4Z,EACD,2BAAO30B,KAAK,OACVgxB,YAAap5B,KAAKkvB,MAAMkK,YACxBC,SAAUr5B,KAAK08B,gBACfqB,QAAS/9B,KAAK68B,kBACdmB,OAAQh+B,KAAK88B,gBACbxD,UAAWt5B,KAAKk5B,cAChB75B,MAAOW,KAAKkvB,MAAMsN,MAClBsB,SAAU99B,KAAK6I,MAAMi1B,SACrBtE,UAAWA,MC3IrB,MAAM9U,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BsZ,YAAa,CACXza,GAAI,cACJC,eAAgB,CAAF,4CAGhBya,yBAA0B,CACxB1a,GAAI,2BACJC,eAAgB,CAAF,mFAKlB,MAAM0a,WAAqBj5B,IAAAA,UACzBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CAEXkP,QAASv1B,EAAMu1B,QACf39B,MAAO09B,GAAaE,aAAax1B,EAAMu1B,SACvCjB,cAAegB,GAAahB,cAAct0B,EAAMu1B,QAASv1B,EAAMy1B,mBAAoBz1B,EAAM01B,gBACzFC,cAAe,GACfC,kBAAmB51B,EAAMqd,KAAKD,cAAcvB,GAASuZ,aACrDS,iBAAkBP,GAAaO,iBAAiB71B,EAAMu1B,UAGxDp+B,KAAK45B,sBAAwB55B,KAAK45B,sBAAsBxT,KAAKpmB,MAC7DA,KAAK2+B,oBAAsB3+B,KAAK2+B,oBAAoBvY,KAAKpmB,MACzDA,KAAK4+B,oBAAsB5+B,KAAK4+B,oBAAoBxY,KAAKpmB,MACzDA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAG1B,oBAACo+B,GAClB,IAAI39B,EAAQ,GAIZ,OAHA29B,EAAQlzB,KAAK4zB,IACXr+B,EAAMq+B,EAAEvX,MAAQ,CAACwX,MAAO,EAAGC,SAAS,MAE/Bv+B,EAGW,qBAAC29B,EAASa,EAAaV,GACzC,IAAIW,EAAO,GAMX,OALAd,EAAQlzB,KAAK4zB,KACPG,GAAeH,EAAEvX,MAAQgX,IAC3BW,EAAKz7B,KAAKq7B,EAAEvX,SAGT2X,EAGc,wBAACd,GACtB,IAAIe,EAAM,GAIV,OAHAf,EAAQlzB,KAAK4zB,IACXK,EAAI17B,KAAKq7B,EAAEvX,SAEN4X,EAGTvF,sBAAsBjO,EAAQlrB,GAC5B,IAAIyR,EAASlS,KAAKkvB,MAAMzuB,MAAMkrB,GAC9B,GAAIzZ,EAAQ,CACV,GAAIA,EAAO8sB,QAET,OAEF9sB,EAAO6sB,OAAS,EAChB7sB,EAAO8sB,SAAU,OAEjB9sB,EAAS,CAAC6sB,MAAO,EAAGC,SAAS,GAG/B,IAAIF,EAAI9+B,KAAKkvB,MAAMkP,QAAQlgC,QAC3B4gC,EAAEr7B,KAAKzD,KAAK6I,MAAMsuB,SAAS12B,IAE3B,MAAM0+B,EAAMhB,GAAaO,iBAAiBI,GAEpCt5B,EAAIxF,KAAKkvB,MAAMzuB,MACrB+E,EAAEmmB,GAAUzZ,EAEZlS,KAAKqvB,SAAS,CAAC+O,QAASU,EAAGr+B,MAAO+E,EAAGk5B,iBAAkBS,IAGzDR,oBAAoBhT,EAAQlrB,GAC1B,MAAMyR,EAASlS,KAAKkvB,MAAMzuB,MAAMkrB,GAChC,IAAKzZ,IAAWA,EAAO8sB,QACrB,OAEF9sB,EAAO8sB,SAAU,EACjB9sB,EAAO6sB,OAAS,EAEhB,IAAID,EAAI9+B,KAAKkvB,MAAMkP,QAAQlgC,QAC3B4gC,EAAEM,OAAO3+B,EAAO,GAEhB,MAAM0+B,EAAMhB,GAAaO,iBAAiBI,GAEpCt5B,EAAIxF,KAAKkvB,MAAMzuB,MACrB+E,EAAEmmB,GAAUzZ,EAEZlS,KAAKqvB,SAAS,CAAC+O,QAASU,EAAGr+B,MAAO+E,EAAGk5B,iBAAkBS,IAGzDP,oBAAoB1+B,GAClB,MAAM,cAAC+lB,GAAiBjmB,KAAK6I,MAAMqd,KAC7B4C,EAAO5oB,EAEX+lB,EAAcvB,GAASwZ,yBAA0B,CAAC96B,MAAOlD,IADzD+lB,EAAcvB,GAASuZ,aAGzBj+B,KAAKqvB,SAAS,CAACmP,cAAet+B,EAAKu+B,kBAAmB3V,IAG/B,0BAAC7d,EAAQF,GAChC,GAAIE,EAAQ,CACV,IAAK,IAAIzF,EAAE,EAAGA,EAAEuF,EAAO9H,OAAQuC,IAC7B,GAAIuF,EAAOvF,GAAG6nB,QAAQpiB,IAAW,EAC/B,OAAO,EAGX,OAAO,EAET,OAAO,EAGT4zB,eACE,IAAIv7B,EAAWtD,KACXo+B,EAAU,GACViB,EAAQ,GACR5B,EAAU,GAEH59B,OAAO2H,KAAKxH,KAAKkvB,MAAMzuB,OAC7ByK,KAAI,SAASxD,GACZpE,EAAS4rB,MAAMzuB,MAAMiH,GAAGs3B,SAC1BZ,EAAQ36B,KAAKiE,GAGXpE,EAAS4rB,MAAMzuB,MAAMiH,GAAGq3B,MAAQ,EAClCM,EAAM57B,KAAKiE,GACFpE,EAAS4rB,MAAMzuB,MAAMiH,GAAGq3B,MAAQ,GACzCtB,EAAQh6B,KAAKiE,MAGjB1H,KAAK6I,MAAMy2B,SAASlB,EAASiB,EAAO5B,GAGtCtB,eACEn8B,KAAK6I,MAAM8mB,WAGb1M,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KACnC,OACE,yBAAK1C,GAAG,iBACN,yBAAKL,UAAU,kBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,qCAG7D,yBAAKN,UAAU,kBACb,kBAACmZ,GAAD,CACElV,OAAQpnB,KAAK6I,MAAMue,OACnB2V,MAAO/8B,KAAKkvB,MAAMkP,QAClBjB,cAAen9B,KAAKkvB,MAAMiO,cAC1BH,OAAO,cACP5F,WAAYp3B,KAAK4+B,oBACjBlB,cAAe19B,KAAK2+B,uBAExB,yBAAKxb,UAAU,kBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,oCAI5D,kBAAC,GAAD,CACE2D,OAAQpnB,KAAK6I,MAAMue,OACnB+P,SAAUn3B,KAAK6I,MAAMsuB,SACrBM,SAAUz3B,KAAK6I,MAAM4uB,SACrBT,cAAeh3B,KAAKkvB,MAAMwP,iBAC1BzzB,OAAQjL,KAAKkvB,MAAMsP,cACnBpH,WAAY+G,GAAaoB,mBACzB5G,iBAAkB34B,KAAKkvB,MAAMuP,kBAC7BnI,YAAY,EACZ0B,YAAY,EACZE,gBAAiBl4B,KAAK45B,wBACxB,yBAAKpW,GAAG,wBAAwBL,UAAU,kBACxC,4BAAQA,UAAU,YAAYG,QAAStjB,KAAKm8B,cAC1C,kBAAC,EAAA5Y,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAErD,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,4BAQ3D,UAAeyG,EAAAA,EAAAA,YAAWiU,ICjMpBzZ,IAAWC,EAAAA,EAAAA,gBAAe,CAC9B6a,OAAQ,CACNhc,GAAI,kBACJC,eAAgB,CAAF,kEAGhBgP,OAAQ,CACNjP,GAAI,kBACJC,eAAgB,CAAF,kEAGhBgc,OAAQ,CACNjc,GAAI,mBACJC,eAAgB,CAAF,mEAGhBic,OAAQ,CACNlc,GAAI,kBACJC,eAAgB,CAAF,0EAGhBkc,SAAU,CACRnc,GAAI,mBACJC,eAAgB,CAAF,qEAGhBmc,OAAQ,CACNpc,GAAI,mBACJC,eAAgB,CAAF,mEAGhBoc,QAAS,CACPrc,GAAI,oBACJC,eAAgB,CAAF,oEAGhBqc,MAAO,CACLtc,GAAI,mBACJC,eAAgB,CAAF,qEAKlB,MAAMsc,WAA0B76B,IAAAA,UAC9BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXxmB,MAAOG,EAAMH,MAAQ,IAAI3F,QAAQ,IAAK,KAGxC/C,KAAKy7B,aAAez7B,KAAKy7B,aAAarV,KAAKpmB,MAC3CA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAG7Cy7B,aAAav7B,GACX,IAAIwI,EAAO1I,KAAKkvB,MAAMxmB,MAEV,GADAA,EAAK2kB,QAAQntB,GAEvBwI,GAAQxI,EAERwI,EAAOA,EAAK3F,QAAQ7C,EAAK,IAE3BF,KAAKqvB,SAAS,CAAC3mB,KAAMA,IAGvBm2B,eAEE,MAAMn2B,GAAQ1I,KAAKkvB,MAAMxmB,MAAQ,KAAKmqB,MAAM,IAAIrF,OAAO3f,KAAK,IAExDnF,KADY1I,KAAK6I,MAAMH,MAAQ,KAAKmqB,MAAM,IAAIrF,OAAO3f,KAAK,IAE5D7N,KAAK6I,MAAMy2B,SAAS52B,GAEpB1I,KAAK6I,MAAM8mB,WAIfwM,eACEn8B,KAAK6I,MAAM8mB,WAGb1M,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KAC7Blb,EAAM,WACNg1B,EAAQ,CACZ,EAAK/Z,EAAcvB,GAAS8a,OAAQ,CAACt/B,IAAK,MAC1C,EAAK+lB,EAAcvB,GAAS+N,OAAQ,CAACvyB,IAAK,MAC1C,EAAK+lB,EAAcvB,GAAS+a,OAAQ,CAACv/B,IAAK,MAC1C,EAAK+lB,EAAcvB,GAASgb,OAAQ,CAACx/B,IAAK,MAC1C,EAAK+lB,EAAcvB,GAASib,SAAU,CAACz/B,IAAK,MAC5C,EAAK+lB,EAAcvB,GAASkb,OAAQ,CAAC1/B,IAAK,MAC1C,EAAK+lB,EAAcvB,GAASmb,QAAS,CAAC3/B,IAAK,MAC3C,EAAK+lB,EAAcvB,GAASob,MAAO,CAAC5/B,IAAK,OAG3C,IAAI+/B,EAAOjgC,KAAK6I,MAAMo3B,MAAQ,GAC1Bv3B,EAAO1I,KAAKkvB,MAAMxmB,KAClBw3B,GAAWlgC,KAAK6I,MAAMq3B,SAAW,IAAIn9B,QAAQ,IAAK,IAClDQ,EAAQ,GACZ,IAAK,IAAIiC,EAAE,EAAGA,EAAEwF,EAAI/H,OAAQuC,IAAK,CAC/B,IAAIC,EAAIuF,EAAIyU,OAAOja,GACfy6B,EAAK5S,QAAQ5nB,IAAM,GAAKiD,EAAK2kB,QAAQ5nB,GAAK,GAI9ClC,EAAME,KACJ,wBAAIgB,IAAKgB,GACP,4BAAKu6B,EAAMv6B,IACX,wBAAI0d,UAAU,YAAY8c,EAAK5S,QAAQ5nB,GAAK,EAC1C,kBAAC+1B,GAAD,CAAU15B,KAAM2D,EAAGi2B,QAAUhzB,EAAK2kB,QAAQ5nB,IAAM,EAAI4zB,SAAUr5B,KAAKy7B,eAEnE,kBAACD,GAAD,CAAU15B,KAAM2D,EAAGi2B,QAAUhzB,EAAK2kB,QAAQ5nB,IAAM,KAC3CzF,KAAK6I,MAAMq3B,QAAU,wBAAI/c,UAAU,YACxC,kBAACqY,GAAD,CAAU15B,KAAM2D,EAAGi2B,QAAUwE,EAAQ7S,QAAQ5nB,IAAM,KAC7C,OAKd,OACE,yBAAK0d,UAAU,qBACZnjB,KAAK6I,MAAMs3B,UACV,wBAAIhd,UAAU,qBAAoB,kBAAC,GAAD,CAChCiE,OAAQpnB,KAAK6I,MAAMue,OACnBe,KAAMnoB,KAAK6I,MAAMsf,KACjBvJ,MAAO5e,KAAK6I,MAAMs3B,UAClB1J,OAAO,EACPjI,OAAQwB,GAAahwB,KAAK6I,MAAMu3B,WAAapgC,KAAK6I,MAAMu3B,WAAa,SAAiB,KAC1F,2BAAOjd,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAC3CC,eAAc,kCAChB,2BAAON,UAAU,qBAChBnjB,KAAK6I,MAAMq3B,QACV,+BAAO,4BACL,6BAAS,4BAAKlgC,KAAK6I,MAAMw3B,WACzB,4BAAKrgC,KAAK6I,MAAMy3B,gBAElB,KACF,+BACG/8B,IAEH,6BACA,yBAAK4f,UAAU,kBACb,4BAAQA,UAAU,UAAUG,QAAStjB,KAAKm8B,cACxC,kBAAC,EAAA5Y,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAErD,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,4BAQ3D,UAAeyG,EAAAA,EAAAA,YAAW6V,IChKX,MAAMQ,WAAgBr7B,IAAAA,UACnCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CAEXsR,KAAM,EACNC,KAAM,EAENC,QAAS,EACTC,QAAS,EAETC,KAAM,EAENC,QAAS,EACTC,QAjBmB,KAsBrB9gC,KAAK+gC,QAAU77B,IAAAA,YAEflF,KAAKghC,OAAS97B,IAAAA,YAEdlF,KAAKy1B,QAAUvwB,IAAAA,YAEflF,KAAKihC,YAAc/7B,IAAAA,YAGnBlF,KAAKkhC,WAAa,EAClBlhC,KAAKmhC,YAAc,EAGnBnhC,KAAKohC,OAAS,EACdphC,KAAKqhC,OAAS,EAEdrhC,KAAKshC,aAAe,EAGpBthC,KAAKuhC,WAAa,GAClBvhC,KAAKwhC,SAAW,GAEhBxhC,KAAK0gC,QAAU,EACf1gC,KAAK2gC,QAAU,EAEf3gC,KAAKyhC,YAAczhC,KAAKyhC,YAAYrb,KAAKpmB,MACzCA,KAAK0hC,OAAS1hC,KAAK0hC,OAAOtb,KAAKpmB,MAC/BA,KAAK2hC,WAAa3hC,KAAK2hC,WAAWvb,KAAKpmB,MACvCA,KAAK4hC,UAAY5hC,KAAK4hC,UAAUxb,KAAKpmB,MACrCA,KAAK6hC,QAAU7hC,KAAK6hC,QAAQzb,KAAKpmB,MACjCA,KAAK8hC,UAAY9hC,KAAK8hC,UAAU1b,KAAKpmB,MACrCA,KAAK+hC,WAAa/hC,KAAK+hC,WAAW3b,KAAKpmB,MACvCA,KAAKgiC,YAAchiC,KAAKgiC,YAAY5b,KAAKpmB,MACzCA,KAAKiiC,UAAYjiC,KAAKiiC,UAAU7b,KAAKpmB,MAGvCynB,oBACEznB,KAAK+gC,QAAQlZ,QAAQ/E,iBAAiB,YAAa9iB,KAAK4hC,UAAW,CAAEM,SAAS,IAC9EliC,KAAK+gC,QAAQlZ,QAAQ/E,iBAAiB,aAAc9iB,KAAK4hC,UAAW,CAAEM,SAAS,IAG/EliC,KAAKwhC,SAAWxhC,KAAKihC,YAAYpZ,QAAQsa,wBAEzCniC,KAAK0gC,QAAU1gC,KAAKwhC,SAAS/R,MAAQ,EACrCzvB,KAAK2gC,QAAU3gC,KAAKwhC,SAASrR,OAAS,EAGtCnwB,KAAKuhC,WAAavhC,KAAKghC,OAAOnZ,QAAQsa,wBAGxCxa,uBACE3nB,KAAK+gC,QAAQlZ,QAAQD,oBAAoB,YAAa5nB,KAAK4hC,WAC3D5hC,KAAK+gC,QAAQlZ,QAAQD,oBAAoB,aAAc5nB,KAAK4hC,WAI9DI,YAAYxB,EAAMC,EAAMG,GAEtB5gC,KAAKqvB,SAAS,CACZmR,KAAMA,EACNC,KAAMA,EACNG,KAAMA,EACNF,QAAS1gC,KAAK0gC,QAAUF,EACxBG,QAAS3gC,KAAK2gC,QAAUF,IAI1B,MAAMjX,GAAQxpB,KAAK0gC,QAAUF,GAAQI,EAAO5gC,KAAK0gC,QAC3C7W,GAAO7pB,KAAK2gC,QAAUF,GAAQG,EAAO5gC,KAAK2gC,QAChD3gC,KAAK6I,MAAMwwB,UACR7P,EAAOxpB,KAAKuhC,WAAW/X,KAAOxpB,KAAKwhC,SAAShY,MAAQoX,GACpD/W,EAAM7pB,KAAKuhC,WAAW1X,IAAM7pB,KAAKwhC,SAAS3X,KAAO+W,EAClD5gC,KAAKuhC,WAAW9R,MAAQmR,EACxB5gC,KAAKuhC,WAAWpR,OAASyQ,EACzBA,GAIa,kBAACwB,EAAS5Q,EAAKwP,EAAQjC,GACtC,IAAIsD,EAAW5gB,KAAKkJ,IAAI,EAAGqW,EAAO,GAAKxP,EAAI,GAAKuN,EAAOvN,EAAI,GAAKwP,EAAO,GAAKjC,GAQ5E,OAPgB,GAAZsD,GAGO5gB,KAAKkJ,IAAI,EAAGqW,EAAO,GAAKxP,EAAI,GAAIA,EAAI,GAAKwP,EAAO,IAAMqB,KAD/DD,GAAWrD,GAKNqD,EAGTX,cAEE,MAAMa,EAAUtiC,KAAKy1B,QAAQ5N,QAAQsa,wBAErCniC,KAAKkhC,WAAaoB,EAAQ7S,MAC1BzvB,KAAKmhC,YAAcmB,EAAQnS,OAG3B,MAAM0Q,EAAUpf,KAAKgS,IAAIzzB,KAAKuhC,WAAW9R,MAAQ6S,EAAQ7S,MAAOzvB,KAAKuhC,WAAWpR,OAASmS,EAAQnS,QACjGnwB,KAAKqvB,SAAS,CACZwR,QAASA,EACTC,QAASrf,KAAKgS,IA3HK,IA2HiBoN,EAAU,KAIhD,MAAMD,EAAOnf,KAAKgS,IAAIzzB,KAAKwhC,SAAS/R,MAAQ6S,EAAQ7S,MAAOzvB,KAAKwhC,SAASrR,OAASmS,EAAQnS,QAEpFqQ,EAAOxgC,KAAKuhC,WAAW/X,KAAOxpB,KAAKwhC,SAAShY,MAC/C8Y,EAAQ7S,MAAQzvB,KAAKuhC,WAAW9R,OAAS,EACtCgR,EAAOzgC,KAAKuhC,WAAW1X,IAAM7pB,KAAKwhC,SAAS3X,KAC9CyY,EAAQnS,OAASnwB,KAAKuhC,WAAWpR,QAAU,EAE9CnwB,KAAKgiC,YAAYxB,EAAMC,EAAMG,GAG/Bc,OAAO38B,GACL/E,KAAK2hC,WAAW58B,EAAE+iB,OAAOzoB,OAG3BsiC,WAAWf,GACT,IAAIJ,EAAOxgC,KAAKkvB,MAAMsR,KAClBC,EAAOzgC,KAAKkvB,MAAMuR,KAItB,MAAM8B,EAAUviC,KAAK0gC,SAAW1gC,KAAK0gC,QAAUF,GAAQI,EACjD4B,EAAWD,EAAUviC,KAAKkhC,WAAaN,EAEvC6B,EAASziC,KAAKuhC,WAAW/X,KAAOxpB,KAAKwhC,SAAShY,KAC9CkZ,EAAUD,EAASziC,KAAKuhC,WAAW9R,MACrCgT,EAASF,EACX/B,GAAQ+B,EAAUE,EACTC,EAAUF,IACnBhC,GAAQkC,EAAUF,GAEpB,MAAMG,EAAS3iC,KAAK2gC,SAAW3gC,KAAK2gC,QAAUF,GAAQG,EAChDgC,EAAYD,EAAS3iC,KAAKmhC,YAAcP,EACxCiC,EAAQ7iC,KAAKuhC,WAAW1X,IAAM7pB,KAAKwhC,SAAS3X,IAC5CiZ,EAAWD,EAAQ7iC,KAAKuhC,WAAWpR,OACrC0S,EAAQF,EACVlC,GAAQkC,EAASE,EACRC,EAAWF,IACpBnC,GAAQqC,EAAWF,GAGrB5iC,KAAKgiC,YAAYxB,EAAMC,EAAMG,GAG/BgB,UAAU78B,GACJA,EAAEg+B,SACJ/iC,KAAKohC,OAASr8B,EAAEg+B,QAAQ,GAAGzN,MAC3Bt1B,KAAKqhC,OAASt8B,EAAEg+B,QAAQ,GAAG1N,QAE3Br1B,KAAKohC,OAASr8B,EAAEuwB,MAChBt1B,KAAKqhC,OAASt8B,EAAEswB,OAGlBrwB,OAAO8d,iBAAiB,YAAa9iB,KAAK8hC,UAAW,CAAEI,SAAS,IAChEl9B,OAAO8d,iBAAiB,YAAa9iB,KAAK+hC,WAAY,CAAEG,SAAS,IACjEl9B,OAAO8d,iBAAiB,UAAW9iB,KAAK6hC,QAAS,CAAEK,SAAS,IAC5Dl9B,OAAO8d,iBAAiB,WAAY9iB,KAAK6hC,QAAS,CAAEK,SAAS,IAE7Dxa,SAASjR,KAAKuT,MAAd,WAAoC,OAItCiY,UAAU3M,EAAOD,GACf,MAAM2N,EAAK1N,EAAQt1B,KAAKohC,OAClB6B,EAAK5N,EAAQr1B,KAAKqhC,OAExBrhC,KAAKohC,OAAS9L,EACdt1B,KAAKqhC,OAAShM,EAGd,MAAMiN,EAAUtiC,KAAKy1B,QAAQ5N,QAAQsa,wBAGrC,IAAI3B,EAAOD,GAAQ2C,WAAWljC,KAAKkvB,MAAMsR,KAAM,CAAC8B,EAAQ9Y,KAAM8Y,EAAQ5Y,OACpE,CAAC1pB,KAAKuhC,WAAW/X,KAAMxpB,KAAKuhC,WAAW7X,OAAQsZ,GAC7CvC,EAAOF,GAAQ2C,WAAWljC,KAAKkvB,MAAMuR,KAAM,CAAC6B,EAAQzY,IAAKyY,EAAQxY,QACnE,CAAC9pB,KAAKuhC,WAAW1X,IAAK7pB,KAAKuhC,WAAWzX,QAASmZ,GAEjDjjC,KAAKgiC,YAAYxB,EAAMC,EAAMzgC,KAAKkvB,MAAM0R,MAI1CkB,UAAU/8B,GACRA,EAAEgjB,iBACF/nB,KAAKiiC,UAAUl9B,EAAEuwB,MAAOvwB,EAAEswB,OAG5B0M,WAAWh9B,GAGT,GAFAA,EAAEgjB,iBAEsB,GAApBhjB,EAAEg+B,QAAQ9/B,OAEZ,YADAjD,KAAKiiC,UAAUl9B,EAAEg+B,QAAQ,GAAGzN,MAAOvwB,EAAEg+B,QAAQ,GAAG1N,OAKlD,MAAO8N,EAAQC,GAAUr+B,EAAEg+B,QACrBM,EAAW5hB,KAAK6hB,MAAMH,EAAO7N,MAAQ8N,EAAO9N,QAAU6N,EAAO7N,MAAQ8N,EAAO9N,QAC/E6N,EAAO9N,MAAQ+N,EAAO/N,QAAU8N,EAAO9N,MAAQ+N,EAAO/N,QAEpDr1B,KAAKshC,eACNthC,KAAKshC,aAAe+B,EAAWrjC,KAAKkvB,MAAM0R,MAG9C,IAAIrQ,EAAS8S,EAAWrjC,KAAKshC,aAC7BthC,KAAK2hC,WAAWlgB,KAAKgS,IAAIzzB,KAAK6gC,QAASpf,KAAKkJ,IAAI3qB,KAAK8gC,QAASvQ,KAGhEsR,QAAQ98B,GACNC,OAAO4iB,oBAAoB,YAAa5nB,KAAK8hC,WAC7C98B,OAAO4iB,oBAAoB,YAAa5nB,KAAK+hC,YAC7C/8B,OAAO4iB,oBAAoB,UAAW5nB,KAAK6hC,SAC3C78B,OAAO4iB,oBAAoB,WAAY5nB,KAAK6hC,SAE5Cna,SAASjR,KAAKuT,MAAd,WAAoC,GAEpChqB,KAAKgiC,YAAYhiC,KAAKkvB,MAAMsR,KAAMxgC,KAAKkvB,MAAMuR,KAAMzgC,KAAKkvB,MAAM0R,MAGhE3d,SAEE,MAAMsgB,EAAM,eAAH,OAAkBvjC,KAAKkvB,MAAMsR,KAA7B,eAAwCxgC,KAAKkvB,MAAMuR,KAAnD,wBAAuEzgC,KAAKkvB,MAAM0R,KAAlF,KAEH4C,EAAO,GAAH,OAAMxjC,KAAKkvB,MAAMwR,QAAjB,cAA8B1gC,KAAKkvB,MAAMyR,QAAzC,MAIJI,EAAU,CACdlX,IAAK,GAAF,OAAK7pB,KAAK2gC,QAAU3gC,KAAKkvB,MAAMyR,QAAU3gC,KAAKkvB,MAAM0R,KAApD,MACHpX,KAAM,GAAF,OAAKxpB,KAAK0gC,QAAU1gC,KAAKkvB,MAAMwR,QAAU1gC,KAAKkvB,MAAM0R,KAApD,MACJnR,MAAO,GAAF,OAAKzvB,KAAKkhC,WAAalhC,KAAKkvB,MAAM0R,KAAlC,MACLzQ,OAAQ,GAAF,OAAKnwB,KAAKmhC,YAAcnhC,KAAKkvB,MAAM0R,KAAnC,OAER,OACE,yBAAKzd,UAAU,WACb,yBAAKA,UAAU,eAAe8G,IAAKjqB,KAAKihC,aACtC,yBAAKlS,IAAK/uB,KAAK6I,MAAM4xB,OAAQtX,UAAU,UAAU2L,IAAI,GACnD9E,MAAO,CAACyZ,UAAWF,EAAKG,gBAAiBF,GAAOvZ,IAAKjqB,KAAKy1B,QAASkO,OAAQ3jC,KAAKyhC,cAClF,yBAAKte,UAAU,gBAAgB8G,IAAKjqB,KAAKghC,SACzC,yBAAK7d,UAAU,UAAU6G,MAAO+W,EAAS9W,IAAKjqB,KAAK+gC,WAErD,yBAAK5d,UAAU,gBACb,2BAAO/a,KAAK,QAAQ+a,UAAU,SAC5BygB,KAAK,QAAQjZ,IAAK3qB,KAAKkvB,MAAM2R,QAASpN,IAAKzzB,KAAKkvB,MAAM4R,QAASzhC,MAAOW,KAAKkvB,MAAM0R,KAAMvH,SAAUr5B,KAAK0hC,YC1QnG,MAAMmC,WAAmB3+B,IAAAA,cACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXrF,IAAK,EACLL,KAAM,EACNiG,MAAO,EACPU,OAAQ,EACRI,MAAO,GAGTvwB,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKy7B,aAAez7B,KAAKy7B,aAAarV,KAAKpmB,MAI7Cy7B,aAAajS,EAAMK,EAAK4F,EAAOU,EAAQI,GACrCvwB,KAAKqvB,SAAS,CACZ7F,KAAMA,EACNK,IAAKA,EACL4F,MAAOA,EACPU,OAAQA,EACRI,MAAOA,IAIXsO,elBsGK,IAAmB7N,EAAM8S,EAAQta,EAAMK,EAAK4F,EAAOU,EAAQI,GAAxCS,EkBrGZhxB,KAAK6I,MAAMmoB,KlBqGO8S,EkBrGD9jC,KAAK6I,MAAM2lB,OlBqGFhF,EkBpGlCxpB,KAAKkvB,MAAM1F,KlBoG6BK,EkBpGvB7pB,KAAKkvB,MAAMrF,IlBoGiB4F,EkBpGZzvB,KAAKkvB,MAAMO,MlBoGQU,EkBpGDnwB,KAAKkvB,MAAMiB,OlBoGFI,EkBnG5DvwB,KAAKkvB,MAAMqB,MlBoGR,IAAIjyB,SAAQ,CAACC,EAASC,KAC3B,MAAMgzB,EAAM,IAAIC,MAChBD,EAAIE,YAAc,YAClBF,EAAI7yB,QAAWsd,IACbzd,EAAO,IAAIyH,MAAM,+BAEnBurB,EAAIG,OAAS,KAEXC,IAAIC,gBAAgBL,EAAIzC,KAExB,IAAIgD,EAASrK,SAAS2E,cAAc,UACpC0F,EAAOtC,MAAQA,EAAQc,EACvBwB,EAAO5B,OAASA,EAASI,EACzB,IAAIyB,EAAMD,EAAOE,WAAW,MAC5BD,EAAIE,uBAAwB,EAC5BF,EAAIG,UAAUX,EAAKhI,EAAMK,EAAK4F,EAAOU,EAAQ,EAAG,EAAG4B,EAAOtC,MAAOsC,EAAO5B,QAExEa,EAAOlB,GAAwBnoB,SAASqpB,GAAQA,EAAO,aAEvDe,EAAOM,QAAQD,IAEbL,EAAS,KACLK,EACF7zB,EAAQ,CAACyyB,KAAMA,EAAMoB,KAAMA,EAAM3C,MAAOA,EAAOU,OAAQA,IAEvD3xB,EAAO,IAAIyH,MAAM,+BAElB+qB,IAELQ,EAAIzC,IAAM+U,MkBhIP3kC,MAAKqyB,IACJxxB,KAAK6I,MAAMy2B,SAAS9N,EAAIR,KAAMQ,EAAIY,KAAMZ,EAAI/B,MAAO+B,EAAIrB,WAExDpqB,OAAMkW,IACLjc,KAAK6I,MAAM0f,QAAQtM,MAIzBgH,SACE,OACE,yBAAKE,UAAU,cACb,yBAAKA,UAAU,kBACb,kBAACod,GAAD,CACE9F,OAAQz6B,KAAK6I,MAAM2lB,OACnB6K,SAAUr5B,KAAKy7B,gBAEnB,yBAAKtY,UAAU,kBACZnjB,KAAK6I,MAAM8mB,SACV,4BAAQxM,UAAU,YAAYG,QAAStjB,KAAK6I,MAAM8mB,UAChD,kBAAC,EAAApM,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAEnD,KACJ,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,4BC1D5C,MAAMsgB,WAAwB7+B,IAAAA,cAC3CU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX7vB,MAAOW,KAAK6I,MAAMxJ,MAClB6jB,SAAS,GAGXljB,KAAKgkC,iBAAmBhkC,KAAKgkC,iBAAiB5d,KAAKpmB,MACnDA,KAAKikC,gBAAkBjkC,KAAKikC,gBAAgB7d,KAAKpmB,MACjDA,KAAKk5B,cAAgBl5B,KAAKk5B,cAAc9S,KAAKpmB,MAC7CA,KAAKkkC,sBAAwBlkC,KAAKkkC,sBAAsB9d,KAAKpmB,MAG/DikC,gBAAgBl/B,GACd/E,KAAKqvB,SAAS,CAAChwB,MAAO0F,EAAE+iB,OAAOzoB,QAC3BW,KAAK6I,MAAMwwB,UACbr5B,KAAK6I,MAAMwwB,SAASt0B,GAIxBi/B,iBAAiBj/B,GACfA,EAAEgjB,iBACF/nB,KAAKqvB,SAAS,CAACnM,SAAUljB,KAAKkvB,MAAMhM,UAGtCgW,cAAcn0B,GACK,IAAbA,EAAEmjB,SAEJloB,KAAKqvB,SAAS,CAAChwB,MAAOW,KAAK6I,MAAMxJ,MAAO6jB,SAAS,IAC7CljB,KAAK6I,MAAMs7B,YACbnkC,KAAK6I,MAAMs7B,cAES,IAAbp/B,EAAEmjB,SAEXloB,KAAKkkC,wBAITA,sBAAsBn/B,GACpB,GAAIA,EAAG,CACL,IAAIqjB,EAAgBrjB,EAAEqjB,cACtBnV,YAAW,KACJmV,EAAcrN,SAAS2M,SAAS0c,gBAC/BpkC,KAAK6I,MAAMs7B,YACbnkC,KAAK6I,MAAMs7B,WAAWnkC,KAAKkvB,MAAM7vB,SAGpC,QACMW,KAAK6I,MAAMs7B,YACpBnkC,KAAK6I,MAAMs7B,WAAWnkC,KAAKkvB,MAAM7vB,MAAM0tB,QAI3C9J,SACE,OACE,yBAAK6a,SAAS,KAAK3a,UAAU,cAC3B6a,OAAQh+B,KAAKkkC,uBACb,2BAAO/gB,UAAU,kBACf/a,KAAMpI,KAAKkvB,MAAMhM,QAAU,OAAS,WACpC7jB,MAAOW,KAAKkvB,MAAM7vB,MAClB+5B,YAAap5B,KAAK6I,MAAMuwB,YACxBG,SAAUv5B,KAAK6I,MAAM0wB,SAAW,WAAa,GAC7CC,UAAWx5B,KAAK6I,MAAM2wB,UAAY,YAAc,GAChD6K,aAAcrkC,KAAK6I,MAAMw7B,aACzBhL,SAAUr5B,KAAKikC,gBACf3K,UAAWt5B,KAAKk5B,gBAClB,0BAAM5V,QAAStjB,KAAKgkC,kBAClB,uBAAG7gB,UAAU,uCACVnjB,KAAKkvB,MAAMhM,QAAU,aAAe,qBCpElC,MAAMohB,WAAoBp/B,IAAAA,UACvCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKgmB,QAAU9gB,IAAAA,YAEflF,KAAKkvB,MAAQ,CACXqV,OAAQ17B,EAAM07B,OACdC,aAAc37B,EAAMxJ,OAAS,GAC7BA,MAAOwJ,EAAMxJ,OAAS,IAGxBW,KAAKikC,gBAAkBjkC,KAAKikC,gBAAgB7d,KAAKpmB,MACjDA,KAAKk5B,cAAgBl5B,KAAKk5B,cAAc9S,KAAKpmB,MAC7CA,KAAKykC,mBAAqBzkC,KAAKykC,mBAAmBre,KAAKpmB,MACvDA,KAAKkkC,sBAAwBlkC,KAAKkkC,sBAAsB9d,KAAKpmB,MAC7DA,KAAK0kC,uBAAyB1kC,KAAK0kC,uBAAuBte,KAAKpmB,MAGjEsvB,mBAAmBC,EAAW+N,GAG5B,MAAM5nB,EAAW1V,KAAK6I,MAAMxJ,OAAS,GACjCi+B,EAAUkH,cAAgB9uB,GAAa4nB,EAAUiH,QACnDvkC,KAAKqvB,SAAS,CACZmV,aAAc9uB,EACdrW,MAAOqW,IAKbuuB,gBAAgBl/B,GACd/E,KAAKqvB,SAAS,CAAChwB,MAAO0F,EAAE+iB,OAAOzoB,OAAS,KAG1C65B,cAAcn0B,GACM,KAAdA,EAAEmjB,QAEJloB,KAAKqvB,SAAS,CAAChwB,MAAOW,KAAK6I,MAAMxJ,MAAOklC,QAAQ,IACzB,KAAdx/B,EAAEmjB,SAEXloB,KAAKkkC,sBAAsBn/B,GAI9B0/B,qBACMzkC,KAAK6I,MAAMkyB,UACd/6B,KAAKqvB,SAAS,CAACkV,QAAQ,IAAO,KACxBvkC,KAAKgmB,QAAQ6B,SACf7nB,KAAKgmB,QAAQ6B,QAAQ8c,WAM7BT,sBAAsB/hC,GACpB,MAAM9C,EAAQW,KAAKkvB,MAAM7vB,MAAM0tB,QAC3B/sB,KAAK6I,MAAM0wB,UAAcp3B,EAAM2lB,OAAO8c,iBAAoBvlC,GAK9DW,KAAKqvB,SAAS,CAACkV,QAAQ,KAClBllC,GAASW,KAAK6I,MAAMxJ,QAAWA,IAAUW,KAAK6I,MAAMxJ,OACvDW,KAAK6I,MAAMs7B,WAAW9kC,IALtBW,KAAKqvB,SAAS,CAAChwB,MAAOW,KAAK6I,MAAMxJ,MAAOklC,QAAQ,IASpDG,uBAAuBrlC,GACrBW,KAAKqvB,SAAS,CAACkV,QAAQ,IACnBllC,GAAUA,IAAUW,KAAK6I,MAAMxJ,OACjCW,KAAK6I,MAAMs7B,WAAW9kC,GAI1B4jB,SACE,IAAKjjB,KAAKkvB,MAAMqV,OAAQ,CACtB,IAAIM,EAA8B,YAAnB7kC,KAAK6I,MAAMT,KAAqB,WAAapI,KAAKkvB,MAAM7vB,MACnEylC,EAAY,iBAAmB9kC,KAAK6I,MAAMkyB,SAAW,YAAc,IASvE,OARK8J,IACHA,EAAW7kC,KAAK6I,MAAMuwB,YACtB0L,GAAa,gBAEV9kC,KAAK6I,MAAMk8B,WAAqC,GAAxB/kC,KAAK6I,MAAMk8B,YACtCD,GAAa,UAGP,0BAAM3hB,UAAW2hB,EAAWxhB,QAAStjB,KAAKykC,oBAChD,8BAAOI,IAIX,IAAIG,EACJ,MAAM5R,EAAO,GAwBb,MAvBuB,YAAnBpzB,KAAK6I,MAAMT,MACb48B,EAAUjB,GACV3Q,EAAK+Q,WAAankC,KAAK0kC,yBAEnB1kC,KAAK6I,MAAMk8B,UAAY,GACzBC,EAAU,WACV5R,EAAK6R,KAAOjlC,KAAK6I,MAAMk8B,UACvB3R,EAAKjQ,UAAY,iBAEjB6hB,EAAU,QACV5R,EAAKhrB,KAAOpI,KAAK6I,MAAMT,MAAQ,QAEjCgrB,EAAK/zB,MAAQW,KAAKkvB,MAAM7vB,MACxB+zB,EAAKiG,SAAWr5B,KAAKikC,gBACrB7Q,EAAKkG,UAAYt5B,KAAKk5B,cACtB9F,EAAK4K,OAASh+B,KAAKkkC,uBAErB9Q,EAAKgG,YAAcp5B,KAAK6I,MAAMuwB,YAC9BhG,EAAKmG,SAAWv5B,KAAK6I,MAAM0wB,SAAW,WAAa,GACnDnG,EAAKiR,aAAerkC,KAAK6I,MAAMw7B,aAC/BjR,EAAKoG,WAAY,EACjBpG,EAAKnJ,IAAMjqB,KAAKgmB,QAET9gB,IAAAA,cAAoB8/B,EAAS5R,EAAM,OCjH/B,MAAM8R,WAAmBhgC,IAAAA,UACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXiW,KAAMnlC,KAAK6I,MAAMs8B,MAAQ,GACzBC,SAAU,GACVC,UAAWrlC,KAAK6I,MAAMw8B,WAGxBrlC,KAAKslC,eAAiBtlC,KAAKslC,eAAelf,KAAKpmB,MAC/CA,KAAKulC,aAAevlC,KAAKulC,aAAanf,KAAKpmB,MAC3CA,KAAKwlC,gBAAkBxlC,KAAKwlC,gBAAgBpf,KAAKpmB,MACjDA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAGd,gCAACylC,EAAWnI,GACzC,MAAM6H,EAAOM,EAAUN,MAAQ,GAC/B,OAAK7X,GAAW6X,EAAM7H,EAAU6H,OAAU7H,EAAU+H,UAG7C,KAFE,CAACF,KAAMA,GAKlBG,eAAevJ,GAEb,GADA/7B,KAAKqvB,SAAS,CAAC+V,SAAUrJ,IACrBA,EAAK94B,OAAS,EAAG,CACnB,MAAMyiC,EAAO3J,EAAKA,EAAK94B,OAAO,GACf,KAAX84B,EAAK,GAEHA,EAAK94B,OAAS,GAAa,KAARyiC,GACrB1lC,KAAKulC,aAAaxJ,EAAK3O,UAAU,EAAG2O,EAAK94B,OAAO,IAEjC,KAARyiC,GAAuB,KAARA,GAAuB,KAARA,GAAuB,KAARA,GAEtD1lC,KAAKulC,aAAaxJ,EAAK3O,UAAU,EAAG2O,EAAK94B,OAAO,GAAG8pB,SAKzDwY,aAAaI,GACX,MAAMC,EAAc5lC,KAAK6I,MAAMue,OAAOye,eAAe,c/BtB5B,I+BwBzB,GAAIF,EAAI1iC,OAAS,GAAKjD,KAAKkvB,MAAMiW,KAAKliC,OAAS2iC,EAAa,CAC1D,MAAMT,EAAOnlC,KAAKkvB,MAAMiW,KAAKjnC,MAAM,GAOnC,OANAinC,EAAK1hC,KAAKkiC,GAEV3lC,KAAKqvB,SAAS,CAAC8V,KAAMA,EAAMC,SAAU,KACjCplC,KAAK6I,MAAMi9B,eACb9lC,KAAK6I,MAAMi9B,cAAcX,GAEpBA,EAET,OAAOnlC,KAAKkvB,MAAMiW,KAGpBK,gBAAgBG,EAAKllC,GACnB,MAAM0kC,EAAOnlC,KAAKkvB,MAAMiW,KAAKjnC,MAAM,GACnCinC,EAAK/F,OAAO3+B,EAAO,GACnBT,KAAKqvB,SAAS,CAAC8V,KAAMA,IACjBnlC,KAAK6I,MAAMi9B,eACb9lC,KAAK6I,MAAMi9B,cAAcX,GAI7BtG,eAEE7+B,KAAK6I,MAAMy2B,SAASt/B,KAAKulC,aAAavlC,KAAKkvB,MAAMkW,SAASrY,SAC1D/sB,KAAKqvB,SAAS,CAACgW,WAAW,EAAOF,KAAMnlC,KAAK6I,MAAMs8B,MAAQ,KAG5DhJ,eACEn8B,KAAKqvB,SAAS,CAACgW,WAAW,EAAOD,SAAU,GAAID,KAAMnlC,KAAK6I,MAAMs8B,MAAQ,KACpEnlC,KAAK6I,MAAM8mB,UACb3vB,KAAK6I,MAAM8mB,WAIf1M,SACE,MAAM8iB,EAAe/lC,KAAK6I,MAAMue,OAAOye,eAAe,e/BhE5B,G+BiEpBG,EAAehmC,KAAK6I,MAAMue,OAAOye,eAAe,e/B/D5B,I+BiE1B,IAAIV,EAAO,GAiBX,OAhBInlC,KAAKkvB,MAAMmW,UACbrlC,KAAKkvB,MAAMiW,KAAKj6B,KAAKy6B,IACnBR,EAAK1hC,KAAK,CAAC8jB,KAAMoe,EAAKvJ,QAAUuJ,EAAI1iC,OAAS8iC,GAAgBJ,EAAI1iC,OAAS+iC,QAG5EhmC,KAAKkvB,MAAMiW,KAAKj6B,KAAKy6B,IACnBR,EAAK1hC,KAAK,0BAAM0f,UAAU,QAAQ1e,IAAK0gC,EAAKliC,QAAS0iC,OAEpC,GAAfR,EAAKliC,SACPkiC,EACE,2BACE,kBAAC,EAAA5hB,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,mDAM1D,yBAAKN,UAAU,qBACb,yBAAKA,UAAU,kBACb,2BAAOA,UAAU,SAASnjB,KAAK6I,MAAM+V,QAEtC5e,KAAKkvB,MAAMmW,UACZ,6BACE,kBAAC,EAAA9hB,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,mCAEtDwiB,GAAoB,kBAAC3J,GAAD,CACnBlV,OAAQpnB,KAAK6I,MAAMue,OACnB2V,MAAOoI,EACPtH,gBAAgB,EAChBb,OAAQiJ,EACRnI,SAAU99B,KAAK6I,MAAMi1B,SACrBF,QAAS59B,KAAKulC,aACd5H,YAAa39B,KAAKulC,aAClB5V,SAAU3vB,KAAKm8B,aACfuB,cAAe19B,KAAKwlC,gBACpBpO,WAAYp3B,KAAKslC,mBAEpBtlC,KAAK6I,MAAMy2B,UAAYt/B,KAAK6I,MAAM8mB,SACjC,yBAAKnM,GAAG,sBAAsBL,UAAU,iCACtC,4BAAQA,UAAU,UAAUG,QAAStjB,KAAKm8B,cACxC,kBAAC,EAAA5Y,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAErD,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,0BAGnD,MAGJ,yBAAKN,UAAU,UACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,cAAcG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAKqvB,SAAS,CAACgW,WAAW,MACjG,uBAAGliB,UAAU,kBAAb,QADF,KAC+C,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,6BAGtG,oCAAG0hB,MClIE,MAAMe,WAAsBhhC,IAAAA,UACzCU,YAAYiD,GACVxC,MAAMwC,GAEN,MAAMse,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC9C0O,EAAM1O,EAAMgf,gBAClBnmC,KAAKkvB,MAAQ,CACXkX,KAAMrjB,KAAAA,cAAqB/iB,KAAK6I,MAAMse,OACtC2Y,MAAOjK,GAAOA,EAAI6B,UAClB2O,SAAUlf,EAAMqQ,OAASrQ,EAAMqQ,OAAO1kB,QAAKpP,EAC3C4zB,QAASnQ,EAAMmQ,QAAUnQ,EAAMmQ,QAAQZ,QAAU,KACjD4P,YAAanf,EAAMqQ,OAASrQ,EAAMqQ,OAAO3K,UAAOnpB,EAChD8qB,OAAQwB,GAAa7I,EAAMqQ,OAASrQ,EAAMqQ,OAAOrK,MAAQ,MACzDgY,KAAMhe,EAAMge,QAAU,GACtBoB,UAAW,KACXC,cAAe,MAGjBxmC,KAAKymC,eAAiB,KAEtBzmC,KAAK0mC,UAAY1mC,KAAK0mC,UAAUtgB,KAAKpmB,MACrCA,KAAK2mC,qBAAuB3mC,KAAK2mC,qBAAqBvgB,KAAKpmB,MAC3DA,KAAK4mC,mBAAqB5mC,KAAK4mC,mBAAmBxgB,KAAKpmB,MACvDA,KAAK6mC,oBAAsB7mC,KAAK6mC,oBAAoBzgB,KAAKpmB,MACzDA,KAAK8mC,oBAAsB9mC,KAAK8mC,oBAAoB1gB,KAAKpmB,MACzDA,KAAK+mC,wBAA0B/mC,KAAK+mC,wBAAwB3gB,KAAKpmB,MACjEA,KAAKgnC,aAAehnC,KAAKgnC,aAAa5gB,KAAKpmB,MAC3CA,KAAKinC,uBAAyBjnC,KAAKinC,uBAAuB7gB,KAAKpmB,MAC/DA,KAAKknC,kBAAoBlnC,KAAKknC,kBAAkB9gB,KAAKpmB,MAGvDynB,oBACE,MAAMN,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OACpDnnB,KAAKymC,eAAiBtf,EAAMggB,cAC5BhgB,EAAMggB,cAAgBnnC,KAAK0mC,UAG7B/e,uBACgB3nB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC9CggB,cAAgBnnC,KAAKymC,eAG7BC,UAAUvB,GACRnlC,KAAKqvB,SAAS,CAAC8V,KAAMA,IAGvBwB,qBAAqB7zB,IACnBA,EAAKA,EAAGia,OAAOK,UAAU,EAAG5I,MAClBxkB,KAAKkvB,MAAMmX,WAAavzB,IAChC9S,KAAKqvB,SAAS,CAACgX,SAAUvzB,IACzB9S,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAOuF,GAAQ5Z,EAAI,QAI/Dg0B,oBAAoBpQ,GAClBA,EAAUA,EAAQ3J,OAAOK,UAAU,EAAG5I,IAClCxkB,KAAKkvB,MAAMoI,UAAYZ,IACzB12B,KAAKqvB,SAAS,CAACiI,QAASZ,IACxB12B,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAO,KAAMuP,GAAW3T,KAAAA,WAIpEgkB,wBAAwBM,IACtBA,EAAOA,EAAKta,OAAOK,UAAU,EAAG3I,OAE9BzkB,KAAKqvB,SAAS,CAACiX,YAAae,IAC5BrnC,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAOuF,GAAQ,KAAM,KAAM,KAAM2a,KAK7ET,mBAAmB5V,EAAMQ,GAEvBxxB,KAAKqvB,SAAS,CAACkX,UAAW/U,EAAKgV,cAAexV,IACzCQ,IAEHxxB,KAAKqvB,SAAS,CAACb,OAAQ,OACvBxuB,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAOuF,GAAQ,KAAM3J,KAAAA,YAKjE8jB,oBAAoB7V,EAAMoB,EAAM3C,EAAOU,GACrC,MAAMxC,EAAMyE,EAAOR,IAAIW,gBAAgBH,GAAQ,KAC/CpyB,KAAKqvB,SAAS,CAACb,OAAQb,EAAK4Y,UAAW,KAAMC,cAAe,OACxDpU,GACFpyB,KAAKgnC,aAAahW,EAAMoB,EAAM3C,EAAOU,GAMzC6W,aAAahW,EAAMoB,EAAM3C,EAAOU,GAC9B,MAAMmX,EAAiBzoB,IACrB,IAAI,KAACmS,EAAD,KAAOoB,GAAQvT,EACnB,GAAIuT,EAAKtd,KhC5DiB,KgC4DQ,CAEhC,MAAMyyB,EAAWvnC,KAAK6I,MAAMue,OAAOogB,qBAEnCxnC,KAAKqvB,SAAS,CAAC6L,WAAW,IAC1BqM,EAASE,OAAOrV,GACbjzB,MAAMwuB,IACL3tB,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAOuF,GAAQ,KAAMiB,OAE9D5nB,OAAOkW,IACNjc,KAAK6I,MAAM0f,QAAQtM,EAAK,UAEzByrB,SAAQ,KACP1nC,KAAKqvB,SAAS,CAAC6L,WAAW,YAG9Bl7B,KAAKqvB,SAAS,CAAC6L,WAAW,IAE1B1I,GAAaJ,GACVjzB,MAAK21B,IACJ,MAAM6S,EAAK3X,GAAa,CAACnpB,KAAMiuB,EAAIlC,KAAMxqB,KAAM4oB,IAC/ChxB,KAAKqvB,SAAS,CAACoL,OAAQkN,IACvB3nC,KAAK6I,MAAMu+B,kBAAkBpnC,KAAK6I,MAAMse,MAAOuF,GAAQ,KAAMib,IAC7D3nC,KAAKqvB,SAAS,CAAC6L,WAAW,QAK9BzL,EAAQrL,IAAe+L,EAAS/L,IAAeqL,GAASU,EAE1DkB,GAAYe,EAAMhO,GAAaA,GAAaE,IAA4B,GACrEnlB,MAAK01B,GAAUyS,EAAczS,KAC7B9uB,OAAMkW,GAAOjc,KAAK6I,MAAM0f,QAAQtM,EAAK,SAExCqrB,EAAc,CAACtW,KAAMA,EAAMoB,KAAMA,EAAM3C,MAAOA,EAAOU,OAAQA,IAIjE8W,uBAAuBzV,GACrBxxB,KAAKqvB,SAAS,CAACkX,UAAW,KAAMC,cAAe,OAGjDU,kBAAkB/B,GAEZ7X,GAAWttB,KAAKkvB,MAAMiW,KAAKjnC,MAAM,GAAIinC,EAAKjnC,MAAM,KAGpD8B,KAAK6I,MAAM++B,aAAazC,GAG1BliB,SACE,GAAIjjB,KAAKkvB,MAAMqX,UACb,OACE,kBAAC1C,GAAD,CACErV,OAAQxuB,KAAKkvB,MAAMqX,UACnBvV,KAAMhxB,KAAKkvB,MAAMsX,cACjBlH,SAAUt/B,KAAK6mC,oBACflX,SAAU3vB,KAAKinC,uBACf1e,QAASvoB,KAAK6I,MAAM0f,UAI1B,MAAMsf,EAAW7nC,KAAKkvB,MAAMkX,MAAQpmC,KAAKkvB,MAAM4Q,MAE/C,OACE,oCACA,yBAAK3c,UAAU,qBACb,gCACE,kBAACqX,GAAD,CACEpT,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQxuB,KAAKkvB,MAAMV,OACnBuM,UAAW8M,EACX/N,IAAK95B,KAAK6I,MAAMse,MAChBvI,MAAO5e,KAAKkvB,MAAMmX,SAClBzL,eAAgB56B,KAAK4mC,mBACrBre,QAASvoB,KAAK6I,MAAM0f,WAEvBvoB,KAAKkvB,MAAMkX,KACV,yBAAKjjB,UAAU,SACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,gCAGvD,6BACE,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,8CAEnDqkB,GAA0B,kBAACxD,GAAD,CACzBlL,YAAa0O,EACbzoC,MAAOW,KAAKkvB,MAAMmX,SAClB9M,UAAU,EACV4K,WAAYnkC,KAAK2mC,2BAKzB,oCACE,yBAAKxjB,UAAU,SACb,6BAAK,2BAAOA,UAAU,SACpB,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,4BAGxD,6BACE,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,iCAAkCC,eAAc,gDAEjEskB,GAA2B,kBAACzD,GAAD,CAC1BlL,YAAa2O,EACbhN,UAAW8M,EACXxoC,MAAOW,KAAKkvB,MAAMmX,SAClB9M,UAAU,EACV4K,WAAYnkC,KAAK2mC,2BAIzB,yBAAKxjB,UAAU,SACb,6BACE,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,uCAIvD,6BACE,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,8BAA+BC,eAAc,yCAE9DukB,GAAwB,kBAAC1D,GAAD,CACvBlL,YAAa4O,EACb3oC,MAAOW,KAAKkvB,MAAMoI,QAClB6M,WAAYnkC,KAAK8mC,2BAM5Be,GAAY7nC,KAAKkvB,MAAMoX,YACtB,yBAAKnjB,UAAU,SACb,6BAAK,2BAAOA,UAAU,SACpB,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,mCAGzD,6BACE,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,kCAClBC,eAAc,4CAEbukB,GAAwB,kBAAC1D,GAAD,CACvBlL,YAAa4O,EACbjN,UAAW8M,EACXxoC,MAAOW,KAAKkvB,MAAMoX,YAClBvB,UAAW,EACXZ,WAAYnkC,KAAK+mC,8BAIvB,MAGLc,EACC,oCACE,yBAAK1kB,UAAU,OACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,+CAElDwkB,GAAsB,kBAAC/C,GAAD,CACrB9d,OAAQpnB,KAAK6I,MAAMue,OACnBxI,MAAOqpB,EACP5C,WAAW,EACXF,KAAMnlC,KAAKkvB,MAAMiW,KACjB7F,SAAUt/B,KAAKknC,uBAGrB,OC7QK,MAAMgB,WAAoBhjC,IAAAA,UACvCU,YAAYiD,GACVxC,MAAMwC,GAEN,MAAMse,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC9C0O,EAAM1O,EAAMgf,gBAClBnmC,KAAKkvB,MAAQ,CACXkX,KAA0B,MAApBpmC,KAAK6I,MAAMse,MACjB2Y,MAAOjK,GAAOA,EAAI6B,UAClByQ,aAAchhB,EAAMihB,eAAiBjhB,EAAMihB,iBAAmB,OAAS,GACvEC,eAAe,EACfC,gBAAgB,EAChBC,QAAS,GACTpD,KAAM,IAGRnlC,KAAKwoC,yBAAsB9kC,EAE3B1D,KAAKmnC,cAAgBnnC,KAAKmnC,cAAc/gB,KAAKpmB,MAC7CA,KAAKknC,kBAAoBlnC,KAAKknC,kBAAkB9gB,KAAKpmB,MACrDA,KAAKyoC,eAAiBzoC,KAAKyoC,eAAeriB,KAAKpmB,MAC/CA,KAAK0oC,iBAAmB1oC,KAAK0oC,iBAAiBtiB,KAAKpmB,MACnDA,KAAK2oC,kBAAoB3oC,KAAK2oC,kBAAkBviB,KAAKpmB,MACrDA,KAAK4oC,kBAAoB5oC,KAAK4oC,kBAAkBxiB,KAAKpmB,MAIvDsvB,mBAAmBzmB,GACjB,MAAMse,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASxe,EAAMse,OAC1CA,IAILA,EAAM0hB,eAAiB7oC,KAAKyoC,eAExBthB,EAAMggB,eAAiBnnC,KAAKmnC,gBACP,OAAnBhgB,EAAM2hB,WACR9oC,KAAKwoC,oBAAsBrhB,EAAMggB,cACjChgB,EAAMggB,cAAgBnnC,KAAKmnC,eAE3BnnC,KAAKwoC,yBAAsB9kC,GAI3B1D,KAAKkvB,MAAM/H,OAASte,EAAMse,OAC5BnnB,KAAKqvB,SAAS,CAAClI,MAAOte,EAAMse,SAIhCQ,uBACE,MAAMR,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OACpDA,EAAM0hB,oBAAiBnlC,EACvByjB,EAAMggB,cAAgBnnC,KAAKwoC,oBAG7BC,eAAeM,GACb/oC,KAAKqvB,SAAS,CAAC8Y,YAAaY,GAAS,KAGvCL,iBAAiB3jC,GACf/E,KAAKqvB,SAAS,CAACkZ,QAASxjC,EAAE+iB,OAAOzoB,MAAOipC,gBAAgB,IAG1DK,kBAAkB5jC,GACE,KAAdA,EAAEmjB,QAEJloB,KAAKqvB,SAAS,CAACkZ,QAAS,GAAIF,eAAe,IACpB,KAAdtjC,EAAEmjB,SAEXloB,KAAK4oC,kBAAkB7jC,GAI3B6jC,kBAAkB7jC,GAChB,MAAM1F,EAAQW,KAAKkvB,MAAMqZ,QAAQxb,OACjC,IAAK1tB,EAEH,YADAW,KAAKqvB,SAAS,CAACgZ,eAAe,EAAOC,gBAAgB,IAIvD,IACIvpC,EADAmB,E7BFD,SAAiBA,GAEtB,OADAA,EAAMA,EAAI6sB,OACN,6EAA6E/Y,KAAK9T,GAC7EA,EAAI6C,QAAQ,WAAY,IAE1B,K6BHKimC,CAAQ3pC,GAEda,EACFnB,EAAS,OAETmB,E7BEC,SAAiBA,GAEtB,OADAA,EAAMA,EAAI6sB,OACN,mDAAmD/Y,KAAK9T,GACnDA,EAEF,K6BPG+oC,CAAQ5pC,GACVa,IACFnB,EAAS,UAGTA,GACFiB,KAAK6I,MAAMqgC,UAAUnqC,EAAQmB,GAC7BF,KAAKqvB,SAAS,CAACgZ,eAAe,EAAOE,QAAS,MAE9CvoC,KAAKqvB,SAAS,CAACiZ,gBAAgB,IAKnCnB,cAAchC,GACZnlC,KAAKqvB,SAAS,CAAC8V,KAAMA,IAEjBnlC,KAAKwoC,qBAAuBxoC,KAAKwoC,qBAAuBxoC,KAAKmnC,eAC/DnnC,KAAKwoC,oBAAoBrD,GAK7B+B,kBAAkB/B,GACX7X,GAAWttB,KAAKkvB,MAAMiW,KAAKjnC,MAAM,GAAIinC,EAAKjnC,MAAM,KACnD8B,KAAK6I,MAAMsgC,yBAAyBnpC,KAAK6I,MAAMse,MAAOge,GAI1DliB,SACE,MAAMklB,EAAc,GAiBpB,OAhBInoC,KAAKkvB,MAAMkX,MACbpmC,KAAKkvB,MAAMiZ,YAAYj9B,KAAKk+B,IAC1BjB,EAAY1kC,KACV,yBAAKgB,IAAK2kC,EAAKC,KAAO,IAAMD,EAAKlpC,IAAM,IAAMkpC,EAAKE,MAAOF,EAAKC,KAA9D,KAAqE,4BAAKD,EAAKlpC,KAC7E,kCAASkpC,EAAKE,KAKZ,KAJA,uBAAG/c,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM0gC,cAAcH,EAAKC,KAAMD,EAAKlpC,OACxF,kBAAC,EAAAqjB,iBAAD,CAAkBC,GAAE,6BAA8BC,eAAc,8BAFpE,IAKQ,uBAAG8I,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM2gC,aAAaJ,EAAKC,KAAMD,EAAKlpC,OAAQ,uBACvGijB,UAAU,uBAD6F,yBAQ/G,yBAAKA,UAAU,oBACb,kBAAC+iB,GAAD,CACE9e,OAAQpnB,KAAK6I,MAAMue,OACnBD,MAAOnnB,KAAK6I,MAAMse,MAClBigB,kBAAmBpnC,KAAK6I,MAAMu+B,kBAC9BQ,aAAc5nC,KAAKknC,kBACnB3e,QAASvoB,KAAK6I,MAAM0f,UACrBvoB,KAAKkvB,MAAMkX,KACV,oCACE,yBAAKjjB,UAAU,OACf,yBAAKA,UAAU,qBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,gCAG3D,yBAAKN,UAAU,UACZglB,EACAnoC,KAAKkvB,MAAMmZ,cACV,2BAAOjgC,KAAK,OAAO/I,MAAOW,KAAKkvB,MAAM7vB,MACnC8jB,UAAWnjB,KAAKkvB,MAAMoZ,eAAiB,UAAY,KACnDlP,YAAY,wBAAwBG,SAAS,WAAWC,WAAS,EACjEH,SAAUr5B,KAAK0oC,iBAAkBpP,UAAWt5B,KAAK2oC,kBAAmB3K,OAAQh+B,KAAK4oC,oBACjF,KACJ,6BACE,uBAAGrc,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAKqvB,SAAS,CAACgZ,eAAe,MAC7E,uBAAGllB,UAAU,kBAAb,OACA,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,sCAKlE,OCtKV,MAAMiB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BC,eAAgB,CACdpB,GAAI,wBACJC,eAAgB,CAAF,kCAGhBqC,uBAAwB,CACtBtC,GAAI,yBACJC,eAAgB,CAAF,qFAGhBgmB,gBAAiB,CACfjmB,GAAI,yBACJC,eAAgB,CAAF,0CAGhB6B,wBAAyB,CACvB9B,GAAI,0BACJC,eAAgB,CAAF,mGAGhB2B,aAAc,CACZ5B,GAAI,eACJC,eAAgB,CAAF,uCAGhB4B,qBAAsB,CACpB7B,GAAI,uBACJC,eAAgB,CAAF,2FAGhBimB,WAAY,CACVlmB,GAAI,oBACJC,eAAgB,CAAF,sCAGhBkmB,mBAAoB,CAClBnmB,GAAI,qBACJC,eAAgB,CAAF,qEAGhBmmB,cAAe,CACbpmB,GAAI,uBACJC,eAAgB,CAAF,iCAGhBomB,sBAAuB,CACrBrmB,GAAI,wBACJC,eAAgB,CAAF,gEAGhBqmB,YAAa,CACXtmB,GAAI,qBACJC,eAAgB,CAAF,uCAGhBsmB,oBAAqB,CACnBvmB,GAAI,sBACJC,eAAgB,CAAF,gFAGhBumB,WAAY,CACVxmB,GAAI,mBACJC,eAAgB,CAAF,2BAKlB,MAAMwmB,WAAsB/kC,IAAAA,cAC1BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkqC,kBAAoBlqC,KAAKkqC,kBAAkB9jB,KAAKpmB,MACrDA,KAAKmqC,qBAAuBnqC,KAAKmqC,qBAAqB/jB,KAAKpmB,MAC3DA,KAAKoqC,YAAcpqC,KAAKoqC,YAAYhkB,KAAKpmB,MACzCA,KAAKqqC,YAAcrqC,KAAKqqC,YAAYjkB,KAAKpmB,MACzCA,KAAKsqC,aAAetqC,KAAKsqC,aAAalkB,KAAKpmB,MAG7CkqC,kBAAkBnlC,GAChBA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcvB,GAASU,cACvBa,EAAcvB,GAASW,uBACtB,KAAQrlB,KAAK6I,MAAM0hC,cAAcvqC,KAAK6I,MAAMse,SAC7C,MACA,EACA,MAIJgjB,qBAAqBplC,GACnBA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcjmB,KAAK6I,MAAMg3B,QAAUnb,GAAS+kB,gBAAkB/kB,GAASE,gBACvEqB,EAAcjmB,KAAK6I,MAAMg3B,QAAUnb,GAASY,wBAA0BZ,GAASoB,yBAC9E,KAAQ9lB,KAAK6I,MAAM2hC,iBAAiBxqC,KAAK6I,MAAMse,SAChD,MACA,EACA,MAIJijB,YAAYrlC,GACVA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcvB,GAASglB,YACvBzjB,EAAcvB,GAASilB,qBACtB,KAAQ3pC,KAAK6I,MAAM4hC,aAAazqC,KAAK6I,MAAMse,SAC5C,MACA,EACA,MAIJkjB,YAAYtlC,GACVA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcvB,GAASklB,eACvB3jB,EAAcvB,GAASmlB,wBACtB,KAAQ7pC,KAAK6I,MAAM6hC,aAAa1qC,KAAK6I,MAAMse,SAC5C,MACA,EACA,MAIJmjB,aAAavlC,GACXA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcvB,GAASolB,aACvB7jB,EAAcvB,GAASqlB,sBACtB,KAAQ/pC,KAAK6I,MAAM8hC,cAAc3qC,KAAK6I,MAAMse,SAC7C,MACA,EACA,MAIJlE,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KACnC,OACE,yBAAK/C,UAAU,oBACb,yBAAKA,UAAU,qBACXnjB,KAAK6I,MAAMyL,QAOX,KANA,uBAAGiY,KAAK,IAAIpJ,UAAU,cAAcG,QAAStjB,KAAKmqC,sBAChD,uBAAGhnB,UAAU,kBAAb,kBADF,KAEI8C,EAAcjmB,KAAK6I,MAAMg3B,QAAUnb,GAAS+kB,gBAAkB/kB,GAASE,iBAM5E5kB,KAAK6I,MAAMi3B,MACV,uBAAGvT,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAStjB,KAAKkqC,mBACvD,uBAAG/mB,UAAU,kBAAb,UADF,KACkD8C,EAAcvB,GAASU,eAGzE,uBAAGmH,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAStjB,KAAKoqC,aACvD,uBAAGjnB,UAAU,kBAAb,eADF,KACuD8C,EAAcvB,GAASglB,aAG9E1pC,KAAK6I,MAAM+hC,WAKX,KAJA,uBAAGre,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAStjB,KAAKqqC,aACvD,uBAAGlnB,UAAU,kBAAb,SADF,KACiD8C,EAAcvB,GAASklB,gBAKxE5pC,KAAK6I,MAAMi3B,MAKX,KAJA,uBAAGvT,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAStjB,KAAKsqC,cACvD,uBAAGnnB,UAAU,kBAAb,UADF,KACkD8C,EAAcvB,GAASolB,eAM7E,yBAAK3mB,UAAU,OACf,yBAAKA,UAAU,qBACZnjB,KAAK6I,MAAM+hC,WACV,oCACE,yBAAKznB,UAAU,SACb,+BACE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,yBAA0BC,eAAc,wCAFhE,IAIW,wBAAIN,UAAU,YACrBG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMgiC,0BAA0B,UACzE7qC,KAAK6I,MAAMiiC,SAGd9qC,KAAK6I,MAAMyL,QA4BX,KA3BA,yBAAK6O,UAAU,SACb,6BACE,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,4BAA6BC,eAAc,4CAInE,yBAAKN,UAAU,UACb,sCAAW,wBAAIA,UAAWnjB,KAAK6I,MAAMi3B,MAAQ,YAAc,KACzDxc,QAAUve,IACRA,EAAEgjB,iBACE/nB,KAAK6I,MAAMi3B,OACb9/B,KAAK6I,MAAMgiC,0BAA0B,UAErC7qC,KAAK6I,MAAMqS,OAEjB,sCAAW,wBAAIiI,UAAWnjB,KAAK6I,MAAMi3B,MAAQ,YAAc,KACzDxc,QAAUve,IACRA,EAAEgjB,iBACE/nB,KAAK6I,MAAMi3B,OACb9/B,KAAK6I,MAAMgiC,0BAA0B,UAErC7qC,KAAK6I,MAAMkiC,UASzB,yBAAK5nB,UAAU,SACb,6BACE,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,oCAI3D,yBAAKN,UAAU,UACb,6BACE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,0BADjD,IAEgD,wBAAIN,UAAU,YAC1DG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMgiC,0BAA0B,UACzE7qC,KAAK6I,MAAMiiC,SAGhB,6BAAM9qC,KAAK6I,MAAMw9B,SAAWrmC,KAAK6I,MAAMw9B,SAAWpgB,EAAcvB,GAASslB,YAAzE,MACQ,wBAAI7mB,UAAU,YAClBG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMgiC,0BAA0B,WACzE7qC,KAAK6I,MAAMmiC,kBAYhC,UAAe9gB,EAAAA,EAAAA,YAAW+f,ICnPpBvlB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BjW,KAAM,CACJ8U,GAAI,mBAEJC,eAAgB,CAAF,wBAEhBwnB,QAAS,CACPznB,GAAI,sBAEJC,eAAgB,CAAF,2BAEhBynB,SAAU,CACR1nB,GAAI,uBAEJC,eAAgB,CAAF,4BAEhB2a,QAAS,CACP5a,GAAI,sBAEJC,eAAgB,CAAF,2BAEhB0nB,KAAM,CACJ3nB,GAAI,mBAEJC,eAAgB,CAAF,kCAEhB2nB,UAAW,CACT5nB,GAAI,wBACJC,eAAgB,CAAF,6BAGhB4nB,WAAY,CACV7nB,GAAI,sBACJC,eAAgB,CAAF,2BAGhB6nB,UAAW,CACT9nB,GAAI,4BACJC,eAAgB,CAAF,iCAGhB8nB,UAAW,CACT/nB,GAAI,wBACJC,eAAgB,CAAF,6BAGhB+nB,UAAW,CACThoB,GAAI,mBACJC,eAAgB,CAAF,sCAGhBoC,iBAAkB,CAChBrC,GAAI,6BACJC,eAAgB,CAAF,sCAKlB,SAASgoB,GAAMnmC,EAAKrC,GAClB,OAAOqC,GAAOA,EAAI8nB,UAAU,EAAGnqB,GAGjC,MAAMyoC,WAAiBxmC,IAAAA,UACrBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX/H,MAAO,KACP2Y,OAAO,EACP6L,OAAO,EACP/L,QAAQ,EACRC,SAAS,EACT+L,OAAO,EACPC,QAAS,KACTjB,gBAAYlnC,EACZ4Q,aAAS5Q,EACT2iC,cAAU3iC,EACV4iC,iBAAa5iC,EACb8qB,OAAQ,KACR8I,QAAS,KACTwU,gBAAiB,KACjBhB,OAAQ,KACRiB,UAAW,KACXC,SAAU,KACVhB,WAAY,KACZiB,UAAW,KACX/wB,KAAM,KACN6vB,KAAM,KACNmB,YAAa,GACb3Q,cAAe,GACf4Q,sBAAkBzoC,EAClB0oC,yBAAqB1oC,GAGvB1D,KAAKqsC,UAAYrsC,KAAKqsC,UAAUjmB,KAAKpmB,MACrCA,KAAKssC,UAAYtsC,KAAKssC,UAAUlmB,KAAKpmB,MACrCA,KAAKusC,UAAYvsC,KAAKusC,UAAUnmB,KAAKpmB,MACrCA,KAAKwsC,WAAaxsC,KAAKwsC,WAAWpmB,KAAKpmB,MACvCA,KAAKysC,cAAgBzsC,KAAKysC,cAAcrmB,KAAKpmB,MAC7CA,KAAK0sC,mBAAqB1sC,KAAK0sC,mBAAmBtmB,KAAKpmB,MACvDA,KAAK2sC,YAAc3sC,KAAK2sC,YAAYvmB,KAAKpmB,MACzCA,KAAK4sC,gBAAkB5sC,KAAK4sC,gBAAgBxmB,KAAKpmB,MACjDA,KAAK6sC,yBAA2B7sC,KAAK6sC,yBAAyBzmB,KAAKpmB,MACnEA,KAAK8sC,8BAAgC9sC,KAAK8sC,8BAA8B1mB,KAAKpmB,MAC7EA,KAAK+sC,qBAAuB/sC,KAAK+sC,qBAAqB3mB,KAAKpmB,MAC3DA,KAAKgtC,0BAA4BhtC,KAAKgtC,0BAA0B5mB,KAAKpmB,MACrEA,KAAKitC,qBAAuBjtC,KAAKitC,qBAAqB7mB,KAAKpmB,MAC3DA,KAAKktC,kBAAoBltC,KAAKktC,kBAAkB9mB,KAAKpmB,MACrDA,KAAKmtC,mBAAqBntC,KAAKmtC,mBAAmB/mB,KAAKpmB,MAIzDsvB,mBAAmBzmB,GACjB,MAAMse,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASxe,EAAMse,OAC1CA,IAIDnnB,KAAKwsC,YAAcrlB,EAAMqlB,aAC3BxsC,KAAKmsC,iBAAmBhlB,EAAMqlB,WAC9BrlB,EAAMqlB,WAAaxsC,KAAKwsC,WAExBxsC,KAAKosC,oBAAsBjlB,EAAMslB,cACjCtlB,EAAMslB,cAAgBzsC,KAAKysC,eAGzBzsC,KAAKkvB,MAAM/H,OAASte,EAAMse,QAC5BnnB,KAAKqvB,SAAS,CAAClI,MAAOte,EAAMse,QAC5BnnB,KAAKssC,UAAUnlB,EAAOte,GACtB7I,KAAKqsC,UAAUllB,EAAOte,GACtB7I,KAAKusC,UAAUplB,KAInBQ,uBACE,MAAMR,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC/CA,IAGLnnB,KAAKqvB,SAAS,CAAClI,MAAO,OACtBA,EAAMqlB,WAAaxsC,KAAKmsC,iBACxBhlB,EAAMslB,cAAgBzsC,KAAKosC,qBAG7BC,UAAUllB,EAAOte,GACf,MAAMukC,EAAW,CAAClB,YAAa,IAC/B,GAAuB,OAAnB/kB,EAAM2hB,UAAoB,CAG5B,MAAMuE,EAAQlmB,EAAMmmB,WAAWzkC,EAAMse,OACjCkmB,GACFD,EAASpC,WAAaqC,EAAMxX,IAAI0X,WAChCH,EAASnB,UAAYoB,EAAMxX,IAAI2X,YAE/BJ,EAASpC,WnCzIa,ImC0ItBoC,EAASnB,UnC1Ia,UmC6IxB9kB,EAAMsmB,aAAaC,IACjBN,EAASlB,YAAYzoC,KAAKiqC,KACzB1tC,MAGLA,KAAKqvB,SAAS+d,GAGhBd,UAAUnlB,EAAOte,GACf,MAAM8kC,EAASxmB,EAAMymB,oBAAsB,GACrC/X,EAAM1O,EAAMgf,gBAEZ9b,EAAS,GACf,GAAIlD,EAAMgR,QACR,IAAK,MAAO1zB,EAAKvE,KAAQL,OAAO4K,QAAQ0c,EAAMgR,SACxCj4B,GACFmqB,EAAO5mB,KAAKgB,GAKlBzE,KAAKqvB,SAAS,CACZyQ,MAAOjK,GAAOA,EAAI6B,UAClBiU,MAAO9V,GAAOA,EAAIgY,UAClBjO,OAAQ/J,GAAOA,EAAIiY,WACnBjO,QAAShK,GAAOA,EAAIkY,YACpBnC,MAAO/V,GAAOA,EAAIG,UAElBqQ,SAAUoF,GAAMtkB,EAAMqQ,OAASrQ,EAAMqQ,OAAO1kB,QAAKpP,EAAW8gB,IAC5D8hB,YAAamF,GAAMtkB,EAAMqQ,OAASrQ,EAAMqQ,OAAO3K,UAAOnpB,EAAW+gB,IACjE+J,OAAQwB,GAAa7I,EAAMqQ,OAASrQ,EAAMqQ,OAAOrK,MAAQ,MACzDoO,cAAelR,EACfiN,QAASmU,GAAMtkB,EAAMmQ,QAAUnQ,EAAMmQ,QAAQZ,QAAU,KAAMlS,IAC7DwpB,SAAU7mB,EAAM8mB,aAChBpC,QAAS1kB,EAAMrlB,KACf8oC,WAAYzjB,EAAM+mB,cAClB55B,QAAS6S,EAAMgnB,iBAAmBhnB,EAAMinB,KACxCtD,OAAQjV,EAAMA,EAAIE,eAAYryB,EAC9BqoC,UAAWlW,EAAMA,EAAI0X,gBAAa7pC,EAClCsoC,SAAUnW,EAAMA,EAAI2X,eAAY9pC,EAChCwX,KAAMyyB,EAAOzyB,KACb6vB,KAAM4C,EAAO5C,OAIjBwB,UAAUplB,GACR,GAAuB,OAAnBA,EAAM2hB,UACR,OAGF,MAAMjT,EAAM1O,EAAMgf,gBACdtQ,GAAOA,EAAI6B,WAEbvQ,EAAMknB,QAAQlnB,EAAMmnB,iBAAiBC,WAAWC,SAIpDhC,WAAWnF,GACT,MAAMlgB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC/CA,IAGLnnB,KAAKssC,UAAUnlB,EAAOnnB,KAAK6I,OAEvB7I,KAAKmsC,kBAAoBnsC,KAAKmsC,kBAAoBnsC,KAAKwsC,YACzDxsC,KAAKmsC,iBAAiB9E,IAI1BoF,cAAcgC,GACZ,MAAMtnB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OAC/CA,IAGLnnB,KAAKqsC,UAAUllB,EAAOnnB,KAAK6I,OAEvB7I,KAAKosC,qBAAuBpsC,KAAKosC,qBAAuBpsC,KAAKysC,eAC/DzsC,KAAKosC,oBAAoBqC,IAI7B/B,mBAAmBlb,GACjBxxB,KAAKqvB,SAAS,CAACb,OAAQgD,IACvBxxB,KAAK6I,MAAM6lC,kBAAkB1uC,KAAK6I,MAAMse,MAAOuF,GAAQ,KAAM8E,GAAOzO,KAAAA,UAAkB,MAGxF4pB,YAAYgC,EAASjT,GACnB17B,KAAKqvB,SAAS,CAACuc,MAAOlQ,IACtB17B,KAAK6I,MAAM+lC,oBAAoB5uC,KAAK6I,MAAMse,MAAOuU,EAAU,KAAO,MAGpEkR,gBAAgB+B,EAASE,GACvB7uC,KAAK6I,MAAMimC,iBAAiB9uC,KAAK6I,MAAMse,OAGzC0lB,yBAAyBkC,EAAOC,GAC9B,OAAQD,GACN,IAAK,OACH/uC,KAAK6I,MAAMomC,yBAAyBjvC,KAAK6I,MAAMse,MAAO,KAAM,KAAM,CAACjM,KAAM8zB,IACzE,MACF,IAAK,OACHhvC,KAAK6I,MAAMomC,yBAAyBjvC,KAAK6I,MAAMse,MAAO,KAAM,KAAM,CAAC4jB,KAAMiE,IACzE,MACF,IAAK,OACL,IAAK,OACHhvC,KAAK6I,MAAM+lC,oBAAoB5uC,KAAK6I,MAAMse,MAAO6nB,GACjD,MACF,IAAK,QACHhvC,KAAK6I,MAAM+lC,oBAAoB5uC,KAAK6I,MAAMse,MAAO6nB,EAAMhvC,KAAK6I,MAAMse,OAClE,MACF,IAAK,OACHnnB,KAAK6I,MAAM+lC,oBAAoB5uC,KAAK6I,MAAMse,MAAO6nB,EAAMhvC,KAAKkvB,MAAMggB,uBAItElvC,KAAKmtC,qBAGPL,8BAA8BiC,EAAOjV,GACnC,MAAM,cAAC7T,GAAiBjmB,KAAK6I,MAAMqd,KACnC,IAAIipB,EAAQC,EAAWC,EAAQC,EAAWC,EAAcpP,EAAWC,EACnE,OAAQ2O,GACN,IAAK,OACHI,EAASnvC,KAAKkvB,MAAM4b,OACpB,MACF,IAAK,OACHqE,EAASnvC,KAAKkvB,MAAM8c,SACpBoD,EAAYpvC,KAAKkvB,MAAM6c,UACnB/rC,KAAKkvB,MAAM4Q,MAEbuP,EAAS,KAGTA,EAAStsB,KAAAA,WAAAA,OAAyBA,KAAAA,WAAAA,KAAuB,OAAQ/iB,KAAKkvB,MAAM6c,YACxE/rC,KAAKkvB,MAAM5a,UAEb+6B,GAAU,MAGdC,EAAYrpB,EAAcvB,GAAS0mB,WACnCmE,EAAetpB,EAAcvB,GAAS2mB,YACtC,MACF,IAAK,QACH8D,EAASnvC,KAAKkvB,MAAM8b,WACpBoE,EAAYpvC,KAAKkvB,MAAM+c,UACvBoD,EAASrvC,KAAKkvB,MAAM0b,WAAc5qC,KAAKkvB,MAAM4Q,MAAQ,GAAK,IAAO,OACjEwP,EAAYrpB,EAAcvB,GAAS2mB,YACnCkE,EAAetpB,EAAcvB,GAAS0mB,WACtC,MACF,IAAK,OACH+D,EAASnvC,KAAKkvB,MAAMhU,KACpBm0B,EAAS,IACT,MACF,IAAK,OACHF,EAASnvC,KAAKkvB,MAAM6b,KACpBsE,EAAS,IACT,MACF,IAAK,OAAQ,CACX,MAAMloB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OACpD,IAAKA,EACH,OAEF,MAAMI,EAAOJ,EAAMmmB,WAAWxT,GAC9B,IAAKvS,IAASA,EAAKsO,IACjB,OAEFsZ,EAAS5nB,EAAKsO,IAAI0X,WAClB6B,EAAY7nB,EAAKsO,IAAI2X,UACrB6B,EAASrvC,KAAKkvB,MAAM4Q,MAAQ,GAAK,IACjCwP,EAAYrpB,EAAcvB,GAAS2mB,YACnCkE,EAAetpB,EAAcvB,GAAS0mB,WAClC7jB,EAAKiQ,SACP2I,EAAY5Y,EAAKiQ,OAAO1kB,GACxBstB,EAAa7Y,EAAKiQ,OAAOrK,OAE3B,MAEF,QAEE,YADA5f,QAAQ3O,MAAM,oCAAsCmwC,EAAQ,KAGhE/uC,KAAKqvB,SAAS,CACZ6f,sBAAuBpV,EACvB0V,qBAAsBrP,EACtBsP,sBAAuBrP,EACvBsP,kBAAmBP,EACnBQ,qBAAsBP,EACtBQ,uBAAwBN,EACxBO,0BAA2BN,EAC3BO,yBAA0BT,IAE5BrvC,KAAK6I,MAAMknC,WAAX,eAA8BhB,IAGhChC,qBAAqBhoC,GACnBA,EAAEgjB,iBACF/nB,KAAK6I,MAAMgxB,aACX75B,KAAK6I,MAAMknC,WAAW,WAGxB/C,0BAA0B5O,EAASiB,EAAO5B,GACxCz9B,KAAK6I,MAAMmnC,sBAAsBhwC,KAAK6I,MAAMse,MAAOkY,EAAO5B,GAC1Dz9B,KAAK6I,MAAMknC,WAAW,QAGxB9C,qBAAqBnT,GACnB95B,KAAKqvB,SAAS,CAACyc,gBAAiBhS,IAGlCqT,qBACE,MAAMnuC,GAAQgB,KAAK6I,MAAMonC,OAAS,QAAQpd,MAAM,KACjC,QAAX7zB,EAAK,GACPgB,KAAK6I,MAAMknC,WAAW,MACF,QAAX/wC,EAAK,GACC,QAAXA,EAAK,GACPgB,KAAK6I,MAAMknC,WAAW,QAEtB/vC,KAAK6I,MAAMknC,WAAW,YAGxB/vC,KAAK6I,MAAMknC,WAAW,QAI1B7C,kBAAkBzmB,GAChB,MAAM,cAACR,GAAiBjmB,KAAK6I,MAAMqd,KAC7BiB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAK6I,MAAMse,OACpD,IAAKA,EACH,OAEF,MAAMI,EAAOJ,EAAMmmB,WAAW7mB,EAAOS,WACrC,IAAKK,IAASA,EAAKsO,IACjB,OAGF,MAAMuQ,EAAOpmC,KAAK6I,MAAMue,OAAOgf,KAAK3f,EAAOS,WACrCgpB,EAAY,CAChB,CAACtxB,MAAOqH,EAAcvB,GAASmB,kBAAmBW,QAAS,KACzDxmB,KAAK8sC,8BAA8B1G,EAAO,OAAS,OAAQ3f,EAAOS,cAGjEkf,GACH8J,EAAUzsC,KAAK,iBAEjBysC,EAAUzsC,KAAK8jB,EAAKsO,IAAIG,UAAY,gBAAkB,eACjDoQ,GACH8J,EAAUzsC,KAAK8jB,EAAKsO,IAAII,WAAa,eAAiB,kBAExDj2B,KAAK6I,MAAMusB,gBAAgB,CACzBlO,UAAWlnB,KAAK6I,MAAMse,MACtByC,EAAGnD,EAAOmD,EACVG,EAAGtD,EAAOsD,EACVxC,KAAMd,EAAOS,WAAYgpB,GAG7BjtB,SACE,MAAMjkB,GAAQgB,KAAK6I,MAAMonC,OAAS,QAAQpd,MAAM,KAC1Csd,EAAOnxC,EAAK,GAClBA,EAAKoxC,QAEL,MAAM,cAACnqB,GAAiBjmB,KAAK6I,MAAMqd,KAC7BmqB,EAAapqB,GAAuB,QAARkqB,EAAiBzrB,GAAS,QAAU1lB,EAAK,IAAM0lB,GAASyrB,KACrFzrB,GAAQ,MAEb,OACE,yBAAKlB,GAAG,aACN,yBAAKL,UAAU,gBAAgBK,GAAG,sBAChC,yBAAKL,UAAU,cAAcK,GAAG,cAAc6sB,GAC9C,6BACE,kBAAC1U,GAAD,CAAYhM,SAAU3vB,KAAKmtC,uBAG9BntC,KAAK6I,MAAMynC,cACV,kBAAC1U,GAAD,CACEC,MAAO77B,KAAK6I,MAAM0nC,WAClBxU,KAAM/7B,KAAK6I,MAAM2nC,UACjB1U,aAAc97B,KAAK6I,MAAM0f,UAAc,KAClC,WAAR4nB,EACC,kBAAC,GAAD,CACE/oB,OAAQpnB,KAAK6I,MAAMue,OACnBgX,QAASp+B,KAAKkvB,MAAMgd,YACpB3N,eAAgBv+B,KAAK6I,MAAM4uB,SAC3B6G,oBAAqBt+B,KAAKkvB,MAAMyc,QAAU3rC,KAAKkvB,MAAM4Q,MACrDrI,SAAUz3B,KAAK6I,MAAM4uB,SACrBN,SAAUn3B,KAAK6I,MAAM4nC,mBACrB9gB,SAAU3vB,KAAKmtC,mBACf7N,SAAUt/B,KAAKgtC,4BAEX,QAARmD,GAAkBnxC,EAAKiE,OAAS,EAC9B,kBAAC,GAAD,CACEmkB,OAAQpnB,KAAK6I,MAAMue,OACnB1e,KAAM1I,KAAKkvB,MAAMwgB,kBACjBxP,QAASlgC,KAAKkvB,MAAMygB,qBACpB1P,KAAMjgC,KAAKkvB,MAAM4gB,yBACjBzP,UAAWrgC,KAAKkvB,MAAM0gB,uBACtBtP,aAActgC,KAAKkvB,MAAM2gB,0BACzB1P,UAAWngC,KAAKkvB,MAAMsgB,qBACtBrnB,KAAMnoB,KAAKkvB,MAAMggB,sBACjB9O,WAAYpgC,KAAKkvB,MAAMugB,sBACvBnQ,SAAW52B,GAAS1I,KAAK6sC,yBAAyB7tC,EAAK,GAAI0J,GAC3DinB,SAAU3vB,KAAKmtC,qBAEX,WAARgD,EACE,kBAACjI,GAAD,CACE9gB,OAAQpnB,KAAK6I,MAAMue,OACnBD,MAAOnnB,KAAK6I,MAAMse,MAClB+hB,UAAWlpC,KAAK6I,MAAMqgC,UACtBC,yBAA0BnpC,KAAK6I,MAAMsgC,yBACrCI,cAAevpC,KAAK6I,MAAM0gC,cAC1BC,aAAcxpC,KAAK6I,MAAM2gC,aACzBpC,kBAAmBpnC,KAAK6I,MAAMomC,yBAC9B1mB,QAASvoB,KAAK6I,MAAM0f,UAEhB,YAAR4nB,EACE,kBAAC,GAAD,CACEhpB,MAAOnnB,KAAK6I,MAAMse,MAClB2Y,MAAO9/B,KAAKkvB,MAAM4Q,MAClB6L,MAAO3rC,KAAKkvB,MAAMyc,MAClB/L,OAAQ5/B,KAAKkvB,MAAM0Q,OACnBC,QAAS7/B,KAAKkvB,MAAM2Q,QACpB+L,MAAO5rC,KAAKkvB,MAAM0c,MAElBhB,WAAY5qC,KAAKkvB,MAAM0b,WACvBt2B,QAAStU,KAAKkvB,MAAM5a,QACpBw2B,OAAQ9qC,KAAKkvB,MAAM4b,OACnBiB,UAAW/rC,KAAKkvB,MAAM6c,UACtBC,SAAUhsC,KAAKkvB,MAAM8c,SACrBhB,WAAYhrC,KAAKkvB,MAAM8b,WACvBiB,UAAWjsC,KAAKkvB,MAAM+c,UACtB/wB,KAAMlb,KAAKkvB,MAAMhU,KACjB6vB,KAAM/qC,KAAKkvB,MAAM6b,KAEjBpkB,YAAa3mB,KAAK6I,MAAM8d,YACxB6jB,iBAAkBxqC,KAAK6I,MAAM2hC,iBAC7BC,aAAczqC,KAAK6I,MAAM4hC,aACzBC,aAAc1qC,KAAK6I,MAAM6hC,aACzBC,cAAe3qC,KAAK6I,MAAM8hC,cAC1BE,0BAA2B7qC,KAAK8sC,8BAChCiD,WAAY/vC,KAAK6I,MAAMknC,aAEzB,yBAAKvsB,GAAG,oBAAoBL,UAAU,oBACpC,yBAAKA,UAAU,qBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,0BAA0BG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,aACzG,uBAAG5sB,UAAU,kBAAb,QADF,IAEE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,cAAeC,eAAc,2BAEnD,gCACE,kBAAC+W,GAAD,CACEpT,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQxuB,KAAKkvB,MAAMV,OACnBuM,UAAU,EACVjB,IAAK95B,KAAK6I,MAAMse,MAChBvI,MAAO5e,KAAKkvB,MAAMmX,YAEtB,yBAAKljB,UAAU,SACb,6BAAK,2BAAOA,UAAU,SACpB,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,4BAGxD,yBAAKN,UAAU,oBAAoBnjB,KAAKkvB,MAAMmX,SAC7CrmC,KAAKkvB,MAAM5a,QAAU,yBAAKya,IAAI,mBAAmB5L,UAAU,UAAU2L,IAAI,YAAe,OAE1F9uB,KAAKkvB,MAAMoI,QACV,yBAAKnU,UAAU,SACb,6BAAK,2BAAOA,UAAU,SACpB,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,uCAGrD,yBAAKN,UAAU,oBAAoBnjB,KAAKkvB,MAAMoI,UAE9C,KAEJ,yBAAKnU,UAAU,SACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,0BAD9E,IAGE,4BAAKzjB,KAAKkvB,MAAM2c,UAElB,yBAAK1oB,UAAU,SACb,kBAAC,GAAD,CAAWoY,cAAev7B,KAAKkvB,MAAMqM,iBAEtCv7B,KAAKkvB,MAAMoX,YACV,yBAAKnjB,UAAU,SACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,kCAGzD,6BAAMzjB,KAAKkvB,MAAMoX,cACV,MAEb,yBAAKnjB,UAAU,OACf,yBAAKA,UAAU,kBACb,+BACE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,6BAG1D,kBAAC+X,GAAD,CAAU15B,KAAK,IAAI45B,QAAS17B,KAAKkvB,MAAM0c,MAAOvS,SAAUr5B,KAAK2sC,eAE9D3sC,KAAKkvB,MAAM8e,SACV,yBAAK7qB,UAAU,kBACb,+BACE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,wBAAyBC,eAAc,gCAG7D,kBAAC+X,GAAD,CAAU15B,KAAK,WAAW45B,SAAS,EAAMrC,SAAUr5B,KAAK4sC,mBAG1D,KAEF,yBAAKzpB,UAAU,OACf,yBAAKA,UAAU,kBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,cAAcG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,cAC7F,uBAAG5sB,UAAU,kBAAb,YADF,IACkD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClEC,eAAc,gCAGnBzjB,KAAKkvB,MAAM0b,YAAc5qC,KAAKkvB,MAAM0Q,OACnC,oCACE,yBAAKzc,UAAU,OACf,yBAAKA,UAAU,kBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,sCAI7D,yBAAKN,UAAU,kBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,cAAcG,QAAStjB,KAAK+sC,sBAChD,uBAAG5pB,UAAU,kBAAb,cADF,KACqD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,qBACrEC,eAAc,mCAGpB,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,gCAEvDitB,GAAe,kBAAC,GAAD,CACdtpB,OAAQpnB,KAAK6I,MAAMue,OACnB+P,SAAUn3B,KAAKkvB,MAAMgd,YACrBzU,SAAUz3B,KAAK6I,MAAM4uB,SACrBkB,iBAAkB+X,EAClB1Z,cAAeh3B,KAAKkvB,MAAM4c,gBAC1BxV,YAAY,EACZ0B,YAAY,EACZlC,UAAU,EACV0C,UAAU,EACVN,gBAAiBl4B,KAAKitC,qBACtB7X,kBAAiBp1B,KAAKkvB,MAAMyc,OAAQ3rC,KAAKktC,uBAK/C,QASd,UAAehjB,EAAAA,EAAAA,YAAWwhB,ICtnBX,MAAMiF,WAAmBzrC,IAAAA,UACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX0hB,WAAY,KACZlhB,SAAU,GAGZ1vB,KAAK6wC,aAAe7wC,KAAK6wC,aAAazqB,KAAKpmB,MAC3CA,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAG7C6wC,aAAaljB,EAAKrC,EAAUwlB,GAC1B,IAAKnjB,EAEH,YADA3tB,KAAK6I,MAAM0f,QAAQ,yBAA2BoF,EAAM,KAItD,MAAMijB,EAAa5wC,KAAK6I,MAAMue,OAAOogB,qBACrCxnC,KAAKqvB,SAAS,CAACuhB,WAAYA,IAC3BA,EAAWG,SAASpjB,EAAKrC,EAAUwlB,GAChCE,IACChxC,KAAKqvB,SAAS,CAACK,SAAUshB,EAAShxC,KAAK6I,MAAMiM,UAE9CmH,IACCjc,KAAK6I,MAAM0f,QAAQtM,EAAK,UAE1B9c,MAAK,KACLa,KAAKqvB,SAAS,CAACuhB,WAAY,KAAMlhB,SAAU,OAC1C3pB,OAAOkW,IACJA,GACFjc,KAAK6I,MAAM0f,QAAQ,2BAA6BtM,EAAI9V,QAAS,OAE/DnG,KAAKqvB,SAAS,CAACuhB,WAAY,KAAMlhB,SAAU,OAI/CyM,eACMn8B,KAAK6I,MAAMqyB,UACbl7B,KAAK6I,MAAMgnB,iBACF7vB,KAAKkvB,MAAM0hB,YACpB5wC,KAAKkvB,MAAM0hB,WAAWtiB,SAI1BrL,SACE,IAAIqI,EAAWtrB,KAAK6I,MAAMyiB,UAAY,kBAClCA,EAASroB,OAAS,KACpBqoB,EAAWA,EAASxX,OAAO,EAAG,IAAM,MAAQwX,EAASxX,QAAQ,KAE/D,IAOI6Z,EAAKsjB,EAPLn8B,EAAO9U,KAAK6I,MAAMiM,KAAO,EAC3B,0BAAMqO,UAAU,cAAhB,IAA+B0H,GAAiB7qB,KAAK6I,MAAMiM,MAA3D,KACA,KAMG9U,KAAK6I,MAAMqyB,WAAcl7B,KAAKkvB,MAAM0hB,ahC6CtC,SAAuBjjB,GAC5B,OAAOA,IAAQ,kCAAkC3Z,KAAK2Z,GgC9CGujB,CAAclxC,KAAK6I,MAAMsoC,cAQ9ExjB,EAAMD,GAAY1tB,KAAK6I,MAAMsoC,aAC7BF,EAAa,OAPbtjB,EAAM,IACNsjB,EAAclsC,IACZA,EAAEgjB,iBACF/nB,KAAK6wC,aAAa7wC,KAAK6I,MAAMsoC,YAAanxC,KAAK6I,MAAMyiB,SAAUtrB,KAAK6I,MAAMioC,YAM9E,MAAMM,EAAiB,oCAAE,uBAAGjuB,UAAU,kBAAb,iBAAF,IAAkD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAC3FC,eAAc,2BAChB,OACE,yBAAKN,UAAU,cACb,6BAAK,uBAAGA,UAAU,2BAAb,sBACL,yBAAKA,UAAU,eACb,6BAAMmI,EAAN,IAAiBxW,GAChB9U,KAAK6I,MAAMqyB,WAAal7B,KAAKkvB,MAAM0hB,WAClC,kBAACphB,GAAD,CAAcE,SAAU1vB,KAAK6I,MAAMqyB,UAAYl7B,KAAK6I,MAAM6mB,SAAW1vB,KAAKkvB,MAAMQ,SAC9EC,SAAU3vB,KAAKm8B,eAEjB,6BAAMxO,EACJ,uBAAGpB,KAAMoB,EAAKojB,SAAU/wC,KAAK6I,MAAMyiB,SAAUhI,QAAS2tB,GAAaG,GAEnE,0BAAMjuB,UAAU,cAAciuB,OCnF5C,MAAM1sB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9B0sB,gBAAiB,CACf7tB,GAAI,kBACJC,eAAgB,CAAF,8BAGhB6tB,uBAAwB,CACtB9tB,GAAI,yBACJC,eAAgB,CAAF,4BAKlB,MAAM8tB,WAAuBrsC,IAAAA,cAC3B+d,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KACnC,IAAIsrB,EAEFA,EADExxC,KAAK6I,MAAMqlB,UAAYnL,KAAAA,uBACbkD,EAAcvB,GAAS2sB,iBAC1BrxC,KAAK6I,MAAMqlB,UAAYnL,KAAAA,sBACpBkD,EAAcvB,GAAS4sB,wBAEvBtxC,KAAK6I,MAAM2oC,UAAUC,mBAAmBzxC,KAAK6I,MAAMqd,KAAKwrB,OAAQ,CAAEC,UAAW,UAG3F,MAAMpnB,EAAO0D,GAAejuB,KAAK6I,MAAMqlB,UACjCgI,EAAS3L,EAAO,uBAAGpH,UAAW,wBAA0BoH,EAAKD,OAAQC,EAAKzoB,MAAY,KAE5F,OACE,0BAAMqhB,UAAU,aACbquB,EAAW,IAAUtb,IAM9B,UAAehM,EAAAA,EAAAA,YAAWqnB,I,mNC/B1B,MAAMK,WAAwB1sC,IAAAA,cAC5BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXQ,SAAU,GAGR7mB,EAAM0+B,WACR1+B,EAAM0+B,SAASsK,WAAa7xC,KAAK8xC,eAAe1rB,KAAKpmB,OAGvDA,KAAK+xC,mBAAqB/xC,KAAK+xC,mBAAmB3rB,KAAKpmB,MACvDA,KAAKgyC,sBAAwBhyC,KAAKgyC,sBAAsB5rB,KAAKpmB,MAC7DA,KAAKk1B,mBAAqBl1B,KAAKk1B,mBAAmB9O,KAAKpmB,MACvDA,KAAKiyC,mBAAqBjyC,KAAKiyC,mBAAmB7rB,KAAKpmB,MACvDA,KAAKkyC,iBAAmBlyC,KAAKkyC,iBAAiB9rB,KAAKpmB,MAEnDA,KAAKmyC,iBAAmB,CACtBlsB,cAAepd,EAAMqd,KAAKD,cAAcG,KAAKvd,EAAMqd,MACnDmN,cAAexqB,EAAMwqB,cACrBxE,aAAchmB,EAAMue,OAAOyH,aAAazI,KAAKvd,EAAMue,QACnDsM,eAAgB1zB,KAAK+xC,mBACrBhe,kBAAmB/zB,KAAKgyC,sBACxB9d,aAAcl0B,KAAKkyC,kBAIvBH,mBAAmBhtC,GACjBA,EAAEgjB,iBACF/nB,KAAK6I,MAAM6qB,eAAe,CACxB/F,IAAK5oB,EAAE+iB,OAAOiH,IACdzD,SAAUvmB,EAAE+iB,OAAOlJ,MACnB6Q,MAAO1qB,EAAE+iB,OAAOO,QAAQoH,MACxBU,OAAQprB,EAAE+iB,OAAOO,QAAQ8H,OACzBrb,KAAM/P,EAAE+iB,OAAOO,QAAQvT,KACvB1M,KAAMrD,EAAE+iB,OAAOO,QAAQ2I,OAI3BghB,sBAAsBjtC,GACpBA,EAAEgjB,iBACF,MAAMlhB,EAAO,CACX6hB,IAAK1oB,KAAK6I,MAAM6f,IAElB7hB,KAAY,IACR9B,EAAE+iB,OAAOO,QAAQvmB,OACnB+E,EAAKurC,KAAKrtC,EAAE+iB,OAAOO,QAAQvmB,MAAQiD,EAAE+iB,OAAOO,QAAQnoB,IAAM6E,EAAE+iB,OAAOO,QAAQnoB,SAChDwD,IAAzBqB,EAAE+iB,OAAOO,QAAQnoB,IAAoB,EAAI,GAAK6E,EAAE+iB,OAAOO,QAAQnoB,KAEvC,OAAxB6E,EAAE+iB,OAAOO,QAAQgqB,MACnBxrC,EAAKojB,IAAMyD,GAAY3oB,EAAE+iB,OAAOO,QAAQ4B,MAAQ,eAElD,MAAM8R,EAAOh3B,EAAE+iB,OAAOO,QAAQzJ,OAAS,UACvC5e,KAAK6I,MAAMypC,eAAevtC,EAAE+iB,OAAOO,QAAQgqB,IAAKtW,EAAMl1B,GAGxDquB,mBAAmBnwB,GACjBA,EAAEgjB,iBACFhjB,EAAEijB,kBACF,MAAMkoB,EAAY,GACdlwC,KAAK6I,MAAMqlB,UAAYnL,OAAOwvB,uBAChCrC,EAAUzsC,KAAK,wBAEbzD,KAAK6I,MAAM2pC,cACXxyC,KAAK6I,MAAMqlB,SAAWnL,OAAOwvB,uBAC7BvyC,KAAK6I,MAAMqlB,SAAWnL,OAAO0vB,0BAC/BvC,EAAUzsC,KAAK,mBAEjBysC,EAAUzsC,KAAK,qBAEfzD,KAAK6I,MAAMusB,gBAAgB,CACzB1M,IAAK1oB,KAAK6I,MAAM6f,IAChBtF,QAASpjB,KAAK6I,MAAMua,QACpB+F,SAAUnpB,KAAK6I,MAAMsgB,SACrBC,SAAUppB,KAAK6I,MAAMugB,SACrBW,EAAGhlB,EAAEswB,MACLzL,EAAG7kB,EAAEuwB,MACLpM,UAAWlpB,KAAK6I,MAAMqgB,WACrBgnB,GAGL4B,eAAeY,GACb1yC,KAAKqvB,SAAS,CAACK,SAAUgjB,IAG3BT,qBACEjyC,KAAK6I,MAAMgnB,eAAe7vB,KAAK6I,MAAM6f,IAAK1oB,KAAK6I,MAAM0+B,UAGvD2K,iBAAiBntC,GACfA,EAAEgjB,iBACFhjB,EAAEijB,kBACF,MAAM2qB,EAAa3yC,KAAK6I,MAAM8pC,WAC1BA,GACF3yC,KAAK6I,MAAMqrB,aAAaye,GAI5B1vB,SACE,MAAM2vB,EAAY5yC,KAAK6I,MAAMgqC,SAAW,KAAO7yC,KAAK6I,MAAMuI,SAAW,OAAS,SACxE0hC,EAAsC,UAAvB9yC,KAAK6I,MAAMgqC,UAA+C,QAAvB7yC,KAAK6I,MAAMgqC,SAAsB,aAAe,SAClGrkB,EAASxuB,KAAK6I,MAAMu3B,aAAc,EAClC2S,EAAe/yC,KAAK6I,MAAM4lB,SAAWzuB,KAAK6I,MAAMuI,WAC5B,UAAvBpR,KAAK6I,MAAMgqC,UAA+C,QAAvB7yC,KAAK6I,MAAMgqC,UAEjD,IAAIzvB,EAAUpjB,KAAK6I,MAAMua,QACzB,MAAM4vB,EAAc,GACpB,GAAIhzC,KAAK6I,MAAMmkB,UAAYwG,GAAAA,OAAAA,kBAA2BA,GAAAA,OAAAA,QAAepQ,GAAU,CAC7EoQ,GAAAA,OAAAA,YAAmBpQ,GAAS,CAAC6vB,EAAKztC,KAChB,oBAAZytC,EAAIjiB,MAKRgiB,EAAYvvC,KAAK,kBAACktC,GAAD,CACfvpB,OAAQpnB,KAAK6I,MAAMue,OACnB+pB,YAAa3d,GAAAA,OAAAA,eAAsByf,GACnC3nB,SAAU2nB,EAAInxC,KACdo5B,UAAW1H,GAAAA,OAAAA,aAAoByf,GAC/BnC,SAAUmC,EAAIjiB,KACdlc,KAAM0e,GAAAA,OAAAA,cAAqByf,GAC3BvjB,SAAU1vB,KAAKkvB,MAAMQ,SACrBG,eAAgB7vB,KAAKiyC,mBACrB1pB,QAASvoB,KAAK6I,MAAM0f,QACpB9jB,IAAKe,OACNxF,MACH,MAAMkzC,EAAO1f,GAAAA,OAAAA,OAAcpQ,EAASwQ,GAAe5zB,KAAKmyC,kBACxD/uB,EAAUle,IAAAA,cAAoBA,IAAAA,SAAgB,KAAMguC,OACzB,iBAAX9vB,IAChBA,EAAU,oCAAE,uBAAGD,UAAU,uBAAb,iBAAF,IAAuD,uBAAGA,UAAU,QAC5E,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClBC,eAAc,wCAGpB,OACE,wBAAIwG,IAAKjqB,KAAK6I,MAAMsqC,SAAUhwB,UAAWyvB,GACtC5yC,KAAK6I,MAAM4lB,SAAWzuB,KAAK6I,MAAMuI,SAChC,yBAAK+R,UAAU,cACZ4vB,EACC,kBAACxkB,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBD,MAAOnnB,KAAK6I,MAAMsgB,SAClBvK,MAAO5e,KAAK6I,MAAMugB,SAClBoF,OAAQA,IACV,MAEJ,KACF,6BACE,yBAAKrL,UAAW2vB,GACd,yBAAK3vB,UAAU,gBACb,yBAAKA,UAAU,mBACZC,EACA4vB,GAEFhzC,KAAK6I,MAAM2oC,UACV,kBAAC,GAAD,CACEA,UAAWxxC,KAAK6I,MAAM2oC,UACtBtjB,SAAUluB,KAAK6I,MAAMqlB,WACrB,MAELluB,KAAK6I,MAAMusB,gBACV,0BAAMjS,UAAU,eACd,uBAAGoJ,KAAK,IAAIjJ,QAAStjB,KAAKk1B,oBACxB,uBAAG/R,UAAU,kBAAb,iBAEM,MAGb4vB,EACC,yBAAK5vB,UAAU,UACZnjB,KAAK6I,MAAMugB,UACV,2BAAG,kBAAC,EAAA7F,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,iCAIzD,QAQd,MAAM2vB,IAAkBlpB,EAAAA,EAAAA,YAAW0nB,IAGnC,GAFoB1sC,IAAAA,YAAiB,CAAC2D,EAAOohB,IAAQ,kBAACmpB,GAAD,IAAiBD,SAAYlpB,GAASphB,MCzLrFwqC,GAAiB,IAYR,MAAMC,WAAsBpuC,IAAAA,cACzCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXqkB,SAAS,EACTC,YAAa,KACbC,WAAW,EACXC,QAAQ,EACRC,SAAU,QAGZ3zC,KAAK4zC,UAAY5zC,KAAK4zC,UAAUxtB,KAAKpmB,MACrCA,KAAK6zC,mBAAqB7zC,KAAK6zC,mBAAmBztB,KAAKpmB,MACvDA,KAAK8zC,aAAe9zC,KAAK8zC,aAAa1tB,KAAKpmB,MAC3CA,KAAK+zC,YAAc/zC,KAAK+zC,YAAY3tB,KAAKpmB,MACzCA,KAAKg0C,aAAeh0C,KAAKg0C,aAAa5tB,KAAKpmB,MAC3CA,KAAKi0C,WAAaj0C,KAAKi0C,WAAW7tB,KAAKpmB,MAEvCA,KAAKk0C,eAAiB,EACtBl0C,KAAKm0C,UAAY,KACjBn0C,KAAKo0C,WAAa,GAClBp0C,KAAKq0C,UAAYnvC,IAAAA,YAGnBuiB,oBACEznB,KAAKs0C,cAAgBt0C,KAAKq0C,UAAUxsB,QAAQoK,WAAW,MACvDjyB,KAAKs0C,cAAcC,QAAU,QAE7Bv0C,KAAKs0C,cAAcrS,UAAU,GAAK,IAElCjiC,KAAKw0C,OAAS,KACdx0C,KAAKy0C,cAAgB,KACrBz0C,KAAK00C,aAAe,KACpB10C,KAAK20C,WAAa,KAClB30C,KAAK40C,WAAa,KAClB50C,KAAK60C,SAAW,KAEhB70C,KAAK80C,YAAc,GAGnB,IACE3+B,UAAU4+B,aAAaC,aAAa,CAACC,OAAO,EAAMC,OAAO,IAAQ/1C,KAAKa,KAAK6zC,mBAAoB7zC,KAAK6I,MAAM0f,SAC1G,MAAOtM,GACPjc,KAAK6I,MAAM0f,QAAQtM,IAKvB23B,YACE,MAAMuB,EAAU,IAAI9hC,WAAWrT,KAAK60C,SAASO,mBACvC3lB,EAAQzvB,KAAKq0C,UAAUxsB,QAAQ4H,MAC/BU,EAASnwB,KAAKq0C,UAAUxsB,QAAQsI,OAChCklB,EAAc5lB,EAAQ,EAA0B,EAChD6lB,EAAejC,GAAiBgC,EAEtCr1C,KAAKs0C,cAAciB,UA9DL,UA+Ddv1C,KAAKs0C,cAAckB,UAzEJ,EA0Efx1C,KAAKs0C,cAAcmB,YApEL,UAsEd,IAAIC,EAAe,EACnB,MAAMC,EAAY,KAChB,IAAK31C,KAAKm0C,UACR,OAEFnvC,OAAO4wC,sBAAsBD,GAE7B,MAAMhC,EAAW3zC,KAAKk0C,gBAAkB7mC,KAAKD,MAAQpN,KAAKm0C,WAE1Dn0C,KAAKqvB,SAAS,CAACskB,SAAUnpB,GAAcmpB,EAAW,OAG9CA,EA1EW,MA2Eb3zC,KAAKy0C,cAAcoB,QACnB71C,KAAKk0C,gBAAkB7mC,KAAKD,MAAQpN,KAAKm0C,UACzCn0C,KAAKm0C,UAAY,KACjBn0C,KAAKqvB,SAAS,CAACkkB,SAAS,EAAOE,WAAW,EAAOE,SAAUnpB,GAAcxqB,KAAKk0C,eAAiB,QAMjGl0C,KAAK60C,SAASiB,sBAAsBX,GACpC,IAAIY,EAAS,EACb,IAAK,MAAMC,KAAab,EACtBY,IAAWC,EAAY,MAAQ,EAEjCD,EAASt0B,KAAK6hB,KAAKyS,EAAOZ,EAAQlyC,QAElC,IAAIgzC,EAAYtC,EAAWN,GAAkB,EAE7C,MAAM6C,EAAKZ,EAAe3B,EAAW,GAClCA,EAAWN,GAAiB4C,GAAY5C,GAAzC,EAEEqC,GAAgBO,IAClBP,EAAeO,EAEfj2C,KAAKo0C,WAAW3wC,KAAKsyC,GACjB/1C,KAAKo0C,WAAWnxC,OAASoyC,GAE3Br1C,KAAKo0C,WAAWhE,SAKpBpwC,KAAKs0C,cAAc6B,SAAS,EAAG,EAAG1mB,EAAOU,GAGzCnwB,KAAKs0C,cAAc8B,YACnB,IAAK,IAAI5wC,EAAI,EAAGA,EAAIxF,KAAKo0C,WAAWnxC,OAAQuC,IAAK,CAC/C,IAAIokB,EAAQ,EAAJpkB,EAA6B0wC,EACjCnsB,EAAI/pB,KAAKo0C,WAAW5uC,GAvHd,GAuH+B2qB,EAEzCnwB,KAAKs0C,cAAc+B,OAAOzsB,EAAkB,IAAduG,EAASpG,IACvC/pB,KAAKs0C,cAAcgC,OAAO1sB,EAAY,GAATuG,EAAmB,GAAJpG,GAG9C/pB,KAAKs0C,cAAciC,UAGrBZ,IAGF5B,YAAYhvC,GACVA,EAAEgjB,iBACF/nB,KAAKy0C,cAAcoB,QACnB71C,KAAKk0C,gBAAkB7mC,KAAKD,MAAQpN,KAAKm0C,UACzCn0C,KAAKm0C,UAAY,KACjBn0C,KAAKqvB,SAAS,CAACokB,WAAW,EAAOE,SAAUnpB,GAAcxqB,KAAKk0C,eAAiB,OAGjFJ,aAAa/uC,GACXA,EAAEgjB,iBACE/nB,KAAKkvB,MAAMqkB,UACbvzC,KAAKm0C,UAAY9mC,KAAKD,MACtBpN,KAAKy0C,cAAc+B,SACnBx2C,KAAKqvB,SAAS,CAACokB,WAAW,GAAOzzC,KAAK4zC,YAI1CI,aAAajvC,GACXA,EAAEgjB,iBACF/nB,KAAKk0C,eAAiB,EACtBl0C,KAAKm0C,UAAY,KACjBn0C,KAAKy0C,cAAcgC,OACnBz2C,KAAKy0C,cAAgB,KACrBz0C,KAAKqvB,SAAS,CAACokB,WAAW,IAG5BQ,WAAWlvC,GACTA,EAAEgjB,iBACF/nB,KAAKqvB,SAAS,CAACokB,WAAW,IACtBzzC,KAAKm0C,YACPn0C,KAAKk0C,gBAAkB7mC,KAAKD,MAAQpN,KAAKm0C,UACzCn0C,KAAKm0C,UAAY,MAGfn0C,KAAKy0C,eACPz0C,KAAKy0C,cAAcgC,OAIvB5C,mBAAmBW,GACjBx0C,KAAKw0C,OAASA,EACdx0C,KAAKy0C,cAAgB,IAAIiC,cAAclC,GAGvCx0C,KAAK00C,aAAe,IAAIiC,aACxB32C,KAAK20C,WAAa30C,KAAK00C,aAAaC,WACpC30C,KAAK40C,WAAa50C,KAAK00C,aAAakC,wBAAwBpC,GAC5Dx0C,KAAK60C,SAAW70C,KAAK00C,aAAamC,iBAClC72C,KAAK60C,SAASiC,QA7LE,IA8LhB92C,KAAK40C,WAAWmC,QAAQ/2C,KAAK60C,UAE7B70C,KAAKy0C,cAAcuC,OAAS/vC,IAC1B,MAAMmrB,EAAO,IAAIsC,KAAK10B,KAAK80C,YAAa,CAAE,KAAS,0BACnD90C,KAAK80C,YAAc,GACnB,MAAMnnB,EAAM3oB,OAAO4sB,IAAIW,gBAAgBH,GACnCpyB,KAAKk0C,eAtLM,IAuLbl0C,KAAK6I,MAAMs7B,WAAWxW,EAAK3tB,KAAKk0C,gBAEhCl0C,KAAK6I,MAAMouC,aAIfj3C,KAAKy0C,cAAcyC,gBAAmBnyC,IAChCA,EAAE8B,KAAKiO,KAAO,GAChB9U,KAAK80C,YAAYrxC,KAAKsB,EAAE8B,OAI5B7G,KAAKk0C,eAAiB,EACtBl0C,KAAKm0C,UAAY9mC,KAAKD,MACtBpN,KAAKy0C,cAAc0C,QACnBn3C,KAAK4zC,YAGP3wB,SACE,MAAMm0B,EAAc,mBAAqBp3C,KAAKkvB,MAAMqkB,QAAU,MAAQ,QACtE,OACE,yBAAKpwB,UAAU,SACb,uBAAGoJ,KAAK,IAAIjJ,QAAStjB,KAAKg0C,aAAcp1B,MAAM,UAC5C,uBAAGuE,UAAU,kBAAb,mBAEF,4BAAQA,UAAU,aAAasM,MAAM,MAAMU,OAAO,KAAKlG,IAAKjqB,KAAKq0C,YACjE,yBAAKlxB,UAAU,YAAYnjB,KAAKkvB,MAAMykB,UACrC3zC,KAAKkvB,MAAMukB,UACV,uBAAGlnB,KAAK,IAAIjJ,QAAStjB,KAAK+zC,YAAan1B,MAAM,SAC3C,uBAAGuE,UAAU,kBAAb,yBAEF,uBAAGoJ,KAAK,IAAIjJ,QAAStjB,KAAK8zC,aAAcl1B,MAAM,UAC5C,uBAAGuE,UAAWi0B,GAAd,wBAGJ,uBAAG7qB,KAAK,IAAIjJ,QAAStjB,KAAKi0C,WAAYr1B,MAAM,QAC1C,uBAAGuE,UAAU,kBAAb,WCrOV,MAAMuB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9B0yB,mBAAoB,CAClB7zB,GAAI,4BACJC,eAAgB,CAAF,sCAGhB6zB,iBAAkB,CAChB9zB,GAAI,qBACJC,eAAgB,CAAF,+BAGhB8zB,kBAAmB,CACjB/zB,GAAI,uBACJC,eAAgB,CAAF,iCAGhB+zB,0BAA2B,CACzBh0B,GAAI,4BACJC,eAAgB,CAAF,uIAGhBg0B,uBAAwB,CACtBj0B,GAAI,8BACJC,eAAgB,CAAF,kDAKlB,MAAMi0B,WAAoBxyC,IAAAA,cACxBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXyoB,MAAO,KACPxxC,QAAS,GACTyxC,UAAU,EACVC,eAAgB1hC,UAAU4+B,aAAaC,aAEvC8C,mBAAmB,IAAIzqC,MAAO0qC,UAAY9zB,GAAiB,GAG7DjkB,KAAKg4C,iBAAmBh4C,KAAKg4C,iBAAiB5xB,KAAKpmB,MACnDA,KAAKi4C,kBAAoBj4C,KAAKi4C,kBAAkB7xB,KAAKpmB,MACrDA,KAAKk4C,iBAAmBl4C,KAAKk4C,iBAAiB9xB,KAAKpmB,MACnDA,KAAKm4C,kBAAoBn4C,KAAKm4C,kBAAkB/xB,KAAKpmB,MACrDA,KAAKo4C,WAAap4C,KAAKo4C,WAAWhyB,KAAKpmB,MACvCA,KAAKq4C,eAAiBr4C,KAAKq4C,eAAejyB,KAAKpmB,MAC/CA,KAAKs4C,oBAAsBt4C,KAAKs4C,oBAAoBlyB,KAAKpmB,MAEzDA,KAAKkyC,iBAAmBlyC,KAAKkyC,iBAAiB9rB,KAAKpmB,MAEnDA,KAAKu4C,YAAcv4C,KAAKu4C,YAAYnyB,KAAKpmB,MAG3CynB,oBACMznB,KAAKw4C,iBACPx4C,KAAKw4C,gBAAgB11B,iBAAiB,QAAS9iB,KAAKg4C,kBAAkB,GAGxEh4C,KAAKqvB,SAAS,CAACsoB,MAAO33C,KAAKu4C,gBAG7B5wB,uBACM3nB,KAAKw4C,iBACPx4C,KAAKw4C,gBAAgB5wB,oBAAoB,QAAS5nB,KAAKg4C,kBAAkB,GAI7E1oB,mBAAmBC,GACbvvB,KAAKw4C,iBACPx4C,KAAKw4C,gBAAgB7T,QAGnBpV,EAAUrK,OAASllB,KAAK6I,MAAMqc,OAChCllB,KAAKqvB,SAAS,CAACsoB,MAAO33C,KAAKu4C,gBAI/BA,cACE,OAAOv4C,KAAK6I,MAAMqc,MAChBsO,GAAAA,OAAAA,OAAcxzB,KAAK6I,MAAMqc,MAAM9B,QAAS2R,GAAgB,CACtD9O,cAAejmB,KAAK6I,MAAMqd,KAAKD,cAAcG,KAAKpmB,KAAK6I,MAAMqd,MAC7D2I,aAAc7uB,KAAK6I,MAAMue,OAAOyH,aAAazI,KAAKpmB,KAAK6I,MAAMue,UAC1D,KAGT4wB,iBAAiBjzC,GACX/E,KAAK6I,MAAM4vC,U9BqGZ,SAAoBt2C,EAAOu2C,EAAgBC,EAAqBpwB,GACrE,MAAMhlB,GAASpB,EAAMy2C,eAAiBz2C,EAAM02C,cAAcD,eAAiB,IAAIr1C,MAC/E,IAAKvF,MAAMuvB,QAAQhqB,GACjB,OAAO,EAGT,IAAK,IAAIiC,KAAKjC,EAAO,CACnB,MAAM4kB,EAAO5kB,EAAMiC,GACnB,GAAkB,SAAd2iB,EAAK2wB,KAAiB,CACxB,MAAMC,EAAO5wB,EAAK6wB,YAClB,IAAKD,EAAM,CACTxrC,QAAQ3O,MAAM,kDAAmDupB,EAAK2wB,KAAM3wB,EAAK/f,MACjFmgB,EAAQ,mDACR,SAQF,OANIwwB,EAAK3wC,MAAmC,SAA3B2wC,EAAK3wC,KAAKyqB,MAAM,KAAK,GACpC6lB,EAAeK,GAEfJ,EAAoBI,IAGf,GAIX,OAAO,E8B1HDE,CAAWl0C,GACb,CAAC6tB,EAAM5B,EAAMvB,EAAOU,EAAQY,KAC1B/wB,KAAK6I,MAAMqwC,cAAcloB,EAAM4B,EAAMnD,EAAOU,EAAQY,MAEtD,CAACC,EAAM4B,EAAM7B,KACX/wB,KAAK6I,MAAMswC,aAAanoB,EAAM4B,EAAM7B,KAEtC/wB,KAAK6I,MAAM0f,UAGXxjB,EAAEgjB,iBAINkwB,kBAAkBlzC,GACZA,EAAE+iB,OAAO6S,OAAS51B,EAAE+iB,OAAO6S,MAAM13B,OAAS,GAC5CjD,KAAK6I,MAAMqwC,cAAcn0C,EAAE+iB,OAAO6S,MAAM,IAG1C51B,EAAE+iB,OAAOzoB,MAAQ,GAGnB64C,iBAAiBnzC,GACXA,EAAE+iB,OAAO6S,OAAS51B,EAAE+iB,OAAO6S,MAAM13B,OAAS,GAC5CjD,KAAK6I,MAAMswC,aAAap0C,EAAE+iB,OAAO6S,MAAM,IAGzC51B,EAAE+iB,OAAOzoB,MAAQ,GAGnB84C,kBAAkBxqB,EAAKgmB,GACrB3zC,KAAKqvB,SAAS,CAACuoB,UAAU,IACzBrqC,QAAQkB,IAAI,2BAA4Bkf,EAAKgmB,GAI/CyE,WAAWrzC,GACTA,EAAEgjB,iBACF,MAAM5hB,EAAUnG,KAAKkvB,MAAM/oB,QAAQ4mB,QAC/B5mB,GAAWnG,KAAK6I,MAAMuwC,aAAep5C,KAAK6I,MAAMwwC,WAClDr5C,KAAK6I,MAAMywC,cAAcnzC,GACzBnG,KAAKqvB,SAAS,CAAClpB,QAAS,MAK5BkyC,eAAetzC,GACb,GAAI/E,KAAKkvB,MAAM0oB,SAIb,OAFA7yC,EAAEgjB,sBACFhjB,EAAEijB,kBAKU,UAAVjjB,EAAEN,MAECM,EAAEw0C,WACLx0C,EAAEgjB,iBACFhjB,EAAEijB,kBAEFhoB,KAAKo4C,WAAWrzC,KAKtBuzC,oBAAoBvzC,GAClB,MAAMqoC,EAAW,CAACjnC,QAASpB,EAAE+iB,OAAOzoB,OACpC,GAAIW,KAAK6I,MAAM2wC,WAAY,CACzB,MAAMpsC,GAAM,IAAIC,MAAO0qC,UACnB3qC,EAAMpN,KAAKkvB,MAAM4oB,kBAAoB7zB,KACvCjkB,KAAK6I,MAAM2wC,aACXpM,EAAS0K,kBAAoB1qC,GAGjCpN,KAAKqvB,SAAS+d,GAGhB8E,iBAAiBntC,GAGf,GAFAA,EAAEgjB,iBACFhjB,EAAEijB,kBACEhoB,KAAK6I,MAAMqc,OAASllB,KAAK6I,MAAMqrB,aAAc,CAC/C,MAAMye,EAAa3yC,KAAK6I,MAAMqc,MAAMwD,IACpC1oB,KAAK6I,MAAMqrB,aAAaye,IAI5B1vB,SACE,MAAM,cAAEgD,GAAkBjmB,KAAK6I,MAAMqd,KAC/B8W,EAASh9B,KAAK6I,MAAM4vC,SACxBxyB,EAAcvB,GAAS2yB,oBACtBr3C,KAAK6I,MAAM4wC,cACVxzB,EAAcvB,GAAS1kB,KAAK6I,MAAM4wC,gBAClCxzB,EAAcvB,GAAS4yB,kBAErBK,EACH,yBAAKn0B,GAAG,uBACP,yBAAKL,UAAU,UACb,uBAAGoJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM6wC,kBAAmB,uBAAGv2B,UAAU,uBAAb,WAEhFnjB,KAAKkvB,MAAMyoB,OAGhB,OACE,yBAAKn0B,GAAG,wBACLxjB,KAAKkvB,MAAMyoB,QAAU33C,KAAK6I,MAAMwwC,QAAU1B,EAAQ,KACnD,yBAAKn0B,GAAG,sBACJxjB,KAAK6I,MAAM4vC,SAwCX,yBAAKj1B,GAAG,oBAAoBwZ,GAvC5B,oCACGh9B,KAAK6I,MAAMswC,eAAiBn5C,KAAKkvB,MAAM0oB,SACtC,oCACE,uBAAGrrB,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK25C,YAAYC,SAAWh7B,MAAM,aACjF,uBAAGuE,UAAU,4BAAb,UAEF,uBAAGoJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK65C,WAAWD,SAAWh7B,MAAM,eAChF,uBAAGuE,UAAU,4BAAb,iBAIJ,KACDnjB,KAAK6I,MAAMwwC,QACTr5C,KAAKkvB,MAAMyoB,MAAQA,EAAQ,yBAAKx0B,UAAU,YAC1CnjB,KAAKkvB,MAAM0oB,SACV,kBAACtE,GAAD,CACE2D,UAAWhwC,GAAKjH,KAAKqvB,SAAS,CAACuoB,UAAU,IACzCzT,WAAYnkC,KAAKm4C,oBACnB,8BAAU30B,GAAG,cAAc4V,YAAa4D,EACtC39B,MAAOW,KAAKkvB,MAAM/oB,QAASkzB,SAAUr5B,KAAKs4C,oBAC1CkB,WAAYx5C,KAAKq4C,eACjBpuB,IAAMA,IAASjqB,KAAKw4C,gBAAkBvuB,GACtCuP,WAAS,IACdx5B,KAAKkvB,MAAM/oB,UAAYnG,KAAKkvB,MAAM2oB,aACjC,uBAAGtrB,KAAK,IAAIjJ,QAAStjB,KAAKo4C,WAAYx5B,MAAM,QAC1C,uBAAGuE,UAAU,kBAAb,SAEDnjB,KAAKkvB,MAAM0oB,SAIV,KAHA,uBAAGrrB,KAAK,IAAIjJ,QAASve,IAAMA,EAAEgjB,iBAAkB/nB,KAAKqvB,SAAS,CAACuoB,UAAU,KAASh5B,MAAM,SACrF,uBAAGuE,UAAU,kBAAb,QAIN,2BAAO/a,KAAK,OAAO6hB,IAAMA,IAASjqB,KAAK65C,WAAa5vB,GAClDoP,SAAUr5B,KAAKk4C,iBAAkBluB,MAAO,CAAC8vB,QAAS,UACpD,2BAAO1xC,KAAK,OAAO6hB,IAAMA,IAASjqB,KAAK25C,YAAc1vB,GAAO+Q,OAAO,UACjE3B,SAAUr5B,KAAKi4C,kBAAmBjuB,MAAO,CAAC8vB,QAAS,cAWnE,UAAe5vB,EAAAA,EAAAA,YAAWwtB,ICnPX,MAAMqC,WAAmB70C,IAAAA,cACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKg6C,cAAgBh6C,KAAKg6C,cAAc5zB,KAAKpmB,MAG/Cg6C,cAAcC,GACZj6C,KAAK6I,MAAMqxC,UACXl6C,KAAK6I,MAAMywC,cAAct5C,KAAK6I,MAAMua,QAAQ21B,MAG9C91B,SACE,OAAKjjB,KAAK6I,MAAMua,QAId,yBAAKI,GAAG,iBACN,yBAAKA,GAAG,+BACN,8BAAOxjB,KAAK6I,MAAMua,QAAQthB,MAC1B,uBAAGyqB,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMqxC,YAAa,uBAAG/2B,UAAU,uBAAb,WAE3E,yBAAKK,GAAG,2BACN,yBAAKL,UAAU,sBACb,uBAAGA,UAAU,uBA/BzB,SAAsB6N,GAEpB,MAAMmpB,EAAa,CAACC,QAAS,oBAAqBv7B,MAAO,QAASkd,KAAM,cAAemZ,MAAO,YAE9F,OAAOiF,EAAWnpB,IAASmpB,GAAYnpB,GAAQ,IAAI6B,MAAM,KAAK,KAAOsnB,EAAU,QA2BjCE,CAAar6C,KAAK6I,MAAMua,QAAQhb,OACpE,6BAAK,2BAAG,kBAAC,EAAAmb,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,iCAA7D,IAC8C4H,GAAgBrrB,KAAK6I,MAAMua,QAAQthB,KAAM,KAAO,KAC9F,6BAAK,2BAAG,kBAAC,EAAAyhB,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,oCAAhE,IAC2DzjB,KAAK6I,MAAMua,QAAQhb,MAAQ,4BACtF,6BAAK,2BAAG,kBAAC,EAAAmb,iBAAD,CAAkBC,GAAE,aAAcC,eAAc,4BAAxD,IAC4CoH,GAAiB7qB,KAAK6I,MAAMua,QAAQtO,SAGpF,kBAAC,GAAD,CACEukC,SAAS,EACTjyB,OAAQpnB,KAAK6I,MAAMue,OACnBlC,MAAOllB,KAAK6I,MAAMqc,MAClBw0B,cAAe15C,KAAK6I,MAAM6wC,cAC1BJ,cAAet5C,KAAKg6C,cACpBzxB,QAASvoB,KAAK6I,MAAM0f,WAzBjB,MClBE,MAAM+xB,WAAkBp1C,IAAAA,UACrCU,YAAYiD,GACVxC,MAAMwC,GAGRoa,SACE,MAAMs3B,EAAc,GACdC,GAAcx6C,KAAK6I,MAAM4kC,aAAe,IAAIxqC,OAC5Cw3C,EAAch5B,KAAKkJ,I1CmEM,E0CnEmB6vB,GAelD,OAbCx6C,KAAK6I,MAAM4kC,aAAe,IAAIiN,MAAMhN,IACnC6M,EAAY92C,KACV,yBAAK0f,UAAU,aAAa1e,IAAKipC,EAAInmB,MACnC,kBAACgH,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBD,MAAOumB,EAAInmB,KACXiH,OAAQwB,GAAa0d,EAAIlW,OAASkW,EAAIlW,OAAOrK,MAAQ,QAAS,EAC9DvO,MAAO8uB,EAAIlW,OAASkW,EAAIlW,OAAO1kB,GAAK,SAGnCynC,EAAYt3C,QAAUw3C,KAI7B,yBAAKj3B,GAAG,eAAe+2B,EAAvB,IAAqCC,EAAaC,EAChD,8BACE,kBAAC,EAAAl3B,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,sEAErD1Y,OAAQ,CAAE4vC,SAAWH,EAAaC,MAC9B,OC/BH,MAAMG,WAAqB11C,IAAAA,cACxCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXO,MAAO,EACPU,OAAQ,GAGVnwB,KAAK66C,gBAAkB76C,KAAK66C,gBAAgBz0B,KAAKpmB,MAGnD86C,YAAYC,GACV,GAAIA,IAAS/6C,KAAKkvB,MAAMO,MAAO,CAC7B,MAAMhG,EAASsxB,EAAK5Y,wBACpBniC,KAAKqvB,SAAS,CACZI,MAAsB,EAAfhG,EAAOgG,MACdU,OAAwB,EAAhB1G,EAAO0G,UAKrB0qB,gBAAgBZ,GACdj6C,KAAK6I,MAAMqxC,UACXl6C,KAAK6I,MAAMywC,cAAcW,EAASj6C,KAAK6I,MAAMua,QAAQgP,MAGvDnP,SACE,IAAKjjB,KAAK6I,MAAMua,QACd,OAAO,KAGT,MAAM0O,EAAM5B,GAAalwB,KAAK6I,MAAMua,QAAQqM,MAAOzvB,KAAK6I,MAAMua,QAAQ+M,OACpEnwB,KAAKkvB,MAAMO,MAAOzvB,KAAKkvB,MAAMiB,QAAQ,GACjCrb,EAAOgd,EAAM,CAAErC,MAAOqC,EAAItB,SAAW,KAAML,OAAQ2B,EAAIrB,UAAY,MACrEzwB,KAAK6I,MAAMua,QAAQqM,MAAQzvB,KAAK6I,MAAMua,QAAQ+M,OAAU,CAACV,MAAO,QAAU,CAACU,OAAQ,QACvFrb,EAAKsb,SAAW,OAChBtb,EAAKub,UAAY,OAGjB,MAAM2qB,EAAYv5B,KAAKgS,KAAMzzB,KAAKkvB,MAAMO,MAAQtL,GAAW,IAAO,GAAK,EAAG,IACpE4M,EAAQ1F,GAAgBrrB,KAAK6I,MAAMua,QAAQthB,KAAMk5C,IAAc,IAE/DvrB,EAAQzvB,KAAK6I,MAAMua,QAAQqM,OAAS,IACpCU,EAASnwB,KAAK6I,MAAMua,QAAQ+M,QAAU,IAC5C,OACE,yBAAK3M,GAAG,iBACN,yBAAKA,GAAG,+BACJxjB,KAAK6I,MAAMywC,cAMX,8BAAOvoB,GALP,uBAAGxE,KAAMvsB,KAAK6I,MAAMua,QAAQuK,IAAKojB,UAAU,GACzC,uBAAG5tB,UAAU,kBAAb,iBADF,IACkD,kBAAC,EAAAI,iBAAD,CAC9CC,GAAE,kBAAmBC,eAAc,+BAKzC,uBAAG8I,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMqxC,YAAa,uBAAG/2B,UAAU,uBAAb,WAE3E,yBAAKK,GAAG,0BAA0ByG,IAAM8wB,GAAS/6C,KAAK86C,YAAYC,IAChE,yBAAKhsB,IAAK/uB,KAAK6I,MAAMua,QAAQuK,IAAK3D,MAAOlV,EAAMqO,UAAU,gBAAgB2L,IAAK9uB,KAAK6I,MAAMua,QAAQthB,QAElG9B,KAAK6I,MAAMywC,cACV,kBAAC,GAAD,CACEG,cAAc,oBACdL,aAAa,EACbhyB,OAAQpnB,KAAK6I,MAAMue,OACnBlC,MAAOllB,KAAK6I,MAAMqc,MAClBw0B,cAAe15C,KAAK6I,MAAM6wC,cAC1BJ,cAAet5C,KAAK66C,gBACpBtyB,QAASvoB,KAAK6I,MAAM0f,UAEtB,yBAAK/E,GAAG,wBACN,6BACE,6BAAK,2BAAG,kBAAC,EAAAD,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,kCAE7D,6BAAK,0BAAM7E,MAAO5e,KAAK6I,MAAMua,QAAQthB,MAAOivB,KAE9C,6BACE,6BAAK,2BAAG,kBAAC,EAAAxN,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,qCAEhE,6BAAMzjB,KAAK6I,MAAMua,QAAQhb,OAE3B,6BACE,6BAAK,2BAAG,kBAAC,EAAAmb,iBAAD,CAAkBC,GAAE,aAAcC,eAAc,6BAExD,6BAAMgM,EAAN,MAAsBU,EAAtB,QAAmCtF,GAAiB7qB,KAAK6I,MAAMua,QAAQtO,WCzFtE,MAAMmmC,WAAmB/1C,IAAAA,cACtCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKk7C,mBAAqBl7C,KAAKk7C,mBAAmB90B,KAAKpmB,MAGzDk7C,mBAAmBvoB,EAAK9rB,GACtB8rB,EAAI5K,iBACJ/nB,KAAK6I,MAAMyf,SAASzhB,GAGtBoc,SACE,OACE,yBAAKE,UAAU,uBACb,yBAAKA,UAAU,SACb,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClBC,eAAc,sFAGlB,yBAAKN,UAAU,UACb,4BAAQA,UAAU,UAAUG,QAASnhB,IAAWnC,KAAKk7C,mBAAmB/4C,EAAO,YAC7E,kBAAC,EAAAohB,iBAAD,CAAkBC,GAAE,yBAClBC,eAAc,6BAElB,4BAAQN,UAAU,YAAYG,QAASnhB,IAAWnC,KAAKk7C,mBAAmB/4C,EAAO,YAC/E,kBAAC,EAAAohB,iBAAD,CAAkBC,GAAE,yBAClBC,eAAc,6BAElB,4BAAQN,UAAU,YAAYG,QAASnhB,IAAWnC,KAAKk7C,mBAAmB/4C,EAAO,WAC/E,kBAAC,EAAAohB,iBAAD,CAAkBC,GAAE,wBAClBC,eAAc,+BC3Bb,MAAM03B,WAAiBj2C,IAAAA,cACpC+d,SACE,MAAMlhB,EAAU6hB,GAAW,KAAOb,KAAAA,aAAsB,IACxD,OACE,yBAAKS,GAAG,cACN,6BACE,uBAAG+I,KAAK,mCACN,yBAAK/I,GAAG,OAAOsL,IAAI,OAAOC,IAAI,iBAC9B,2CAEF,2BAAG,kBAAC,EAAAxL,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,6BAArD,IAC+C1hB,GAC/C,2BAAG,kBAAC,EAAAwhB,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,6BAArD,IAC+CzjB,KAAK6I,MAAMuyC,cAD1D,KAC2Ep7C,KAAK6I,MAAMwyC,cADtF,QCfK,MAAMC,WAAoBp2C,IAAAA,cACvCU,YAAYiD,GACVxC,MAAMwC,GAGRoa,SACE,IAAIG,EAAU,KACV0vB,EAAc,SAclB,OAbI9yC,KAAK6I,MAAM+lB,SAEbxL,EAAU,oCAAE,uBAAGD,UAAU,uBAAb,SAAF,IAA+C,uBAAGA,UAAU,QACpE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClBC,eAAc,uCAElBqvB,GAAe,YACN9yC,KAAK6I,MAAM0yC,OAEpBn4B,EAAU,oCAAGpjB,KAAK6I,MAAM0yC,MACxBzI,GAAe,SAGZ1vB,EAKH,wBAAID,UAAU,QACZ,yBAAKA,UAAW2vB,GACd,yBAAK3vB,UAAU,mBACZC,KAPA,oCAAG,OCvBD,MAAMo4B,GACA,oBAAC3vB,GAElB,MAAM4vB,EAAQ5vB,EAAKgH,MAAM,IAAK,GACxBpM,EAAS,GACf,IAAIi1B,EAAO,GAYX,OAXID,EAAM,KACRC,EAAOD,EAAM,GAAG14C,QAAQ,IAAK,IAAI8vB,MAAM,MAErC4oB,EAAM,IACRA,EAAM,GAAG5oB,MAAM,KAAKlzB,SAAQ,SAASg8C,GACnC,MAAMxzB,EAAOwzB,EAAK9oB,MAAM,KACpB1K,EAAK,KACP1B,EAAOm1B,mBAAmBzzB,EAAK,KAAOyzB,mBAAmBzzB,EAAK,QAI7D,CAACuzB,KAAMA,EAAMj1B,OAAQA,GAGb,kBAACkH,GAChB3oB,OAAO62C,SAAShwB,KAAO8B,EAGJ,sBAAC+tB,EAAMj1B,GAC1B,IAAIkH,EAAM+tB,EAAK7tC,KAAK,KACpB,MAAM7O,EAAO,GACb,IAAK,MAAMyF,KAAOgiB,EACZA,EAAOxhB,eAAeR,IACxBzF,EAAKyE,KAAKgB,EAAM,IAAMgiB,EAAOhiB,IAMjC,OAHIzF,EAAKiE,OAAS,IAChB0qB,GAAO,IAAM3uB,EAAK6O,KAAK,MAElB8f,EAGS,mBAAC9B,EAAMpnB,EAAKpF,GAC5B,MAAMy8C,EAAS97C,KAAK+7C,aAAalwB,GAEjC,OADAiwB,EAAOr1B,OAAOhiB,GAAOpF,EACdW,KAAKg8C,eAAeF,EAAOJ,KAAMI,EAAOr1B,QAG5B,sBAACoF,EAAMpnB,GAC1B,MAAMq3C,EAAS97C,KAAK+7C,aAAalwB,GAEjC,cADOiwB,EAAOr1B,OAAOhiB,GACdzE,KAAKg8C,eAAeF,EAAOJ,KAAMI,EAAOr1B,QAG3B,uBAACoF,EAAMowB,GAC3B,MAAMH,EAAS97C,KAAK+7C,aAAalwB,GAEjC,OADAiwB,EAAOJ,KAAK,GAAKO,EACVj8C,KAAKg8C,eAAeF,EAAOJ,KAAMI,EAAOr1B,QAG3B,uBAACoF,EAAMqwB,GAC3B,MAAMJ,EAAS97C,KAAK+7C,aAAalwB,GAMjC,OALIqwB,EACFJ,EAAOr1B,OAAO/X,KAAOwtC,SAEdJ,EAAOr1B,OAAO/X,KAEhB1O,KAAKg8C,eAAeF,EAAOJ,KAAMI,EAAOr1B,QAG/B,mBAACoF,EAAM1E,GACvB,MAAM20B,EAAS97C,KAAK+7C,aAAalwB,GAIjC,OAHAiwB,EAAOJ,KAAK,GAAKv0B,SAEV20B,EAAOr1B,OAAO/X,KACd1O,KAAKg8C,eAAeF,EAAOJ,KAAMI,EAAOr1B,SCrEnD,MAAM+M,GAASzQ,KAAAA,OA2BT2B,IAAWC,EAAAA,EAAAA,gBAAe,CAC9Bw3B,WAAY,CACV34B,GAAI,aACJC,eAAgB,CAAF,8BAGhB24B,UAAW,CACT54B,GAAI,sBACJC,eAAgB,CAAF,6BAGhB44B,UAAW,CACT74B,GAAI,kBACJC,eAAgB,CAAF,6BAGhBnP,QAAS,CACPkP,GAAI,UACJC,eAAgB,CAAF,2BAGhB+zB,0BAA2B,CACzBh0B,GAAI,4BACJC,eAAgB,CAAF,uIAGhB64B,gBAAiB,CACf94B,GAAI,kBACJC,eAAgB,CAAF,qCAMlB,SAAS84B,GAAc1mB,GACrB,GAAIA,EAAK,CACP,MAAM2mB,EAAK3mB,EAAI4mB,gBAAkB,GACjC,OAAO5mB,EAAII,SAAS,WAAaumB,EAAG70C,SAAS,MAAQ60C,EAAG70C,SAAS,MAEnE,OAAO,EAGT,SAAS+0C,GAAiB7mB,GACxB,GAAIA,EAAK,CACP,MAAM7iB,EAAK6iB,EAAI8mB,cAAgB,GAC/B,OAAO9mB,EAAII,SAAS,UAAYjjB,EAAGrL,SAAS,MAAQqL,EAAGrL,SAAS,MAElE,OAAO,EAGT,MAAMi1C,WAAqB13C,IAAAA,UACzBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ0tB,GAAaC,yBAAyBh0C,EAAO,IAE1D7I,KAAK88C,MAAQ98C,KAAK88C,MAAM12B,KAAKpmB,MAC7BA,KAAK+8C,YAAc/8C,KAAK+8C,YAAY32B,KAAKpmB,MACzCA,KAAKg9C,UAAYh9C,KAAKg9C,UAAU52B,KAAKpmB,MACrCA,KAAKi9C,oBAAsBj9C,KAAKi9C,oBAAoB72B,KAAKpmB,MACzDA,KAAKk9C,mBAAqBl9C,KAAKk9C,mBAAmB92B,KAAKpmB,MACvDA,KAAKm9C,aAAen9C,KAAKm9C,aAAa/2B,KAAKpmB,MAC3CA,KAAK4c,UAAY5c,KAAK4c,UAAUwJ,KAAKpmB,MACrCA,KAAKo9C,sBAAwBp9C,KAAKo9C,sBAAsBh3B,KAAKpmB,MAC7DA,KAAKq9C,kBAAoBr9C,KAAKq9C,kBAAkBj3B,KAAKpmB,MACrDA,KAAKs9C,iBAAmBt9C,KAAKs9C,iBAAiBl3B,KAAKpmB,MACnDA,KAAKu9C,kBAAoBv9C,KAAKu9C,kBAAkBn3B,KAAKpmB,MACrDA,KAAKw9C,oBAAsBx9C,KAAKw9C,oBAAoBp3B,KAAKpmB,MACzDA,KAAKy9C,0BAA4Bz9C,KAAKy9C,0BAA0Br3B,KAAKpmB,MACrEA,KAAK09C,kBAAoB19C,KAAK09C,kBAAkBt3B,KAAKpmB,MACrDA,KAAK29C,oBAAsB39C,KAAK29C,oBAAoBv3B,KAAKpmB,MACzDA,KAAK49C,mBAAqB59C,KAAK49C,mBAAmBx3B,KAAKpmB,MACvDA,KAAK69C,mBAAqB79C,KAAK69C,mBAAmBz3B,KAAKpmB,MACvDA,KAAKk1B,mBAAqBl1B,KAAKk1B,mBAAmB9O,KAAKpmB,MACvDA,KAAK89C,6BAA+B99C,KAAK89C,6BAA6B13B,KAAKpmB,MAC3EA,KAAK+9C,wBAA0B/9C,KAAK+9C,wBAAwB33B,KAAKpmB,MACjEA,KAAKg+C,iBAAmBh+C,KAAKg+C,iBAAiB53B,KAAKpmB,MACnDA,KAAKk4C,iBAAmBl4C,KAAKk4C,iBAAiB9xB,KAAKpmB,MACnDA,KAAKi4C,kBAAoBj4C,KAAKi4C,kBAAkB7xB,KAAKpmB,MACrDA,KAAKiyC,mBAAqBjyC,KAAKiyC,mBAAmB7rB,KAAKpmB,MACvDA,KAAKi+C,qBAAuBj+C,KAAKi+C,qBAAqB73B,KAAKpmB,MAC3DA,KAAKk+C,uBAAyBl+C,KAAKk+C,uBAAuB93B,KAAKpmB,MAC/DA,KAAKm+C,kBAAoBn+C,KAAKm+C,kBAAkB/3B,KAAKpmB,MAErDA,KAAKo+C,gBAAkBp+C,KAAKo+C,gBAAgBh4B,KAAKpmB,MACjDA,KAAKq+C,kBAAoBr+C,KAAKq+C,kBAAkBj4B,KAAKpmB,MACrDA,KAAKkyC,iBAAmBlyC,KAAKkyC,iBAAiB9rB,KAAKpmB,MAEnDA,KAAKs+C,gBAAkB,GACvBt+C,KAAKu+C,sBAAwBv+C,KAAKu+C,sBAAsBn4B,KAAKpmB,MAE7DA,KAAKw+C,sBAAwB,GAC7Bx+C,KAAKy+C,sBAAwB,KAG/BF,sBAAsBG,GACpB,GAAI1+C,KAAKs+C,gBAAgBr5C,eAAey5C,GACtC,OAAO1+C,KAAKs+C,gBAAgBI,GAE9B,MAAMz0B,EAAM/kB,IAAAA,YAEZ,OADAlF,KAAKs+C,gBAAgBI,GAASz0B,EACvBA,EAGTxC,oBACMznB,KAAK2+C,kBACP3+C,KAAK2+C,iBAAiB77B,iBAAiB,SAAU9iB,KAAKq9C,mBAI1D11B,uBACM3nB,KAAK2+C,kBACP3+C,KAAK2+C,iBAAiB/2B,oBAAoB,SAAU5nB,KAAKq9C,mBAI3Dr9C,KAAKk+C,yBAKP5uB,mBAAmBC,EAAW+N,IACxBt9B,KAAK2+C,kBACNrhB,EAAUnW,OAASnnB,KAAKkvB,MAAM/H,OAASmW,EAAUshB,cAAgB5+C,KAAKkvB,MAAM0vB,cAEzE5+C,KAAKkvB,MAAM2vB,eA/HQ,MAgIrB7+C,KAAK2+C,iBAAiBG,UAAY9+C,KAAK2+C,iBAAiBI,aACtD/+C,KAAKkvB,MAAM2vB,eACX7+C,KAAK2+C,iBAAiBK,cAI5B,MAAM73B,EAAQnnB,KAAK6I,MAAMue,OAASpnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,YAASzjB,EAC7E1D,KAAKkvB,MAAM/H,OAASmW,EAAUnW,QAC5BmW,EAAUnW,QAAUpE,KAAAA,oBAA2Bua,EAAUnW,QAC3DnnB,KAAK88C,MAAMxf,EAAUnW,OAGnBA,IACFA,EAAM83B,OAASj/C,KAAKw9C,oBACpBr2B,EAAM+3B,sBAAwBl/C,KAAKy9C,0BACnCt2B,EAAMg4B,OAASn/C,KAAK09C,kBACpBv2B,EAAMqlB,WAAaxsC,KAAKs9C,iBACxBn2B,EAAMslB,cAAgBzsC,KAAKu9C,kBAC3Bp2B,EAAMi4B,OAASp/C,KAAKu9C,oBAInBv9C,KAAK6I,MAAMw2C,mBAKdr/C,KAAKi+C,qBAAqB,GAH1Bj+C,KAAKk+C,0BAMH/2B,GAAWnnB,KAAKkvB,MAAM/H,OAASmW,EAAUnW,OAAWoI,EAAU+vB,QAC5Dn4B,EAAMo4B,KACRhyC,QAAQkB,IAAI,kCAGZzO,KAAK4c,UAAUuK,IAKU,gCAACse,EAAWnI,GACzC,IAAIkiB,EAAY,GAChB,GAAK/Z,EAAUte,OAwBR,GAAIse,EAAUte,OAASmW,EAAUnW,MAAO,CAC7C,MAAMA,EAAQse,EAAUre,OAAOC,SAASoe,EAAUte,OAElD,IAAIjC,EAAQ,KACZ,GAAIugB,EAAUga,eAAgB,CAG5Bv6B,EAAQ,CACN9B,QAFcqiB,EAAUga,eAAehqB,QAGvC/M,IAAK,MAiBT,GAbA82B,EAAY,CACVr4B,MAAOse,EAAUte,MACjBu4B,WAAY,KACZC,aAAc,KACdC,cAAe,KACfC,iBAAiB,EACjBhB,eAAgB,EAChBiB,kBAAkB,EAClB56B,MAAOA,EACP66B,oBAAoB,EACpBnxB,QAASzH,EAAMoR,UAGbpR,EAAO,CAET,MAAMsnB,EAAO,GAEThJ,EAAUxN,WACZ9Q,EAAMsmB,aAAaC,IACbA,EAAInY,QAAUmY,EAAInmB,MAAQke,EAAUhO,UACtCgX,EAAKhrC,KAAKiqC,MAKhB7tC,OAAO2P,OAAOgwC,EAAW,CACvBQ,WAAYvR,IAGVtnB,EAAMqQ,OACR33B,OAAO2P,OAAOgwC,EAAW,CACvB5gC,MAAOuI,EAAMqQ,OAAO1kB,GACpB0b,OAAQwB,GAAa7I,EAAMqQ,OAAOrK,SAGpCttB,OAAO2P,OAAOgwC,EAAW,CACvB5gC,MAAO,GACP4P,OAAQ,OAIZ,MAAMyxB,EAAO94B,EAAM+4B,cACfD,EACFpgD,OAAO2P,OAAOgwC,EAAW,CACvBW,sBAAuBzD,GAAiBuD,EAAKpqB,OAEtCyH,EAAU6iB,uBACnBtgD,OAAO2P,OAAOgwC,EAAW,CACvBW,uBAAuB,IAG3BtgD,OAAO2P,OAAOgwC,EAAW,CACvBZ,aAAcz3B,EAAMy3B,eACpBwB,cAAej5B,EAAMk5B,aACrB/rC,QAAS6S,EAAMgnB,uBAIjBtuC,OAAO2P,OAAOgwC,EAAW,CACvBZ,aAAc,EACdwB,eAAgB,EAChBJ,WAAY,GACZphC,MAAO,GACP4P,OAAQ,KACR2xB,uBAAuB,EACvB7rC,SAAS,UApGbkrC,EAAY,CACVZ,aAAc,EACdwB,eAAgB,EAChBJ,WAAY,GACZ74B,MAAO,KACPvI,MAAO,GACP4P,OAAQ,KACRkH,YAAY,EACZC,SAAS,EACTC,aAAa,EACbhH,SAAS,EACT8wB,WAAY,KACZC,aAAc,KACdC,cAAe,KACfC,iBAAiB,EACjBhB,eAAgB,EAChBiB,kBAAkB,EAClBK,uBAAuB,EACvB7rC,SAAS,EACT4Q,MAAO,KACP66B,oBAAoB,GA0HxB,OArCIta,EAAU5P,KACR4P,EAAU5P,IAAImE,YAAcsD,EAAUtD,WACxCwlB,EAAUxlB,UAAYsD,EAAUtD,UAE9ByL,EAAU5P,IAAIyqB,YAAchjB,EAAUgjB,WACxCd,EAAUc,UAAYhjB,EAAUgjB,WAE7B7a,EAAU5P,IAAIyqB,SAAS,UAAYhjB,EAAUijB,iBAChDf,EAAUe,gBAAkBjjB,EAAUijB,gBAEpC9a,EAAU5P,IAAIiY,YAAcxQ,EAAUwQ,WACxC0R,EAAU1R,UAAYxQ,EAAUwQ,YAG9BxQ,EAAUtD,WACZwlB,EAAUxlB,UAAW,GAEnBsD,EAAUgjB,WACZd,EAAUc,UAAW,GAElBhjB,EAAUijB,iBACbjjB,EAAUijB,gBAAiB,GAEzBjjB,EAAUwQ,WACZ0R,EAAU1R,UAAW,IAIrByO,GAAc9W,EAAU5P,OAASyH,EAAUkjB,cAC7ChB,EAAUiB,aAAenjB,EAAUkjB,cAIhC/a,EAAUxN,WAAaqF,EAAU0iB,YAAc1iB,EAAU0iB,WAAW/8C,OAAS,IAChFu8C,EAAUQ,WAAa,IAGlBR,EAGT5iC,UAAUuK,GACR,GAAIA,EAAMu5B,iBAAmB1gD,KAAK6I,MAAMy2C,MACtC,OAIF,MAAMqB,EAAY3gD,KAAK6I,MAAM+3C,gBAAkB5gD,KAAK6I,MAAM+3C,eAAeC,YAAc7gD,KAAK6I,MAAMse,MAIlG,IAAI25B,EAAW35B,EAAMmnB,iBAAiByS,gBAAgBC,gBAClDhhD,KAAKkvB,MAAMoxB,UAAYK,KAEzBG,EAAWA,EAASG,chD/SG,IgDgTnBjhD,KAAKkvB,MAAMoxB,WACbQ,EAAWA,EAASI,gBAGtBlhD,KAAKqvB,SAAS,CAAEywB,kBAAkB,KAGpC,MAAMqB,EAAWR,EAAW3gD,KAAK6I,MAAM+3C,oBAAiBl9C,EACxDyjB,EAAMvK,UAAUkkC,EAAStS,QAAS2S,GAC/BhiD,MAAM6nB,IACY,KAAbA,EAAK9gB,MAKLlG,KAAKkvB,MAAM/H,OAASH,EAAKG,OAC3BnnB,KAAKqvB,SAAS,CAAClI,MAAOH,EAAKG,QAE7BnnB,KAAK6I,MAAMu4C,kBAAkBphD,KAAK6I,MAAMse,MAAOH,EAAKG,OAEpDA,EAAMk6B,gBAAgBC,KACfA,EAAIC,UAAYp6B,EAAMu5B,gBACzB1gD,KAAKg9C,UAAUsE,OAVjB9F,GAAegG,WAAWhG,GAAeiG,YAAY,GAAIz6B,EAAKP,OAAOU,WAcxEphB,OAAOkW,IACN1O,QAAQ3O,MAAM,yBAA0BoB,KAAKkvB,MAAM/H,OACnDnnB,KAAK6I,MAAM0f,QAAQtM,EAAI9V,QAAS,OAChC,MAAMu7C,EAAa9E,GAAaC,yBAAyB,GAAI,IAC7D6E,EAAW9iC,MAAQ5e,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAAS23B,WAC1Dr8C,KAAKqvB,SAASqyB,MAIpB5E,MAAM6E,GACJ,IAAKA,IAAiB3hD,KAAK6I,MAAMue,OAAOw6B,cAAcD,GACpD,OAEF,MAAME,EAAW7hD,KAAK6I,MAAMue,OAAOC,SAASs6B,GACxCE,GAAYA,EAASnB,gBACvBmB,EAAS/E,OAAM,GACZ/2C,OAAM,SACN2hC,SAAQ,KAGP1nC,KAAKqvB,SAAS,CAACywB,kBAAkB,IACjC+B,EAAS5C,YAASv7C,EAClBm+C,EAAS3C,2BAAwBx7C,EACjCm+C,EAAS1C,YAASz7C,EAClBm+C,EAASrV,gBAAa9oC,EACtBm+C,EAASpV,mBAAgB/oC,EACzBm+C,EAASzC,YAAS17C,KAM1B05C,sBAAsBrC,GAChBA,IACFA,EAAKj4B,iBAAiB,SAAU9iB,KAAKq9C,mBACrCr9C,KAAK2+C,iBAAmB5D,EACxB/6C,KAAK2+C,iBAAiBG,UAAY9+C,KAAK2+C,iBAAiBI,aAAe/+C,KAAKkvB,MAAM2vB,gBAKtFxB,kBAAkBl7C,GAChB,MAAM2/C,EAAM3/C,EAAM2lB,OAAOi3B,aAAe58C,EAAM2lB,OAAOg3B,UAAY38C,EAAM2lB,OAAOk3B,aAM9E,GALAh/C,KAAKqvB,SAAS,CACZwvB,eAAgBiD,EAChB/B,mBAAoB+B,EAlZG,OAqZrB9hD,KAAKkvB,MAAM4wB,kBAIX39C,EAAM2lB,OAAOg3B,WAAa,EAAG,CAC/B,MAAM33B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OAChDA,GAASA,EAAMu5B,gBAAkBv5B,EAAM46B,sBACzC/hD,KAAKqvB,SAAS,CAACywB,kBAAkB,IAAO,KACtC34B,EAAM66B,gBhDlYa,IgDmYhBj8C,OAAOkW,GAAQjc,KAAK6I,MAAM0f,QAAQtM,EAAI9V,QAAS,SAC/CuhC,SAAQ,IAAM1nC,KAAKqvB,SAAS,CAACywB,kBAAkB,UAM1D3B,oBACEn+C,KAAKqvB,SAAS,CAACwvB,eAAgB,IAC3B7+C,KAAK2+C,mBACP3+C,KAAK2+C,iBAAiBG,UAAY9+C,KAAK2+C,iBAAiBI,cAI5DzB,iBAAiBjW,GACXA,EAAK7P,OACPx3B,KAAKqvB,SAAS,CACZzQ,MAAOyoB,EAAK7P,OAAO1kB,GACnB0b,OAAQwB,GAAaqX,EAAK7P,OAAOrK,SAGnCntB,KAAKqvB,SAAS,CACZzQ,MAAO,GACP4P,OAAQ,OAIR6Y,EAAKxR,KACP71B,KAAKqvB,SAAS,CACZ2K,SAAUqN,EAAKxR,IAAImE,WACnBsmB,SAAUjZ,EAAKxR,IAAIyqB,WACnBC,gBAAiBlZ,EAAKxR,IAAIyqB,SAAS,SACnCG,YAAalE,GAAclV,EAAKxR,OAKtCooB,qBAAqBv1B,GAEnB,IAAK1oB,KAAK6I,MAAMw2C,mBACd,OAIGr/C,KAAKy+C,wBACRz+C,KAAKy+C,sBAAwBwD,aAAY,KACvC,GAAyC,GAArCjiD,KAAKw+C,sBAAsBv7C,OAI7B,OAFAi/C,cAAcliD,KAAKy+C,4BACnBz+C,KAAKy+C,sBAAwB,MAI/B,IAAI/1B,GAAO,EACX,KAAO1oB,KAAKw+C,sBAAsBv7C,OAAS,GAAG,CAC5C,MAAMkB,EAAInE,KAAKw+C,sBAAsB,GACrC,GAAIr6C,EAAE+iB,WAAalnB,KAAKkvB,MAAM/H,MAAO,CAEnCnnB,KAAKw+C,sBAAsBpO,QAC3B,SAGF,MAAMhjC,EAAM,IAAIC,KAChB,KAAIlJ,EAAEg+C,QAAU/0C,GAKd,MAHApN,KAAKw+C,sBAAsBpO,QAC3B1nB,EAAMjH,KAAKgS,IAAI/K,EAAKvkB,EAAEukB,KAO1B,GAAIA,GAAO,EAAG,CACZ,MAAMvB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OAChDA,GACFA,EAAMi7B,SAAS15B,MA5eQ,MAkf/B,MAAMtb,EAAM,IAAIC,KAChBrN,KAAKw+C,sBAAsB/6C,KAAK,CAC9ByjB,UAAWlnB,KAAKkvB,MAAM/H,MACtBuB,IAAKA,EACLy5B,OAAQ/0C,EAAIi1C,gBAAgBj1C,EAAIk1C,kBhD7fZ,OgDkgBxBpE,yBACEl+C,KAAKw+C,sBAAwB,GACzBx+C,KAAKy+C,wBACPyD,cAAcliD,KAAKy+C,uBACnBz+C,KAAKy+C,sBAAwB,MAIjClB,oBACE,GAAIv9C,KAAKkvB,MAAM/H,MAAO,CACpB,MAAMsnB,EAAO,GACPtnB,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OACpDA,EAAMsmB,aAAaC,IACbA,EAAInY,QAAUmY,EAAInmB,MAAQvnB,KAAK6I,MAAM4uB,UACvCgX,EAAKhrC,KAAKiqC,MAGd,MAAMN,EAAW,CAAC4S,WAAYvR,GACxBwR,EAAO94B,EAAM+4B,cACfD,EACFpgD,OAAO2P,OAAO49B,EAAU,CACtB+S,sBAAuBzD,GAAiBuD,EAAKpqB,OAEtC71B,KAAKkvB,MAAMixB,uBACpBtgD,OAAO2P,OAAO49B,EAAU,CACtB+S,uBAAuB,IAG3BngD,KAAKqvB,SAAS+d,IAKlBoQ,oBAAoB10B,GAClB,MAAM3B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OACpD,IAAK2B,EAIH,YADA9oB,KAAKqvB,SAAS,CAAC+wB,cAAej5B,EAAMk5B,eAItCkC,aAAaviD,KAAKwiD,eAClBxiD,KAAKqvB,SAAS,CAACuvB,aAAcz3B,EAAMy3B,eAAgBiB,iBAAiB,IAIhE14B,EAAMs7B,aAAa35B,EAAIJ,OACrB1oB,KAAKkvB,MAAM2vB,eAziBQ,IA0iBrB7+C,KAAKqvB,SAAS,CAAC0wB,oBAAoB,IAEnC//C,KAAKqvB,SAAS,CAACwvB,eAAgB,KAMpB13B,EAAM4Q,UAAUjP,GAAK,IACtB/F,KAAAA,qBAA8B+F,EAAIhe,MAAQ9K,KAAK6I,MAAM4uB,UACjEz3B,KAAKi+C,qBAAqBn1B,EAAIJ,KAGhC1oB,KAAK6I,MAAMo2C,OAAOn2B,GAGpB20B,0BAA0Bp6C,GACxBrD,KAAKqvB,SAAS,CAACywB,kBAAkB,IAC7Bz8C,EAAQ,GAEVrD,KAAKi+C,qBAAqB,GAI9BP,kBAAkBhvC,GAChB,OAAQA,EAAKg0C,MACX,IAAK,KACHH,aAAaviD,KAAKwiD,eAClBxiD,KAAKwiD,cAAgBvvC,YAAW,KAC9BjT,KAAKqvB,SAAS,CAACwwB,iBAAiB,MAC/B57B,KACEjkB,KAAKkvB,MAAM2wB,iBACd7/C,KAAKqvB,SAAS,CAACwwB,iBAAiB,IAElC,MAEF,IAAK,OACL,IAAK,OAEH7/C,KAAK2iD,cACL,MACF,QACEp1C,QAAQmB,KAAK,0BAA2BA,EAAKg0C,OAInD/E,oBAAoBv6B,GAClBpjB,KAAKqvB,SAAS,CAAEuwB,cAAex8B,IAGjCw6B,qBACM59C,KAAKkvB,MAAMywB,cAAgB3/C,KAAKkvB,MAAMywB,aAAahyB,KACrDiE,IAAIC,gBAAgB7xB,KAAKkvB,MAAMywB,aAAahyB,KAE9C3tB,KAAKqvB,SAAS,CAAEuwB,cAAe,KAAMD,aAAc,KAAMD,WAAY,OAGvE7B,mBAAmBjnB,EAAQmF,EAAMl1B,GAC/B,GAAc,OAAV+vB,EACF52B,KAAK+8C,YAAYvpB,GAAOovB,WAAWpvB,GAAOqvB,MAAM9mB,GAAOl1B,SAClD,GAAc,OAAV+vB,EAAiB,CAC1B,MAAMjJ,EAAM,IAAIiE,IAAI/qB,EAAKojB,KACnBxD,EAASkH,EAAIm1B,aACnB,IAAK,IAAIr+C,KAAOoC,EAAKurC,KACfvrC,EAAKurC,KAAKntC,eAAeR,IAC3BgiB,EAAOxmB,IAAIwE,EAAKoC,EAAKurC,KAAK3tC,IAG9B,CAAC,OAAQ,OAAOyG,KAAKzG,IACfoC,EAAKpC,IACPgiB,EAAOxmB,IAAIwE,EAAKoC,EAAKpC,OAGzBgiB,EAAOxmB,IAAI,MAAOD,KAAK6I,MAAM4uB,UAC7BhR,EAAOxmB,IAAI,QAASD,KAAKkvB,MAAM/H,OAC/BwG,EAAImL,OAASrS,EACbzhB,OAAOmd,KAAKwL,EAAK,eAEjBpgB,QAAQmB,KAAK,yBAA0BkoB,GAI3C1B,mBAAmBnwB,GACjBA,EAAEgjB,iBACFhjB,EAAEijB,kBACFhoB,KAAK6I,MAAMusB,gBAAgB,CAAElO,UAAWlnB,KAAKkvB,MAAM/H,MAAO4C,EAAGhlB,EAAEswB,MAAOzL,EAAG7kB,EAAEuwB,QAG7EwoB,6BAA6Br3B,EAAQs8B,GACZ,QAAnBt8B,EAAO0C,WACT1C,EAAO0C,SAAWnpB,KAAKkvB,MAAM/H,MAC7BV,EAAO2C,SAAWppB,KAAKkvB,MAAMtQ,OAE/B6H,EAAOS,UAAYlnB,KAAKkvB,MAAM/H,MAC9B,MAAM+oB,EAAY6S,GAA4B,GACxC57B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASZ,EAAOS,WAChD,GAAIC,EAAO,CACJA,EAAMgnB,iBACT+B,EAAUzsC,KAAK,kBAEjB,MAAMoyB,EAAM1O,EAAMgf,gBACdtQ,GAAOA,EAAIkY,aACbmC,EAAUzsC,KAAK,uBAGnBzD,KAAK6I,MAAMusB,gBAAgB3O,EAAQypB,GAGrC6N,wBAAwBnnB,GACtB52B,KAAK6I,MAAMm6C,UAAUhjD,KAAKkvB,MAAM/H,MAAOyP,GAGzConB,iBAAiBj5C,GACfA,EAAEgjB,iBACF/nB,KAAK6I,MAAM+lC,oBAAoB5uC,KAAKkvB,MAAM/H,MAAOjD,GAAyBlkB,KAAKkvB,MAAM/H,OAGvFg2B,eACE,MAAMh2B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OAChDA,EAAMu5B,gBACRv5B,EAAM87B,eAKVlG,YAAYj0B,EAAKo6B,EAAyB3b,GACxC,IAAIrb,EACAlsB,KAAK6I,MAAM42C,gBAEb32B,EAAM9oB,KAAK6I,MAAM42C,eAAe32B,IAChCoD,EAAOlsB,KAAK6I,MAAM42C,eAAevzB,KACjClsB,KAAKq+C,qBACIr+C,KAAKkvB,MAAMhK,OAASllB,KAAKkvB,MAAMhK,MAAM9B,UAE9C8I,EAAO,CAAChH,MAAO,GAAKllB,KAAKkvB,MAAMhK,MAAMwD,KAEnB,iBAAPI,IACTA,EAAM0K,GAAOqvB,MAAM/5B,IAErBA,EAAM0K,GAAO7gB,OAAO6gB,GAAO2vB,iBAAiBnjD,KAAKkvB,MAAMhK,MAAM9B,SAAU0F,GACvE9oB,KAAKq+C,qBAEPr+C,KAAK6I,MAAMk0C,YAAYj0B,EAAKo6B,EAAyB3b,EAAUrb,GAIjE8wB,UAAUsE,GACRthD,KAAK6I,MAAMk0C,YAAYuE,EAAIl+B,aAAS1f,OAAWA,EAAW49C,EAAIp1B,MAMhEgxB,mBAAmBnE,GAEjB,MAAMqK,EAC0B,IADCpjD,KAAK6I,MAAMue,OAAOye,eAAe,iBAChExhB,IAAqC,KAAQ,EAE/C,GAAI00B,EAAKjkC,KAAOsuC,EAAyB,CAEvC,MAAM7b,EAAWvnC,KAAK6I,MAAMue,OAAOogB,qBAC7B0b,EAA0B3b,EAASE,OAAOsR,GAC1CjwB,EAAM0K,GAAOqmB,WAAW,KAAM,CAClC7oB,KAAM+nB,EAAK3wC,KACXkjB,SAAUytB,EAAKj3C,KACfgT,KAAMikC,EAAKjkC,KACXuuC,WAAYH,IAGdljD,KAAK+8C,YAAYj0B,EAAKo6B,EAAyB3b,QtCvkB9C,SAAsBwR,GAC3B,OAAO,IAAIz6C,SAAQ,CAACC,EAASC,KAC3B,MAAMi0B,EAAS,IAAIC,WACnBD,EAAO9zB,QAAWg0B,IAChBn0B,EAAOi0B,EAAO7zB,QAEhB6zB,EAAOd,OAAS,KACdpzB,EAAQ,CAACyyB,KAAM+nB,EAAK3wC,KAAMwqB,KAAMH,EAAO/zB,OAAOm0B,MAAM,KAAK,GAAI/wB,KAAMi3C,EAAKj3C,QAE1E2wB,EAAOK,cAAcimB,OsCikBnBuK,CAAavK,GACV55C,MAAK21B,GAAO90B,KAAK+8C,YAAYvpB,GAAOqmB,WAAW,KAAM,CAAC7oB,KAAM8D,EAAI9D,KAAMnqB,KAAMiuB,EAAIlC,KAAMtH,SAAUwJ,EAAIhzB,UACpGiE,OAAMkW,GAAOjc,KAAK6I,MAAM0f,QAAQtM,KAKvCi8B,iBAAiBa,GACf,MAAMwK,EAA0BvjD,KAAK6I,MAAMue,OAAOye,eAAe,oBAAqBvhB,IAElFy0B,EAAKjkC,KAAOyuC,EAEdvjD,KAAK6I,MAAM0f,QAAQvoB,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAAS8yB,0BACxD,CAAC1iC,KAAM+V,GAAiBkuB,EAAKjkC,MAAO0uC,MAAO34B,GAAiB04B,KAA4B,OAE1FvjD,KAAKqvB,SAAS,CACZqwB,WAAY,CACV3G,KAAMA,EACNj3C,KAAMi3C,EAAKj3C,KACXgT,KAAMikC,EAAKjkC,KACX1M,KAAM2wC,EAAK3wC,QAOnB60C,oBAAoBhD,EAAS7nB,GAC3B,MAAMpB,EAAOhxB,KAAKkvB,MAAMywB,aAAa3uB,KAC/BvB,EAAQzvB,KAAKkvB,MAAMywB,aAAalwB,MAChCU,EAASnwB,KAAKkvB,MAAMywB,aAAaxvB,OACjCY,EAAQ/wB,KAAKkvB,MAAMywB,aAAa79C,KAGhCshD,EAC0B,IADCpjD,KAAK6I,MAAMue,OAAOye,eAAe,iBAChExhB,IAAqC,KAAQ,EAE/C,GAAI+N,EAAKtd,KAAOsuC,EAAhB,CAEE,MAAM7b,EAAWvnC,KAAK6I,MAAMue,OAAOogB,qBACnC,IAAKD,EAEH,YADAvnC,KAAK6I,MAAM0f,QAAQvoB,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAAS+yB,yBAG5D,MAAMyL,EAA0B3b,EAASE,OAAOrV,GAGhDf,GAAYe,EhDttBe,OgDstB8B,GAAG,GAEzDjzB,MAAK01B,GAAUrC,GAAaqC,EAAOzC,QACnCjzB,MAAK21B,IACF,IAAIhM,EAAM0K,GAAOiwB,YAAY,KAAM,EAAG,CACpCzyB,KAAMA,EACN0yB,aAAc5uB,EAAIlC,KAClBnD,MAAOA,EACPU,OAAQA,EACR7E,SAAUyF,EACVjc,KAAMsd,EAAKtd,KACXuuC,WAAYH,IAEVjJ,IACFnxB,EAAM0K,GAAOmwB,gBAAgB76B,GAC7BA,EAAM0K,GAAO7gB,OAAOmW,EAAK0K,GAAOqvB,MAAM5I,KAGxCj6C,KAAK+8C,YAAYj0B,EAAKo6B,EAAyB3b,MAChDxhC,OAAOkW,IACRjc,KAAK6I,MAAM0f,QAAQtM,EAAK,eAM9BuW,GAAaJ,GACVjzB,MAAK21B,IACJ,IAAIhM,EAAM0K,GAAOiwB,YAAY,KAAM,EAAG,CACpCzyB,KAAM8D,EAAI9D,KACVyE,QAASX,EAAIlC,KACbnD,MAAOA,EACPU,OAAQA,EACR7E,SAAUyF,EACVjc,KAAMsd,EAAKtd,OAETmlC,IACFnxB,EAAM0K,GAAOmwB,gBAAgB76B,GAC7BA,EAAM0K,GAAO7gB,OAAOmW,EAAK0K,GAAOqvB,MAAM5I,KAExCj6C,KAAK+8C,YAAYj0B,MAKvBmvB,kBAAkBc,GAIhB1nB,GAAY0nB,EhD1wBa,UgDuwBO/4C,KAAK6I,MAAMue,OAAOye,eAAe,oBAAqBvhB,KAGb,GACtEnlB,MAAK01B,IACJ70B,KAAKqvB,SAAS,CAACswB,aAAc,CAC3BhyB,IAAKiE,IAAIW,gBAAgBsC,EAAOzC,MAChCA,KAAMyC,EAAOzC,KACbtwB,KAAM+yB,EAAO/yB,KACb2tB,MAAOoF,EAAOpF,MACdU,OAAQ0E,EAAO1E,OACfrb,KAAM+f,EAAOzC,KAAKtd,KAClBkc,KAAM6D,EAAO7D,WAEdjrB,OAAMkW,IACPjc,KAAK6I,MAAM0f,QAAQtM,EAAK,UAI9Bg2B,mBAAmBvpB,EAAK6e,GACtB,MACMqc,EADQ5jD,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OAChC08B,YAAYn7B,GAC5Bk7B,IACFA,EAAME,YAAa,GAErBvc,EAASjZ,SAQX8vB,gBAAgB11B,EAAKtF,EAASzI,EAAUopC,GACtC/jD,KAAKqvB,SAAS,CAACnK,MAAO,OAEjBwD,GAAQtF,IAIbA,EAA4B,iBAAXA,EAAsBoQ,GAAOwwB,KAAK5gC,GAAWA,EAE5DA,EADEoQ,GAAOywB,QAAQ7gC,GACPoQ,GAAO0wB,aAAa9gC,EhD5xBD,IgD+xBnBoQ,GAAO7gB,OAAO6gB,GAAOwwB,KAAK,MAClCxwB,GAAO2wB,SAASnkD,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAAS43B,iBAAkB,OAG7Et8C,KAAKqvB,SAAS,CACZnK,MAAO,CACL9B,QAASoQ,GAAOmkB,MAAMoM,EAAYppC,EAAUyI,GAC5CsF,IAAKA,KAGT1oB,KAAK6I,MAAMu7C,0BAGb/F,oBACEr+C,KAAKqvB,SAAS,CAACnK,MAAO,OACtBllB,KAAK6I,MAAMu7C,yBAGblS,iBAAiBS,GACf,MAAM1oB,EAAMjqB,KAAKu+C,sBAAsB5L,GACnC1oB,GAAOA,EAAIpC,SACboC,EAAIpC,QAAQw8B,eAAe,CAAC7+B,MAAO,SAAU8+B,SAAU,WACvDr6B,EAAIpC,QAAQ08B,UAAU34C,IAAI,SAC1BqH,YAAW,KAAQgX,EAAIpC,QAAQ08B,UAAUhvC,OAAO,WAAa,MAE7DhI,QAAQ3O,MAAM,yBAA0B+zC,GAI5C1vB,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KAEnC,IAAIhd,EACJ,GAAIlJ,KAAK6I,MAAM27C,SACbt7C,EAAY,UACP,GAAKlJ,KAAKkvB,MAAM/H,MAMhB,CACL,IAAIs9B,EACJ,GAAIzkD,KAAKkvB,MAAMywB,aAEb8E,EACE,kBAAC7J,GAAD,CACEx3B,QAASpjB,KAAKkvB,MAAMywB,aACpBv4B,OAAQpnB,KAAK6I,MAAMue,OACnBlC,MAAOllB,KAAKkvB,MAAMhK,MAClBw0B,cAAe15C,KAAKq+C,kBACpBnE,QAASl6C,KAAK49C,mBACdtE,cAAet5C,KAAKi9C,2BAEnB,GAAIj9C,KAAKkvB,MAAM0wB,cAEpB6E,EACE,kBAAC7J,GAAD,CACEx3B,QAASpjB,KAAKkvB,MAAM0wB,cACpB1F,QAASl6C,KAAK49C,0BAEb,GAAI59C,KAAKkvB,MAAMwwB,WAEpB+E,EACE,kBAAC1K,GAAD,CACE32B,QAASpjB,KAAKkvB,MAAMwwB,WACpBt4B,OAAQpnB,KAAK6I,MAAMue,OACnBlC,MAAOllB,KAAKkvB,MAAMhK,MAClBw0B,cAAe15C,KAAKq+C,kBACpBnE,QAASl6C,KAAK49C,mBACdtE,cAAet5C,KAAKk9C,yBAEnB,CACL,MAAM/1B,EAAQnnB,KAAK6I,MAAMue,OAAOC,SAASrnB,KAAKkvB,MAAM/H,OAC9CoP,EAAYpP,EAAMgnB,gBAClBvD,EAAazjB,EAAM+mB,gBAAkB3X,EACrCf,EAAc,GAChBrO,EAAMgR,UACJhR,EAAMgR,QAAQC,UAChB5C,EAAY/xB,KAAK,CAAC8mB,KAAM,WAAYD,MAAO,cAEzCnD,EAAMgR,QAAQE,OAChB7C,EAAY/xB,KAAK,CAAC8mB,KAAM,QAASD,MAAO,cAEtCnD,EAAMgR,QAAQG,QAChB9C,EAAY/xB,KAAK,CAAC8mB,KAAM,YAAaD,MAAO,eAIhD,MAAMo6B,EAAe,GACrB,IAAIC,EAAe,KACfC,EAAe,KACnB,MAAMC,EAAU,IAAIC,KAAKC,eAAe/kD,KAAK6I,MAAMqd,KAAKwrB,QACxDvqB,EAAMzC,UAAS,CAACoE,EAAKk8B,EAAMnjC,EAAMrc,KAC/B,IAAIy/C,EAAWpjC,EAAQA,EAAK/W,MAAQ,OAAU,KAE1C+nC,EAAW,SACXqS,EAAWp8B,EAAIhe,MAAQ,OACvBo6C,GAAYP,EAEZ9R,EADEqS,GAAYD,EACH,SAEA,OAEJC,GAAYD,IACrBpS,EAAW,SAEb8R,EAAeO,EAEf,MAAMC,IAAYD,GAAYllD,KAAK6I,MAAM4uB,UACnCG,EAAiBzQ,EAAM4Q,UAAUjP,GAAK,GAE5C,IAAyBM,EAAUgX,EAA/BjX,EAAW+7B,EACf,MAAM39B,EAAOJ,EAAMi+B,SAASF,GACxB39B,GAAQA,EAAKiQ,SACfpO,EAAW7B,EAAKiQ,OAAO1kB,GACvBstB,EAAapQ,GAAazI,EAAKiQ,OAAOrK,QAExCy3B,EAAeha,EAAa,iBAAmB,WAG/C,MAAM3gB,EAAMjqB,KAAKu+C,sBAAsBz1B,EAAIJ,KAC3C,IAAIiqB,EAAa7pB,EAAIoD,KAAOm5B,SAASv8B,EAAIoD,KAAKhH,OAAS,KAClDytB,IAAc2S,MAAM3S,KACvBA,EAAa,MAIVqS,GAAQ,IAAI33C,KAAK23C,EAAKO,IAAIC,gBAAkB,IAAIn4C,KAAKyb,EAAIy8B,IAAIC,gBAChEd,EAAajhD,KACb,kBAAC63C,GAAD,CACEC,KAAMsJ,EAAQY,OAAO38B,EAAIy8B,IACzB7T,OAAQ1xC,KAAK6I,MAAMqd,KAAKwrB,OACxBjtC,IAAK,QAAUqkB,EAAIJ,OAInBI,EAAI48B,GAENhB,EAAajhD,KACX,kBAAC63C,GAAD,CACE1sB,SAAS,EACTnqB,IAAKqkB,EAAIJ,OAGbg8B,EAAajhD,KACX,kBAAC,GAAD,CACE2jB,OAAQpnB,KAAK6I,MAAMue,OACnBhE,QAAS0F,EAAI1F,QACb4J,SAAUlE,EAAIoD,KAAOpD,EAAIoD,KAAK8E,KAAO,KACrCwgB,UAAW1oB,EAAIy8B,GACfn0C,SAAU+zC,EACVz8B,IAAKI,EAAIJ,IACT+F,QAASmc,EACT+a,OAAQ3lD,KAAKkvB,MAAM5a,QACnB6U,SAAUA,EACVC,SAAUA,EACVgX,WAAYA,EACZyS,SAAUA,EACV3kB,SAAU0J,EACV2P,SAAUze,EAAI88B,UACdvyB,cAAerzB,KAAK6I,MAAMwqB,cAC1B+B,gBAAiBp1B,KAAK89C,6BACtBpqB,eAAgB1zB,KAAK29C,oBACrBrL,eAAgBtyC,KAAK69C,mBACrBt1B,QAASvoB,KAAK6I,MAAM0f,QACpBsH,eAAgB7vB,KAAKiyC,mBACrB/oB,UAAWlpB,KAAKo+C,gBAChBzL,WAAYA,EACZze,aAAcl0B,KAAKkyC,iBACnBjoB,IAAKA,EACLuoB,aAAcxyC,KAAKkvB,MAAM8K,SACzBv1B,IAAKqkB,EAAIJ,UAKjB,IAAIm9B,EAAW,KACf,GAAItvB,EACFsvB,EAAW5/B,EAAcvB,GAASpQ,aAC7B,CACL,MAAMwxC,EAAO9lD,KAAK6I,MAAMue,OAAO2+B,aAAaC,WAAWhmD,KAAKkvB,MAAM/H,OAC9D2+B,GAAQ/iC,KAAAA,eAAsB+iC,EAAK3+B,SACjC2+B,EAAKvwB,OACPswB,EAAW5/B,EAAcvB,GAASy3B,YACzB2J,EAAKG,OACdJ,EAAW5/B,EAAcvB,GAAS03B,WAAa,K7CxjCtD,SAAyBj9C,EAAMuyC,GACpCA,EAASA,GAAU1sC,OAAOmR,UAAU+vC,cAAgBlhD,OAAOmR,UAAUgwC,SACrE,MAAM/4C,EAAM,IAAIC,KAChB,OAAIlO,EAAKinD,eAAiBh5C,EAAIg5C,cAExBjnD,EAAKknD,YAAcj5C,EAAIi5C,YAAclnD,EAAKmnD,WAAal5C,EAAIk5C,UAEtDnnD,EAAKsyC,mBAAmBC,EAAQ,CAAC6U,QAAQ,EAAOC,KAAM,UAAWC,OAAQ,YAG3EtnD,EAAKunD,mBAAmBhV,EAC7B,CAAC6U,QAAQ,EAAOI,MAAO,QAASC,IAAK,UAAWJ,KAAM,UAAWC,OAAQ,YAGtEtnD,EAAKunD,mBAAmBhV,EAAQ,CAACmV,KAAM,UAAWF,MAAO,QAASC,IAAK,Y6C2iChEE,CAAgBhB,EAAKG,KAAKc,KAAM/mD,KAAK6I,MAAMqd,KAAKwrB,UAKxD,MAAMljB,EAASxuB,KAAKkvB,MAAMV,SAAU,EAC9B+G,EAASv1B,KAAKkvB,MAAMN,QAAU,KAClC5uB,KAAK6I,MAAM0sB,OAAS,UAAYv1B,KAAKkvB,MAAM2wB,gBAAkB,UAAY,IAAM,UAE3E1pB,EAAa,eAAiBn2B,KAAKkvB,MAAMN,QAAU,WAAa,IAEtE61B,EACE,oCACE,yBAAKjhC,GAAG,sBAAsBL,UAAU,iBACrCnjB,KAAK6I,MAAMynC,cACV,uBAAG/jB,KAAK,IAAI/I,GAAG,oBAAoBF,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMm+C,uBACjF,uBAAG7jC,UAAU,kBAAb,eAGF,KACF,yBAAKA,UAAU,cACb,kBAACoL,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQA,EACRrH,MAAOnnB,KAAKkvB,MAAM/H,MAClBvI,MAAO5e,KAAKkvB,MAAMtQ,MAClBgQ,QAAS5uB,KAAKkvB,MAAMN,UACpB2H,EAA0C,KAA9B,0BAAMpT,UAAWoS,KAEjC,yBAAK/R,GAAG,qBACN,yBAAKA,GAAG,cAAcL,UAAWgT,GAC/Bn2B,KAAKkvB,MAAMtQ,OACX,2BAAG,kBAAC,EAAA2E,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,8BAEvD,kBAAC2G,GAAD,CAAeC,OAAQmL,KACxB,yBAAKhS,GAAG,mBAAmBqiC,IAE5Bjb,EACC,kBAAC0P,GAAD,CACElzB,OAAQpnB,KAAK6I,MAAMue,OACnBqmB,YAAaztC,KAAKkvB,MAAM8wB,aAC1B,yBAAKx8B,GAAG,gBAEV,6BACE,uBAAG+I,KAAK,IAAIjJ,QAAStjB,KAAKk1B,oBACxB,uBAAG/R,UAAU,kBAAb,gBAILnjB,KAAK6I,MAAMynC,cACV,kBAAC1U,GAAD,CACEC,MAAO77B,KAAK6I,MAAM0nC,WAClBxU,KAAM/7B,KAAK6I,MAAM2nC,UACjB1U,aAAc97B,KAAK6I,MAAM0f,UACzB,KACJ,kBAAC6R,GAAD,CAAaG,KAAMv6B,KAAKkvB,MAAM4wB,mBAC9B,yBAAKt8B,GAAG,sBACN,4BAAQL,UAAW,iBAAmBnjB,KAAKkvB,MAAM6wB,mBAAqB,GAAK,WACzEz8B,QAAStjB,KAAKm+C,mBACd,uBAAGh7B,UAAU,kBAAb,mBAEF,yBAAKK,GAAG,iBAAiByG,IAAKjqB,KAAKo9C,uBACjC,wBAAI55B,GAAG,WAAWL,UAAWyhC,GAC1BF,IAGH1kD,KAAKkvB,MAAMoxB,SASX,KARF,yBAAK98B,GAAG,yBACLxjB,KAAKkvB,MAAMqxB,eACZ,yBAAK/8B,GAAG,mBACN,kBAAC,EAAAD,iBAAD,CAAkBC,GAAE,wBAAyBC,eAAc,4CAG3D,OAILzjB,KAAKkvB,MAAMixB,wBAA0BngD,KAAKkvB,MAAMuxB,YAC/C,yBAAKj9B,GAAG,gCACN,uBAAGL,UAAU,4BAAb,SADF,IACoD,kBAAC,EAAAI,iBAAD,CAChDC,GAAE,2BAA4BC,eAAc,mDAFhD,IAGqE,uBAAG8I,KAAK,IACvEjJ,QAAStjB,KAAKg+C,kBAAkB,kBAAC,EAAAz6B,iBAAD,CAAkBC,GAAE,yBACpDC,eAAc,6BALpB,KAMS,KACVzjB,KAAKkvB,MAAMuxB,YACV,kBAACxF,GAAD,CAAY3yB,SAAUtoB,KAAK+9C,0BAE3B,kBAAC,GAAD,CACE32B,OAAQpnB,KAAK6I,MAAMue,OACnBiyB,UAAWr5C,KAAK6I,MAAM42C,eACtBhH,UAAWz4C,KAAKkvB,MAAM8K,UAAYh6B,KAAKkvB,MAAMN,QAC7C4qB,WAAYx5C,KAAKm9C,aACjB7D,cAAet5C,KAAK+8C,YACpB5D,aAAcn5C,KAAK6I,MAAM42C,eAAiB,KAAOz/C,KAAKk4C,iBACtDgB,cAAel5C,KAAK6I,MAAM42C,eAAiB,KAAOz/C,KAAKi4C,kBACvD1vB,QAASvoB,KAAK6I,MAAM0f,QACpBrD,MAAOllB,KAAKkvB,MAAMhK,MAClBgP,aAAcl0B,KAAKkyC,iBACnBwH,cAAe15C,KAAKq+C,qBAK9Bn1C,EAAY,yBAAKsa,GAAG,cAAcihC,QA/PlCv7C,EACE,kBAACiyC,GAAD,CACEC,cAAep7C,KAAK6I,MAAMuyC,cAC1BC,cAAer7C,KAAK6I,MAAMwyC,gBA8PhC,OAAOnyC,GAIX,UAAeghB,EAAAA,EAAAA,YAAW0yB,ICrqCX,MAAMqK,WAAmB/hD,IAAAA,cACtC+d,SACE,OACE,uBAAGsJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMq+C,WAC1D,uBAAG/jC,UAAU,kBAAb,gBCLO,MAAMgkC,WAAqBjiD,IAAAA,cACxC+d,SACE,OACE,6BACE,uBAAGsJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMu+C,eAAgB,uBAAGjkC,UAAU,kBAAb,SAD9E,IAGE,uBAAGoJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMw+C,eAAgB,uBAAGlkC,UAAU,kBAAb,eCNrE,MAAMmkC,WAAkBpiD,IAAAA,cACrC+d,SACE,OACI,6BACE,uBAAGsJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM0+C,aAAc,uBAAGpkC,UAAU,kBAAb,eAD5E,IAGE,uBAAGoJ,KAAK,IAAIjJ,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMw+C,eAAgB,uBAAGlkC,UAAU,kBAAb,eCEvE,MAAMqkC,WAAmBtiD,IAAAA,cACtC+d,SACE,MAAMuS,EAAc,GAChBx1B,KAAK6I,MAAM0yB,eACbv7B,KAAK6I,MAAM0yB,cAAcrwB,KAAK5D,IAC5BkuB,EAAY/xB,KAAK,CAAC8mB,KAAMjjB,EAAGgjB,MAAO,iBAGtC,IAAIkE,EAAS,KAIb,OAHIxuB,KAAK6I,MAAMue,SACboH,EAASxuB,KAAK6I,MAAMue,OAAOyH,aAAad,GAAiB/tB,KAAK6I,MAAM2lB,UAGpE,yBAAKhL,GAAG,qBAAqBL,UAAU,iBACpCnjB,KAAK6I,MAAM8mB,SAAW,kBAACs3B,GAAD,CAAYC,OAAQlnD,KAAK6I,MAAM8mB,WAAe,KACpEnB,EACC,yBAAKhL,GAAG,cAAcL,UAAU,cAC9B,kBAACoL,GAAD,CACEnH,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQA,EACRrH,MAAOnnB,KAAK6I,MAAM4uB,SAClB7Y,MAAO5e,KAAK6I,MAAM+V,SAGtB,KACF,yBAAK4E,GAAG,kBAAkBL,UAAU,eAAenjB,KAAK6I,MAAM+V,MAAM,kBAACwL,GAAD,CAAeC,OAAQmL,KACrE,UAArBx1B,KAAK6I,MAAMqmB,MACR,kBAACo4B,GAAD,CAAWC,SAAUvnD,KAAK6I,MAAM0+C,SAAUF,WAAYrnD,KAAK6I,MAAMw+C,aAC9C,aAArBrnD,KAAK6I,MAAMqmB,MACT,kBAACi4B,GAAD,CAAcC,WAAYpnD,KAAK6I,MAAMu+C,WAAYC,WAAYrnD,KAAK6I,MAAMw+C,aAC1E,OC9BV,SAAS5b,GAAMnmC,EAAKrC,GAClB,OAAOqC,GAAOA,EAAI8nB,UAAU,EAAGnqB,GAGlB,MAAMwkD,WAA4BviD,IAAAA,UAC/CU,YAAYiD,GACVxC,MAAMwC,GAEN,MAAM6+C,EAAK1nD,KAAK6I,MAAMue,OAAO2+B,aAC7B/lD,KAAKkvB,MAAQ,CACXmX,SAAUoF,GAAMic,EAAGlwB,OAASkwB,EAAGlwB,OAAO1kB,QAAKpP,EAAW8gB,IACtD8hB,YAAamF,GAAMic,EAAGlwB,OAASkwB,EAAGlwB,OAAO3K,UAAOnpB,EAAW+gB,IAC3D+J,OAAQwB,GAAa03B,EAAGlwB,OAASkwB,EAAGlwB,OAAOrK,MAAQ,OAIvDlK,SACE,OACE,yBAAKE,UAAU,oBACb,yBAAKA,UAAU,qBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,0BAA0BG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,aACzG,uBAAG5sB,UAAU,kBAAb,QADF,IAEE,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,cAAeC,eAAc,2BAEnD,gCACE,kBAAC+W,GAAD,CACEpT,OAAQpnB,KAAK6I,MAAMue,OACnBoH,OAAQxuB,KAAKkvB,MAAMV,OACnBuM,UAAU,EACVjB,IAAK95B,KAAK6I,MAAM4uB,SAChB7Y,MAAO5e,KAAKkvB,MAAMmX,YAEtB,yBAAKljB,UAAU,SACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,gCAGvD,yBAAKN,UAAU,oBAAoBnjB,KAAKkvB,MAAMmX,WAEhD,yBAAKljB,UAAU,SACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,0BAD9E,IAGE,4BAAKzjB,KAAK6I,MAAM4uB,WAElB,yBAAKtU,UAAU,SACb,kBAAC,GAAD,CAAWoY,cAAev7B,KAAK6I,MAAM0yB,iBAEtCv7B,KAAKkvB,MAAMoX,YACV,yBAAKnjB,UAAU,SACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,kCAGzD,6BAAMzjB,KAAKkvB,MAAMoX,cACV,MAEb,yBAAKnjB,UAAU,OACf,yBAAKA,UAAU,qBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,cAAcG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,WAC7F,uBAAG5sB,UAAU,kBAAb,iBADF,IACuD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,oCACvEC,eAAc,oCAElB,uBAAG8I,KAAK,IAAIpJ,UAAU,cAAcG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,cAC7F,uBAAG5sB,UAAU,kBAAb,YADF,IACkD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAClEC,eAAc,+BAElB,uBAAG8I,KAAK,IAAIpJ,UAAU,cAAcG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAMknC,WAAW,aAC7F,uBAAG5sB,UAAU,kBAAb,mBADF,IACyD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,8BACzEC,eAAc,iCCtE5B,MAAMiB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BgjC,wBAAyB,CACvBnkC,GAAI,oBACJC,eAAgB,CAAF,mFAKH,MAAMmkC,WAAqB1iD,IAAAA,UACxCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAK6nD,aAAe7nD,KAAK6nD,aAAazhC,KAAKpmB,MAE3CA,KAAKkvB,MAAQ04B,GAAarrB,qBAAqB1zB,GAGtB,4BAACA,GAC1B,MAAMsuB,EAAW,GACjB,IAAI2wB,EAAgB,EAChBC,EAAgB,EAmCpB,OAlCAl/C,EAAMm/C,SAAS98C,KAAKzF,IAClB,MAAMwiD,EAAUxiD,EAAEowB,MAAQpwB,EAAEowB,IAAII,WAE5BgyB,GAAWp/C,EAAMo/C,SACnB9wB,EAAS1zB,KAAKgC,GAEZwiD,GAAWp/C,EAAMo/C,UAIjBxiD,EAAE6xB,SAAW7xB,EAAE6xB,QAAQ4wB,KACrBr/C,EAAM8c,QACRwR,EAAS1zB,KAAKgC,GAEdsiD,IAEQl/C,EAAM8c,UAChBwR,EAAS1zB,KAAKgC,GACdqiD,GAAiBriD,EAAE+wB,OAAS,EAAI,EAAI,OAIxCW,EAAS3J,MAAK,CAACjpB,EAAG+C,KACRA,EAAE6gD,SAAW,IAAM5jD,EAAE4jD,SAAW,KAGtCJ,EAAgB,GAClB5wB,EAAS1zB,KAAK,CACZmzB,OAAQ,UACRhY,MAAO8F,GAASijC,wBAChB58C,OAAQ,CAAC1H,MAAO0kD,KAIb,CACL7b,YAAa/U,EACb2wB,cAAeA,GAInBx4B,mBAAmBC,EAAW+N,GAC5B,GAAI/N,EAAUy4B,UAAYhoD,KAAK6I,MAAMm/C,UACjCz4B,EAAU5J,SAAW3lB,KAAK6I,MAAM8c,SAChC4J,EAAU04B,SAAWjoD,KAAK6I,MAAMo/C,QAAS,CAC3C,MAAM7a,EAAWwa,GAAarrB,qBAAqBv8B,KAAK6I,OACxD7I,KAAKqvB,SAAS+d,GACVA,EAAS0a,eAAiBxqB,EAAUwqB,eACtC/7B,GAAcqhB,EAAS0a,gBAK7BD,aAAaO,GACXpoD,KAAK6I,MAAMw/C,gBAGbplC,SACE,OACE,kBAAC,EAAAM,iBAAD,CAAkBC,GAAE,qBAClBC,eAAc,yFAEbwa,GAAgB,kBAAC,GAAD,CACf7W,OAAQpnB,KAAK6I,MAAMue,OACnB6Q,UAAWj4B,KAAK6I,MAAMovB,UACtBd,SAAUn3B,KAAKkvB,MAAMgd,YACrBvT,iBAAkBsF,EAClBjH,cAAeh3B,KAAK6I,MAAMmuB,cAC1BS,SAAUz3B,KAAK6I,MAAM4uB,SACrBnB,YAAY,EACZ0B,YAAY,EACZE,gBAAiBl4B,KAAK6I,MAAMqvB,gBAC5B9C,gBAAiBp1B,KAAK6I,MAAMusB,gBAC5B9M,SAAUtoB,KAAK6nD,kBClGV,MAAMS,GAEH,iBAAC7jD,EAAKpF,GACpBkpD,aAAaC,QAAQ/jD,EAAKmS,KAAKC,UAAUxX,IAI3B,iBAACoF,GACf,MAAMpF,EAAQkpD,aAAaE,QAAQhkD,GACnC,OAAOpF,GAASuX,KAAKisC,MAAMxjD,GAIV,oBAACoF,EAAKpF,GACvB,MAAMqpD,EAAS1oD,KAAK2oD,UAAUlkD,GAC9BzE,KAAK4oD,UAAUnkD,EAAK5E,OAAO2P,OAAOk5C,GAAU,GAAIrpD,IAIjC,kBAACoF,GAChB8jD,aAAaM,WAAWpkD,ICVb,MAAMqkD,WAA0B5jD,IAAAA,cAC7CU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX65B,MAAO,GACPC,SAAU,GACVC,MAAO,GACPn2C,GAAI,GACJo2C,aAAc,KACdC,cAAc,EACdC,UAAWd,GAAiBK,UAAU,mBAGxC3oD,KAAKqpD,kBAAoBrpD,KAAKqpD,kBAAkBjjC,KAAKpmB,MACrDA,KAAKspD,qBAAuBtpD,KAAKspD,qBAAqBljC,KAAKpmB,MAC3DA,KAAKupD,kBAAoBvpD,KAAKupD,kBAAkBnjC,KAAKpmB,MACrDA,KAAKwpD,eAAiBxpD,KAAKwpD,eAAepjC,KAAKpmB,MAC/CA,KAAK0sC,mBAAqB1sC,KAAK0sC,mBAAmBtmB,KAAKpmB,MACvDA,KAAKypD,sBAAwBzpD,KAAKypD,sBAAsBrjC,KAAKpmB,MAC7DA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAG7CqpD,kBAAkBtkD,GAChB/E,KAAKqvB,SAAS,CAAC05B,MAAOhkD,EAAE+iB,OAAOzoB,QAGjCiqD,qBAAqBN,GACnBhpD,KAAKqvB,SAAS,CAAC25B,SAAUA,IAG3BO,kBAAkBxkD,GAChB/E,KAAKqvB,SAAS,CAAC45B,MAAOlkD,EAAE+iB,OAAOzoB,QAGjCmqD,eAAezkD,GACb/E,KAAKqvB,SAAS,CAACvc,GAAI/N,EAAE+iB,OAAOzoB,QAG9BqtC,mBAAmBlb,GACjBxxB,KAAKqvB,SAAS,CAAC65B,aAAc13B,IAG/Bi4B,wBACEnB,GAAiBM,UAAU,kBAAmB5oD,KAAKkvB,MAAMk6B,WACzDppD,KAAKqvB,SAAS,CAAC+5B,WAAYppD,KAAKkvB,MAAMk6B,YAGxCvqB,aAAa95B,GACXA,EAAEgjB,iBACF/nB,KAAKqvB,SAAS,CAAC85B,cAAc,IAC7BnpD,KAAK6I,MAAM6gD,gBACT1pD,KAAKkvB,MAAM65B,MAAMh8B,OACjB/sB,KAAKkvB,MAAM85B,SAASj8B,OACpBL,GAAQ1sB,KAAKkvB,MAAMpc,GAAGia,OAAOK,UAAU,EAAG5I,IAAmBxkB,KAAKkvB,MAAMg6B,cACxE,CAAC,KAAQ,QAAS,IAAOlpD,KAAKkvB,MAAM+5B,QAGxChmC,SACE,IAAI0mC,EAAgB,UAKpB,OAJI3pD,KAAK6I,MAAM4vC,WACbkR,GAAiB,aAIjB,0BAAMxmC,UAAU,oBAAoBmc,SAAUt/B,KAAK6+B,cACjD,yBAAK1b,UAAU,kBACb,yBAAKA,UAAU,qBACb,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,2BAE/CmmC,GAAiB,2BAAOxhD,KAAK,OAAOgxB,YAAawwB,EAAcvlB,aAAa,YAC3EhlC,MAAOW,KAAKkvB,MAAM65B,MAAO1vB,SAAUr5B,KAAKqpD,kBAAmB9vB,UAAQ,EAACC,WAAS,MAEjF,kBAAC,EAAAjW,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,8BAElDomC,GAAoB,kBAAC9lB,GAAD,CAAiB3K,YAAaywB,EAAiBxlB,aAAa,eAC/EhlC,MAAOW,KAAKkvB,MAAM85B,SAAU7kB,WAAYnkC,KAAKspD,qBAC7C/vB,UAAU,OAGhB,kBAACiB,GAAD,CACEpT,OAAQpnB,KAAK6I,MAAMue,OACnB0iC,eAAgB9pD,KAAK0sC,mBACrBnkB,QAASvoB,KAAK6I,MAAM0f,WAExB,yBAAMpF,UAAU,kBACd,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,8CAEnDsmC,GAAqB,2BAAO3hD,KAAK,OAAOgxB,YAAa2wB,EAAkB1lB,aAAa,OACnFhlC,MAAOW,KAAKkvB,MAAMpc,GAAIumB,SAAUr5B,KAAKwpD,eAAgBjwB,UAAQ,OAGnE,yBAAKpW,UAAU,kBACb,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,kDAE/CumC,GAAiB,2BAAO5hD,KAAK,QAAQgxB,YAAa4wB,EAAc3lB,aAAa,QAC5EhlC,MAAOW,KAAKkvB,MAAM+5B,MAAO5vB,SAAUr5B,KAAKupD,kBAAmBhwB,UAAQ,OAGzE,yBAAKpW,UAAU,kBACb,kBAACqY,GAAD,CAAUhY,GAAG,aAAa1hB,KAAK,aAAa45B,QAAS17B,KAAKkvB,MAAMk6B,UAC9D/vB,SAAUr5B,KAAKypD,wBACjB,kBAAC,EAAAlmC,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,oCAEjDwmC,GAAmB,2BAAOhvB,QAAQ,cAAf,IAAmCgvB,MAG3D,yBAAK9mC,UAAU,kBACb,4BAAQA,UAAWwmC,EAAevhD,KAAK,UACrC,kBAAC,EAAAmb,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,iCCpHjD,MAAMymC,WAAwBhlD,IAAAA,cAC3CU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKmqD,oBAAsBnqD,KAAKmqD,oBAAoB/jC,KAAKpmB,MAG3DmqD,oBAAoBzH,EAAMhnB,GACZ,SAARgnB,EACF1iD,KAAK6I,MAAMuhD,sBAAsB1uB,GAChB,SAARgnB,EACT1iD,KAAK6I,MAAMwhD,0BAA0B3uB,GACpB,aAARgnB,GACT1iD,KAAK6I,MAAMyhD,sBAAsB5uB,GAIrCzY,SACE,OACI,yBAAKE,UAAU,oBACb,yBAAKA,UAAU,kBACb,2BAAO8X,QAAQ,iBACb,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,qCAG3D,kBAAC+X,GAAD,CAAU15B,KAAK,QAAQ0hB,GAAG,gBACxBkY,QAAS17B,KAAK6I,MAAM0hD,cAAelxB,SAAUr5B,KAAKmqD,uBAEtD,yBAAKhnC,UAAU,kBACb,2BAAO8X,QAAQ,kBACdj7B,KAAK6I,MAAM2hD,qBACV,kBAAC,EAAAjnC,iBAAD,CAAkBC,GAAE,2BAA4BC,eAAc,0CAG9D,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,oCAClBC,eAAc,4DAIlB,kBAAC+X,GAAD,CAAU15B,KAAK,QAAQ0hB,GAAG,iBACxBkY,QAAS17B,KAAK6I,MAAM4hD,cACpBpxB,SAAUr5B,KAAK6I,MAAM2hD,qBAAuBxqD,KAAKmqD,oBAAsB,QAE3E,yBAAKhnC,UAAU,kBACb,2BAAO8X,QAAQ,kBACb,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,sCAG5D,kBAAC+X,GAAD,CAAU15B,KAAK,YAAY0hB,GAAG,iBAC5BkY,QAAS17B,KAAK6I,MAAM6hD,cAAerxB,SAAUr5B,KAAKmqD,yBChDhE,MAAMzlC,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BgmC,eAAgB,CACdnnC,GAAI,iBACJC,eAAgB,CAAF,kCAGhBmnC,uBAAwB,CACtBpnC,GAAI,yBACJC,eAAgB,CAAF,wFAKlB,MAAMonC,WAAwB3lD,IAAAA,UAC5BU,YAAYiD,GACVxC,MAAMwC,GAEN,MAAM6+C,EAAK1nD,KAAK6I,MAAMue,OAAO2+B,aAC7B,IAAI+E,EAAe,EACnBpD,EAAGvwB,UAAU1xB,IACPA,EAAEowB,MAAQpwB,EAAEowB,IAAII,YAClB60B,OAGJ,MAAMnd,EAAS+Z,EAAG9Z,mBAClB5tC,KAAKkvB,MAAQ,CACXhU,KAAMyyB,EAASA,EAAOzyB,KAAO,KAC7B6vB,KAAM4C,EAASA,EAAO5C,KAAO,KAC7BggB,6BAAyBrnD,EACzBonD,aAAcA,GAGhB9qD,KAAKgrD,qBAAuBhrD,KAAKgrD,qBAAqB5kC,KAAKpmB,MAC3DA,KAAK8sC,8BAAgC9sC,KAAK8sC,8BAA8B1mB,KAAKpmB,MAC7EA,KAAKirD,4BAA8BjrD,KAAKirD,4BAA4B7kC,KAAKpmB,MACzEA,KAAK6sC,yBAA2B7sC,KAAK6sC,yBAAyBzmB,KAAKpmB,MACnEA,KAAKkrD,oBAAsBlrD,KAAKkrD,oBAAoB9kC,KAAKpmB,MAG3DgrD,qBAAqBG,GACnBnrD,KAAKqvB,SAAS,CAAC25B,SAAUmC,IACzBnrD,KAAK6I,MAAMuiD,iBAAiBD,GAG9Bre,8BAA8BiC,GAC5B/uC,KAAKqvB,SAAS,CACZ07B,wBAAyBhc,EACzBW,kBAAmB1vC,KAAKkvB,MAAM6f,KAIlCkc,8BACEjrD,KAAKqvB,SAAS,CAAC07B,6BAAyBrnD,IAG1CmpC,yBAAyBmC,GACvB,IAAIrB,EAAS,GACbA,EAAO3tC,KAAKkvB,MAAM67B,yBAA2B/b,EAC7ChvC,KAAK6I,MAAMwiD,oBAAoB,UAAM3nD,OAAWA,EAAWiqC,GAE3D,IAAIP,EAAW,CAAC2d,6BAAyBrnD,GACzC0pC,EAASptC,KAAKkvB,MAAM67B,yBAA2B/b,EAC/ChvC,KAAKqvB,SAAS+d,GAGhB8d,oBAAoBnmD,GAClBA,EAAEgjB,iBACF,MAAM,cAAC9B,GAAiBjmB,KAAK6I,MAAMqd,KACnClmB,KAAK6I,MAAM8d,YACTV,EAAcvB,GAASimC,gBACvB1kC,EAAcvB,GAASkmC,yBACtB,KAAQ5qD,KAAK6I,MAAMyiD,oBACpB,MACA,EACA,MAIJroC,SACE,OACE,oCAAGjjB,KAAKkvB,MAAM67B,wBACZ,kBAAC,GAAD,CACEriD,KAAM1I,KAAKkvB,MAAMwgB,kBACjBzP,KAAK,IACLX,SAAUt/B,KAAK6sC,yBACfld,SAAU3vB,KAAKirD,8BAEjB,yBAAK9nC,UAAU,oBACb,yBAAKA,UAAU,qBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,+BAGtD,6BACE,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,4BAA6BC,eAAc,+BAE5D8nC,GAAuB,kBAACjnB,GAAD,CACtBlL,YAAamyB,EACbnjD,KAAK,WACL+7B,WAAYnkC,KAAKgrD,2BAIzB,yBAAK7nC,UAAU,OACf,yBAAKA,UAAU,qBACb,uBAAGoJ,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM2iD,aACzF,uBAAGroC,UAAU,kBAAb,eADF,KACsD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBACtEC,eAAc,6BAElB,uBAAG8I,KAAK,IAAIpJ,UAAU,qBAAqBG,QAAUve,IAAO/E,KAAKkrD,oBAAoBnmD,KACnF,uBAAGoe,UAAU,kBAAb,UADF,KACiD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,wBACjEC,eAAc,sCAGpB,yBAAKN,UAAU,OACf,yBAAKA,UAAU,qBACb,6BACE,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,4BAA6BC,eAAc,4CAInE,yBAAKN,UAAU,UACb,sCAAW,wBAAIA,UAAU,YACvBG,QAAStjB,KAAK8sC,8BAA8B1mB,KAAKpmB,KAAM,SAAUA,KAAKkvB,MAAMhU,OAC9E,sCAAW,wBAAIiI,UAAU,YACvBG,QAAStjB,KAAK8sC,8BAA8B1mB,KAAKpmB,KAAM,SAAUA,KAAKkvB,MAAM6b,SAGjF/qC,KAAKkvB,MAAM47B,aAAe,EACzB,oCACE,yBAAK3nC,UAAU,OACf,yBAAKA,UAAU,kBACb,uBAAGA,UAAU,kBAAb,SADF,IAEE,uBAAGoJ,KAAK,IAAIpJ,UAAU,OAAOG,QAAUve,IAAOA,EAAEgjB,iBAAkB/nB,KAAK6I,MAAM4iD,kBAC3E,kBAAC,EAAAloC,iBAAD,CAAkBC,GAAE,wBAAyBC,eAAc,gFACzD1Y,OAAQ,CAAE1H,MAAOrD,KAAKkvB,MAAM47B,mBAIlC,QAOd,UAAe5gC,EAAAA,EAAAA,YAAW2gC,IClJX,MAAMa,WAAuBxmD,IAAAA,cAC1C+d,SACE,OACE,yBAAKE,UAAU,oBACb,yBAAKA,UAAU,qBACb,uBAAGoJ,K3DwFkB,0B2DxFKpJ,UAAU,cAAc2E,OAAO,UACvD,uBAAG3E,UAAU,kBAAb,SADF,KACgD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,kBAChEC,eAAc,iCAElB,uBAAG8I,K3D0FwB,+B2D1FKpJ,UAAU,cAAc2E,OAAO,UAC7D,uBAAG3E,UAAU,kBAAb,eADF,KACsD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,wBACtEC,eAAc,uCAElB,uBAAG8I,K3DmFsB,iC2DnFKpJ,UAAU,cAAc2E,OAAO,UAC3D,uBAAG3E,UAAU,kBAAb,UADF,KACiD,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,sBACjEC,eAAc,sCAGpB,yBAAKN,UAAU,OACf,yBAAKA,UAAU,qBACb,yBAAKA,UAAU,kBACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,8BAE1EG,IAEH,yBAAKT,UAAU,kBACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,2BACvEV,KAAAA,cAEH,yBAAKI,UAAU,kBACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,8BAE1EzjB,KAAK6I,MAAMuyC,eAEd,yBAAKj4B,UAAU,kBACb,2BAAOA,UAAU,SAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,sCAClFzjB,KAAK6I,MAAMwyC,kBCnCT,MAAMsQ,WAAkBzmD,IAAAA,UACrCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX65B,MAAOlgD,EAAMkgD,MACbC,SAAU,GACV4C,SAAU/iD,EAAMwyC,cAChB+N,UAAWvgD,EAAMgjD,SAEnB7rD,KAAKqpD,kBAAoBrpD,KAAKqpD,kBAAkBjjC,KAAKpmB,MACrDA,KAAKspD,qBAAuBtpD,KAAKspD,qBAAqBljC,KAAKpmB,MAC3DA,KAAKypD,sBAAwBzpD,KAAKypD,sBAAsBrjC,KAAKpmB,MAC7DA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAG7CqpD,kBAAkBtkD,GAChB/E,KAAKqvB,SAAS,CAAC05B,MAAOhkD,EAAE+iB,OAAOzoB,QAGjCiqD,qBAAqBvkD,GACnB/E,KAAKqvB,SAAS,CAAC25B,SAAUjkD,EAAE+iB,OAAOzoB,QAGpCoqD,wBACEzpD,KAAK6I,MAAMijD,qBAAqB9rD,KAAKkvB,MAAMk6B,WAC3CppD,KAAKqvB,SAAS,CAAC+5B,WAAYppD,KAAKkvB,MAAMk6B,YAGxCvqB,aAAa95B,GACXA,EAAEgjB,iBACF/nB,KAAK6I,MAAMkjD,QAAQ/rD,KAAKkvB,MAAM65B,MAAMh8B,OAAQ/sB,KAAKkvB,MAAM85B,SAASj8B,QAGlE9J,SACE,IAAI0mC,EAAgB,UAKpB,OAJI3pD,KAAK6I,MAAM4vC,WACbkR,GAAiB,aAIjB,0BAAMnmC,GAAG,aAAa8b,SAAUt/B,KAAK6+B,cACnC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,2BAG/CmmC,GAAiB,2BAAOxhD,KAAK,OAAOob,GAAG,aACtC4V,YAAawwB,EACbvlB,aAAa,WACb2nB,YAAY,MACZC,eAAe,OACf5sD,MAAOW,KAAKkvB,MAAM65B,MAClB1vB,SAAUr5B,KAAKqpD,kBACf9vB,UAAQ,EAACC,WAAS,MAGtB,kBAAC,EAAAjW,iBAAD,CAAkBC,GAAE,kBAAmBC,eAAc,8BAGlDomC,GAAoB,kBAAC9lB,GAAD,CAAiB37B,KAAK,WAAWob,GAAG,gBACvD4V,YAAaywB,EACbxlB,aAAa,mBACbhlC,MAAOW,KAAKkvB,MAAM85B,SAClB3vB,SAAUr5B,KAAKspD,qBACf/vB,UAAU,MAGd,yBAAKpW,UAAU,kBACb,kBAACqY,GAAD,CAAUhY,GAAG,aAAa1hB,KAAK,aAAa45B,QAAS17B,KAAKkvB,MAAMk6B,UAC9D/vB,SAAUr5B,KAAKypD,wBACjB,2BAAOxuB,QAAQ,cAAf,IACE,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,qCAGtD,uBAAG8I,KAAK,UACN,kBAAC,EAAAhJ,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,wCAI9D,yBAAKN,UAAU,kBACb,4BAAQA,UAAWwmC,EAAevhD,KAAK,UACrC,kBAAC,EAAAmb,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,iCCtFhE,MAAMiB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BunC,WAAY,CACV1oC,GAAI,mBACJC,eAAgB,CAAF,gCAKlB,MAAM0oC,WAAqBjnD,IAAAA,cACzBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXk9B,QAAS,IAGXpsD,KAAKy7B,aAAez7B,KAAKy7B,aAAarV,KAAKpmB,MAC3CA,KAAKq4C,eAAiBr4C,KAAKq4C,eAAejyB,KAAKpmB,MAC/CA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAG7Cy7B,aAAa12B,GACX/E,KAAKqvB,SAAS,CAAC+8B,QAASrnD,EAAE+iB,OAAOzoB,QAGnCg5C,eAAetzC,GACC,UAAVA,EAAEN,KACJzE,KAAK6+B,aAAa95B,GAItB85B,aAAa95B,GAEX,GADAA,EAAEgjB,iBACE/nB,KAAKkvB,MAAMk9B,QAAS,CACtB,MAAMtqD,EAAO9B,KAAKkvB,MAAMk9B,QAAQr/B,OAC1Bs/B,EAASvqD,EAAKgS,OAAO,EAAG,GAC1BhS,EAAKmB,OAAS,GAAK,CAAC,MAAO,MAAO,OAAO0E,SAAS0kD,GACpDrsD,KAAK6I,MAAMy2B,SAASx9B,GAEpB9B,KAAK6I,MAAM0f,QAAQvoB,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAASwnC,YAAa,QAK7EjpC,SACE,OACE,yBAAKE,UAAU,cACb,yBAAKA,UAAU,kBACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,sCAEvDuZ,GAAW,2BAAO50B,KAAK,OAAOgxB,YAAa4D,EAC1C39B,MAAOW,KAAKkvB,MAAMk9B,QAAS/yB,SAAUr5B,KAAKy7B,aAC1C+d,WAAYx5C,KAAKq4C,eAAgB9e,UAAQ,EAACC,WAAS,OAGvD,yBAAKrW,UAAU,kBACb,4BAAQA,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,mCASlE,UAAeyG,EAAAA,EAAAA,YAAWiiC,IC5DX,MAAMG,WAAsBpnD,IAAAA,cACzCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKqmC,SAAWnhC,IAAAA,YAEhBlF,KAAKkvB,MAAQ,CACXmX,SAAU,GACV/O,QAAS,GACTgP,YAAa,GACb4iB,aAAc,KACd/jB,KAAM,GACN5O,WAAW,GAGbv2B,KAAKusD,gBAAkBvsD,KAAKusD,gBAAgBnmC,KAAKpmB,MACjDA,KAAK0sC,mBAAqB1sC,KAAK0sC,mBAAmBtmB,KAAKpmB,MACvDA,KAAKwsD,kBAAoBxsD,KAAKwsD,kBAAkBpmC,KAAKpmB,MACrDA,KAAKysD,oBAAsBzsD,KAAKysD,oBAAoBrmC,KAAKpmB,MACzDA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAG7CynB,qBAIA8kC,gBAAgBzqD,EAAMiD,GACpB/E,KAAKqvB,SAAS,CAAC,CAACvtB,GAAOiD,EAAE+iB,OAAOzoB,OAAS,KAG3CqtC,mBAAmBlb,GACjBxxB,KAAKqvB,SAAS,CAAC65B,aAAc13B,IAG/Bg7B,kBAAkBrnB,GAChBnlC,KAAKqvB,SAAS,CAAC8V,KAAMA,IAGvBsnB,sBACEzsD,KAAKqvB,SAAS,CAACkH,WAAYv2B,KAAKkvB,MAAMqH,YAGxCsI,aAAa95B,GACXA,EAAEgjB,iBAEF,MAAMjV,EAAK9S,KAAKkvB,MAAMmX,SAAStZ,OAAOK,UAAU,EAAG5I,IAC7CkS,EAAU12B,KAAKkvB,MAAMoI,QAAQvK,OAAOK,UAAU,EAAG5I,IACjD8hB,EAActmC,KAAKkvB,MAAMoX,YAAYvZ,OAAOK,UAAU,EAAG3I,IAC3D3R,GACF9S,KAAK6I,MAAMy2B,SAASxsB,EAAIwzB,EAAatmC,KAAKkvB,MAAMg6B,aAAcxyB,EAAS12B,KAAKkvB,MAAMiW,KAAMnlC,KAAKkvB,MAAMqH,WAIvGtT,SACE,IAAI0mC,EAAgB,UAIpB,OAHI3pD,KAAK6I,MAAM4vC,WACbkR,GAAiB,aAGjB,0BAAMxmC,UAAU,aAAamc,SAAUt/B,KAAK6+B,cAC1C,yBAAK1b,UAAU,qBACb,gCACE,kBAACqX,GAAD,CACEpT,OAAQpnB,KAAK6I,MAAMue,OACnBmB,QAASvoB,KAAK6I,MAAM0f,QACpBuhC,eAAgB9pD,KAAK0sC,sBAEzB,yBAAKvpB,UAAU,SACb,2BAAOA,UAAU,QAAQ8X,QAAQ,gBAC/B,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,mBAAoBC,eAAc,2BAGxD,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,iCAAkCC,eAAc,gDAEjE2V,GAAgB,2BAAOhxB,KAAK,OAAOob,GAAG,eAAe4V,YAAaA,EACjEnP,IAAKjqB,KAAKqmC,SAAUhnC,MAAOW,KAAKkvB,MAAMmX,SAAUhN,SAAUr5B,KAAKusD,gBAAgBnmC,KAAKpmB,KAAM,YAC1Fw5B,WAAS,EAACD,UAAQ,EAACuE,SAAU,OAGnC,yBAAK3a,UAAU,SACb,2BAAOA,UAAU,QAAQ8X,QAAQ,kBAC/B,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,sCAGrD,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,8BAA+BC,eAAc,yCAE9D2V,GAAgB,2BAAOhxB,KAAK,OAAOob,GAAG,iBAAiB4V,YAAaA,EACnE/5B,MAAOW,KAAKkvB,MAAMoI,QAAS+B,SAAUr5B,KAAKusD,gBAAgBnmC,KAAKpmB,KAAM,WAAY89B,SAAU,OAGjG,yBAAK3a,UAAU,SACb,2BAAOA,UAAU,QAAQ8X,QAAQ,kBAC/B,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,kCAGzD,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,kCAAmCC,eAAc,4CAElE2V,GAAgB,2BAAOhxB,KAAK,OAAOob,GAAG,iBAAiB4V,YAAaA,EACnE/5B,MAAOW,KAAKkvB,MAAMoX,YAAajN,SAAUr5B,KAAKusD,gBAAgBnmC,KAAKpmB,KAAM,eAAgB89B,SAAU,QAI3G,yBAAK3a,UAAU,kBACb,kBAACqY,GAAD,CAAUE,QAAS17B,KAAKkvB,MAAMqH,UAAWuH,SAAU,EAAGzE,SAAUr5B,KAAKysD,sBADvE,IAEE,2BAAOnpC,QAAStjB,KAAKysD,qBAAqB,kBAAC,EAAAlpC,iBAAD,CAAkBC,GAAE,iBAC5DC,eAAc,yCAGlB,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,oBAAqBC,eAAc,+CAEpD7E,GAAU,kBAACsmB,GAAD,CACT9d,OAAQpnB,KAAK6I,MAAMue,OACnB+d,KAAMnlC,KAAKkvB,MAAMiW,KACjBE,WAAW,EACXS,cAAe9lC,KAAKwsD,kBACpB1uB,SAAU,EACVlf,MAAOA,MAEX,yBAAKuE,UAAU,kBACb,4BAAQA,UAAWwmC,GACjB,kBAAC,EAAApmC,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,gCCnH/D,MAAMiB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9B+nC,oBAAqB,CACnBlpC,GAAI,sBACJC,eAAgB,CAAF,+CAGhBkpC,kBAAmB,CACjBnpC,GAAI,oBACJC,eAAgB,CAAF,8CAGhByW,mBAAoB,CAClB1W,GAAI,qBACJC,eAAgB,CAAF,2EAKlB,MAAMmpC,WAAqB1nD,IAAAA,UACzBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX29B,YAAa,OACbC,YAAa,MAGf9sD,KAAK+sD,eAAiB/sD,KAAK+sD,eAAe3mC,KAAKpmB,MAC/CA,KAAK25B,qBAAuB35B,KAAK25B,qBAAqBvT,KAAKpmB,MAC3DA,KAAKgtD,2BAA6BhtD,KAAKgtD,2BAA2B5mC,KAAKpmB,MACvEA,KAAKitD,qBAAuBjtD,KAAKitD,qBAAqB7mC,KAAKpmB,MAC3DA,KAAKktD,gBAAkBltD,KAAKktD,gBAAgB9mC,KAAKpmB,MAGnDynB,oBACEznB,KAAK6I,MAAMgxB,aAGbkzB,eAAehoD,GACbA,EAAEgjB,iBACFyzB,GAAegG,WAAWhG,GAAe2R,YAAYnoD,OAAO62C,SAAShwB,KAAM,MAAO9mB,EAAEqjB,cAAcC,QAAQ7E,KAC1GxjB,KAAKqvB,SAAS,CAACw9B,YAAa9nD,EAAEqjB,cAAcC,QAAQ7E,KAGtDmW,qBAAqBv2B,GACnBpD,KAAK6I,MAAMswB,iBAAiB/1B,GAC5BpD,KAAKqvB,SAAS,CAACy9B,YAAa/pC,KAAAA,YAAmB3f,GAAS,KAAOA,IAGjE4pD,2BAA2BlrD,GACK,QAA1B9B,KAAKkvB,MAAM29B,cACbrR,GAAegG,WAAWhG,GAAe4R,eAAepoD,OAAO62C,SAAShwB,KAAM,QAC9E7rB,KAAK6I,MAAMwkD,cAAcvrD,IAI7BmrD,qBAAqBnrD,EAAMwkC,EAAagnB,EAASC,EAAMpoB,EAAM5O,GAC3DilB,GAAegG,WAAWhG,GAAe4R,eAAepoD,OAAO62C,SAAShwB,KAAM,QAC9E7rB,KAAK6I,MAAMwkD,mBAAc3pD,EACvB,CAAC8zB,OAAQ9K,GAAQ5qB,EAAMwrD,EAAS,KAAMhnB,GAAchP,QAASi2B,EAAMpoB,KAAMA,GAAO5O,GAGpF22B,gBAAgBhmC,GACds0B,GAAegG,WAAWhG,GAAe4R,eAAepoD,OAAO62C,SAAShwB,KAAM,QAC9E7rB,KAAK6I,MAAMwkD,cAAcnmC,GAG3BjE,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KAC7BsnC,EAA0BvnC,EAAcjmB,KAAKkvB,MAAM49B,YACvDpoC,GAASioC,kBAAoBjoC,GAASgoC,qBAClCxyB,EAAqBjU,EAAcvB,GAASwV,oBAClD,OACE,yBAAK/W,UAAU,eACb,wBAAIA,UAAU,UACZ,wBAAIA,UAAsC,SAA3BnjB,KAAKkvB,MAAM29B,YAAyB,SAAW,MAC5D,uBAAGtgC,KAAK,IAAI,UAAQ,OAAOjJ,QAAStjB,KAAK+sD,gBACvC,kBAAC,EAAAxpC,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,4BAI5D,wBAAIN,UAAsC,QAA3BnjB,KAAKkvB,MAAM29B,YAAwB,SAAW,MAC3D,uBAAGtgC,KAAK,IAAI,UAAQ,MAAMjJ,QAAStjB,KAAK+sD,gBACtC,kBAAC,EAAAxpC,iBAAD,CAAkBC,GAAE,qBAAsBC,eAAc,iCAI5D,wBAAIN,UAAsC,SAA3BnjB,KAAKkvB,MAAM29B,YAAyB,SAAW,MAC5D,uBAAGtgC,KAAK,IAAI,UAAQ,OAAOjJ,QAAStjB,KAAK+sD,gBACvC,kBAAC,EAAAxpC,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,8BAKpC,QAA3BzjB,KAAKkvB,MAAM29B,YACV,kBAACP,GAAD,CACEllC,OAAQpnB,KAAK6I,MAAMue,OACnBkY,SAAUt/B,KAAKitD,uBACU,SAA3BjtD,KAAKkvB,MAAM29B,YACT,kBAAC,GAAD,CACEvtB,SAAUt/B,KAAKktD,gBACf3kC,QAASvoB,KAAK6I,MAAM0f,UACtB,yBAAKpF,UAAU,eACb,kBAACyV,GAAD,CACEQ,YAAac,EACbf,iBAAkBn5B,KAAK25B,uBACzB,kBAAC,GAAD,CACEvS,OAAQpnB,KAAK6I,MAAMue,OACnB+P,SAAUn3B,KAAK6I,MAAMkxB,cACrBtC,SAAUz3B,KAAK6I,MAAM4uB,SACrBkB,iBAAkB60B,EAClBl3B,YAAY,EACZ0B,YAAY,EACZ5C,iBAAiB,EACjB8C,gBAAiBl4B,KAAKgtD,gCAOtC,UAAe9iC,EAAAA,EAAAA,YAAW0iC,IC/HX,MAAMa,WAA0BvoD,IAAAA,cAC7CU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX+5B,MAAO,GACPD,SAAU,GACV0E,MAAM,GAGR1tD,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKupD,kBAAoBvpD,KAAKupD,kBAAkBnjC,KAAKpmB,MACrDA,KAAKspD,qBAAuBtpD,KAAKspD,qBAAqBljC,KAAKpmB,MAG7DynB,oBACE,MAAMq0B,EAASN,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MAC3D7rB,KAAKqvB,SAAS,CAAChe,MAAOyqC,EAAOr1B,OAAOpV,MAAOs8C,OAAQ7R,EAAOr1B,OAAOknC,SAGnE9uB,aAAa95B,GAEX,GADAA,EAAEgjB,iBACE/nB,KAAKkvB,MAAM7d,MACbrR,KAAK6I,MAAM+kD,QAAQ5tD,KAAKkvB,MAAMy+B,OAAQ3tD,KAAKkvB,MAAM85B,SAASj8B,OAAQ/sB,KAAKkvB,MAAM7d,YACxE,GAAIrR,KAAKkvB,MAAMw+B,KACpB1tD,KAAK6I,MAAM8mB,eACN,CACL,MAAMs5B,EAAQjpD,KAAKkvB,MAAM+5B,MAAMl8B,OAC/B/sB,KAAKqvB,SAAS,CAAC45B,MAAOA,IACtBjpD,KAAK6I,MAAMglD,UAAU,QAAS5E,GAAO9pD,MAAK,KACxCa,KAAKqvB,SAAS,CAACq+B,MAAM,QAK3BnE,kBAAkBxkD,GAChB/E,KAAKqvB,SAAS,CAAC45B,MAAOlkD,EAAE+iB,OAAOzoB,QAGjCiqD,qBAAqBvkD,GACnB/E,KAAKqvB,SAAS,CAAC25B,SAAUjkD,EAAE+iB,OAAOzoB,QAGpC4jB,SACE,OACE,0BAAMO,GAAG,sBAAsB8b,SAAUt/B,KAAK6+B,cAC1C7+B,KAAKkvB,MAAM7d,OAASrR,KAAKkvB,MAAMy+B,OAC/B,oCAAE,2BAAO1yB,QAAQ,gBACf,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,qBAClBC,eAAc,mCAGlB,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,2BAA4BC,eAAc,wCAE3D2V,GAAgB,kBAAC2K,GAAD,CACfvgB,GAAG,eACH4V,YAAaA,EACbiL,aAAa,eACbhlC,MAAOW,KAAKkvB,MAAM85B,SAClBzvB,UAAU,EAAMC,WAAW,EAC3BH,SAAUr5B,KAAKspD,0BAEnBtpD,KAAKkvB,MAAMw+B,KACX,oCACE,6BACA,gCAAQ,uBAAGvqC,UAAU,6BAAb,aACR,6BACA,gCAAQ,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,4BAC1BC,eAAc,mJACd1Y,OAAQ,CAAEk+C,MAAO,4BAAKjpD,KAAKkvB,MAAM+5B,YAIrC,oCACE,2BAAOhuB,QAAQ,cACb,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,uBAClBC,eAAc,mDAGlB,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,0BAA2BC,eAAc,6CAE1D2V,GAAgB,2BAAOhxB,KAAK,QAAQob,GAAG,aACtC4V,YAAaA,EACbiL,aAAa,QACbhlC,MAAOW,KAAKkvB,MAAM+5B,MAClB5vB,SAAUr5B,KAAKupD,kBACfhwB,UAAQ,EAACC,WAAS,OAI1B,yBAAKrW,UAAU,kBACb,4BAAQA,UAAU,UAAU/a,KAAK,UAC9BpI,KAAKkvB,MAAM7d,OAASrR,KAAKkvB,MAAMy+B,OAChC,kBAAC,EAAApqC,iBAAD,CAAkBC,GAAE,eAAgBC,eAAc,2BAChDzjB,KAAKkvB,MAAMw+B,KACb,kBAAC,EAAAnqC,iBAAD,CAAkBC,GAAE,YAAaC,eAAc,wBAG/C,kBAAC,EAAAF,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,sCCpGtD,MAAMqqC,WAAqB5oD,IAAAA,cACxCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACX08B,SAAU/iD,EAAMwyC,cAChB0S,SAAS,GAGX/tD,KAAKguD,qBAAuBhuD,KAAKguD,qBAAqB5nC,KAAKpmB,MAC3DA,KAAKkkC,sBAAwBlkC,KAAKkkC,sBAAsB9d,KAAKpmB,MAG/DguD,qBAAqBjpD,GACnB/E,KAAKqvB,SAAS,CAACu8B,SAAU7mD,EAAE+iB,OAAOzoB,MAAO0uD,SAAS,IAGpD7pB,wBACMlkC,KAAKkvB,MAAM6+B,UACb/tD,KAAKqvB,SAAS,CAAC0+B,SAAS,IACxB/tD,KAAK6I,MAAMolD,sBAAsBjuD,KAAKkvB,MAAM08B,SAAS7+B,SAIzD9J,SACE,IAAIirC,EAAc,GAClB,IAAK,IAAIzpD,KAAOof,GAAa,CAC3B,IAAIsE,EAAOtE,GAAYpf,GACvBypD,EAAYzqD,KACV,4BAAQgB,IAAK0jB,EAAM9oB,MAAO8oB,KAG9B,OACE,yBAAKhF,UAAU,kBACb,2BAAO/a,KAAK,SAASob,GAAG,YAAY4V,YAAap5B,KAAK6I,MAAM+iD,SAAUuC,KAAK,cACzEhrC,UAAU,SAAS9jB,MAAOW,KAAKkvB,MAAM08B,SAAUvyB,SAAUr5B,KAAKguD,qBAC9DhwB,OAAQh+B,KAAKkkC,sBAAuB3K,UAAQ,IAC9C,8BAAU/V,GAAG,eACV0qC,KCtCI,MAAME,WAAqBlpD,IAAAA,cACxCU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXm/B,UAAWxlD,EAAMwlD,WAAa,MAC9BhT,cAAexyC,EAAMwyC,eAGvBr7C,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKsuD,wBAA0BtuD,KAAKsuD,wBAAwBloC,KAAKpmB,MACjEA,KAAKuuD,0BAA4BvuD,KAAKuuD,0BAA0BnoC,KAAKpmB,MAGvE6+B,aAAa95B,GACXA,EAAEgjB,iBACF/nB,KAAK6I,MAAM2lD,SAAS,CAClBH,UAAWruD,KAAKkvB,MAAMm/B,UACtBhT,cAAer7C,KAAKkvB,MAAMmsB,gBAI9BiT,wBAAwBvpD,GACtB/E,KAAKqvB,SAAS,CAACg/B,UAAWtpD,EAAEqjB,cAAc/oB,QAG5CkvD,0BAA0BzsD,GACxB9B,KAAKqvB,SAAS,CAACgsB,cAAev5C,IAGhCmhB,SACE,MAAM+c,EAAQ,CAACyuB,IAAK,UAAWC,GAAI,YAAaC,GAAI,gBAC9CC,EAAmB,GAczB,MAbA,CAAC,MAAO,KAAM,MAAM1jD,KAAKid,IACvB,MAAM3E,EAAK,aAAe2E,EACpBrmB,EAAOk+B,EAAM7X,GACnBymC,EAAiBnrD,KACf,wBAAIgB,IAAK0jB,GACP,2BAAO/f,KAAK,QAAQob,GAAIA,EAAI1hB,KAAK,mBAAmBzC,MAAO8oB,EACzDuT,QAAS17B,KAAKkvB,MAAMm/B,YAAclmC,EAClCkR,SAAUr5B,KAAKsuD,0BACjB,2BAAOrzB,QAASzX,GAAK1hB,QAMzB,0BAAM0hB,GAAG,gBAAgBL,UAAU,aAAamc,SAAUt/B,KAAK6+B,cAC7D,yBAAK1b,UAAU,kBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,sBAAuBC,eAAc,sCAI7D,kBAACqqC,GAAD,CAAczS,cAAer7C,KAAKkvB,MAAMmsB,cACtC4S,sBAAuBjuD,KAAKuuD,4BAC9B,yBAAKprC,UAAU,kBACb,2BAAOA,UAAU,SACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,uBAAwBC,eAAc,uCAI9D,yBAAKN,UAAU,kBACb,wBAAIA,UAAU,UACXyrC,IAGL,yBAAKzrC,UAAU,kBACb,4BAAQ/a,KAAK,SAAS+a,UAAU,WAC9B,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,gCCvE/D,MAAMiB,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BkqC,MAAO,CACLrrC,GAAI,eACJC,eAAgB,CAAF,yBAGhBwlC,MAAO,CACLzlC,GAAI,eACJC,eAAgB,CAAF,2BAKlB,MAAMqrC,WAAuB5pD,IAAAA,cAC3BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKkvB,MAAQ,CACXhpB,KAAM2C,EAAMkmD,UAAY,IAG1B/uD,KAAKy7B,aAAez7B,KAAKy7B,aAAarV,KAAKpmB,MAC3CA,KAAKq4C,eAAiBr4C,KAAKq4C,eAAejyB,KAAKpmB,MAC/CA,KAAK6+B,aAAe7+B,KAAK6+B,aAAazY,KAAKpmB,MAC3CA,KAAKm8B,aAAen8B,KAAKm8B,aAAa/V,KAAKpmB,MAG7Cy7B,aAAa12B,GACX/E,KAAKqvB,SAAS,CAACnpB,KAAMnB,EAAE+iB,OAAOzoB,QAGhCg5C,eAAetzC,GACC,UAAVA,EAAEN,IACJzE,KAAK6+B,aAAa95B,GACA,UAATA,EAAEN,KACXzE,KAAKm8B,aAAap3B,GAItB85B,aAAa95B,GACXA,EAAEgjB,iBACE/nB,KAAKkvB,MAAMhpB,MAAQlG,KAAKkvB,MAAMhpB,KAAK6mB,QACrC/sB,KAAK6I,MAAMy2B,SAASt/B,KAAK6I,MAAMmmD,WAAYhvD,KAAKkvB,MAAMhpB,KAAK6mB,QAI/DoP,aAAap3B,GACXA,EAAEgjB,iBACF/nB,KAAK6I,MAAM8mB,WAIb1M,SACE,MAAM,cAAEgD,GAAkBjmB,KAAK6I,MAAMqd,KAErC,IAAInnB,EADY,CAAC,MAASknB,EAAcvB,GAASukC,OAAQ,IAAOhjC,EAAcvB,GAASmqC,QAClE7uD,KAAK6I,MAAMmmD,aAAehvD,KAAK6I,MAAMmmD,WAC1D,OACE,yBAAK7rC,UAAU,cACb,yBAAKA,UAAU,kBACb,2BAAOA,UAAU,QAAQ8X,QAAQ,2BAC/B,kBAAC,EAAA1X,iBAAD,CAAkBC,GAAE,iCAClBC,eAAc,sGAEd1Y,OAAQ,CAAChM,OAAQA,OAGvB,yBAAKokB,UAAU,kBACf,kBAAC,EAAAI,iBAAD,CAAkBC,GAAE,mCAClBC,eAAc,kCACbwrC,GAAiB,2BAAO7mD,KAAK,OAAOob,GAAG,0BACtC4V,YAAa61B,EACb5vD,MAAOW,KAAKkvB,MAAMhpB,KAAMmzB,SAAUr5B,KAAKy7B,aACvC+d,WAAYx5C,KAAKq4C,eAAgB9e,UAAQ,OAG7C,yBAAKpW,UAAU,kBACb,4BAAQA,UAAU,YAAYG,QAAStjB,KAAKm8B,cAC1C,kBAAC,EAAA5Y,iBAAD,CAAkBC,GAAE,gBAAiBC,eAAc,6BAErD,4BAAQN,UAAU,UAAUG,QAAStjB,KAAK6+B,cACxC,kBAAC,EAAAtb,iBAAD,CAAkBC,GAAE,iBAAkBC,eAAc,iCAQhE,UAAeyG,EAAAA,EAAAA,YAAW4kC,ICvEpBpqC,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BokC,MAAO,CACLvlC,GAAI,wBAEJC,eAAgB,CAAF,2BAEhBrD,SAAU,CACRoD,GAAI,2BAEJC,eAAgB,CAAF,kCAEhByrC,SAAU,CACR1rC,GAAI,2BAEJC,eAAgB,CAAF,4BAEhB0rC,KAAM,CACJ3rC,GAAI,mCAEJC,eAAgB,CAAF,oCAEhBwnB,QAAS,CACPznB,GAAI,sBAEJC,eAAgB,CAAF,2BAEhBynB,SAAU,CACR1nB,GAAI,uBAEJC,eAAgB,CAAF,4BAEhB0nB,KAAM,CACJ3nB,GAAI,mBAEJC,eAAgB,CAAF,kCAEhB2rC,MAAO,CACL5rC,GAAI,oCAEJC,eAAgB,CAAF,iCAEhB4rC,QAAS,CACP7rC,GAAI,8BAEJC,eAAgB,CAAF,2BAEhB6rC,OAAQ,CACN9rC,GAAI,yBAEJC,eAAgB,CAAF,kCAEhB2lB,KAAM,CACJ5lB,GAAI,uBAEJC,eAAgB,CAAF,uCAEhB8rC,MAAO,CACL/rC,GAAI,wBAEJC,eAAgB,CAAF,kCAEhBkC,QAAS,CACPnC,GAAI,0BAEJC,eAAgB,CAAF,kCAEhBwkC,QAAS,CACPzkC,GAAI,0BAEJC,eAAgB,CAAF,mCAIlB,MAAM+rC,WAAsBtqD,IAAAA,UAC1BU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKyvD,eAAiBzvD,KAAKyvD,eAAerpC,KAAKpmB,MAGjDyvD,iBACEzvD,KAAK6I,MAAMknC,WAAW,UAGxB9sB,SACE,MAAM,cAACgD,GAAiBjmB,KAAK6I,MAAMqd,KAC7BiqB,EAAOnwC,KAAK6I,MAAMqmB,QAAUlvB,KAAK6I,MAAM4uB,SAAW,WAAa,SAErE,IAAI7Y,EAAO4P,EAAQnE,EAWfsF,EAKJ,MAfY,YAARwgB,GACFvxB,EAAQ5e,KAAK6I,MAAM+V,MACnB4P,GAASxuB,KAAK6I,MAAM2lB,QAASxuB,KAAK6I,MAAM2lB,OACxCnE,EAASrqB,KAAK6I,MAAM0yB,gBAEpB3c,EAAQqH,EAAcvB,GAASyrB,IAC/B3hB,GAAS,EACTnE,EAAS,OAIiC,GAAxC,CAAC,QAAS,YAAYgD,QAAQ8iB,KAChCxgB,EAAW3vB,KAAK6I,MAAM8mB,UAItB,yBAAKnM,GAAG,YAAYL,UAAWnjB,KAAK6I,MAAM27C,SAAW,YAAc,MACjE,kBAACgD,GAAD,CACEt4B,MAAOihB,EACPvxB,MAAOA,EACP4P,OAAQA,EACRpH,OAAQpnB,KAAK6I,MAAMue,OACnBmU,cAAelR,EACfoN,SAAUz3B,KAAK6I,MAAM4uB,SACrB8vB,SAAUvnD,KAAK6I,MAAM0+C,SACrBF,WAAYrnD,KAAK6I,MAAMw+C,WACvBD,WAAYpnD,KAAKyvD,eACjB9/B,SAAUA,IAEZ,kBAACiM,GAAD,CACEC,MAAO77B,KAAK6I,MAAM0nC,WAClBxU,KAAM/7B,KAAK6I,MAAM2nC,UACjB5Z,OAAQ52B,KAAK6I,MAAM6mD,YACnBzzB,WAAYj8B,KAAK6I,MAAM8mD,gBACvB7zB,aAAc97B,KAAK6I,MAAM0f,UAE3B,kBAAC6R,GAAD,CAAaG,KAAMv6B,KAAK6I,MAAM+mD,qBAEpB,UAATzf,EACC,kBAACwb,GAAD,CACE5C,MAAO/oD,KAAK6I,MAAMkgD,MAClBtQ,SAAUz4C,KAAK6I,MAAMgnD,cACrBhE,QAAS7rD,KAAK6I,MAAMgjD,QACpBE,QAAS/rD,KAAK6I,MAAMinD,eACpBhE,oBAAqB9rD,KAAK6I,MAAMijD,sBAEzB,aAAT3b,EACA,kBAAC2Y,GAAD,CACE1hC,OAAQpnB,KAAK6I,MAAMue,OACnBsiC,gBAAiB1pD,KAAK6I,MAAM6gD,gBAC5B/5B,SAAU3vB,KAAK6I,MAAM8mB,SACrBpH,QAASvoB,KAAK6I,MAAM0f,UAEb,aAAT4nB,EACA,kBAACie,GAAD,CACEC,UAAWruD,KAAK6I,MAAMwlD,UACtBhT,cAAer7C,KAAK6I,MAAMwyC,cAC1B1rB,SAAU3vB,KAAK6I,MAAM8mB,SACrB6+B,SAAUxuD,KAAK6I,MAAMknD,mBAEd,SAAT5f,EACA,kBAACsX,GAAD,CACErgC,OAAQpnB,KAAK6I,MAAMue,OACnBqQ,SAAUz3B,KAAK6I,MAAM4uB,SACrB8D,cAAev7B,KAAK6I,MAAM0yB,cAC1BwU,WAAY/vC,KAAK6I,MAAMknC,aAEf,YAATI,GAA+B,SAATA,EACvB,kBAACjI,GAAD,CACE/gB,MAAM,KACNC,OAAQpnB,KAAK6I,MAAMue,OACnBqQ,SAAUz3B,KAAK6I,MAAM4uB,SACrB2P,kBAAmBpnC,KAAK6I,MAAMwiD,oBAC9BzjB,aAAc5nC,KAAK6I,MAAMmnD,oBACzB9mB,UAAWlpC,KAAK6I,MAAMqgC,UACtBM,aAAcxpC,KAAK6I,MAAM2gC,aACzBD,cAAevpC,KAAK6I,MAAM0gC,cAC1BhhB,QAASvoB,KAAK6I,MAAM0f,UAEb,UAAT4nB,EACA,kBAAC,GAAD,CACEoa,cAAevqD,KAAK6I,MAAM0hD,cAC1BE,cAAezqD,KAAK6I,MAAM4hD,cAC1BD,qBAAsBxqD,KAAK6I,MAAM2hD,qBACjCE,cAAe1qD,KAAK6I,MAAM6hD,cAC1BL,0BAA2BrqD,KAAK6I,MAAMwhD,0BACtCD,sBAAuBpqD,KAAK6I,MAAMuhD,sBAClCE,sBAAuBtqD,KAAK6I,MAAMyhD,wBAE3B,aAATna,EACA,kBAAC,GAAD,CACE/oB,OAAQpnB,KAAK6I,MAAMue,OACnBikC,oBAAqBrrD,KAAK6I,MAAMwiD,oBAChCD,iBAAkBprD,KAAK6I,MAAMuiD,iBAC7BI,SAAUxrD,KAAK6I,MAAM2iD,SACrBF,gBAAiBtrD,KAAK6I,MAAMyiD,gBAC5B3kC,YAAa3mB,KAAK6I,MAAM8d,YACxB8kC,cAAezrD,KAAK6I,MAAM4iD,gBAEnB,YAATtb,EACA,kBAACub,GAAD,CACErQ,cAAer7C,KAAK6I,MAAMwyC,cAC1BD,cAAep7C,KAAK6I,MAAMuyC,gBAElB,aAATjL,GAA+B,WAARA,GAA6B,WAARA,EAC7C,kBAACyX,GAAD,CACExgC,OAAQpnB,KAAK6I,MAAMue,OACnBqQ,SAAUz3B,KAAK6I,MAAM4uB,SACrBQ,UAAWj4B,KAAK6I,MAAMovB,UACtBjB,cAAeh3B,KAAK6I,MAAMmuB,cAC1BrR,QAAiB,WAARwqB,EACT8X,QAAiB,WAAR9X,EACT6X,SAAUhoD,KAAK6I,MAAMm/C,SACrB5yB,gBAAiBp1B,KAAK6I,MAAMusB,gBAC5B8C,gBAAiBl4B,KAAK6I,MAAMqvB,gBAC5BmwB,cAAeroD,KAAK6I,MAAMw/C,gBAEnB,WAATlY,EACA,kBAAC,GAAD,CACE/oB,OAAQpnB,KAAK6I,MAAMue,OACnB2S,cAAe/5B,KAAK6I,MAAMkxB,cAC1BF,WAAY75B,KAAK6I,MAAMgxB,WACvBV,iBAAkBn5B,KAAK6I,MAAMswB,iBAC7Bk0B,cAAertD,KAAK6I,MAAMwkD,cAC1B9kC,QAASvoB,KAAK6I,MAAM0f,UAEb,SAAT4nB,EACA,kBAAC,GAAD,CACE4e,SAAU/uD,KAAK6I,MAAMkmD,SACrBC,WAAYhvD,KAAK6I,MAAMmmD,WACvB1vB,SAAUt/B,KAAK6I,MAAMonD,sBACrBtgC,SAAU3vB,KAAK6I,MAAM8mB,SACrBpH,QAASvoB,KAAK6I,MAAM0f,UAEb,UAAT4nB,EACA,kBAACsd,GAAD,CACEI,UAAW7tD,KAAK6I,MAAMqnD,uBACtBtC,QAAS5tD,KAAK6I,MAAMsnD,gBACpBxgC,SAAU3vB,KAAK6I,MAAM8mB,WACvB,OAMV,UAAezF,EAAAA,EAAAA,YAAWslC,IC5PnB,SAASY,KACd,IAAIC,EAAOrsC,GAQX,MAP8B,iBAAnBhf,OAAO62C,WACgB,SAA5B72C,OAAO62C,SAASyU,UAAmD,aAA5BtrD,OAAO62C,SAAS0U,SACzDF,EAAOxsC,GAAYE,MACV/e,OAAO62C,SAAS0U,WACzBF,EAAOrrD,OAAO62C,SAAS0U,UAAYvrD,OAAO62C,SAAS2U,KAAO,IAAMxrD,OAAO62C,SAAS2U,KAAO,MAGpFH,EAIF,SAASI,KACd,MAA8B,iBAAnBzrD,OAAO62C,UACmB,UAA5B72C,OAAO62C,SAASyU,SCW3B,MAAMI,GAAY,IAAIC,MAAM,iBAEtBjsC,IAAWC,EAAAA,EAAAA,gBAAe,CAC9BisC,oBAAqB,CACnBptC,GAAI,sBACJC,eAAgB,CAAF,8FAGhBotC,cAAe,CACbrtC,GAAI,gBACJC,eAAgB,CAAF,2BAGhBqtC,iBAAkB,CAChBttC,GAAI,mBACJC,eAAgB,CAAF,2DAGhBstC,uBAAwB,CACtBvtC,GAAI,yBACJC,eAAgB,CAAF,0CAGhButC,cAAe,CACbxtC,GAAI,gBACJC,eAAgB,CAAF,iCAGhBwtC,mBAAoB,CAClBztC,GAAI,qBACJC,eAAgB,CAAF,uCAGhBytC,eAAgB,CACd1tC,GAAI,iBACJC,eAAgB,CAAF,0BAKlB,MAAM0tC,WAAkBjsD,IAAAA,UACtBU,YAAYiD,GACVxC,MAAMwC,GAEN7I,KAAKgmB,QAAU9gB,IAAAA,YAEflF,KAAKkvB,MAAQlvB,KAAKoxD,gBAElBpxD,KAAKqxD,aAAerxD,KAAKqxD,aAAajrC,KAAKpmB,MAC3CA,KAAKsxD,gBAAkBtxD,KAAKsxD,gBAAgBlrC,KAAKpmB,MACjDA,KAAKuxD,aAAevxD,KAAKuxD,aAAanrC,KAAKpmB,MAC3CA,KAAKwxD,kBAAoBxxD,KAAKwxD,kBAAkBprC,KAAKpmB,MACrDA,KAAKyxD,sBAAwBzxD,KAAKyxD,sBAAsBrrC,KAAKpmB,MAC7DA,KAAK0xD,YAAc1xD,KAAK0xD,YAAYtrC,KAAKpmB,MACzCA,KAAK2xD,mBAAqB3xD,KAAK2xD,mBAAmBvrC,KAAKpmB,MACvDA,KAAK4xD,wBAA0B5xD,KAAK4xD,wBAAwBxrC,KAAKpmB,MACjEA,KAAK6xD,gBAAkB7xD,KAAK6xD,gBAAgBzrC,KAAKpmB,MACjDA,KAAK8xD,6BAA+B9xD,KAAK8xD,6BAA6B1rC,KAAKpmB,MAC3EA,KAAK+xD,QAAU/xD,KAAK+xD,QAAQ3rC,KAAKpmB,MACjCA,KAAKgyD,yBAA2BhyD,KAAKgyD,yBAAyB5rC,KAAKpmB,MACnEA,KAAKiyD,sBAAwBjyD,KAAKiyD,sBAAsB7rC,KAAKpmB,MAC7DA,KAAKkyD,iBAAmBlyD,KAAKkyD,iBAAiB9rC,KAAKpmB,MACnDA,KAAKmyD,aAAenyD,KAAKmyD,aAAa/rC,KAAKpmB,MAC3CA,KAAKoyD,kBAAoBpyD,KAAKoyD,kBAAkBhsC,KAAKpmB,MACrDA,KAAKqyD,gBAAkBryD,KAAKqyD,gBAAgBjsC,KAAKpmB,MACjDA,KAAKsyD,iBAAmBtyD,KAAKsyD,iBAAiBlsC,KAAKpmB,MACnDA,KAAKuyD,OAASvyD,KAAKuyD,OAAOnsC,KAAKpmB,MAC/BA,KAAKwyD,WAAaxyD,KAAKwyD,WAAWpsC,KAAKpmB,MACvCA,KAAKyyD,iBAAmBzyD,KAAKyyD,iBAAiBrsC,KAAKpmB,MACnDA,KAAK25B,qBAAuB35B,KAAK25B,qBAAqBvT,KAAKpmB,MAC3DA,KAAK0yD,oBAAsB1yD,KAAK0yD,oBAAoBtsC,KAAKpmB,MACzDA,KAAK2yD,uBAAyB3yD,KAAK2yD,uBAAuBvsC,KAAKpmB,MAC/DA,KAAK4yD,kBAAoB5yD,KAAK4yD,kBAAkBxsC,KAAKpmB,MACrDA,KAAK6yD,wBAA0B7yD,KAAK6yD,wBAAwBzsC,KAAKpmB,MACjEA,KAAK8yD,iBAAmB9yD,KAAK8yD,iBAAiB1sC,KAAKpmB,MACnDA,KAAK+yD,wBAA0B/yD,KAAK+yD,wBAAwB3sC,KAAKpmB,MACjEA,KAAKgzD,4BAA8BhzD,KAAKgzD,4BAA4B5sC,KAAKpmB,MACzEA,KAAKizD,+BAAiCjzD,KAAKizD,+BAA+B7sC,KAAKpmB,MAC/EA,KAAKkzD,0BAA4BlzD,KAAKkzD,0BAA0B9sC,KAAKpmB,MACrEA,KAAKmzD,eAAiBnzD,KAAKmzD,eAAe/sC,KAAKpmB,MAC/CA,KAAKozD,qBAAuBpzD,KAAKozD,qBAAqBhtC,KAAKpmB,MAC3DA,KAAKqzD,kBAAoBrzD,KAAKqzD,kBAAkBjtC,KAAKpmB,MACrDA,KAAKszD,kBAAoBtzD,KAAKszD,kBAAkBltC,KAAKpmB,MACrDA,KAAKuzD,0BAA4BvzD,KAAKuzD,0BAA0BntC,KAAKpmB,MACrEA,KAAKwzD,cAAgBxzD,KAAKwzD,cAAcptC,KAAKpmB,MAC7CA,KAAKyzD,iBAAmBzzD,KAAKyzD,iBAAiBrtC,KAAKpmB,MACnDA,KAAK0zD,kBAAoB1zD,KAAK0zD,kBAAkBttC,KAAKpmB,MACrDA,KAAK2zD,gBAAkB3zD,KAAK2zD,gBAAgBvtC,KAAKpmB,MACjDA,KAAK4zD,eAAiB5zD,KAAK4zD,eAAextC,KAAKpmB,MAC/CA,KAAK6zD,kBAAoB7zD,KAAK6zD,kBAAkBztC,KAAKpmB,MACrDA,KAAK8zD,sBAAwB9zD,KAAK8zD,sBAAsB1tC,KAAKpmB,MAC7DA,KAAK+zD,wBAA0B/zD,KAAK+zD,wBAAwB3tC,KAAKpmB,MACjEA,KAAKg0D,sBAAwBh0D,KAAKg0D,sBAAsB5tC,KAAKpmB,MAC7DA,KAAKi0D,yBAA2Bj0D,KAAKi0D,yBAAyB7tC,KAAKpmB,MACnEA,KAAK4sC,gBAAkB5sC,KAAK4sC,gBAAgBxmB,KAAKpmB,MACjDA,KAAKk0D,wBAA0Bl0D,KAAKk0D,wBAAwB9tC,KAAKpmB,MACjEA,KAAKm0D,wBAA0Bn0D,KAAKm0D,wBAAwB/tC,KAAKpmB,MACjEA,KAAKo0D,aAAep0D,KAAKo0D,aAAahuC,KAAKpmB,MAC3CA,KAAKkrD,oBAAsBlrD,KAAKkrD,oBAAoB9kC,KAAKpmB,MACzDA,KAAKq0D,yBAA2Br0D,KAAKq0D,yBAAyBjuC,KAAKpmB,MACnEA,KAAKs0D,4BAA8Bt0D,KAAKs0D,4BAA4BluC,KAAKpmB,MACzEA,KAAKu0D,wBAA0Bv0D,KAAKu0D,wBAAwBnuC,KAAKpmB,MACjEA,KAAKw0D,wBAA0Bx0D,KAAKw0D,wBAAwBpuC,KAAKpmB,MACjEA,KAAKy0D,kBAAoBz0D,KAAKy0D,kBAAkBruC,KAAKpmB,MACrDA,KAAK00D,sBAAwB10D,KAAK00D,sBAAsBtuC,KAAKpmB,MAC7DA,KAAK20D,wBAA0B30D,KAAK20D,wBAAwBvuC,KAAKpmB,MACjEA,KAAK40D,sBAAwB50D,KAAK40D,sBAAsBxuC,KAAKpmB,MAC7DA,KAAK60D,gBAAkB70D,KAAK60D,gBAAgBzuC,KAAKpmB,MACjDA,KAAK80D,mBAAqB90D,KAAK80D,mBAAmB1uC,KAAKpmB,MACvDA,KAAKgtC,0BAA4BhtC,KAAKgtC,0BAA0B5mB,KAAKpmB,MACrEA,KAAK+0D,iCAAmC/0D,KAAK+0D,iCAAiC3uC,KAAKpmB,MACnFA,KAAKg1D,2BAA6Bh1D,KAAKg1D,2BAA2B5uC,KAAKpmB,MACvEA,KAAKi1D,oBAAsBj1D,KAAKi1D,oBAAoB7uC,KAAKpmB,MACzDA,KAAKk1D,wBAA0Bl1D,KAAKk1D,wBAAwB9uC,KAAKpmB,MAEjEA,KAAKm1D,wBAA0Bn1D,KAAKm1D,wBAAwB/uC,KAAKpmB,MACjEA,KAAKo1D,wBAA0Bp1D,KAAKo1D,wBAAwBhvC,KAAKpmB,MAEjEA,KAAKq1D,mBAAqBr1D,KAAKq1D,mBAAmBjvC,KAAKpmB,MAGzDoxD,gBACE,MAAMlC,EAAW5G,GAAiBK,UAAU,aAAe,GACrDkD,IAAYvD,GAAiBK,UAAU,kBAE7C,MAAO,CACL1wB,WAAW,EAEXqnB,OAAO,EAEPgW,WAAW,EACXjH,UAAWa,EAASb,WAAa,KACjChT,cAAe6T,EAAS7T,eAAiB+U,KACzChV,cAAe,gBAEfmP,eAAgB2E,EAASqG,iBACzB7K,eAAe,EAEfD,cAAeoB,KAAaqD,EAASzE,cAErCD,sBAAuBiG,MDjJG,iBAAnBzrD,OAAO62C,UACmB,aAA5B72C,OAAO62C,SAAS0U,WCiJ6C,oBAAbp6C,WAC1B,oBAAjBq/C,cACVC,cAAe5J,EAAUvD,GAAiBK,UAAU,kBAAoB,KAExEtJ,oBAAqB33B,SAASguC,OAE9BllB,UAAW,GACXD,WAAY,KACZmf,iBAAahsD,EACbisD,gBAAiB,KAEjBgG,kBAAmB,QACnBC,eAAgB,KAChBC,gBAAiB,KACjBC,gBAAiB,GACjBlG,oBAAoB,EAEpB7G,MAAO,GACPC,SAAU,GACV6C,QAASA,EACTp0B,SAAU,KACVs+B,eAAgB5/C,UAAUC,OAC1B4gB,cAAe,GACfg/B,qBAAqB,EACrBC,iBAAkB,KAClBrV,eAAgB,KAChBiP,eAAe,EACfvf,cAAgBtrC,OAAOkxD,YtE1JG,IsE2J1BC,eAAWzyD,EACX0yD,YAAa,YAEbC,oBAAoB,EACpBC,kBAAmB,KACnBC,mBAAoB,KACpBC,kBAAmB,KACnBC,iBAAkB,GAElBC,sBAAsB,EACtBjX,eAAgB,KAGhBkX,cAAc,EACdC,YAAa,GAGb5O,SAAU,GAEVjuB,cAAe,GAEf0W,mBAAoB,GACpBue,gBAAYtrD,EACZqrD,cAAUrrD,EAEVmzD,oBAAgBnzD,GAIpB+jB,oBAOE,GANAziB,OAAO8d,iBAAiB,SAAU9iB,KAAKqxD,cACvCrsD,OAAO8d,iBAAiB,UAAW/d,IAAQ/E,KAAKuxD,cAAa,MAC7DvsD,OAAO8d,iBAAiB,WAAY/d,IAAQ/E,KAAKuxD,cAAa,MAC9DvsD,OAAO8d,iBAAiB,aAAc9iB,KAAKsxD,iBAGZ,mBAApB18C,iBAAgC,CACZ,IAAIA,iBAAiB,aAC7BkO,iBAAiB,UAAW9iB,KAAK6zD,wBAGtDtmD,QAAQoB,KAAK,uFAIf+Y,SAAS5E,iBAAiB,mBAAoB9iB,KAAKyxD,uBAEnDzxD,KAAKqvB,SAAS,CACZgE,cAAe3L,SAASovC,gBAAgBC,YACxCC,eAAgBtvC,SAASovC,gBAAgBG,eAG3C,IAAI34D,SAAQ,CAACC,EAASC,KACpBwB,KAAKonB,OAAS+pC,GAAU+F,QAAQl3D,KAAKkvB,MAAMmsB,cAAer7C,KAAKkvB,MAAMm/B,UACnEruD,KAAK6I,MAAMqd,KAAKwrB,OAAQ1xC,KAAKkvB,MAAM28B,QAASttD,GAC9CyB,KAAKonB,OAAO+vC,UAAYn3D,KAAK6xD,gBAC7B7xD,KAAKonB,OAAOgwC,aAAep3D,KAAKkyD,iBAChClyD,KAAKonB,OAAOiwC,yBAA2Br3D,KAAK8xD,gCAC3C3yD,MAAK,KAEFa,KAAKkvB,MAAMs7B,sBACbxqD,KAAK2zD,kBAAkBx0D,MAAK,KACtBa,KAAKkvB,MAAMu7B,eACbzqD,KAAKonB,OAAOkwC,eAAet3D,KAAKkvB,MAAMumC,kBAEvC1vD,OAAM,SAOX,MAAMwxD,EAAY/b,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MAG9D7rB,KAAKsyD,mBAEL,MAAMjhD,EAAQrR,KAAKkvB,MAAM28B,QAAUvD,GAAiBK,UAAU,mBAAgBjlD,EAC1E2N,IACFrR,KAAKqvB,SAAS,CAACimC,WAAW,IAG1BjkD,EAAMmmD,QAAU,IAAInqD,KAAKgE,EAAMmmD,SAC/Bx3D,KAAKonB,OAAOqwC,aAAapmD,GACzBrR,KAAKonB,OAAO2vB,UAAUhxC,OAAOkW,IAE3Bjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,WAK7B,CAAC,OAAQ,QAAS,YAAYwB,SAAS4vD,EAAU7b,KAAK,MAEzD17C,KAAKqvB,SAAS,CAACwnC,eAAgBU,EAAU7b,KAAK,KAC9CF,GAAegG,WAAW,KAG5BxhD,KAAK03D,UAAY,KACjB13D,KAAK23D,kBAAoB,KAEzB33D,KAAKsxD,qBAIT3pC,uBACE3iB,OAAO4iB,oBAAoB,SAAU5nB,KAAKqxD,cAC1CrsD,OAAO4iB,oBAAoB,aAAc5nB,KAAKsxD,iBAC9C5pC,SAASE,oBAAoB,mBAAoB5nB,KAAKyxD,uBAI1C,eAACpW,EAAegT,EAAW3c,EAAQkmB,EAAiBC,GAChE,MAAMzwC,EAAS,IAAIrE,KAAJ,CAAW,CAAC/S,QAAS4T,GAAUysC,KAAMhV,EAAejpC,OtEhThD,mCsEgTiEi8C,UAAWA,EAC7FyJ,OAAQrH,KAAsB5E,QAAS+L,GAAkBC,GAG3D,OAFAzwC,EAAO2wC,iBAAiBrmB,GACxBtqB,EAAO4wC,ctE1SoB,MsE0SW,GAC/B5wC,EAITysC,kBAAkBx1C,GAChB,MAAMxX,EAAOwX,EAAQxX,MAAQ,GAC7B,OAAQA,EAAK67C,MACb,IAAK,MACC3/B,KAAAA,mBAA0Blc,EAAKsgB,QAEjCnnB,KAAKonB,OAAO6wC,gBAAgB,MAAOpxD,EAAKsgB,MAAOtgB,EAAK6hB,IAAK,YAE3D,MACF,IAAK,OAEH1oB,KAAKonB,OAAO6wC,gBAAgB,OAAQpxD,EAAKsgB,MAAOtgB,EAAK6hB,KACrD,MACF,QACEnb,QAAQoB,KAAK,4BAA6B9H,EAAK67C,KAAM77C,IAIzD8sD,kBACE,MAAM,cAAC1tC,EAAD,OAAgByrB,GAAU1xC,KAAK6I,MAAMqd,KACrCqC,EAAU,CAACO,EAAK7M,KACpB1O,QAAQ3O,MAAMkqB,EAAK7M,GACnBjc,KAAK0xD,YAAYzrC,EAAcvB,GAASosC,kBAAmB,OAC3D9wD,KAAKqvB,SAAS,CAACm7B,sBAAsB,EAAOiL,cAAe,OAC3DnN,GAAiB4P,aAAa,WAAY,CAACzN,eAAe,KAG5D,IAEE,OADAzqD,KAAKm4D,IAAMC,GAAqBC,EAAgB7C,cAAe5xC,KACxDzN,UAAUgK,cAAcC,SAAS,sBAAsBjhB,MAAMm5D,IAClEt4D,KAAKwxD,kBAAkB8G,GACvBA,EAAI/zB,OAAO/vB,YAAYoC,KAAKC,UAAU,CAAC66B,OAAQA,EAAQ3vC,QAAS8O,MACzDynD,KACNn5D,MAAMm5D,GAEAnH,GAAUoH,gBAAgBv4D,KAAKm4D,IAAKG,KAC1Cn5D,MAAMkS,IACP,MAAMw6C,EAAUvD,GAAiBK,UAAU,kBACvCt3C,GAASrR,KAAKkvB,MAAMumC,gBACtBz1D,KAAKonB,OAAOkwC,eAAejmD,GACvBw6C,GACFvD,GAAiBM,UAAU,iBAAkBv3C,IAGjDrR,KAAKqvB,SAAS,CAAComC,cAAepkD,EAAOo5C,eAAe,IAChDoB,GACFvD,GAAiB4P,aAAa,WAAY,CAACzN,eAAe,IAM5D+N,GAAkBx4D,KAAKm4D,KAAM95C,IAAcre,KAAK6zD,kBAAkBx1C,SACjEtY,OAAOkW,IAGR,MADAsM,EAAQtM,GACFA,KAER,MAAOA,GAEP,OADAsM,EAAQtM,GACD3d,QAAQE,OAAOyd,IAKJ,uBAACk8C,EAAKM,GAC1B,OAAOC,GAAiBP,EAAK,CAC3Bt3C,0BAA2B43C,EAC3Bj9C,SAAUg6C,cAAcmD,oBACvBx5D,MAAMkS,GACHA,GAIKkP,aAAaE,oBAAoBthB,MAAMqhB,IAC5C,GAAmB,YAAfA,EACF,OAAOk4C,GAAiBP,EAAK,CAC3Bt3C,0BAA2By3C,IAC3B98C,SAAUg6C,cAAcmD,oBACvBx5D,MAAMkS,IACP,GAAIA,EACF,OAAOA,EAEP,MAAM,IAAIpL,MAAM,yCAIpB,MAAM,IAAIA,MAAM,wCAA0Cua,QAOpE6wC,eACE,MAAMuH,EAASlxC,SAASovC,gBAAgBC,atElXZ,IsEmX5B/2D,KAAKqvB,SAAS,CACZgE,cAAe3L,SAASovC,gBAAgBC,YACxCC,eAAgBtvC,SAASovC,gBAAgBG,eAEvCj3D,KAAKkvB,MAAMohB,eAAiBsoB,GAC9B54D,KAAKqvB,SAAS,CAACihB,cAAesoB,IAKlCpH,kBAAkB8G,GAChBA,EAAIO,cAAgB,KAClB,MAAMC,EAAmBR,EAAIS,WAC7BD,EAAiBE,cAAgB,KAC/B,GAA8B,aAA1BF,EAAiB5pC,OAAwB/Y,UAAUgK,cAAc84C,WAAY,CAC/E,MAAMnwC,EAAM,oCACV,kBAAC,EAAAvF,iBAAD,CAAkBC,GAAE,mBAClBC,eAAc,uCAFN,IAGwD,uBAAG8I,KAAK,IACxE,kBAAC,EAAAhJ,iBAAD,CAAkBC,GAAE,gBAClBC,eAAc,6BALR,KAQZzjB,KAAK0xD,YAAY5oC,EAAK,WAO9BwoC,kBACE,MAAMzlC,EAAO2vB,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MACzD,GAAIA,EAAK6vB,MAAQ7vB,EAAK6vB,KAAKz4C,OAAS,EAAG,CAEjC,CAAC,WAAW,WAAW,OAAO,QAAQ,WAAW,UAAU,UAAU,OACrE,OAAO,QAAQ,SAAS,UAAU,UAAU,WAAW,IAAI0E,SAASkkB,EAAK6vB,KAAK,IAChF17C,KAAKqvB,SAAS,CAACsmC,kBAAmB9pC,EAAK6vB,KAAK,KAE5CnuC,QAAQoB,KAAK,yBAA0Bkd,EAAK6vB,KAAK,IAInD,IAAIx0B,EAAY2E,EAAK6vB,KAAK,IAAM,KAC5Bx0B,GAAalnB,KAAKkvB,MAAM8H,gBACrBjU,KAAAA,UAAiBmE,KAEpBA,EAAY,MAEdlnB,KAAKqvB,SAAS,CACZ2H,cAAe9P,EACf+uC,iBAAkBj2D,KAAKonB,OAAO8xC,mBAAmBhyC,WAKrDlnB,KAAKqvB,SAAS,CAACsmC,kBAAmB,GAAI3+B,cAAe,OAInDnL,EAAKpF,OAAO1nB,QACdiB,KAAKqvB,SAAS,CAAE2/B,WAAYnjC,EAAKpF,OAAO1nB,SAEtC8sB,EAAKpF,OAAOvgB,MACdlG,KAAKqvB,SAAS,CAAE0/B,SAAUljC,EAAKpF,OAAOvgB,OAIxClG,KAAKqvB,SAAS,CACZ8mC,UAAWtqC,EAAKpF,OAAO/X,KACvByqD,oBAAqBttC,EAAKpF,OAAO2yC,MAIrC7H,aAAah8B,GACPA,GACFv1B,KAAK0xD,cACLxP,cAAcliD,KAAKq5D,oBACnBr5D,KAAKonB,OAAOkyC,aAEZt5D,KAAK0xD,YAAY1xD,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAASssC,eAAgB,QAE1EhxD,KAAKqvB,SAAS,CAAC0mC,eAAgBxgC,IAGjCk8B,wBACEzxD,KAAKqvB,SAAS,CAACgwB,oBAAqB33B,SAASguC,SAG/ChE,YAAYz1C,EAAK4f,EAAOjF,EAAQqF,GAC9Bj8B,KAAKqvB,SAAS,CAACmhB,UAAWv0B,EAAKs0B,WAAY1U,EAAO6zB,YAAa94B,EAAQ+4B,gBAAiB1zB,IAI1F01B,mBAAmB5I,EAAOC,GACxBhpD,KAAKqvB,SAAS,CACZwgC,eAAe,EACf9G,MAAOA,EACPC,SAAUA,EACV4G,oBAAoB,EACpB0F,WAAW,IAEbt1D,KAAK0xD,YAAY,GAAI,MAEjB1xD,KAAKonB,OAAOmyC,cACdv5D,KAAK+xD,QAAQhJ,EAAOC,EAAU,CAAC3f,KAAMrpC,KAAKkvB,MAAM8/B,WAAY5c,KAAMpyC,KAAKkvB,MAAM6/B,WAE7E/uD,KAAKonB,OAAO2vB,UAAUhxC,OAAOkW,IAE3Bjc,KAAKqvB,SAAS,CAACwgC,eAAe,EAAOyF,WAAW,EAAO1F,oBAAoB,IAC3E5vD,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAMpCyrD,wBAAwB/F,GAClBA,EACF7rD,KAAKonB,OAAOoyC,cAAcr6D,MAAK,KAC7BmpD,GAAiBM,UAAU,kBAAkB,GAC7C5oD,KAAKqvB,SAAS,CAACw8B,SAAS,OAG1B7rD,KAAKonB,OAAOqyC,eAAet6D,MAAK,KAC9BmpD,GAAiBM,UAAU,kBAAkB,GAC7C5oD,KAAKqvB,SAAS,CAACw8B,SAAS,OAM9BgG,kBAEE3P,cAAcliD,KAAKq5D,oBACnBr5D,KAAK0xD,cAEL,MAAMjrC,EAASzmB,KAAKonB,OAAOsyC,gBAC3B15D,KAAKqvB,SAAS,CACZ+rB,cAAe30B,EAAOkzC,IAAM,KAAOlzC,EAAO+nB,MAAQ/nB,EAAO+nB,MAAQ,UAG/DxuC,KAAKkvB,MAAMomC,WACbt1D,KAAK+xD,QAAQ/xD,KAAKkvB,MAAM65B,MAAO/oD,KAAKkvB,MAAM85B,SAAU,CAAC3f,KAAMrpC,KAAKkvB,MAAM8/B,WAAY5c,KAAMpyC,KAAKkvB,MAAM6/B,WAKvG+C,6BAA6BlnC,EAAKgvC,GAGhC,GAFA1X,cAAcliD,KAAKq5D,oBAEfzuC,EAAM,EAGR,YADA5qB,KAAK0xD,cAIP,GAAIkI,EAQF,YANAA,EAAKz6D,MAAK,KAERa,KAAK0xD,iBACJ3rD,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKlC,MAAM,cAAC8f,GAAiBjmB,KAAK6I,MAAMqd,KACnC,IAAI7iB,EAAQunB,EAAM,IAClBvnB,GAAgBA,EAChBrD,KAAKq5D,mBAAqBpX,aAAY,KACpC,MAAM4X,EAAYx2D,EAAQ,GAAMmnB,GAAcnnB,GAASA,EACvDrD,KAAK0xD,YACHzrC,EAAcvB,GAASksC,oBAAqB,CAACnmC,QAASovC,IACtD,QACA,KACE3X,cAAcliD,KAAKq5D,oBACnBr5D,KAAKonB,OAAOkyC,cAEdrzC,EAAcvB,GAASmsC,gBAEzBxtD,GAAS,IACR,KAIL6uD,iBAAiBj2C,GACfjc,KAAKqvB,SAAS,CACZ4I,WAAW,EACXqnB,OAAO,EACP0W,qBAAqB,EACrBxlB,UAAWv0B,GAAOA,EAAI9V,QAAU8V,EAAI9V,QAAU,eAC9CoqC,WAAYt0B,GAAOA,EAAI9V,QAAU,MAAQ,OACzC0pD,eAAe,EACfwG,oBAAoB,EACpBK,sBAAsB,EACtBtb,cAAe,kBAInB2W,QAAQhJ,EAAOC,EAAU5f,GACvB,GAAIppC,KAAKonB,OAAO0yC,kBAGd,YADAte,GAAegG,WAAW,IAI5BpY,EAAOrmB,KAAAA,WAAkBqmB,GAEzB,IAAIvjC,EAAU,KACVwL,EAAQrR,KAAKonB,OAAO2yC,eACpBhR,GAASC,GACX33C,EAAQ,KACRrR,KAAKqvB,SAAS,CAAC25B,SAAU,OACzBnjD,EAAU7F,KAAKonB,OAAO4yC,WAAWjR,EAAOC,EAAU5f,IACzC/3B,IACTxL,EAAU7F,KAAKonB,OAAO6yC,WAAW5oD,EAAMA,MAAO+3B,IAG5CvjC,EACFA,EAAQ1G,MAAM6nB,IACRA,EAAK9gB,MAAQ,KAAqB,yBAAd8gB,EAAK+U,MAC3B/7B,KAAKqvB,SAAS,CAACugC,oBAAoB,IAC/BxmB,GACFppC,KAAK0xD,YAAY1xD,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAASusC,oBAAqB,QAE/EjxD,KAAKgyD,yBAAyBhrC,EAAKP,SAEnCzmB,KAAKiyD,2BAENlsD,OAAOkW,IAERjc,KAAKqvB,SAAS,CACZwgC,eAAe,EACfb,gBAAYtrD,EACZqrD,cAAUrrD,EACVksD,oBAAoB,EACpB0F,WAAW,IAEbt1D,KAAK0xD,YAAYz1C,EAAI9V,QAAS,OAC1BkL,GACFrR,KAAKo0D,eAEP5Y,GAAegG,WAAW,QAK5BhG,GAAegG,WAAW,IAC1BxhD,KAAKqvB,SAAS,CAACwgC,eAAe,KAIlCmC,yBAAyBvrC,GACvB,MAAMq1B,EAASN,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MAC3DiwB,EAAOJ,KAAK,GAAK,OACjBI,EAAOr1B,OAAP,OAA0BA,EAAO2iB,KAAK,GACtCoS,GAAegG,WAAWhG,GAAeQ,eAAeF,EAAOJ,KAAMI,EAAOr1B,SAG9EwrC,wBACEjyD,KAAK0xD,cAGDpJ,GAAiBK,UAAU,mBAC7BL,GAAiBM,UAAU,aAAc5oD,KAAKonB,OAAO2yC,gBAGvD,MAAMG,EAAYl6D,KAAKkvB,MAAM2nC,eAEvBnP,EAAK1nD,KAAKonB,OAAO2+B,aACvB2B,EAAGlb,WAAaxsC,KAAKmyD,aACrBzK,EAAGyS,gBAAkBn6D,KAAKoyD,kBAC1B1K,EAAGjb,cAAgBzsC,KAAKqyD,gBACxBryD,KAAKqvB,SAAS,CACZ4I,WAAW,EACX+2B,gBAAYtrD,EACZqrD,cAAUrrD,EACV+zB,SAAUz3B,KAAKonB,OAAOgzC,mBACtB9E,WAAW,EACXuB,oBAAgBnzD,IAGlBgkD,EAAG9qC,UACD8qC,EAAGpZ,iBACD0S,eACAqZ,WACA9rB,WACA+rB,WACA9rB,SACAzoC,OAAOkW,IACPjc,KAAKonB,OAAOmzC,aACZhS,aAAaM,WAAW,cACxB7oD,KAAK0xD,YAAYz1C,EAAI9V,QAAS,OAC9Bq1C,GAAegG,WAAW,OACzB9Z,SAAQ,KACT1nC,KAAKqvB,SAAS,CAACugC,oBAAoB,OAEvC,IAAI4K,EAAUhf,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KAAM,YAC/DquC,IACFM,EAAUhf,GAAeiG,YAAY+Y,EAASN,IAEhD1e,GAAegG,WAAWgZ,GAG5BrI,aAAa9qB,GACX,GAAIA,EAAM,CAOR,GANIA,EAAK7P,QACPx3B,KAAKqvB,SAAS,CACZumC,eAAgBvuB,EAAK7P,OAAO1kB,GAC5B+iD,gBAAiB7lC,GAAaqX,EAAK7P,OAAOrK,SAG1Cka,EAAKlP,QAAS,CAChB,MAAM9N,EAAS,GACf,IAAK,MAAO5lB,EAAKvE,KAAQL,OAAO4K,QAAQ48B,EAAKlP,SACvCj4B,GACFmqB,EAAO5mB,KAAKgB,GAGhBzE,KAAKqvB,SAAS,CACZymC,gBAAiBzrC,IAGjBgd,EAAKxR,KACP71B,KAAKqvB,SAAS,CACZq7B,eAAgBrjB,EAAKxR,IAAI6kC,iBAOjCtI,kBAAkB1P,EAAMoD,GACtB,GAAY,MAARpD,GAAwB,OAARA,EAClB1iD,KAAKsyD,mBACDtyD,KAAKkvB,MAAM8H,eAAiB8uB,EAAK3+B,OACnCnnB,KAAKqvB,SAAS,CAAC2mC,oBAA8B,MAARtT,SAElC,GAAY,QAARA,EACT1iD,KAAKsyD,wBACA,GAAY,OAAR5P,EAAe,CAExB,MAAMv7B,EAAQnnB,KAAKonB,OAAOC,SAASy+B,EAAK3+B,OAClC6mB,EAAW7mB,GAASA,EAAM8mB,aAG5B6X,EAAKtvB,OAAS,GAAKx2B,KAAKkvB,MAAMq7B,gBAAkBvc,IAE9CtmB,SAASguC,QAAU11D,KAAKkvB,MAAM8H,eAAiB8uB,EAAK3+B,QACtDupC,GAAUiK,OAId36D,KAAKsyD,uBACY,QAAR5P,IAEQ,QAARA,GAA0B,SAARA,GAGvB1iD,KAAKkvB,MAAM8H,eAAiB8uB,EAAK3+B,OACnCnnB,KAAK0yD,oBAAoB,MAG3B1yD,KAAKsyD,oBACY,OAAR5P,EAGL1iD,KAAKkvB,MAAM8H,eAAiB8uB,EAAK3+B,OACnCnnB,KAAKqvB,SAAS,CAAC4mC,iBAAkBnQ,EAAKjwB,MAEvB,OAAR6sB,GAEQ,OAARA,GAKTn1C,QAAQmB,KAAK,qCAAuCg0C,EAAO,QAAUoD,EAAK3+B,QAI9EkrC,gBAAgBuI,GACd56D,KAAKsyD,mBAKyB,iCAACtK,EAAU6S,GACzC,MAAMC,EAAS,GAIf,IAAK,MAAMr1D,KAAKuiD,EACVjlC,KAAAA,eAAsBtd,EAAE0hB,SACxB2zC,EAAOr1D,EAAE0hB,OAAS,CAChBI,KAAM9hB,EAAE0hB,MACR4zC,QAASt1D,EAAEs1D,QACXvjC,OAAQ/xB,EAAE+xB,OACVF,QAAS7xB,EAAE6xB,QACXzB,IAAKpwB,EAAEowB,MAMf,IAAK,MAAMpwB,KAAKo1D,EACTC,EAAOr1D,EAAE8hB,QACZuzC,EAAOr1D,EAAE8hB,MAAQ9hB,GAIrB,OAAO5F,OAAOkL,OAAO+vD,GAGvBxI,mBACE,MAAMllB,EAAW,CACf4a,SAAU,IAGPhoD,KAAKkvB,MAAMowB,QACdlS,EAASkS,OAAQ,GAGnBt/C,KAAKonB,OAAO2+B,aAAa5uB,UAAU1xB,IAC5BA,EAAE0hB,OAAU1hB,EAAE8hB,OAEjB9hB,EAAE0hB,MAAQ1hB,EAAE3D,MAGdsrC,EAAS4a,SAASvkD,KAAKgC,GACnBzF,KAAKkvB,MAAM8H,eAAiBvxB,EAAE0hB,QAChCimB,EAAS4oB,oBAAsBvwD,EAAE8vB,OACjC6X,EAAS6oB,iBAAmBxwD,EAAEowB,QAIlC,MAAMmlC,EAAO,IAAI3tD,KAAK,GACtB+/B,EAAS4a,SAASx6B,MAAK,CAACjpB,EAAG+C,KACjB/C,EAAE4jD,SAAW6S,GAAMjjB,WAAazwC,EAAE6gD,SAAW6S,GAAMjjB,YAI7D3K,EAASqD,mBAAqB0gB,GAAU8J,0BAA0B7tB,EAAS4a,SAAUhoD,KAAKkvB,MAAM6K,eAChG/5B,KAAKqvB,SAAS+d,GAIhBmlB,OAAO1rD,GACL,MAAMsgB,EAAQnnB,KAAKonB,OAAOC,SAASxgB,EAAKsgB,OACpCA,EAAM4Q,UAAUlxB,GAAM,IAASkc,KAAAA,qBAA8Blc,EAAKiE,MAAQ9K,KAAKkvB,MAAMuI,WACvF8qB,aAAaviD,KAAKk7D,eAClBl7D,KAAKk7D,cAAgBjoD,YAAW,KAC9BjT,KAAKk7D,mBAAgBx3D,EACrByjB,EAAMg0C,SAASt0D,EAAK6hB,OtE/0BE,MsEq1B5B8pC,aACE,MAAM4I,EAAMp7D,KAAKonB,OAAOi0C,cACxBD,EAAI3uB,cAAgBzsC,KAAKyyD,iBACrB2I,EAAI1a,eACN1gD,KAAKyyD,mBAEL2I,EAAIx+C,UAAUw+C,EAAI9sB,iBAAiBgtB,UAAU9sB,SAASzoC,OAAOkW,IAC3Djc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKpCssD,mBACE,MAAMoI,EAAgB,GAEtB76D,KAAKonB,OAAOi0C,cAAclkC,UAAUokC,IAClCV,EAAcp3D,KAAK83D,MAErBv7D,KAAKqvB,SAAS,CACZ0K,cAAe8gC,EACfpqB,mBAAoB0gB,GAAU8J,0BAA0Bj7D,KAAKkvB,MAAM84B,SAAU6S,KAOjFlhC,qBAAqBv2B,GACnB,MAAMg4D,EAAMp7D,KAAKonB,OAAOi0C,cACxBD,EAAII,QAAQ,CAACn0B,KAAM,CAAC7P,OAAQp0B,KAASjE,MAAM6nB,GAClCo0C,EAAI/sB,QAAQ+sB,EAAI9sB,iBAAiBgtB,UAAU9sB,WACjDzoC,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKlCusD,oBAAoBxrC,GAEdlnB,KAAKkvB,MAAM0xB,gBAAkB5gD,KAAKkvB,MAAM0xB,eAAeC,YAAc35B,GACvElnB,KAAKqvB,SAAS,CACZuxB,eAAgB,OAIhB15B,GACFlnB,KAAKqvB,SAAS,CACZmhB,UAAW,GACXD,WAAY,KACZ6lB,YAAa,aACbD,eAAWzyD,IAGT1D,KAAKkvB,MAAM8H,eAAiB9P,IAC9BlnB,KAAKqvB,SAAS,CACZ2mC,oBAAqBh2D,KAAKonB,OAAOq0C,cAAcv0C,GAC/C+uC,iBAAkBj2D,KAAKonB,OAAO8xC,mBAAmBhyC,GACjDu4B,eAAgB,OAElBjE,GAAegG,WAAWhG,GAAeiG,YAAY,GAAIv6B,OAI3DlnB,KAAKqvB,SAAS,CACZmhB,UAAW,GACXD,WAAY,KACZ6lB,YAAa,YACbJ,qBAAqB,EACrBC,iBAAkB,KAClBE,eAAWzyD,EACX+7C,eAAgB,OAGlBjE,GAAegG,WAAWhG,GAAeiG,YAAY,GAAI,QAK7DkR,yBACE3yD,KAAKqvB,SAAS,CACZ+mC,YAAa,cAEf5a,GAAegG,WAAWhG,GAAeiG,YAAYz8C,OAAO62C,SAAShwB,KAAM,OAS7E+mC,kBAAkB9pC,EAAKjjB,EAAS0hC,EAAUrb,GACxC,MAAM/E,EAAQnnB,KAAKonB,OAAOC,SAASrnB,KAAKkvB,MAAM8H,eAC9Ch3B,KAAKq1D,mBAAmBluC,EAAO2B,EAAKjjB,EAAS0hC,EAAUrb,GAGzDmpC,mBAAmBluC,EAAO2B,EAAKjjB,EAAS0hC,EAAUrb,IAChDpD,EAAM3B,EAAM4B,cAAcD,GAAK,IAE3B88B,UAAYre,EAEZrb,IACFpD,EAAIoD,KAAOrsB,OAAO2P,OAAOsZ,EAAIoD,MAAQ,GAAIA,IAGtC/E,EAAMu5B,iBAEJ76C,IACHA,EAAUvH,QAAQC,WAEpBsH,EAAUA,EACP1G,MAAK,IAAMgoB,EAAMvK,cACjBzd,MAAK,KAEJgoB,EAAMk6B,gBAAgBC,KACfA,EAAIC,UAAYp6B,EAAMu5B,gBACzB1gD,KAAK+8C,YAAYuE,UAMvBz7C,IACFA,EAAUA,EAAQE,OAAOkW,IACvBjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,WAIlCghB,EAAM6B,aAAaF,EAAKjjB,GACrB1G,MAAM6nB,IACL,GAAIG,EAAM8mB,aACR,OAAO9mB,EAAMxB,SAAQ,MAGxB5f,OAAOkW,IACNjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIpC0sD,wBAAwB3rC,EAAW0P,GACjC,MAAMzP,EAAQnnB,KAAKonB,OAAOC,SAASH,GACnC,IAAI9V,EAAW,KACf,OAAQwlB,GACN,IAAK,SAEH,MAAMluB,EAAOye,EAAMgf,gBAAgBoH,WACnCn8B,EAAW+V,EAAMq0C,QAAQ,CAAC9tB,IAAK,CAAChlC,KAAMA,KAClCye,EAAMu0C,cAGRtqD,EAAWA,EAASjS,MAAM6nB,IACxBG,EAAMq0C,QAAQ,CAAC9tB,IAAK,CAACnmB,KAAML,EAAWxe,KAAMA,SAGhD,MACF,IAAK,SAEH0I,EAAW+V,EAAMG,UAAS,GAC1B,MACF,IAAK,QAGH,MAAMq0C,EAAKx0C,EAAMgf,gBAAgBy1B,WAAW,OAAOpuB,UACnDp8B,EAAW+V,EAAMq0C,QAAQ,CAAC9tB,IAAK,CAAChlC,KAAMizD,KAAMx8D,MAAM6nB,GACzChnB,KAAK0yD,oBAAoB,QAElC,MACF,QACEnlD,QAAQoB,KAAK,4BAA6B,IAAMioB,EAAS,MAG7C,MAAZxlB,GACFA,EAASrL,OAAOkW,IACdjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAMpC2sD,mBACE9yD,KAAK0xD,cAELlW,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KAAM,aAIjFknC,wBAAwB8I,EAAQC,EAAWC,EAASC,EAAOC,GAEzDj8D,KAAK0xD,cAEL1xD,KAAKonB,OAAO2vB,QAAQ/2C,KAAKkvB,MAAMmsB,eAC5Bl8C,MAAK,IACGa,KAAKonB,OAAO80C,mBAAmBL,EAAQC,EAC5C,CAACtkC,OAAQukC,EAAS52B,KAAM82B,EAAO7yB,KAAMrmB,KAAAA,WAAkBi5C,OACxD78D,MAAM6nB,IACHA,EAAK9gB,MAAQ,KAAoB,wBAAb8gB,EAAK+U,KAC3B/7B,KAAKgyD,yBAAyBhrC,EAAKP,QAEnCzmB,KAAKiyD,sBAAsBjyD,SAE5B+F,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIpC+sD,0BAA0BiJ,GAExBn8D,KAAKqvB,SAAS,CAACq7B,cAAe,OAE9B,MAAMhD,EAAK1nD,KAAKonB,OAAO2+B,aACjB4V,EAAKjU,EAAGvhB,gBAAgBy1B,WAAWO,EAAK,KAAO,MAAM3uB,UAC3Dka,EAAG8T,QAAQ,CAAC9tB,IAAK,CAAChlC,KAAMizD,KAAM51D,OAAOkW,IAEnCjc,KAAKqvB,SAAS,CAACq7B,eAAgByR,IAC/Bn8D,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlC8sD,+BAA+B9tB,GAC7BnlC,KAAKonB,OAAO2+B,aAAayV,QAAQ,CAACr2B,KAAMA,IACrCp/B,OAAOkW,IACNjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKpCgtD,iBACEnzD,KAAK0xD,cAELlW,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KACvE7rB,KAAKkvB,MAAMuI,SAAW,OAAS,aAInC27B,qBAAqBlE,GACnB,MAAM7T,EAAgB6T,EAAS7T,eAAiBr7C,KAAKkvB,MAAMmsB,cACrDgT,EAAYa,EAASb,WAAaruD,KAAKkvB,MAAMm/B,UAC/CruD,KAAKonB,SACPpnB,KAAKonB,OAAOqyC,eACZz5D,KAAKonB,OAAOgwC,kBAAe1zD,EAC3B1D,KAAKonB,OAAOmzC,cAEdv6D,KAAKonB,OAAS+pC,GAAU+F,QAAQ7b,EAAegT,EAAWruD,KAAK6I,MAAMqd,KAAKwrB,OACxE4W,GAAiBK,UAAU,mBAC7B3oD,KAAKonB,OAAO+vC,UAAYn3D,KAAK6xD,gBAC7B7xD,KAAKonB,OAAOgwC,aAAep3D,KAAKkyD,iBAChClyD,KAAKonB,OAAOiwC,yBAA2Br3D,KAAK8xD,6BAE5C9xD,KAAKqvB,SAAS,CACZgsB,cAAeA,EACfgT,UAAWA,IAEb/F,GAAiBM,UAAU,WAAY,CACrCvN,cAAeA,EACfgT,UAAWA,IAGb7S,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KAAM,KAIjFwnC,oBACE7X,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KACvE7rB,KAAKkvB,MAAMuI,SAAW,UAAY,KAItC67B,oBACE9X,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KACvE7rB,KAAKkvB,MAAMuI,SAAW,UAAY,KAGtCm8B,eAAergB,GACTA,GACFvzC,KAAKqvB,SAAS,CAACo7B,cAAe,OACzBzqD,KAAKkvB,MAAMumC,eAGdz1D,KAAKqvB,SAAS,CAACo7B,eAAe,IAC1BnC,GAAiBK,UAAU,mBAC7BL,GAAiB4P,aAAa,WAAY,CAACzN,eAAe,KAJ5DzqD,KAAK2zD,mBAOE3zD,KAAKkvB,MAAMumC,eAAiBz1D,KAAKm4D,IAC1CiE,GAAiBp8D,KAAKm4D,KAAKpyD,OAAOkW,IAChC1O,QAAQ3O,MAAM,0BAA2Bqd,MACxCyrB,SAAQ,KACT4gB,GAAiB4P,aAAa,WAAY,CAACzN,eAAe,IAC1DlC,aAAaM,WAAW,kBACxB7oD,KAAKqvB,SAAS,CAACo7B,eAAe,EAAOgL,cAAe,OAEpDz1D,KAAKonB,OAAOkwC,eAAe,UAG7Bt3D,KAAKqvB,SAAS,CAACo7B,eAAe,EAAOgL,cAAe,OACpDnN,GAAiB4P,aAAa,WAAY,CAACzN,eAAe,KAI9D8I,0BAA0BhgB,GACxBvzC,KAAKqvB,SAAS,CAACk7B,cAAehX,IAC9B+U,GAAiB4P,aAAa,WAAY,CACxC3C,kBAAmBhiB,IAIvBigB,cAAcz0D,EAAQM,GACTW,KAAKonB,OAAO2+B,aACpByV,QAAQ,CAACpyB,KAAM,CAACC,KAAMtqC,EAAQmB,IAAKb,KAAS0G,OAAOkW,IACpDjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCstD,iBAAiB10D,EAAQM,GACZW,KAAKonB,OAAO2+B,aACpBsW,cAAct9D,EAAQM,GAAO0G,OAAOkW,IACrCjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCutD,kBAAkB30D,EAAQqS,GACxBpR,KAAKgyD,yBAAyB,CAAC5oB,KAAM,CAACrqC,KAIxC+0D,wBACE,MAAMhY,EAASN,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MAC3D,IAAI6vB,EAAO,GACP,CAAC,WAAW,UAAU,UAAU,SAAS/zC,SAASm0C,EAAOJ,KAAK,IAChEA,EAAO,OACE,QAAUI,EAAOJ,KAAK,GAC/BA,EAAO,UACE,WAAaI,EAAOJ,KAAK,GAClCA,EAAO,WACE17C,KAAKkvB,MAAMuI,WACpBikB,EAAO,YAETI,EAAOJ,KAAK,GAAKA,EACbI,EAAOr1B,gBACFq1B,EAAOr1B,OAAOvgB,YACd41C,EAAOr1B,OAAO1nB,cACd+8C,EAAOr1B,OAAO2yC,WACdtd,EAAOr1B,OAAOknC,cACd7R,EAAOr1B,OAAOpV,OAEvBmqC,GAAegG,WAAWhG,GAAeQ,eAAeF,EAAOJ,KAAMI,EAAOr1B,SAC5EzmB,KAAKqvB,SAAS,CAACmhB,UAAW,GAAID,WAAY,OAI5C+rB,eAAezwC,GACb2vB,GAAegG,WAAWhG,GAAeif,gBAAgBz1D,OAAO62C,SAAShwB,KAAMA,IAIjF0wC,cAAc1wC,GACZ2vB,GAAegG,WAAWhG,GAAeghB,gBAAgBx3D,OAAO62C,SAAShwB,KAAMA,IAIjFkoC,wBAAwB7sC,EAAW05B,EAAgBrqB,GAEjD,GAAIrP,GAAalnB,KAAKonB,OAAOw6B,cAAc16B,GAEzC,YADAlnB,KAAK0yD,oBAAoBxrC,GAI3B,MAAMT,EAAS,GACX1D,KAAAA,eAAsBmE,IAExBT,EAAOinB,IAAM,CAAChlC,KAAMwb,IAEpBuC,EAAO4gB,KAAO,CAACsG,OAAQ,CAACzyB,KAAMgJ,OAE9BgD,EAAYA,GAAalnB,KAAKonB,OAAOq1C,kBAAkBlmC,GACnDqqB,IACFn6B,EAAO4gB,KAAO,CAAC7P,OAAQopB,EAAeppB,OAAQF,QAAS,CAACZ,QAASkqB,EAAetpB,UAChF7Q,EAAO0e,KAAOyb,EAAezb,OAGjC1e,EAAOo6B,WAAa35B,EACpBlnB,KAAKqvB,SAAS,CAACuxB,eAAgBn6B,IAAS,KAAOzmB,KAAK0yD,oBAAoBxrC,MAI1E8sC,sBAAsB0I,EAASC,GACzB38D,KAAKkvB,MAAM8H,eAAiB0lC,GAAWA,GAAWC,GAIpD38D,KAAKqvB,SAAS,CAAC2H,cAAe2lC,IAAU,KACtCnhB,GAAegG,WAAWhG,GAAeiG,YAAY,GAAIkb,OAK/D1I,yBAAyB/sC,EAAWo6B,EAAKiM,EAAM5f,GAC7C3tC,KAAK0xD,cAEL,MAAMvqC,EAAQnnB,KAAKonB,OAAOC,SAASH,GACnC,GAAIC,EAAO,CACT,MAAMV,EAAS,GACf,IAAIusB,EACAsO,IACEA,EAAIn0B,QACFm0B,EAAIn0B,MAAMlD,KAAOq3B,EAAIn0B,MAAMlD,KAAOlH,KAAAA,SACpCiwB,EAAc,CAACsO,EAAIn0B,MAAMlD,KACfq3B,EAAIn0B,MAAMtmB,MAAQy6C,EAAIn0B,MAAMtmB,MAAQkc,KAAAA,WAC9Cu+B,EAAIn0B,MAAQpK,KAAAA,WAGhB0D,EAAO+Q,OAAS8pB,GAGC,iBAARiM,IACT9mC,EAAO6Q,QAAWi2B,IAASxqC,KAAAA,SACzBA,KAAAA,SAAkB,CAAC2T,QAAS62B,IAE5B5f,IACFlnB,EAAOknB,OAASA,GAElBxmB,EAAMq0C,QAAQ,CAACn0B,KAAM5gB,EAAQusB,YAAaA,IAAcjtC,OAAOkW,IAC7Djc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,WAKpCymC,gBAAgB1lB,GACd,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC/BC,GACFA,EAAMxB,SAAQ,GAIlBqtC,4BAA4BhK,GAC1BhpD,KAAK0xD,cAED1I,GACFhpD,KAAKonB,OAAOw1C,mBAAmB,KAAM58D,KAAKonB,OAAOy1C,kBAAmB7T,GAAUjjD,OAAOkW,IACnFjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKpC+tD,wBAAwBhtC,EAAWxe,EAAMoxB,GACvC,MAAM3S,EAAQnnB,KAAKonB,OAAOC,SAASH,GACnC,GAAIC,EAAO,CACT,MAAMw0C,EAAKx0C,EAAMgf,gBACbrM,GACF6hC,EAAGmB,YAAYp0D,GACfA,EAAOizD,EAAGpuB,aAEVouB,EAAGC,WAAWlzD,GACdA,EAAOizD,EAAGnuB,WAEZrmB,EAAMq0C,QAAQ,CAAC9tB,IAAK,CAACnmB,KAAMuS,EAAKpxB,KAAMA,KAAQ3C,OAAOkW,IACnDjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,WAKpCguD,wBAAwBjtC,EAAWie,GACjC,MAAMhe,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC/BC,GACFA,EAAMq0C,QAAQ,CAACr2B,KAAMA,IAAOp/B,OAAOkW,IACjCjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAKpCiuD,eAaE,IAAI2I,EAZJhxC,GAAc,GAGdw8B,aAAaM,WAAW,cACxBN,aAAaM,WAAW,kBACxBN,aAAaM,WAAW,YACpB7oD,KAAKkvB,MAAMumC,eACb2G,GAAiBp8D,KAAKm4D,KAGxBjW,cAAcliD,KAAKq5D,oBAGfr5D,KAAKonB,QACP21C,EAAU/8D,KAAKonB,OAAOqyC,eACtBz5D,KAAKonB,OAAOgwC,kBAAe1zD,EAC3B1D,KAAKonB,OAAOmzC,cAEZwC,EAAUC,QAAQz+D,UAEpByB,KAAKqvB,SAASrvB,KAAKoxD,iBAEnB2L,EAAQ59D,MAAK,KACXa,KAAKonB,OAAS+pC,GAAU+F,QAAQl3D,KAAKkvB,MAAMmsB,cACzCr7C,KAAKkvB,MAAMm/B,UAAWruD,KAAK6I,MAAMqd,KAAKwrB,OAAQ4W,GAAiBK,UAAU,mBAAmB,KAC1F3oD,KAAKonB,OAAO+vC,UAAYn3D,KAAK6xD,gBAC7B7xD,KAAKonB,OAAOgwC,aAAep3D,KAAKkyD,iBAChClyD,KAAKonB,OAAOiwC,yBAA2Br3D,KAAK8xD,6BAC5CtW,GAAegG,WAAW,UAKlC0J,sBACElrD,KAAKonB,OAAO61C,gBAAe,GAAM99D,MAAM6nB,IACrChnB,KAAKo0D,kBAITC,yBAAyBntC,GACvB,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,GAKLA,EAAMG,UAAS,GAAMnoB,MAAM6nB,IAEzBw0B,GAAegG,WAAWhG,GAAeiG,YAAYz8C,OAAO62C,SAAShwB,KAAM,QAC1E9lB,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCmuD,4BAA4BptC,GAC1B,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,GAKLA,EAAMwB,gBAAe,GAAM5iB,OAAOkW,IAChCjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCouD,wBAAwBrtC,GACtB,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,GAILA,EAAM21B,OAAM,GAAM39C,MAAM6nB,IAEtBw0B,GAAegG,WAAWhG,GAAeiG,YAAYz8C,OAAO62C,SAAShwB,KAAM,QAC1E9lB,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCquD,wBAAwBttC,GACtB,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,GAILA,EAAM8B,WAAW,KAAM,OAAO9pB,MAAM6nB,IAElCw0B,GAAegG,WAAWhG,GAAeiG,YAAYz8C,OAAO62C,SAAShwB,KAAM,QAC1E9lB,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIlCsuD,kBAAkBvtC,GAChB,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,IAKLnnB,KAAKonB,OAAO81C,QAAQn6C,KAAAA,UAAkBA,KAAAA,OAAAA,WAAyB,KAAM,CACnE,OAAU,SACV,OAAUmE,KAIZC,EAAM8B,WAAW,KAAM,OAAO9pB,MAAM6nB,IAElCw0B,GAAegG,WAAWhG,GAAeiG,YAAYz8C,OAAO62C,SAAShwB,KAAM,QAC1E9lB,OAAOkW,IACRjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,WAIlCuuD,sBAAsBjuC,EAAQypB,GAC5BlwC,KAAKqvB,SAAS,CACZgnC,oBAAoB,EACpBE,mBAAoB,CAAC3sC,EAAGnD,EAAOmD,EAAGG,EAAGtD,EAAOsD,GAC5CysC,kBAAmB/vC,EACnBgwC,iBAAkBvmB,GAAalwC,KAAK20D,wBAAwBluC,EAAOS,WACnEovC,kBAAmBt2D,KAAKgmB,QAAQ6B,QAAQsa,0BAK5CgzB,wBAAwB1uC,GACc,UAAhCzmB,KAAKkvB,MAAMymC,mBAEb31D,KAAK8zD,wBAEP,MAAMqJ,EAAS,KAAO12C,EAAO2C,SACvBhG,EAAmC,iBAAlBqD,EAAOrD,QACtBL,KAAAA,OAAAA,KAAmB0D,EAAOrD,SAAWL,KAAAA,OAAAA,iBAA+B0D,EAAOrD,SAC7EqS,EAAU1S,KAAAA,OAAAA,QAAsBK,EtEp2CF,QsEq2CE1f,EAA+B,MAApB+iB,EAAOkR,WAClD7O,EAAM/F,KAAAA,OAAAA,OACRA,KAAAA,OAAAA,gBACIA,KAAAA,OAAAA,QAAsBo6C,EAAQ12C,EAAO0C,WACzC/F,GAEEg6C,EAAar6C,KAAAA,OAAAA,MAAoBo6C,EAAQ12C,EAAO0C,SAAUsM,GAC1DvJ,EAAO,CACXyL,UAAWlR,EAAOS,UAAY,IAAMT,EAAOiC,KAE7C1oB,KAAKqvB,SAAS,CACZqnC,sBAAsB,EACtBjX,eAAgB,CAAEvzB,KAAMA,EAAMpD,IAAKA,EAAK2M,QAAS2nC,KAIrDzI,wBAAwBztC,GACtB,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAEnC,IAAI0kB,GAAQ,EAAOqc,GAAU,EAAOoV,GAAe,EAAOC,GAAa,EAAOz9B,GAAU,EAAOmO,GAAW,EAC1G,GAAI7mB,EAAO,CACTm2C,EAAan2C,EAAMu5B,eACnB1S,EAAW7mB,EAAM8mB,aAEjB,MAAMpY,EAAM1O,EAAMgf,gBACdtQ,IACF+V,EAAQ/V,EAAIG,UACZiyB,GAAWpyB,EAAII,WACfonC,GAAgBxnC,EAAII,SAAS,QAC7B4J,EAAUhK,EAAIkY,aAIlB,MAAO,CACLuvB,EAAa,CACX1+C,MAAO5e,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAASwsC,gBAC9C1qC,QAASxmB,KAAK80D,oBACZ,KACJwI,EAAa,iBAAmB,KAChCA,GAAcz9B,EAAU,sBAAwB,KAChD+L,EAASqc,EAAU,KAAO,eAAkB,aAC5CoV,EAAe,gBAAkB,cACjCrvB,EAAW,gBAAkB,gBAC7B,gBAIJ4mB,wBACE50D,KAAKqvB,SAAS,CACZgnC,oBAAoB,EACpBE,mBAAoB,KACpBC,kBAAmB,KACnBF,kBAAmB,OAIvBlB,wBAAwBmI,GACtBv9D,KAAKqvB,SAAS,CACZqnC,sBAAsB,EACtBjX,eAAgB8d,EAAuBv9D,KAAKkvB,MAAMuwB,eAAiB,OAIvEyV,wBAAwBt+B,EAAQ/wB,EAAS4gB,GACzB,iBAAVmQ,EACE/wB,GAAW4gB,EAAOS,WAAaT,EAAOS,WAAalnB,KAAKkvB,MAAM8H,eAChEnxB,EAAQ1G,MAAK,KACXa,KAAK0yD,oBAAoB,SAGV,qBAAV97B,GACT52B,KAAKm1D,wBAAwB1uC,GAIjCouC,gBAAgBj2C,EAAOwE,EAASM,EAAW85C,EAAan6C,EAAUo6C,GAChEz9D,KAAKqvB,SAAS,CACZsnC,cAAc,EACdC,YAAa,CACXh4C,MAAOA,EACPwE,QAASA,EACTM,UAAWA,EACXC,QAAS65C,EACTn6C,SAAUA,EACV7kB,OAAQi/D,KAKd3I,qBACEtZ,GAAegG,WAAWhG,GAAe2R,YAAYnoD,OAAO62C,SAAShwB,KAAM,OAAQ,SACnF7rB,KAAKqvB,SAAS,CAAC8mC,UAAW,SAG5BnpB,0BAA0B9lB,EAAWmY,EAAO5B,GAC1C,IAAKvW,EACH,OAGF,MAAMC,EAAQnnB,KAAKonB,OAAOC,SAASH,GAC9BC,IAIDkY,GAASA,EAAMp8B,OAAS,GAC1Bo8B,EAAMn0B,KAAK4uB,IACT3S,EAAMu2C,OAAO5jC,EAAK,MAAM/zB,OAAOkW,IAC7Bjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,aAKhCs3B,GAAWA,EAAQx6B,OAAS,GAC9Bw6B,EAAQvyB,KAAK4uB,IACX3S,EAAMK,gBAAgBsS,GAAK/zB,OAAOkW,IAChCjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,cAMtC4uD,iCAAiC3rB,EAAMljC,GACrC,GAAIlG,KAAKonB,OAAO0yC,kBAAmB,CACtB95D,KAAKonB,OAAO2+B,aACpByV,QAAQ,CAACpyB,KAAM,CAACC,KAAMD,EAAMgJ,KAAMlsC,KAClC/G,MAAK,KACJq8C,GAAegG,WAAW,OAE3Bz7C,OAAOkW,IACNjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,eAGlCnG,KAAKqvB,SAAS,CAAC2/B,WAAY5lB,EAAM2lB,SAAU7oD,IAC3ClG,KAAK+xD,QAAQ,KAAM,KAAM,CAAC1oB,KAAMD,EAAMgJ,KAAMlsC,IAIhD8uD,2BAA2Bj2D,EAAQM,GAEjC,OAAOW,KAAKonB,OAAO2vB,UAChB53C,MAAK,IACGa,KAAKonB,OAAOu2C,uBAAuB,QAAS5+D,EAAQM,KAE5D0G,OAAOkW,IAENjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAIpC8uD,oBAAoBtH,EAAQiQ,EAAavsD,IACvCA,E5Dt2CG,SAAwB/L,GAC7B,GAAIA,EAAK,CACPA,EAAMA,EAAIvC,QAAQ,KAAM,KAAKA,QAAQ,KAAM,KAC3C,IACEuC,EAAMsO,KAAKsG,KAAK5U,IAChB,MAAM2W,GACN1O,QAAQ3O,MAAM,qCAAsCqd,GACpD3W,EAAM,MAGV,OAAOA,E4D41CGu4D,CAAexsD,IAIrBrR,KAAKonB,OAAO2vB,UACT53C,MAAK,IACGa,KAAKonB,OAAOw1C,mBAAmB,KAAM,KAAMgB,EAAa,CAACvsD,MAAOA,MAExElS,MAAK,KACJq8C,GAAegG,WAAW,OAE3Bz7C,OAAOkW,IAENjc,KAAK0xD,YAAYz1C,EAAI9V,QAAS,UAXlCnG,KAAK0xD,YAAY1xD,KAAK6I,MAAMqd,KAAKD,cAAcvB,GAASqsC,wBAAyB,OAgBrF9tC,SACE,OACE,yBAAKO,GAAG,gBAAgByG,IAAKjqB,KAAKgmB,SAC/BhmB,KAAKkvB,MAAMmnC,mBACV,kBAAC,GAAD,CACEjvC,OAAQpnB,KAAKonB,OACbqC,OAAQzpB,KAAKkvB,MAAMonC,kBACnB3sC,QAAS3pB,KAAKkvB,MAAMqnC,mBACpB9vC,OAAQzmB,KAAKkvB,MAAMsnC,kBACnBjzD,MAAOvD,KAAKkvB,MAAMunC,iBAClBxuC,KAAMjoB,KAAK40D,sBACXjuC,YAAa3mB,KAAK60D,gBAClBvsC,SAAUtoB,KAAKk1D,wBACfjuC,eAAiBC,IACXA,GAAalnB,KAAKkvB,MAAM8H,eAC1Bh3B,KAAK0yD,oBAAoB,OAG7BnqC,QAASvoB,KAAK0xD,cAEhB,KAED1xD,KAAKkvB,MAAMwnC,qBACV,kBAACj9B,GAAD,CACErS,OAAQpnB,KAAKonB,OACb+P,SAAUn3B,KAAKkvB,MAAM84B,SACrBhxB,cAAeh3B,KAAKkvB,MAAM8H,cAC1BS,SAAUz3B,KAAKkvB,MAAMuI,SAErBxP,KAAMjoB,KAAKo1D,wBACXv7B,WAAY75B,KAAKwyD,WACjBz4B,cAAe/5B,KAAKkvB,MAAM6K,cAC1BZ,iBAAkBn5B,KAAK25B,qBACvBzB,gBAAiBl4B,KAAK+zD,0BAGxB,KAEF,kBAAC/wC,GAAD,CACEE,QAASljB,KAAKkvB,MAAMynC,aACpB/3C,MAAO5e,KAAKkvB,MAAM0nC,YAAYh4C,MAC9BwE,QAASpjB,KAAKkvB,MAAM0nC,YAAYxzC,QAChCC,SAAUrjB,KAAKkvB,MAAM0nC,YAAYvzC,SAAY,KAAQrjB,KAAKqvB,SAAS,CAACsnC,cAAc,KAAc,KAChGn4D,OAAQwB,KAAKkvB,MAAM0nC,YAAYp4D,OAC/BklB,UAAW,KAAQ1jB,KAAKqvB,SAAS,CAACsnC,cAAc,IAAS32D,KAAKkvB,MAAM0nC,YAAYlzC,aAChFC,QAAS3jB,KAAKkvB,MAAM0nC,YAAYjzC,UAElC,kBAAC,GAAD,CACEyD,OAAQpnB,KAAKonB,OACb6Q,UAAWj4B,KAAKkvB,MAAM+I,UACtBqY,cAAetwC,KAAKkvB,MAAMohB,cAC1BkU,SAAUxkD,KAAKkvB,MAAMohB,eAA4C,cAA3BtwC,KAAKkvB,MAAMknC,YACjDlnC,MAAOlvB,KAAKkvB,MAAMymC,kBAClB/2C,MAAO5e,KAAKkvB,MAAM0mC,eAClBpnC,OAAQxuB,KAAKkvB,MAAM2mC,gBACnBt6B,cAAev7B,KAAKkvB,MAAM4mC,gBAC1B/M,MAAO/oD,KAAKkvB,MAAM65B,MAClB8C,QAAS7rD,KAAKkvB,MAAM28B,QACpBp0B,SAAUz3B,KAAKkvB,MAAMuI,SACrBo4B,cAAe7vD,KAAKkvB,MAAM2gC,cAC1BD,mBAAoB5vD,KAAKkvB,MAAM0gC,mBAE/Bpf,UAAWxwC,KAAKkvB,MAAMshB,UACtBD,WAAYvwC,KAAKkvB,MAAMqhB,WACvBmf,YAAa1vD,KAAKkvB,MAAMwgC,YACxBC,gBAAiB3vD,KAAKkvB,MAAMygC,gBAE5B34B,cAAeh3B,KAAKkvB,MAAM8H,cAC1BgxB,SAAUhoD,KAAKkvB,MAAM84B,SACrBgH,WAAYhvD,KAAKkvB,MAAM8/B,WACvBD,SAAU/uD,KAAKkvB,MAAM6/B,SAErBV,UAAWruD,KAAKkvB,MAAMm/B,UACtB9D,cAAevqD,KAAKkvB,MAAMq7B,cAC1BE,cAAezqD,KAAKkvB,MAAMu7B,cAC1BD,qBAAsBxqD,KAAKkvB,MAAMs7B,qBACjCE,cAAe1qD,KAAKkvB,MAAMw7B,cAC1BrP,cAAer7C,KAAKkvB,MAAMmsB,cAC1BD,cAAep7C,KAAKkvB,MAAMksB,cAE1B2U,iBAAkB/vD,KAAKozD,qBACvB7L,SAAUvnD,KAAK8yD,iBACfzL,WAAYrnD,KAAKmzD,eACjBpjB,WAAY/vC,KAAKs8D,eACjBxM,eAAgB9vD,KAAK2xD,mBACrB7F,oBAAqB9rD,KAAK4xD,wBAC1BlI,gBAAiB1pD,KAAK+yD,wBACtB1H,oBAAqBrrD,KAAKi0D,yBAC1B7I,iBAAkBprD,KAAKgzD,4BACvBhD,oBAAqBhwD,KAAKizD,+BAC1B5I,0BAA2BrqD,KAAK4zD,eAChCxJ,sBAAuBpqD,KAAKuzD,0BAC5BjJ,sBAAuBtqD,KAAKkzD,0BAC5BhqB,UAAWlpC,KAAKwzD,cAChBhqB,aAAcxpC,KAAKyzD,iBACnBlqB,cAAevpC,KAAK0zD,kBACpBx7B,gBAAiBl4B,KAAK0yD,oBACtBrF,cAAertD,KAAK+zD,wBACpBvI,SAAUxrD,KAAKo0D,aACf9I,gBAAiBtrD,KAAKkrD,oBACtBvkC,YAAa3mB,KAAK60D,gBAClBllC,SAAU3vB,KAAK8zD,sBACfvrC,QAASvoB,KAAK0xD,YACdzB,sBAAuBjwD,KAAK+0D,iCAC5B7E,uBAAwBlwD,KAAKg1D,2BAC7B7E,gBAAiBnwD,KAAKi1D,oBACtB5M,cAAeroD,KAAKqzD,kBACpB5H,cAAezrD,KAAKszD,kBAEpBz5B,WAAY75B,KAAKwyD,WACjBz4B,cAAe/5B,KAAKkvB,MAAM6K,cAC1BZ,iBAAkBn5B,KAAK25B,qBAEvBvE,gBAAiBp1B,KAAK00D,wBAExB,kBAAC,GAAD,CACEttC,OAAQpnB,KAAKonB,OACb6Q,UAAWj4B,KAAKkvB,MAAM+I,UACtBqnB,MAAOt/C,KAAKkvB,MAAMowB,MAClB/pB,OAAQv1B,KAAKkvB,MAAM8mC,oBACnBngC,IAAK71B,KAAKkvB,MAAM+mC,iBAChB3lB,cAAetwC,KAAKkvB,MAAMohB,cAC1Bjd,cAAerzB,KAAKkvB,MAAMmE,cAC1B2jC,eAAgBh3D,KAAKkvB,MAAM8nC,eAC3BxS,SAAUxkD,KAAKkvB,MAAMohB,gBACS,eAA3BtwC,KAAKkvB,MAAMknC,aAAgCp2D,KAAKkvB,MAAMinC,WACzDhvC,MAAOnnB,KAAKkvB,MAAM8H,cAClBS,SAAUz3B,KAAKkvB,MAAMuI,SAErBqmC,WAAY99D,KAAKkvB,MAAM0mC,eACvBxa,cAAep7C,KAAKkvB,MAAMksB,cAC1BC,cAAer7C,KAAKkvB,MAAMmsB,cAC1BgE,mBAAoBr/C,KAAKkvB,MAAMmwB,mBAE/BI,eAAgBz/C,KAAKkvB,MAAMuwB,eAC3B2E,uBAAwBpkD,KAAKo1D,wBAE7B5kB,UAAWxwC,KAAKkvB,MAAMshB,UACtBD,WAAYvwC,KAAKkvB,MAAMqhB,WACvBmf,YAAa1vD,KAAKkvB,MAAMwgC,YACxBC,gBAAiB3vD,KAAKkvB,MAAMygC,gBAE5B/O,eAAgB5gD,KAAKkvB,MAAM0xB,eAE3BoG,mBAAoBhnD,KAAK2yD,uBACzB1T,OAAQj/C,KAAKuyD,OACbhqC,QAASvoB,KAAK0xD,YACdtQ,kBAAmBphD,KAAKg0D,sBACxB5+B,gBAAiBp1B,KAAK00D,sBACtB9lB,oBAAqB5uC,KAAKk0D,wBAC1BlR,UAAWhjD,KAAK6yD,wBAChB9V,YAAa/8C,KAAK4yD,oBAEnB5yD,KAAKkvB,MAAMinC,UACV,kBAAC,GAAD,CACE/uC,OAAQpnB,KAAKonB,OACb6Q,UAAWj4B,KAAKkvB,MAAM+I,UACtBqY,cAAetwC,KAAKkvB,MAAMohB,cAC1BnpB,MAAOnnB,KAAKkvB,MAAM8H,cAClByZ,mBAAoBzwC,KAAKkvB,MAAMuhB,mBAC/BhZ,SAAUz3B,KAAKkvB,MAAMuI,SACrBwY,MAAOjwC,KAAKkvB,MAAMinC,UAElB3lB,UAAWxwC,KAAKkvB,MAAMshB,UACtBD,WAAYvwC,KAAKkvB,MAAMqhB,WACvBmf,YAAa1vD,KAAKkvB,MAAMwgC,YACxBC,gBAAiB3vD,KAAKkvB,MAAMygC,gBAE5B5f,WAAY/vC,KAAKu8D,cACjBttB,yBAA0BjvC,KAAKi0D,yBAC/BttC,YAAa3mB,KAAK60D,gBAClBjmB,oBAAqB5uC,KAAKk0D,wBAC1BlkB,sBAAuBhwC,KAAKgtC,0BAC5BzC,cAAevqC,KAAKq0D,yBACpB7pB,iBAAkBxqC,KAAKs0D,4BACvB7pB,aAAczqC,KAAKu0D,wBACnB7pB,aAAc1qC,KAAKw0D,wBACnB7pB,cAAe3qC,KAAKy0D,kBACpBsJ,YAAa/9D,KAAKg+D,yBAClB70B,yBAA0BnpC,KAAKm0D,wBAC/BrlB,iBAAkB9uC,KAAK4sC,gBACvB/S,WAAY75B,KAAKwyD,WACjBjqC,QAASvoB,KAAK0xD,YAEdt8B,gBAAiBp1B,KAAK00D,wBAGxB,OAOV,UAAexqC,EAAAA,EAAAA,YAAWinC,KCnyDpB,OAAE1qC,IAAW+0B,GAAeO,aAAa/2C,OAAO62C,SAAShwB,MACzDs6B,GAAY1/B,IAAUA,GAAOw3C,IAChC9nD,UAAU+nD,WAAa/nD,UAAU+nD,UAAU,IAC5C/nD,UAAUgwC,UACVhwC,UAAU+vC,cACV,KAGIiY,GAAehY,GAAS5uB,cAAc1E,MAAM,QAAQ,GAGpDurC,GAAWC,EAAYlY,IAAYmY,SAAWD,EAAYF,IAAgBA,GAAe,KACzFz5C,GAAW25C,EAAYD,IAG7B12C,SAASyE,qBAAqB,QAAQ,GAAGoyC,aAAa,OAAQH,IAE9Dj5D,IAAAA,OACE,kBAAC,eAAD,KACE,kBAAC,EAAAq5D,aAAD,CAAc9sB,OAAQyU,GAAUzhC,SAAUA,GAAU+5C,cAAev5D,IAAAA,UACjE,kBAAC,GAAD,QAGJwiB,SAASuE,eAAe,gB","sources":["webpack://tinode-webapp/./node_modules/idb/build/idb.js","webpack://tinode-webapp/webpack/bootstrap","webpack://tinode-webapp/webpack/runtime/compat get default export","webpack://tinode-webapp/webpack/runtime/define property getters","webpack://tinode-webapp/webpack/runtime/global","webpack://tinode-webapp/webpack/runtime/hasOwnProperty shorthand","webpack://tinode-webapp/external var \"React\"","webpack://tinode-webapp/external var \"ReactDOM\"","webpack://tinode-webapp/external var \"ReactIntl\"","webpack://tinode-webapp/./node_modules/@firebase/util/dist/index.esm2017.js","webpack://tinode-webapp/./node_modules/@firebase/component/dist/esm/index.esm2017.js","webpack://tinode-webapp/./node_modules/@firebase/logger/dist/esm/index.esm2017.js","webpack://tinode-webapp/./node_modules/@firebase/app/dist/esm/index.esm2017.js","webpack://tinode-webapp/./node_modules/firebase/app/dist/index.esm.js","webpack://tinode-webapp/./node_modules/@firebase/installations/dist/esm/index.esm2017.js","webpack://tinode-webapp/./node_modules/@firebase/messaging/dist/esm/index.esm2017.js","webpack://tinode-webapp/external var \"Tinode\"","webpack://tinode-webapp/./src/widgets/alert.jsx","webpack://tinode-webapp/./src/version.js","webpack://tinode-webapp/./src/config.js","webpack://tinode-webapp/./src/widgets/context-menu.jsx","webpack://tinode-webapp/./src/widgets/contact-badges.jsx","webpack://tinode-webapp/./src/lib/strformat.js","webpack://tinode-webapp/./src/lib/utils.js","webpack://tinode-webapp/./src/widgets/letter-tile.jsx","webpack://tinode-webapp/./src/widgets/unread-badge.jsx","webpack://tinode-webapp/./src/widgets/lazy-image.jsx","webpack://tinode-webapp/./src/widgets/file-progress.jsx","webpack://tinode-webapp/./src/widgets/uploading-image.jsx","webpack://tinode-webapp/./src/lib/blob-helpers.js","webpack://tinode-webapp/./src/lib/formatters.js","webpack://tinode-webapp/./src/widgets/contact.jsx","webpack://tinode-webapp/./src/widgets/contact-action.jsx","webpack://tinode-webapp/./src/widgets/contact-list.jsx","webpack://tinode-webapp/./src/widgets/search-contacts.jsx","webpack://tinode-webapp/./src/widgets/forward-dialog.jsx","webpack://tinode-webapp/./src/widgets/load-spinner.jsx","webpack://tinode-webapp/./src/widgets/avatar-upload.jsx","webpack://tinode-webapp/./src/widgets/badge-list.jsx","webpack://tinode-webapp/./src/widgets/checkbox.jsx","webpack://tinode-webapp/./src/widgets/menu-cancel.jsx","webpack://tinode-webapp/./src/widgets/error-panel.jsx","webpack://tinode-webapp/./src/widgets/chip.jsx","webpack://tinode-webapp/./src/widgets/chip-input.jsx","webpack://tinode-webapp/./src/widgets/group-manager.jsx","webpack://tinode-webapp/./src/widgets/permissions-editor.jsx","webpack://tinode-webapp/./src/widgets/cropper.jsx","webpack://tinode-webapp/./src/widgets/avatar-crop.jsx","webpack://tinode-webapp/./src/widgets/visible-password.jsx","webpack://tinode-webapp/./src/widgets/in-place-edit.jsx","webpack://tinode-webapp/./src/widgets/tag-manager.jsx","webpack://tinode-webapp/./src/widgets/topic-desc-edit.jsx","webpack://tinode-webapp/./src/widgets/topic-common.jsx","webpack://tinode-webapp/./src/widgets/topic-security.jsx","webpack://tinode-webapp/./src/views/info-view.jsx","webpack://tinode-webapp/./src/widgets/attachment.jsx","webpack://tinode-webapp/./src/widgets/received-marker.jsx","webpack://tinode-webapp/./src/widgets/chat-message.jsx","webpack://tinode-webapp/./src/widgets/audio-recorder.jsx","webpack://tinode-webapp/./src/widgets/send-message.jsx","webpack://tinode-webapp/./src/widgets/doc-preview.jsx","webpack://tinode-webapp/./src/widgets/group-subs.jsx","webpack://tinode-webapp/./src/widgets/image-preview.jsx","webpack://tinode-webapp/./src/widgets/invitation.jsx","webpack://tinode-webapp/./src/views/logo-view.jsx","webpack://tinode-webapp/./src/widgets/meta-message.jsx","webpack://tinode-webapp/./src/lib/navigation.js","webpack://tinode-webapp/./src/views/messages-view.jsx","webpack://tinode-webapp/./src/widgets/button-back.jsx","webpack://tinode-webapp/./src/widgets/menu-contacts.jsx","webpack://tinode-webapp/./src/widgets/menu-start.jsx","webpack://tinode-webapp/./src/widgets/side-navbar.jsx","webpack://tinode-webapp/./src/views/account-settings-view.jsx","webpack://tinode-webapp/./src/views/contacts-view.jsx","webpack://tinode-webapp/./src/lib/local-storage.js","webpack://tinode-webapp/./src/views/create-account-view.jsx","webpack://tinode-webapp/./src/views/acc-notifications-view.jsx","webpack://tinode-webapp/./src/views/acc-security-view.jsx","webpack://tinode-webapp/./src/views/acc-support-view.jsx","webpack://tinode-webapp/./src/views/login-view.jsx","webpack://tinode-webapp/./src/widgets/new-topic-by-id.jsx","webpack://tinode-webapp/./src/widgets/new-topic-group.jsx","webpack://tinode-webapp/./src/views/new-topic-view.jsx","webpack://tinode-webapp/./src/views/password-reset-view.jsx","webpack://tinode-webapp/./src/widgets/host-selector.jsx","webpack://tinode-webapp/./src/views/settings-view.jsx","webpack://tinode-webapp/./src/views/validation-view.jsx","webpack://tinode-webapp/./src/views/sidepanel-view.jsx","webpack://tinode-webapp/./src/lib/host-name.js","webpack://tinode-webapp/./src/views/tinode-web.jsx","webpack://tinode-webapp/./src/index.js"],"sourcesContent":["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (global = global || self, factory(global.idb = {}));\n}(this, function (exports) { 'use strict';\n\n function toArray(arr) {\n return Array.prototype.slice.call(arr);\n }\n\n function promisifyRequest(request) {\n return new Promise(function(resolve, reject) {\n request.onsuccess = function() {\n resolve(request.result);\n };\n\n request.onerror = function() {\n reject(request.error);\n };\n });\n }\n\n function promisifyRequestCall(obj, method, args) {\n var request;\n var p = new Promise(function(resolve, reject) {\n request = obj[method].apply(obj, args);\n promisifyRequest(request).then(resolve, reject);\n });\n\n p.request = request;\n return p;\n }\n\n function promisifyCursorRequestCall(obj, method, args) {\n var p = promisifyRequestCall(obj, method, args);\n return p.then(function(value) {\n if (!value) return;\n return new Cursor(value, p.request);\n });\n }\n\n function proxyProperties(ProxyClass, targetProp, properties) {\n properties.forEach(function(prop) {\n Object.defineProperty(ProxyClass.prototype, prop, {\n get: function() {\n return this[targetProp][prop];\n },\n set: function(val) {\n this[targetProp][prop] = val;\n }\n });\n });\n }\n\n function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return promisifyRequestCall(this[targetProp], prop, arguments);\n };\n });\n }\n\n function proxyMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return this[targetProp][prop].apply(this[targetProp], arguments);\n };\n });\n }\n\n function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) {\n properties.forEach(function(prop) {\n if (!(prop in Constructor.prototype)) return;\n ProxyClass.prototype[prop] = function() {\n return promisifyCursorRequestCall(this[targetProp], prop, arguments);\n };\n });\n }\n\n function Index(index) {\n this._index = index;\n }\n\n proxyProperties(Index, '_index', [\n 'name',\n 'keyPath',\n 'multiEntry',\n 'unique'\n ]);\n\n proxyRequestMethods(Index, '_index', IDBIndex, [\n 'get',\n 'getKey',\n 'getAll',\n 'getAllKeys',\n 'count'\n ]);\n\n proxyCursorRequestMethods(Index, '_index', IDBIndex, [\n 'openCursor',\n 'openKeyCursor'\n ]);\n\n function Cursor(cursor, request) {\n this._cursor = cursor;\n this._request = request;\n }\n\n proxyProperties(Cursor, '_cursor', [\n 'direction',\n 'key',\n 'primaryKey',\n 'value'\n ]);\n\n proxyRequestMethods(Cursor, '_cursor', IDBCursor, [\n 'update',\n 'delete'\n ]);\n\n // proxy 'next' methods\n ['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) {\n if (!(methodName in IDBCursor.prototype)) return;\n Cursor.prototype[methodName] = function() {\n var cursor = this;\n var args = arguments;\n return Promise.resolve().then(function() {\n cursor._cursor[methodName].apply(cursor._cursor, args);\n return promisifyRequest(cursor._request).then(function(value) {\n if (!value) return;\n return new Cursor(value, cursor._request);\n });\n });\n };\n });\n\n function ObjectStore(store) {\n this._store = store;\n }\n\n ObjectStore.prototype.createIndex = function() {\n return new Index(this._store.createIndex.apply(this._store, arguments));\n };\n\n ObjectStore.prototype.index = function() {\n return new Index(this._store.index.apply(this._store, arguments));\n };\n\n proxyProperties(ObjectStore, '_store', [\n 'name',\n 'keyPath',\n 'indexNames',\n 'autoIncrement'\n ]);\n\n proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [\n 'put',\n 'add',\n 'delete',\n 'clear',\n 'get',\n 'getAll',\n 'getKey',\n 'getAllKeys',\n 'count'\n ]);\n\n proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [\n 'openCursor',\n 'openKeyCursor'\n ]);\n\n proxyMethods(ObjectStore, '_store', IDBObjectStore, [\n 'deleteIndex'\n ]);\n\n function Transaction(idbTransaction) {\n this._tx = idbTransaction;\n this.complete = new Promise(function(resolve, reject) {\n idbTransaction.oncomplete = function() {\n resolve();\n };\n idbTransaction.onerror = function() {\n reject(idbTransaction.error);\n };\n idbTransaction.onabort = function() {\n reject(idbTransaction.error);\n };\n });\n }\n\n Transaction.prototype.objectStore = function() {\n return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments));\n };\n\n proxyProperties(Transaction, '_tx', [\n 'objectStoreNames',\n 'mode'\n ]);\n\n proxyMethods(Transaction, '_tx', IDBTransaction, [\n 'abort'\n ]);\n\n function UpgradeDB(db, oldVersion, transaction) {\n this._db = db;\n this.oldVersion = oldVersion;\n this.transaction = new Transaction(transaction);\n }\n\n UpgradeDB.prototype.createObjectStore = function() {\n return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments));\n };\n\n proxyProperties(UpgradeDB, '_db', [\n 'name',\n 'version',\n 'objectStoreNames'\n ]);\n\n proxyMethods(UpgradeDB, '_db', IDBDatabase, [\n 'deleteObjectStore',\n 'close'\n ]);\n\n function DB(db) {\n this._db = db;\n }\n\n DB.prototype.transaction = function() {\n return new Transaction(this._db.transaction.apply(this._db, arguments));\n };\n\n proxyProperties(DB, '_db', [\n 'name',\n 'version',\n 'objectStoreNames'\n ]);\n\n proxyMethods(DB, '_db', IDBDatabase, [\n 'close'\n ]);\n\n // Add cursor iterators\n // TODO: remove this once browsers do the right thing with promises\n ['openCursor', 'openKeyCursor'].forEach(function(funcName) {\n [ObjectStore, Index].forEach(function(Constructor) {\n // Don't create iterateKeyCursor if openKeyCursor doesn't exist.\n if (!(funcName in Constructor.prototype)) return;\n\n Constructor.prototype[funcName.replace('open', 'iterate')] = function() {\n var args = toArray(arguments);\n var callback = args[args.length - 1];\n var nativeObject = this._store || this._index;\n var request = nativeObject[funcName].apply(nativeObject, args.slice(0, -1));\n request.onsuccess = function() {\n callback(request.result);\n };\n };\n });\n });\n\n // polyfill getAll\n [Index, ObjectStore].forEach(function(Constructor) {\n if (Constructor.prototype.getAll) return;\n Constructor.prototype.getAll = function(query, count) {\n var instance = this;\n var items = [];\n\n return new Promise(function(resolve) {\n instance.iterateCursor(query, function(cursor) {\n if (!cursor) {\n resolve(items);\n return;\n }\n items.push(cursor.value);\n\n if (count !== undefined && items.length == count) {\n resolve(items);\n return;\n }\n cursor.continue();\n });\n });\n };\n });\n\n function openDb(name, version, upgradeCallback) {\n var p = promisifyRequestCall(indexedDB, 'open', [name, version]);\n var request = p.request;\n\n if (request) {\n request.onupgradeneeded = function(event) {\n if (upgradeCallback) {\n upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction));\n }\n };\n }\n\n return p.then(function(db) {\n return new DB(db);\n });\n }\n\n function deleteDb(name) {\n return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]);\n }\n\n exports.openDb = openDb;\n exports.deleteDb = deleteDb;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\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].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","const __WEBPACK_NAMESPACE_OBJECT__ = React;","const __WEBPACK_NAMESPACE_OBJECT__ = ReactDOM;","const __WEBPACK_NAMESPACE_OBJECT__ = ReactIntl;","/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @fileoverview Firebase constants. Some of these (@defines) can be overridden at compile-time.\r\n */\r\nconst CONSTANTS = {\r\n /**\r\n * @define {boolean} Whether this is the client Node.js SDK.\r\n */\r\n NODE_CLIENT: false,\r\n /**\r\n * @define {boolean} Whether this is the Admin Node.js SDK.\r\n */\r\n NODE_ADMIN: false,\r\n /**\r\n * Firebase SDK Version\r\n */\r\n SDK_VERSION: '${JSCORE_VERSION}'\r\n};\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Throws an error if the provided assertion is falsy\r\n */\r\nconst assert = function (assertion, message) {\r\n if (!assertion) {\r\n throw assertionError(message);\r\n }\r\n};\r\n/**\r\n * Returns an Error object suitable for throwing.\r\n */\r\nconst assertionError = function (message) {\r\n return new Error('Firebase Database (' +\r\n CONSTANTS.SDK_VERSION +\r\n ') INTERNAL ASSERT FAILED: ' +\r\n message);\r\n};\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst stringToByteArray$1 = function (str) {\r\n // TODO(user): Use native implementations if/when available\r\n const out = [];\r\n let p = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n let c = str.charCodeAt(i);\r\n if (c < 128) {\r\n out[p++] = c;\r\n }\r\n else if (c < 2048) {\r\n out[p++] = (c >> 6) | 192;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else if ((c & 0xfc00) === 0xd800 &&\r\n i + 1 < str.length &&\r\n (str.charCodeAt(i + 1) & 0xfc00) === 0xdc00) {\r\n // Surrogate Pair\r\n c = 0x10000 + ((c & 0x03ff) << 10) + (str.charCodeAt(++i) & 0x03ff);\r\n out[p++] = (c >> 18) | 240;\r\n out[p++] = ((c >> 12) & 63) | 128;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else {\r\n out[p++] = (c >> 12) | 224;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n }\r\n return out;\r\n};\r\n/**\r\n * Turns an array of numbers into the string given by the concatenation of the\r\n * characters to which the numbers correspond.\r\n * @param bytes Array of numbers representing characters.\r\n * @return Stringification of the array.\r\n */\r\nconst byteArrayToString = function (bytes) {\r\n // TODO(user): Use native implementations if/when available\r\n const out = [];\r\n let pos = 0, c = 0;\r\n while (pos < bytes.length) {\r\n const c1 = bytes[pos++];\r\n if (c1 < 128) {\r\n out[c++] = String.fromCharCode(c1);\r\n }\r\n else if (c1 > 191 && c1 < 224) {\r\n const c2 = bytes[pos++];\r\n out[c++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));\r\n }\r\n else if (c1 > 239 && c1 < 365) {\r\n // Surrogate Pair\r\n const c2 = bytes[pos++];\r\n const c3 = bytes[pos++];\r\n const c4 = bytes[pos++];\r\n const u = (((c1 & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63)) -\r\n 0x10000;\r\n out[c++] = String.fromCharCode(0xd800 + (u >> 10));\r\n out[c++] = String.fromCharCode(0xdc00 + (u & 1023));\r\n }\r\n else {\r\n const c2 = bytes[pos++];\r\n const c3 = bytes[pos++];\r\n out[c++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));\r\n }\r\n }\r\n return out.join('');\r\n};\r\n// We define it as an object literal instead of a class because a class compiled down to es5 can't\r\n// be treeshaked. https://github.com/rollup/rollup/issues/1691\r\n// Static lookup maps, lazily populated by init_()\r\nconst base64 = {\r\n /**\r\n * Maps bytes to characters.\r\n */\r\n byteToCharMap_: null,\r\n /**\r\n * Maps characters to bytes.\r\n */\r\n charToByteMap_: null,\r\n /**\r\n * Maps bytes to websafe characters.\r\n * @private\r\n */\r\n byteToCharMapWebSafe_: null,\r\n /**\r\n * Maps websafe characters to bytes.\r\n * @private\r\n */\r\n charToByteMapWebSafe_: null,\r\n /**\r\n * Our default alphabet, shared between\r\n * ENCODED_VALS and ENCODED_VALS_WEBSAFE\r\n */\r\n ENCODED_VALS_BASE: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789',\r\n /**\r\n * Our default alphabet. Value 64 (=) is special; it means \"nothing.\"\r\n */\r\n get ENCODED_VALS() {\r\n return this.ENCODED_VALS_BASE + '+/=';\r\n },\r\n /**\r\n * Our websafe alphabet.\r\n */\r\n get ENCODED_VALS_WEBSAFE() {\r\n return this.ENCODED_VALS_BASE + '-_.';\r\n },\r\n /**\r\n * Whether this browser supports the atob and btoa functions. This extension\r\n * started at Mozilla but is now implemented by many browsers. We use the\r\n * ASSUME_* variables to avoid pulling in the full useragent detection library\r\n * but still allowing the standard per-browser compilations.\r\n *\r\n */\r\n HAS_NATIVE_SUPPORT: typeof atob === 'function',\r\n /**\r\n * Base64-encode an array of bytes.\r\n *\r\n * @param input An array of bytes (numbers with\r\n * value in [0, 255]) to encode.\r\n * @param webSafe Boolean indicating we should use the\r\n * alternative alphabet.\r\n * @return The base64 encoded string.\r\n */\r\n encodeByteArray(input, webSafe) {\r\n if (!Array.isArray(input)) {\r\n throw Error('encodeByteArray takes an array as a parameter');\r\n }\r\n this.init_();\r\n const byteToCharMap = webSafe\r\n ? this.byteToCharMapWebSafe_\r\n : this.byteToCharMap_;\r\n const output = [];\r\n for (let i = 0; i < input.length; i += 3) {\r\n const byte1 = input[i];\r\n const haveByte2 = i + 1 < input.length;\r\n const byte2 = haveByte2 ? input[i + 1] : 0;\r\n const haveByte3 = i + 2 < input.length;\r\n const byte3 = haveByte3 ? input[i + 2] : 0;\r\n const outByte1 = byte1 >> 2;\r\n const outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);\r\n let outByte3 = ((byte2 & 0x0f) << 2) | (byte3 >> 6);\r\n let outByte4 = byte3 & 0x3f;\r\n if (!haveByte3) {\r\n outByte4 = 64;\r\n if (!haveByte2) {\r\n outByte3 = 64;\r\n }\r\n }\r\n output.push(byteToCharMap[outByte1], byteToCharMap[outByte2], byteToCharMap[outByte3], byteToCharMap[outByte4]);\r\n }\r\n return output.join('');\r\n },\r\n /**\r\n * Base64-encode a string.\r\n *\r\n * @param input A string to encode.\r\n * @param webSafe If true, we should use the\r\n * alternative alphabet.\r\n * @return The base64 encoded string.\r\n */\r\n encodeString(input, webSafe) {\r\n // Shortcut for Mozilla browsers that implement\r\n // a native base64 encoder in the form of \"btoa/atob\"\r\n if (this.HAS_NATIVE_SUPPORT && !webSafe) {\r\n return btoa(input);\r\n }\r\n return this.encodeByteArray(stringToByteArray$1(input), webSafe);\r\n },\r\n /**\r\n * Base64-decode a string.\r\n *\r\n * @param input to decode.\r\n * @param webSafe True if we should use the\r\n * alternative alphabet.\r\n * @return string representing the decoded value.\r\n */\r\n decodeString(input, webSafe) {\r\n // Shortcut for Mozilla browsers that implement\r\n // a native base64 encoder in the form of \"btoa/atob\"\r\n if (this.HAS_NATIVE_SUPPORT && !webSafe) {\r\n return atob(input);\r\n }\r\n return byteArrayToString(this.decodeStringToByteArray(input, webSafe));\r\n },\r\n /**\r\n * Base64-decode a string.\r\n *\r\n * In base-64 decoding, groups of four characters are converted into three\r\n * bytes. If the encoder did not apply padding, the input length may not\r\n * be a multiple of 4.\r\n *\r\n * In this case, the last group will have fewer than 4 characters, and\r\n * padding will be inferred. If the group has one or two characters, it decodes\r\n * to one byte. If the group has three characters, it decodes to two bytes.\r\n *\r\n * @param input Input to decode.\r\n * @param webSafe True if we should use the web-safe alphabet.\r\n * @return bytes representing the decoded value.\r\n */\r\n decodeStringToByteArray(input, webSafe) {\r\n this.init_();\r\n const charToByteMap = webSafe\r\n ? this.charToByteMapWebSafe_\r\n : this.charToByteMap_;\r\n const output = [];\r\n for (let i = 0; i < input.length;) {\r\n const byte1 = charToByteMap[input.charAt(i++)];\r\n const haveByte2 = i < input.length;\r\n const byte2 = haveByte2 ? charToByteMap[input.charAt(i)] : 0;\r\n ++i;\r\n const haveByte3 = i < input.length;\r\n const byte3 = haveByte3 ? charToByteMap[input.charAt(i)] : 64;\r\n ++i;\r\n const haveByte4 = i < input.length;\r\n const byte4 = haveByte4 ? charToByteMap[input.charAt(i)] : 64;\r\n ++i;\r\n if (byte1 == null || byte2 == null || byte3 == null || byte4 == null) {\r\n throw Error();\r\n }\r\n const outByte1 = (byte1 << 2) | (byte2 >> 4);\r\n output.push(outByte1);\r\n if (byte3 !== 64) {\r\n const outByte2 = ((byte2 << 4) & 0xf0) | (byte3 >> 2);\r\n output.push(outByte2);\r\n if (byte4 !== 64) {\r\n const outByte3 = ((byte3 << 6) & 0xc0) | byte4;\r\n output.push(outByte3);\r\n }\r\n }\r\n }\r\n return output;\r\n },\r\n /**\r\n * Lazy static initialization function. Called before\r\n * accessing any of the static map variables.\r\n * @private\r\n */\r\n init_() {\r\n if (!this.byteToCharMap_) {\r\n this.byteToCharMap_ = {};\r\n this.charToByteMap_ = {};\r\n this.byteToCharMapWebSafe_ = {};\r\n this.charToByteMapWebSafe_ = {};\r\n // We want quick mappings back and forth, so we precompute two maps.\r\n for (let i = 0; i < this.ENCODED_VALS.length; i++) {\r\n this.byteToCharMap_[i] = this.ENCODED_VALS.charAt(i);\r\n this.charToByteMap_[this.byteToCharMap_[i]] = i;\r\n this.byteToCharMapWebSafe_[i] = this.ENCODED_VALS_WEBSAFE.charAt(i);\r\n this.charToByteMapWebSafe_[this.byteToCharMapWebSafe_[i]] = i;\r\n // Be forgiving when decoding and correctly decode both encodings.\r\n if (i >= this.ENCODED_VALS_BASE.length) {\r\n this.charToByteMap_[this.ENCODED_VALS_WEBSAFE.charAt(i)] = i;\r\n this.charToByteMapWebSafe_[this.ENCODED_VALS.charAt(i)] = i;\r\n }\r\n }\r\n }\r\n }\r\n};\r\n/**\r\n * URL-safe base64 encoding\r\n */\r\nconst base64Encode = function (str) {\r\n const utf8Bytes = stringToByteArray$1(str);\r\n return base64.encodeByteArray(utf8Bytes, true);\r\n};\r\n/**\r\n * URL-safe base64 encoding (without \".\" padding in the end).\r\n * e.g. Used in JSON Web Token (JWT) parts.\r\n */\r\nconst base64urlEncodeWithoutPadding = function (str) {\r\n // Use base64url encoding and remove padding in the end (dot characters).\r\n return base64Encode(str).replace(/\\./g, '');\r\n};\r\n/**\r\n * URL-safe base64 decoding\r\n *\r\n * NOTE: DO NOT use the global atob() function - it does NOT support the\r\n * base64Url variant encoding.\r\n *\r\n * @param str To be decoded\r\n * @return Decoded result, if possible\r\n */\r\nconst base64Decode = function (str) {\r\n try {\r\n return base64.decodeString(str, true);\r\n }\r\n catch (e) {\r\n console.error('base64Decode failed: ', e);\r\n }\r\n return null;\r\n};\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Do a deep-copy of basic JavaScript Objects or Arrays.\r\n */\r\nfunction deepCopy(value) {\r\n return deepExtend(undefined, value);\r\n}\r\n/**\r\n * Copy properties from source to target (recursively allows extension\r\n * of Objects and Arrays). Scalar values in the target are over-written.\r\n * If target is undefined, an object of the appropriate type will be created\r\n * (and returned).\r\n *\r\n * We recursively copy all child properties of plain Objects in the source- so\r\n * that namespace- like dictionaries are merged.\r\n *\r\n * Note that the target can be a function, in which case the properties in\r\n * the source Object are copied onto it as static properties of the Function.\r\n *\r\n * Note: we don't merge __proto__ to prevent prototype pollution\r\n */\r\nfunction deepExtend(target, source) {\r\n if (!(source instanceof Object)) {\r\n return source;\r\n }\r\n switch (source.constructor) {\r\n case Date:\r\n // Treat Dates like scalars; if the target date object had any child\r\n // properties - they will be lost!\r\n const dateValue = source;\r\n return new Date(dateValue.getTime());\r\n case Object:\r\n if (target === undefined) {\r\n target = {};\r\n }\r\n break;\r\n case Array:\r\n // Always copy the array source and overwrite the target.\r\n target = [];\r\n break;\r\n default:\r\n // Not a plain Object - treat it as a scalar.\r\n return source;\r\n }\r\n for (const prop in source) {\r\n // use isValidKey to guard against prototype pollution. See https://snyk.io/vuln/SNYK-JS-LODASH-450202\r\n if (!source.hasOwnProperty(prop) || !isValidKey(prop)) {\r\n continue;\r\n }\r\n target[prop] = deepExtend(target[prop], source[prop]);\r\n }\r\n return target;\r\n}\r\nfunction isValidKey(key) {\r\n return key !== '__proto__';\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass Deferred {\r\n constructor() {\r\n this.reject = () => { };\r\n this.resolve = () => { };\r\n this.promise = new Promise((resolve, reject) => {\r\n this.resolve = resolve;\r\n this.reject = reject;\r\n });\r\n }\r\n /**\r\n * Our API internals are not promiseified and cannot because our callback APIs have subtle expectations around\r\n * invoking promises inline, which Promises are forbidden to do. This method accepts an optional node-style callback\r\n * and returns a node-style callback which will resolve or reject the Deferred's promise.\r\n */\r\n wrapCallback(callback) {\r\n return (error, value) => {\r\n if (error) {\r\n this.reject(error);\r\n }\r\n else {\r\n this.resolve(value);\r\n }\r\n if (typeof callback === 'function') {\r\n // Attaching noop handler just in case developer wasn't expecting\r\n // promises\r\n this.promise.catch(() => { });\r\n // Some of our callbacks don't expect a value and our own tests\r\n // assert that the parameter length is 1\r\n if (callback.length === 1) {\r\n callback(error);\r\n }\r\n else {\r\n callback(error, value);\r\n }\r\n }\r\n };\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2021 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction createMockUserToken(token, projectId) {\r\n if (token.uid) {\r\n throw new Error('The \"uid\" field is no longer supported by mockUserToken. Please use \"sub\" instead for Firebase Auth User ID.');\r\n }\r\n // Unsecured JWTs use \"none\" as the algorithm.\r\n const header = {\r\n alg: 'none',\r\n type: 'JWT'\r\n };\r\n const project = projectId || 'demo-project';\r\n const iat = token.iat || 0;\r\n const sub = token.sub || token.user_id;\r\n if (!sub) {\r\n throw new Error(\"mockUserToken must contain 'sub' or 'user_id' field!\");\r\n }\r\n const payload = Object.assign({ \r\n // Set all required fields to decent defaults\r\n iss: `https://securetoken.google.com/${project}`, aud: project, iat, exp: iat + 3600, auth_time: iat, sub, user_id: sub, firebase: {\r\n sign_in_provider: 'custom',\r\n identities: {}\r\n } }, token);\r\n // Unsecured JWTs use the empty string as a signature.\r\n const signature = '';\r\n return [\r\n base64urlEncodeWithoutPadding(JSON.stringify(header)),\r\n base64urlEncodeWithoutPadding(JSON.stringify(payload)),\r\n signature\r\n ].join('.');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns navigator.userAgent string or '' if it's not defined.\r\n * @return user agent string\r\n */\r\nfunction getUA() {\r\n if (typeof navigator !== 'undefined' &&\r\n typeof navigator['userAgent'] === 'string') {\r\n return navigator['userAgent'];\r\n }\r\n else {\r\n return '';\r\n }\r\n}\r\n/**\r\n * Detect Cordova / PhoneGap / Ionic frameworks on a mobile device.\r\n *\r\n * Deliberately does not rely on checking `file://` URLs (as this fails PhoneGap\r\n * in the Ripple emulator) nor Cordova `onDeviceReady`, which would normally\r\n * wait for a callback.\r\n */\r\nfunction isMobileCordova() {\r\n return (typeof window !== 'undefined' &&\r\n // @ts-ignore Setting up an broadly applicable index signature for Window\r\n // just to deal with this case would probably be a bad idea.\r\n !!(window['cordova'] || window['phonegap'] || window['PhoneGap']) &&\r\n /ios|iphone|ipod|ipad|android|blackberry|iemobile/i.test(getUA()));\r\n}\r\n/**\r\n * Detect Node.js.\r\n *\r\n * @return true if Node.js environment is detected.\r\n */\r\n// Node detection logic from: https://github.com/iliakan/detect-node/\r\nfunction isNode() {\r\n try {\r\n return (Object.prototype.toString.call(global.process) === '[object process]');\r\n }\r\n catch (e) {\r\n return false;\r\n }\r\n}\r\n/**\r\n * Detect Browser Environment\r\n */\r\nfunction isBrowser() {\r\n return typeof self === 'object' && self.self === self;\r\n}\r\nfunction isBrowserExtension() {\r\n const runtime = typeof chrome === 'object'\r\n ? chrome.runtime\r\n : typeof browser === 'object'\r\n ? browser.runtime\r\n : undefined;\r\n return typeof runtime === 'object' && runtime.id !== undefined;\r\n}\r\n/**\r\n * Detect React Native.\r\n *\r\n * @return true if ReactNative environment is detected.\r\n */\r\nfunction isReactNative() {\r\n return (typeof navigator === 'object' && navigator['product'] === 'ReactNative');\r\n}\r\n/** Detects Electron apps. */\r\nfunction isElectron() {\r\n return getUA().indexOf('Electron/') >= 0;\r\n}\r\n/** Detects Internet Explorer. */\r\nfunction isIE() {\r\n const ua = getUA();\r\n return ua.indexOf('MSIE ') >= 0 || ua.indexOf('Trident/') >= 0;\r\n}\r\n/** Detects Universal Windows Platform apps. */\r\nfunction isUWP() {\r\n return getUA().indexOf('MSAppHost/') >= 0;\r\n}\r\n/**\r\n * Detect whether the current SDK build is the Node version.\r\n *\r\n * @return true if it's the Node SDK build.\r\n */\r\nfunction isNodeSdk() {\r\n return CONSTANTS.NODE_CLIENT === true || CONSTANTS.NODE_ADMIN === true;\r\n}\r\n/** Returns true if we are running in Safari. */\r\nfunction isSafari() {\r\n return (!isNode() &&\r\n navigator.userAgent.includes('Safari') &&\r\n !navigator.userAgent.includes('Chrome'));\r\n}\r\n/**\r\n * This method checks if indexedDB is supported by current browser/service worker context\r\n * @return true if indexedDB is supported by current browser/service worker context\r\n */\r\nfunction isIndexedDBAvailable() {\r\n return typeof indexedDB === 'object';\r\n}\r\n/**\r\n * This method validates browser/sw context for indexedDB by opening a dummy indexedDB database and reject\r\n * if errors occur during the database open operation.\r\n *\r\n * @throws exception if current browser/sw context can't run idb.open (ex: Safari iframe, Firefox\r\n * private browsing)\r\n */\r\nfunction validateIndexedDBOpenable() {\r\n return new Promise((resolve, reject) => {\r\n try {\r\n let preExist = true;\r\n const DB_CHECK_NAME = 'validate-browser-context-for-indexeddb-analytics-module';\r\n const request = self.indexedDB.open(DB_CHECK_NAME);\r\n request.onsuccess = () => {\r\n request.result.close();\r\n // delete database only when it doesn't pre-exist\r\n if (!preExist) {\r\n self.indexedDB.deleteDatabase(DB_CHECK_NAME);\r\n }\r\n resolve(true);\r\n };\r\n request.onupgradeneeded = () => {\r\n preExist = false;\r\n };\r\n request.onerror = () => {\r\n var _a;\r\n reject(((_a = request.error) === null || _a === void 0 ? void 0 : _a.message) || '');\r\n };\r\n }\r\n catch (error) {\r\n reject(error);\r\n }\r\n });\r\n}\r\n/**\r\n *\r\n * This method checks whether cookie is enabled within current browser\r\n * @return true if cookie is enabled within current browser\r\n */\r\nfunction areCookiesEnabled() {\r\n if (typeof navigator === 'undefined' || !navigator.cookieEnabled) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n/**\r\n * Polyfill for `globalThis` object.\r\n * @returns the `globalThis` object for the given environment.\r\n */\r\nfunction getGlobal() {\r\n if (typeof self !== 'undefined') {\r\n return self;\r\n }\r\n if (typeof window !== 'undefined') {\r\n return window;\r\n }\r\n if (typeof global !== 'undefined') {\r\n return global;\r\n }\r\n throw new Error('Unable to locate global object.');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @fileoverview Standardized Firebase Error.\r\n *\r\n * Usage:\r\n *\r\n * // Typescript string literals for type-safe codes\r\n * type Err =\r\n * 'unknown' |\r\n * 'object-not-found'\r\n * ;\r\n *\r\n * // Closure enum for type-safe error codes\r\n * // at-enum {string}\r\n * var Err = {\r\n * UNKNOWN: 'unknown',\r\n * OBJECT_NOT_FOUND: 'object-not-found',\r\n * }\r\n *\r\n * let errors: Map = {\r\n * 'generic-error': \"Unknown error\",\r\n * 'file-not-found': \"Could not find file: {$file}\",\r\n * };\r\n *\r\n * // Type-safe function - must pass a valid error code as param.\r\n * let error = new ErrorFactory('service', 'Service', errors);\r\n *\r\n * ...\r\n * throw error.create(Err.GENERIC);\r\n * ...\r\n * throw error.create(Err.FILE_NOT_FOUND, {'file': fileName});\r\n * ...\r\n * // Service: Could not file file: foo.txt (service/file-not-found).\r\n *\r\n * catch (e) {\r\n * assert(e.message === \"Could not find file: foo.txt.\");\r\n * if (e.code === 'service/file-not-found') {\r\n * console.log(\"Could not read file: \" + e['file']);\r\n * }\r\n * }\r\n */\r\nconst ERROR_NAME = 'FirebaseError';\r\n// Based on code from:\r\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types\r\nclass FirebaseError extends Error {\r\n constructor(\r\n /** The error code for this error. */\r\n code, message, \r\n /** Custom data for this error. */\r\n customData) {\r\n super(message);\r\n this.code = code;\r\n this.customData = customData;\r\n /** The custom name for all FirebaseErrors. */\r\n this.name = ERROR_NAME;\r\n // Fix For ES5\r\n // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work\r\n Object.setPrototypeOf(this, FirebaseError.prototype);\r\n // Maintains proper stack trace for where our error was thrown.\r\n // Only available on V8.\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, ErrorFactory.prototype.create);\r\n }\r\n }\r\n}\r\nclass ErrorFactory {\r\n constructor(service, serviceName, errors) {\r\n this.service = service;\r\n this.serviceName = serviceName;\r\n this.errors = errors;\r\n }\r\n create(code, ...data) {\r\n const customData = data[0] || {};\r\n const fullCode = `${this.service}/${code}`;\r\n const template = this.errors[code];\r\n const message = template ? replaceTemplate(template, customData) : 'Error';\r\n // Service Name: Error message (service/code).\r\n const fullMessage = `${this.serviceName}: ${message} (${fullCode}).`;\r\n const error = new FirebaseError(fullCode, fullMessage, customData);\r\n return error;\r\n }\r\n}\r\nfunction replaceTemplate(template, data) {\r\n return template.replace(PATTERN, (_, key) => {\r\n const value = data[key];\r\n return value != null ? String(value) : `<${key}?>`;\r\n });\r\n}\r\nconst PATTERN = /\\{\\$([^}]+)}/g;\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Evaluates a JSON string into a javascript object.\r\n *\r\n * @param {string} str A string containing JSON.\r\n * @return {*} The javascript object representing the specified JSON.\r\n */\r\nfunction jsonEval(str) {\r\n return JSON.parse(str);\r\n}\r\n/**\r\n * Returns JSON representing a javascript object.\r\n * @param {*} data Javascript object to be stringified.\r\n * @return {string} The JSON contents of the object.\r\n */\r\nfunction stringify(data) {\r\n return JSON.stringify(data);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Decodes a Firebase auth. token into constituent parts.\r\n *\r\n * Notes:\r\n * - May return with invalid / incomplete claims if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst decode = function (token) {\r\n let header = {}, claims = {}, data = {}, signature = '';\r\n try {\r\n const parts = token.split('.');\r\n header = jsonEval(base64Decode(parts[0]) || '');\r\n claims = jsonEval(base64Decode(parts[1]) || '');\r\n signature = parts[2];\r\n data = claims['d'] || {};\r\n delete claims['d'];\r\n }\r\n catch (e) { }\r\n return {\r\n header,\r\n claims,\r\n data,\r\n signature\r\n };\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and checks the validity of its time-based claims. Will return true if the\r\n * token is within the time window authorized by the 'nbf' (not-before) and 'iat' (issued-at) claims.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isValidTimestamp = function (token) {\r\n const claims = decode(token).claims;\r\n const now = Math.floor(new Date().getTime() / 1000);\r\n let validSince = 0, validUntil = 0;\r\n if (typeof claims === 'object') {\r\n if (claims.hasOwnProperty('nbf')) {\r\n validSince = claims['nbf'];\r\n }\r\n else if (claims.hasOwnProperty('iat')) {\r\n validSince = claims['iat'];\r\n }\r\n if (claims.hasOwnProperty('exp')) {\r\n validUntil = claims['exp'];\r\n }\r\n else {\r\n // token will expire after 24h by default\r\n validUntil = validSince + 86400;\r\n }\r\n }\r\n return (!!now &&\r\n !!validSince &&\r\n !!validUntil &&\r\n now >= validSince &&\r\n now <= validUntil);\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and returns its issued at time if valid, null otherwise.\r\n *\r\n * Notes:\r\n * - May return null if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst issuedAtTime = function (token) {\r\n const claims = decode(token).claims;\r\n if (typeof claims === 'object' && claims.hasOwnProperty('iat')) {\r\n return claims['iat'];\r\n }\r\n return null;\r\n};\r\n/**\r\n * Decodes a Firebase auth. token and checks the validity of its format. Expects a valid issued-at time.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isValidFormat = function (token) {\r\n const decoded = decode(token), claims = decoded.claims;\r\n return !!claims && typeof claims === 'object' && claims.hasOwnProperty('iat');\r\n};\r\n/**\r\n * Attempts to peer into an auth token and determine if it's an admin auth token by looking at the claims portion.\r\n *\r\n * Notes:\r\n * - May return a false negative if there's no native base64 decoding support.\r\n * - Doesn't check if the token is actually valid.\r\n */\r\nconst isAdmin = function (token) {\r\n const claims = decode(token).claims;\r\n return typeof claims === 'object' && claims['admin'] === true;\r\n};\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction contains(obj, key) {\r\n return Object.prototype.hasOwnProperty.call(obj, key);\r\n}\r\nfunction safeGet(obj, key) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n return obj[key];\r\n }\r\n else {\r\n return undefined;\r\n }\r\n}\r\nfunction isEmpty(obj) {\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\nfunction map(obj, fn, contextObj) {\r\n const res = {};\r\n for (const key in obj) {\r\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\r\n res[key] = fn.call(contextObj, obj[key], key, obj);\r\n }\r\n }\r\n return res;\r\n}\r\n/**\r\n * Deep equal two objects. Support Arrays and Objects.\r\n */\r\nfunction deepEqual(a, b) {\r\n if (a === b) {\r\n return true;\r\n }\r\n const aKeys = Object.keys(a);\r\n const bKeys = Object.keys(b);\r\n for (const k of aKeys) {\r\n if (!bKeys.includes(k)) {\r\n return false;\r\n }\r\n const aProp = a[k];\r\n const bProp = b[k];\r\n if (isObject(aProp) && isObject(bProp)) {\r\n if (!deepEqual(aProp, bProp)) {\r\n return false;\r\n }\r\n }\r\n else if (aProp !== bProp) {\r\n return false;\r\n }\r\n }\r\n for (const k of bKeys) {\r\n if (!aKeys.includes(k)) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\nfunction isObject(thing) {\r\n return thing !== null && typeof thing === 'object';\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a querystring-formatted string (e.g. &arg=val&arg2=val2) from a\r\n * params object (e.g. {arg: 'val', arg2: 'val2'})\r\n * Note: You must prepend it with ? when adding it to a URL.\r\n */\r\nfunction querystring(querystringParams) {\r\n const params = [];\r\n for (const [key, value] of Object.entries(querystringParams)) {\r\n if (Array.isArray(value)) {\r\n value.forEach(arrayVal => {\r\n params.push(encodeURIComponent(key) + '=' + encodeURIComponent(arrayVal));\r\n });\r\n }\r\n else {\r\n params.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));\r\n }\r\n }\r\n return params.length ? '&' + params.join('&') : '';\r\n}\r\n/**\r\n * Decodes a querystring (e.g. ?arg=val&arg2=val2) into a params object\r\n * (e.g. {arg: 'val', arg2: 'val2'})\r\n */\r\nfunction querystringDecode(querystring) {\r\n const obj = {};\r\n const tokens = querystring.replace(/^\\?/, '').split('&');\r\n tokens.forEach(token => {\r\n if (token) {\r\n const [key, value] = token.split('=');\r\n obj[decodeURIComponent(key)] = decodeURIComponent(value);\r\n }\r\n });\r\n return obj;\r\n}\r\n/**\r\n * Extract the query string part of a URL, including the leading question mark (if present).\r\n */\r\nfunction extractQuerystring(url) {\r\n const queryStart = url.indexOf('?');\r\n if (!queryStart) {\r\n return '';\r\n }\r\n const fragmentStart = url.indexOf('#', queryStart);\r\n return url.substring(queryStart, fragmentStart > 0 ? fragmentStart : undefined);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @fileoverview SHA-1 cryptographic hash.\r\n * Variable names follow the notation in FIPS PUB 180-3:\r\n * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf.\r\n *\r\n * Usage:\r\n * var sha1 = new sha1();\r\n * sha1.update(bytes);\r\n * var hash = sha1.digest();\r\n *\r\n * Performance:\r\n * Chrome 23: ~400 Mbit/s\r\n * Firefox 16: ~250 Mbit/s\r\n *\r\n */\r\n/**\r\n * SHA-1 cryptographic hash constructor.\r\n *\r\n * The properties declared here are discussed in the above algorithm document.\r\n * @constructor\r\n * @final\r\n * @struct\r\n */\r\nclass Sha1 {\r\n constructor() {\r\n /**\r\n * Holds the previous values of accumulated variables a-e in the compress_\r\n * function.\r\n * @private\r\n */\r\n this.chain_ = [];\r\n /**\r\n * A buffer holding the partially computed hash result.\r\n * @private\r\n */\r\n this.buf_ = [];\r\n /**\r\n * An array of 80 bytes, each a part of the message to be hashed. Referred to\r\n * as the message schedule in the docs.\r\n * @private\r\n */\r\n this.W_ = [];\r\n /**\r\n * Contains data needed to pad messages less than 64 bytes.\r\n * @private\r\n */\r\n this.pad_ = [];\r\n /**\r\n * @private {number}\r\n */\r\n this.inbuf_ = 0;\r\n /**\r\n * @private {number}\r\n */\r\n this.total_ = 0;\r\n this.blockSize = 512 / 8;\r\n this.pad_[0] = 128;\r\n for (let i = 1; i < this.blockSize; ++i) {\r\n this.pad_[i] = 0;\r\n }\r\n this.reset();\r\n }\r\n reset() {\r\n this.chain_[0] = 0x67452301;\r\n this.chain_[1] = 0xefcdab89;\r\n this.chain_[2] = 0x98badcfe;\r\n this.chain_[3] = 0x10325476;\r\n this.chain_[4] = 0xc3d2e1f0;\r\n this.inbuf_ = 0;\r\n this.total_ = 0;\r\n }\r\n /**\r\n * Internal compress helper function.\r\n * @param buf Block to compress.\r\n * @param offset Offset of the block in the buffer.\r\n * @private\r\n */\r\n compress_(buf, offset) {\r\n if (!offset) {\r\n offset = 0;\r\n }\r\n const W = this.W_;\r\n // get 16 big endian words\r\n if (typeof buf === 'string') {\r\n for (let i = 0; i < 16; i++) {\r\n // TODO(user): [bug 8140122] Recent versions of Safari for Mac OS and iOS\r\n // have a bug that turns the post-increment ++ operator into pre-increment\r\n // during JIT compilation. We have code that depends heavily on SHA-1 for\r\n // correctness and which is affected by this bug, so I've removed all uses\r\n // of post-increment ++ in which the result value is used. We can revert\r\n // this change once the Safari bug\r\n // (https://bugs.webkit.org/show_bug.cgi?id=109036) has been fixed and\r\n // most clients have been updated.\r\n W[i] =\r\n (buf.charCodeAt(offset) << 24) |\r\n (buf.charCodeAt(offset + 1) << 16) |\r\n (buf.charCodeAt(offset + 2) << 8) |\r\n buf.charCodeAt(offset + 3);\r\n offset += 4;\r\n }\r\n }\r\n else {\r\n for (let i = 0; i < 16; i++) {\r\n W[i] =\r\n (buf[offset] << 24) |\r\n (buf[offset + 1] << 16) |\r\n (buf[offset + 2] << 8) |\r\n buf[offset + 3];\r\n offset += 4;\r\n }\r\n }\r\n // expand to 80 words\r\n for (let i = 16; i < 80; i++) {\r\n const t = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];\r\n W[i] = ((t << 1) | (t >>> 31)) & 0xffffffff;\r\n }\r\n let a = this.chain_[0];\r\n let b = this.chain_[1];\r\n let c = this.chain_[2];\r\n let d = this.chain_[3];\r\n let e = this.chain_[4];\r\n let f, k;\r\n // TODO(user): Try to unroll this loop to speed up the computation.\r\n for (let i = 0; i < 80; i++) {\r\n if (i < 40) {\r\n if (i < 20) {\r\n f = d ^ (b & (c ^ d));\r\n k = 0x5a827999;\r\n }\r\n else {\r\n f = b ^ c ^ d;\r\n k = 0x6ed9eba1;\r\n }\r\n }\r\n else {\r\n if (i < 60) {\r\n f = (b & c) | (d & (b | c));\r\n k = 0x8f1bbcdc;\r\n }\r\n else {\r\n f = b ^ c ^ d;\r\n k = 0xca62c1d6;\r\n }\r\n }\r\n const t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) & 0xffffffff;\r\n e = d;\r\n d = c;\r\n c = ((b << 30) | (b >>> 2)) & 0xffffffff;\r\n b = a;\r\n a = t;\r\n }\r\n this.chain_[0] = (this.chain_[0] + a) & 0xffffffff;\r\n this.chain_[1] = (this.chain_[1] + b) & 0xffffffff;\r\n this.chain_[2] = (this.chain_[2] + c) & 0xffffffff;\r\n this.chain_[3] = (this.chain_[3] + d) & 0xffffffff;\r\n this.chain_[4] = (this.chain_[4] + e) & 0xffffffff;\r\n }\r\n update(bytes, length) {\r\n // TODO(johnlenz): tighten the function signature and remove this check\r\n if (bytes == null) {\r\n return;\r\n }\r\n if (length === undefined) {\r\n length = bytes.length;\r\n }\r\n const lengthMinusBlock = length - this.blockSize;\r\n let n = 0;\r\n // Using local instead of member variables gives ~5% speedup on Firefox 16.\r\n const buf = this.buf_;\r\n let inbuf = this.inbuf_;\r\n // The outer while loop should execute at most twice.\r\n while (n < length) {\r\n // When we have no data in the block to top up, we can directly process the\r\n // input buffer (assuming it contains sufficient data). This gives ~25%\r\n // speedup on Chrome 23 and ~15% speedup on Firefox 16, but requires that\r\n // the data is provided in large chunks (or in multiples of 64 bytes).\r\n if (inbuf === 0) {\r\n while (n <= lengthMinusBlock) {\r\n this.compress_(bytes, n);\r\n n += this.blockSize;\r\n }\r\n }\r\n if (typeof bytes === 'string') {\r\n while (n < length) {\r\n buf[inbuf] = bytes.charCodeAt(n);\r\n ++inbuf;\r\n ++n;\r\n if (inbuf === this.blockSize) {\r\n this.compress_(buf);\r\n inbuf = 0;\r\n // Jump to the outer loop so we use the full-block optimization.\r\n break;\r\n }\r\n }\r\n }\r\n else {\r\n while (n < length) {\r\n buf[inbuf] = bytes[n];\r\n ++inbuf;\r\n ++n;\r\n if (inbuf === this.blockSize) {\r\n this.compress_(buf);\r\n inbuf = 0;\r\n // Jump to the outer loop so we use the full-block optimization.\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n this.inbuf_ = inbuf;\r\n this.total_ += length;\r\n }\r\n /** @override */\r\n digest() {\r\n const digest = [];\r\n let totalBits = this.total_ * 8;\r\n // Add pad 0x80 0x00*.\r\n if (this.inbuf_ < 56) {\r\n this.update(this.pad_, 56 - this.inbuf_);\r\n }\r\n else {\r\n this.update(this.pad_, this.blockSize - (this.inbuf_ - 56));\r\n }\r\n // Add # bits.\r\n for (let i = this.blockSize - 1; i >= 56; i--) {\r\n this.buf_[i] = totalBits & 255;\r\n totalBits /= 256; // Don't use bit-shifting here!\r\n }\r\n this.compress_(this.buf_);\r\n let n = 0;\r\n for (let i = 0; i < 5; i++) {\r\n for (let j = 24; j >= 0; j -= 8) {\r\n digest[n] = (this.chain_[i] >> j) & 255;\r\n ++n;\r\n }\r\n }\r\n return digest;\r\n }\r\n}\n\n/**\r\n * Helper to make a Subscribe function (just like Promise helps make a\r\n * Thenable).\r\n *\r\n * @param executor Function which can make calls to a single Observer\r\n * as a proxy.\r\n * @param onNoObservers Callback when count of Observers goes to zero.\r\n */\r\nfunction createSubscribe(executor, onNoObservers) {\r\n const proxy = new ObserverProxy(executor, onNoObservers);\r\n return proxy.subscribe.bind(proxy);\r\n}\r\n/**\r\n * Implement fan-out for any number of Observers attached via a subscribe\r\n * function.\r\n */\r\nclass ObserverProxy {\r\n /**\r\n * @param executor Function which can make calls to a single Observer\r\n * as a proxy.\r\n * @param onNoObservers Callback when count of Observers goes to zero.\r\n */\r\n constructor(executor, onNoObservers) {\r\n this.observers = [];\r\n this.unsubscribes = [];\r\n this.observerCount = 0;\r\n // Micro-task scheduling by calling task.then().\r\n this.task = Promise.resolve();\r\n this.finalized = false;\r\n this.onNoObservers = onNoObservers;\r\n // Call the executor asynchronously so subscribers that are called\r\n // synchronously after the creation of the subscribe function\r\n // can still receive the very first value generated in the executor.\r\n this.task\r\n .then(() => {\r\n executor(this);\r\n })\r\n .catch(e => {\r\n this.error(e);\r\n });\r\n }\r\n next(value) {\r\n this.forEachObserver((observer) => {\r\n observer.next(value);\r\n });\r\n }\r\n error(error) {\r\n this.forEachObserver((observer) => {\r\n observer.error(error);\r\n });\r\n this.close(error);\r\n }\r\n complete() {\r\n this.forEachObserver((observer) => {\r\n observer.complete();\r\n });\r\n this.close();\r\n }\r\n /**\r\n * Subscribe function that can be used to add an Observer to the fan-out list.\r\n *\r\n * - We require that no event is sent to a subscriber sychronously to their\r\n * call to subscribe().\r\n */\r\n subscribe(nextOrObserver, error, complete) {\r\n let observer;\r\n if (nextOrObserver === undefined &&\r\n error === undefined &&\r\n complete === undefined) {\r\n throw new Error('Missing Observer.');\r\n }\r\n // Assemble an Observer object when passed as callback functions.\r\n if (implementsAnyMethods(nextOrObserver, [\r\n 'next',\r\n 'error',\r\n 'complete'\r\n ])) {\r\n observer = nextOrObserver;\r\n }\r\n else {\r\n observer = {\r\n next: nextOrObserver,\r\n error,\r\n complete\r\n };\r\n }\r\n if (observer.next === undefined) {\r\n observer.next = noop;\r\n }\r\n if (observer.error === undefined) {\r\n observer.error = noop;\r\n }\r\n if (observer.complete === undefined) {\r\n observer.complete = noop;\r\n }\r\n const unsub = this.unsubscribeOne.bind(this, this.observers.length);\r\n // Attempt to subscribe to a terminated Observable - we\r\n // just respond to the Observer with the final error or complete\r\n // event.\r\n if (this.finalized) {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n try {\r\n if (this.finalError) {\r\n observer.error(this.finalError);\r\n }\r\n else {\r\n observer.complete();\r\n }\r\n }\r\n catch (e) {\r\n // nothing\r\n }\r\n return;\r\n });\r\n }\r\n this.observers.push(observer);\r\n return unsub;\r\n }\r\n // Unsubscribe is synchronous - we guarantee that no events are sent to\r\n // any unsubscribed Observer.\r\n unsubscribeOne(i) {\r\n if (this.observers === undefined || this.observers[i] === undefined) {\r\n return;\r\n }\r\n delete this.observers[i];\r\n this.observerCount -= 1;\r\n if (this.observerCount === 0 && this.onNoObservers !== undefined) {\r\n this.onNoObservers(this);\r\n }\r\n }\r\n forEachObserver(fn) {\r\n if (this.finalized) {\r\n // Already closed by previous event....just eat the additional values.\r\n return;\r\n }\r\n // Since sendOne calls asynchronously - there is no chance that\r\n // this.observers will become undefined.\r\n for (let i = 0; i < this.observers.length; i++) {\r\n this.sendOne(i, fn);\r\n }\r\n }\r\n // Call the Observer via one of it's callback function. We are careful to\r\n // confirm that the observe has not been unsubscribed since this asynchronous\r\n // function had been queued.\r\n sendOne(i, fn) {\r\n // Execute the callback asynchronously\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n if (this.observers !== undefined && this.observers[i] !== undefined) {\r\n try {\r\n fn(this.observers[i]);\r\n }\r\n catch (e) {\r\n // Ignore exceptions raised in Observers or missing methods of an\r\n // Observer.\r\n // Log error to console. b/31404806\r\n if (typeof console !== 'undefined' && console.error) {\r\n console.error(e);\r\n }\r\n }\r\n }\r\n });\r\n }\r\n close(err) {\r\n if (this.finalized) {\r\n return;\r\n }\r\n this.finalized = true;\r\n if (err !== undefined) {\r\n this.finalError = err;\r\n }\r\n // Proxy is no longer needed - garbage collect references\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this.task.then(() => {\r\n this.observers = undefined;\r\n this.onNoObservers = undefined;\r\n });\r\n }\r\n}\r\n/** Turn synchronous function into one called asynchronously. */\r\n// eslint-disable-next-line @typescript-eslint/ban-types\r\nfunction async(fn, onError) {\r\n return (...args) => {\r\n Promise.resolve(true)\r\n .then(() => {\r\n fn(...args);\r\n })\r\n .catch((error) => {\r\n if (onError) {\r\n onError(error);\r\n }\r\n });\r\n };\r\n}\r\n/**\r\n * Return true if the object passed in implements any of the named methods.\r\n */\r\nfunction implementsAnyMethods(obj, methods) {\r\n if (typeof obj !== 'object' || obj === null) {\r\n return false;\r\n }\r\n for (const method of methods) {\r\n if (method in obj && typeof obj[method] === 'function') {\r\n return true;\r\n }\r\n }\r\n return false;\r\n}\r\nfunction noop() {\r\n // do nothing\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Check to make sure the appropriate number of arguments are provided for a public function.\r\n * Throws an error if it fails.\r\n *\r\n * @param fnName The function name\r\n * @param minCount The minimum number of arguments to allow for the function call\r\n * @param maxCount The maximum number of argument to allow for the function call\r\n * @param argCount The actual number of arguments provided.\r\n */\r\nconst validateArgCount = function (fnName, minCount, maxCount, argCount) {\r\n let argError;\r\n if (argCount < minCount) {\r\n argError = 'at least ' + minCount;\r\n }\r\n else if (argCount > maxCount) {\r\n argError = maxCount === 0 ? 'none' : 'no more than ' + maxCount;\r\n }\r\n if (argError) {\r\n const error = fnName +\r\n ' failed: Was called with ' +\r\n argCount +\r\n (argCount === 1 ? ' argument.' : ' arguments.') +\r\n ' Expects ' +\r\n argError +\r\n '.';\r\n throw new Error(error);\r\n }\r\n};\r\n/**\r\n * Generates a string to prefix an error message about failed argument validation\r\n *\r\n * @param fnName The function name\r\n * @param argName The name of the argument\r\n * @return The prefix to add to the error thrown for validation.\r\n */\r\nfunction errorPrefix(fnName, argName) {\r\n return `${fnName} failed: ${argName} argument `;\r\n}\r\n/**\r\n * @param fnName\r\n * @param argumentNumber\r\n * @param namespace\r\n * @param optional\r\n */\r\nfunction validateNamespace(fnName, namespace, optional) {\r\n if (optional && !namespace) {\r\n return;\r\n }\r\n if (typeof namespace !== 'string') {\r\n //TODO: I should do more validation here. We only allow certain chars in namespaces.\r\n throw new Error(errorPrefix(fnName, 'namespace') + 'must be a valid firebase namespace.');\r\n }\r\n}\r\nfunction validateCallback(fnName, argumentName, \r\n// eslint-disable-next-line @typescript-eslint/ban-types\r\ncallback, optional) {\r\n if (optional && !callback) {\r\n return;\r\n }\r\n if (typeof callback !== 'function') {\r\n throw new Error(errorPrefix(fnName, argumentName) + 'must be a valid function.');\r\n }\r\n}\r\nfunction validateContextObject(fnName, argumentName, context, optional) {\r\n if (optional && !context) {\r\n return;\r\n }\r\n if (typeof context !== 'object' || context === null) {\r\n throw new Error(errorPrefix(fnName, argumentName) + 'must be a valid context object.');\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// Code originally came from goog.crypt.stringToUtf8ByteArray, but for some reason they\r\n// automatically replaced '\\r\\n' with '\\n', and they didn't handle surrogate pairs,\r\n// so it's been modified.\r\n// Note that not all Unicode characters appear as single characters in JavaScript strings.\r\n// fromCharCode returns the UTF-16 encoding of a character - so some Unicode characters\r\n// use 2 characters in Javascript. All 4-byte UTF-8 characters begin with a first\r\n// character in the range 0xD800 - 0xDBFF (the first character of a so-called surrogate\r\n// pair).\r\n// See http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3\r\n/**\r\n * @param {string} str\r\n * @return {Array}\r\n */\r\nconst stringToByteArray = function (str) {\r\n const out = [];\r\n let p = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n let c = str.charCodeAt(i);\r\n // Is this the lead surrogate in a surrogate pair?\r\n if (c >= 0xd800 && c <= 0xdbff) {\r\n const high = c - 0xd800; // the high 10 bits.\r\n i++;\r\n assert(i < str.length, 'Surrogate pair missing trail surrogate.');\r\n const low = str.charCodeAt(i) - 0xdc00; // the low 10 bits.\r\n c = 0x10000 + (high << 10) + low;\r\n }\r\n if (c < 128) {\r\n out[p++] = c;\r\n }\r\n else if (c < 2048) {\r\n out[p++] = (c >> 6) | 192;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else if (c < 65536) {\r\n out[p++] = (c >> 12) | 224;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n else {\r\n out[p++] = (c >> 18) | 240;\r\n out[p++] = ((c >> 12) & 63) | 128;\r\n out[p++] = ((c >> 6) & 63) | 128;\r\n out[p++] = (c & 63) | 128;\r\n }\r\n }\r\n return out;\r\n};\r\n/**\r\n * Calculate length without actually converting; useful for doing cheaper validation.\r\n * @param {string} str\r\n * @return {number}\r\n */\r\nconst stringLength = function (str) {\r\n let p = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const c = str.charCodeAt(i);\r\n if (c < 128) {\r\n p++;\r\n }\r\n else if (c < 2048) {\r\n p += 2;\r\n }\r\n else if (c >= 0xd800 && c <= 0xdbff) {\r\n // Lead surrogate of a surrogate pair. The pair together will take 4 bytes to represent.\r\n p += 4;\r\n i++; // skip trail surrogate.\r\n }\r\n else {\r\n p += 3;\r\n }\r\n }\r\n return p;\r\n};\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The amount of milliseconds to exponentially increase.\r\n */\r\nconst DEFAULT_INTERVAL_MILLIS = 1000;\r\n/**\r\n * The factor to backoff by.\r\n * Should be a number greater than 1.\r\n */\r\nconst DEFAULT_BACKOFF_FACTOR = 2;\r\n/**\r\n * The maximum milliseconds to increase to.\r\n *\r\n * Visible for testing\r\n */\r\nconst MAX_VALUE_MILLIS = 4 * 60 * 60 * 1000; // Four hours, like iOS and Android.\r\n/**\r\n * The percentage of backoff time to randomize by.\r\n * See\r\n * http://go/safe-client-behavior#step-1-determine-the-appropriate-retry-interval-to-handle-spike-traffic\r\n * for context.\r\n *\r\n *
Visible for testing\r\n */\r\nconst RANDOM_FACTOR = 0.5;\r\n/**\r\n * Based on the backoff method from\r\n * https://github.com/google/closure-library/blob/master/closure/goog/math/exponentialbackoff.js.\r\n * Extracted here so we don't need to pass metadata and a stateful ExponentialBackoff object around.\r\n */\r\nfunction calculateBackoffMillis(backoffCount, intervalMillis = DEFAULT_INTERVAL_MILLIS, backoffFactor = DEFAULT_BACKOFF_FACTOR) {\r\n // Calculates an exponentially increasing value.\r\n // Deviation: calculates value from count and a constant interval, so we only need to save value\r\n // and count to restore state.\r\n const currBaseValue = intervalMillis * Math.pow(backoffFactor, backoffCount);\r\n // A random \"fuzz\" to avoid waves of retries.\r\n // Deviation: randomFactor is required.\r\n const randomWait = Math.round(\r\n // A fraction of the backoff value to add/subtract.\r\n // Deviation: changes multiplication order to improve readability.\r\n RANDOM_FACTOR *\r\n currBaseValue *\r\n // A random float (rounded to int by Math.round above) in the range [-1, 1]. Determines\r\n // if we add or subtract.\r\n (Math.random() - 0.5) *\r\n 2);\r\n // Limits backoff to max to avoid effectively permanent backoff.\r\n return Math.min(MAX_VALUE_MILLIS, currBaseValue + randomWait);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Provide English ordinal letters after a number\r\n */\r\nfunction ordinal(i) {\r\n if (!Number.isFinite(i)) {\r\n return `${i}`;\r\n }\r\n return i + indicator(i);\r\n}\r\nfunction indicator(i) {\r\n i = Math.abs(i);\r\n const cent = i % 100;\r\n if (cent >= 10 && cent <= 20) {\r\n return 'th';\r\n }\r\n const dec = i % 10;\r\n if (dec === 1) {\r\n return 'st';\r\n }\r\n if (dec === 2) {\r\n return 'nd';\r\n }\r\n if (dec === 3) {\r\n return 'rd';\r\n }\r\n return 'th';\r\n}\n\n/**\r\n * @license\r\n * Copyright 2021 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction getModularInstance(service) {\r\n if (service && service._delegate) {\r\n return service._delegate;\r\n }\r\n else {\r\n return service;\r\n }\r\n}\n\nexport { CONSTANTS, Deferred, ErrorFactory, FirebaseError, MAX_VALUE_MILLIS, RANDOM_FACTOR, Sha1, areCookiesEnabled, assert, assertionError, async, base64, base64Decode, base64Encode, base64urlEncodeWithoutPadding, calculateBackoffMillis, contains, createMockUserToken, createSubscribe, decode, deepCopy, deepEqual, deepExtend, errorPrefix, extractQuerystring, getGlobal, getModularInstance, getUA, isAdmin, isBrowser, isBrowserExtension, isElectron, isEmpty, isIE, isIndexedDBAvailable, isMobileCordova, isNode, isNodeSdk, isReactNative, isSafari, isUWP, isValidFormat, isValidTimestamp, issuedAtTime, jsonEval, map, ordinal, querystring, querystringDecode, safeGet, stringLength, stringToByteArray, stringify, validateArgCount, validateCallback, validateContextObject, validateIndexedDBOpenable, validateNamespace };\n//# sourceMappingURL=index.esm2017.js.map\n","import { Deferred } from '@firebase/util';\n\n/**\r\n * Component for service name T, e.g. `auth`, `auth-internal`\r\n */\r\nclass Component {\r\n /**\r\n *\r\n * @param name The public service name, e.g. app, auth, firestore, database\r\n * @param instanceFactory Service factory responsible for creating the public interface\r\n * @param type whether the service provided by the component is public or private\r\n */\r\n constructor(name, instanceFactory, type) {\r\n this.name = name;\r\n this.instanceFactory = instanceFactory;\r\n this.type = type;\r\n this.multipleInstances = false;\r\n /**\r\n * Properties to be added to the service namespace\r\n */\r\n this.serviceProps = {};\r\n this.instantiationMode = \"LAZY\" /* LAZY */;\r\n this.onInstanceCreated = null;\r\n }\r\n setInstantiationMode(mode) {\r\n this.instantiationMode = mode;\r\n return this;\r\n }\r\n setMultipleInstances(multipleInstances) {\r\n this.multipleInstances = multipleInstances;\r\n return this;\r\n }\r\n setServiceProps(props) {\r\n this.serviceProps = props;\r\n return this;\r\n }\r\n setInstanceCreatedCallback(callback) {\r\n this.onInstanceCreated = callback;\r\n return this;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DEFAULT_ENTRY_NAME = '[DEFAULT]';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Provider for instance for service name T, e.g. 'auth', 'auth-internal'\r\n * NameServiceMapping[T] is an alias for the type of the instance\r\n */\r\nclass Provider {\r\n constructor(name, container) {\r\n this.name = name;\r\n this.container = container;\r\n this.component = null;\r\n this.instances = new Map();\r\n this.instancesDeferred = new Map();\r\n this.instancesOptions = new Map();\r\n this.onInitCallbacks = new Map();\r\n }\r\n /**\r\n * @param identifier A provider can provide mulitple instances of a service\r\n * if this.component.multipleInstances is true.\r\n */\r\n get(identifier) {\r\n // if multipleInstances is not supported, use the default name\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);\r\n if (!this.instancesDeferred.has(normalizedIdentifier)) {\r\n const deferred = new Deferred();\r\n this.instancesDeferred.set(normalizedIdentifier, deferred);\r\n if (this.isInitialized(normalizedIdentifier) ||\r\n this.shouldAutoInitialize()) {\r\n // initialize the service if it can be auto-initialized\r\n try {\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n if (instance) {\r\n deferred.resolve(instance);\r\n }\r\n }\r\n catch (e) {\r\n // when the instance factory throws an exception during get(), it should not cause\r\n // a fatal error. We just return the unresolved promise in this case.\r\n }\r\n }\r\n }\r\n return this.instancesDeferred.get(normalizedIdentifier).promise;\r\n }\r\n getImmediate(options) {\r\n var _a;\r\n // if multipleInstances is not supported, use the default name\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(options === null || options === void 0 ? void 0 : options.identifier);\r\n const optional = (_a = options === null || options === void 0 ? void 0 : options.optional) !== null && _a !== void 0 ? _a : false;\r\n if (this.isInitialized(normalizedIdentifier) ||\r\n this.shouldAutoInitialize()) {\r\n try {\r\n return this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n }\r\n catch (e) {\r\n if (optional) {\r\n return null;\r\n }\r\n else {\r\n throw e;\r\n }\r\n }\r\n }\r\n else {\r\n // In case a component is not initialized and should/can not be auto-initialized at the moment, return null if the optional flag is set, or throw\r\n if (optional) {\r\n return null;\r\n }\r\n else {\r\n throw Error(`Service ${this.name} is not available`);\r\n }\r\n }\r\n }\r\n getComponent() {\r\n return this.component;\r\n }\r\n setComponent(component) {\r\n if (component.name !== this.name) {\r\n throw Error(`Mismatching Component ${component.name} for Provider ${this.name}.`);\r\n }\r\n if (this.component) {\r\n throw Error(`Component for ${this.name} has already been provided`);\r\n }\r\n this.component = component;\r\n // return early without attempting to initialize the component if the component requires explicit initialization (calling `Provider.initialize()`)\r\n if (!this.shouldAutoInitialize()) {\r\n return;\r\n }\r\n // if the service is eager, initialize the default instance\r\n if (isComponentEager(component)) {\r\n try {\r\n this.getOrInitializeService({ instanceIdentifier: DEFAULT_ENTRY_NAME });\r\n }\r\n catch (e) {\r\n // when the instance factory for an eager Component throws an exception during the eager\r\n // initialization, it should not cause a fatal error.\r\n // TODO: Investigate if we need to make it configurable, because some component may want to cause\r\n // a fatal error in this case?\r\n }\r\n }\r\n // Create service instances for the pending promises and resolve them\r\n // NOTE: if this.multipleInstances is false, only the default instance will be created\r\n // and all promises with resolve with it regardless of the identifier.\r\n for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) {\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier);\r\n try {\r\n // `getOrInitializeService()` should always return a valid instance since a component is guaranteed. use ! to make typescript happy.\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier\r\n });\r\n instanceDeferred.resolve(instance);\r\n }\r\n catch (e) {\r\n // when the instance factory throws an exception, it should not cause\r\n // a fatal error. We just leave the promise unresolved.\r\n }\r\n }\r\n }\r\n clearInstance(identifier = DEFAULT_ENTRY_NAME) {\r\n this.instancesDeferred.delete(identifier);\r\n this.instancesOptions.delete(identifier);\r\n this.instances.delete(identifier);\r\n }\r\n // app.delete() will call this method on every provider to delete the services\r\n // TODO: should we mark the provider as deleted?\r\n async delete() {\r\n const services = Array.from(this.instances.values());\r\n await Promise.all([\r\n ...services\r\n .filter(service => 'INTERNAL' in service) // legacy services\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n .map(service => service.INTERNAL.delete()),\r\n ...services\r\n .filter(service => '_delete' in service) // modularized services\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n .map(service => service._delete())\r\n ]);\r\n }\r\n isComponentSet() {\r\n return this.component != null;\r\n }\r\n isInitialized(identifier = DEFAULT_ENTRY_NAME) {\r\n return this.instances.has(identifier);\r\n }\r\n getOptions(identifier = DEFAULT_ENTRY_NAME) {\r\n return this.instancesOptions.get(identifier) || {};\r\n }\r\n initialize(opts = {}) {\r\n const { options = {} } = opts;\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(opts.instanceIdentifier);\r\n if (this.isInitialized(normalizedIdentifier)) {\r\n throw Error(`${this.name}(${normalizedIdentifier}) has already been initialized`);\r\n }\r\n if (!this.isComponentSet()) {\r\n throw Error(`Component ${this.name} has not been registered yet`);\r\n }\r\n const instance = this.getOrInitializeService({\r\n instanceIdentifier: normalizedIdentifier,\r\n options\r\n });\r\n // resolve any pending promise waiting for the service instance\r\n for (const [instanceIdentifier, instanceDeferred] of this.instancesDeferred.entries()) {\r\n const normalizedDeferredIdentifier = this.normalizeInstanceIdentifier(instanceIdentifier);\r\n if (normalizedIdentifier === normalizedDeferredIdentifier) {\r\n instanceDeferred.resolve(instance);\r\n }\r\n }\r\n return instance;\r\n }\r\n /**\r\n *\r\n * @param callback - a function that will be invoked after the provider has been initialized by calling provider.initialize().\r\n * The function is invoked SYNCHRONOUSLY, so it should not execute any longrunning tasks in order to not block the program.\r\n *\r\n * @param identifier An optional instance identifier\r\n * @returns a function to unregister the callback\r\n */\r\n onInit(callback, identifier) {\r\n var _a;\r\n const normalizedIdentifier = this.normalizeInstanceIdentifier(identifier);\r\n const existingCallbacks = (_a = this.onInitCallbacks.get(normalizedIdentifier)) !== null && _a !== void 0 ? _a : new Set();\r\n existingCallbacks.add(callback);\r\n this.onInitCallbacks.set(normalizedIdentifier, existingCallbacks);\r\n const existingInstance = this.instances.get(normalizedIdentifier);\r\n if (existingInstance) {\r\n callback(existingInstance, normalizedIdentifier);\r\n }\r\n return () => {\r\n existingCallbacks.delete(callback);\r\n };\r\n }\r\n /**\r\n * Invoke onInit callbacks synchronously\r\n * @param instance the service instance`\r\n */\r\n invokeOnInitCallbacks(instance, identifier) {\r\n const callbacks = this.onInitCallbacks.get(identifier);\r\n if (!callbacks) {\r\n return;\r\n }\r\n for (const callback of callbacks) {\r\n try {\r\n callback(instance, identifier);\r\n }\r\n catch (_a) {\r\n // ignore errors in the onInit callback\r\n }\r\n }\r\n }\r\n getOrInitializeService({ instanceIdentifier, options = {} }) {\r\n let instance = this.instances.get(instanceIdentifier);\r\n if (!instance && this.component) {\r\n instance = this.component.instanceFactory(this.container, {\r\n instanceIdentifier: normalizeIdentifierForFactory(instanceIdentifier),\r\n options\r\n });\r\n this.instances.set(instanceIdentifier, instance);\r\n this.instancesOptions.set(instanceIdentifier, options);\r\n /**\r\n * Invoke onInit listeners.\r\n * Note this.component.onInstanceCreated is different, which is used by the component creator,\r\n * while onInit listeners are registered by consumers of the provider.\r\n */\r\n this.invokeOnInitCallbacks(instance, instanceIdentifier);\r\n /**\r\n * Order is important\r\n * onInstanceCreated() should be called after this.instances.set(instanceIdentifier, instance); which\r\n * makes `isInitialized()` return true.\r\n */\r\n if (this.component.onInstanceCreated) {\r\n try {\r\n this.component.onInstanceCreated(this.container, instanceIdentifier, instance);\r\n }\r\n catch (_a) {\r\n // ignore errors in the onInstanceCreatedCallback\r\n }\r\n }\r\n }\r\n return instance || null;\r\n }\r\n normalizeInstanceIdentifier(identifier = DEFAULT_ENTRY_NAME) {\r\n if (this.component) {\r\n return this.component.multipleInstances ? identifier : DEFAULT_ENTRY_NAME;\r\n }\r\n else {\r\n return identifier; // assume multiple instances are supported before the component is provided.\r\n }\r\n }\r\n shouldAutoInitialize() {\r\n return (!!this.component &&\r\n this.component.instantiationMode !== \"EXPLICIT\" /* EXPLICIT */);\r\n }\r\n}\r\n// undefined should be passed to the service factory for the default instance\r\nfunction normalizeIdentifierForFactory(identifier) {\r\n return identifier === DEFAULT_ENTRY_NAME ? undefined : identifier;\r\n}\r\nfunction isComponentEager(component) {\r\n return component.instantiationMode === \"EAGER\" /* EAGER */;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * ComponentContainer that provides Providers for service name T, e.g. `auth`, `auth-internal`\r\n */\r\nclass ComponentContainer {\r\n constructor(name) {\r\n this.name = name;\r\n this.providers = new Map();\r\n }\r\n /**\r\n *\r\n * @param component Component being added\r\n * @param overwrite When a component with the same name has already been registered,\r\n * if overwrite is true: overwrite the existing component with the new component and create a new\r\n * provider with the new component. It can be useful in tests where you want to use different mocks\r\n * for different tests.\r\n * if overwrite is false: throw an exception\r\n */\r\n addComponent(component) {\r\n const provider = this.getProvider(component.name);\r\n if (provider.isComponentSet()) {\r\n throw new Error(`Component ${component.name} has already been registered with ${this.name}`);\r\n }\r\n provider.setComponent(component);\r\n }\r\n addOrOverwriteComponent(component) {\r\n const provider = this.getProvider(component.name);\r\n if (provider.isComponentSet()) {\r\n // delete the existing provider from the container, so we can register the new component\r\n this.providers.delete(component.name);\r\n }\r\n this.addComponent(component);\r\n }\r\n /**\r\n * getProvider provides a type safe interface where it can only be called with a field name\r\n * present in NameServiceMapping interface.\r\n *\r\n * Firebase SDKs providing services should extend NameServiceMapping interface to register\r\n * themselves.\r\n */\r\n getProvider(name) {\r\n if (this.providers.has(name)) {\r\n return this.providers.get(name);\r\n }\r\n // create a Provider for a service that hasn't registered with Firebase\r\n const provider = new Provider(name, this);\r\n this.providers.set(name, provider);\r\n return provider;\r\n }\r\n getProviders() {\r\n return Array.from(this.providers.values());\r\n }\r\n}\n\nexport { Component, ComponentContainer, Provider };\n//# sourceMappingURL=index.esm2017.js.map\n","/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * A container for all of the Logger instances\r\n */\r\nconst instances = [];\r\n/**\r\n * The JS SDK supports 5 log levels and also allows a user the ability to\r\n * silence the logs altogether.\r\n *\r\n * The order is a follows:\r\n * DEBUG < VERBOSE < INFO < WARN < ERROR\r\n *\r\n * All of the log types above the current log level will be captured (i.e. if\r\n * you set the log level to `INFO`, errors will still be logged, but `DEBUG` and\r\n * `VERBOSE` logs will not)\r\n */\r\nvar LogLevel;\r\n(function (LogLevel) {\r\n LogLevel[LogLevel[\"DEBUG\"] = 0] = \"DEBUG\";\r\n LogLevel[LogLevel[\"VERBOSE\"] = 1] = \"VERBOSE\";\r\n LogLevel[LogLevel[\"INFO\"] = 2] = \"INFO\";\r\n LogLevel[LogLevel[\"WARN\"] = 3] = \"WARN\";\r\n LogLevel[LogLevel[\"ERROR\"] = 4] = \"ERROR\";\r\n LogLevel[LogLevel[\"SILENT\"] = 5] = \"SILENT\";\r\n})(LogLevel || (LogLevel = {}));\r\nconst levelStringToEnum = {\r\n 'debug': LogLevel.DEBUG,\r\n 'verbose': LogLevel.VERBOSE,\r\n 'info': LogLevel.INFO,\r\n 'warn': LogLevel.WARN,\r\n 'error': LogLevel.ERROR,\r\n 'silent': LogLevel.SILENT\r\n};\r\n/**\r\n * The default log level\r\n */\r\nconst defaultLogLevel = LogLevel.INFO;\r\n/**\r\n * By default, `console.debug` is not displayed in the developer console (in\r\n * chrome). To avoid forcing users to have to opt-in to these logs twice\r\n * (i.e. once for firebase, and once in the console), we are sending `DEBUG`\r\n * logs to the `console.log` function.\r\n */\r\nconst ConsoleMethod = {\r\n [LogLevel.DEBUG]: 'log',\r\n [LogLevel.VERBOSE]: 'log',\r\n [LogLevel.INFO]: 'info',\r\n [LogLevel.WARN]: 'warn',\r\n [LogLevel.ERROR]: 'error'\r\n};\r\n/**\r\n * The default log handler will forward DEBUG, VERBOSE, INFO, WARN, and ERROR\r\n * messages on to their corresponding console counterparts (if the log method\r\n * is supported by the current log level)\r\n */\r\nconst defaultLogHandler = (instance, logType, ...args) => {\r\n if (logType < instance.logLevel) {\r\n return;\r\n }\r\n const now = new Date().toISOString();\r\n const method = ConsoleMethod[logType];\r\n if (method) {\r\n console[method](`[${now}] ${instance.name}:`, ...args);\r\n }\r\n else {\r\n throw new Error(`Attempted to log a message with an invalid logType (value: ${logType})`);\r\n }\r\n};\r\nclass Logger {\r\n /**\r\n * Gives you an instance of a Logger to capture messages according to\r\n * Firebase's logging scheme.\r\n *\r\n * @param name The name that the logs will be associated with\r\n */\r\n constructor(name) {\r\n this.name = name;\r\n /**\r\n * The log level of the given Logger instance.\r\n */\r\n this._logLevel = defaultLogLevel;\r\n /**\r\n * The main (internal) log handler for the Logger instance.\r\n * Can be set to a new function in internal package code but not by user.\r\n */\r\n this._logHandler = defaultLogHandler;\r\n /**\r\n * The optional, additional, user-defined log handler for the Logger instance.\r\n */\r\n this._userLogHandler = null;\r\n /**\r\n * Capture the current instance for later use\r\n */\r\n instances.push(this);\r\n }\r\n get logLevel() {\r\n return this._logLevel;\r\n }\r\n set logLevel(val) {\r\n if (!(val in LogLevel)) {\r\n throw new TypeError(`Invalid value \"${val}\" assigned to \\`logLevel\\``);\r\n }\r\n this._logLevel = val;\r\n }\r\n // Workaround for setter/getter having to be the same type.\r\n setLogLevel(val) {\r\n this._logLevel = typeof val === 'string' ? levelStringToEnum[val] : val;\r\n }\r\n get logHandler() {\r\n return this._logHandler;\r\n }\r\n set logHandler(val) {\r\n if (typeof val !== 'function') {\r\n throw new TypeError('Value assigned to `logHandler` must be a function');\r\n }\r\n this._logHandler = val;\r\n }\r\n get userLogHandler() {\r\n return this._userLogHandler;\r\n }\r\n set userLogHandler(val) {\r\n this._userLogHandler = val;\r\n }\r\n /**\r\n * The functions below are all based on the `console` interface\r\n */\r\n debug(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.DEBUG, ...args);\r\n this._logHandler(this, LogLevel.DEBUG, ...args);\r\n }\r\n log(...args) {\r\n this._userLogHandler &&\r\n this._userLogHandler(this, LogLevel.VERBOSE, ...args);\r\n this._logHandler(this, LogLevel.VERBOSE, ...args);\r\n }\r\n info(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.INFO, ...args);\r\n this._logHandler(this, LogLevel.INFO, ...args);\r\n }\r\n warn(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.WARN, ...args);\r\n this._logHandler(this, LogLevel.WARN, ...args);\r\n }\r\n error(...args) {\r\n this._userLogHandler && this._userLogHandler(this, LogLevel.ERROR, ...args);\r\n this._logHandler(this, LogLevel.ERROR, ...args);\r\n }\r\n}\r\nfunction setLogLevel(level) {\r\n instances.forEach(inst => {\r\n inst.setLogLevel(level);\r\n });\r\n}\r\nfunction setUserLogHandler(logCallback, options) {\r\n for (const instance of instances) {\r\n let customLogLevel = null;\r\n if (options && options.level) {\r\n customLogLevel = levelStringToEnum[options.level];\r\n }\r\n if (logCallback === null) {\r\n instance.userLogHandler = null;\r\n }\r\n else {\r\n instance.userLogHandler = (instance, level, ...args) => {\r\n const message = args\r\n .map(arg => {\r\n if (arg == null) {\r\n return null;\r\n }\r\n else if (typeof arg === 'string') {\r\n return arg;\r\n }\r\n else if (typeof arg === 'number' || typeof arg === 'boolean') {\r\n return arg.toString();\r\n }\r\n else if (arg instanceof Error) {\r\n return arg.message;\r\n }\r\n else {\r\n try {\r\n return JSON.stringify(arg);\r\n }\r\n catch (ignored) {\r\n return null;\r\n }\r\n }\r\n })\r\n .filter(arg => arg)\r\n .join(' ');\r\n if (level >= (customLogLevel !== null && customLogLevel !== void 0 ? customLogLevel : instance.logLevel)) {\r\n logCallback({\r\n level: LogLevel[level].toLowerCase(),\r\n message,\r\n args,\r\n type: instance.name\r\n });\r\n }\r\n };\r\n }\r\n }\r\n}\n\nexport { LogLevel, Logger, setLogLevel, setUserLogHandler };\n//# sourceMappingURL=index.esm2017.js.map\n","import { Component, ComponentContainer } from '@firebase/component';\nimport { Logger, setUserLogHandler, setLogLevel as setLogLevel$1 } from '@firebase/logger';\nimport { ErrorFactory, deepEqual } from '@firebase/util';\nexport { FirebaseError } from '@firebase/util';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass PlatformLoggerServiceImpl {\r\n constructor(container) {\r\n this.container = container;\r\n }\r\n // In initial implementation, this will be called by installations on\r\n // auth token refresh, and installations will send this string.\r\n getPlatformInfoString() {\r\n const providers = this.container.getProviders();\r\n // Loop through providers and get library/version pairs from any that are\r\n // version components.\r\n return providers\r\n .map(provider => {\r\n if (isVersionServiceProvider(provider)) {\r\n const service = provider.getImmediate();\r\n return `${service.library}/${service.version}`;\r\n }\r\n else {\r\n return null;\r\n }\r\n })\r\n .filter(logString => logString)\r\n .join(' ');\r\n }\r\n}\r\n/**\r\n *\r\n * @param provider check if this provider provides a VersionService\r\n *\r\n * NOTE: Using Provider<'app-version'> is a hack to indicate that the provider\r\n * provides VersionService. The provider is not necessarily a 'app-version'\r\n * provider.\r\n */\r\nfunction isVersionServiceProvider(provider) {\r\n const component = provider.getComponent();\r\n return (component === null || component === void 0 ? void 0 : component.type) === \"VERSION\" /* VERSION */;\r\n}\n\nconst name$o = \"@firebase/app\";\nconst version$1 = \"0.7.17\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst logger = new Logger('@firebase/app');\n\nconst name$n = \"@firebase/app-compat\";\n\nconst name$m = \"@firebase/analytics-compat\";\n\nconst name$l = \"@firebase/analytics\";\n\nconst name$k = \"@firebase/app-check-compat\";\n\nconst name$j = \"@firebase/app-check\";\n\nconst name$i = \"@firebase/auth\";\n\nconst name$h = \"@firebase/auth-compat\";\n\nconst name$g = \"@firebase/database\";\n\nconst name$f = \"@firebase/database-compat\";\n\nconst name$e = \"@firebase/functions\";\n\nconst name$d = \"@firebase/functions-compat\";\n\nconst name$c = \"@firebase/installations\";\n\nconst name$b = \"@firebase/installations-compat\";\n\nconst name$a = \"@firebase/messaging\";\n\nconst name$9 = \"@firebase/messaging-compat\";\n\nconst name$8 = \"@firebase/performance\";\n\nconst name$7 = \"@firebase/performance-compat\";\n\nconst name$6 = \"@firebase/remote-config\";\n\nconst name$5 = \"@firebase/remote-config-compat\";\n\nconst name$4 = \"@firebase/storage\";\n\nconst name$3 = \"@firebase/storage-compat\";\n\nconst name$2 = \"@firebase/firestore\";\n\nconst name$1 = \"@firebase/firestore-compat\";\n\nconst name = \"firebase\";\nconst version = \"9.6.7\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The default app name\r\n *\r\n * @internal\r\n */\r\nconst DEFAULT_ENTRY_NAME = '[DEFAULT]';\r\nconst PLATFORM_LOG_STRING = {\r\n [name$o]: 'fire-core',\r\n [name$n]: 'fire-core-compat',\r\n [name$l]: 'fire-analytics',\r\n [name$m]: 'fire-analytics-compat',\r\n [name$j]: 'fire-app-check',\r\n [name$k]: 'fire-app-check-compat',\r\n [name$i]: 'fire-auth',\r\n [name$h]: 'fire-auth-compat',\r\n [name$g]: 'fire-rtdb',\r\n [name$f]: 'fire-rtdb-compat',\r\n [name$e]: 'fire-fn',\r\n [name$d]: 'fire-fn-compat',\r\n [name$c]: 'fire-iid',\r\n [name$b]: 'fire-iid-compat',\r\n [name$a]: 'fire-fcm',\r\n [name$9]: 'fire-fcm-compat',\r\n [name$8]: 'fire-perf',\r\n [name$7]: 'fire-perf-compat',\r\n [name$6]: 'fire-rc',\r\n [name$5]: 'fire-rc-compat',\r\n [name$4]: 'fire-gcs',\r\n [name$3]: 'fire-gcs-compat',\r\n [name$2]: 'fire-fst',\r\n [name$1]: 'fire-fst-compat',\r\n 'fire-js': 'fire-js',\r\n [name]: 'fire-js-all'\r\n};\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * @internal\r\n */\r\nconst _apps = new Map();\r\n/**\r\n * Registered components.\r\n *\r\n * @internal\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nconst _components = new Map();\r\n/**\r\n * @param component - the component being added to this app's container\r\n *\r\n * @internal\r\n */\r\nfunction _addComponent(app, component) {\r\n try {\r\n app.container.addComponent(component);\r\n }\r\n catch (e) {\r\n logger.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e);\r\n }\r\n}\r\n/**\r\n *\r\n * @internal\r\n */\r\nfunction _addOrOverwriteComponent(app, component) {\r\n app.container.addOrOverwriteComponent(component);\r\n}\r\n/**\r\n *\r\n * @param component - the component to register\r\n * @returns whether or not the component is registered successfully\r\n *\r\n * @internal\r\n */\r\nfunction _registerComponent(component) {\r\n const componentName = component.name;\r\n if (_components.has(componentName)) {\r\n logger.debug(`There were multiple attempts to register component ${componentName}.`);\r\n return false;\r\n }\r\n _components.set(componentName, component);\r\n // add the component to existing app instances\r\n for (const app of _apps.values()) {\r\n _addComponent(app, component);\r\n }\r\n return true;\r\n}\r\n/**\r\n *\r\n * @param app - FirebaseApp instance\r\n * @param name - service name\r\n *\r\n * @returns the provider for the service with the matching name\r\n *\r\n * @internal\r\n */\r\nfunction _getProvider(app, name) {\r\n return app.container.getProvider(name);\r\n}\r\n/**\r\n *\r\n * @param app - FirebaseApp instance\r\n * @param name - service name\r\n * @param instanceIdentifier - service instance identifier in case the service supports multiple instances\r\n *\r\n * @internal\r\n */\r\nfunction _removeServiceInstance(app, name, instanceIdentifier = DEFAULT_ENTRY_NAME) {\r\n _getProvider(app, name).clearInstance(instanceIdentifier);\r\n}\r\n/**\r\n * Test only\r\n *\r\n * @internal\r\n */\r\nfunction _clearComponents() {\r\n _components.clear();\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERRORS = {\r\n [\"no-app\" /* NO_APP */]: \"No Firebase App '{$appName}' has been created - \" +\r\n 'call Firebase App.initializeApp()',\r\n [\"bad-app-name\" /* BAD_APP_NAME */]: \"Illegal App name: '{$appName}\",\r\n [\"duplicate-app\" /* DUPLICATE_APP */]: \"Firebase App named '{$appName}' already exists with different options or config\",\r\n [\"app-deleted\" /* APP_DELETED */]: \"Firebase App named '{$appName}' already deleted\",\r\n [\"invalid-app-argument\" /* INVALID_APP_ARGUMENT */]: 'firebase.{$appName}() takes either no argument or a ' +\r\n 'Firebase App instance.',\r\n [\"invalid-log-argument\" /* INVALID_LOG_ARGUMENT */]: 'First argument to `onLog` must be null or a function.'\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory('app', 'Firebase', ERRORS);\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass FirebaseAppImpl {\r\n constructor(options, config, container) {\r\n this._isDeleted = false;\r\n this._options = Object.assign({}, options);\r\n this._config = Object.assign({}, config);\r\n this._name = config.name;\r\n this._automaticDataCollectionEnabled =\r\n config.automaticDataCollectionEnabled;\r\n this._container = container;\r\n this.container.addComponent(new Component('app', () => this, \"PUBLIC\" /* PUBLIC */));\r\n }\r\n get automaticDataCollectionEnabled() {\r\n this.checkDestroyed();\r\n return this._automaticDataCollectionEnabled;\r\n }\r\n set automaticDataCollectionEnabled(val) {\r\n this.checkDestroyed();\r\n this._automaticDataCollectionEnabled = val;\r\n }\r\n get name() {\r\n this.checkDestroyed();\r\n return this._name;\r\n }\r\n get options() {\r\n this.checkDestroyed();\r\n return this._options;\r\n }\r\n get config() {\r\n this.checkDestroyed();\r\n return this._config;\r\n }\r\n get container() {\r\n return this._container;\r\n }\r\n get isDeleted() {\r\n return this._isDeleted;\r\n }\r\n set isDeleted(val) {\r\n this._isDeleted = val;\r\n }\r\n /**\r\n * This function will throw an Error if the App has already been deleted -\r\n * use before performing API actions on the App.\r\n */\r\n checkDestroyed() {\r\n if (this.isDeleted) {\r\n throw ERROR_FACTORY.create(\"app-deleted\" /* APP_DELETED */, { appName: this._name });\r\n }\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * The current SDK version.\r\n *\r\n * @public\r\n */\r\nconst SDK_VERSION = version;\r\nfunction initializeApp(options, rawConfig = {}) {\r\n if (typeof rawConfig !== 'object') {\r\n const name = rawConfig;\r\n rawConfig = { name };\r\n }\r\n const config = Object.assign({ name: DEFAULT_ENTRY_NAME, automaticDataCollectionEnabled: false }, rawConfig);\r\n const name = config.name;\r\n if (typeof name !== 'string' || !name) {\r\n throw ERROR_FACTORY.create(\"bad-app-name\" /* BAD_APP_NAME */, {\r\n appName: String(name)\r\n });\r\n }\r\n const existingApp = _apps.get(name);\r\n if (existingApp) {\r\n // return the existing app if options and config deep equal the ones in the existing app.\r\n if (deepEqual(options, existingApp.options) &&\r\n deepEqual(config, existingApp.config)) {\r\n return existingApp;\r\n }\r\n else {\r\n throw ERROR_FACTORY.create(\"duplicate-app\" /* DUPLICATE_APP */, { appName: name });\r\n }\r\n }\r\n const container = new ComponentContainer(name);\r\n for (const component of _components.values()) {\r\n container.addComponent(component);\r\n }\r\n const newApp = new FirebaseAppImpl(options, config, container);\r\n _apps.set(name, newApp);\r\n return newApp;\r\n}\r\n/**\r\n * Retrieves a {@link @firebase/app#FirebaseApp} instance.\r\n *\r\n * When called with no arguments, the default app is returned. When an app name\r\n * is provided, the app corresponding to that name is returned.\r\n *\r\n * An exception is thrown if the app being retrieved has not yet been\r\n * initialized.\r\n *\r\n * @example\r\n * ```javascript\r\n * // Return the default app\r\n * const app = getApp();\r\n * ```\r\n *\r\n * @example\r\n * ```javascript\r\n * // Return a named app\r\n * const otherApp = getApp(\"otherApp\");\r\n * ```\r\n *\r\n * @param name - Optional name of the app to return. If no name is\r\n * provided, the default is `\"[DEFAULT]\"`.\r\n *\r\n * @returns The app corresponding to the provided app name.\r\n * If no app name is provided, the default app is returned.\r\n *\r\n * @public\r\n */\r\nfunction getApp(name = DEFAULT_ENTRY_NAME) {\r\n const app = _apps.get(name);\r\n if (!app) {\r\n throw ERROR_FACTORY.create(\"no-app\" /* NO_APP */, { appName: name });\r\n }\r\n return app;\r\n}\r\n/**\r\n * A (read-only) array of all initialized apps.\r\n * @public\r\n */\r\nfunction getApps() {\r\n return Array.from(_apps.values());\r\n}\r\n/**\r\n * Renders this app unusable and frees the resources of all associated\r\n * services.\r\n *\r\n * @example\r\n * ```javascript\r\n * deleteApp(app)\r\n * .then(function() {\r\n * console.log(\"App deleted successfully\");\r\n * })\r\n * .catch(function(error) {\r\n * console.log(\"Error deleting app:\", error);\r\n * });\r\n * ```\r\n *\r\n * @public\r\n */\r\nasync function deleteApp(app) {\r\n const name = app.name;\r\n if (_apps.has(name)) {\r\n _apps.delete(name);\r\n await Promise.all(app.container\r\n .getProviders()\r\n .map(provider => provider.delete()));\r\n app.isDeleted = true;\r\n }\r\n}\r\n/**\r\n * Registers a library's name and version for platform logging purposes.\r\n * @param library - Name of 1p or 3p library (e.g. firestore, angularfire)\r\n * @param version - Current version of that library.\r\n * @param variant - Bundle variant, e.g., node, rn, etc.\r\n *\r\n * @public\r\n */\r\nfunction registerVersion(libraryKeyOrName, version, variant) {\r\n var _a;\r\n // TODO: We can use this check to whitelist strings when/if we set up\r\n // a good whitelist system.\r\n let library = (_a = PLATFORM_LOG_STRING[libraryKeyOrName]) !== null && _a !== void 0 ? _a : libraryKeyOrName;\r\n if (variant) {\r\n library += `-${variant}`;\r\n }\r\n const libraryMismatch = library.match(/\\s|\\//);\r\n const versionMismatch = version.match(/\\s|\\//);\r\n if (libraryMismatch || versionMismatch) {\r\n const warning = [\r\n `Unable to register library \"${library}\" with version \"${version}\":`\r\n ];\r\n if (libraryMismatch) {\r\n warning.push(`library name \"${library}\" contains illegal characters (whitespace or \"/\")`);\r\n }\r\n if (libraryMismatch && versionMismatch) {\r\n warning.push('and');\r\n }\r\n if (versionMismatch) {\r\n warning.push(`version name \"${version}\" contains illegal characters (whitespace or \"/\")`);\r\n }\r\n logger.warn(warning.join(' '));\r\n return;\r\n }\r\n _registerComponent(new Component(`${library}-version`, () => ({ library, version }), \"VERSION\" /* VERSION */));\r\n}\r\n/**\r\n * Sets log handler for all Firebase SDKs.\r\n * @param logCallback - An optional custom log handler that executes user code whenever\r\n * the Firebase SDK makes a logging call.\r\n *\r\n * @public\r\n */\r\nfunction onLog(logCallback, options) {\r\n if (logCallback !== null && typeof logCallback !== 'function') {\r\n throw ERROR_FACTORY.create(\"invalid-log-argument\" /* INVALID_LOG_ARGUMENT */);\r\n }\r\n setUserLogHandler(logCallback, options);\r\n}\r\n/**\r\n * Sets log level for all Firebase SDKs.\r\n *\r\n * All of the log types above the current log level are captured (i.e. if\r\n * you set the log level to `info`, errors are logged, but `debug` and\r\n * `verbose` logs are not).\r\n *\r\n * @public\r\n */\r\nfunction setLogLevel(logLevel) {\r\n setLogLevel$1(logLevel);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction registerCoreComponents(variant) {\r\n _registerComponent(new Component('platform-logger', container => new PlatformLoggerServiceImpl(container), \"PRIVATE\" /* PRIVATE */));\r\n // Register `app` package.\r\n registerVersion(name$o, version$1, variant);\r\n // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\n registerVersion(name$o, version$1, 'esm2017');\r\n // Register platform SDK identifier (no version).\r\n registerVersion('fire-js', '');\r\n}\n\n/**\r\n * Firebase App\r\n *\r\n * @remarks This package coordinates the communication between the different Firebase components\r\n * @packageDocumentation\r\n */\r\nregisterCoreComponents('');\n\nexport { SDK_VERSION, DEFAULT_ENTRY_NAME as _DEFAULT_ENTRY_NAME, _addComponent, _addOrOverwriteComponent, _apps, _clearComponents, _components, _getProvider, _registerComponent, _removeServiceInstance, deleteApp, getApp, getApps, initializeApp, onLog, registerVersion, setLogLevel };\n//# sourceMappingURL=index.esm2017.js.map\n","import { registerVersion } from '@firebase/app';\nexport * from '@firebase/app';\n\nvar name = \"firebase\";\nvar version = \"9.6.7\";\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nregisterVersion(name, version, 'app');\n//# sourceMappingURL=index.esm.js.map\n","import { getApp, _getProvider, _registerComponent, registerVersion } from '@firebase/app';\nimport { Component } from '@firebase/component';\nimport { ErrorFactory, FirebaseError } from '@firebase/util';\nimport { openDb } from 'idb';\n\nconst name = \"@firebase/installations\";\nconst version = \"0.5.5\";\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst PENDING_TIMEOUT_MS = 10000;\r\nconst PACKAGE_VERSION = `w:${version}`;\r\nconst INTERNAL_AUTH_VERSION = 'FIS_v2';\r\nconst INSTALLATIONS_API_URL = 'https://firebaseinstallations.googleapis.com/v1';\r\nconst TOKEN_EXPIRATION_BUFFER = 60 * 60 * 1000; // One hour\r\nconst SERVICE = 'installations';\r\nconst SERVICE_NAME = 'Installations';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERROR_DESCRIPTION_MAP = {\r\n [\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: \"{$valueName}\"',\r\n [\"not-registered\" /* NOT_REGISTERED */]: 'Firebase Installation is not registered.',\r\n [\"installation-not-found\" /* INSTALLATION_NOT_FOUND */]: 'Firebase Installation not found.',\r\n [\"request-failed\" /* REQUEST_FAILED */]: '{$requestName} request failed with error \"{$serverCode} {$serverStatus}: {$serverMessage}\"',\r\n [\"app-offline\" /* APP_OFFLINE */]: 'Could not process request. Application offline.',\r\n [\"delete-pending-registration\" /* DELETE_PENDING_REGISTRATION */]: \"Can't delete installation while there is a pending registration request.\"\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory(SERVICE, SERVICE_NAME, ERROR_DESCRIPTION_MAP);\r\n/** Returns true if error is a FirebaseError that is based on an error from the server. */\r\nfunction isServerError(error) {\r\n return (error instanceof FirebaseError &&\r\n error.code.includes(\"request-failed\" /* REQUEST_FAILED */));\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction getInstallationsEndpoint({ projectId }) {\r\n return `${INSTALLATIONS_API_URL}/projects/${projectId}/installations`;\r\n}\r\nfunction extractAuthTokenInfoFromResponse(response) {\r\n return {\r\n token: response.token,\r\n requestStatus: 2 /* COMPLETED */,\r\n expiresIn: getExpiresInFromResponseExpiresIn(response.expiresIn),\r\n creationTime: Date.now()\r\n };\r\n}\r\nasync function getErrorFromResponse(requestName, response) {\r\n const responseJson = await response.json();\r\n const errorData = responseJson.error;\r\n return ERROR_FACTORY.create(\"request-failed\" /* REQUEST_FAILED */, {\r\n requestName,\r\n serverCode: errorData.code,\r\n serverMessage: errorData.message,\r\n serverStatus: errorData.status\r\n });\r\n}\r\nfunction getHeaders({ apiKey }) {\r\n return new Headers({\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'x-goog-api-key': apiKey\r\n });\r\n}\r\nfunction getHeadersWithAuth(appConfig, { refreshToken }) {\r\n const headers = getHeaders(appConfig);\r\n headers.append('Authorization', getAuthorizationHeader(refreshToken));\r\n return headers;\r\n}\r\n/**\r\n * Calls the passed in fetch wrapper and returns the response.\r\n * If the returned response has a status of 5xx, re-runs the function once and\r\n * returns the response.\r\n */\r\nasync function retryIfServerError(fn) {\r\n const result = await fn();\r\n if (result.status >= 500 && result.status < 600) {\r\n // Internal Server Error. Retry request.\r\n return fn();\r\n }\r\n return result;\r\n}\r\nfunction getExpiresInFromResponseExpiresIn(responseExpiresIn) {\r\n // This works because the server will never respond with fractions of a second.\r\n return Number(responseExpiresIn.replace('s', '000'));\r\n}\r\nfunction getAuthorizationHeader(refreshToken) {\r\n return `${INTERNAL_AUTH_VERSION} ${refreshToken}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function createInstallationRequest(appConfig, { fid }) {\r\n const endpoint = getInstallationsEndpoint(appConfig);\r\n const headers = getHeaders(appConfig);\r\n const body = {\r\n fid,\r\n authVersion: INTERNAL_AUTH_VERSION,\r\n appId: appConfig.appId,\r\n sdkVersion: PACKAGE_VERSION\r\n };\r\n const request = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (response.ok) {\r\n const responseValue = await response.json();\r\n const registeredInstallationEntry = {\r\n fid: responseValue.fid || fid,\r\n registrationStatus: 2 /* COMPLETED */,\r\n refreshToken: responseValue.refreshToken,\r\n authToken: extractAuthTokenInfoFromResponse(responseValue.authToken)\r\n };\r\n return registeredInstallationEntry;\r\n }\r\n else {\r\n throw await getErrorFromResponse('Create Installation', response);\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/** Returns a promise that resolves after given time passes. */\r\nfunction sleep(ms) {\r\n return new Promise(resolve => {\r\n setTimeout(resolve, ms);\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction bufferToBase64UrlSafe(array) {\r\n const b64 = btoa(String.fromCharCode(...array));\r\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst VALID_FID_PATTERN = /^[cdef][\\w-]{21}$/;\r\nconst INVALID_FID = '';\r\n/**\r\n * Generates a new FID using random values from Web Crypto API.\r\n * Returns an empty string if FID generation fails for any reason.\r\n */\r\nfunction generateFid() {\r\n try {\r\n // A valid FID has exactly 22 base64 characters, which is 132 bits, or 16.5\r\n // bytes. our implementation generates a 17 byte array instead.\r\n const fidByteArray = new Uint8Array(17);\r\n const crypto = self.crypto || self.msCrypto;\r\n crypto.getRandomValues(fidByteArray);\r\n // Replace the first 4 random bits with the constant FID header of 0b0111.\r\n fidByteArray[0] = 0b01110000 + (fidByteArray[0] % 0b00010000);\r\n const fid = encode(fidByteArray);\r\n return VALID_FID_PATTERN.test(fid) ? fid : INVALID_FID;\r\n }\r\n catch (_a) {\r\n // FID generation errored\r\n return INVALID_FID;\r\n }\r\n}\r\n/** Converts a FID Uint8Array to a base64 string representation. */\r\nfunction encode(fidByteArray) {\r\n const b64String = bufferToBase64UrlSafe(fidByteArray);\r\n // Remove the 23rd character that was added because of the extra 4 bits at the\r\n // end of our 17 byte array, and the '=' padding.\r\n return b64String.substr(0, 22);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/** Returns a string key that can be used to identify the app. */\r\nfunction getKey(appConfig) {\r\n return `${appConfig.appName}!${appConfig.appId}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst fidChangeCallbacks = new Map();\r\n/**\r\n * Calls the onIdChange callbacks with the new FID value, and broadcasts the\r\n * change to other tabs.\r\n */\r\nfunction fidChanged(appConfig, fid) {\r\n const key = getKey(appConfig);\r\n callFidChangeCallbacks(key, fid);\r\n broadcastFidChange(key, fid);\r\n}\r\nfunction addCallback(appConfig, callback) {\r\n // Open the broadcast channel if it's not already open,\r\n // to be able to listen to change events from other tabs.\r\n getBroadcastChannel();\r\n const key = getKey(appConfig);\r\n let callbackSet = fidChangeCallbacks.get(key);\r\n if (!callbackSet) {\r\n callbackSet = new Set();\r\n fidChangeCallbacks.set(key, callbackSet);\r\n }\r\n callbackSet.add(callback);\r\n}\r\nfunction removeCallback(appConfig, callback) {\r\n const key = getKey(appConfig);\r\n const callbackSet = fidChangeCallbacks.get(key);\r\n if (!callbackSet) {\r\n return;\r\n }\r\n callbackSet.delete(callback);\r\n if (callbackSet.size === 0) {\r\n fidChangeCallbacks.delete(key);\r\n }\r\n // Close broadcast channel if there are no more callbacks.\r\n closeBroadcastChannel();\r\n}\r\nfunction callFidChangeCallbacks(key, fid) {\r\n const callbacks = fidChangeCallbacks.get(key);\r\n if (!callbacks) {\r\n return;\r\n }\r\n for (const callback of callbacks) {\r\n callback(fid);\r\n }\r\n}\r\nfunction broadcastFidChange(key, fid) {\r\n const channel = getBroadcastChannel();\r\n if (channel) {\r\n channel.postMessage({ key, fid });\r\n }\r\n closeBroadcastChannel();\r\n}\r\nlet broadcastChannel = null;\r\n/** Opens and returns a BroadcastChannel if it is supported by the browser. */\r\nfunction getBroadcastChannel() {\r\n if (!broadcastChannel && 'BroadcastChannel' in self) {\r\n broadcastChannel = new BroadcastChannel('[Firebase] FID Change');\r\n broadcastChannel.onmessage = e => {\r\n callFidChangeCallbacks(e.data.key, e.data.fid);\r\n };\r\n }\r\n return broadcastChannel;\r\n}\r\nfunction closeBroadcastChannel() {\r\n if (fidChangeCallbacks.size === 0 && broadcastChannel) {\r\n broadcastChannel.close();\r\n broadcastChannel = null;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DATABASE_NAME = 'firebase-installations-database';\r\nconst DATABASE_VERSION = 1;\r\nconst OBJECT_STORE_NAME = 'firebase-installations-store';\r\nlet dbPromise = null;\r\nfunction getDbPromise() {\r\n if (!dbPromise) {\r\n dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, upgradeDB => {\r\n // We don't use 'break' in this switch statement, the fall-through\r\n // behavior is what we want, because if there are multiple versions between\r\n // the old version and the current version, we want ALL the migrations\r\n // that correspond to those versions to run, not only the last one.\r\n // eslint-disable-next-line default-case\r\n switch (upgradeDB.oldVersion) {\r\n case 0:\r\n upgradeDB.createObjectStore(OBJECT_STORE_NAME);\r\n }\r\n });\r\n }\r\n return dbPromise;\r\n}\r\n/** Assigns or overwrites the record for the given key with the given value. */\r\nasync function set(appConfig, value) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n const objectStore = tx.objectStore(OBJECT_STORE_NAME);\r\n const oldValue = await objectStore.get(key);\r\n await objectStore.put(value, key);\r\n await tx.complete;\r\n if (!oldValue || oldValue.fid !== value.fid) {\r\n fidChanged(appConfig, value.fid);\r\n }\r\n return value;\r\n}\r\n/** Removes record(s) from the objectStore that match the given key. */\r\nasync function remove(appConfig) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).delete(key);\r\n await tx.complete;\r\n}\r\n/**\r\n * Atomically updates a record with the result of updateFn, which gets\r\n * called with the current value. If newValue is undefined, the record is\r\n * deleted instead.\r\n * @return Updated value\r\n */\r\nasync function update(appConfig, updateFn) {\r\n const key = getKey(appConfig);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n const store = tx.objectStore(OBJECT_STORE_NAME);\r\n const oldValue = await store.get(key);\r\n const newValue = updateFn(oldValue);\r\n if (newValue === undefined) {\r\n await store.delete(key);\r\n }\r\n else {\r\n await store.put(newValue, key);\r\n }\r\n await tx.complete;\r\n if (newValue && (!oldValue || oldValue.fid !== newValue.fid)) {\r\n fidChanged(appConfig, newValue.fid);\r\n }\r\n return newValue;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Updates and returns the InstallationEntry from the database.\r\n * Also triggers a registration request if it is necessary and possible.\r\n */\r\nasync function getInstallationEntry(appConfig) {\r\n let registrationPromise;\r\n const installationEntry = await update(appConfig, oldEntry => {\r\n const installationEntry = updateOrCreateInstallationEntry(oldEntry);\r\n const entryWithPromise = triggerRegistrationIfNecessary(appConfig, installationEntry);\r\n registrationPromise = entryWithPromise.registrationPromise;\r\n return entryWithPromise.installationEntry;\r\n });\r\n if (installationEntry.fid === INVALID_FID) {\r\n // FID generation failed. Waiting for the FID from the server.\r\n return { installationEntry: await registrationPromise };\r\n }\r\n return {\r\n installationEntry,\r\n registrationPromise\r\n };\r\n}\r\n/**\r\n * Creates a new Installation Entry if one does not exist.\r\n * Also clears timed out pending requests.\r\n */\r\nfunction updateOrCreateInstallationEntry(oldEntry) {\r\n const entry = oldEntry || {\r\n fid: generateFid(),\r\n registrationStatus: 0 /* NOT_STARTED */\r\n };\r\n return clearTimedOutRequest(entry);\r\n}\r\n/**\r\n * If the Firebase Installation is not registered yet, this will trigger the\r\n * registration and return an InProgressInstallationEntry.\r\n *\r\n * If registrationPromise does not exist, the installationEntry is guaranteed\r\n * to be registered.\r\n */\r\nfunction triggerRegistrationIfNecessary(appConfig, installationEntry) {\r\n if (installationEntry.registrationStatus === 0 /* NOT_STARTED */) {\r\n if (!navigator.onLine) {\r\n // Registration required but app is offline.\r\n const registrationPromiseWithError = Promise.reject(ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */));\r\n return {\r\n installationEntry,\r\n registrationPromise: registrationPromiseWithError\r\n };\r\n }\r\n // Try registering. Change status to IN_PROGRESS.\r\n const inProgressEntry = {\r\n fid: installationEntry.fid,\r\n registrationStatus: 1 /* IN_PROGRESS */,\r\n registrationTime: Date.now()\r\n };\r\n const registrationPromise = registerInstallation(appConfig, inProgressEntry);\r\n return { installationEntry: inProgressEntry, registrationPromise };\r\n }\r\n else if (installationEntry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n return {\r\n installationEntry,\r\n registrationPromise: waitUntilFidRegistration(appConfig)\r\n };\r\n }\r\n else {\r\n return { installationEntry };\r\n }\r\n}\r\n/** This will be executed only once for each new Firebase Installation. */\r\nasync function registerInstallation(appConfig, installationEntry) {\r\n try {\r\n const registeredInstallationEntry = await createInstallationRequest(appConfig, installationEntry);\r\n return set(appConfig, registeredInstallationEntry);\r\n }\r\n catch (e) {\r\n if (isServerError(e) && e.customData.serverCode === 409) {\r\n // Server returned a \"FID can not be used\" error.\r\n // Generate a new ID next time.\r\n await remove(appConfig);\r\n }\r\n else {\r\n // Registration failed. Set FID as not registered.\r\n await set(appConfig, {\r\n fid: installationEntry.fid,\r\n registrationStatus: 0 /* NOT_STARTED */\r\n });\r\n }\r\n throw e;\r\n }\r\n}\r\n/** Call if FID registration is pending in another request. */\r\nasync function waitUntilFidRegistration(appConfig) {\r\n // Unfortunately, there is no way of reliably observing when a value in\r\n // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),\r\n // so we need to poll.\r\n let entry = await updateInstallationRequest(appConfig);\r\n while (entry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n // createInstallation request still in progress.\r\n await sleep(100);\r\n entry = await updateInstallationRequest(appConfig);\r\n }\r\n if (entry.registrationStatus === 0 /* NOT_STARTED */) {\r\n // The request timed out or failed in a different call. Try again.\r\n const { installationEntry, registrationPromise } = await getInstallationEntry(appConfig);\r\n if (registrationPromise) {\r\n return registrationPromise;\r\n }\r\n else {\r\n // if there is no registrationPromise, entry is registered.\r\n return installationEntry;\r\n }\r\n }\r\n return entry;\r\n}\r\n/**\r\n * Called only if there is a CreateInstallation request in progress.\r\n *\r\n * Updates the InstallationEntry in the DB based on the status of the\r\n * CreateInstallation request.\r\n *\r\n * Returns the updated InstallationEntry.\r\n */\r\nfunction updateInstallationRequest(appConfig) {\r\n return update(appConfig, oldEntry => {\r\n if (!oldEntry) {\r\n throw ERROR_FACTORY.create(\"installation-not-found\" /* INSTALLATION_NOT_FOUND */);\r\n }\r\n return clearTimedOutRequest(oldEntry);\r\n });\r\n}\r\nfunction clearTimedOutRequest(entry) {\r\n if (hasInstallationRequestTimedOut(entry)) {\r\n return {\r\n fid: entry.fid,\r\n registrationStatus: 0 /* NOT_STARTED */\r\n };\r\n }\r\n return entry;\r\n}\r\nfunction hasInstallationRequestTimedOut(installationEntry) {\r\n return (installationEntry.registrationStatus === 1 /* IN_PROGRESS */ &&\r\n installationEntry.registrationTime + PENDING_TIMEOUT_MS < Date.now());\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function generateAuthTokenRequest({ appConfig, platformLoggerProvider }, installationEntry) {\r\n const endpoint = getGenerateAuthTokenEndpoint(appConfig, installationEntry);\r\n const headers = getHeadersWithAuth(appConfig, installationEntry);\r\n // If platform logger exists, add the platform info string to the header.\r\n const platformLogger = platformLoggerProvider.getImmediate({\r\n optional: true\r\n });\r\n if (platformLogger) {\r\n headers.append('x-firebase-client', platformLogger.getPlatformInfoString());\r\n }\r\n const body = {\r\n installation: {\r\n sdkVersion: PACKAGE_VERSION\r\n }\r\n };\r\n const request = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (response.ok) {\r\n const responseValue = await response.json();\r\n const completedAuthToken = extractAuthTokenInfoFromResponse(responseValue);\r\n return completedAuthToken;\r\n }\r\n else {\r\n throw await getErrorFromResponse('Generate Auth Token', response);\r\n }\r\n}\r\nfunction getGenerateAuthTokenEndpoint(appConfig, { fid }) {\r\n return `${getInstallationsEndpoint(appConfig)}/${fid}/authTokens:generate`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a valid authentication token for the installation. Generates a new\r\n * token if one doesn't exist, is expired or about to expire.\r\n *\r\n * Should only be called if the Firebase Installation is registered.\r\n */\r\nasync function refreshAuthToken(installations, forceRefresh = false) {\r\n let tokenPromise;\r\n const entry = await update(installations.appConfig, oldEntry => {\r\n if (!isEntryRegistered(oldEntry)) {\r\n throw ERROR_FACTORY.create(\"not-registered\" /* NOT_REGISTERED */);\r\n }\r\n const oldAuthToken = oldEntry.authToken;\r\n if (!forceRefresh && isAuthTokenValid(oldAuthToken)) {\r\n // There is a valid token in the DB.\r\n return oldEntry;\r\n }\r\n else if (oldAuthToken.requestStatus === 1 /* IN_PROGRESS */) {\r\n // There already is a token request in progress.\r\n tokenPromise = waitUntilAuthTokenRequest(installations, forceRefresh);\r\n return oldEntry;\r\n }\r\n else {\r\n // No token or token expired.\r\n if (!navigator.onLine) {\r\n throw ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */);\r\n }\r\n const inProgressEntry = makeAuthTokenRequestInProgressEntry(oldEntry);\r\n tokenPromise = fetchAuthTokenFromServer(installations, inProgressEntry);\r\n return inProgressEntry;\r\n }\r\n });\r\n const authToken = tokenPromise\r\n ? await tokenPromise\r\n : entry.authToken;\r\n return authToken;\r\n}\r\n/**\r\n * Call only if FID is registered and Auth Token request is in progress.\r\n *\r\n * Waits until the current pending request finishes. If the request times out,\r\n * tries once in this thread as well.\r\n */\r\nasync function waitUntilAuthTokenRequest(installations, forceRefresh) {\r\n // Unfortunately, there is no way of reliably observing when a value in\r\n // IndexedDB changes (yet, see https://github.com/WICG/indexed-db-observers),\r\n // so we need to poll.\r\n let entry = await updateAuthTokenRequest(installations.appConfig);\r\n while (entry.authToken.requestStatus === 1 /* IN_PROGRESS */) {\r\n // generateAuthToken still in progress.\r\n await sleep(100);\r\n entry = await updateAuthTokenRequest(installations.appConfig);\r\n }\r\n const authToken = entry.authToken;\r\n if (authToken.requestStatus === 0 /* NOT_STARTED */) {\r\n // The request timed out or failed in a different call. Try again.\r\n return refreshAuthToken(installations, forceRefresh);\r\n }\r\n else {\r\n return authToken;\r\n }\r\n}\r\n/**\r\n * Called only if there is a GenerateAuthToken request in progress.\r\n *\r\n * Updates the InstallationEntry in the DB based on the status of the\r\n * GenerateAuthToken request.\r\n *\r\n * Returns the updated InstallationEntry.\r\n */\r\nfunction updateAuthTokenRequest(appConfig) {\r\n return update(appConfig, oldEntry => {\r\n if (!isEntryRegistered(oldEntry)) {\r\n throw ERROR_FACTORY.create(\"not-registered\" /* NOT_REGISTERED */);\r\n }\r\n const oldAuthToken = oldEntry.authToken;\r\n if (hasAuthTokenRequestTimedOut(oldAuthToken)) {\r\n return Object.assign(Object.assign({}, oldEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });\r\n }\r\n return oldEntry;\r\n });\r\n}\r\nasync function fetchAuthTokenFromServer(installations, installationEntry) {\r\n try {\r\n const authToken = await generateAuthTokenRequest(installations, installationEntry);\r\n const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken });\r\n await set(installations.appConfig, updatedInstallationEntry);\r\n return authToken;\r\n }\r\n catch (e) {\r\n if (isServerError(e) &&\r\n (e.customData.serverCode === 401 || e.customData.serverCode === 404)) {\r\n // Server returned a \"FID not found\" or a \"Invalid authentication\" error.\r\n // Generate a new ID next time.\r\n await remove(installations.appConfig);\r\n }\r\n else {\r\n const updatedInstallationEntry = Object.assign(Object.assign({}, installationEntry), { authToken: { requestStatus: 0 /* NOT_STARTED */ } });\r\n await set(installations.appConfig, updatedInstallationEntry);\r\n }\r\n throw e;\r\n }\r\n}\r\nfunction isEntryRegistered(installationEntry) {\r\n return (installationEntry !== undefined &&\r\n installationEntry.registrationStatus === 2 /* COMPLETED */);\r\n}\r\nfunction isAuthTokenValid(authToken) {\r\n return (authToken.requestStatus === 2 /* COMPLETED */ &&\r\n !isAuthTokenExpired(authToken));\r\n}\r\nfunction isAuthTokenExpired(authToken) {\r\n const now = Date.now();\r\n return (now < authToken.creationTime ||\r\n authToken.creationTime + authToken.expiresIn < now + TOKEN_EXPIRATION_BUFFER);\r\n}\r\n/** Returns an updated InstallationEntry with an InProgressAuthToken. */\r\nfunction makeAuthTokenRequestInProgressEntry(oldEntry) {\r\n const inProgressAuthToken = {\r\n requestStatus: 1 /* IN_PROGRESS */,\r\n requestTime: Date.now()\r\n };\r\n return Object.assign(Object.assign({}, oldEntry), { authToken: inProgressAuthToken });\r\n}\r\nfunction hasAuthTokenRequestTimedOut(authToken) {\r\n return (authToken.requestStatus === 1 /* IN_PROGRESS */ &&\r\n authToken.requestTime + PENDING_TIMEOUT_MS < Date.now());\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Creates a Firebase Installation if there isn't one for the app and\r\n * returns the Installation ID.\r\n * @param installations - The `Installations` instance.\r\n *\r\n * @public\r\n */\r\nasync function getId(installations) {\r\n const installationsImpl = installations;\r\n const { installationEntry, registrationPromise } = await getInstallationEntry(installationsImpl.appConfig);\r\n if (registrationPromise) {\r\n registrationPromise.catch(console.error);\r\n }\r\n else {\r\n // If the installation is already registered, update the authentication\r\n // token if needed.\r\n refreshAuthToken(installationsImpl).catch(console.error);\r\n }\r\n return installationEntry.fid;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns a Firebase Installations auth token, identifying the current\r\n * Firebase Installation.\r\n * @param installations - The `Installations` instance.\r\n * @param forceRefresh - Force refresh regardless of token expiration.\r\n *\r\n * @public\r\n */\r\nasync function getToken(installations, forceRefresh = false) {\r\n const installationsImpl = installations;\r\n await completeInstallationRegistration(installationsImpl.appConfig);\r\n // At this point we either have a Registered Installation in the DB, or we've\r\n // already thrown an error.\r\n const authToken = await refreshAuthToken(installationsImpl, forceRefresh);\r\n return authToken.token;\r\n}\r\nasync function completeInstallationRegistration(appConfig) {\r\n const { registrationPromise } = await getInstallationEntry(appConfig);\r\n if (registrationPromise) {\r\n // A createInstallation request is in progress. Wait until it finishes.\r\n await registrationPromise;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function deleteInstallationRequest(appConfig, installationEntry) {\r\n const endpoint = getDeleteEndpoint(appConfig, installationEntry);\r\n const headers = getHeadersWithAuth(appConfig, installationEntry);\r\n const request = {\r\n method: 'DELETE',\r\n headers\r\n };\r\n const response = await retryIfServerError(() => fetch(endpoint, request));\r\n if (!response.ok) {\r\n throw await getErrorFromResponse('Delete Installation', response);\r\n }\r\n}\r\nfunction getDeleteEndpoint(appConfig, { fid }) {\r\n return `${getInstallationsEndpoint(appConfig)}/${fid}`;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Deletes the Firebase Installation and all associated data.\r\n * @param installations - The `Installations` instance.\r\n *\r\n * @public\r\n */\r\nasync function deleteInstallations(installations) {\r\n const { appConfig } = installations;\r\n const entry = await update(appConfig, oldEntry => {\r\n if (oldEntry && oldEntry.registrationStatus === 0 /* NOT_STARTED */) {\r\n // Delete the unregistered entry without sending a deleteInstallation request.\r\n return undefined;\r\n }\r\n return oldEntry;\r\n });\r\n if (entry) {\r\n if (entry.registrationStatus === 1 /* IN_PROGRESS */) {\r\n // Can't delete while trying to register.\r\n throw ERROR_FACTORY.create(\"delete-pending-registration\" /* DELETE_PENDING_REGISTRATION */);\r\n }\r\n else if (entry.registrationStatus === 2 /* COMPLETED */) {\r\n if (!navigator.onLine) {\r\n throw ERROR_FACTORY.create(\"app-offline\" /* APP_OFFLINE */);\r\n }\r\n else {\r\n await deleteInstallationRequest(appConfig, entry);\r\n await remove(appConfig);\r\n }\r\n }\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Sets a new callback that will get called when Installation ID changes.\r\n * Returns an unsubscribe function that will remove the callback when called.\r\n * @param installations - The `Installations` instance.\r\n * @param callback - The callback function that is invoked when FID changes.\r\n * @returns A function that can be called to unsubscribe.\r\n *\r\n * @public\r\n */\r\nfunction onIdChange(installations, callback) {\r\n const { appConfig } = installations;\r\n addCallback(appConfig, callback);\r\n return () => {\r\n removeCallback(appConfig, callback);\r\n };\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Returns an instance of {@link Installations} associated with the given\r\n * {@link @firebase/app#FirebaseApp} instance.\r\n * @param app - The {@link @firebase/app#FirebaseApp} instance.\r\n *\r\n * @public\r\n */\r\nfunction getInstallations(app = getApp()) {\r\n const installationsImpl = _getProvider(app, 'installations').getImmediate();\r\n return installationsImpl;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction extractAppConfig(app) {\r\n if (!app || !app.options) {\r\n throw getMissingValueError('App Configuration');\r\n }\r\n if (!app.name) {\r\n throw getMissingValueError('App Name');\r\n }\r\n // Required app config keys\r\n const configKeys = [\r\n 'projectId',\r\n 'apiKey',\r\n 'appId'\r\n ];\r\n for (const keyName of configKeys) {\r\n if (!app.options[keyName]) {\r\n throw getMissingValueError(keyName);\r\n }\r\n }\r\n return {\r\n appName: app.name,\r\n projectId: app.options.projectId,\r\n apiKey: app.options.apiKey,\r\n appId: app.options.appId\r\n };\r\n}\r\nfunction getMissingValueError(valueName) {\r\n return ERROR_FACTORY.create(\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */, {\r\n valueName\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst INSTALLATIONS_NAME = 'installations';\r\nconst INSTALLATIONS_NAME_INTERNAL = 'installations-internal';\r\nconst publicFactory = (container) => {\r\n const app = container.getProvider('app').getImmediate();\r\n // Throws if app isn't configured properly.\r\n const appConfig = extractAppConfig(app);\r\n const platformLoggerProvider = _getProvider(app, 'platform-logger');\r\n const installationsImpl = {\r\n app,\r\n appConfig,\r\n platformLoggerProvider,\r\n _delete: () => Promise.resolve()\r\n };\r\n return installationsImpl;\r\n};\r\nconst internalFactory = (container) => {\r\n const app = container.getProvider('app').getImmediate();\r\n // Internal FIS instance relies on public FIS instance.\r\n const installations = _getProvider(app, INSTALLATIONS_NAME).getImmediate();\r\n const installationsInternal = {\r\n getId: () => getId(installations),\r\n getToken: (forceRefresh) => getToken(installations, forceRefresh)\r\n };\r\n return installationsInternal;\r\n};\r\nfunction registerInstallations() {\r\n _registerComponent(new Component(INSTALLATIONS_NAME, publicFactory, \"PUBLIC\" /* PUBLIC */));\r\n _registerComponent(new Component(INSTALLATIONS_NAME_INTERNAL, internalFactory, \"PRIVATE\" /* PRIVATE */));\r\n}\n\n/**\r\n * Firebase Installations\r\n *\r\n * @packageDocumentation\r\n */\r\nregisterInstallations();\r\nregisterVersion(name, version);\r\n// BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\nregisterVersion(name, version, 'esm2017');\n\nexport { deleteInstallations, getId, getInstallations, getToken, onIdChange };\n//# sourceMappingURL=index.esm2017.js.map\n","import '@firebase/installations';\nimport { Component } from '@firebase/component';\nimport { openDb, deleteDb } from 'idb';\nimport { ErrorFactory, validateIndexedDBOpenable, isIndexedDBAvailable, areCookiesEnabled, getModularInstance } from '@firebase/util';\nimport { _registerComponent, registerVersion, getApp, _getProvider } from '@firebase/app';\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst DEFAULT_SW_PATH = '/firebase-messaging-sw.js';\r\nconst DEFAULT_SW_SCOPE = '/firebase-cloud-messaging-push-scope';\r\nconst DEFAULT_VAPID_KEY = 'BDOU99-h67HcA6JeFXHbSNMu7e2yNNu3RzoMj8TM4W88jITfq7ZmPvIM1Iv-4_l2LxQcYwhqby2xGpWwzjfAnG4';\r\nconst ENDPOINT = 'https://fcmregistrations.googleapis.com/v1';\r\nconst CONSOLE_CAMPAIGN_ID = 'google.c.a.c_id';\r\nconst CONSOLE_CAMPAIGN_NAME = 'google.c.a.c_l';\r\nconst CONSOLE_CAMPAIGN_TIME = 'google.c.a.ts';\r\n/** Set to '1' if Analytics is enabled for the campaign */\r\nconst CONSOLE_CAMPAIGN_ANALYTICS_ENABLED = 'google.c.a.e';\r\nvar MessageType$1;\r\n(function (MessageType) {\r\n MessageType[MessageType[\"DATA_MESSAGE\"] = 1] = \"DATA_MESSAGE\";\r\n MessageType[MessageType[\"DISPLAY_NOTIFICATION\"] = 3] = \"DISPLAY_NOTIFICATION\";\r\n})(MessageType$1 || (MessageType$1 = {}));\n\n/**\r\n * @license\r\n * Copyright 2018 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\r\n * in compliance with the License. You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software distributed under the License\r\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\r\n * or implied. See the License for the specific language governing permissions and limitations under\r\n * the License.\r\n */\r\nvar MessageType;\r\n(function (MessageType) {\r\n MessageType[\"PUSH_RECEIVED\"] = \"push-received\";\r\n MessageType[\"NOTIFICATION_CLICKED\"] = \"notification-clicked\";\r\n})(MessageType || (MessageType = {}));\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction arrayToBase64(array) {\r\n const uint8Array = new Uint8Array(array);\r\n const base64String = btoa(String.fromCharCode(...uint8Array));\r\n return base64String.replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\nfunction base64ToArray(base64String) {\r\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\r\n const base64 = (base64String + padding)\r\n .replace(/\\-/g, '+')\r\n .replace(/_/g, '/');\r\n const rawData = atob(base64);\r\n const outputArray = new Uint8Array(rawData.length);\r\n for (let i = 0; i < rawData.length; ++i) {\r\n outputArray[i] = rawData.charCodeAt(i);\r\n }\r\n return outputArray;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst OLD_DB_NAME = 'fcm_token_details_db';\r\n/**\r\n * The last DB version of 'fcm_token_details_db' was 4. This is one higher, so that the upgrade\r\n * callback is called for all versions of the old DB.\r\n */\r\nconst OLD_DB_VERSION = 5;\r\nconst OLD_OBJECT_STORE_NAME = 'fcm_token_object_Store';\r\nasync function migrateOldDatabase(senderId) {\r\n if ('databases' in indexedDB) {\r\n // indexedDb.databases() is an IndexedDB v3 API and does not exist in all browsers. TODO: Remove\r\n // typecast when it lands in TS types.\r\n const databases = await indexedDB.databases();\r\n const dbNames = databases.map(db => db.name);\r\n if (!dbNames.includes(OLD_DB_NAME)) {\r\n // old DB didn't exist, no need to open.\r\n return null;\r\n }\r\n }\r\n let tokenDetails = null;\r\n const db = await openDb(OLD_DB_NAME, OLD_DB_VERSION, async (db) => {\r\n var _a;\r\n if (db.oldVersion < 2) {\r\n // Database too old, skip migration.\r\n return;\r\n }\r\n if (!db.objectStoreNames.contains(OLD_OBJECT_STORE_NAME)) {\r\n // Database did not exist. Nothing to do.\r\n return;\r\n }\r\n const objectStore = db.transaction.objectStore(OLD_OBJECT_STORE_NAME);\r\n const value = await objectStore.index('fcmSenderId').get(senderId);\r\n await objectStore.clear();\r\n if (!value) {\r\n // No entry in the database, nothing to migrate.\r\n return;\r\n }\r\n if (db.oldVersion === 2) {\r\n const oldDetails = value;\r\n if (!oldDetails.auth || !oldDetails.p256dh || !oldDetails.endpoint) {\r\n return;\r\n }\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: (_a = oldDetails.createTime) !== null && _a !== void 0 ? _a : Date.now(),\r\n subscriptionOptions: {\r\n auth: oldDetails.auth,\r\n p256dh: oldDetails.p256dh,\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: typeof oldDetails.vapidKey === 'string'\r\n ? oldDetails.vapidKey\r\n : arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n else if (db.oldVersion === 3) {\r\n const oldDetails = value;\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: oldDetails.createTime,\r\n subscriptionOptions: {\r\n auth: arrayToBase64(oldDetails.auth),\r\n p256dh: arrayToBase64(oldDetails.p256dh),\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n else if (db.oldVersion === 4) {\r\n const oldDetails = value;\r\n tokenDetails = {\r\n token: oldDetails.fcmToken,\r\n createTime: oldDetails.createTime,\r\n subscriptionOptions: {\r\n auth: arrayToBase64(oldDetails.auth),\r\n p256dh: arrayToBase64(oldDetails.p256dh),\r\n endpoint: oldDetails.endpoint,\r\n swScope: oldDetails.swScope,\r\n vapidKey: arrayToBase64(oldDetails.vapidKey)\r\n }\r\n };\r\n }\r\n });\r\n db.close();\r\n // Delete all old databases.\r\n await deleteDb(OLD_DB_NAME);\r\n await deleteDb('fcm_vapid_details_db');\r\n await deleteDb('undefined');\r\n return checkTokenDetails(tokenDetails) ? tokenDetails : null;\r\n}\r\nfunction checkTokenDetails(tokenDetails) {\r\n if (!tokenDetails || !tokenDetails.subscriptionOptions) {\r\n return false;\r\n }\r\n const { subscriptionOptions } = tokenDetails;\r\n return (typeof tokenDetails.createTime === 'number' &&\r\n tokenDetails.createTime > 0 &&\r\n typeof tokenDetails.token === 'string' &&\r\n tokenDetails.token.length > 0 &&\r\n typeof subscriptionOptions.auth === 'string' &&\r\n subscriptionOptions.auth.length > 0 &&\r\n typeof subscriptionOptions.p256dh === 'string' &&\r\n subscriptionOptions.p256dh.length > 0 &&\r\n typeof subscriptionOptions.endpoint === 'string' &&\r\n subscriptionOptions.endpoint.length > 0 &&\r\n typeof subscriptionOptions.swScope === 'string' &&\r\n subscriptionOptions.swScope.length > 0 &&\r\n typeof subscriptionOptions.vapidKey === 'string' &&\r\n subscriptionOptions.vapidKey.length > 0);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// Exported for tests.\r\nconst DATABASE_NAME = 'firebase-messaging-database';\r\nconst DATABASE_VERSION = 1;\r\nconst OBJECT_STORE_NAME = 'firebase-messaging-store';\r\nlet dbPromise = null;\r\nfunction getDbPromise() {\r\n if (!dbPromise) {\r\n dbPromise = openDb(DATABASE_NAME, DATABASE_VERSION, upgradeDb => {\r\n // We don't use 'break' in this switch statement, the fall-through behavior is what we want,\r\n // because if there are multiple versions between the old version and the current version, we\r\n // want ALL the migrations that correspond to those versions to run, not only the last one.\r\n // eslint-disable-next-line default-case\r\n switch (upgradeDb.oldVersion) {\r\n case 0:\r\n upgradeDb.createObjectStore(OBJECT_STORE_NAME);\r\n }\r\n });\r\n }\r\n return dbPromise;\r\n}\r\n/** Gets record(s) from the objectStore that match the given key. */\r\nasync function dbGet(firebaseDependencies) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tokenDetails = await db\r\n .transaction(OBJECT_STORE_NAME)\r\n .objectStore(OBJECT_STORE_NAME)\r\n .get(key);\r\n if (tokenDetails) {\r\n return tokenDetails;\r\n }\r\n else {\r\n // Check if there is a tokenDetails object in the old DB.\r\n const oldTokenDetails = await migrateOldDatabase(firebaseDependencies.appConfig.senderId);\r\n if (oldTokenDetails) {\r\n await dbSet(firebaseDependencies, oldTokenDetails);\r\n return oldTokenDetails;\r\n }\r\n }\r\n}\r\n/** Assigns or overwrites the record for the given key with the given value. */\r\nasync function dbSet(firebaseDependencies, tokenDetails) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).put(tokenDetails, key);\r\n await tx.complete;\r\n return tokenDetails;\r\n}\r\n/** Removes record(s) from the objectStore that match the given key. */\r\nasync function dbRemove(firebaseDependencies) {\r\n const key = getKey(firebaseDependencies);\r\n const db = await getDbPromise();\r\n const tx = db.transaction(OBJECT_STORE_NAME, 'readwrite');\r\n await tx.objectStore(OBJECT_STORE_NAME).delete(key);\r\n await tx.complete;\r\n}\r\nfunction getKey({ appConfig }) {\r\n return appConfig.appId;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst ERROR_MAP = {\r\n [\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */]: 'Missing App configuration value: \"{$valueName}\"',\r\n [\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */]: 'This method is available in a Window context.',\r\n [\"only-available-in-sw\" /* AVAILABLE_IN_SW */]: 'This method is available in a service worker context.',\r\n [\"permission-default\" /* PERMISSION_DEFAULT */]: 'The notification permission was not granted and dismissed instead.',\r\n [\"permission-blocked\" /* PERMISSION_BLOCKED */]: 'The notification permission was not granted and blocked instead.',\r\n [\"unsupported-browser\" /* UNSUPPORTED_BROWSER */]: \"This browser doesn't support the API's required to use the Firebase SDK.\",\r\n [\"indexed-db-unsupported\" /* INDEXED_DB_UNSUPPORTED */]: \"This browser doesn't support indexedDb.open() (ex. Safari iFrame, Firefox Private Browsing, etc)\",\r\n [\"failed-service-worker-registration\" /* FAILED_DEFAULT_REGISTRATION */]: 'We are unable to register the default service worker. {$browserErrorMessage}',\r\n [\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */]: 'A problem occurred while subscribing the user to FCM: {$errorInfo}',\r\n [\"token-subscribe-no-token\" /* TOKEN_SUBSCRIBE_NO_TOKEN */]: 'FCM returned no token when subscribing the user to push.',\r\n [\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */]: 'A problem occurred while unsubscribing the ' +\r\n 'user from FCM: {$errorInfo}',\r\n [\"token-update-failed\" /* TOKEN_UPDATE_FAILED */]: 'A problem occurred while updating the user from FCM: {$errorInfo}',\r\n [\"token-update-no-token\" /* TOKEN_UPDATE_NO_TOKEN */]: 'FCM returned no token when updating the user to push.',\r\n [\"use-sw-after-get-token\" /* USE_SW_AFTER_GET_TOKEN */]: 'The useServiceWorker() method may only be called once and must be ' +\r\n 'called before calling getToken() to ensure your service worker is used.',\r\n [\"invalid-sw-registration\" /* INVALID_SW_REGISTRATION */]: 'The input to useServiceWorker() must be a ServiceWorkerRegistration.',\r\n [\"invalid-bg-handler\" /* INVALID_BG_HANDLER */]: 'The input to setBackgroundMessageHandler() must be a function.',\r\n [\"invalid-vapid-key\" /* INVALID_VAPID_KEY */]: 'The public VAPID key must be a string.',\r\n [\"use-vapid-key-after-get-token\" /* USE_VAPID_KEY_AFTER_GET_TOKEN */]: 'The usePublicVapidKey() method may only be called once and must be ' +\r\n 'called before calling getToken() to ensure your VAPID key is used.'\r\n};\r\nconst ERROR_FACTORY = new ErrorFactory('messaging', 'Messaging', ERROR_MAP);\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function requestGetToken(firebaseDependencies, subscriptionOptions) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const body = getBody(subscriptionOptions);\r\n const subscribeOptions = {\r\n method: 'POST',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n let responseData;\r\n try {\r\n const response = await fetch(getEndpoint(firebaseDependencies.appConfig), subscribeOptions);\r\n responseData = await response.json();\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-subscribe-failed\" /* TOKEN_SUBSCRIBE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n if (!responseData.token) {\r\n throw ERROR_FACTORY.create(\"token-subscribe-no-token\" /* TOKEN_SUBSCRIBE_NO_TOKEN */);\r\n }\r\n return responseData.token;\r\n}\r\nasync function requestUpdateToken(firebaseDependencies, tokenDetails) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const body = getBody(tokenDetails.subscriptionOptions);\r\n const updateOptions = {\r\n method: 'PATCH',\r\n headers,\r\n body: JSON.stringify(body)\r\n };\r\n let responseData;\r\n try {\r\n const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${tokenDetails.token}`, updateOptions);\r\n responseData = await response.json();\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-update-failed\" /* TOKEN_UPDATE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-update-failed\" /* TOKEN_UPDATE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n if (!responseData.token) {\r\n throw ERROR_FACTORY.create(\"token-update-no-token\" /* TOKEN_UPDATE_NO_TOKEN */);\r\n }\r\n return responseData.token;\r\n}\r\nasync function requestDeleteToken(firebaseDependencies, token) {\r\n const headers = await getHeaders(firebaseDependencies);\r\n const unsubscribeOptions = {\r\n method: 'DELETE',\r\n headers\r\n };\r\n try {\r\n const response = await fetch(`${getEndpoint(firebaseDependencies.appConfig)}/${token}`, unsubscribeOptions);\r\n const responseData = await response.json();\r\n if (responseData.error) {\r\n const message = responseData.error.message;\r\n throw ERROR_FACTORY.create(\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */, {\r\n errorInfo: message\r\n });\r\n }\r\n }\r\n catch (err) {\r\n throw ERROR_FACTORY.create(\"token-unsubscribe-failed\" /* TOKEN_UNSUBSCRIBE_FAILED */, {\r\n errorInfo: err\r\n });\r\n }\r\n}\r\nfunction getEndpoint({ projectId }) {\r\n return `${ENDPOINT}/projects/${projectId}/registrations`;\r\n}\r\nasync function getHeaders({ appConfig, installations }) {\r\n const authToken = await installations.getToken();\r\n return new Headers({\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'x-goog-api-key': appConfig.apiKey,\r\n 'x-goog-firebase-installations-auth': `FIS ${authToken}`\r\n });\r\n}\r\nfunction getBody({ p256dh, auth, endpoint, vapidKey }) {\r\n const body = {\r\n web: {\r\n endpoint,\r\n auth,\r\n p256dh\r\n }\r\n };\r\n if (vapidKey !== DEFAULT_VAPID_KEY) {\r\n body.web.applicationPubKey = vapidKey;\r\n }\r\n return body;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n// UpdateRegistration will be called once every week.\r\nconst TOKEN_EXPIRATION_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\r\nasync function getTokenInternal(messaging) {\r\n const pushSubscription = await getPushSubscription(messaging.swRegistration, messaging.vapidKey);\r\n const subscriptionOptions = {\r\n vapidKey: messaging.vapidKey,\r\n swScope: messaging.swRegistration.scope,\r\n endpoint: pushSubscription.endpoint,\r\n auth: arrayToBase64(pushSubscription.getKey('auth')),\r\n p256dh: arrayToBase64(pushSubscription.getKey('p256dh'))\r\n };\r\n const tokenDetails = await dbGet(messaging.firebaseDependencies);\r\n if (!tokenDetails) {\r\n // No token, get a new one.\r\n return getNewToken(messaging.firebaseDependencies, subscriptionOptions);\r\n }\r\n else if (!isTokenValid(tokenDetails.subscriptionOptions, subscriptionOptions)) {\r\n // Invalid token, get a new one.\r\n try {\r\n await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);\r\n }\r\n catch (e) {\r\n // Suppress errors because of #2364\r\n console.warn(e);\r\n }\r\n return getNewToken(messaging.firebaseDependencies, subscriptionOptions);\r\n }\r\n else if (Date.now() >= tokenDetails.createTime + TOKEN_EXPIRATION_MS) {\r\n // Weekly token refresh\r\n return updateToken(messaging, {\r\n token: tokenDetails.token,\r\n createTime: Date.now(),\r\n subscriptionOptions\r\n });\r\n }\r\n else {\r\n // Valid token, nothing to do.\r\n return tokenDetails.token;\r\n }\r\n}\r\n/**\r\n * This method deletes the token from the database, unsubscribes the token from FCM, and unregisters\r\n * the push subscription if it exists.\r\n */\r\nasync function deleteTokenInternal(messaging) {\r\n const tokenDetails = await dbGet(messaging.firebaseDependencies);\r\n if (tokenDetails) {\r\n await requestDeleteToken(messaging.firebaseDependencies, tokenDetails.token);\r\n await dbRemove(messaging.firebaseDependencies);\r\n }\r\n // Unsubscribe from the push subscription.\r\n const pushSubscription = await messaging.swRegistration.pushManager.getSubscription();\r\n if (pushSubscription) {\r\n return pushSubscription.unsubscribe();\r\n }\r\n // If there's no SW, consider it a success.\r\n return true;\r\n}\r\nasync function updateToken(messaging, tokenDetails) {\r\n try {\r\n const updatedToken = await requestUpdateToken(messaging.firebaseDependencies, tokenDetails);\r\n const updatedTokenDetails = Object.assign(Object.assign({}, tokenDetails), { token: updatedToken, createTime: Date.now() });\r\n await dbSet(messaging.firebaseDependencies, updatedTokenDetails);\r\n return updatedToken;\r\n }\r\n catch (e) {\r\n await deleteTokenInternal(messaging);\r\n throw e;\r\n }\r\n}\r\nasync function getNewToken(firebaseDependencies, subscriptionOptions) {\r\n const token = await requestGetToken(firebaseDependencies, subscriptionOptions);\r\n const tokenDetails = {\r\n token,\r\n createTime: Date.now(),\r\n subscriptionOptions\r\n };\r\n await dbSet(firebaseDependencies, tokenDetails);\r\n return tokenDetails.token;\r\n}\r\n/**\r\n * Gets a PushSubscription for the current user.\r\n */\r\nasync function getPushSubscription(swRegistration, vapidKey) {\r\n const subscription = await swRegistration.pushManager.getSubscription();\r\n if (subscription) {\r\n return subscription;\r\n }\r\n return swRegistration.pushManager.subscribe({\r\n userVisibleOnly: true,\r\n // Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key\r\n // submitted to pushManager#subscribe must be of type Uint8Array.\r\n applicationServerKey: base64ToArray(vapidKey)\r\n });\r\n}\r\n/**\r\n * Checks if the saved tokenDetails object matches the configuration provided.\r\n */\r\nfunction isTokenValid(dbOptions, currentOptions) {\r\n const isVapidKeyEqual = currentOptions.vapidKey === dbOptions.vapidKey;\r\n const isEndpointEqual = currentOptions.endpoint === dbOptions.endpoint;\r\n const isAuthEqual = currentOptions.auth === dbOptions.auth;\r\n const isP256dhEqual = currentOptions.p256dh === dbOptions.p256dh;\r\n return isVapidKeyEqual && isEndpointEqual && isAuthEqual && isP256dhEqual;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction externalizePayload(internalPayload) {\r\n const payload = {\r\n from: internalPayload.from,\r\n // eslint-disable-next-line camelcase\r\n collapseKey: internalPayload.collapse_key,\r\n // eslint-disable-next-line camelcase\r\n messageId: internalPayload.fcmMessageId\r\n };\r\n propagateNotificationPayload(payload, internalPayload);\r\n propagateDataPayload(payload, internalPayload);\r\n propagateFcmOptions(payload, internalPayload);\r\n return payload;\r\n}\r\nfunction propagateNotificationPayload(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.notification) {\r\n return;\r\n }\r\n payload.notification = {};\r\n const title = messagePayloadInternal.notification.title;\r\n if (!!title) {\r\n payload.notification.title = title;\r\n }\r\n const body = messagePayloadInternal.notification.body;\r\n if (!!body) {\r\n payload.notification.body = body;\r\n }\r\n const image = messagePayloadInternal.notification.image;\r\n if (!!image) {\r\n payload.notification.image = image;\r\n }\r\n}\r\nfunction propagateDataPayload(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.data) {\r\n return;\r\n }\r\n payload.data = messagePayloadInternal.data;\r\n}\r\nfunction propagateFcmOptions(payload, messagePayloadInternal) {\r\n if (!messagePayloadInternal.fcmOptions) {\r\n return;\r\n }\r\n payload.fcmOptions = {};\r\n const link = messagePayloadInternal.fcmOptions.link;\r\n if (!!link) {\r\n payload.fcmOptions.link = link;\r\n }\r\n // eslint-disable-next-line camelcase\r\n const analyticsLabel = messagePayloadInternal.fcmOptions.analytics_label;\r\n if (!!analyticsLabel) {\r\n payload.fcmOptions.analyticsLabel = analyticsLabel;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction isConsoleMessage(data) {\r\n // This message has a campaign ID, meaning it was sent using the Firebase Console.\r\n return typeof data === 'object' && !!data && CONSOLE_CAMPAIGN_ID in data;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n_mergeStrings('hts/frbslgigp.ogepscmv/ieo/eaylg', 'tp:/ieaeogn-agolai.o/1frlglgc/o');\r\n_mergeStrings('AzSCbw63g1R0nCw85jG8', 'Iaya3yLKwmgvh7cF0q4');\r\nfunction _mergeStrings(s1, s2) {\r\n const resultArray = [];\r\n for (let i = 0; i < s1.length; i++) {\r\n resultArray.push(s1.charAt(i));\r\n if (i < s2.length) {\r\n resultArray.push(s2.charAt(i));\r\n }\r\n }\r\n return resultArray.join('');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction extractAppConfig(app) {\r\n if (!app || !app.options) {\r\n throw getMissingValueError('App Configuration Object');\r\n }\r\n if (!app.name) {\r\n throw getMissingValueError('App Name');\r\n }\r\n // Required app config keys\r\n const configKeys = [\r\n 'projectId',\r\n 'apiKey',\r\n 'appId',\r\n 'messagingSenderId'\r\n ];\r\n const { options } = app;\r\n for (const keyName of configKeys) {\r\n if (!options[keyName]) {\r\n throw getMissingValueError(keyName);\r\n }\r\n }\r\n return {\r\n appName: app.name,\r\n projectId: options.projectId,\r\n apiKey: options.apiKey,\r\n appId: options.appId,\r\n senderId: options.messagingSenderId\r\n };\r\n}\r\nfunction getMissingValueError(valueName) {\r\n return ERROR_FACTORY.create(\"missing-app-config-values\" /* MISSING_APP_CONFIG_VALUES */, {\r\n valueName\r\n });\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nclass MessagingService {\r\n constructor(app, installations, analyticsProvider) {\r\n // logging is only done with end user consent. Default to false.\r\n this.deliveryMetricsExportedToBigQueryEnabled = false;\r\n this.onBackgroundMessageHandler = null;\r\n this.onMessageHandler = null;\r\n this.logEvents = [];\r\n this.isLogServiceStarted = false;\r\n const appConfig = extractAppConfig(app);\r\n this.firebaseDependencies = {\r\n app,\r\n appConfig,\r\n installations,\r\n analyticsProvider\r\n };\r\n }\r\n _delete() {\r\n return Promise.resolve();\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function registerDefaultSw(messaging) {\r\n try {\r\n messaging.swRegistration = await navigator.serviceWorker.register(DEFAULT_SW_PATH, {\r\n scope: DEFAULT_SW_SCOPE\r\n });\r\n // The timing when browser updates sw when sw has an update is unreliable from experiment. It\r\n // leads to version conflict when the SDK upgrades to a newer version in the main page, but sw\r\n // is stuck with the old version. For example,\r\n // https://github.com/firebase/firebase-js-sdk/issues/2590 The following line reliably updates\r\n // sw if there was an update.\r\n messaging.swRegistration.update().catch(() => {\r\n /* it is non blocking and we don't care if it failed */\r\n });\r\n }\r\n catch (e) {\r\n throw ERROR_FACTORY.create(\"failed-service-worker-registration\" /* FAILED_DEFAULT_REGISTRATION */, {\r\n browserErrorMessage: e.message\r\n });\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function updateSwReg(messaging, swRegistration) {\r\n if (!swRegistration && !messaging.swRegistration) {\r\n await registerDefaultSw(messaging);\r\n }\r\n if (!swRegistration && !!messaging.swRegistration) {\r\n return;\r\n }\r\n if (!(swRegistration instanceof ServiceWorkerRegistration)) {\r\n throw ERROR_FACTORY.create(\"invalid-sw-registration\" /* INVALID_SW_REGISTRATION */);\r\n }\r\n messaging.swRegistration = swRegistration;\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function updateVapidKey(messaging, vapidKey) {\r\n if (!!vapidKey) {\r\n messaging.vapidKey = vapidKey;\r\n }\r\n else if (!messaging.vapidKey) {\r\n messaging.vapidKey = DEFAULT_VAPID_KEY;\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function getToken$1(messaging, options) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n if (Notification.permission === 'default') {\r\n await Notification.requestPermission();\r\n }\r\n if (Notification.permission !== 'granted') {\r\n throw ERROR_FACTORY.create(\"permission-blocked\" /* PERMISSION_BLOCKED */);\r\n }\r\n await updateVapidKey(messaging, options === null || options === void 0 ? void 0 : options.vapidKey);\r\n await updateSwReg(messaging, options === null || options === void 0 ? void 0 : options.serviceWorkerRegistration);\r\n return getTokenInternal(messaging);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2019 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function logToScion(messaging, messageType, data) {\r\n const eventType = getEventType(messageType);\r\n const analytics = await messaging.firebaseDependencies.analyticsProvider.get();\r\n analytics.logEvent(eventType, {\r\n /* eslint-disable camelcase */\r\n message_id: data[CONSOLE_CAMPAIGN_ID],\r\n message_name: data[CONSOLE_CAMPAIGN_NAME],\r\n message_time: data[CONSOLE_CAMPAIGN_TIME],\r\n message_device_time: Math.floor(Date.now() / 1000)\r\n /* eslint-enable camelcase */\r\n });\r\n}\r\nfunction getEventType(messageType) {\r\n switch (messageType) {\r\n case MessageType.NOTIFICATION_CLICKED:\r\n return 'notification_open';\r\n case MessageType.PUSH_RECEIVED:\r\n return 'notification_foreground';\r\n default:\r\n throw new Error();\r\n }\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function messageEventListener(messaging, event) {\r\n const internalPayload = event.data;\r\n if (!internalPayload.isFirebaseMessaging) {\r\n return;\r\n }\r\n if (messaging.onMessageHandler &&\r\n internalPayload.messageType === MessageType.PUSH_RECEIVED) {\r\n if (typeof messaging.onMessageHandler === 'function') {\r\n messaging.onMessageHandler(externalizePayload(internalPayload));\r\n }\r\n else {\r\n messaging.onMessageHandler.next(externalizePayload(internalPayload));\r\n }\r\n }\r\n // Log to Scion if applicable\r\n const dataPayload = internalPayload.data;\r\n if (isConsoleMessage(dataPayload) &&\r\n dataPayload[CONSOLE_CAMPAIGN_ANALYTICS_ENABLED] === '1') {\r\n await logToScion(messaging, internalPayload.messageType, dataPayload);\r\n }\r\n}\n\nconst name = \"@firebase/messaging\";\nconst version = \"0.9.8\";\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nconst WindowMessagingFactory = (container) => {\r\n const messaging = new MessagingService(container.getProvider('app').getImmediate(), container.getProvider('installations-internal').getImmediate(), container.getProvider('analytics-internal'));\r\n navigator.serviceWorker.addEventListener('message', e => messageEventListener(messaging, e));\r\n return messaging;\r\n};\r\nconst WindowMessagingInternalFactory = (container) => {\r\n const messaging = container\r\n .getProvider('messaging')\r\n .getImmediate();\r\n const messagingInternal = {\r\n getToken: (options) => getToken$1(messaging, options)\r\n };\r\n return messagingInternal;\r\n};\r\nfunction registerMessagingInWindow() {\r\n _registerComponent(new Component('messaging', WindowMessagingFactory, \"PUBLIC\" /* PUBLIC */));\r\n _registerComponent(new Component('messaging-internal', WindowMessagingInternalFactory, \"PRIVATE\" /* PRIVATE */));\r\n registerVersion(name, version);\r\n // BUILD_TARGET will be replaced by values like esm5, esm2017, cjs5, etc during the compilation\r\n registerVersion(name, version, 'esm2017');\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Checks if all required APIs exist in the browser.\r\n * @returns a Promise that resolves to a boolean.\r\n *\r\n * @public\r\n */\r\nasync function isWindowSupported() {\r\n try {\r\n // This throws if open() is unsupported, so adding it to the conditional\r\n // statement below can cause an uncaught error.\r\n await validateIndexedDBOpenable();\r\n }\r\n catch (e) {\r\n return false;\r\n }\r\n // firebase-js-sdk/issues/2393 reveals that idb#open in Safari iframe and Firefox private browsing\r\n // might be prohibited to run. In these contexts, an error would be thrown during the messaging\r\n // instantiating phase, informing the developers to import/call isSupported for special handling.\r\n return (typeof window !== 'undefined' &&\r\n isIndexedDBAvailable() &&\r\n areCookiesEnabled() &&\r\n 'serviceWorker' in navigator &&\r\n 'PushManager' in window &&\r\n 'Notification' in window &&\r\n 'fetch' in window &&\r\n ServiceWorkerRegistration.prototype.hasOwnProperty('showNotification') &&\r\n PushSubscription.prototype.hasOwnProperty('getKey'));\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nasync function deleteToken$1(messaging) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n if (!messaging.swRegistration) {\r\n await registerDefaultSw(messaging);\r\n }\r\n return deleteTokenInternal(messaging);\r\n}\n\n/**\r\n * @license\r\n * Copyright 2020 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\nfunction onMessage$1(messaging, nextOrObserver) {\r\n if (!navigator) {\r\n throw ERROR_FACTORY.create(\"only-available-in-window\" /* AVAILABLE_IN_WINDOW */);\r\n }\r\n messaging.onMessageHandler = nextOrObserver;\r\n return () => {\r\n messaging.onMessageHandler = null;\r\n };\r\n}\n\n/**\r\n * @license\r\n * Copyright 2017 Google LLC\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n * http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n/**\r\n * Retrieves a Firebase Cloud Messaging instance.\r\n *\r\n * @returns The Firebase Cloud Messaging instance associated with the provided firebase app.\r\n *\r\n * @public\r\n */\r\nfunction getMessagingInWindow(app = getApp()) {\r\n // Conscious decision to make this async check non-blocking during the messaging instance\r\n // initialization phase for performance consideration. An error would be thrown latter for\r\n // developer's information. Developers can then choose to import and call `isSupported` for\r\n // special handling.\r\n isWindowSupported().then(isSupported => {\r\n // If `isWindowSupported()` resolved, but returned false.\r\n if (!isSupported) {\r\n throw ERROR_FACTORY.create(\"unsupported-browser\" /* UNSUPPORTED_BROWSER */);\r\n }\r\n }, _ => {\r\n // If `isWindowSupported()` rejected.\r\n throw ERROR_FACTORY.create(\"indexed-db-unsupported\" /* INDEXED_DB_UNSUPPORTED */);\r\n });\r\n return _getProvider(getModularInstance(app), 'messaging').getImmediate();\r\n}\r\n/**\r\n * Subscribes the {@link Messaging} instance to push notifications. Returns an Firebase Cloud\r\n * Messaging registration token that can be used to send push messages to that {@link Messaging}\r\n * instance.\r\n *\r\n * If a notification permission isn't already granted, this method asks the user for permission. The\r\n * returned promise rejects if the user does not allow the app to show notifications.\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n * @param options - Provides an optional vapid key and an optinoal service worker registration\r\n *\r\n * @returns The promise resolves with an FCM registration token.\r\n *\r\n * @public\r\n */\r\nasync function getToken(messaging, options) {\r\n messaging = getModularInstance(messaging);\r\n return getToken$1(messaging, options);\r\n}\r\n/**\r\n * Deletes the registration token associated with this {@link Messaging} instance and unsubscribes\r\n * the {@link Messaging} instance from the push subscription.\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n *\r\n * @returns The promise resolves when the token has been successfully deleted.\r\n *\r\n * @public\r\n */\r\nfunction deleteToken(messaging) {\r\n messaging = getModularInstance(messaging);\r\n return deleteToken$1(messaging);\r\n}\r\n/**\r\n * When a push message is received and the user is currently on a page for your origin, the\r\n * message is passed to the page and an `onMessage()` event is dispatched with the payload of\r\n * the push message.\r\n *\r\n *\r\n * @param messaging - The {@link Messaging} instance.\r\n * @param nextOrObserver - This function, or observer object with `next` defined,\r\n * is called when a message is received and the user is currently viewing your page.\r\n * @returns To stop listening for messages execute this returned function.\r\n *\r\n * @public\r\n */\r\nfunction onMessage(messaging, nextOrObserver) {\r\n messaging = getModularInstance(messaging);\r\n return onMessage$1(messaging, nextOrObserver);\r\n}\n\n/**\r\n * Firebase Cloud Messaging\r\n *\r\n * @packageDocumentation\r\n */\r\nregisterMessagingInWindow();\n\nexport { deleteToken, getMessagingInWindow as getMessaging, getToken, isWindowSupported as isSupported, onMessage };\n//# sourceMappingURL=index.esm2017.js.map\n","const __WEBPACK_NAMESPACE_OBJECT__ = Tinode;","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Alert extends React.PureComponent {\n render() {\n return this.props.visible ?\n
\n
\n
{this.props.title}
\n
{this.props.content}
\n
\n {this.props.onReject ?\n \n :\n null\n }\n \n
\n
\n
\n :\n null;\n }\n};\n","// This is a generated file. Don't edit.\n\nexport const PACKAGE_VERSION = \"0.19.0-alpha1\";\n","import { PACKAGE_VERSION } from './version.js';\n\n// Name of this application, used in the User-Agent.\nexport const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.17');\n\n// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own\nexport const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';\n\n// The array of possible hosts to connect to.\nexport const KNOWN_HOSTS = {hosted: 'web.tinode.co', local: 'localhost:6060'};\n\n// Default host name and port to connect to.\nexport const DEFAULT_HOST = KNOWN_HOSTS.hosted;\n\n// Enable console logging.\nexport const LOGGING_ENABLED = true;\n\n// Minimum time between two keypress notifications, milliseconds.\nexport const KEYPRESS_DELAY = 3*1000;\n// Delay before sending a {note} for reciving a message, milliseconds.\nexport const RECEIVED_DELAY = 500;\n// Delay before sending a read notification, milliseconds.\nexport const READ_DELAY = 1000;\n\n// The default shortest allowed tag length. Matches the value on the server.\nexport const MIN_TAG_LENGTH = 2;\n// The default greatest allowed tag length. Matches the value on the server.\nexport const MAX_TAG_LENGTH = 96;\n// The default maximum number of tags allowed. Matches the value on the server.\nexport const MAX_TAG_COUNT = 16;\n\n// Access mode for P2P subscriptions initiated by the current user.\nexport const DEFAULT_P2P_ACCESS_MODE = 'JRWPS';\n// Access mode for new group topics created by the current user.\nexport const NEW_GRP_ACCESS_MODE = 'JRWPSAO';\n// Access mode for a channel.\nexport const CHANNEL_ACCESS_MODE = 'JR';\n\n// Access mode for no access.\nexport const NO_ACCESS_MODE = 'N';\n\n// Mediaquery breakpoint between desktop and mobile, in px. Should match the value\n// in @media (max-size: 640px) in base.css\nexport const MEDIA_BREAKPOINT = 640;\n// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px.\nexport const REM_SIZE = 13;\n\n// Size of the avatar image: when image dimensions are greater or the image is not square, it's resized to\n// a square of this size or less.\nexport const AVATAR_SIZE = 384;\n\n// Maximum size of an avatar in bytes for sending in-band. Bigger avatars will be sent out of band (as uploads).\nexport const MAX_AVATAR_BYTES = 4096;\n\n// Size of the broken_image shown in MessagesView\nexport const BROKEN_IMAGE_SIZE = 32;\n\n// Number of chat messages to fetch in one call.\nexport const MESSAGES_PAGE = 24;\n\n// Default maximum in-band (included directly into the message) attachment size which fits into\n// a message of 256K in size. Used when the server-provided value is unavailable. The actual\n// binary size of the attachment should be smaller due to base64 encoding expansion and some overhead,\n// for instance 1024 bytes.\n// Increase this limit to a greater value in production, if desired. Also increase\n// max_message_size in the server config.\nexport const MAX_INBAND_ATTACHMENT_SIZE = 262144;\n\n// Default absolute maximum attachment size to be used with the server = 8MB.\n// Used when the server-provided value is unavailable. Increase to something like 100MB in production.\nexport const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23;\n\n// Maximum allowed linear dimension (pixels) of an image sent inline. Larger images will be shrunk\n// to make the larger dimension fit under this size. You may want to adjust it to 1600 or\n// 2400 for production.\nexport const MAX_IMAGE_DIM = 1024;\n\n// Linear dimensions of image preview: shrink image under this size for previews.\nexport const IMAGE_PREVIEW_DIM = 64;\n\n// Linear dimensions of image thumbnail: shrink image under this size for thumbnails in reply quote previews.\nexport const IMAGE_THUMBNAIL_DIM = 36;\n\n// Maximum number of online users to be shown in a topic title bar. Others will be\n// hidden under \"+X more\"\nexport const MAX_ONLINE_IN_TOPIC = 4;\n\n// Maximum length of user name, topic title, and private comment.\nexport const MAX_TITLE_LENGTH = 60;\n// Maximum length of topic description.\nexport const MAX_TOPIC_DESCRIPTION_LENGTH = 360;\n\n// Length of message previews in chat list, in characters.\nexport const MESSAGE_PREVIEW_LENGTH = 80;\n\n// Length of a quote in quoted reply (for outgoing messages).\nexport const QUOTED_REPLY_LENGTH = 30;\n// Length of a preview of a forwarded message.\nexport const FORWARDED_PREVIEW_LENGTH = 84;\n\n// Link for \"Contact Us\".\nexport const LINK_CONTACT_US = 'email:support@tinode.co';\n\n// Link to Privacy Policy.\nexport const LINK_PRIVACY_POLICY = 'https://tinode.co/privacy.html';\n\n// Link to Terms of Service.\nexport const LINK_TERMS_OF_SERVICE = 'https://tinode.co/terms.html';\n","// Context Menu: popup/dropdown menu.\nimport React from 'react';\nimport { injectIntl, defineMessages } from 'react-intl';\n\nimport { REM_SIZE } from '../config.js';\n\nconst messages = defineMessages({\n info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n },\n clear_messages: {\n id: 'menu_item_clear_messages',\n defaultMessage: 'Clear messages',\n description: 'Delete all messages'\n },\n clear_for_all: {\n id: 'menu_item_clear_messages_for_all',\n defaultMessage: 'Clear for All',\n description: 'Delete all message(s) for all members'\n },\n delete: {\n id: 'menu_item_delete',\n defaultMessage: 'Delete',\n description: 'Delete selected messages'\n },\n delete_for_all: {\n id: 'menu_item_delete_for_all',\n defaultMessage: 'Delete for All',\n description: 'Delete selected message(s) for all members'\n },\n send_retry: {\n id: 'menu_item_send_retry',\n defaultMessage: 'Retry',\n description: 'Retry sending message'\n },\n mute: {\n id: 'menu_item_mute',\n defaultMessage: 'Mute',\n description: 'Turn off notifications'\n },\n unmute: {\n id: 'menu_item_unmute',\n defaultMessage: 'Unmute',\n description: 'Turn notifications on'\n },\n reply: {\n id: 'menu_item_reply',\n defaultMessage: 'Reply',\n description: 'Reply to message'\n },\n forward: {\n id: 'menu_item_forward',\n defaultMessage: 'Forward',\n description: 'Forward message'\n },\n topic_delete: {\n id: 'menu_item_delete_topic',\n defaultMessage: 'Delete',\n description: 'Delete entire topic'\n },\n topic_delete_warning: {\n id: 'topic_delete_warning',\n defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',\n description: 'Alert warning when deleting entire topic'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n unblock: {\n id: 'menu_item_unblock',\n defaultMessage: 'Unblock',\n description: 'Unblock topic or user'\n },\n // Generic string suitable for either topic or user.\n block: {\n id: 'menu_item_block',\n defaultMessage: 'Block',\n description: 'Block topic or user'\n },\n topic_block_warning: {\n id: 'topic_block_warning',\n defaultMessage: 'Are you sure you want to block this conversation?',\n description: 'Alert warning when blocking a topic.'\n },\n member_delete: {\n id: 'menu_item_member_delete',\n defaultMessage: 'Remove',\n description: 'Remove user from topic'\n },\n archive: {\n id: 'menu_item_archive_topic',\n defaultMessage: 'Archive',\n description: 'Move topic from the list of active chats to archive'\n },\n unarchive: {\n id: 'menu_item_restore_topic',\n defaultMessage: 'Restore',\n description: 'Restore topic from archive'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n});\n\nclass ContextMenu extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n const {formatMessage} = props.intl;\n\n this.handlePageClick = this.handlePageClick.bind(this);\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this.handleClick = this.handleClick.bind(this);\n\n // Preconfigured menu items.\n this.MenuItems = {\n 'topic_info': {\n id: 'topic_info',\n title: formatMessage(messages.info),\n handler: null\n },\n 'messages_clear': {\n id: 'messages_clear',\n title: formatMessage(messages.clear_messages),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_messages), // title\n formatMessage(messages.clear_messages_warning), // content\n (() => { this.deleteMessages(true, false, params, errorHandler); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'messages_clear_hard': {\n id: 'messages_clear_hard',\n title: formatMessage(messages.clear_for_all),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_for_all), // title\n formatMessage(message.delete_messages_warning), // content\n (() => { return this.deleteMessages(true, true, params, errorHandler); }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'message_delete': {\n id: 'message_delete',\n title: formatMessage(messages.delete),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, false, params, errorHandler);\n }\n },\n 'message_delete_hard': {\n id: 'message_delete_hard',\n title: formatMessage(messages.delete_for_all),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, true, params, errorHandler);\n }\n },\n 'menu_item_send_retry': {\n id: 'menu_item_send_retry',\n title: formatMessage(messages.send_retry),\n handler: (params, errorHandler) => {\n return this.retryMessage(params, errorHandler);\n }\n },\n 'menu_item_reply': {\n id: 'menu_item_reply',\n title: formatMessage(messages.reply),\n handler: (params, errorHandler) => {\n return this.replyToMessage(params, errorHandler);\n }\n },\n 'menu_item_forward': {\n id: 'menu_item_forward',\n title: formatMessage(messages.forward),\n handler: () => {} /* the action is taken directly in tinode-web */\n },\n 'topic_unmute': {\n id: 'topic_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'topic_mute': {\n id: 'topic_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'topic_unblock': {\n id: 'topic_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n 'topic_block': {\n id: 'topic_block',\n title: formatMessage(messages.block),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.block), // title\n formatMessage(messages.topic_block_warning), // content\n (() => {\n return this.topicPermissionSetter('-JP', params, errorHandler).then((ctrl) => {\n this.props.onTopicRemoved(params.topicName);\n return ctrl;\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_delete': {\n id: 'topic_delete',\n title: formatMessage(messages.topic_delete),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.topic_delete), // title\n formatMessage(messages.topic_delete_warning), // content\n (() => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.delTopic(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_archive': {\n id: 'topic_archive',\n title: formatMessage(messages.archive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'topic_restore': {\n id: 'topic_restore',\n title: formatMessage(messages.unarchive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(false).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n // menu_item_edit_permissions is defined elsewhere.\n 'permissions': {\n id: 'permissions',\n title: formatMessage(messages.edit_permissions),\n handler: null\n },\n 'member_delete': {\n id: 'member_delete',\n title: formatMessage(messages.member_delete),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic || !params.user) {\n console.log(\"Topic or user not found: '\" + params.topicName + \"', '\" + params.user + \"'\");\n return;\n }\n return topic.delSubscription(params.user).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'member_mute': {\n id: 'member_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'member_unmute': {\n id: 'member_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'member_block': {\n id: 'member_block',\n title: formatMessage(messages.block),\n handler: this.topicPermissionSetter.bind(this, '-JP')\n },\n 'member_unblock': {\n id: 'member_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n };\n }\n\n componentDidMount() {\n document.addEventListener('mousedown', this.handlePageClick, false);\n document.addEventListener('keyup', this.handleEscapeKey, false);\n }\n\n componentWillUnmount() {\n document.removeEventListener('mousedown', this.handlePageClick, false);\n document.removeEventListener('keyup', this.handleEscapeKey, false);\n }\n\n handlePageClick(e) {\n if (this.selfRef.current.contains(e.target)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n }\n\n handleEscapeKey(e) {\n if (e.keyCode === 27) {\n this.props.hide();\n }\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n let item = this.props.items[e.currentTarget.dataset.id];\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n\n if (!item) {\n console.error(\"Invalid menu item ID\", e.currentTarget.dataset.id);\n } else {\n this.props.onAction(\n item.id,\n item.handler(this.props.params, this.props.onError),\n this.props.params);\n }\n }\n\n // Menu Actions\n\n deleteMessages(all, hard, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n\n // We don't know if the message is still pending (e.g. attachment is being uploaded),\n // so try cancelling first. No harm if we can't cancel.\n // The message can be cancelled if transmission to the server has not\n // started yet or if the message send has failed.\n if (!all && topic.cancelSend(params.seq)) {\n return;\n }\n // Can't cancel. Delete instead.\n const promise = all ?\n topic.delMessagesAll(hard) :\n topic.delMessagesList([params.seq], hard);\n\n return promise.catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Retries sending failed message.\n retryMessage(params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n // Remove the existing message entry.\n if (!topic || !topic.flushMessage(params.seq)) {\n return;\n }\n const msg = topic.createMessage(params.content, false);\n return topic.publishDraft(msg).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Function is used by context menu to set permissions.\n topicPermissionSetter(mode, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found\", params.topicName);\n return;\n }\n\n let result = topic.updateMode(params.user, mode);\n if (errorHandler) {\n result = result.catch((err) => {\n errorHandler(err.message, 'err');\n });\n }\n return result;\n }\n\n replyToMessage(params, errorHandler) {\n params.pickReply(params.seq, params.content, params.userFrom, params.userName, errorHandler);\n }\n\n render() {\n const menu = [];\n let count = 0;\n this.props.items.map((item) => {\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n if (item && item.title) {\n menu.push(\n item.title == '-' ?\n \n :\n {item.title}\n );\n }\n count++;\n });\n\n // Ensure that menu is inside the app-container.\n const hSize = 12 * REM_SIZE;\n const vSize = REM_SIZE * (0.7 + menu.length * 2.5);\n const left = (this.props.bounds.right - this.props.clickAt.x < hSize) ?\n (this.props.clickAt.x - this.props.bounds.left - hSize) :\n (this.props.clickAt.x - this.props.bounds.left);\n const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ?\n (this.props.clickAt.y - this.props.bounds.top - vSize) :\n (this.props.clickAt.y - this.props.bounds.top);\n\n const position = {\n left: left + 'px',\n top: top + 'px'\n };\n\n return (\n \n );\n }\n}\n\nexport default injectIntl(ContextMenu);\n","import React from 'react';\n\nconst icon_mapping = {'muted': 'notifications_off', 'banned': 'block', 'staff': 'verified_user'};\n\n/* Contact's labels: [you], or icons [muted], [blocked] */\nexport default class ContactBadges extends React.PureComponent {\n render() {\n let badges = null;\n if (this.props.badges && this.props.badges.length > 0) {\n badges = [];\n this.props.badges.map((b) => {\n const color = (b.color ? ' ' + b.color : '');\n if (b.icon) {\n badges.push({icon_mapping[b.icon] || b.icon});\n } else {\n badges.push({b.name});\n }\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n","// Short representation of time in the past.\nexport function shortDateFormat(then, locale) {\n locale = locale || window.navigator.userLanguage || window.navigator.language;\n const now = new Date();\n if (then.getFullYear() == now.getFullYear()) {\n // Same year.\n if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) {\n // Same month and day, show time only.\n return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'});\n }\n // Different month and/or day, show month day, time.\n return then.toLocaleDateString(locale,\n {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'});\n }\n // Different year: just show the date.\n return then.toLocaleDateString(locale, {year: 'numeric', month: 'short', day: 'numeric'});\n}\n\n// Convert seconds to minutes:seconds, i.e. 156 sec -> 2:36.\n// If fixedMins is true, then minutes are represented by at least two digits.\nexport function secondsToTime(seconds, fixedMin) {\n let min = Math.floor(seconds / 60);\n if (fixedMin) {\n min = min < 10 ? `0${min}` : min;\n }\n let sec = (seconds % 60) | 0;\n sec = sec < 10 ? `0${sec}` : sec;\n return `${min}:${sec}`;\n}\n\n// Convert a number of bytes to human-readable format.\nexport function bytesToHumanSize(bytes) {\n if (!bytes || bytes == 0) {\n return '0 Bytes';\n }\n\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const bucket = Math.min(Math.floor(Math.log2(bytes) / 10) | 0, sizes.length-1);\n const count = bytes / Math.pow(1024, bucket);\n const round = bucket > 0 ? (count < 3 ? 2 : (count < 30 ? 1 : 0)) : 0;\n return count.toFixed(round) + ' ' + sizes[bucket];\n}\n\n// Shorten a file name to be under maxLength by clipping out the middle.\nexport function shortenFileName(filename, maxLength) {\n if (typeof filename != 'string') {\n return filename;\n }\n return filename.length > maxLength ?\n filename.slice(0, maxLength/2 - 1) + '…' + filename.slice(1 - maxLength/2) : filename;\n}\n\n// Get 32 bit integer hash value for a string. Ideally it should produce the same value\n// as Java's String#hash().\nfunction stringToColorHash(value) {\n let hash = 0;\n value = '' + value;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) - hash) + value.charCodeAt(i);\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash;\n}\n\n// User avatar letter color id.\nexport function letterTileColorId(userId) {\n return Math.abs(stringToColorHash(userId)) % 16;\n}\n\n// Converts user or topic ID to a CSS color class. Ideally it should produce the same color value as Java version.\n// params:\n// - {string} id: user or topic ID\n// - {boolean} light: light or dark version of the color.\n// - {boolean} fg: foreground (text) or background color.\nexport function idToColorClass(id, light, fg) {\n return (light ? 'lt-' : 'dk-') + (fg ? 'fg-' : 'bg-') + letterTileColorId(id);\n}\n","// Odds and ends\n\nimport Tinode from 'tinode-sdk';\n\n// Make shortcut icon appear with a green dot + show unread count in title.\nexport function updateFavicon(count) {\n const oldIcon = document.getElementById('shortcut-icon');\n const head = document.head || document.getElementsByTagName('head')[0];\n const newIcon = document.createElement('link');\n newIcon.type = 'image/png';\n newIcon.id = 'shortcut-icon';\n newIcon.rel = 'shortcut icon';\n newIcon.href = 'img/logo32x32' + (count > 0 ? 'a' : '') + '.png';\n if (oldIcon) {\n head.removeChild(oldIcon);\n }\n head.appendChild(newIcon);\n\n document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode';\n}\n\n// Create theCard which represents user's or topic's \"public\" info.\nexport function theCard(fn, imageUrl, imageMimeType, note) {\n let card = null;\n fn = fn && fn.trim();\n note = note && note.trim();\n\n if (fn) {\n card = {\n fn: fn\n };\n }\n\n if (typeof note == 'string') {\n card = card || {};\n card.note = note ? note : Tinode.DEL_CHAR;\n }\n\n if (imageUrl) {\n card = card || {};\n let mimeType = imageMimeType;\n // Is this a data URL \"data:[][;base64],\"?\n const matches = /^data:(image\\/[-a-z0-9+.]+)?(;base64)?,/i.exec(imageUrl);\n if (matches) {\n mimeType = matches[1];\n card.photo = {\n data: imageUrl.substring(imageUrl.indexOf(',') + 1),\n ref: Tinode.DEL_CHAR\n };\n } else {\n card.photo = {\n data: Tinode.DEL_CHAR,\n ref: imageUrl\n };\n }\n card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);\n }\n\n return card;\n}\n\n// Deep-shallow compare two arrays: shallow compare each element.\nexport function arrayEqual(a, b) {\n if (a === b) {\n return true;\n }\n\n if (!Array.isArray(a) || !Array.isArray(b)) {\n return false;\n }\n\n // Compare lengths first.\n if (a.length != b.length) {\n return false;\n }\n // Order of elements is ignored.\n a.sort();\n b.sort();\n for (let i = 0, l = a.length; i < l; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n// Checks (loosely) if the given string is a phone. If so, returns the phone number in a format\n// as close to E.164 as possible.\nexport function asPhone(val) {\n val = val.trim();\n if (/^(?:\\+?(\\d{1,3}))?[- (.]*(\\d{3})[- ).]*(\\d{3})[- .]*(\\d{2})[- .]*(\\d{2})?$/.test(val)) {\n return val.replace(/[- ().]*/, '');\n }\n return null;\n}\n\n// Checks (loosely) if the given string is an email. If so returns the email.\nexport function asEmail(val) {\n val = val.trim();\n if (/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$/.test(val)) {\n return val;\n }\n return null;\n}\n\n// Checks if URL is a relative url, i.e. has no 'scheme://', including the case of missing scheme '//'.\n// The scheme is expected to be RFC-compliant, e.g. [a-z][a-z0-9+.-]*\n// example.html - ok\n// https:example.com - not ok.\n// http:/example.com - not ok.\n// ' ↲ https://example.com' - not ok. (↲ means carriage return)\nexport function isUrlRelative(url) {\n return url && !/^\\s*([a-z][a-z0-9+.-]*:|\\/\\/)/im.test(url);\n}\n\n// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of\n// strings with permitted URL schemes, such as ['ftp', 'ftps']; otherwise accept http and https only.\nexport function sanitizeUrl(url, allowedSchemes) {\n if (typeof url != 'string') {\n return url;\n }\n\n // Strip control characters and whitespace. They are not valid URL characters anyway.\n url = url.replace(/[^\\x20-\\x7E]/gmi, '').trim();\n\n // Relative URLs are safe.\n // Relative URL does not start with ':', abcd123: or '//'.\n if (!/^([a-z][a-z0-9+.-]*:|\\/\\/)/i.test(url)) {\n return url;\n }\n\n // Blob URLs are safe.\n if (/^blob:http/.test(url)) {\n return url;\n }\n\n // Absolute URL. Accept only safe schemes, or no scheme.\n const schemes = Array.isArray(allowedSchemes) ? allowedSchemes.join('|') : 'http|https';\n const re = new RegExp('^((' + schemes + '):|//)', 'i');\n if (!re.test(url)) {\n return null;\n }\n\n return url;\n}\n\n// Ensure URL is suitable for
field: the URL must be a relative URL or\n// have http:, https:, blob: or data: scheme.\n// In case of data: scheme, the URL must start with a 'data:image/XXXX;base64,'.\nexport function sanitizeImageUrl(url) {\n if (!url) {\n return null;\n }\n\n const sanitizedUrl = sanitizeUrl(url);\n if (sanitizedUrl) {\n return sanitizedUrl;\n }\n\n // Is this a data: URL of an image?\n if (/data:image\\/[a-z0-9.-]+;base64,/i.test(url.trim())) {\n return url;\n }\n\n return null;\n}\n\n// Given message's received status, return name and color of a delivery indicator icon.\nexport function deliveryMarker(received) {\n switch (received) {\n case Tinode.MESSAGE_STATUS_SENDING:\n return { name: 'access_time' }; // watch face\n case Tinode.MESSAGE_STATUS_FAILED:\n return { name: 'warning', color: 'danger-color' }; // yellow icon /!\\\n case Tinode.MESSAGE_STATUS_SENT:\n return { name: 'done' }; // checkmark\n case Tinode.MESSAGE_STATUS_RECEIVED:\n return { name: 'done_all' }; // double checkmark\n case Tinode.MESSAGE_STATUS_READ:\n return { name: 'done_all', color: 'blue' }; // blue double checkmark\n }\n return null;\n}\n\n// Wraps a promise to make it cancelable.\n// The parameter can be either a promise or an error. If it's an error, the wrapped promise is\n// created in a rejected state.\nexport function cancelablePromise(promise) {\n let hasCanceled = false;\n\n const wrappedPromise = promise instanceof Error ?\n Promise.reject(promise) :\n new Promise((resolve, reject) => {\n promise.then(\n result => hasCanceled ? reject({isCanceled: true}) : resolve(result),\n error => hasCanceled ? reject({isCanceled: true}) : reject(error)\n );\n });\n\n return {\n promise: wrappedPromise,\n cancel() {\n hasCanceled = true;\n },\n };\n};\n","import React from 'react';\n\nimport Tinode from 'tinode-sdk';\n\nimport { idToColorClass } from '../lib/strformat.js';\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nexport default class LetterTile extends React.PureComponent {\n render() {\n let avatar;\n if (this.props.avatar === true) {\n const isGroup = Tinode.isGroupTopicName(this.props.topic);\n const iconColor = idToColorClass(this.props.topic, isGroup);\n if (this.props.topic && this.props.title && this.props.title.trim()) {\n const letter = this.props.title.trim().charAt(0);\n const className = 'lettertile ' + iconColor + (this.props.deleted ? ' disabled' : '');\n avatar = ()\n } else {\n const className = 'material-icons ' + iconColor + (this.props.deleted ? ' disabled' : '');\n avatar = isGroup ? group : person;\n }\n } else if (this.props.avatar) {\n const url = this.props.tinode.authorizeURL(sanitizeImageUrl(this.props.avatar));\n // If avatar image is invalid, show a placeholder.\n const className = 'avatar' + (this.props.deleted ? ' deleted' : '');\n avatar =
{e.target.onerror = null; e.target.src=\"../img/broken_image.png\"}} />;\n } else {\n avatar = null;\n }\n return avatar;\n }\n}\n","// The counter of unread messages in the topic.\nimport React from 'react';\n\nexport default class UnreadBadge extends React.PureComponent {\n render() {\n return (this.props.count > 0 ?\n {this.props.count > 9 ? \"9+\" : this.props.count}\n : null);\n }\n};\n","// Image with a placeholder which is replaced when the promise is resolved.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class LazyImage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n src: 'img/placeholder.png',\n style: Object.assign({padding: '4px'}, this.props.style),\n className: this.props.className,\n alt: this.props.alt,\n onClick: this.props.onClick,\n };\n }\n\n componentDidMount() {\n // whenDone is a wrapper around an actual promise to be able to cancel it.\n this.props.whenDone\n .promise\n .then(data => this.setState({src: data.src, style: {...this.state.style, padding: 0}}))\n .catch(() => this.setState({src: 'img/broken_image.png'}));\n }\n\n componentWillUnmount() {\n this.props.whenDone.cancel();\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.whenDone != this.props.whenDone) {\n this.setState({src: 'img/placeholder.png', style: {...this.state.style, padding: '4px'}});\n this.props.whenDone\n .promise\n .then(data => this.setState({src: data.src, style: {...this.state.style, padding: 0}}))\n .catch(() => this.setState({src: 'img/broken_image.png'}));\n }\n }\n\n render() {\n return React.createElement('img', this.state);\n }\n};\n","// File uload/download progress indicator with a cancel inside.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class FileProgress extends React.PureComponent {\n render() {\n return (\n \n );\n }\n}\n","// Image view with progress bar and a cancel button.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\n\nexport default class UploadingImage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n return (\n \n {React.createElement('img', this.props)}\n
\n \n
\n
\n );\n }\n};\n","// File and image helper functions.\n\n// Supported image MIME types and corresponding file extensions.\nexport const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];\nexport const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];\n\n// Get an URL from a theCard photo: either create a data: URL or return reference URL.\nexport function makeImageUrl(photo) {\n if (photo && typeof photo == 'object') {\n if (photo.ref) {\n return photo.ref\n }\n if (photo.data && photo.type) {\n const mime = photo.type.startsWith('image/') ? photo.type : ('image/' + photo.type);\n return 'data:' + mime + ';base64,' + photo.data;\n }\n }\n return null;\n}\n\n// Calculate linear dimensions for scaling image down to fit under a certain size.\n// Returns an object which contains destination sizes, source sizes, and offsets\n// into source (when making square images).\nexport function fitImageSize(width, height, maxWidth, maxHeight, forceSquare) {\n // Sanitize input\n width = width | 0;\n height = height | 0;\n maxWidth = maxWidth | 0;\n maxHeight = maxHeight | 0;\n\n if (width <= 0 || height <= 0 || maxWidth <= 0 || maxHeight <= 0) {\n return null;\n }\n\n if (forceSquare) {\n maxWidth = maxHeight = Math.min(maxWidth, maxHeight);\n }\n\n const scale = Math.min(\n Math.min(width, maxWidth) / width,\n Math.min(height, maxHeight) / height\n );\n\n const size = {\n dstWidth: (width * scale) | 0,\n dstHeight: (height * scale) | 0,\n };\n\n if (forceSquare) {\n // Also calculate parameters for making the image square.\n size.dstWidth = size.dstHeight = Math.min(size.dstWidth, size.dstHeight);\n size.srcWidth = size.srcHeight = Math.min(width, height);\n size.xoffset = ((width - size.srcWidth) / 2) | 0;\n size.yoffset = ((height - size.srcWidth) / 2) | 0;\n } else {\n size.xoffset = size.yoffset = 0;\n size.srcWidth = width;\n size.srcHeight = height;\n }\n return size;\n}\n\n// Ensure file's extension matches mime content type\nexport function fileNameForMime(fname, mime) {\n const idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime);\n if (idx < 0 || !fname) {\n // Unknown mime or empty name.\n return fname;\n }\n const ext = MIME_EXTENSIONS[idx];\n\n const at = fname.lastIndexOf('.');\n if (at >= 0) {\n fname = fname.substring(0, at);\n }\n return fname + '.' + ext;\n}\n\n// Scale uploaded image to fit under certain dimensions and byte size, optionally constraining to a square.\n// Returns a promise which is resolven on success or rejected on failure.\nexport function imageScaled(fileOrBlob, maxWidth, maxHeight, maxSize, forceSquare) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = function(err) {\n reject(new Error(\"Image format unrecognized\"));\n }\n img.onload = async function() {\n // Once the image is loaded, the URL is no longer needed.\n URL.revokeObjectURL(img.src);\n\n // Calculate the desired image dimensions.\n const dim = fitImageSize(img.width, img.height, maxWidth, maxHeight, forceSquare);\n if (!dim) {\n reject(new Error(\"Invalid image\"));\n return;\n }\n let canvas = document.createElement('canvas');\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n let ctx = canvas.getContext('2d');\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(img, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n\n const mime = SUPPORTED_IMAGE_FORMATS.includes(fileOrBlob.type) ? fileOrBlob.type : 'image/jpeg';\n // Generate blob to check size of the image.\n let blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n if (!blob) {\n reject(new Error(\"Unsupported image format\"));\n return;\n }\n // Ensure the image is not too large. Shrink the image keeping the aspect ratio.\n // Do nothing if maxsize is <= 0.\n while (maxSize > 0 && blob.length > maxSize) {\n dim.dstWidth = (dim.dstWidth * 0.70710678118) | 0;\n dim.dstHeight = (dim.dstHeight * 0.70710678118) | 0;\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n ctx = canvas.getContext('2d');\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(img, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n }\n\n canvas = null;\n resolve({mime: mime, blob: blob, width: dim.dstWidth, height: dim.dstHeight, name: fileNameForMime(fileOrBlob.name, mime)});\n };\n img.src = URL.createObjectURL(fileOrBlob);\n });\n}\n\n// Scale and crop image according to specified dimensions.\n// The coordinates are in unscaled image pixels, i.e. cut the rectangle first then scale it.\n// returns a promise.\nexport function imageCrop(mime, objURL, left, top, width, height, scale) {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = (err) => {\n reject(new Error(\"Image format unrecognized\"));\n };\n img.onload = () => {\n // Once the image is loaded, the URL is no longer needed.\n URL.revokeObjectURL(img.src);\n\n let canvas = document.createElement('canvas');\n canvas.width = width * scale;\n canvas.height = height * scale;\n let ctx = canvas.getContext('2d');\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(img, left, top, width, height, 0, 0, canvas.width, canvas.height);\n\n mime = SUPPORTED_IMAGE_FORMATS.includes(mime) ? mime : 'image/jpeg';\n // Generate blob to check size of the image.\n canvas.toBlob((blob) => {\n // Allow GC.\n canvas = null;\n if (blob) {\n resolve({mime: mime, blob: blob, width: width, height: height});\n } else {\n reject(new Error(\"Unsupported image format\"));\n }\n }, mime);\n };\n img.src = objURL;\n });\n}\n\n// Convert file to base64 string.\nexport function fileToBase64(file) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = (evt) => {\n reject(reader.error);\n };\n reader.onload = () => {\n resolve({mime: file.type, bits: reader.result.split(',')[1], name: file.name});\n };\n reader.readAsDataURL(file);\n });\n}\n\n// Convert Blob to base64 string. Returns a promise resolved with the base64 string and mime.\nexport function blobToBase64(blob) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onerror = (evt) => {\n reject(reader.error);\n };\n reader.onload = () => {\n resolve({mime: blob.type, bits: reader.result.split(',')[1]});\n };\n reader.readAsDataURL(blob);\n });\n}\n\n// File pasted from the clipboard. It's either an inline image or a file attachment.\nexport function filePasted(event, onImageSuccess, onAttachmentSuccess, onError) {\n const items = (event.clipboardData || event.originalEvent.clipboardData || {}).items;\n if (!Array.isArray(items)) {\n return false;\n }\n\n for (let i in items) {\n const item = items[i];\n if (item.kind === 'file') {\n const file = item.getAsFile();\n if (!file) {\n console.error(\"Failed to get file object from pasted file item\", item.kind, item.type);\n onError(\"Failed to get file object from pasted file item\");\n continue;\n }\n if (file.type && file.type.split('/')[0] == 'image') {\n onImageSuccess(file);\n } else {\n onAttachmentSuccess(file);\n }\n // Indicate that the pasted data contains a file.\n return true;\n }\n }\n // No file found.\n return false;\n}\n\n// Get mime type from data URL header.\nexport function getMimeType(header) {\n var mime = /^data:(image\\/[-+a-z0-9.]+);base64/.exec(header);\n return (mime && mime.length > 1) ? mime[1] : null;\n}\n\n// Given length of a binary object in bytes, calculate the length after\n// base64 encoding.\nexport function base64EncodedLen(n) {\n return Math.floor((n + 2) / 3) * 4;\n}\n\n// Given length of a base64-encoded object, calculate decoded size of the\n// pbject in bytes.\nexport function base64DecodedLen(n) {\n return Math.floor(n / 4) * 3;\n}\n\n// Re-encode string to standard base64 encoding with padding.\n// The string may be base64-URL encoded without the padding.\nexport function base64ReEncode(str) {\n if (str) {\n str = str.replace(/-/g, '+').replace(/_/g, '/');\n try {\n str = btoa(atob(str));\n } catch(err) {\n console.error(\"Failed to base64 re-encode string.\", err);\n str = null;\n }\n }\n return str;\n}\n\n// Convert a base64 encoded string with the provided mime type into a Blob.\nexport function base64ToBlob(str, mime) {\n if (!str) {\n return null;\n }\n\n try {\n // Make blob.\n const bin = atob(str);\n const length = bin.length;\n const buf = new ArrayBuffer(length);\n const arr = new Uint8Array(buf);\n for (let i = 0; i < length; i++) {\n arr[i] = bin.charCodeAt(i);\n }\n\n return new Blob([buf], { type: mime });\n } catch(err) {\n console.error(\"Failed to convert base64 to blob: \", err);\n }\n\n return null;\n}\n","import React from 'react';\nimport { defineMessages } from 'react-intl';\n\nimport { Drafty } from 'tinode-sdk';\n\nimport LazyImage from '../widgets/lazy-image.jsx'\nimport UploadingImage from '../widgets/uploading-image.jsx'\n\nimport { IMAGE_THUMBNAIL_DIM, BROKEN_IMAGE_SIZE, REM_SIZE } from '../config.js';\nimport { base64ToBlob, blobToBase64, fitImageSize, imageScaled } from './blob-helpers.js';\nimport { idToColorClass, shortenFileName } from './strformat.js';\nimport { cancelablePromise, sanitizeImageUrl } from './utils.js';\n\nconst messages = defineMessages({\n drafty_form: {\n id: 'drafty_form',\n defaultMessage: 'Form: ',\n description: 'Comment for form in drafty preview'\n },\n drafty_attachment: {\n id: 'drafty_attachment',\n defaultMessage: 'Attachment',\n description: 'Comment for attachment in drafty preview'\n },\n drafty_image: {\n id: 'drafty_image',\n defaultMessage: 'Picture',\n description: 'Comment for embedded images in drafty preview'\n }\n});\n\n// Size the already scaled image.\nfunction handleImageData(el, data, attr) {\n if (!data) {\n attr.src = 'img/broken_image.png';\n attr.style = {\n width: IMAGE_THUMBNAIL_DIM + 'px',\n height: IMAGE_THUMBNAIL_DIM + 'px',\n };\n return el;\n }\n\n attr.className = 'inline-image';\n const dim = fitImageSize(data.width, data.height,\n this.viewportWidth > 0 ? Math.min(this.viewportWidth - REM_SIZE * 6.5, REM_SIZE * 34.5) :\n REM_SIZE * 34.5, REM_SIZE * 24, false) ||\n {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = {\n width: dim.dstWidth + 'px',\n height: dim.dstHeight + 'px',\n // Looks like a Chrome bug: broken image does not respect 'width' and 'height'.\n minWidth: dim.dstWidth + 'px',\n minHeight: dim.dstHeight + 'px'\n };\n if (!Drafty.isProcessing(data)) {\n attr.src = this.authorizeURL(sanitizeImageUrl(attr.src));\n attr.alt = data.name;\n if (attr.src) {\n if (Math.max(data.width || 0, data.height || 0) > IMAGE_THUMBNAIL_DIM) {\n // Allow previews for large enough images.\n attr.onClick = this.onImagePreview;\n attr.className += ' image-clickable';\n }\n attr.loading = 'lazy';\n } else {\n attr.src = 'img/broken_image.png';\n }\n } else {\n // Use custom element instead of
.\n el = UploadingImage;\n }\n\n return el;\n}\n\nfunction quotedImage(data) {\n let promise;\n // Get the blob from the image data.\n if (data.val) {\n const blob = base64ToBlob(data.val, data.mime);\n promise = blob ? Promise.resolve(blob) : Prmise.reject(new Error(\"Invalid image\"));\n } else {\n promise = fetch(this.authorizeURL(sanitizeImageUrl(data.ref))).then(evt => {\n if (evt.ok) {\n return evt.blob();\n } else {\n throw new Error(`Image fetch unsuccessful: ${evt.status} ${evt.statusText}`);\n }\n });\n }\n\n // Scale the blob.\n return promise\n .then(blob => {\n return imageScaled(blob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, true)\n }).then(scaled => {\n data.mime = scaled.mime;\n data.size = scaled.blob.size;\n data.width = scaled.width;\n data.height = scaled.height;\n delete data.ref;\n // Keeping the original file name, if provided: ex.data.name;\n\n return blobToBase64(scaled.blob);\n }).then(b64 => {\n data.val = b64.bits;\n return data;\n }).catch(err => {\n delete data.val;\n delete data.name;\n data.width = IMAGE_THUMBNAIL_DIM;\n data.height = IMAGE_THUMBNAIL_DIM;\n data.maxWidth = IMAGE_THUMBNAIL_DIM;\n data.maxHeight = IMAGE_THUMBNAIL_DIM;\n // Rethrow.\n throw err;\n });\n}\n\n// The main Drafty formatter: converts Drafty elements into React classes. 'this' is set by the caller.\n// 'this' must contain:\n// viewportWidth: this.props.viewportWidth;\n// authorizeURL: this.props.tinode.authorizeURL\n// onImagePreview: this.handleImagePreview\n// onFormButtonClick: this.handleFormButtonClick\n// onQuoteClick: this.handleQuoteClick (optional)\nexport function fullFormatter(style, data, values, key, stack) {\n if (stack.includes('QQ')) {\n return quoteFormatter.call(this, style, data, values, key);\n }\n\n if (!style) {\n // Unformatted.\n return values;\n }\n\n let el = Drafty.tagName(style);\n const attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'BR':\n values = null;\n break;\n case 'HL':\n // Highlighted text. Assign class name.\n attr.className = 'highlight';\n break;\n case 'HD':\n el = null;\n values = null;\n break;\n case 'IM':\n // Additional processing for images\n el = handleImageData.call(this, el, data, attr);\n // Image element cannot have content.\n values = null;\n break;\n case 'BN':\n // Button\n attr.onClick = this.onFormButtonClick;\n let inner = React.Children.map(values, (child) => {\n return typeof child == 'string' ? child : undefined;\n });\n if (!inner || inner.length == 0) {\n inner = [attr.name]\n }\n // Get text which will be sent back when the button is clicked.\n attr['data-title'] = inner.join('');\n break;\n case 'MN':\n // Mention\n attr.className = 'mention'\n if (data) {\n attr.className += ' ' + idToColorClass(data.val, false, true);\n }\n break;\n case 'FM':\n // Form\n attr.className = 'bot-form';\n break;\n case 'RW':\n // Form element formatting is dependent on element content.\n break;\n case 'QQ':\n // Quote/citation.\n attr.className = 'reply-quote'\n attr.onClick = this.onQuoteClick;\n break;\n default:\n if (el == '_UNKN') {\n // Unknown element.\n el = React.Fragment;\n values = [extension, ' '].concat(values || []);\n }\n break;\n }\n if (!el) {\n return values;\n }\n return React.createElement(el, attr, values);\n}\n\n// Converts Drafty object into a one-line preview. 'this' is set by the caller.\n// 'this' must contain:\n// formatMessage: this.props.intl.formatMessage\n// messages: formatjs messages defined with defineMessages.\nexport function previewFormatter(style, data, values, key) {\n if (!style) {\n // Unformatted.\n return values;\n }\n\n let el = Drafty.tagName(style);\n const attr = { key: key };\n switch (style) {\n case 'BR':\n // Replace new line with a space.\n el = React.Fragment;\n values = [' '];\n break;\n case 'HL':\n // Make highlight less prominent in preview.\n attr.className = 'highlight preview';\n break;\n case 'LN':\n case 'MN':\n // Disable links in previews.\n el = 'span';\n break;\n case 'IM':\n // Replace image with '[icon] Image'.\n el = React.Fragment;\n values = [photo, this.formatMessage(messages.drafty_image)];\n break;\n case 'BN':\n el = 'span';\n attr.className = 'flat-button faux';\n break;\n case 'FM':\n el = React.Fragment;\n values = [dashboard,\n this.formatMessage(messages.drafty_form)].concat(' ', values || []);\n break;\n case 'RW':\n el = React.Fragment;\n break;\n case 'EX':\n if (data) {\n if (data.mime == 'application/json') {\n // Ignore JSON attachments: they are form response payloads.\n return null;\n }\n // Clear payload.\n delete data.val;\n delete data.ref;\n }\n el = React.Fragment;\n values = [attachment, this.formatMessage(messages.drafty_attachment)];\n break;\n case 'QQ':\n case 'HD':\n el = null;\n values = null;\n break;\n default:\n if (el == '_UNKN') {\n el = React.Fragment;\n values = [extension, ' '].concat(values || []);\n }\n break;\n }\n if (!el) {\n return values;\n }\n return React.createElement(el, attr, values);\n};\n\n// Converts Drafty object into a quoted reply. 'this' is set by the caller.\nfunction inlineImageAttr(attr, data) {\n attr.style = {\n width: IMAGE_THUMBNAIL_DIM + 'px',\n height: IMAGE_THUMBNAIL_DIM + 'px',\n maxWidth: IMAGE_THUMBNAIL_DIM + 'px',\n maxHeight: IMAGE_THUMBNAIL_DIM + 'px',\n }\n attr.className = 'inline-image';\n attr.alt = this.formatMessage(messages.drafty_image);\n if (!data) {\n attr.src = 'img/broken_image.png';\n }\n attr.title = attr.alt;\n return attr;\n}\n\n// Displays a portion of Drafty within 'QQ' quotes. 'this' is set by the caller.\n// 'this' must contain:\n// formatMessage: this.props.intl.formatMessage\n// messages: formatjs messages defined with defineMessages.\n// authorizeURL: this.props.tinode.authorizeURL\n// onQuoteClick: this.handleQuoteClick (optional)\nfunction quoteFormatter(style, data, values, key) {\n if (['BR', 'EX', 'IM', 'MN'].includes(style)) {\n let el = Drafty.tagName(style);\n let attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch(style) {\n case 'BR':\n values = null;\n break;\n case 'IM':\n attr = inlineImageAttr.call(this, attr, data);\n values = [React.createElement('img', attr, null), ' ', attr.alt];\n el = React.Fragment;\n // Fragment attributes.\n attr = {key: key};\n break;\n case 'MN':\n el = 'span';\n attr.className = 'mention'\n if (data) {\n attr.className += ' ' + idToColorClass(data.val, false, true);\n }\n break;\n case 'EX':\n let fname;\n if (data) {\n if (data.mime == 'application/json') {\n // Ignore JSON attachments: they are form response payloads.\n return null;\n }\n fname = data.name;\n // Clear payload.\n delete data.val;\n delete data.ref;\n }\n el = React.Fragment;\n values = [attachment,\n shortenFileName(fname, 16) || this.formatMessage(messages.drafty_attachment)];\n break;\n }\n return React.createElement(el, attr, values);\n }\n return previewFormatter.call(this, style, data, values, key);\n}\n\n// Create image thumbnail suitable for inclusion in a quote.\nfunction quoteImage(data) {\n let promise;\n // Get the blob from the image data.\n if (data.val) {\n const blob = base64ToBlob(data.val, data.mime);\n if (!blob) {\n throw new Error(\"Invalid image\");\n }\n promise = Promise.resolve(blob);\n } else if (data.ref) {\n promise = fetch(this.authorizeURL(sanitizeImageUrl(data.ref)))\n .then(evt => {\n if (evt.ok) {\n return evt.blob();\n } else {\n throw new Error(`Image fetch unsuccessful: ${evt.status} ${evt.statusText}`);\n }\n });\n } else {\n throw new Error(\"Missing image data\");\n }\n\n // Scale the blob.\n return promise\n .then(blob => {\n // Cut the square from the center of the image and shrink it.\n return imageScaled(blob, IMAGE_THUMBNAIL_DIM, IMAGE_THUMBNAIL_DIM, -1, true)\n }).then(scaled => {\n data.mime = scaled.mime;\n data.size = scaled.blob.size;\n data.width = scaled.width;\n data.height = scaled.height;\n delete data.ref;\n // Keeping the original file name, if provided: ex.data.name;\n\n data.src = URL.createObjectURL(scaled.blob);\n return blobToBase64(scaled.blob);\n }).then(b64 => {\n data.val = b64.bits;\n return data;\n }).catch(err => {\n delete data.val;\n delete data.src;\n data.width = IMAGE_THUMBNAIL_DIM;\n data.height = IMAGE_THUMBNAIL_DIM;\n // Rethrow.\n throw err;\n });\n}\n\n// Create a preview of a reply.\nexport function replyFormatter(style, data, values, key, stack) {\n if (style == 'IM') {\n const attr = inlineImageAttr.call(this, {key: key}, data);\n let loadedPromise;\n try {\n loadedPromise = cancelablePromise(quoteImage.call(this, data));\n } catch (error) {\n loadedPromise = cancelablePromise(error);\n }\n attr.whenDone = loadedPromise;\n values = [React.createElement(LazyImage, attr, null), ' ', attr.alt];\n return React.createElement(React.Fragment, {key: key}, values);\n } else if (style == 'QQ') {\n if (stack.includes('QQ')) {\n // Quote inside quote when forwarding a message.\n return React.createElement('span', {key: key},\n [format_quote, ' ']);\n }\n\n const attr = Drafty.attrValue('QQ', data) || {};\n attr.key = key;\n attr.className = 'reply-quote'\n return React.createElement(Drafty.tagName('QQ'), attr, values);\n }\n return quoteFormatter.call(this, style, data, values, key);\n}\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage, injectIntl } from 'react-intl';\n\nimport ContactBadges from './contact-badges.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport UnreadBadge from './unread-badge.jsx';\n\nimport { Drafty } from 'tinode-sdk';\n\nimport { previewFormatter } from '../lib/formatters.js';\nimport { deliveryMarker } from '../lib/utils.js';\n\nclass Contact extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n if (this.props.onSelected) {\n // Index this.props.index is used by GroupManager.\n this.props.onSelected(this.props.item, this.props.index);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.props.item, y: e.pageY, x: e.pageX });\n }\n\n render() {\n let title = this.props.title;\n if (!title) {\n title = ;\n } else if (title.length > 30) {\n // FIXME: this is probably wrong for RTL languages.\n title = title.substring(0, 28) + '…';\n }\n const online = this.props.now ? 'online' : 'offline';\n const avatar = this.props.avatar ? this.props.avatar : true;\n const badges = this.props.badges ? this.props.badges.slice() : [];\n const icon_badges = [];\n if (this.props.isVerified) {\n icon_badges.push({icon: 'verified', color: 'verified-color'});\n }\n if (this.props.isStaff) {\n icon_badges.push({icon: 'staff', color: 'staff-color'});\n }\n if (this.props.isDangerous) {\n icon_badges.push({icon: 'dangerous', color: 'danger-color'});\n }\n if (this.props.acs) {\n if (this.props.showMode) {\n badges.push({name: this.props.acs.getMode(), key: 'mode'});\n }\n if (this.props.acs.isMuted()) {\n icon_badges.push({icon: 'muted'});\n }\n if (!this.props.acs.isJoiner()) {\n icon_badges.push({icon: 'banned'});\n }\n }\n\n // The this.props.preview contains alreay shortened Drafty or string.\n let preview;\n if (typeof this.props.preview == 'string') {\n preview = this.props.preview;\n } else if (Drafty.isValid(this.props.preview)) {\n preview = React.createElement(React.Fragment, null, Drafty.format(this.props.preview, previewFormatter,\n {formatMessage: this.props.intl.formatMessage}));\n } else if (this.props.preview) {\n preview =\n <>warning_amber \n \n >;\n }\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n const titleClass = 'contact-title' + (this.props.deleted ? ' deleted' : '');\n\n return (\n \n \n \n {this.props.deleted ? cancel :\n this.props.showOnline ? :\n (this.props.showCheckmark && this.props.selected) ?\n check_circle\n : null}\n
\n \n
{title}\n {this.props.isChannel ?

: null}\n
\n {!this.props.deleted ?
: null}\n
\n {this.props.showMode ?\n
:\n this.props.small ? null :
{marker}{preview || this.props.comment || '\\u00A0'}
}\n
\n {this.props.showContextMenu ?\n \n \n expand_more\n \n : null}\n \n );\n }\n};\n\nexport default injectIntl(Contact);\n","// A panel included into a list of contacts with an action text.\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nclass ContactAction extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.onAction(this.props.action);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n {formatMessage(this.props.title, this.props.values)}
\n \n );\n }\n};\n\nexport default injectIntl(ContactAction);\n","// ContactList: component for showing a list of contacts,\n// such as a list of group members in a group chat.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport { Drafty } from 'tinode-sdk';\nimport Tinode from 'tinode-sdk';\n\nimport Contact from './contact.jsx';\nimport ContactAction from './contact-action.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\nimport { MESSAGE_PREVIEW_LENGTH } from '../config.js';\n\nconst messages = defineMessages({\n badge_you: {\n id: 'badge_you',\n defaultMessage: 'you',\n description: 'Badge for indicating the current user'\n },\n badge_owner: {\n id: 'badge_owner',\n defaultMessage: 'owner',\n description: 'Badge for indicating the owner'\n }\n});\n\nclass ContactList extends React.Component {\n render() {\n const { formatMessage } = this.props.intl;\n const showCheckmark = Array.isArray(this.props.topicSelected);\n const contactNodes = [];\n let contactsCount = 0;\n if (this.props.contacts && this.props.contacts.length > 0) {\n this.props.contacts.map((c) => {\n if (c.action) {\n // Action item\n contactNodes.push(\n );\n } else {\n // Normal contact\n const key = this.props.showMode ? c.user : (c.topic || c.user);\n // If filter function is provided, filter out the items\n // which don't satisfy the condition.\n if (this.props.filterFunc && this.props.filter) {\n const filterOn = [key];\n if (c.private && c.private.comment) {\n filterOn.push(('' + c.private.comment).toLowerCase());\n }\n if (c.public && c.public.fn) {\n filterOn.push(('' + c.public.fn).toLowerCase());\n }\n if (!this.props.filterFunc(this.props.filter, filterOn)) {\n return;\n }\n }\n\n const isChannel = Tinode.isChannelTopicName(key);\n const selected = showCheckmark ?\n (this.props.topicSelected.indexOf(key) > -1) :\n (this.props.topicSelected === key);\n const badges = [];\n if (this.props.showMode) {\n if (key == this.props.myUserId) {\n badges.push({name: formatMessage(messages.badge_you), color: 'green'});\n }\n if (c.acs && c.acs.isOwner()) {\n badges.push({name: formatMessage(messages.badge_owner), color: 'blue'});\n }\n }\n\n const comment = Array.isArray(c.private) ?\n c.private.join(',') : (c.private ? c.private.comment : null);\n let preview;\n let forwarded;\n let deliveryStatus;\n if (!this.props.showMode && c.latestMessage) {\n const msg = c.latestMessage(true);\n if (msg) {\n forwarded = msg.head ? msg.head.forwarded : null;\n deliveryStatus = msg._status || c.msgStatus(msg, true);\n if (msg.content) {\n preview = typeof msg.content == 'string' ?\n msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :\n Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH);\n }\n }\n }\n\n contactNodes.push(\n \n );\n contactsCount ++;\n }\n }, this);\n }\n\n return (\n \n {contactsCount == 0 ?\n
\n :\n null\n }\n {contactNodes.length > 0 ?\n
\n :\n null\n }\n
\n );\n }\n};\n\nexport default injectIntl(ContactList);\n","import React from 'react';\n\nimport Tinode from 'tinode-sdk';\n\nexport default class SearchContacts extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n edited: false,\n search: ''\n };\n\n this.handleSearchChange = this.handleSearchChange.bind(this);\n this.handleSearch = this.handleSearch.bind(this);\n this.handleClear = this.handleClear.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n componentWillUnmount() {\n if (this.state.edited) {\n this.setState({search: '', edited: false});\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n }\n\n handleSearchChange(e) {\n this.setState({search: e.target.value});\n }\n\n handleSearch(e) {\n e.preventDefault();\n var query = this.state.search.trim();\n this.setState({edited: (query.length > 0)});\n this.props.onSearchContacts(query.length > 0 ? query : Tinode.DEL_CHAR);\n }\n\n handleClear(e) {\n e.preventDefault();\n if (this.state.edited) {\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n this.setState({search: '', edited: false});\n }\n\n handleKeyDown(e) {\n if (e.key === 'Enter') {\n this.handleSearch(e);\n } else if (e.key === 'Escape') {\n this.handleClear();\n }\n }\n\n render() {\n return (\n \n );\n }\n};\n","// Forward Menu: message forwarding popup/dropdown menu.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from './contact-list.jsx';\nimport SearchContacts from './search-contacts.jsx';\n\nexport default class ForwardDialog extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n query: null\n };\n\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this.handleClose = this.handleClose.bind(this);\n\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleContactSelected = this.handleContactSelected.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleEscapeKey(e) {\n if (e.keyCode === 27) {\n // Not forwarding the message.\n this.props.hide(false);\n }\n }\n\n handleClose(e) {\n e.preventDefault();\n this.props.hide(false);\n }\n\n handleSearchContacts(query) {\n this.setState({ query: Tinode.isNullValue(query) ? null : query });\n this.props.onSearchContacts(query);\n }\n\n handleContactSelected(uid) {\n this.props.onTopicSelected(uid);\n this.props.hide(true);\n }\n\n render() {\n let contacts = this.state.query != null ? this.props.searchResults : this.props.contacts;\n // Filter out contacts without a 'W' or 'J' permissions as well as the current topic.\n contacts = contacts.filter((c) => {\n return (c.name != this.props.topicSelected) && c.acs.isJoiner() && c.acs.isWriter();\n });\n\n return (\n \n
\n
\n
{\n (search_placeholder) => \n }\n
{\n (not_found_placeholder) => \n }\n
\n
\n );\n }\n}\n","import React from 'react';\n\nexport default class LoadSpinner extends React.PureComponent {\n render() {\n const className = 'load-spinner-box' + (this.props.large ? ' large' : '')\n + (this.props.clear ? ' clear' : '') + (this.props.centered ? ' centered' : '');\n return (this.props.show ?\n : null);\n }\n}\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\nimport LoadSpinner from './load-spinner.jsx';\n\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nimport { AVATAR_SIZE } from '../config.js';\n\nexport default class AvatarUpload extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n source: props.avatar\n };\n\n this.handleFileReceived = this.handleFileReceived.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (this.props.avatar != prevProps.avatar) {\n this.setState({source: this.props.avatar});\n }\n }\n\n handleFileReceived(e) {\n const image = e.target.files[0];\n this.props.onImageUpdated(image.type, URL.createObjectURL(image), image.name);\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n render() {\n // Randomize id value in case more than one AvatarUpload is shown\n // at the same time.\n const randId = 'file-input-avatar-' + ('' + Math.random()).substring(0, 4);\n const className = 'avatar-upload' + (this.props.readOnly ? ' read-only' : '');\n return (\n \n {this.props.readOnly || !this.state.source ?\n null :\n
{e.preventDefault(); this.props.onImageUpdated();}}>\n clear\n }\n {this.state.source ?\n
)})
:\n this.props.readOnly && this.props.uid ?\n
\n \n
\n :\n
{AVATAR_SIZE}×{AVATAR_SIZE}
}\n {this.props.readOnly ? null :\n
}\n {this.props.readOnly ? null :\n
}\n
\n
\n );\n }\n};\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nconst icon_mapping = {'staff': 'verified_user'};\n\nconst messages = defineMessages({\n badge_verified: {\n id: 'badge_verified',\n defaultMessage: 'Verified/official',\n description: 'Explanation of a verified account or topic badge'\n },\n badge_staff: {\n id: 'badge_staff',\n defaultMessage: 'Staff-managed',\n description: 'Explanation of a staff-managed account or topic badge'\n },\n badge_danger: {\n id: 'badge_danger',\n defaultMessage: 'Untrustworthy',\n description: 'Suspicious or untrustworthy account or topic badge'\n },\n});\n\n\n/* Trusted badges with labels: verified, staff, danger */\nclass BadgeList extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let badges = null;\n if (this.props.trustedBadges && this.props.trustedBadges.length > 0) {\n badges = [];\n this.props.trustedBadges.map((b) => {\n const comment = formatMessage(messages['badge_' + b]);\n const style = 'material-icons ' + b + '-color';\n badges.push({icon_mapping[b] || b} {comment}
);\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n\nexport default injectIntl(BadgeList);\n","import React from 'react';\n\n/* BEGIN CheckBox: styled checkbox */\nexport default class CheckBox extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleChange = this.handleChange.bind(this);\n }\n\n handleChange() {\n this.props.onChange(this.props.name, !this.props.checked);\n }\n\n render() {\n return (\n this.props.onChange ? (\n this.props.checked === true ?\n check_box :\n this.props.checked === false ?\n check_box_outline_blank :\n indeterminate_check_box\n ) : (\n this.props.checked ?\n check_box :\n check_box_outline_blank\n )\n );\n }\n}\n/* END CheckBox */\n","// The X menu to be displayed in title bars.\nimport React from 'react';\n\nexport default class MenuCancel extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onCancel();}}>\n close\n \n );\n }\n}\n","import React from 'react';\n\nimport MenuCancel from './menu-cancel.jsx';\n\nexport default class ErrorPanel extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n show: false,\n };\n\n this.hide = this.hide.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.level !== this.props.level) {\n this.setState({\n show: !(!this.props.level)\n });\n }\n }\n\n hide() {\n this.setState({show: false});\n if (this.props.onClearError) {\n this.props.onClearError();\n }\n }\n\n render() {\n const icons = {err: 'error', warn: 'warning', info: 'info'}\n const level = icons[this.props.level] || '';\n const className = 'info-box ' + level;\n return (\n \n );\n }\n};\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\n\nexport default class Chip extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel(this.props.topic, this.props.index);\n }\n\n render() {\n const title = this.props.title || this.props.topic;\n const className = this.props.invalid ? 'chip invalid' : 'chip';\n return (\n \n {this.props.noAvatar ?\n
:\n
\n \n
\n }\n
{title}\n {this.props.onCancel && !this.props.required ?\n
×\n :
}\n
\n );\n }\n};\n","import React from 'react';\n\nimport Chip from './chip.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\n/* BEGIN ChipInput: group membership widget */\nexport default class ChipInput extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = ChipInput.deriveStateFromProps(props);\n this.state.input = '';\n this.state.focused = false;\n\n this.handleTextInput = this.handleTextInput.bind(this);\n this.removeChipAt = this.removeChipAt.bind(this);\n this.handleChipCancel = this.handleChipCancel.bind(this);\n this.handleFocusGained = this.handleFocusGained.bind(this);\n this.handleFocusLost = this.handleFocusLost.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n static deriveStateFromProps(props) {\n return {\n placeholder: props.chips ? '' : props.prompt,\n sortedChips: ChipInput.sortChips(props.chips, props.staticMembers),\n chipIndex: ChipInput.indexChips(props.chips)\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chips != this.props.chips ||\n prevProps.staticMembers != this.props.staticMembers ||\n prevProps.prompt != this.props.prompt) {\n this.setState(ChipInput.deriveStateFromProps(this.props));\n }\n if (!prevState || this.props.chips.length > prevState.sortedChips.length) {\n this.setState({input: ''});\n }\n }\n\n // Map chip index to user name\n static indexChips(chips) {\n const index = {};\n let count = 0;\n chips.map((item) => {\n index[item.user] = count;\n count ++;\n });\n return index;\n }\n\n // Have non-removable chips appear before all other chips.\n static sortChips(chips, keep) {\n const required = [];\n const normal = [];\n chips.map((item) => {\n if (keep && keep.includes(item.user)) {\n required.push(item);\n } else {\n normal.push(item);\n }\n });\n return required.concat(normal);\n }\n\n handleTextInput(e) {\n this.setState({input: e.target.value});\n if (this.props.filterFunc) {\n this.props.filterFunc(e.target.value);\n }\n }\n\n removeChipAt(idx) {\n const removed = this.state.sortedChips[idx];\n this.props.onChipRemoved(removed.user, this.state.chipIndex[removed.user]);\n }\n\n handleChipCancel(item, idx) {\n this.removeChipAt(idx);\n }\n\n handleFocusGained() {\n this.setState({focused: true});\n }\n\n handleFocusLost() {\n this.setState({focused: false});\n if (this.props.onFocusLost) {\n this.props.onFocusLost(this.state.input);\n }\n }\n\n handleKeyDown(e) {\n if (e.key === 'Backspace') {\n if (this.state.input.length == 0 && this.state.sortedChips.length > 0) {\n const at = this.state.sortedChips.length - 1;\n if (this.state.sortedChips[at].user !== this.props.staticMembers) {\n this.removeChipAt(at);\n }\n }\n } else if (e.key === 'Enter') {\n if (this.props.onEnter) {\n this.props.onEnter(this.state.input);\n }\n } else if (e.key === 'Escape') {\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n }\n\n render() {\n const chips = [];\n let count = 0;\n const staticMembers = this.props.staticMembers || [];\n this.state.sortedChips.map((item) => {\n chips.push(\n \n );\n count++;\n });\n const className = \"chip-input\" + (this.state.focused ? ' focused' : '');\n const autoFocus = !(this.props.tabIndex > 0);\n return (\n \n {chips}\n \n
\n );\n }\n};\n","// GroupMembers: control for managing a list of group members.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\nimport ContactList from './contact-list.jsx';\n\nconst messages = defineMessages({\n no_contacts: {\n id: 'no_contacts',\n defaultMessage: 'You have no contacts :-(',\n description: 'Shown in ContactsView when the user has no contacts'\n },\n contacts_not_found_short: {\n id: 'contacts_not_found_short',\n defaultMessage: \"No contacts match ''{query}''\",\n description: 'Shown in ContactsView when search returned no results'\n }\n});\n\nclass GroupManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Array of topic members\n members: props.members,\n index: GroupManager.indexMembers(props.members),\n staticMembers: GroupManager.staticMembers(props.members, props.keepInitialMembers, props.requiredMember),\n contactFilter: '',\n noContactsMessage: props.intl.formatMessage(messages.no_contacts),\n selectedContacts: GroupManager.selectedContacts(props.members)\n };\n\n this.handleContactSelected = this.handleContactSelected.bind(this);\n this.handleMemberRemoved = this.handleMemberRemoved.bind(this);\n this.handleContactFilter = this.handleContactFilter.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static indexMembers(members) {\n let index = {};\n members.map((m) => {\n index[m.user] = {delta: 0, present: true}; // Delta: 0 unchanged, +1 added, -1 removed\n });\n return index;\n }\n\n static staticMembers(members, keepInitial, requiredMember) {\n let stat = [];\n members.map((m) => {\n if (keepInitial || m.user == requiredMember) {\n stat.push(m.user);\n }\n });\n return stat;\n }\n\n static selectedContacts(members) {\n let sel = [];\n members.map((m) => {\n sel.push(m.user);\n });\n return sel;\n }\n\n handleContactSelected(userId, index) {\n let status = this.state.index[userId];\n if (status) {\n if (status.present) {\n // Prevent duplicate members\n return;\n }\n status.delta += 1;\n status.present = true;\n } else {\n status = {delta: 1, present: true};\n }\n\n let m = this.state.members.slice();\n m.push(this.props.contacts[index]);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleMemberRemoved(userId, index) {\n const status = this.state.index[userId];\n if (!status || !status.present) {\n return;\n }\n status.present = false;\n status.delta -= 1;\n\n let m = this.state.members.slice();\n m.splice(index, 1);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleContactFilter(val) {\n const {formatMessage} = this.props.intl;\n const msg = !val ?\n formatMessage(messages.no_contacts) :\n formatMessage(messages.contacts_not_found_short, {query: val});\n\n this.setState({contactFilter: val, noContactsMessage: msg});\n }\n\n static doContactFiltering(filter, values) {\n if (filter) {\n for (let i=0; i= 0) {\n return true;\n }\n }\n return false;\n }\n return true;\n }\n\n handleSubmit() {\n var instance = this;\n var members = [];\n var added = [];\n var removed = [];\n\n var keys = Object.keys(this.state.index);\n keys.map(function(k) {\n if (instance.state.index[k].present) {\n members.push(k);\n }\n\n if (instance.state.index[k].delta > 0) {\n added.push(k);\n } else if (instance.state.index[k].delta < 0) {\n removed.push(k);\n }\n });\n this.props.onSubmit(members, added, removed);\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(GroupManager);\n","// PermissionsEditor: Component for editing permissions\n// \nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport CheckBox from './checkbox.jsx';\nimport Contact from './contact.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\n// Translatable permission names.\nconst messages = defineMessages({\n joiner: {\n id: 'permission_join',\n defaultMessage: \"Join ({val})\",\n description: 'Name of J permission'\n },\n reader: {\n id: 'permission_read',\n defaultMessage: \"Read ({val})\",\n description: 'Name of R permission'\n },\n writer: {\n id: 'permission_write',\n defaultMessage: \"Write ({val})\",\n description: 'Name of W permission'\n },\n preser: {\n id: 'permission_pres',\n defaultMessage: \"Get notified ({val})\",\n description: 'Name of P permission'\n },\n approver: {\n id: 'permission_admin',\n defaultMessage: \"Approve ({val})\",\n description: 'Name of A permission'\n },\n sharer: {\n id: 'permission_share',\n defaultMessage: \"Share ({val})\",\n description: 'Name of S permission'\n },\n deleter: {\n id: 'permission_delete',\n defaultMessage: \"Delete ({val})\",\n description: 'Name of D permission'\n },\n owner: {\n id: 'permission_owner',\n defaultMessage: \"Owner ({val})\",\n description: 'Name of O permission'\n }\n});\n\nclass PermissionsEditor extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n mode: (props.mode || '').replace('N', '')\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(val) {\n let mode = this.state.mode;\n const idx = mode.indexOf(val);\n if (idx == -1) {\n mode += val;\n } else {\n mode = mode.replace(val, '');\n }\n this.setState({mode: mode});\n }\n\n handleSubmit() {\n // Normalize string, otherwise cannot check if mode has changed.\n const mode = (this.state.mode || 'N').split('').sort().join('');\n const before = (this.props.mode || 'N').split('').sort().join('')\n if (mode !== before) {\n this.props.onSubmit(mode);\n } else {\n this.props.onCancel();\n }\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const all = 'JRWPASDO';\n const names = {\n 'J': formatMessage(messages.joiner, {val: 'J'}),\n 'R': formatMessage(messages.reader, {val: 'R'}),\n 'W': formatMessage(messages.writer, {val: 'W'}),\n 'P': formatMessage(messages.preser, {val: 'P'}),\n 'A': formatMessage(messages.approver, {val: 'A'}),\n 'S': formatMessage(messages.sharer, {val: 'S'}),\n 'D': formatMessage(messages.deleter, {val: 'D'}),\n 'O': formatMessage(messages.owner, {val: 'O'})\n };\n\n let skip = this.props.skip || '';\n let mode = this.state.mode;\n let compare = (this.props.compare || '').replace('N', '');\n let items = [];\n for (let i=0; i= 0 && mode.indexOf(c) < 0) {\n // Permission is marked as inactive: hide unchecked permissions, disable checked permissions\n continue;\n }\n items.push(\n \n | {names[c]} | \n {skip.indexOf(c) < 0 ?\n = 0)} onChange={this.handleChange}/>\n :\n = 0)} />\n } | {this.props.compare ? \n = 0)}/>\n | : null}\n
\n );\n }\n\n return (\n \n {this.props.userTitle ?\n
: null}\n
\n
\n {this.props.compare ?\n \n | {this.props.modeTitle} | \n {this.props.compareTitle} | \n
:\n null}\n \n {items}\n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(PermissionsEditor);\n","// Image zoom and crop widget.\n\nimport React from 'react';\n\nconst DEFAULT_MAX_ZOOM = 2.5;\n\nexport default class Cropper extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Coordinates of the top left corner of the image in container coordinates.\n panX: 0,\n panY: 0,\n // Origin of the zoom in image coordinates.\n originX: 0,\n originY: 0,\n // Zoom scale.\n zoom: 1,\n // Zoom min and max.\n minZoom: 0,\n maxZoom: DEFAULT_MAX_ZOOM,\n };\n\n // References to DOM elements for hooking up events and getting screen dimensions.\n // The transparent element which captures mouse moves (moves with the image).\n this.overlay = React.createRef();\n // The image cutout window or view port (static).\n this.cutout = React.createRef();\n // Scaled and translated image preview.\n this.preview = React.createRef();\n // The main container (static).\n this.boundingBox = React.createRef();\n\n // The original image width and height before any scaling.\n this.imageWidth = 0;\n this.imageHeight = 0;\n\n // Mouse position when dragging.\n this.mouseX = 0\n this.mouseY = 0;\n // Length of the previous mouse move when dragging.\n this.prevDistance = 0;\n\n // Bounding rectangles of static elements.\n this.cutoutRect = {};\n this.bBoxRect = {};\n // Center of the bounding box.\n this.originX = 0;\n this.originY = 0;\n\n this.initScaling = this.initScaling.bind(this);\n this.onZoom = this.onZoom.bind(this);\n this.handleZoom = this.handleZoom.bind(this);\n this.mouseDown = this.mouseDown.bind(this);\n this.mouseUp = this.mouseUp.bind(this);\n this.mouseMove = this.mouseMove.bind(this);\n this.mouseTouch = this.mouseTouch.bind(this);\n this.positionAll = this.positionAll.bind(this);\n this.translate = this.translate.bind(this);\n }\n\n componentDidMount() {\n this.overlay.current.addEventListener('mousedown', this.mouseDown, { passive: true });\n this.overlay.current.addEventListener('touchstart', this.mouseDown, { passive: true });\n\n // The rectangle position is in viewport coordinates.\n this.bBoxRect = this.boundingBox.current.getBoundingClientRect();\n // Center of the bounding box in container coordinates.\n this.originX = this.bBoxRect.width / 2;\n this.originY = this.bBoxRect.height / 2;\n\n // The rectangle position is in viewport coordinates.\n this.cutoutRect = this.cutout.current.getBoundingClientRect();\n }\n\n componentWillUnmount() {\n this.overlay.current.removeEventListener('mousedown', this.mouseDown);\n this.overlay.current.removeEventListener('touchstart', this.mouseDown);\n }\n\n // Position all elements.\n positionAll(panX, panY, zoom) {\n // Zoom origin in image preview coordinates.\n this.setState({\n panX: panX,\n panY: panY,\n zoom: zoom,\n originX: this.originX - panX,\n originY: this.originY - panY,\n });\n\n // Pass cut out coordinates to caller.\n const left = (this.originX - panX) * zoom - this.originX;\n const top = (this.originY - panY) * zoom - this.originY;\n this.props.onChange(\n (left + this.cutoutRect.left - this.bBoxRect.left) / zoom,\n (top + this.cutoutRect.top - this.bBoxRect.top) / zoom,\n this.cutoutRect.width / zoom,\n this.cutoutRect.height / zoom,\n zoom);\n }\n\n // Check if new location is within the limits.\n static checkBound(currPan, img, cutout, delta) {\n let nextDiff = Math.min(0, cutout[0] - img[0] - delta, img[1] - cutout[1] + delta);\n if (nextDiff == 0) {\n // Cutout is completely within the image.\n currPan += delta;\n } else if (Math.min(0, cutout[0] - img[0], img[1] - cutout[1]) < nextDiff) {\n // Cutout is outside of the range but closer to the bound.\n currPan += delta;\n }\n return currPan;\n }\n\n initScaling() {\n // Initialize components.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n this.imageWidth = imgRect.width;\n this.imageHeight = imgRect.height;\n\n // Set zoom slider's min and max values. Make sure the scaled image cannot be smaller than the cutout.\n const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);\n this.setState({\n minZoom: minZoom,\n maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)\n });\n\n // Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. \"fill\" not \"fit\".\n const zoom = Math.max(this.bBoxRect.width / imgRect.width, this.bBoxRect.height / imgRect.height);\n // Converting from viewport coordinates to container, then panning.\n const panX = this.cutoutRect.left - this.bBoxRect.left -\n (imgRect.width - this.cutoutRect.width) / 2;\n const panY = this.cutoutRect.top - this.bBoxRect.top -\n (imgRect.height - this.cutoutRect.height) / 2;\n\n this.positionAll(panX, panY, zoom);\n }\n\n onZoom(e) {\n this.handleZoom(e.target.value);\n }\n\n handleZoom(zoom) {\n let panX = this.state.panX;\n let panY = this.state.panY;\n\n // Ensure that the image at the new zoom is not outside of the cutout boundaries.\n // Calculate image coordinates at the new zoom.\n const imgLeft = this.originX - (this.originX - panX) * zoom;\n const imgRight = imgLeft + this.imageWidth * zoom;\n // Cutout in the same coordinates as the image.\n const coLeft = this.cutoutRect.left - this.bBoxRect.left;\n const coRight = coLeft + this.cutoutRect.width;\n if (coLeft < imgLeft) {\n panX -= imgLeft - coLeft;\n } else if (coRight > imgRight) {\n panX += coRight - imgRight;\n }\n const imgTop = this.originY - (this.originY - panY) * zoom;\n const imgBottom = imgTop + this.imageHeight * zoom;\n const coTop = this.cutoutRect.top - this.bBoxRect.top;\n const coBottom = coTop + this.cutoutRect.height;\n if (coTop < imgTop) {\n panY -= imgTop - coTop;\n } else if (coBottom > imgBottom) {\n panY += coBottom - imgBottom;\n }\n\n this.positionAll(panX, panY, zoom);\n }\n\n mouseDown(e) {\n if (e.touches) {\n this.mouseX = e.touches[0].pageX;\n this.mouseY = e.touches[0].pageY;\n } else {\n this.mouseX = e.pageX;\n this.mouseY = e.pageY;\n }\n\n window.addEventListener('mousemove', this.mouseMove, { passive: false });\n window.addEventListener('touchmove', this.mouseTouch, { passive: false });\n window.addEventListener('mouseup', this.mouseUp, { passive: true });\n window.addEventListener('touchend', this.mouseUp, { passive: true });\n // Disable text selection in the entire document.\n document.body.style['userSelect'] = 'none';\n }\n\n // Perform image panning.\n translate(pageX, pageY) {\n const dX = pageX - this.mouseX;\n const dY = pageY - this.mouseY;\n\n this.mouseX = pageX;\n this.mouseY = pageY;\n\n // Make sure the image is within the cutout window.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n // Check if the new position is within the boundaries, and if not if it's closer to them.\n let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],\n [this.cutoutRect.left, this.cutoutRect.right], dX);\n let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],\n [this.cutoutRect.top, this.cutoutRect.bottom], dY);\n\n this.positionAll(panX, panY, this.state.zoom);\n }\n\n // Image panning.\n mouseMove(e) {\n e.preventDefault();\n this.translate(e.pageX, e.pageY);\n }\n\n mouseTouch(e) {\n e.preventDefault();\n\n if (e.touches.length == 1) {\n this.translate(e.touches[0].pageX, e.touches[0].pageY);\n return;\n }\n\n // Image zooming by pinching.\n const [touch0, touch1] = e.touches;\n const distance = Math.sqrt((touch0.pageX - touch1.pageX) * (touch0.pageX - touch1.pageX) +\n (touch0.pageY - touch1.pageY) * (touch0.pageY - touch1.pageY));\n\n if (!this.prevDistance) {\n this.prevDistance = distance / this.state.zoom;\n }\n\n let scale = (distance / this.prevDistance);\n this.handleZoom(Math.max(this.minZoom, Math.min(this.maxZoom, scale)));\n }\n\n mouseUp(e) {\n window.removeEventListener('mousemove', this.mouseMove);\n window.removeEventListener('touchmove', this.mouseTouch);\n window.removeEventListener('mouseup', this.mouseUp);\n window.removeEventListener('touchend', this.mouseUp);\n // Re-enable text selection.\n document.body.style['userSelect'] = '';\n\n this.positionAll(this.state.panX, this.state.panY, this.state.zoom);\n }\n\n render() {\n // transform3d: position and scale.\n const t3d = `translate3d(${this.state.panX}px, ${this.state.panY}px, 0) scale(${this.state.zoom})`;\n // transformOrigin: zoom origin.\n const orig = `${this.state.originX}px ${this.state.originY}px`;\n\n // Overlay position and size are exactly equal to the position and size of the transformed image\n // except it uses different coordinates.\n const overlay = {\n top: `${this.originY - this.state.originY * this.state.zoom}px`,\n left: `${this.originX - this.state.originX * this.state.zoom}px`,\n width: `${this.imageWidth * this.state.zoom}px`,\n height: `${this.imageHeight * this.state.zoom}px`\n };\n return (\n \n
\n

\n
\n
\n
\n
\n \n
\n
\n );\n }\n}\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Cropper from './cropper.jsx';\nimport { imageCrop } from '../lib/blob-helpers.js';\n\nexport default class AvatarCrop extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n top: 0,\n left: 0,\n width: 0,\n height: 0,\n scale: 1\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleChange = this.handleChange.bind(this);\n }\n\n // Cut out in image coordinates and desired scale factor.\n handleChange(left, top, width, height, scale) {\n this.setState({\n left: left,\n top: top,\n width: width,\n height: height,\n scale: scale\n });\n }\n\n handleSubmit() {\n imageCrop(this.props.mime, this.props.avatar,\n this.state.left, this.state.top, this.state.width, this.state.height,\n this.state.scale)\n .then(img => {\n this.props.onSubmit(img.mime, img.blob, img.width, img.height);\n })\n .catch(err => {\n this.props.onError(err);\n });\n }\n\n render() {\n return (\n \n
\n \n
\n
\n {this.props.onCancel ?\n \n : null}\n \n
\n
\n );\n }\n};\n","// Password with a visiblity toggle.\nimport React from 'react';\n\nexport default class VisiblePassword extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n value: this.props.value,\n visible: false\n };\n\n this.handleVisibility = this.handleVisibility.bind(this);\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value});\n if (this.props.onChange) {\n this.props.onChange(e);\n }\n }\n\n handleVisibility(e) {\n e.preventDefault();\n this.setState({visible: !this.state.visible});\n }\n\n handleKeyDown(e) {\n if (e.keyCode == 27) {\n // Escape pressed\n this.setState({value: this.props.value, visible: false});\n if (this.props.onFinished) {\n this.props.onFinished();\n }\n } else if (e.keyCode == 13) {\n // Enter pressed\n this.handleEditingFinished();\n }\n }\n\n handleEditingFinished(e) {\n if (e) {\n let currentTarget = e.currentTarget;\n setTimeout(() => {\n if (!currentTarget.contains(document.activeElement)) {\n if (this.props.onFinished) {\n this.props.onFinished(this.state.value);\n }\n }\n }, 0);\n } else if (this.props.onFinished) {\n this.props.onFinished(this.state.value.trim());\n }\n }\n\n render() {\n return (\n \n \n \n \n {this.state.visible ? 'visibility' : 'visibility_off'}\n \n \n
\n );\n }\n}\n","// In-place text editor. Shows text with an icon which toggles it into an input field.\nimport React from 'react';\n\nimport VisiblePassword from './visible-password.jsx';\n\nexport default class InPlaceEdit extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n this.state = {\n active: props.active,\n initialValue: props.value || '',\n value: props.value || ''\n };\n\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleStartEditing = this.handleStartEditing.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n this.handlePasswordFinished = this.handlePasswordFinished.bind(this);\n }\n\n componentDidUpdate(prevProps, prevState) {\n // If text has changed while in read mode, update text and discard changes.\n // Ignore update if in edit mode.\n const newValue = this.props.value || '';\n if (prevState.initialValue != newValue && !prevState.active) {\n this.setState({\n initialValue: newValue,\n value: newValue\n });\n }\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value || ''});\n }\n\n handleKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({value: this.props.value, active: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleEditingFinished(e);\n }\n }\n\n handleStartEditing() {\n if (!this.props.readOnly) {\n this.setState({active: true}, () => {\n if (this.selfRef.current) {\n this.selfRef.current.focus();\n }\n });\n }\n }\n\n handleEditingFinished(event) {\n const value = this.state.value.trim();\n if (this.props.required && (!event.target.checkValidity() || !value)) {\n // Empty input\n this.setState({value: this.props.value, active: false});\n return;\n }\n this.setState({active: false});\n if ((value || this.props.value) && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n handlePasswordFinished(value) {\n this.setState({active: false});\n if (value && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n render() {\n if (!this.state.active) {\n let spanText = this.props.type == 'password' ? '••••••••' : this.state.value;\n let spanClass = 'in-place-edit' + (this.props.readOnly ? ' disabled' : '');\n if (!spanText) {\n spanText = this.props.placeholder;\n spanClass += ' placeholder';\n }\n if (!this.props.multiline || this.props.multiline == 1) {\n spanClass += ' short';\n }\n\n return (\n {spanText}\n );\n }\n\n let element;\n const attr = {};\n if (this.props.type == 'password') {\n element = VisiblePassword;\n attr.onFinished = this.handlePasswordFinished;\n } else {\n if (this.props.multiline > 1) {\n element = 'textarea';\n attr.rows = this.props.multiline;\n attr.className = 'inplace-edit';\n } else {\n element = 'input';\n attr.type = this.props.type || 'text';\n }\n attr.value = this.state.value;\n attr.onChange = this.handeTextChange;\n attr.onKeyDown = this.handleKeyDown;\n attr.onBlur = this.handleEditingFinished;\n }\n attr.placeholder = this.props.placeholder;\n attr.required = this.props.required ? 'required' : '';\n attr.autoComplete = this.props.autoComplete;\n attr.autoFocus = true;\n attr.ref = this.selfRef;\n\n return React.createElement(element, attr, null);\n }\n};\n","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { MAX_TAG_COUNT, MAX_TAG_LENGTH, MIN_TAG_LENGTH } from '../config.js';\nimport { arrayEqual } from '../lib/utils.js';\n\nexport default class TagManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tags: this.props.tags || [],\n tagInput: '',\n activated: this.props.activated\n };\n\n this.handleTagInput = this.handleTagInput.bind(this);\n this.handleAddTag = this.handleAddTag.bind(this);\n this.handleRemoveTag = this.handleRemoveTag.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n const tags = nextProps.tags || [];\n if (!arrayEqual(tags, prevState.tags) && !prevState.activated) {\n return {tags: tags};\n }\n return null;\n }\n\n handleTagInput(text) {\n this.setState({tagInput: text});\n if (text.length > 0) {\n const last = text[text.length-1];\n if (text[0] == '\"') {\n // This is a quoted string.\n if (text.length > 1 && last == '\"') {\n this.handleAddTag(text.substring(1, text.length-1));\n }\n } else if (last == ',' || last == ' ' || last == ';' || last == '\"') {\n // User entered ',', ' ' or ';'\n this.handleAddTag(text.substring(0, text.length-1).trim());\n }\n }\n }\n\n handleAddTag(tag) {\n const maxTagCount = this.props.tinode.getServerLimit('maxTagCount', MAX_TAG_COUNT);\n\n if (tag.length > 0 && this.state.tags.length < maxTagCount) {\n const tags = this.state.tags.slice(0);\n tags.push(tag);\n\n this.setState({tags: tags, tagInput: ''});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n return tags;\n }\n return this.state.tags;\n }\n\n handleRemoveTag(tag, index) {\n const tags = this.state.tags.slice(0);\n tags.splice(index, 1);\n this.setState({tags: tags});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n }\n\n handleSubmit() {\n // Add unprocessed input to tags, submit the list.\n this.props.onSubmit(this.handleAddTag(this.state.tagInput.trim()));\n this.setState({activated: false, tags: this.props.tags || []});\n }\n\n handleCancel() {\n this.setState({activated: false, tagInput: '', tags: this.props.tags || []});\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n\n render() {\n const minTagLength = this.props.tinode.getServerLimit('minTagLength', MIN_TAG_LENGTH);\n const maxTagLength = this.props.tinode.getServerLimit('maxTagLength', MAX_TAG_LENGTH);\n\n let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < minTagLength || tag.length > maxTagLength)});\n });\n } else {\n this.state.tags.map((tag) => {\n tags.push({tag});\n });\n if (tags.length == 0) {\n tags = (\n \n \n \n );\n }\n }\n return (\n \n
\n \n
\n {this.state.activated ?\n
\n
{\n (add_tags_prompt) => \n }\n {this.props.onSubmit || this.props.onCancel ?\n
\n \n \n
\n : null}\n
\n :\n
\n }\n
\n );\n }\n};\n","// Widget for editing topic description.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarCrop from './avatar-crop.jsx';\nimport AvatarUpload from './avatar-upload.jsx';\nimport InPlaceEdit from './in-place-edit.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { AVATAR_SIZE, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE, MAX_TITLE_LENGTH,\n MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\nimport { imageScaled, blobToBase64, makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nexport default class TopicDescEdit extends React.Component {\n constructor(props) {\n super(props);\n\n const topic = this.props.tinode.getTopic(this.props.topic);\n const acs = topic.getAccessMode();\n this.state = {\n isMe: Tinode.isMeTopicName(this.props.topic),\n owner: acs && acs.isOwner(),\n fullName: topic.public ? topic.public.fn : undefined,\n private: topic.private ? topic.private.comment : null,\n description: topic.public ? topic.public.note : undefined,\n avatar: makeImageUrl(topic.public ? topic.public.photo : null),\n tags: topic.tags() || [],\n newAvatar: null,\n newAvatarMime: null\n };\n\n this.previousOnTags = null;\n\n this.tnNewTags = this.tnNewTags.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handleImageUpdated = this.handleImageUpdated.bind(this);\n this.handleAvatarCropped = this.handleAvatarCropped.bind(this);\n this.handlePrivateUpdate = this.handlePrivateUpdate.bind(this);\n this.handleDescriptionUpdate = this.handleDescriptionUpdate.bind(this);\n this.uploadAvatar = this.uploadAvatar.bind(this);\n this.handleAvatarCropCancel = this.handleAvatarCropCancel.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n }\n\n componentDidMount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n this.previousOnTags = topic.onTagsUpdated;\n topic.onTagsUpdated = this.tnNewTags;\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n topic.onTagsUpdated = this.previousOnTags;\n }\n\n tnNewTags(tags) {\n this.setState({tags: tags});\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn && this.state.fullName !== fn) {\n this.setState({fullName: fn});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(fn, null));\n }\n }\n\n handlePrivateUpdate(comment) {\n comment = comment.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.private !== comment) {\n this.setState({private: comment});\n this.props.onUpdateTopicDesc(this.props.topic, null, comment || Tinode.DEL_CHAR);\n }\n }\n\n handleDescriptionUpdate(desc) {\n desc = desc.trim().substring(0, MAX_TOPIC_DESCRIPTION_LENGTH);\n if (desc) {\n this.setState({description: desc});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, null, null, desc));\n }\n }\n\n // AvatarUpload widget has changed the avatar: received a new file or deleted the avatar.\n handleImageUpdated(mime, img) {\n // This will show the AvatarCropView is img is not null.\n this.setState({newAvatar: img, newAvatarMime: mime});\n if (!img) {\n // Deleting the avatar.\n this.setState({avatar: null});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, Tinode.DEL_CHAR));\n }\n }\n\n // AvatarCropView calls this method when the user has cropped the image.\n handleAvatarCropped(mime, blob, width, height) {\n const url = blob ? URL.createObjectURL(blob) : null;\n this.setState({avatar: url, newAvatar: null, newAvatarMime: null});\n if (blob) {\n this.uploadAvatar(mime, blob, width, height);\n }\n }\n\n // Utility method for converting cropped avatar blob to bytes for sending inband or\n // for uploading it to the server out of band.\n uploadAvatar(mime, blob, width, height) {\n const readyToUpload = (image) => {\n let {mime, blob} = image;\n if (blob.size > MAX_AVATAR_BYTES) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n\n this.setState({uploading: true});\n uploader.upload(blob)\n .then((url) => {\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, url));\n })\n .catch((err) => {\n this.props.onError(err, 'err');\n })\n .finally(() => {\n this.setState({uploading: false});\n });\n } else {\n this.setState({uploading: true});\n // Convert blob to base64-encoded bits.\n blobToBase64(blob)\n .then(b64 => {\n const du = makeImageUrl({data: b64.bits, type: mime});\n this.setState({source: du});\n this.props.onUpdateTopicDesc(this.props.topic, theCard(null, du));\n this.setState({uploading: false});\n });\n }\n };\n\n if (width > AVATAR_SIZE || height > AVATAR_SIZE || width != height) {\n // Avatar is not square or too large even after cropping. Shrink it and make square.\n imageScaled(blob, AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true)\n .then(scaled => readyToUpload(scaled))\n .catch(err => this.props.onError(err, 'err'));\n } else {\n readyToUpload({mime: mime, blob: blob, width: width, height: height});\n }\n }\n\n handleAvatarCropCancel(img) {\n this.setState({newAvatar: null, newAvatarMime: null});\n }\n\n handleTagsUpdated(tags) {\n // Check if tags have actually changed.\n if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n return;\n }\n this.props.onUpdateTags(tags);\n }\n\n render() {\n if (this.state.newAvatar) {\n return (\n \n );\n }\n\n const editable = this.state.isMe || this.state.owner;\n\n return (\n <>\n \n
\n \n \n {this.state.isMe ?\n
\n
\n
\n {\n (full_name_placeholder) => \n }\n
\n
\n :\n <>\n
\n
\n
\n {\n (group_name_placeholder) => \n }\n
\n
\n
\n
\n \n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n >\n }\n {editable || this.state.description ?\n
\n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n : null\n }\n
\n {editable ?\n <>\n \n {\n (title_tag_manager) => \n }\n >\n : null\n }\n >\n );\n }\n};\n","// View for editing topic (including 'me' topic) parameters.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport TopicDescEdit from '../widgets/topic-desc-edit.jsx';\n\nimport { arrayEqual, asEmail, asPhone } from '../lib/utils.js';\n\nexport default class TopicCommon extends React.Component {\n constructor(props) {\n super(props);\n\n const topic = this.props.tinode.getTopic(this.props.topic);\n const acs = topic.getAccessMode();\n this.state = {\n isMe: this.props.topic == 'me',\n owner: acs && acs.isOwner(),\n credentials: (topic.getCredentials ? topic.getCredentials() : null) || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n tags: []\n };\n\n this.previousTagsUpdated = undefined;\n\n this.onTagsUpdated = this.onTagsUpdated.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.tnCredsUpdated = this.tnCredsUpdated.bind(this);\n this.handleCredChange = this.handleCredChange.bind(this);\n this.handleCredKeyDown = this.handleCredKeyDown.bind(this);\n this.handleCredEntered = this.handleCredEntered.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n topic.onCredsUpdated = this.tnCredsUpdated;\n\n if (topic.onTagsUpdated != this.onTagsUpdated) {\n if (topic.getType() == 'grp') {\n this.previousTagsUpdated = topic.onTagsUpdated;\n topic.onTagsUpdated = this.onTagsUpdated;\n } else {\n this.previousTagsUpdated = undefined;\n }\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n topic.onCredsUpdated = undefined;\n topic.onTagsUpdated = this.previousTagsUpdated;\n }\n\n tnCredsUpdated(creds) {\n this.setState({credentials: creds || []});\n }\n\n handleCredChange(e) {\n this.setState({newCred: e.target.value, addCredInvalid: false});\n }\n\n handleCredKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({newCred: '', addCredActive: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleCredEntered(e);\n }\n }\n\n handleCredEntered(e) {\n const value = this.state.newCred.trim();\n if (!value) {\n this.setState({addCredActive: false, addCredInvalid: false});\n return;\n }\n\n let val = asPhone(value);\n let method;\n if (val) {\n method = 'tel';\n } else {\n val = asEmail(value);\n if (val) {\n method = 'email';\n }\n }\n if (method) {\n this.props.onCredAdd(method, val);\n this.setState({addCredActive: false, newCred: ''});\n } else {\n this.setState({addCredInvalid: true});\n }\n }\n\n // Server informs that the tags have been updated.\n onTagsUpdated(tags) {\n this.setState({tags: tags});\n\n if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {\n this.previousTagsUpdated(tags);\n }\n }\n\n // Request server to change tags.\n handleTagsUpdated(tags) {\n if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n this.props.onTopicTagsUpdateRequest(this.props.topic, tags);\n }\n }\n\n render() {\n const credentials = [];\n if (this.state.isMe) {\n this.state.credentials.map((cred) => {\n credentials.push(\n \n );\n });\n }\n\n return (\n \n
\n {this.state.isMe ?\n <>\n
\n
\n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n >\n : null\n }\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from './in-place-edit.jsx';\n\nimport { NO_ACCESS_MODE } from '../config.js';\n\nconst messages = defineMessages({\n clear_messages: {\n id: 'action_clear_messages',\n defaultMessage: 'Clear Messages',\n description: 'Flat button [Clear Messages] (soft-delete messages)'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n delete_messages: {\n id: 'action_delete_messages',\n defaultMessage: 'Clear Messages for All',\n description: 'Flat button [Clear for All] (hard-delete all messages)'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n topic_delete: {\n id: 'topic_delete',\n defaultMessage: 'Delete Conversation',\n description: 'Alert title when deleting the topic.'\n },\n topic_delete_warning: {\n id: 'topic_delete_warning',\n defaultMessage: 'Are you sure you want to delete this conversation? It cannot be undone.',\n description: 'Alert warning when deleting entire topic'\n },\n leave_chat: {\n id: 'action_leave_chat',\n defaultMessage: 'Leave Conversation',\n description: 'Flat button [Leave Conversation]'\n },\n leave_chat_warning: {\n id: 'leave_chat_warning',\n defaultMessage: 'Are you sure you want to leave this conversation?',\n description: 'Alert dialog warning when unsubscribing from a chat.'\n },\n block_contact: {\n id: 'action_block_contact',\n defaultMessage: \"Block Contact\",\n description: \"Flat button [Block Contact]\"\n },\n block_contact_warning: {\n id: 'block_contact_warning',\n defaultMessage: 'Are you sure you want to block this contact?',\n description: 'Alert dialog warning when blocking a contact.'\n },\n report_chat: {\n id: 'action_report_chat',\n defaultMessage: 'Report Conversation',\n description: 'Flat button [Report Group]'\n },\n report_chat_warning: {\n id: 'report_chat_warning',\n defaultMessage: 'Are you sure you want to block and report this conversation?',\n description: 'Alert dialog warning when reporting a conversation for abuse'\n },\n other_user: {\n id: 'label_other_user',\n defaultMessage: 'Other',\n description: 'Label for the other user when the user is unnamed'\n },\n});\n\nclass TopicSecurity extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleDeleteTopic = this.handleDeleteTopic.bind(this);\n this.handleDeleteMessages = this.handleDeleteMessages.bind(this);\n this.handleLeave = this.handleLeave.bind(this);\n this.handleBlock = this.handleBlock.bind(this);\n this.handleReport = this.handleReport.bind(this);\n }\n\n handleDeleteTopic(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.topic_delete), // title\n formatMessage(messages.topic_delete_warning), // content\n (() => { this.props.onDeleteTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleDeleteMessages(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(this.props.deleter ? messages.delete_messages : messages.clear_messages), // title\n formatMessage(this.props.deleter ? messages.delete_messages_warning : messages.clear_messages_warning), // content\n (() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleLeave(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.leave_chat), // title\n formatMessage(messages.leave_chat_warning), // content\n (() => { this.props.onLeaveTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleBlock(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.block_contact), // title\n formatMessage(messages.block_contact_warning), // content\n (() => { this.props.onBlockTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleReport(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.report_chat), // title\n formatMessage(messages.report_chat_warning), // content\n (() => { this.props.onReportTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n
\n
\n
\n {this.props.groupTopic ?\n <>\n
\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('want')}}>\n {this.props.access}\n \n
\n {!this.props.channel ?\n
\n
\n \n
\n
\n
Auth: {\n e.preventDefault();\n if (this.props.owner) {\n this.props.onLaunchPermissionsEditor('auth');\n }\n }}>{this.props.auth}\n
\n
Anon: {\n e.preventDefault();\n if (this.props.owner) {\n this.props.onLaunchPermissionsEditor('anon');\n }\n }}>{this.props.anon}\n
\n
\n
\n :\n null\n }\n >\n :\n
\n
\n \n
\n
\n
\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('want')}}>\n {this.props.access}\n \n
\n
{this.props.fullName ? this.props.fullName : formatMessage(messages.other_user)}:\n {e.preventDefault(); this.props.onLaunchPermissionsEditor('given')}}>\n {this.props.modeGiven2}\n \n
\n
\n
\n }\n
\n
\n );\n }\n};\n\nexport default injectIntl(TopicSecurity);\n","// InfoView: panel with topic/user info.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport ContactList from '../widgets/contact-list.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupManager from '../widgets/group-manager.jsx';\nimport MenuCancel from '../widgets/menu-cancel.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TopicCommon from '../widgets/topic-common.jsx';\nimport TopicSecurity from '../widgets/topic-security.jsx';\n\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH, NO_ACCESS_MODE } from '../config.js';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n info: {\n id: 'panel_title_info',\n description: 'Title for InfoView',\n defaultMessage: 'Info'\n },\n general: {\n id: 'panel_title_general',\n description: 'Title for TopicCommon.',\n defaultMessage: 'General'\n },\n security: {\n id: 'panel_title_security',\n description: 'Title for TopicSecirity and AccSecurity.',\n defaultMessage: 'Security'\n },\n members: {\n id: 'panel_title_members',\n description: 'Title for managing group members view.',\n defaultMessage: 'Members'\n },\n crop: {\n id: 'panel_title_crop',\n description: 'Title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n perm_want: {\n id: 'requested_permissions',\n defaultMessage: 'Requested',\n description: 'Title for permissions'\n },\n perm_given: {\n id: 'granted_permissions',\n defaultMessage: 'Granted',\n description: 'Title for permissions'\n },\n perm_auth: {\n id: 'permissions_authenticated',\n defaultMessage: 'Authenticated',\n description: 'Title for editing default authenticated permissions'\n },\n perm_anon: {\n id: 'permissions_anonymous',\n defaultMessage: 'Anonymous',\n description: 'Title for editing default anonymous permissions'\n },\n perm_user: {\n id: 'permissions_user',\n defaultMessage: 'User\\'s Permissions',\n description: 'Title for editing user\\'s permissions'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n});\n\nfunction _clip(str, length) {\n return str && str.substring(0, length);\n}\n\nclass InfoView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n topic: null,\n owner: false,\n admin: false,\n sharer: false,\n deleter: false,\n muted: false,\n address: null,\n groupTopic: undefined,\n channel: undefined,\n fullName: undefined,\n description: undefined,\n avatar: null,\n private: null,\n selectedContact: null,\n access: null,\n modeGiven: null,\n modeWant: null,\n modeGiven2: null, // P2P topic, the other user mode given\n modeWant2: null, // P2P topic, the other user mode want\n auth: null,\n anon: null,\n contactList: [],\n trustedBadges: [],\n previousMetaDesc: undefined,\n previousSubsUpdated: undefined,\n };\n\n this.resetSubs = this.resetSubs.bind(this);\n this.resetDesc = this.resetDesc.bind(this);\n this.resetTags = this.resetTags.bind(this);\n this.onMetaDesc = this.onMetaDesc.bind(this);\n this.onSubsUpdated = this.onSubsUpdated.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleMuted = this.handleMuted.bind(this);\n this.handleUnarchive = this.handleUnarchive.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleShowAddMembers = this.handleShowAddMembers.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleMemberSelected = this.handleMemberSelected.bind(this);\n this.handleContextMenu = this.handleContextMenu.bind(this);\n this.handleBackNavigate = this.handleBackNavigate.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n if (this.onMetaDesc != topic.onMetaDesc) {\n this.previousMetaDesc = topic.onMetaDesc;\n topic.onMetaDesc = this.onMetaDesc;\n\n this.previousSubsUpdated = topic.onSubsUpdated;\n topic.onSubsUpdated = this.onSubsUpdated;\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n this.resetDesc(topic, props);\n this.resetSubs(topic, props);\n this.resetTags(topic);\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.setState({topic: null});\n topic.onMetaDesc = this.previousMetaDesc;\n topic.onSubsUpdated = this.previousSubsUpdated;\n }\n\n resetSubs(topic, props) {\n const newState = {contactList: []};\n if (topic.getType() == 'p2p') {\n // Fetch the other party in the p2p conversation.\n // Topic may not be ready yet, so check if user is found.\n const user2 = topic.subscriber(props.topic);\n if (user2) {\n newState.modeGiven2 = user2.acs.getGiven();\n newState.modeWant2 = user2.acs.getWant();\n } else {\n newState.modeGiven2 = NO_ACCESS_MODE;\n newState.modeWant2 = NO_ACCESS_MODE;\n }\n } else {\n topic.subscribers((sub) => {\n newState.contactList.push(sub);\n }, this);\n }\n\n this.setState(newState);\n }\n\n resetDesc(topic, props) {\n const defacs = topic.getDefaultAccess() || {};\n const acs = topic.getAccessMode();\n\n const badges = [];\n if (topic.trusted) {\n for (const [key, val] of Object.entries(topic.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n }\n\n this.setState({\n owner: acs && acs.isOwner(),\n admin: acs && acs.isAdmin(),\n sharer: acs && acs.isSharer(),\n deleter: acs && acs.isDeleter(),\n muted: acs && acs.isMuted(),\n\n fullName: _clip(topic.public ? topic.public.fn : undefined, MAX_TITLE_LENGTH),\n description: _clip(topic.public ? topic.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),\n avatar: makeImageUrl(topic.public ? topic.public.photo : null),\n trustedBadges: badges,\n private: _clip(topic.private ? topic.private.comment : null, MAX_TITLE_LENGTH),\n archived: topic.isArchived(),\n address: topic.name,\n groupTopic: topic.isGroupType(),\n channel: topic.isChannelType() || topic.chan,\n access: acs ? acs.getMode() : undefined,\n modeGiven: acs ? acs.getGiven() : undefined,\n modeWant: acs ? acs.getWant() : undefined,\n auth: defacs.auth,\n anon: defacs.anon\n });\n }\n\n resetTags(topic) {\n if (topic.getType() != 'grp') {\n return;\n }\n\n const acs = topic.getAccessMode();\n if (acs && acs.isOwner()) {\n // Requesting tags: owner is editing the topic.\n topic.getMeta(topic.startMetaQuery().withTags().build());\n }\n }\n\n onMetaDesc(desc) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetDesc(topic, this.props);\n\n if (this.previousMetaDesc && this.previousMetaDesc != this.onMetaDesc) {\n this.previousMetaDesc(desc);\n }\n }\n\n onSubsUpdated(subs) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetSubs(topic, this.props);\n\n if (this.previousSubsUpdated && this.previousSubsUpdated != this.onSubsUpdated) {\n this.previousSubsUpdated(subs);\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onTopicDescUpdate(this.props.topic, theCard(null, img || Tinode.DEL_CHAR), null);\n }\n\n handleMuted(ignored, checked) {\n this.setState({muted: checked});\n this.props.onChangePermissions(this.props.topic, checked ? '-P' : '+P');\n }\n\n handleUnarchive(ignored, ignored2) {\n this.props.onTopicUnArchive(this.props.topic);\n }\n\n handlePermissionsChanged(which, perm) {\n switch (which) {\n case 'auth':\n this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {auth: perm});\n break;\n case 'anon':\n this.props.onTopicDescUpdateRequest(this.props.topic, null, null, {anon: perm});\n break;\n case 'mode':\n case 'want':\n this.props.onChangePermissions(this.props.topic, perm);\n break;\n case 'given':\n this.props.onChangePermissions(this.props.topic, perm, this.props.topic);\n break;\n case 'user':\n this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited);\n break;\n }\n\n this.handleBackNavigate();\n }\n\n handleLaunchPermissionsEditor(which, uid) {\n const {formatMessage} = this.props.intl;\n let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar;\n switch (which) {\n case 'mode':\n toEdit = this.state.access;\n break;\n case 'want':\n toEdit = this.state.modeWant;\n toCompare = this.state.modeGiven;\n if (this.state.owner) {\n // Don't allow owner to unset 'O' permission. All other permissions are OK.\n toSkip = 'O';\n } else {\n // Allow accepting any of 'ASDO' permissions but don't allow asking for them.\n toSkip = Tinode.AccessMode.encode(Tinode.AccessMode.diff('ASDO', this.state.modeGiven));\n if (this.state.channel) {\n // Channels are read-only.\n toSkip += 'W';\n }\n }\n titleEdit = formatMessage(messages.perm_want);\n titleCompare = formatMessage(messages.perm_given);\n break;\n case 'given':\n toEdit = this.state.modeGiven2;\n toCompare = this.state.modeWant2;\n toSkip = this.state.groupTopic ? (this.state.owner ? '' : 'O') : 'ASDO';\n titleEdit = formatMessage(messages.perm_given);\n titleCompare = formatMessage(messages.perm_want);\n break;\n case 'auth':\n toEdit = this.state.auth;\n toSkip = 'O';\n break;\n case 'anon':\n toEdit = this.state.anon;\n toSkip = 'O';\n break;\n case 'user': {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(uid);\n if (!user || !user.acs) {\n return;\n }\n toEdit = user.acs.getGiven();\n toCompare = user.acs.getWant();\n toSkip = this.state.owner ? '' : 'O';\n titleEdit = formatMessage(messages.perm_given);\n titleCompare = formatMessage(messages.perm_want);\n if (user.public) {\n userTitle = user.public.fn;\n userAvatar = user.public.photo;\n }\n break;\n }\n default:\n console.error(\"Unknown permission editing mode '\" + which + \"'\");\n return;\n }\n this.setState({\n userPermissionsEdited: uid,\n userPermissionsTitle: userTitle,\n userPermissionsAvatar: userAvatar,\n editedPermissions: toEdit,\n immutablePermissions: toCompare,\n editedPermissionsTitle: titleEdit,\n immutablePermissionsTitle: titleCompare,\n editedPermissionsSkipped: toSkip,\n });\n this.props.onNavigate(`perm/${which}`);\n }\n\n handleShowAddMembers(e) {\n e.preventDefault();\n this.props.onInitFind();\n this.props.onNavigate('members');\n }\n\n handleMemberUpdateRequest(members, added, removed) {\n this.props.onMemberUpdateRequest(this.props.topic, added, removed);\n this.props.onNavigate('info');\n }\n\n handleMemberSelected(uid) {\n this.setState({selectedContact: uid});\n }\n\n handleBackNavigate() {\n const args = (this.props.panel || 'info').split('/');\n if (args[0] == 'info') {\n this.props.onNavigate(null);\n } else if (args[0] == 'perm') {\n if (args[1] == 'user') {\n this.props.onNavigate('info');\n } else {\n this.props.onNavigate('security');\n }\n } else {\n this.props.onNavigate('info');\n }\n }\n\n handleContextMenu(params) {\n const {formatMessage} = this.props.intl;\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(params.topicName);\n if (!user || !user.acs) {\n return;\n }\n\n const isMe = this.props.tinode.isMe(params.topicName);\n const menuItems = [\n {title: formatMessage(messages.edit_permissions), handler: () => {\n this.handleLaunchPermissionsEditor(isMe ? 'want' : 'user', params.topicName);\n }}\n ];\n if (!isMe) {\n menuItems.push('member_delete');\n }\n menuItems.push(user.acs.isMuted() ? 'member_unmute' : 'member_mute');\n if (!isMe) {\n menuItems.push(user.acs.isJoiner() ? 'member_block' : 'member_unblock');\n }\n this.props.showContextMenu({\n topicName: this.props.topic,\n x: params.x,\n y: params.y,\n user: params.topicName}, menuItems);\n }\n\n render() {\n const args = (this.props.panel || 'info').split('/');\n const view = args[0];\n args.shift();\n\n const {formatMessage} = this.props.intl;\n const panelTitle = formatMessage((view == 'perm' ? messages['perm_' + args[0]] : messages[view])\n || messages['info']);\n\n return (\n \n
\n
{panelTitle}
\n
\n \n
\n
\n {this.props.displayMobile ?\n
: null}\n {view == 'members' ?\n
\n :\n view == 'perm' && args.length > 0 ?\n
this.handlePermissionsChanged(args[0], mode)}\n onCancel={this.handleBackNavigate} />\n :\n view == 'general' ?\n \n :\n view == 'security' ?\n \n :\n \n
\n
\n
\n \n \n
\n {this.state.archived ?\n
\n \n \n
\n :\n null\n }\n
\n
\n {this.state.groupTopic && this.state.sharer ?\n <>\n
\n
\n \n
\n
\n
{\n (no_members) => \n }\n >\n :\n null\n }\n
\n }\n \n );\n }\n};\n\nexport default injectIntl(InfoView);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\nimport { bytesToHumanSize } from '../lib/strformat.js';\nimport { isUrlRelative, sanitizeUrl } from '../lib/utils.js';\n\nexport default class Attachment extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n downloader: null,\n progress: 0\n };\n\n this.downloadFile = this.downloadFile.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n downloadFile(url, filename, mimetype) {\n if (!url) {\n this.props.onError(\"Invalid download URL '\" + url + \"'\");\n return;\n }\n\n const downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype,\n (loaded) => {\n this.setState({progress: loaded / this.props.size});\n },\n (err) => {\n this.props.onError(err, 'err');\n }\n ).then(() => {\n this.setState({downloader: null, progress: 0});\n }).catch((err) => {\n if (err) {\n this.props.onError(\"Error downloading file: \" + err.message, 'err');\n }\n this.setState({downloader: null, progress: 0});\n });\n }\n\n handleCancel() {\n if (this.props.uploading) {\n this.props.onCancelUpload();\n } else if (this.state.downloader) {\n this.state.downloader.cancel();\n }\n }\n\n render() {\n let filename = this.props.filename || 'file_attachment';\n if (filename.length > 36) {\n filename = filename.substr(0, 16) + '...' + filename.substr(-16);\n }\n let size = this.props.size > 0 ?\n ({bytesToHumanSize(this.props.size)}) :\n null;\n\n // Detect if the download URL is relative or absolute.\n // If the URL is relative use LargeFileHelper to attach authentication\n // credentials to the request.\n let url, helperFunc;\n if (!this.props.uploading && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) {\n // Relative URL. Use download helper.\n url = '#';\n helperFunc = (e) => {\n e.preventDefault();\n this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);\n };\n } else {\n url = sanitizeUrl(this.props.downloadUrl);\n helperFunc = null;\n }\n const downloadWidget = <>file_download >;\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploading || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\n};\n","// Received/read indicator.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { deliveryMarker } from '../lib/utils.js';\n\nconst messages = defineMessages({\n message_sending: {\n id: 'message_sending',\n defaultMessage: 'sending...',\n description: 'Message being sent, in place of time stamp'\n },\n message_sending_failed: {\n id: 'message_sending_failed',\n defaultMessage: 'failed',\n description: 'Failed to send message, in place of time stamp'\n }\n});\n\nclass ReceivedMarker extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let timestamp;\n if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {\n timestamp = formatMessage(messages.message_sending);\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n timestamp = formatMessage(messages.message_sending_failed);\n } else {\n timestamp = this.props.timestamp.toLocaleTimeString(this.props.intl.locale, { timeStyle: 'short' });\n }\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n\n return (\n \n {timestamp}{'\\u00a0'}{marker}\n \n );\n }\n};\n\nexport default injectIntl(ReceivedMarker);\n","// Single chat message bubble, sent or received.\n\nimport React from 'react';\nimport { FormattedMessage, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport Attachment from './attachment.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport ReceivedMarker from './received-marker.jsx'\n\nimport { fullFormatter } from '../lib/formatters.js';\nimport { sanitizeUrl } from '../lib/utils.js';\n\nclass BaseChatMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n progress: 0\n };\n\n if (props.uploader) {\n props.uploader.onProgress = this.handleProgress.bind(this);\n }\n\n this.handleImagePreview = this.handleImagePreview.bind(this);\n this.handleFormButtonClick = this.handleFormButtonClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.formatterContext = {\n formatMessage: props.intl.formatMessage.bind(props.intl),\n viewportWidth: props.viewportWidth,\n authorizeURL: props.tinode.authorizeURL.bind(props.tinode),\n onImagePreview: this.handleImagePreview,\n onFormButtonClick: this.handleFormButtonClick,\n onQuoteClick: this.handleQuoteClick\n };\n }\n\n handleImagePreview(e) {\n e.preventDefault();\n this.props.onImagePreview({\n url: e.target.src,\n filename: e.target.title,\n width: e.target.dataset.width,\n height: e.target.dataset.height,\n size: e.target.dataset.size,\n type: e.target.dataset.mime\n });\n }\n\n handleFormButtonClick(e) {\n e.preventDefault();\n const data = {\n seq: this.props.seq\n };\n data.resp = {};\n if (e.target.dataset.name) {\n data.resp[e.target.dataset.name] = e.target.dataset.val ? e.target.dataset.val :\n e.target.dataset.val === undefined ? 1 : '' + e.target.dataset.val;\n }\n if (e.target.dataset.act == 'url') {\n data.ref = sanitizeUrl(e.target.dataset.ref) || 'about:blank';\n }\n const text = e.target.dataset.title || 'unknown';\n this.props.onFormResponse(e.target.dataset.act, text, data);\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const menuItems = [];\n if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n menuItems.push('menu_item_send_retry');\n }\n if (this.props.userIsWriter &&\n this.props.received > Tinode.MESSAGE_STATUS_FAILED &&\n this.props.received < Tinode.MESSAGE_STATUS_DEL_RANGE) {\n menuItems.push('menu_item_reply');\n }\n menuItems.push('menu_item_forward');\n\n this.props.showContextMenu({\n seq: this.props.seq,\n content: this.props.content,\n userFrom: this.props.userFrom,\n userName: this.props.userName,\n y: e.pageY,\n x: e.pageX,\n pickReply: this.props.pickReply\n }, menuItems);\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.onCancelUpload(this.props.seq, this.props.uploader);\n }\n\n handleQuoteClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const replyToSeq = this.props.replyToSeq;\n if (replyToSeq) {\n this.props.onQuoteClick(replyToSeq);\n }\n }\n\n render() {\n const sideClass = this.props.sequence + ' ' + (this.props.response ? 'left' : 'right');\n const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';\n const avatar = this.props.userAvatar || true;\n const fullDisplay = (this.props.isGroup && this.props.response &&\n (this.props.sequence == 'single' || this.props.sequence == 'last'));\n\n let content = this.props.content;\n const attachments = [];\n if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) {\n Drafty.attachments(content, (att, i) => {\n if (att.mime == 'application/json') {\n // Don't show json objects as attachments.\n // They are not meant for users.\n return;\n }\n attachments.push();\n }, this);\n const tree = Drafty.format(content, fullFormatter, this.formatterContext);\n content = React.createElement(React.Fragment, null, tree);\n } else if (typeof content != 'string') {\n content = <>warning_amber \n >\n }\n\n return (\n \n {this.props.isGroup && this.props.response ?\n \n {fullDisplay ?\n :\n null}\n
:\n null}\n \n
\n
\n
\n {content}\n {attachments}\n
\n {this.props.timestamp ?\n
\n : null}\n
\n {this.props.showContextMenu ?\n
\n \n expand_more\n \n : null\n }\n
\n {fullDisplay ?\n
\n {this.props.userName ||\n \n }\n
\n : null\n }\n
\n \n );\n }\n};\n\nconst IntlChatMessage = injectIntl(BaseChatMessage);\nconst ChatMessage = React.forwardRef((props, ref) => );\n\nexport default ChatMessage;\n","// Audio recorder widget.\n\nimport React from 'react';\n\nimport { secondsToTime } from '../lib/strformat';\n\n// FFT resolution.\nconst BUFFER_SIZE = 256;\n// Thickness of a visualization bar.\nconst LINE_WIDTH = 4;\n// Spacing between two visualization bars.\nconst SPACING = 2;\n// Duration represented by one visualization bar.\nconst MILLIS_PER_BAR = 100;\n// Color of histogram bars\nconst BAR_COLOR = '#8fbed6';\n// Scaling for visualization bars.\nconst BAR_SCALE = 96.0;\n// Background color\nconst BKG_COLOR = '#eeeeee';\n// Minimum duration of a recording in milliseconds.\nconst MIN_DURATION = 200;\n// Maximum duration of a recording in milliseconds (10 min).\nconst MAX_DURATION = 5000; // 600000;\n\nexport default class AudioRecorder extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n enabled: true,\n audioRecord: null,\n recording: true,\n paused: false,\n duration: '0:00'\n };\n\n this.visualize = this.visualize.bind(this);\n this.initMediaRecording = this.initMediaRecording.bind(this);\n this.handleResume = this.handleResume.bind(this);\n this.handlePause = this.handlePause.bind(this);\n this.handleDelete = this.handleDelete.bind(this);\n this.handleDone = this.handleDone.bind(this);\n\n this.durationMillis = 0;\n this.startedOn = null;\n this.viewBuffer = [];\n this.canvasRef = React.createRef();\n }\n\n componentDidMount() {\n this.canvasContext = this.canvasRef.current.getContext('2d');\n this.canvasContext.lineCap = 'round';\n // To reduce line blurring.\n this.canvasContext.translate(0.5, 0.5);\n\n this.stream = null;\n this.mediaRecorder = null;\n this.audioContext = null;\n this.sampleRate = null;\n this.audioInput = null;\n this.analyser = null;\n\n this.audioChunks = [];\n\n // Start recorder right away.\n try {\n navigator.mediaDevices.getUserMedia({audio: true, video: false}).then(this.initMediaRecording, this.props.onError);\n } catch (err) {\n this.props.onError(err);\n }\n }\n\n // Draw amplitude of sound.\n visualize() {\n const pcmData = new Uint8Array(this.analyser.frequencyBinCount);\n const width = this.canvasRef.current.width;\n const height = this.canvasRef.current.height;\n const viewLength = (width / (LINE_WIDTH + SPACING)) | 0;\n const viewDuration = MILLIS_PER_BAR * viewLength;\n\n this.canvasContext.fillStyle = BKG_COLOR;\n this.canvasContext.lineWidth = LINE_WIDTH;\n this.canvasContext.strokeStyle = BAR_COLOR;\n\n let prevBarCount = 0;\n const drawFrame = () => {\n if (!this.startedOn) {\n return;\n }\n window.requestAnimationFrame(drawFrame);\n\n const duration = this.durationMillis + (Date.now() - this.startedOn);\n // Update record length timer.\n this.setState({duration: secondsToTime(duration / 1000)});\n\n // Check if record is too long.\n if (duration > MAX_DURATION) {\n this.mediaRecorder.pause();\n this.durationMillis += Date.now() - this.startedOn;\n this.startedOn = null;\n this.setState({enabled: false, recording: false, duration: secondsToTime(this.durationMillis / 1000)});\n }\n\n // Draw histogram.\n\n // Get current waveform and calculate its amplitude.\n this.analyser.getByteTimeDomainData(pcmData);\n let volume = 0.0;\n for (const amplitude of pcmData) {\n volume += (amplitude - 127) ** 2;\n }\n volume = Math.sqrt(volume/pcmData.length);\n\n let barCount = (duration / MILLIS_PER_BAR) | 0;\n // Shift of the histogram along x-axis to make scrolling smooth. No need to shift if recording is too short.\n const dx = viewDuration > duration ? 0 :\n (duration - MILLIS_PER_BAR * barCount) / MILLIS_PER_BAR * (LINE_WIDTH + SPACING);\n\n if (prevBarCount != barCount) {\n prevBarCount = barCount;\n // Add new amplitude visualization bar.\n this.viewBuffer.push(volume);\n if (this.viewBuffer.length > viewLength) {\n // Keep at most 'viewLength' amplitude bars.\n this.viewBuffer.shift();\n }\n }\n\n // Clear canvas.\n this.canvasContext.fillRect(0, 0, width, height);\n\n // Draw amplitude bars.\n this.canvasContext.beginPath();\n for (let i = 0; i < this.viewBuffer.length; i++) {\n let x = i * (LINE_WIDTH + SPACING) - dx;\n let y = this.viewBuffer[i] / BAR_SCALE * height;\n\n this.canvasContext.moveTo(x, (height - y) * 0.5);\n this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);\n }\n // Actually draw the bars on canvas.\n this.canvasContext.stroke();\n }\n\n drawFrame();\n }\n\n handlePause(e) {\n e.preventDefault();\n this.mediaRecorder.pause();\n this.durationMillis += Date.now() - this.startedOn;\n this.startedOn = null;\n this.setState({recording: false, duration: secondsToTime(this.durationMillis / 1000)});\n }\n\n handleResume(e) {\n e.preventDefault();\n if (this.state.enabled) {\n this.startedOn = Date.now();\n this.mediaRecorder.resume();\n this.setState({recording: true}, this.visualize);\n }\n }\n\n handleDelete(e) {\n e.preventDefault();\n this.durationMillis = 0;\n this.startedOn = null;\n this.mediaRecorder.stop();\n this.mediaRecorder = null;\n this.setState({recording: false});\n }\n\n handleDone(e) {\n e.preventDefault();\n this.setState({recording: false});\n if (this.startedOn) {\n this.durationMillis += Date.now() - this.startedOn;\n this.startedOn = null;\n }\n // Stop recording and return data.\n if (this.mediaRecorder) {\n this.mediaRecorder.stop();\n }\n }\n\n initMediaRecording(stream) {\n this.stream = stream;\n this.mediaRecorder = new MediaRecorder(stream);\n\n // The following code is needed for visualization.\n this.audioContext = new AudioContext();\n this.sampleRate = this.audioContext.sampleRate;\n this.audioInput = this.audioContext.createMediaStreamSource(stream);\n this.analyser = this.audioContext.createAnalyser();\n this.analyser.fftSize = BUFFER_SIZE;\n this.audioInput.connect(this.analyser);\n\n this.mediaRecorder.onstop = _ => {\n const blob = new Blob(this.audioChunks, { 'type' : 'audio/mp3; codecs=mp3' });\n this.audioChunks = [];\n const url = window.URL.createObjectURL(blob);\n if (this.durationMillis > MIN_DURATION) {\n this.props.onFinished(url, this.durationMillis);\n } else {\n this.props.onDeleted();\n }\n }\n\n this.mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n this.audioChunks.push(e.data);\n }\n }\n\n this.durationMillis = 0;\n this.startedOn = Date.now();\n this.mediaRecorder.start();\n this.visualize();\n }\n\n render() {\n const resumeClass = 'material-icons ' + (this.state.enabled ? 'red' : 'gray');\n return (\n );\n }\n}\n","// Send message form.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport AudioRecorder from './audio-recorder.jsx';\n\nimport { KEYPRESS_DELAY } from '../config.js';\nimport { filePasted } from '../lib/blob-helpers.js';\nimport { replyFormatter } from '../lib/formatters.js';\n\nconst messages = defineMessages({\n messaging_disabled: {\n id: 'messaging_disabled_prompt',\n defaultMessage: 'Messaging disabled',\n description: 'Prompt in SendMessage in read-only topic'\n },\n type_new_message: {\n id: 'new_message_prompt',\n defaultMessage: 'New message',\n description: 'Prompt in send message field'\n },\n add_image_caption: {\n id: 'image_caption_prompt',\n defaultMessage: 'Image caption',\n description: 'Prompt in SendMessage for attached image'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n cannot_initiate_upload: {\n id: 'cannot_initiate_file_upload',\n defaultMessage: 'Cannot initiate file upload.',\n description: 'Generic error messagewhen attachment fails'\n },\n});\n\nclass SendMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n quote: null,\n message: '',\n audioRec: false,\n audioEnabled: !!navigator.mediaDevices.getUserMedia,\n // Make initial keypress time as if it happened 5001 milliseconds in the past.\n keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1\n };\n\n this.handlePasteEvent = this.handlePasteEvent.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleAttachAudio = this.handleAttachAudio.bind(this);\n this.handleSend = this.handleSend.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleMessageTyping = this.handleMessageTyping.bind(this);\n\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.formatReply = this.formatReply.bind(this);\n }\n\n componentDidMount() {\n if (this.messageEditArea) {\n this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);\n }\n\n this.setState({quote: this.formatReply()});\n }\n\n componentWillUnmount() {\n if (this.messageEditArea) {\n this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentDidUpdate(prevProps) {\n if (this.messageEditArea) {\n this.messageEditArea.focus();\n }\n\n if (prevProps.reply != this.props.reply) {\n this.setState({quote: this.formatReply()});\n }\n }\n\n formatReply() {\n return this.props.reply ?\n Drafty.format(this.props.reply.content, replyFormatter, {\n formatMessage: this.props.intl.formatMessage.bind(this.props.intl),\n authorizeURL: this.props.tinode.authorizeURL.bind(this.props.tinode)\n }) : null;\n }\n\n handlePasteEvent(e) {\n if (this.props.disabled) {\n return;\n }\n // FIXME: handle large files too.\n if (filePasted(e,\n (bits, mime, width, height, fname) => {\n this.props.onAttachImage(mime, bits, width, height, fname);\n },\n (mime, bits, fname) => {\n this.props.onAttachFile(mime, bits, fname);\n },\n this.props.onError)) {\n\n // If a file was pasted, don't paste base64 data into input field.\n e.preventDefault();\n }\n }\n\n handleAttachImage(e) {\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachImage(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleAttachFile(e) {\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachFile(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleAttachAudio(url, duration) {\n this.setState({audioRec: false});\n console.log(\"this.props.onAttachAudio\", url, duration);\n // this.props.onAttachAudio(url, duration);\n }\n\n handleSend(e) {\n e.preventDefault();\n const message = this.state.message.trim();\n if (message || this.props.acceptBlank || this.props.noInput) {\n this.props.onSendMessage(message);\n this.setState({message: ''});\n }\n }\n\n /* Send on Enter key */\n handleKeyPress(e) {\n if (this.state.audioRec) {\n // Ignore key presses while audio is being recorded.\n e.preventDefault();\n e.stopPropagation();\n return;\n }\n\n // Remove this if you don't want Enter to trigger send\n if (e.key === 'Enter') {\n // Have Shift-Enter insert a line break instead\n if (!e.shiftKey) {\n e.preventDefault();\n e.stopPropagation();\n\n this.handleSend(e);\n }\n }\n }\n\n handleMessageTyping(e) {\n const newState = {message: e.target.value};\n if (this.props.onKeyPress) {\n const now = new Date().getTime();\n if (now - this.state.keypressTimestamp > KEYPRESS_DELAY) {\n this.props.onKeyPress();\n newState.keypressTimestamp = now;\n }\n }\n this.setState(newState);\n }\n\n handleQuoteClick(e) {\n e.preventDefault();\n e.stopPropagation();\n if (this.props.reply && this.props.onQuoteClick) {\n const replyToSeq = this.props.reply.seq;\n this.props.onQuoteClick(replyToSeq);\n }\n }\n\n render() {\n const { formatMessage } = this.props.intl;\n const prompt = this.props.disabled ?\n formatMessage(messages.messaging_disabled) :\n (this.props.messagePrompt ?\n formatMessage(messages[this.props.messagePrompt]) :\n formatMessage(messages.type_new_message));\n\n const quote =\n (\n
\n {this.state.quote}\n
);\n\n return (\n \n {this.state.quote && !this.props.noInput ? quote : null}\n
\n
\n );\n }\n};\n\nexport default injectIntl(SendMessage);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { bytesToHumanSize, shortenFileName } from '../lib/strformat.js';\n\n// Get material icon name from mime type.\nfunction iconFromMime(mime) {\n // If more icons become available in material icons, add them to this mime-to-icon mapping.\n const mimeToIcon = {default: 'insert_drive_file', image: 'image', text: 'description', video: 'theaters'};\n\n return mimeToIcon[mime] || mimeToIcon[(mime || '').split('/')[0]] || mimeToIcon['default'];\n}\n\nexport default class DocPreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSendDoc = this.handleSendDoc.bind(this);\n }\n\n handleSendDoc(caption) {\n this.props.onClose();\n this.props.onSendMessage(this.props.content.file);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n return (\n \n
\n
\n
\n
{iconFromMime(this.props.content.type)}\n
{shortenFileName(this.props.content.name, 24) || '-'}
\n
{this.props.content.type || 'application/octet-stream'}
\n
{bytesToHumanSize(this.props.content.size)}
\n
\n
\n
\n
\n );\n }\n};\n","// GroupSubs: a list of group subscribers currently online.\n// Show in the topic title bar\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { MAX_ONLINE_IN_TOPIC } from '../config.js'\n\nimport LetterTile from './letter-tile.jsx';\nimport { makeImageUrl } from '../lib/blob-helpers.js';\n\nexport default class GroupSubs extends React.Component {\n constructor(props) {\n super(props);\n }\n\n render() {\n const usersOnline = [];\n const totalCount = (this.props.subscribers || []).length;\n const countToShow = Math.min(MAX_ONLINE_IN_TOPIC, totalCount);\n\n (this.props.subscribers || []).some((sub) => {\n usersOnline.push(\n \n \n
\n );\n return usersOnline.length == countToShow;\n });\n\n return (\n {usersOnline} {totalCount > countToShow ?\n \n \n : null}\n
\n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize, shortenFileName } from '../lib/strformat.js';\n\nexport default class ImagePreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n width: 0,\n height: 0\n };\n\n this.handleSendImage = this.handleSendImage.bind(this);\n }\n\n assignWidth(node) {\n if (node && !this.state.width) {\n const bounds = node.getBoundingClientRect();\n this.setState({\n width: bounds.width | 0,\n height: bounds.height | 0\n });\n }\n }\n\n handleSendImage(caption) {\n this.props.onClose();\n this.props.onSendMessage(caption, this.props.content.blob);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n\n const dim = fitImageSize(this.props.content.width, this.props.content.height,\n this.state.width, this.state.height, false);\n const size = dim ? { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' } :\n ((this.props.content.width > this.props.content.height) ? {width: '100%'} : {height: '100%'});\n size.maxWidth = '100%';\n size.maxHeight = '100%';\n\n // Average font aspect ratio is ~0.5; File name takes 1/3 of the viewport width.\n const maxlength = Math.max(((this.state.width / REM_SIZE / 1.5) | 0) - 2, 12);\n const fname = shortenFileName(this.props.content.name, maxlength) || '-';\n\n const width = this.props.content.width || '-';\n const height = this.props.content.height || '-';\n return (\n \n
\n
this.assignWidth(node)}>\n

\n
\n {this.props.onSendMessage ?\n
\n :\n }\n
\n );\n }\n};\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Invitation extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleButtonAction = this.handleButtonAction.bind(this);\n }\n\n handleButtonAction(evt, data) {\n evt.preventDefault();\n this.props.onAction(data);\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n \n \n
\n
\n );\n }\n};\n","/* This is just a static page to display when no conversation is selected. */\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME } from '../config.js';\n\nexport default class LogoView extends React.PureComponent {\n render() {\n const version = APP_NAME + ' (' + Tinode.getLibrary() + ')';\n return (\n \n
\n
\n
\n Tinode Web
\n \n
{version}
\n
{this.props.serverVersion} ({this.props.serverAddress})
\n
\n
\n );\n }\n};\n","// Message bubble with non-payload content, such as an indicator of deleted content or a date stamp.\n\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class MetaMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n let content = null;\n let bubbleClass = 'bubble';\n if (this.props.deleted) {\n // Message represents a range of deleted messages.\n content = <>block \n \n >\n bubbleClass += ' deleted';\n } else if (this.props.date) {\n // A date badge.\n content = <>{this.props.date}>\n bubbleClass += ' date';\n }\n\n if (!content) {\n return <>{null}>\n }\n\n return (\n \n \n \n );\n }\n};\n","// Utility class for hash navigation.\n\n// Parse hash as in http://www.example.com/path#hash as if it were\n// path and arguments.\nexport default class HashNavigation {\n static parseUrlHash(hash) {\n // Split path from args, path -> parts[0], args->path[1]\n const parts = hash.split('?', 2);\n const params = {};\n let path = [];\n if (parts[0]) {\n path = parts[0].replace('#', '').split('/');\n }\n if (parts[1]) {\n parts[1].split('&').forEach(function(part) {\n const item = part.split('=');\n if (item[0]) {\n params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);\n }\n });\n }\n return {path: path, params: params};\n }\n\n static navigateTo(url) {\n window.location.hash = url;\n }\n\n static composeUrlHash(path, params) {\n let url = path.join('/');\n const args = [];\n for (const key in params) {\n if (params.hasOwnProperty(key)) {\n args.push(key + '=' + params[key]);\n }\n }\n if (args.length > 0) {\n url += '?' + args.join('&');\n }\n return url;\n }\n\n static addUrlParam(hash, key, value) {\n const parsed = this.parseUrlHash(hash);\n parsed.params[key] = value;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static removeUrlParam(hash, key) {\n const parsed = this.parseUrlHash(hash);\n delete parsed.params[key];\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlSidePanel(hash, sidepanel) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[0] = sidepanel;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlInfoPanel(hash, infopanel) {\n const parsed = this.parseUrlHash(hash);\n if (infopanel) {\n parsed.params.info = infopanel;\n } else {\n delete parsed.params.info;\n }\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlTopic(hash, topic) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[1] = topic;\n // Close InfoView on topic change.\n delete parsed.params.info;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n}\n","// Panel with a chat.\n\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\nconst Drafty = Tinode.Drafty;\n\nimport ChatMessage from '../widgets/chat-message.jsx';\nimport ContactBadges from '../widgets/contact-badges.jsx';\nimport DocPreview from '../widgets/doc-preview.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupSubs from '../widgets/group-subs.jsx';\nimport ImagePreview from '../widgets/image-preview.jsx';\nimport Invitation from '../widgets/invitation.jsx';\nimport LetterTile from '../widgets/letter-tile.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport LogoView from './logo-view.jsx';\nimport MetaMessage from '../widgets/meta-message.jsx';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY,\n MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE,\n READ_DELAY, QUOTED_REPLY_LENGTH } from '../config.js';\nimport { blobToBase64, fileToBase64, imageScaled, makeImageUrl } from '../lib/blob-helpers.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\n\n// Run timer with this frequency (ms) for checking notification queue.\nconst NOTIFICATION_EXEC_INTERVAL = 300;\n// Scroll distance before [go to latest message] button is shown.\nconst SHOW_GO_TO_LAST_DIST = 100;\n\nconst messages = defineMessages({\n online_now: {\n id: 'online_now',\n defaultMessage: 'online now',\n description: 'Indicator that the user or topic is currently online',\n },\n last_seen: {\n id: 'last_seen_timestamp',\n defaultMessage: 'Last seen',\n description: 'Label for the timestamp of when the user or topic was last online'\n },\n not_found: {\n id: 'title_not_found',\n defaultMessage: 'Not found',\n description: 'Title shown when topic is not found'\n },\n channel: {\n id: 'channel',\n defaultMessage: 'channel',\n description: 'Subtitle shown for channels in MessagesView instead of last seen'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n invalid_content: {\n id: 'invalid_content',\n defaultMessage: 'invalid content',\n description: 'Shown when the message is unreadable'\n },\n});\n\n// Checks if the access permissions are granted but not yet accepted.\nfunction isUnconfirmed(acs) {\n if (acs) {\n const ex = acs.getExcessive() || '';\n return acs.isJoiner('given') && (ex.includes('R') || ex.includes('W'));\n }\n return false;\n}\n\nfunction isPeerRestricted(acs) {\n if (acs) {\n const ms = acs.getMissing() || '';\n return acs.isJoiner('want') && (ms.includes('R') || ms.includes('W'));\n }\n return false;\n}\n\nclass MessagesView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = MessagesView.getDerivedStateFromProps(props, {});\n\n this.leave = this.leave.bind(this);\n this.sendMessage = this.sendMessage.bind(this);\n this.retrySend = this.retrySend.bind(this);\n this.sendImageAttachment = this.sendImageAttachment.bind(this);\n this.sendFileAttachment = this.sendFileAttachment.bind(this);\n this.sendKeyPress = this.sendKeyPress.bind(this);\n this.subscribe = this.subscribe.bind(this);\n this.handleScrollReference = this.handleScrollReference.bind(this);\n this.handleScrollEvent = this.handleScrollEvent.bind(this);\n this.handleDescChange = this.handleDescChange.bind(this);\n this.handleSubsUpdated = this.handleSubsUpdated.bind(this);\n this.handleMessageUpdate = this.handleMessageUpdate.bind(this);\n this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);\n this.handleInfoReceipt = this.handleInfoReceipt.bind(this);\n this.handleImagePostview = this.handleImagePostview.bind(this);\n this.handleClosePreview = this.handleClosePreview.bind(this);\n this.handleFormResponse = this.handleFormResponse.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleShowMessageContextMenu = this.handleShowMessageContextMenu.bind(this);\n this.handleNewChatAcceptance = this.handleNewChatAcceptance.bind(this);\n this.handleEnablePeer = this.handleEnablePeer.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.postReadNotification = this.postReadNotification.bind(this);\n this.clearNotificationQueue = this.clearNotificationQueue.bind(this);\n this.goToLatestMessage = this.goToLatestMessage.bind(this);\n\n this.handlePickReply = this.handlePickReply.bind(this);\n this.handleCancelReply = this.handleCancelReply.bind(this);\n this.handleQuoteClick = this.handleQuoteClick.bind(this);\n\n this.chatMessageRefs = {};\n this.getOrCreateMessageRef = this.getOrCreateMessageRef.bind(this);\n\n this.readNotificationQueue = [];\n this.readNotificationTimer = null;\n }\n\n getOrCreateMessageRef(seqId) {\n if (this.chatMessageRefs.hasOwnProperty(seqId)) {\n return this.chatMessageRefs[seqId];\n }\n const ref = React.createRef();\n this.chatMessageRefs[seqId] = ref;\n return ref;\n }\n\n componentDidMount() {\n if (this.messagesScroller) {\n this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);\n }\n }\n\n componentWillUnmount() {\n if (this.messagesScroller) {\n this.messagesScroller.removeEventListener('scroll', this.handleScrollEvent);\n }\n\n // Flush all notifications.\n this.clearNotificationQueue();\n }\n\n // Scroll last message into view on component update e.g. on message received\n // or vertical shrinking.\n componentDidUpdate(prevProps, prevState) {\n if (this.messagesScroller &&\n (prevState.topic != this.state.topic || prevState.messageCount != this.state.messageCount)) {\n // New message.\n if (this.state.scrollPosition < SHOW_GO_TO_LAST_DIST) {\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight -\n this.state.scrollPosition -\n this.messagesScroller.offsetHeight;\n }\n }\n\n const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n }\n\n if (topic) {\n topic.onData = this.handleMessageUpdate;\n topic.onAllMessagesReceived = this.handleAllMessagesReceived;\n topic.onInfo = this.handleInfoReceipt;\n topic.onMetaDesc = this.handleDescChange;\n topic.onSubsUpdated = this.handleSubsUpdated;\n topic.onPres = this.handleSubsUpdated;\n }\n }\n\n if (!this.props.applicationVisible) {\n // If application is not visible, flush all unsent 'read' notifications.\n this.clearNotificationQueue();\n } else {\n // Otherwise assume there are unread messages.\n this.postReadNotification(0);\n }\n\n if (topic && ((this.state.topic != prevState.topic) || !prevProps.ready)) {\n if (topic._new) {\n console.log('Fetching new topic description');\n // topic.getMeta(topic.startMetaQuery().withDesc().build());\n } else {\n this.subscribe(topic);\n }\n }\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n let nextState = {};\n if (!nextProps.topic) {\n // Default state: no topic.\n nextState = {\n messageCount: 0,\n latestClearId: -1,\n onlineSubs: [],\n topic: null,\n title: '',\n avatar: null,\n isVerified: false,\n isStaff: false,\n isDangerous: false,\n deleted: false,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: false,\n channel: false,\n reply: null,\n showGoToLastButton: false\n };\n } else if (nextProps.topic != prevState.topic) {\n const topic = nextProps.tinode.getTopic(nextProps.topic);\n\n let reply = null;\n if (nextProps.forwardMessage) {\n // We are forwarding a message. Show preview.\n const preview = nextProps.forwardMessage.preview;\n reply = {\n content: preview,\n seq: null\n };\n }\n\n nextState = {\n topic: nextProps.topic,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n reply: reply,\n showGoToLastButton: false,\n deleted: topic._deleted\n };\n\n if (topic) {\n // Topic exists.\n const subs = [];\n\n if (nextProps.connected) {\n topic.subscribers((sub) => {\n if (sub.online && sub.user != nextProps.myUserId) {\n subs.push(sub);\n }\n });\n }\n\n Object.assign(nextState, {\n onlineSubs: subs\n });\n\n if (topic.public) {\n Object.assign(nextState, {\n title: topic.public.fn,\n avatar: makeImageUrl(topic.public.photo)\n });\n } else {\n Object.assign(nextState, {\n title: '',\n avatar: null\n });\n }\n\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(nextState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (prevState.peerMessagingDisabled) {\n Object.assign(nextState, {\n peerMessagingDisabled: false\n });\n }\n Object.assign(nextState, {\n messageCount: topic.messageCount(),\n latestClearId: topic.maxClearId(),\n channel: topic.isChannelType()\n });\n } else {\n // Invalid topic.\n Object.assign(nextState, {\n messageCount: 0,\n latestClearId: -1,\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: false,\n channel: false\n });\n }\n }\n\n if (nextProps.acs) {\n if (nextProps.acs.isWriter() != prevState.isWriter) {\n nextState.isWriter = !prevState.isWriter;\n }\n if (nextProps.acs.isReader() != prevState.isReader) {\n nextState.isReader = !prevState.isReader;\n }\n if (!nextProps.acs.isReader('given') != prevState.readingBlocked) {\n nextState.readingBlocked = !prevState.readingBlocked;\n }\n if (nextProps.acs.isSharer() != prevState.isSharer) {\n nextState.isSharer = !prevState.isSharer;\n }\n } else {\n if (prevState.isWriter) {\n nextState.isWriter = false;\n }\n if (prevState.isReader) {\n nextState.isReader = false;\n }\n if (!prevState.readingBlocked) {\n prevState.readingBlocked = true;\n }\n if (prevState.isSharer) {\n nextState.isSharer = false;\n }\n }\n\n if (isUnconfirmed(nextProps.acs) == !prevState.unconformed) {\n nextState.unconfirmed = !prevState.unconformed;\n }\n\n // Clear subscribers online when there is no connection.\n if (!nextProps.connected && prevState.onlineSubs && prevState.onlineSubs.length > 0) {\n nextState.onlineSubs = [];\n }\n\n return nextState;\n }\n\n subscribe(topic) {\n if (topic.isSubscribed() || !this.props.ready) {\n return;\n }\n\n // Is this a new topic?\n const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);\n\n // Don't request the tags. They are useless unless the user\n // is the owner and is editing the topic.\n let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();\n if (this.state.isReader || newTopic) {\n // Reading is either permitted or we don't know because it's a new topic. Ask for messages.\n getQuery = getQuery.withLaterData(MESSAGES_PAGE);\n if (this.state.isReader) {\n getQuery = getQuery.withLaterDel();\n }\n // And show \"loading\" spinner.\n this.setState({ fetchingMessages: true });\n }\n\n const setQuery = newTopic ? this.props.newTopicParams : undefined;\n topic.subscribe(getQuery.build(), setQuery)\n .then((ctrl) => {\n if (ctrl.code == 303) {\n // Redirect to another topic requested.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));\n return;\n }\n if (this.state.topic != ctrl.topic) {\n this.setState({topic: ctrl.topic});\n }\n this.props.onNewTopicCreated(this.props.topic, ctrl.topic);\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n this.retrySend(pub);\n }\n });\n })\n .catch((err) => {\n console.error(\"Failed subscription to\", this.state.topic);\n this.props.onError(err.message, 'err');\n const blankState = MessagesView.getDerivedStateFromProps({}, {});\n blankState.title = this.props.intl.formatMessage(messages.not_found);\n this.setState(blankState);\n });\n }\n\n leave(oldTopicName) {\n if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {\n return;\n }\n const oldTopic = this.props.tinode.getTopic(oldTopicName);\n if (oldTopic && oldTopic.isSubscribed()) {\n oldTopic.leave(false)\n .catch(() => { /* do nothing here */ })\n .finally(() => {\n // We don't care if the request succeeded or failed.\n // The topic is dead regardless.\n this.setState({fetchingMessages: false});\n oldTopic.onData = undefined;\n oldTopic.onAllMessagesReceived = undefined;\n oldTopic.onInfo = undefined;\n oldTopic.onMetaDesc = undefined;\n oldTopic.onSubsUpdated = undefined;\n oldTopic.onPres = undefined;\n });\n }\n }\n\n // Don't use React.createRef as the ref.current is not available in componentDidMount in this component.\n handleScrollReference(node) {\n if (node) {\n node.addEventListener('scroll', this.handleScrollEvent);\n this.messagesScroller = node;\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n }\n }\n\n // Get older messages and show/hide [go to latest message] button.\n handleScrollEvent(event) {\n const pos = event.target.scrollHeight - event.target.scrollTop - event.target.offsetHeight;\n this.setState({\n scrollPosition: pos,\n showGoToLastButton: pos > SHOW_GO_TO_LAST_DIST,\n });\n\n if (this.state.fetchingMessages) {\n return;\n }\n\n if (event.target.scrollTop <= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {\n this.setState({fetchingMessages: true}, () => {\n topic.getMessagesPage(MESSAGES_PAGE)\n .catch((err) => this.props.onError(err.message, 'err'))\n .finally(() => this.setState({fetchingMessages: false}));\n });\n }\n }\n }\n\n goToLatestMessage() {\n this.setState({scrollPosition: 0});\n if (this.messagesScroller) {\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight;\n }\n }\n\n handleDescChange(desc) {\n if (desc.public) {\n this.setState({\n title: desc.public.fn,\n avatar: makeImageUrl(desc.public.photo)\n });\n } else {\n this.setState({\n title: '',\n avatar: null\n });\n }\n\n if (desc.acs) {\n this.setState({\n isWriter: desc.acs.isWriter(),\n isReader: desc.acs.isReader(),\n readingBlocked: !desc.acs.isReader('given'),\n unconfirmed: isUnconfirmed(desc.acs),\n });\n }\n }\n\n postReadNotification(seq) {\n // Ignore notifications if the app is invisible.\n if (!this.props.applicationVisible) {\n return;\n }\n\n // Set up the timer if it's not running already.\n if (!this.readNotificationTimer) {\n this.readNotificationTimer = setInterval(() => {\n if (this.readNotificationQueue.length == 0) {\n // Shut down the timer if the queue is empty.\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n return;\n }\n\n let seq = -1;\n while (this.readNotificationQueue.length > 0) {\n const n = this.readNotificationQueue[0];\n if (n.topicName != this.state.topic) {\n // Topic has changed. Drop the notification.\n this.readNotificationQueue.shift();\n continue;\n }\n\n const now = new Date();\n if (n.sendAt <= now) {\n // Remove expired notification from queue.\n this.readNotificationQueue.shift();\n seq = Math.max(seq, n.seq);\n } else {\n break;\n }\n }\n\n // Send only one notification for the whole batch of messages.\n if (seq >= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic) {\n topic.noteRead(seq);\n }\n }\n }, NOTIFICATION_EXEC_INTERVAL);\n }\n\n const now = new Date();\n this.readNotificationQueue.push({\n topicName: this.state.topic,\n seq: seq,\n sendAt: now.setMilliseconds(now.getMilliseconds() + READ_DELAY)\n });\n }\n\n // Clear notification queue and timer.\n clearNotificationQueue() {\n this.readNotificationQueue = [];\n if (this.readNotificationTimer) {\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n }\n }\n\n handleSubsUpdated() {\n if (this.state.topic) {\n const subs = [];\n const topic = this.props.tinode.getTopic(this.state.topic);\n topic.subscribers((sub) => {\n if (sub.online && sub.user != this.props.myUserId) {\n subs.push(sub);\n }\n });\n const newState = {onlineSubs: subs};\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(newState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (this.state.peerMessagingDisabled) {\n Object.assign(newState, {\n peerMessagingDisabled: false\n });\n }\n this.setState(newState);\n }\n }\n\n // The 'msg' could be false-ish if some message ranges were deleted.\n handleMessageUpdate(msg) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (!msg) {\n // msg could be null if one or more messages were deleted.\n // Updating state to force redraw.\n this.setState({latestClearId: topic.maxClearId()});\n return;\n }\n\n clearTimeout(this.keyPressTimer)\n this.setState({messageCount: topic.messageCount(), typingIndicator: false});\n\n // Scroll to the bottom if the message is added to the end of the message list.\n // TODO: This should be replaced by showing a \"scroll to bottom\" button.\n if (topic.isNewMessage(msg.seq)) {\n if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {\n this.setState({showGoToLastButton: true});\n } else {\n this.setState({scrollPosition: 0});\n }\n }\n\n // Aknowledge messages except own messages. They are\n // automatically assumed to be read and recived.\n const status = topic.msgStatus(msg, true);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.postReadNotification(msg.seq);\n }\n // This will send \"received\" notifications right away.\n this.props.onData(msg);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\n if (count > 0) {\n // 0 means \"latest\".\n this.postReadNotification(0);\n }\n }\n\n handleInfoReceipt(info) {\n switch (info.what) {\n case 'kp': {\n clearTimeout(this.keyPressTimer);\n this.keyPressTimer = setTimeout(() => {\n this.setState({typingIndicator: false});\n }, KEYPRESS_DELAY + 1000);\n if (!this.state.typingIndicator) {\n this.setState({typingIndicator: true});\n }\n break;\n }\n case 'read':\n case 'recv':\n // Redraw due to changed recv/read status.\n this.forceUpdate();\n break;\n default:\n console.info(\"Other change in topic: \", info.what);\n }\n }\n\n handleImagePostview(content) {\n this.setState({ imagePostview: content });\n }\n\n handleClosePreview() {\n if (this.state.imagePreview && this.state.imagePreview.url) {\n URL.revokeObjectURL(this.state.imagePreview.url);\n }\n this.setState({ imagePostview: null, imagePreview: null, docPreview: null });\n }\n\n handleFormResponse(action, text, data) {\n if (action == 'pub') {\n this.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));\n } else if (action == 'url') {\n const url = new URL(data.ref);\n const params = url.searchParams;\n for (let key in data.resp) {\n if (data.resp.hasOwnProperty(key)) {\n params.set(key, data.resp[key]);\n }\n }\n ['name', 'seq'].map((key) => {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\n params.set('topic', this.state.topic);\n url.search = params;\n window.open(url, '_blank');\n } else {\n console.info(\"Unknown action in form\", action);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.state.topic, y: e.pageY, x: e.pageX });\n }\n\n handleShowMessageContextMenu(params, messageSpecificMenuItems) {\n if (params.userFrom == 'chan') {\n params.userFrom = this.state.topic;\n params.userName = this.state.title;\n }\n params.topicName = this.state.topic;\n const menuItems = messageSpecificMenuItems || [];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\n if (!topic.isChannelType()) {\n menuItems.push('message_delete');\n }\n const acs = topic.getAccessMode();\n if (acs && acs.isDeleter()) {\n menuItems.push('message_delete_hard');\n }\n }\n this.props.showContextMenu(params, menuItems);\n }\n\n handleNewChatAcceptance(action) {\n this.props.onNewChat(this.state.topic, action);\n }\n\n handleEnablePeer(e) {\n e.preventDefault();\n this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);\n }\n\n sendKeyPress() {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic.isSubscribed()) {\n topic.noteKeyPress();\n }\n }\n\n // sendMessage sends the message with an optional subscription to topic first.\n sendMessage(msg, uploadCompletionPromise, uploader) {\n let head;\n if (this.props.forwardMessage) {\n // We are forwarding a message.\n msg = this.props.forwardMessage.msg;\n head = this.props.forwardMessage.head;\n this.handleCancelReply();\n } else if (this.state.reply && this.state.reply.content) {\n // We are replying to a message in this topic.\n head = {reply: '' + this.state.reply.seq};\n // Turn it into Drafty so we can make a quoted Drafty object later.\n if (typeof msg == 'string') {\n msg = Drafty.parse(msg);\n }\n msg = Drafty.append(Drafty.sanitizeEntities(this.state.reply.content), msg);\n this.handleCancelReply();\n }\n this.props.sendMessage(msg, uploadCompletionPromise, uploader, head);\n }\n\n // Retry sending a message.\n retrySend(pub) {\n this.props.sendMessage(pub.content, undefined, undefined, pub.head);\n }\n\n // Send attachment as Drafty message:\n // - if file is too large, upload it and send a s link.\n // - if file is small enough, just send it in-band.\n sendFileAttachment(file) {\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (file.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, {\n mime: file.type,\n filename: file.name,\n size: file.size,\n urlPromise: uploadCompletionPromise\n });\n // Pass data and the uploader to the TinodeWeb.\n this.sendMessage(msg, uploadCompletionPromise, uploader);\n } else {\n // Small enough to send inband.\n fileToBase64(file)\n .then(b64 => this.sendMessage(Drafty.attachFile(null, {mime: b64.mime, data: b64.bits, filename: b64.name})))\n .catch(err => this.props.onError(err));\n }\n }\n\n // handleAttachFile method is called when [Attach file] button is clicked.\n handleAttachFile(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n if (file.size > maxExternAttachmentSize) {\n // Too large.\n this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');\n } else {\n this.setState({\n docPreview: {\n file: file,\n name: file.name,\n size: file.size,\n type: file.type\n }\n });\n }\n }\n\n // sendImageAttachment sends the image bits inband as Drafty message.\n sendImageAttachment(caption, blob) {\n const mime = this.state.imagePreview.mime;\n const width = this.state.imagePreview.width;\n const height = this.state.imagePreview.height;\n const fname = this.state.imagePreview.name;\n\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (blob.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(blob);\n\n // Make small preview to show while uploading.\n imageScaled(blob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false)\n // Convert tiny image into base64 for serialization and previewing.\n .then(scaled => blobToBase64(scaled.blob))\n .then(b64 => {\n let msg = Drafty.insertImage(null, 0, {\n mime: mime,\n _tempPreview: b64.bits, // This preview will not be serialized.\n width: width,\n height: height,\n filename: fname,\n size: blob.size,\n urlPromise: uploadCompletionPromise\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.parse(caption));\n }\n // Pass data and the uploader to the TinodeWeb.\n this.sendMessage(msg, uploadCompletionPromise, uploader);\n }).catch((err) => {\n this.props.onError(err, 'err');\n });\n return;\n }\n\n // Upload the image if it's too big to be send inband.\n blobToBase64(blob)\n .then(b64 => {\n let msg = Drafty.insertImage(null, 0, {\n mime: b64.mime,\n preview: b64.bits, // Serializable preview\n width: width,\n height: height,\n filename: fname,\n size: blob.size\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.parse(caption));\n }\n this.sendMessage(msg);\n });\n }\n\n // handleAttachImage method is called when [Attach image] button is clicked.\n handleAttachImage(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n // Get image dimensions and size, optionally scale it down.\n imageScaled(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, maxExternAttachmentSize, false)\n .then(scaled => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(scaled.blob),\n blob: scaled.blob,\n name: scaled.name,\n width: scaled.width,\n height: scaled.height,\n size: scaled.blob.size,\n mime: scaled.mime\n }});\n }).catch(err => {\n this.props.onError(err, 'err');\n });\n }\n\n handleCancelUpload(seq, uploader) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const found = topic.findMessage(seq);\n if (found) {\n found._cancelled = true;\n }\n uploader.cancel();\n }\n\n // seq: seq ID of the source message\n // context: message content.\n // forwarded: true if the source message is also a forwarded message.\n // senderId: UID of the sender of the source message.\n // senderName: full name of the sender of the original message.\n handlePickReply(seq, content, senderId, senderName) {\n this.setState({reply: null});\n\n if (!seq || !content) {\n return;\n }\n\n content = typeof content == 'string' ? Drafty.init(content) : content;\n if (Drafty.isValid(content)) {\n content = Drafty.replyContent(content, QUOTED_REPLY_LENGTH);\n } else {\n // /!\\ invalid content.\n content = Drafty.append(Drafty.init('\\u26A0 '),\n Drafty.wrapInto(this.props.intl.formatMessage(messages.invalid_content), 'EM'));\n }\n\n this.setState({\n reply: {\n content: Drafty.quote(senderName, senderId, content),\n seq: seq\n }\n });\n this.props.onCancelForwardMessage();\n }\n\n handleCancelReply() {\n this.setState({reply: null});\n this.props.onCancelForwardMessage();\n }\n\n handleQuoteClick(replyToSeq) {\n const ref = this.getOrCreateMessageRef(replyToSeq);\n if (ref && ref.current) {\n ref.current.scrollIntoView({block: \"center\", behavior: \"smooth\"});\n ref.current.classList.add('flash');\n setTimeout(() => { ref.current.classList.remove('flash') } , 1000);\n } else {\n console.error(\"Unresolved message ref\", replyToSeq);\n }\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n let component;\n if (this.props.hideSelf) {\n component = null;\n } else if (!this.state.topic) {\n component = (\n \n );\n } else {\n let component2;\n if (this.state.imagePreview) {\n // Preview image before sending.\n component2 = (\n \n );\n } else if (this.state.imagePostview) {\n // Expand received image.\n component2 = (\n \n );\n } else if (this.state.docPreview) {\n // Preview attachment before sending.\n component2 = (\n \n );\n } else {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const isChannel = topic.isChannelType();\n const groupTopic = topic.isGroupType() && !isChannel;\n const icon_badges = [];\n if (topic.trusted) {\n if (topic.trusted.verified) {\n icon_badges.push({icon: 'verified', color: 'badge-inv'});\n }\n if (topic.trusted.staff) {\n icon_badges.push({icon: 'staff', color: 'badge-inv'});\n }\n if (topic.trusted.danger) {\n icon_badges.push({icon: 'dangerous', color: 'badge-inv'});\n }\n }\n\n const messageNodes = [];\n let previousFrom = null;\n let chatBoxClass = null;\n const dateFmt = new Intl.DateTimeFormat(this.props.intl.locale);\n topic.messages((msg, prev, next, i) => {\n let nextFrom = next ? (next.from || 'chan') : null;\n\n let sequence = 'single';\n let thisFrom = msg.from || 'chan';\n if (thisFrom == previousFrom) {\n if (thisFrom == nextFrom) {\n sequence = 'middle';\n } else {\n sequence = 'last';\n }\n } else if (thisFrom == nextFrom) {\n sequence = 'first';\n }\n previousFrom = thisFrom;\n\n const isReply = !(thisFrom == this.props.myUserId);\n const deliveryStatus = topic.msgStatus(msg, true);\n\n let userFrom = thisFrom, userName, userAvatar;\n const user = topic.userDesc(thisFrom);\n if (user && user.public) {\n userName = user.public.fn;\n userAvatar = makeImageUrl(user.public.photo);\n }\n chatBoxClass = groupTopic ? 'chat-box group' : 'chat-box';\n\n // Ref for this chat message.\n const ref = this.getOrCreateMessageRef(msg.seq);\n let replyToSeq = msg.head ? parseInt(msg.head.reply) : null;\n if (!replyToSeq || isNaN(replyToSeq)) {\n replyToSeq = null;\n }\n\n // This message was sent on a different date than the previous.\n if (!prev || new Date(prev.ts).toDateString() != new Date(msg.ts).toDateString()) {\n messageNodes.push(\n \n );\n }\n\n if (msg.hi) {\n // Deleted message.\n messageNodes.push(\n \n );\n } else {\n messageNodes.push(\n \n );\n }\n });\n\n let lastSeen = null;\n if (isChannel) {\n lastSeen = formatMessage(messages.channel);\n } else {\n const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.isP2PTopicName(cont.topic)) {\n if (cont.online) {\n lastSeen = formatMessage(messages.online_now);\n } else if (cont.seen) {\n lastSeen = formatMessage(messages.last_seen) + \": \" +\n shortDateFormat(cont.seen.when, this.props.intl.locale);\n // TODO: also handle user agent in c.seen.ua\n }\n }\n }\n const avatar = this.state.avatar || true;\n const online = this.state.deleted ? null :\n this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';\n\n const titleClass = 'panel-title' + (this.state.deleted ? ' deleted' : '');\n\n component2 = (\n <>\n \n {this.props.displayMobile ?\n \n : null}\n \n \n
\n
\n {!this.state.isReader ?\n
\n {this.state.readingBlocked ?\n
\n \n
\n : null }\n
\n : null }\n
\n {this.state.peerMessagingDisabled && !this.state.unconfirmed ?\n : null}\n {this.state.unconfirmed ?\n \n :\n }\n >\n );\n }\n\n component = {component2}
\n }\n return component;\n }\n};\n\nexport default injectIntl(MessagesView);\n","// The <- button to be displayed in title bars.\nimport React from 'react';\n\nexport default class ButtonBack extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onBack();}}>\n arrow_back\n \n );\n }\n}\n","import React from 'react';\n\nexport default class MenuContacts extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nexport default class MenuStart extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\nimport ButtonBack from './button-back.jsx';\nimport ContactBadges from './contact-badges.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nimport { sanitizeImageUrl } from '../lib/utils.js';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n const icon_badges = [];\n if (this.props.trustedBadges) {\n this.props.trustedBadges.map((b) => {\n icon_badges.push({icon: b, color: 'badge-inv'});\n });\n }\n let avatar = null;\n if (this.props.tinode) {\n avatar = this.props.tinode.authorizeURL(sanitizeImageUrl(this.props.avatar));\n }\n return (\n \n {this.props.onCancel ?
: null}\n {avatar ?\n
\n \n
\n :\n null}\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\n\nfunction _clip(str, length) {\n return str && str.substring(0, length);\n}\n\nexport default class AccountSettingsView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: _clip(me.public ? me.public.fn : undefined, MAX_TITLE_LENGTH),\n description: _clip(me.public ? me.public.note : undefined, MAX_TOPIC_DESCRIPTION_LENGTH),\n avatar: makeImageUrl(me.public ? me.public.photo : null)\n };\n }\n\n render() {\n return (\n \n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedMessage, defineMessages } from 'react-intl';\n\nimport ContactList from '../widgets/contact-list.jsx';\n\nimport { updateFavicon } from '../lib/utils.js';\n\nconst messages = defineMessages({\n archived_contacts_title: {\n id: \"archived_contacts\",\n defaultMessage: \"Archived contacts ({count})\",\n description: \"Label for archived chats\"\n }\n});\n\nexport default class ContactsView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleAction = this.handleAction.bind(this);\n\n this.state = ContactsView.deriveStateFromProps(props);\n }\n\n static deriveStateFromProps(props) {\n const contacts = [];\n let unreadThreads = 0;\n let archivedCount = 0;\n props.chatList.map((c) => {\n const blocked = c.acs && !c.acs.isJoiner();\n // Show only blocked contacts only when props.blocked == true.\n if (blocked && props.blocked) {\n contacts.push(c);\n }\n if (blocked || props.blocked) {\n return;\n }\n\n if (c.private && c.private.arch) {\n if (props.archive) {\n contacts.push(c);\n } else {\n archivedCount ++;\n }\n } else if (!props.archive) {\n contacts.push(c);\n unreadThreads += c.unread > 0 ? 1 : 0;\n }\n });\n\n contacts.sort((a, b) => {\n return (b.touched || 0) - (a.touched || 0);\n });\n\n if (archivedCount > 0) {\n contacts.push({\n action: 'archive',\n title: messages.archived_contacts_title,\n values: {count: archivedCount}\n });\n }\n\n return {\n contactList: contacts,\n unreadThreads: unreadThreads\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chatList != this.props.chatList ||\n prevProps.archive != this.props.archive ||\n prevProps.blocked != this.props.blocked) {\n const newState = ContactsView.deriveStateFromProps(this.props);\n this.setState(newState);\n if (newState.unreadThreads != prevState.unreadThreads) {\n updateFavicon(newState.unreadThreads);\n }\n }\n }\n\n handleAction(action_ignored) {\n this.props.onShowArchive();\n }\n\n render() {\n return (\n ¯∖_(ツ)_/¯\"\n description=\"HTML message shown in ContactList when no contacts are found\">{\n (no_contacts) => \n }\n );\n }\n};\n","// Helper functions for storing values in localStorage.\n// By default localStorage can store only strings, not objects or other types.\n\nexport default class LocalStorageUtil {\n // Replace old object with the new one.\n static setObject(key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n }\n\n // Get stored object.\n static getObject(key) {\n const value = localStorage.getItem(key);\n return value && JSON.parse(value);\n }\n\n // Partially or wholly update stored object.\n static updateObject(key, value) {\n const oldVal = this.getObject(key);\n this.setObject(key, Object.assign(oldVal || {}, value));\n }\n\n // Just a wrapper.\n static removeItem(key) {\n localStorage.removeItem(key);\n }\n}\n","// Account registration.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport { theCard } from '../lib/utils.js';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class CreateAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n login: '',\n password: '',\n email: '',\n fn: '', // full/formatted name\n imageDataUrl: null,\n errorCleared: false,\n saveToken: LocalStorageUtil.getObject('keep-logged-in')\n };\n\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(password) {\n this.setState({password: password});\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value})\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleToggleSaveToken() {\n LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.setState({errorCleared: false});\n this.props.onCreateAccount(\n this.state.login.trim(),\n this.state.password.trim(),\n theCard(this.state.fn.trim().substring(0, MAX_TITLE_LENGTH), this.state.imageDataUrl),\n {'meth': 'email', 'val': this.state.email});\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\n\nexport default class EditAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCheckboxClick = this.handleCheckboxClick.bind(this);\n }\n\n handleCheckboxClick(what, checked) {\n if (what == 'sound') {\n this.props.onToggleMessageSounds(checked);\n } else if (what == 'alert') {\n this.props.onTogglePushNotifications(checked);\n } else if (what == 'incognito') {\n this.props.onToggleIncognitoMode(checked);\n }\n }\n\n render() {\n return (\n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\n\nconst messages = defineMessages({\n delete_account: {\n id: 'delete_account',\n defaultMessage: 'Delete account',\n description: 'Title for delete account warning'\n },\n delete_account_warning: {\n id: 'delete_account_warning',\n defaultMessage: 'Are you sure you want to delete your account? It cannot be undone.',\n description: 'Warning message when deleting an account'\n }\n});\n\nclass AccSecurityView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n let blockedCount = 0;\n me.contacts((c) => {\n if (c.acs && !c.acs.isJoiner()) {\n blockedCount ++;\n }\n });\n const defacs = me.getDefaultAccess();\n this.state = {\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n showPermissionEditorFor: undefined,\n blockedCount: blockedCount,\n };\n\n this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdatePassword(pwd);\n }\n\n handleLaunchPermissionsEditor(which) {\n this.setState({\n showPermissionEditorFor: which,\n editedPermissions: this.state[which]\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handlePermissionsChanged(perm) {\n let defacs = {};\n defacs[this.state.showPermissionEditorFor] = perm;\n this.props.onUpdateAccountDesc('me', undefined, undefined, defacs);\n\n let newState = {showPermissionEditorFor: undefined};\n newState[this.state.showPermissionEditorFor] = perm;\n this.setState(newState);\n }\n\n handleDeleteAccount(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.delete_account), // title\n formatMessage(messages.delete_account_warning), // content\n (() => { this.props.onDeleteAccount(); }), // onConfirm\n null, // use default text \"OK\"\n true, // Show Reject button\n null // use default text \"Cancel\"\n );\n }\n\n render() {\n return (\n <>{this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n {this.state.blockedCount > 0 ?\n <>\n
\n
\n >\n : null }\n
\n }>\n );\n }\n};\n\nexport default injectIntl(AccSecurityView);\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME, LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE } from '../config.js';\n\nexport default class AccSupportView extends React.PureComponent {\n render() {\n return (\n \n
\n
\n
\n
\n \n {APP_NAME}\n
\n
\n \n {Tinode.getLibrary()}\n
\n
\n \n {this.props.serverVersion}\n
\n
\n \n {this.props.serverAddress}\n
\n
\n
\n );\n }\n};\n","// Login form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\n\nexport default class LoginView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: props.persist\n };\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n handleToggleSaveToken() {\n this.props.onPersistenceChange(!this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n/* END Login */\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n invalid_id: {\n id: 'error_invalid_id',\n defaultMessage: 'Invalid ID',\n description: 'Error message'\n }\n});\n\nclass NewTopicById extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n groupId: '',\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleChange(e) {\n this.setState({groupId: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.groupId) {\n const name = this.state.groupId.trim();\n const prefix = name.substr(0, 3);\n if (name.length > 3 && ['usr', 'grp', 'chn'].includes(prefix)) {\n this.props.onSubmit(name);\n } else {\n this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');\n }\n }\n }\n\n render() {\n return (\n \n
\n {\n (prompt) => \n }\n
\n
\n \n
\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicById);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from './avatar-upload.jsx';\nimport CheckBox from './checkbox.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH, MAX_TOPIC_DESCRIPTION_LENGTH } from '../config.js';\n\nexport default class NewTopicGroup extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.fullName = React.createRef();\n\n this.state = {\n fullName: '', // full/formatted name\n private: '',\n description: '',\n imageDataUrl: null,\n tags: [],\n isChannel: false\n };\n\n this.handleFieldEdit = this.handleFieldEdit.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleTagsChanged = this.handleTagsChanged.bind(this);\n this.handleChannelToggle = this.handleChannelToggle.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n componentDidMount() {\n // this.fullName.current.focus();\n }\n\n handleFieldEdit(name, e) {\n this.setState({[name]: e.target.value || ''});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleTagsChanged(tags) {\n this.setState({tags: tags});\n }\n\n handleChannelToggle() {\n this.setState({isChannel: !this.state.isChannel});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n\n const fn = this.state.fullName.trim().substring(0, MAX_TITLE_LENGTH);\n const comment = this.state.private.trim().substring(0, MAX_TITLE_LENGTH);\n const description = this.state.description.trim().substring(0, MAX_TOPIC_DESCRIPTION_LENGTH);\n if (fn) {\n this.props.onSubmit(fn, description, this.state.imageDataUrl, comment, this.state.tags, this.state.isChannel);\n }\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n return (\n \n );\n }\n};\n","// Create new topic and invite users or send an invite.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from '../widgets/contact-list.jsx';\nimport NewTopicById from '../widgets/new-topic-by-id.jsx';\nimport NewTopicGroup from '../widgets/new-topic-group.jsx';\nimport SearchContacts from '../widgets/search-contacts.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\nimport { theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n search_for_contacts: {\n id: \"search_for_contacts\",\n defaultMessage: \"Use search to find contacts\",\n description: \"Text shown in contacts view when user entered no search query.\"\n },\n search_no_results: {\n id: \"search_no_results\",\n defaultMessage: \"Search returned no results\",\n description: \"Text shown in contacts view when query returned no results.\"\n },\n search_placeholder: {\n id: \"search_placeholder\",\n defaultMessage: \"List like email:alice@example.com, tel:17025550003...\",\n description: \"Placeholder in contacts search field\"\n },\n});\n\nclass NewTopicView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tabSelected: 'find',\n searchQuery: null\n };\n\n this.handleTabClick = this.handleTabClick.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleSearchResultSelected = this.handleSearchResultSelected.bind(this);\n this.handleNewGroupSubmit = this.handleNewGroupSubmit.bind(this);\n this.handleGroupByID = this.handleGroupByID.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleTabClick(e) {\n e.preventDefault();\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));\n this.setState({tabSelected: e.currentTarget.dataset.id});\n }\n\n handleSearchContacts(query) {\n this.props.onSearchContacts(query);\n this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});\n }\n\n handleSearchResultSelected(name) {\n if (this.state.tabSelected == 'find') {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(name);\n }\n }\n\n handleNewGroupSubmit(name, description, dataUrl, priv, tags, isChannel) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined,\n {public: theCard(name, dataUrl, null, description), private: priv, tags: tags}, isChannel);\n }\n\n handleGroupByID(topicName) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(topicName);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const no_contacts_placeholder = formatMessage(this.state.searchQuery ?\n messages.search_no_results : messages.search_for_contacts);\n const search_placeholder = formatMessage(messages.search_placeholder);\n return (\n \n
\n {this.state.tabSelected === 'grp' ?\n
:\n this.state.tabSelected === 'byid' ?\n
:\n
\n \n \n
}\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicView);\n","// A password reset form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\n\nexport default class PasswordResetView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n email: '',\n password: '',\n sent: false\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n }\n\n componentDidMount() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n this.setState({token: parsed.params.token, scheme: parsed.params.scheme});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.token) {\n this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);\n } else if (this.state.sent) {\n this.props.onCancel();\n } else {\n const email = this.state.email.trim();\n this.setState({email: email});\n this.props.onRequest('email', email).then(() => {\n this.setState({sent: true});\n });\n }\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n render() {\n return (\n \n );\n }\n}\n","import React from 'react';\n\nimport { KNOWN_HOSTS } from '../config.js';\n\n/* Combobox for selecting host name */\n\nexport default class HostSelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n hostName: props.serverAddress,\n changed: false\n };\n\n this.handleHostNameChange = this.handleHostNameChange.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handleHostNameChange(e) {\n this.setState({hostName: e.target.value, changed: true});\n }\n\n handleEditingFinished() {\n if (this.state.changed) {\n this.setState({changed: false});\n this.props.onServerAddressChange(this.state.hostName.trim());\n }\n }\n\n render() {\n var hostOptions = [];\n for (let key in KNOWN_HOSTS) {\n let item = KNOWN_HOSTS[key];\n hostOptions.push(\n \n );\n }\n return (\n \n \n \n
\n );\n }\n}\n","// Tinode config panel.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport HostSelector from '../widgets/host-selector.jsx';\n\nexport default class SettingsView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n transport: props.transport || 'def',\n serverAddress: props.serverAddress,\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleTransportSelected = this.handleTransportSelected.bind(this);\n this.handleServerAddressChange = this.handleServerAddressChange.bind(this);\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onUpdate({\n transport: this.state.transport,\n serverAddress: this.state.serverAddress\n });\n }\n\n handleTransportSelected(e) {\n this.setState({transport: e.currentTarget.value});\n }\n\n handleServerAddressChange(name) {\n this.setState({serverAddress: name});\n }\n\n render() {\n const names = {def: \"default\", ws: \"websocket\", lp: \"long polling\"};\n const transportOptions = [];\n ['def', 'ws', 'lp'].map((item) => {\n const id = 'transport-' + item;\n const name = names[item];\n transportOptions.push(\n \n \n \n \n );\n });\n\n return (\n \n );\n }\n};\n","// ValidationView: panel for confirming credentials, like email or phone.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n phone: {\n id: 'phone_dative',\n defaultMessage: 'phone',\n description: \"Dative case of 'phone', i.e. 'phone' in 'by phone'\",\n },\n email: {\n id: 'email_dative',\n defaultMessage: 'email',\n description: \"Dative case of 'email', i.e. 'email' in 'by email'\",\n }\n});\n\nclass ValidationView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n code: props.credCode || ''\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(e) {\n this.setState({code: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n } else if (e.key == 'Escape') {\n this.handleCancel(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.code && this.state.code.trim()) {\n this.props.onSubmit(this.props.credMethod, this.state.code.trim());\n }\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel();\n }\n\n\n render() {\n const { formatMessage } = this.props.intl;\n const methods = {'email': formatMessage(messages.email), 'tel': formatMessage(messages.phone)};\n let method = methods[this.props.credMethod] || this.props.credMethod;\n return (\n \n
\n \n
\n
\n {\n (numbers_only) => \n }\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(ValidationView);\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport SideNavbar from '../widgets/side-navbar.jsx';\nimport TopicCommon from '../widgets/topic-common.jsx';\n\nimport AccountSettingsView from './account-settings-view.jsx';\nimport ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport AccNotificationsView from './acc-notifications-view.jsx';\nimport AccSecurityView from './acc-security-view.jsx';\nimport AccSupportView from './acc-support-view.jsx';\nimport LoginView from './login-view.jsx';\nimport NewTopicView from './new-topic-view.jsx';\nimport PasswordResetView from './password-reset-view.jsx';\nimport SettingsView from './settings-view.jsx';\nimport ValidationView from './validation-view.jsx';\n\n// Panel titles for translation.\nconst messages = defineMessages({\n login: {\n id: 'sidepanel_title_login',\n description: 'Sidepanel title for LoginView.',\n defaultMessage: 'Sign In'\n },\n register: {\n id: 'sidepanel_title_register',\n description: 'Sidepanel title for CreateAccountView.',\n defaultMessage: 'Create Account'\n },\n settings: {\n id: 'sidepanel_title_settings',\n description: 'Sidepanel title for SettingsView.',\n defaultMessage: 'Settings'\n },\n edit: {\n id: 'sidepanel_title_account_settings',\n description: 'Sidepanel title for AccountSettingsView.',\n defaultMessage: 'Account Settings'\n },\n general: {\n id: 'panel_title_general',\n description: 'Title for TopicCommon.',\n defaultMessage: 'General'\n },\n security: {\n id: 'panel_title_security',\n description: 'Title for TopicSecirity and AccSecurity.',\n defaultMessage: 'Security'\n },\n crop: {\n id: 'panel_title_crop',\n description: 'Title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n notif: {\n id: 'sidepanel_title_acc_notifications',\n description: 'Sidepanel title for AccNotificationsView.',\n defaultMessage: 'Notifications'\n },\n support: {\n id: 'sidepanel_title_acc_support',\n description: 'Sidepanel title for AccSupportView.',\n defaultMessage: 'Support'\n },\n newtpk: {\n id: 'sidepanel_title_newtpk',\n description: 'Sidepanel title for NewTopicView.',\n defaultMessage: 'Start New Chat'\n },\n cred: {\n id: 'sidepanel_title_cred',\n description: 'Sidepanel title for ValidationView.',\n defaultMessage: 'Confirm Credentials'\n },\n reset: {\n id: 'sidepanel_title_reset',\n description: 'Sidepanel title for PasswordResetView.',\n defaultMessage: 'Reset Password'\n },\n archive: {\n id: 'sidepanel_title_archive',\n description: 'Sidepanel title for ContactsView-Archive.',\n defaultMessage: 'Archived Chats'\n },\n blocked: {\n id: 'sidepanel_title_blocked',\n description: 'Sidepanel title for ContactsView-Blocked.',\n defaultMessage: 'Blocked Chats'\n }\n});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleNewTopic = this.handleNewTopic.bind(this);\n }\n\n handleNewTopic() {\n this.props.onNavigate('newtpk');\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');\n\n let title, avatar, badges;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n badges = this.props.trustedBadges;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\n badges = null;\n }\n\n let onCancel;\n if (['login', 'contacts'].indexOf(view) == -1) {\n onCancel = this.props.onCancel;\n }\n\n return (\n \n
\n\n
\n\n
\n\n {view === 'login' ?\n
:\n\n view === 'register' ?\n
:\n\n view === 'settings' ?\n
:\n\n view === 'edit' ?\n
:\n\n (view === 'general' || view === 'crop') ?\n
:\n\n view === 'notif' ?\n
:\n\n view === 'security' ?\n
:\n\n view === 'support' ?\n
:\n\n (view === 'contacts' || view == 'archive' || view == 'blocked') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\n
:\n null}\n
\n );\n }\n};\n\nexport default injectIntl(SidepanelView);\n","import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js';\n\n// Detect server address from the URL\nexport function detectServerAddress() {\n let host = DEFAULT_HOST;\n if (typeof window.location == 'object') {\n if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {\n host = KNOWN_HOSTS.local;\n } else if (window.location.hostname) {\n host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');\n }\n }\n return host;\n}\n\n// Detect if the page is served over HTTPS.\nexport function isSecureConnection() {\n if (typeof window.location == 'object') {\n return window.location.protocol == 'https:';\n }\n return false;\n}\n\nexport function isLocalHost() {\n if (typeof window.location == 'object') {\n return window.location.hostname == 'localhost';\n }\n return false;\n}\n","// The top-level class to hold all functionality together.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport { initializeApp as firebaseInitApp } from 'firebase/app';\nimport { getMessaging as firebaseGetMessaging, getToken as firebaseGetToken,\n deleteToken as firebaseDelToken, onMessage as firebaseOnMessage } from 'firebase/messaging';\n\nimport Tinode from 'tinode-sdk';\n\nimport Alert from '../widgets/alert.jsx';\nimport ContextMenu from '../widgets/context-menu.jsx';\nimport ForwardDialog from '../widgets/forward-dialog.jsx';\n\nimport InfoView from './info-view.jsx';\nimport MessagesView from './messages-view.jsx';\nimport SidepanelView from './sidepanel-view.jsx';\n\nimport { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, FORWARDED_PREVIEW_LENGTH, LOGGING_ENABLED,\n MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';\nimport { PACKAGE_VERSION } from '../version.js';\nimport { base64ReEncode, makeImageUrl } from '../lib/blob-helpers.js';\nimport { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { secondsToTime } from '../lib/strformat.js'\nimport { updateFavicon } from '../lib/utils.js';\n\n// Sound to play on message received.\nconst POP_SOUND = new Audio('audio/msg.mp3');\n\nconst messages = defineMessages({\n reconnect_countdown: {\n id: 'reconnect_countdown',\n defaultMessage: 'Disconnected. Reconnecting in {seconds}…',\n description: 'Message shown when an app update is available.'\n },\n reconnect_now: {\n id: 'reconnect_now',\n defaultMessage: 'Try now',\n description: 'Prompt for reconnecting now'\n },\n push_init_failed: {\n id: 'push_init_failed',\n defaultMessage: 'Failed to initialize push notifications',\n description: 'Error message when push notifications have failed to initialize.'\n },\n invalid_security_token: {\n id: 'invalid_security_token',\n defaultMessage: 'Invalid security token',\n description: 'Error message when resetting password.'\n },\n no_connection: {\n id: 'no_connection',\n defaultMessage: 'No connection',\n description: 'Warning that the user is offline.'\n },\n code_doesnot_match: {\n id: 'code_doesnot_match',\n defaultMessage: 'Code does not match',\n description: 'Error message when the credential validation code is incorrect.'\n },\n menu_item_info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n }\n});\n\nclass TinodeWeb extends React.Component {\n constructor(props) {\n super(props);\n\n this.selfRef = React.createRef();\n\n this.state = this.getBlankState();\n\n this.handleResize = this.handleResize.bind(this);\n this.handleHashRoute = this.handleHashRoute.bind(this);\n this.handleOnline = this.handleOnline.bind(this);\n this.checkForAppUpdate = this.checkForAppUpdate.bind(this);\n this.handleVisibilityEvent = this.handleVisibilityEvent.bind(this);\n this.handleError = this.handleError.bind(this);\n this.handleLoginRequest = this.handleLoginRequest.bind(this);\n this.handlePersistenceChange = this.handlePersistenceChange.bind(this);\n this.handleConnected = this.handleConnected.bind(this);\n this.handleAutoreconnectIteration = this.handleAutoreconnectIteration.bind(this);\n this.doLogin = this.doLogin.bind(this);\n this.handleCredentialsRequest = this.handleCredentialsRequest.bind(this);\n this.handleLoginSuccessful = this.handleLoginSuccessful.bind(this);\n this.handleDisconnect = this.handleDisconnect.bind(this);\n this.tnMeMetaDesc = this.tnMeMetaDesc.bind(this);\n this.tnMeContactUpdate = this.tnMeContactUpdate.bind(this);\n this.tnMeSubsUpdated = this.tnMeSubsUpdated.bind(this);\n this.resetContactList = this.resetContactList.bind(this);\n this.tnData = this.tnData.bind(this);\n this.tnInitFind = this.tnInitFind.bind(this);\n this.tnFndSubsUpdated = this.tnFndSubsUpdated.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleTopicSelected = this.handleTopicSelected.bind(this);\n this.handleHideMessagesView = this.handleHideMessagesView.bind(this);\n this.handleSendMessage = this.handleSendMessage.bind(this);\n this.handleNewChatInvitation = this.handleNewChatInvitation.bind(this);\n this.handleNewAccount = this.handleNewAccount.bind(this);\n this.handleNewAccountRequest = this.handleNewAccountRequest.bind(this);\n this.handleUpdatePasswordRequest = this.handleUpdatePasswordRequest.bind(this);\n this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);\n this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);\n this.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.bind(this);\n this.handleShowBlocked = this.handleShowBlocked.bind(this);\n this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this);\n this.handleCredAdd = this.handleCredAdd.bind(this);\n this.handleCredDelete = this.handleCredDelete.bind(this);\n this.handleCredConfirm = this.handleCredConfirm.bind(this);\n this.initFCMessaging = this.initFCMessaging.bind(this);\n this.toggleFCMToken = this.toggleFCMToken.bind(this);\n this.handlePushMessage = this.handlePushMessage.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);\n this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);\n this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);\n this.handleUnarchive = this.handleUnarchive.bind(this);\n this.handleChangePermissions = this.handleChangePermissions.bind(this);\n this.handleTagsUpdateRequest = this.handleTagsUpdateRequest.bind(this);\n this.handleLogout = this.handleLogout.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n this.handleDeleteTopicRequest = this.handleDeleteTopicRequest.bind(this);\n this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);\n this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);\n this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);\n this.handleReportTopic = this.handleReportTopic.bind(this);\n this.handleShowContextMenu = this.handleShowContextMenu.bind(this);\n this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this);\n this.handleHideContextMenu = this.handleHideContextMenu.bind(this);\n this.handleShowAlert = this.handleShowAlert.bind(this);\n this.handleShowInfoView = this.handleShowInfoView.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);\n this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);\n this.handleResetPassword = this.handleResetPassword.bind(this);\n this.handleContextMenuAction = this.handleContextMenuAction.bind(this);\n\n this.handleShowForwardDialog = this.handleShowForwardDialog.bind(this);\n this.handleHideForwardDialog = this.handleHideForwardDialog.bind(this);\n\n this.sendMessageToTopic = this.sendMessageToTopic.bind(this);\n }\n\n getBlankState() {\n const settings = LocalStorageUtil.getObject('settings') || {};\n const persist = !!LocalStorageUtil.getObject('keep-logged-in');\n\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n // Try to re-login on new connection.\n autoLogin: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n incognitoMode: false,\n // Persistent request to enable alerts.\n desktopAlerts: persist && !!settings.desktopAlerts,\n // Enable / disable checkbox.\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebaseInitApp != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: persist ? LocalStorageUtil.getObject('firebase-token') : null,\n\n applicationVisible: !document.hidden,\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n myTrustedBadges: [],\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\n persist: persist,\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newTopicParams: null,\n loginDisabled: false,\n displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),\n infoPanel: undefined,\n mobilePanel: 'sidepanel',\n\n contextMenuVisible: false,\n contextMenuBounds: null,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuItems: [],\n\n forwardDialogVisible: false,\n forwardMessage: null,\n\n // Modal alert dialog.\n alertVisible: false,\n alertParams: {},\n\n // Chats as shown in the ContactsView\n chatList: [],\n // Contacts returned by a search query.\n searchResults: [],\n // Merged results of a search query and p2p chats.\n searchableContacts: [],\n credMethod: undefined,\n credCode: undefined,\n // Topic to go to after login.\n requestedTopic: undefined\n };\n }\n\n componentDidMount() {\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', (e) => { this.handleOnline(true); });\n window.addEventListener('offline', (e) => { this.handleOnline(false); });\n window.addEventListener('hashchange', this.handleHashRoute);\n\n // Process background notifications from the service worker.\n if (typeof BroadcastChannel == 'function') {\n const serviceWorkerChannel = new BroadcastChannel('tinode-sw');\n serviceWorkerChannel.addEventListener('message', this.handlePushMessage);\n } else {\n // Safari is broken by design.\n console.warn(\"Your browser does not support BroadcastChannel. Some features will not be available\");\n }\n\n // Window/tab visible or invisible for pausing timers.\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n\n new Promise((resolve, reject) => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport,\n this.props.intl.locale, this.state.persist, resolve);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n }).then(() => {\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n this.initFCMessaging().then(() => {\n if (this.state.desktopAlerts) {\n this.tinode.setDeviceToken(this.state.firebaseToken);\n }\n }).catch(() => {\n // do nothing: handled earlier.\n // catch needed to pervent unnecessary logging of error.\n });\n }\n\n // Parse and save the hash navigation params.\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n\n // Read contacts from cache.\n this.resetContactList();\n\n const token = this.state.persist ? LocalStorageUtil.getObject('auth-token') : undefined;\n if (token) {\n this.setState({autoLogin: true});\n\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n // Maybe navigate to home screen.\n if (!['cred', 'reset', 'register'].includes(parsedNav.path[0])) {\n // Save possible topic name.\n this.setState({requestedTopic: parsedNav.path[1]});\n HashNavigation.navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\n });\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n }\n\n // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n static tnSetup(serverAddress, transport, locale, persistentCache, onSetupCompleted) {\n const tinode = new Tinode({appName: APP_NAME, host: serverAddress, apiKey: API_KEY, transport: transport,\n secure: isSecureConnection(), persist: persistentCache}, onSetupCompleted);\n tinode.setHumanLanguage(locale);\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n // Tinode received a push notification from the server.\n handlePushMessage(payload) {\n const data = payload.data || {};\n switch (data.what) {\n case 'msg':\n if (Tinode.isChannelTopicName(data.topic)) {\n // The last argument is a fake user Id: otherwise the update is seen as one from the current user.\n this.tinode.oobNotification('msg', data.topic, data.seq, 'fake-uid');\n }\n break;\n case 'read':\n // Message read on another device, clear it here.\n this.tinode.oobNotification('read', data.topic, data.seq);\n break;\n default:\n console.warn(\"Unknown push type ignored\", data.what, data);\n }\n }\n\n initFCMessaging() {\n const {formatMessage, locale} = this.props.intl;\n const onError = (msg, err) => {\n console.error(msg, err);\n this.handleError(formatMessage(messages.push_init_failed), 'err');\n this.setState({desktopAlertsEnabled: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n\n try {\n this.fcm = firebaseGetMessaging(firebaseInitApp(FIREBASE_INIT, APP_NAME));\n return navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n reg.active.postMessage(JSON.stringify({locale: locale, version: PACKAGE_VERSION}));\n return reg;\n }).then((reg) => {\n // Request token.\n return TinodeWeb.requestFCMToken(this.fcm, reg);\n }).then((token) => {\n const persist = LocalStorageUtil.getObject('keep-logged-in');\n if (token != this.state.firebaseToken) {\n this.tinode.setDeviceToken(token);\n if (persist) {\n LocalStorageUtil.setObject('firebase-token', token);\n }\n }\n this.setState({firebaseToken: token, desktopAlerts: true});\n if (persist) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n\n // Handhe FCM pushes\n // (a) for channels always,\n // (b) pushes when the app is in foreground but has no focus.\n firebaseOnMessage(this.fcm, (payload) => { this.handlePushMessage(payload); });\n }).catch((err) => {\n // SW registration or FCM has failed :(\n onError(err);\n throw err;\n });\n } catch (err) {\n onError(err);\n return Promise.reject(err);\n }\n }\n\n // Google's FCM API is idiotic.\n static requestFCMToken(fcm, sw) {\n return firebaseGetToken(fcm, {\n serviceWorkerRegistration: sw,\n vapidKey: FIREBASE_INIT.messagingVapidKey\n }).then((token) => {\n if (token) {\n return token;\n } else {\n // Try to request permissions.\n return Notification.requestPermission().then((permission) => {\n if (permission === 'granted') {\n return firebaseGetToken(fcm, {\n serviceWorkerRegistration: reg,\n vapidKey: FIREBASE_INIT.messagingVapidKey\n }).then((token) => {\n if (token) {\n return token;\n } else {\n throw new Error(\"Failed to initialize notifications\");\n }\n });\n } else {\n throw new Error(\"No permission to send notifications: \" + permission);\n }\n });\n }\n });\n }\n\n handleResize() {\n const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n if (this.state.displayMobile != mobile) {\n this.setState({displayMobile: mobile});\n }\n }\n\n // Check if a newer version of TinodeWeb app is available at the server.\n checkForAppUpdate(reg) {\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n const msg = <>\n \n \n .>;\n this.handleError(msg, 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n const hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','notif','security','support','general','crop',\n 'cred','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {\n this.setState({sidePanelSelected: hash.path[0]});\n } else {\n console.warn(\"Unknown sidepanel view\", hash.path[0]);\n }\n\n // Topic for MessagesView selector.\n let topicName = hash.path[1] || null;\n if (topicName != this.state.topicSelected) {\n if (!Tinode.topicType(topicName)) {\n // Clear invalid topic name.\n topicName = null;\n }\n this.setState({\n topicSelected: topicName,\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)\n });\n }\n } else {\n // Empty hashpath\n this.setState({sidePanelSelected: '', topicSelected: null});\n }\n\n // Save validation credentials, if available.\n if (hash.params.method) {\n this.setState({ credMethod: hash.params.method });\n }\n if (hash.params.code) {\n this.setState({ credCode: hash.params.code });\n }\n\n // Additional parameters of panels.\n this.setState({\n infoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n\n handleOnline(online) {\n if (online) {\n this.handleError();\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n } else {\n this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n handleVisibilityEvent() {\n this.setState({applicationVisible: !document.hidden});\n }\n\n handleError(err, level, action, actionText) {\n this.setState({errorText: err, errorLevel: level, errorAction: action, errorActionText: actionText});\n }\n\n // User clicked Login button in the side panel.\n handleLoginRequest(login, password) {\n this.setState({\n loginDisabled: true,\n login: login,\n password: password,\n loadSpinnerVisible: true,\n autoLogin: true\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});\n } else {\n this.tinode.connect().catch((err) => {\n // Socket error\n this.setState({loginDisabled: false, autoLogin: false, loadSpinnerVisible: false});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Enable or disable saving the password and IndexedDB.\n handlePersistenceChange(persist) {\n if (persist) {\n this.tinode.initStorage().then(() => {\n LocalStorageUtil.setObject('keep-logged-in', true);\n this.setState({persist: true});\n });\n } else {\n this.tinode.clearStorage().then(() => {\n LocalStorageUtil.setObject('keep-logged-in', false);\n this.setState({persist: false});\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n // Just to be sure.\n clearInterval(this.reconnectCountdown);\n this.handleError();\n\n const params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none')\n });\n\n if (this.state.autoLogin) {\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\n }\n }\n\n // Called for each auto-reconnect iteration.\n handleAutoreconnectIteration(sec, prom) {\n clearInterval(this.reconnectCountdown);\n\n if (sec < 0) {\n // Clear error\n this.handleError();\n return;\n }\n\n if (prom) {\n // Reconnecting now\n prom.then(() => {\n // Reconnected: clear error\n this.handleError();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n return;\n }\n\n const {formatMessage} = this.props.intl;\n let count = sec / 1000;\n count = count | count;\n this.reconnectCountdown = setInterval(() => {\n const timeLeft = (count > 99) ? secondsToTime(count) : count;\n this.handleError(\n formatMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatMessage(messages.reconnect_now)\n );\n count -= 1;\n }, 1000);\n }\n\n // Connection lost\n handleDisconnect(err) {\n this.setState({\n connected: false,\n ready: false,\n topicSelectedOnline: false,\n errorText: err && err.message ? err.message : \"Disconnected\",\n errorLevel: err && err.message ? 'err' : 'warn',\n loginDisabled: false,\n contextMenuVisible: false,\n forwardDialogVisible: false,\n serverVersion: \"no connection\"\n });\n }\n\n doLogin(login, password, cred) {\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n HashNavigation.navigateTo('');\n return;\n }\n // Sanitize and package credentail.\n cred = Tinode.credential(cred);\n // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n let promise = null;\n let token = this.tinode.getAuthToken();\n if (login && password) {\n token = null;\n this.setState({password: null});\n promise = this.tinode.loginBasic(login, password, cred);\n } else if (token) {\n promise = this.tinode.loginToken(token.token, cred);\n }\n\n if (promise) {\n promise.then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n this.setState({loadSpinnerVisible: false});\n if (cred) {\n this.handleError(this.props.intl.formatMessage(messages.code_doesnot_match), 'warn');\n }\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful();\n }\n }).catch((err) => {\n // Login failed, report error.\n this.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined,\n loadSpinnerVisible: false,\n autoLogin: false\n });\n this.handleError(err.message, 'err');\n if (token) {\n this.handleLogout();\n }\n HashNavigation.navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n HashNavigation.navigateTo('');\n this.setState({loginDisabled: false});\n }\n }\n\n handleCredentialsRequest(params) {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n }\n\n handleLoginSuccessful() {\n this.handleError();\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n\n const goToTopic = this.state.requestedTopic;\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n const me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID(),\n autoLogin: true,\n requestedTopic: undefined,\n });\n // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n me.subscribe(\n me.startMetaQuery().\n withLaterSub().\n withDesc().\n withTags().\n withCred().\n build()\n ).catch((err) => {\n this.tinode.disconnect();\n localStorage.removeItem('auth-token');\n this.handleError(err.message, 'err');\n HashNavigation.navigateTo('');\n }).finally(() => {\n this.setState({loadSpinnerVisible: false});\n });\n let urlHash = HashNavigation.setUrlSidePanel(window.location.hash, 'contacts');\n if (goToTopic) {\n urlHash = HashNavigation.setUrlTopic(urlHash, goToTopic);\n }\n HashNavigation.navigateTo(urlHash);\n }\n\n tnMeMetaDesc(desc) {\n if (desc) {\n if (desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\n });\n }\n if (desc.trusted) {\n const badges = [];\n for (const [key, val] of Object.entries(desc.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n this.setState({\n myTrustedBadges: badges,\n });\n }\n if (desc.acs) {\n this.setState({\n incognitoMode: !desc.acs.isPresencer()\n });\n }\n }\n }\n\n // Reactions to updates to the contact list.\n tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedOnline: (what == 'on')});\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // Check if the topic is archived. If so, don't play a sound.\n const topic = this.tinode.getTopic(cont.topic);\n const archived = topic && topic.isArchived();\n\n // New message received. Maybe the message is from the current user, then unread is 0.\n if (cont.unread > 0 && this.state.messageSounds && !archived) {\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (document.hidden || this.state.topicSelected != cont.topic) {\n POP_SOUND.play();\n }\n }\n // Reorder contact list to use possibly updated 'touched'.\n this.resetContactList();\n } else if (what == 'recv') {\n // Explicitly ignoring \"recv\" -- it causes no visible updates to contact list.\n } else if (what == 'gone' || what == 'unsub') {\n // Topic deleted or user unsubscribed. Remove topic from view.\n // If the currently selected topic is gone, clear the selection.\n if (this.state.topicSelected == cont.topic) {\n this.handleTopicSelected(null);\n }\n // Redraw without the deleted topic.\n this.resetContactList();\n } else if (what == 'acs') {\n // Permissions changed. If it's for the currently selected topic,\n // update the views.\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedAcs: cont.acs});\n }\n } else if (what == 'del') {\n // TODO: messages deleted (hard or soft) -- update pill counter.\n } else if (what == 'upd') {\n // upd - handled by the SDK. Explicitly ignoring here.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n console.info(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n\n tnMeSubsUpdated(unused) {\n this.resetContactList();\n }\n\n // Merge search results and contact list to create a single flat\n // list of known contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n const merged = {};\n\n // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n for (const c of chatList) {\n if (Tinode.isP2PTopicName(c.topic)) {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n }\n\n // Add all foundCountacts if they have not been added already.\n for (const c of foundContacts) {\n if (!merged[c.user]) {\n merged[c.user] = c;\n }\n }\n\n return Object.values(merged);\n }\n\n resetContactList() {\n const newState = {\n chatList: []\n };\n\n if (!this.state.ready) {\n newState.ready = true;\n }\n\n this.tinode.getMeTopic().contacts((c) => {\n if (!c.topic && !c.user) {\n // Contacts expect c.topic to be set.\n c.topic = c.name;\n }\n\n newState.chatList.push(c);\n if (this.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n });\n\n const past = new Date(0);\n newState.chatList.sort((a, b) => {\n return (a.touched || past).getTime() - (b.touched || past).getTime();\n });\n\n // Merge search results and chat list.\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n }\n\n // Sending \"received\" notifications\n tnData(data) {\n const topic = this.tinode.getTopic(data.topic);\n if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(() => {\n this.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, RECEIVED_DELAY);\n }\n }\n\n /* Fnd topic: find contacts by tokens */\n tnInitFind() {\n const fnd = this.tinode.getFndTopic();\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.subscribe(fnd.startMetaQuery().withSub().build()).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n tnFndSubsUpdated() {\n const foundContacts = [];\n // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n this.tinode.getFndTopic().contacts((s) => {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\n }\n\n /** Called when the user enters a contact into the contact search field in the NewAccount panel\n @param query {Array} is an array of contacts to search for\n */\n handleSearchContacts(query) {\n const fnd = this.tinode.getFndTopic();\n fnd.setMeta({desc: {public: query}}).then((ctrl) => {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User clicked on a topic in the side panel or deleted a topic.\n handleTopicSelected(topicName) {\n // Clear newTopicParams after use.\n if (this.state.newTopicParams && this.state.newTopicParams._topicName != topicName) {\n this.setState({\n newTopicParams: null\n });\n }\n\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n infoPanel: undefined\n });\n // Different topic selected.\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: this.tinode.isTopicOnline(topicName),\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName),\n forwardMessage: null\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName));\n }\n } else {\n // Currently selected contact deleted\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'sidepanel',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n infoPanel: undefined,\n forwardMessage: null\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));\n }\n }\n\n // In mobile view user requested to show sidepanel\n handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null));\n }\n\n // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n // - msg - Drafty message with content\n // - promise - Promise to be resolved when the upload is completed\n // - uploader - for tracking progress\n // - head - head dictionary to be attached to the message\n handleSendMessage(msg, promise, uploader, head) {\n const topic = this.tinode.getTopic(this.state.topicSelected);\n this.sendMessageToTopic(topic, msg, promise, uploader, head);\n }\n\n sendMessageToTopic(topic, msg, promise, uploader, head) {\n msg = topic.createMessage(msg, false);\n // The uploader is used to show progress.\n msg._uploader = uploader;\n\n if (head) {\n msg.head = Object.assign(msg.head || {}, head);\n }\n\n if (!topic.isSubscribed()) {\n // Topic is not subscribed yet. Subscribe.\n if (!promise) {\n promise = Promise.resolve();\n }\n promise = promise\n .then(() => topic.subscribe())\n .then(() => {\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n this.sendMessage(pub);\n }\n });\n });\n }\n\n if (promise) {\n promise = promise.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise)\n .then((ctrl) => {\n if (topic.isArchived()) {\n return topic.archive(false);\n }\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleNewChatInvitation(topicName, action) {\n const topic = this.tinode.getTopic(topicName);\n let response = null;\n switch (action) {\n case 'accept':\n // Accept given permissions.\n const mode = topic.getAccessMode().getGiven();\n response = topic.setMeta({sub: {mode: mode}});\n if (topic.isP2PType()) {\n // For P2P topics change 'given' permission of the peer too.\n // In p2p topics the other user has the same name as the topic.\n response = response.then((ctrl) => {\n topic.setMeta({sub: {user: topicName, mode: mode}});\n });\n }\n break;\n case 'delete':\n // Ignore invitation by deleting it.\n response = topic.delTopic(true);\n break;\n case 'block':\n // Ban the topic making futher invites impossible.\n // Just self-ban.\n const am = topic.getAccessMode().updateWant('-JP').getWant();\n response = topic.setMeta({sub: {mode: am}}).then((ctrl) => {\n return this.handleTopicSelected(null);\n });\n break;\n default:\n console.warn(\"Unknown invitation action\", '\"' + action + '\"\"');\n }\n\n if (response != null) {\n response.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // User chose a Sign Up menu item.\n handleNewAccount() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register'));\n }\n\n // Actual registration of a new account.\n handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n // Clear old error, if any.\n this.handleError();\n\n this.tinode.connect(this.state.serverAddress)\n .then(() => {\n return this.tinode.createAccountBasic(login_, password_,\n {public: public_, tags: tags_, cred: Tinode.credential(cred_)});\n }).then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful(this);\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleToggleIncognitoMode(on) {\n // Make state undefined.\n this.setState({incognitoMode: null});\n\n const me = this.tinode.getMeTopic();\n const am = me.getAccessMode().updateWant(on ? '-P' : '+P').getWant();\n me.setMeta({sub: {mode: am}}).catch((err) => {\n // Request failed, keep existing state.\n this.setState({incognitoMode: !on});\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountTagsRequest(tags) {\n this.tinode.getMeTopic().setMeta({tags: tags})\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User chose Settings menu item.\n handleSettings() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n const serverAddress = settings.serverAddress || this.state.serverAddress;\n const transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport, this.props.intl.locale,\n LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n LocalStorageUtil.setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, ''));\n }\n\n // User chose 'Archived chats'.\n handleShowArchive() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'archive' : ''));\n }\n\n // User viewes 'Blocked chats'.\n handleShowBlocked() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'blocked' : ''));\n }\n\n toggleFCMToken(enabled) {\n if (enabled) {\n this.setState({desktopAlerts: null});\n if (!this.state.firebaseToken) {\n this.initFCMessaging();\n } else {\n this.setState({desktopAlerts: true});\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }\n } else if (this.state.firebaseToken && this.fcm) {\n firebaseDelToken(this.fcm).catch((err) => {\n console.error(\"Unable to delete token.\", err);\n }).finally(() => {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n localStorage.removeItem('firebase-token');\n this.setState({desktopAlerts: false, firebaseToken: null});\n // Inform the server that the token was deleted.\n this.tinode.setDeviceToken(null);\n });\n } else {\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n }\n\n handleToggleMessageSounds(enabled) {\n this.setState({messageSounds: enabled});\n LocalStorageUtil.updateObject('settings', {\n messageSoundsOff: !enabled\n });\n }\n\n handleCredAdd(method, value) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: method, val: value}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredDelete(method, value) {\n const me = this.tinode.getMeTopic();\n me.delCredential(method, value).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredConfirm(method, response) {\n this.handleCredentialsRequest({cred: [method]});\n }\n\n // User clicked Cancel button in Setting or Sign Up panel.\n handleSidepanelCancel() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n let path = '';\n if (['security','support','general','notif'].includes(parsed.path[0])) {\n path = 'edit';\n } else if ('crop' == parsed.path[0]) {\n path = 'general';\n } else if ('blocked' == parsed.path[0]) {\n path = 'security';\n } else if (this.state.myUserId) {\n path = 'contacts';\n }\n parsed.path[0] = path;\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n delete parsed.params.scheme;\n delete parsed.params.token;\n }\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n this.setState({errorText: '', errorLevel: null});\n }\n\n // Sidepanel navigator. No need to bind to 'this'.\n basicNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));\n }\n\n // Topic info navigator. No need to bind to 'this'.\n infoNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlInfoPanel(window.location.hash, hash));\n }\n\n // Request to start a topic, new or selected from search results, or \"by ID\".\n handleStartTopicRequest(topicName, newTopicParams, isChannel) {\n // Check if topic is indeed new. If not, launch it.\n if (topicName && this.tinode.isTopicCached(topicName)) {\n this.handleTopicSelected(topicName);\n return;\n }\n\n const params = {};\n if (Tinode.isP2PTopicName(topicName)) {\n // Because we are initialing the subscription, set 'want' to all permissions.\n params.sub = {mode: DEFAULT_P2P_ACCESS_MODE};\n // Give the other user all permissions too.\n params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};\n } else {\n topicName = topicName || this.tinode.newGroupTopicName(isChannel);\n if (newTopicParams) {\n params.desc = {public: newTopicParams.public, private: {comment: newTopicParams.private}};\n params.tags = newTopicParams.tags;\n }\n }\n params._topicName = topicName;\n this.setState({newTopicParams: params}, () => {this.handleTopicSelected(topicName)});\n }\n\n // New topic was created, here is the new topic name.\n handleNewTopicCreated(oldName, newName) {\n if (this.state.topicSelected == oldName && oldName != newName) {\n // If the current URl contains the old topic name, replace it with new.\n // Update the name of the selected topic first so the navigator doen't clear\n // the state.\n this.setState({topicSelected: newName}, () => {\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName));\n });\n }\n }\n\n handleTopicUpdateRequest(topicName, pub, priv, defacs) {\n this.handleError();\n\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const params = {};\n let attachments;\n if (pub) {\n if (pub.photo) {\n if (pub.photo.ref && pub.photo.ref != Tinode.DEL_CHAR) {\n attachments = [pub.photo.ref];\n } else if (!pub.photo.data || pub.photo.data == Tinode.DEL_CHAR) {\n pub.photo = Tinode.DEL_CHAR;\n }\n }\n params.public = pub;\n }\n\n if (typeof priv == 'string') {\n params.private = (priv === Tinode.DEL_CHAR) ?\n Tinode.DEL_CHAR : {comment: priv};\n }\n if (defacs) {\n params.defacs = defacs;\n }\n topic.setMeta({desc: params, attachments: attachments}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleUnarchive(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.archive(false);\n }\n }\n\n handleUpdatePasswordRequest(password) {\n this.handleError();\n\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleChangePermissions(topicName, mode, uid) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const am = topic.getAccessMode();\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n topic.setMeta({sub: {user: uid, mode: mode}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleTagsUpdateRequest(topicName, tags) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.setMeta({tags: tags}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleLogout() {\n updateFavicon(0);\n\n // Remove stored data.\n localStorage.removeItem('auth-token');\n localStorage.removeItem('firebase-token');\n localStorage.removeItem('settings');\n if (this.state.firebaseToken) {\n firebaseDelToken(this.fcm);\n }\n\n clearInterval(this.reconnectCountdown);\n\n let cleared;\n if (this.tinode) {\n cleared = this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n } else {\n cleared = Promose.resolve();\n }\n this.setState(this.getBlankState());\n\n cleared.then(() => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress,\n this.state.transport, this.props.intl.locale, LocalStorageUtil.getObject('keep-logged-in'), () => {\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n HashNavigation.navigateTo('');\n })\n });\n }\n\n handleDeleteAccount() {\n this.tinode.delCurrentUser(true).then((ctrl) => {\n this.handleLogout();\n });\n }\n\n handleDeleteTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request to hard-delete topic.\n topic.delTopic(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleDeleteMessagesRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request hard-delete all messages.\n topic.delMessagesAll(true).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleLeaveUnsubRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.leave(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleBlockTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleReportTopic(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Publish spam report.\n this.tinode.publish(Tinode.TOPIC_SYS, Tinode.Drafty.attachJSON(null, {\n 'action': 'report',\n 'target': topicName\n }));\n\n // Remove J and P permissions.\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {x: params.x, y: params.y},\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: this.selfRef.current.getBoundingClientRect()\n });\n }\n\n //\n handleShowForwardDialog(params) {\n if (this.state.sidePanelSelected == 'newtpk') {\n // If the Find panel is active, close it.\n this.handleSidepanelCancel();\n }\n const header = '➦ ' + params.userName;\n const content = typeof params.content == 'string' ?\n Tinode.Drafty.init(params.content) : Tinode.Drafty.forwardedContent(params.content);\n const preview = Tinode.Drafty.preview(content, FORWARDED_PREVIEW_LENGTH,\n undefined, params.forwarded != null);\n const msg = Tinode.Drafty.append(\n Tinode.Drafty.appendLineBreak(\n Tinode.Drafty.mention(header, params.userFrom)),\n content);\n\n const msgPreview = Tinode.Drafty.quote(header, params.userFrom, preview);\n const head = {\n forwarded: params.topicName + ':' + params.seq\n };\n this.setState({\n forwardDialogVisible: true,\n forwardMessage: { head: head, msg: msg, preview: msgPreview }\n });\n }\n\n defaultTopicContextMenu(topicName) {\n const topic = this.tinode.getTopic(topicName);\n\n let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false, archived = false;\n if (topic) {\n subscribed = topic.isSubscribed();\n archived = topic.isArchived();\n\n const acs = topic.getAccessMode();\n if (acs) {\n muted = acs.isMuted();\n blocked = !acs.isJoiner();\n self_blocked = !acs.isJoiner('want');\n deleter = acs.isDeleter();\n }\n }\n\n return [\n subscribed ? {\n title: this.props.intl.formatMessage(messages.menu_item_info),\n handler: this.handleShowInfoView\n } : null,\n subscribed ? 'messages_clear' : null,\n subscribed && deleter ? 'messages_clear_hard' : null,\n muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',\n self_blocked ? 'topic_unblock' : 'topic_block',\n archived ? 'topic_restore' : 'topic_archive',\n 'topic_delete'\n ];\n }\n\n handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n\n handleHideForwardDialog(keepForwardedMessage) {\n this.setState({\n forwardDialogVisible: false,\n forwardMessage: keepForwardedMessage ? this.state.forwardMessage : null\n });\n }\n\n handleContextMenuAction(action, promise, params) {\n if (action == 'topic_archive') {\n if (promise && params.topicName && params.topicName == this.state.topicSelected) {\n promise.then(() => {\n this.handleTopicSelected(null);\n });\n }\n } else if (action == 'menu_item_forward') {\n this.handleShowForwardDialog(params);\n }\n }\n\n handleShowAlert(title, content, onConfirm, confirmText, onReject, rejectText) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirmText,\n onReject: onReject,\n reject: rejectText\n }\n });\n }\n\n handleShowInfoView() {\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', 'info'));\n this.setState({infoPanel: 'info'});\n }\n\n handleMemberUpdateRequest(topicName, added, removed) {\n if (!topicName) {\n return;\n }\n\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map((uid) => {\n topic.invite(uid, null).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map((uid) => {\n topic.delSubscription(uid).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n }\n\n handleValidateCredentialsRequest(cred, code) {\n if (this.tinode.isAuthenticated()) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: cred, resp: code}})\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n } else {\n this.setState({credMethod: cred, credCode: code});\n this.doLogin(null, null, {meth: cred, resp: code});\n }\n }\n\n handlePasswordResetRequest(method, value) {\n // If already connected, connnect() will return a resolved promise.\n return this.tinode.connect()\n .then(() => {\n return this.tinode.requestResetAuthSecret('basic', method, value);\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n handleResetPassword(scheme, newPassword, token) {\n token = base64ReEncode(token);\n if (!token) {\n this.handleError(this.props.intl.formatMessage(messages.invalid_security_token), 'err');\n } else {\n this.tinode.connect()\n .then(() => {\n return this.tinode.updateAccountBasic(null, null, newPassword, {token: token});\n })\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n }\n\n render() {\n return (\n \n {this.state.contextMenuVisible ?\n
{\n if (topicName == this.state.topicSelected) {\n this.handleTopicSelected(null);\n }\n }}\n onError={this.handleError} />\n :\n null\n }\n {this.state.forwardDialogVisible ?\n \n :\n null\n }\n { this.setState({alertVisible: false}); }) : null}\n reject={this.state.alertParams.reject}\n onConfirm={() => { this.setState({alertVisible: false}); this.state.alertParams.onConfirm(); }}\n confirm={this.state.alertParams.confirm}\n />\n \n\n \n\n {this.state.infoPanel ?\n \n :\n null\n }\n \n );\n }\n};\n\nexport default injectIntl(TinodeWeb);\n","// Put all packages together.\n// Used to generate umd/index.prod.js\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport { IntlProvider } from 'react-intl';\n\nimport allMessages from './messages.json';\nimport TinodeWeb from './views/tinode-web.jsx';\nimport HashNavigation from './lib/navigation.js';\n\n// Detect human language to use in the UI:\n// Check parameters from URL hash #?hl=ru, then browser, then use 'en' as a fallback.\nconst { params } = HashNavigation.parseUrlHash(window.location.hash);\nconst language = (params && params.hl) ||\n (navigator.languages && navigator.languages[0]) ||\n navigator.language ||\n navigator.userLanguage ||\n 'en';\n\n// Get the base language 'en' from a more specific 'en_GB' or 'en-US' as a partial fallback.\nconst baseLanguage = language.toLowerCase().split(/[-_]/)[0];\n\n// Try the full locale first, then the locale without the region code, fallback to 'en'.\nconst htmlLang = allMessages[language] ? langauge : allMessages[baseLanguage] ? baseLanguage : 'en';\nconst messages = allMessages[htmlLang];\n\n// Set lang attribute of the HTML element: \ndocument.getElementsByTagName('html')[0].setAttribute('lang', htmlLang);\n\nReactDOM.render(\n \n \n \n \n ,\n document.getElementById('mountPoint')\n);\n"],"names":["exports","toArray","arr","Array","prototype","slice","call","promisifyRequest","request","Promise","resolve","reject","onsuccess","result","onerror","error","promisifyRequestCall","obj","method","args","p","apply","then","promisifyCursorRequestCall","value","Cursor","proxyProperties","ProxyClass","targetProp","properties","forEach","prop","Object","defineProperty","get","this","set","val","proxyRequestMethods","Constructor","arguments","proxyMethods","proxyCursorRequestMethods","Index","index","_index","cursor","_cursor","_request","ObjectStore","store","_store","Transaction","idbTransaction","_tx","complete","oncomplete","onabort","UpgradeDB","db","oldVersion","transaction","_db","DB","openDb","name","version","upgradeCallback","indexedDB","onupgradeneeded","event","deleteDb","IDBIndex","IDBCursor","methodName","createIndex","IDBObjectStore","objectStore","IDBTransaction","createObjectStore","IDBDatabase","funcName","replace","callback","length","nativeObject","getAll","query","count","instance","items","iterateCursor","push","undefined","continue","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","module","__webpack_modules__","n","getter","__esModule","d","a","definition","key","o","enumerable","g","globalThis","Function","e","window","hasOwnProperty","React","ReactDOM","ReactIntl","stringToByteArray$1","str","out","i","c","charCodeAt","Deferred","constructor","promise","wrapCallback","catch","FirebaseError","Error","code","message","customData","super","setPrototypeOf","captureStackTrace","ErrorFactory","create","service","serviceName","errors","data","fullCode","template","PATTERN","_","String","replaceTemplate","fullMessage","deepEqual","b","aKeys","keys","bKeys","k","includes","aProp","bProp","isObject","thing","getModularInstance","_delegate","Component","instanceFactory","type","multipleInstances","serviceProps","instantiationMode","onInstanceCreated","setInstantiationMode","mode","setMultipleInstances","setServiceProps","props","setInstanceCreatedCallback","DEFAULT_ENTRY_NAME","Provider","container","component","instances","Map","instancesDeferred","instancesOptions","onInitCallbacks","identifier","normalizedIdentifier","normalizeInstanceIdentifier","has","deferred","isInitialized","shouldAutoInitialize","getOrInitializeService","instanceIdentifier","getImmediate","options","_a","optional","getComponent","setComponent","isComponentEager","instanceDeferred","entries","clearInstance","delete","async","services","from","values","all","filter","map","INTERNAL","_delete","isComponentSet","getOptions","initialize","opts","onInit","existingCallbacks","Set","add","existingInstance","invokeOnInitCallbacks","callbacks","ComponentContainer","providers","addComponent","provider","getProvider","addOrOverwriteComponent","getProviders","LogLevel","levelStringToEnum","DEBUG","VERBOSE","INFO","WARN","ERROR","SILENT","defaultLogLevel","ConsoleMethod","defaultLogHandler","logType","logLevel","now","Date","toISOString","console","PlatformLoggerServiceImpl","getPlatformInfoString","isVersionServiceProvider","library","logString","join","name$o","version$1","logger","_logLevel","_logHandler","_userLogHandler","TypeError","setLogLevel","logHandler","userLogHandler","debug","log","info","warn","PLATFORM_LOG_STRING","_apps","_components","_addComponent","app","_registerComponent","componentName","ERROR_FACTORY","FirebaseAppImpl","config","_isDeleted","_options","assign","_config","_name","_automaticDataCollectionEnabled","automaticDataCollectionEnabled","_container","checkDestroyed","isDeleted","appName","initializeApp","rawConfig","existingApp","newApp","registerVersion","libraryKeyOrName","variant","libraryMismatch","match","versionMismatch","warning","PENDING_TIMEOUT_MS","PACKAGE_VERSION","INTERNAL_AUTH_VERSION","TOKEN_EXPIRATION_BUFFER","isServerError","getInstallationsEndpoint","projectId","extractAuthTokenInfoFromResponse","response","token","requestStatus","expiresIn","responseExpiresIn","Number","creationTime","getErrorFromResponse","requestName","errorData","json","serverCode","serverMessage","serverStatus","status","getHeaders","apiKey","Headers","Accept","getHeadersWithAuth","appConfig","refreshToken","headers","append","getAuthorizationHeader","retryIfServerError","fn","sleep","ms","setTimeout","VALID_FID_PATTERN","generateFid","fidByteArray","Uint8Array","self","crypto","msCrypto","getRandomValues","fid","array","btoa","fromCharCode","substr","encode","test","getKey","appId","fidChangeCallbacks","fidChanged","callFidChangeCallbacks","channel","getBroadcastChannel","postMessage","closeBroadcastChannel","broadcastFidChange","broadcastChannel","BroadcastChannel","onmessage","size","close","OBJECT_STORE_NAME","dbPromise","getDbPromise","upgradeDB","tx","oldValue","put","remove","update","updateFn","newValue","getInstallationEntry","registrationPromise","installationEntry","oldEntry","clearTimedOutRequest","registrationStatus","updateOrCreateInstallationEntry","entryWithPromise","navigator","onLine","inProgressEntry","registrationTime","registeredInstallationEntry","endpoint","body","authVersion","sdkVersion","JSON","stringify","fetch","ok","responseValue","authToken","createInstallationRequest","registerInstallation","waitUntilFidRegistration","triggerRegistrationIfNecessary","entry","updateInstallationRequest","generateAuthTokenRequest","platformLoggerProvider","getGenerateAuthTokenEndpoint","platformLogger","installation","refreshAuthToken","installations","forceRefresh","tokenPromise","isEntryRegistered","oldAuthToken","isAuthTokenExpired","isAuthTokenValid","updateAuthTokenRequest","waitUntilAuthTokenRequest","inProgressAuthToken","requestTime","makeAuthTokenRequestInProgressEntry","updatedInstallationEntry","fetchAuthTokenFromServer","getToken","installationsImpl","completeInstallationRegistration","getMissingValueError","valueName","INSTALLATIONS_NAME","internalFactory","getId","configKeys","keyName","extractAppConfig","DEFAULT_VAPID_KEY","CONSOLE_CAMPAIGN_ID","MessageType$1","MessageType","arrayToBase64","uint8Array","base64ToArray","base64String","base64","repeat","rawData","atob","outputArray","OLD_DB_NAME","OLD_OBJECT_STORE_NAME","upgradeDb","dbGet","firebaseDependencies","tokenDetails","oldTokenDetails","senderId","dbNames","databases","objectStoreNames","contains","clear","oldDetails","auth","p256dh","fcmToken","createTime","subscriptionOptions","swScope","vapidKey","checkTokenDetails","migrateOldDatabase","dbSet","requestDeleteToken","unsubscribeOptions","getEndpoint","responseData","errorInfo","err","getBody","web","applicationPubKey","getTokenInternal","messaging","pushSubscription","swRegistration","subscription","pushManager","getSubscription","subscribe","userVisibleOnly","applicationServerKey","getPushSubscription","scope","dbOptions","currentOptions","isVapidKeyEqual","isEndpointEqual","isAuthEqual","isP256dhEqual","isTokenValid","updatedToken","updateOptions","requestUpdateToken","updatedTokenDetails","deleteTokenInternal","updateToken","getNewToken","dbRemove","unsubscribe","subscribeOptions","requestGetToken","externalizePayload","internalPayload","payload","collapseKey","collapse_key","messageId","fcmMessageId","messagePayloadInternal","notification","title","image","propagateNotificationPayload","propagateDataPayload","fcmOptions","link","analyticsLabel","analytics_label","propagateFcmOptions","_mergeStrings","s1","s2","resultArray","charAt","MessagingService","analyticsProvider","deliveryMetricsExportedToBigQueryEnabled","onBackgroundMessageHandler","onMessageHandler","logEvents","isLogServiceStarted","messagingSenderId","registerDefaultSw","serviceWorker","register","browserErrorMessage","getToken$1","Notification","permission","requestPermission","updateVapidKey","ServiceWorkerRegistration","updateSwReg","serviceWorkerRegistration","logToScion","messageType","eventType","NOTIFICATION_CLICKED","PUSH_RECEIVED","getEventType","logEvent","message_id","message_name","message_time","message_device_time","Math","floor","messageEventListener","isFirebaseMessaging","next","dataPayload","WindowMessagingInternalFactory","isWindowSupported","preExist","DB_CHECK_NAME","open","deleteDatabase","cookieEnabled","PushSubscription","getMessagingInWindow","isSupported","deleteToken","deleteToken$1","onMessage","nextOrObserver","onMessage$1","addEventListener","Tinode","Alert","render","visible","className","content","onReject","onClick","FormattedMessage","id","defaultMessage","onConfirm","confirm","APP_NAME","KNOWN_HOSTS","hosted","local","DEFAULT_HOST","KEYPRESS_DELAY","DEFAULT_P2P_ACCESS_MODE","REM_SIZE","AVATAR_SIZE","MAX_INBAND_ATTACHMENT_SIZE","MAX_EXTERN_ATTACHMENT_SIZE","IMAGE_THUMBNAIL_DIM","MAX_TITLE_LENGTH","MAX_TOPIC_DESCRIPTION_LENGTH","messages","defineMessages","clear_messages","clear_for_all","delete_for_all","send_retry","mute","unmute","reply","forward","topic_delete","topic_delete_warning","delete_messages_warning","unblock","block","topic_block_warning","member_delete","archive","unarchive","edit_permissions","clear_messages_warning","ContextMenu","selfRef","formatMessage","intl","handlePageClick","bind","handleEscapeKey","handleClick","MenuItems","handler","params","errorHandler","onShowAlert","deleteMessages","retryMessage","replyToMessage","topicPermissionSetter","ctrl","onTopicRemoved","topicName","topic","tinode","getTopic","delTopic","user","delSubscription","componentDidMount","document","componentWillUnmount","removeEventListener","current","target","preventDefault","stopPropagation","hide","keyCode","item","currentTarget","dataset","onAction","onError","hard","cancelSend","seq","delMessagesAll","delMessagesList","flushMessage","msg","createMessage","publishDraft","updateMode","pickReply","userFrom","userName","menu","vSize","position","left","bounds","right","clickAt","x","top","bottom","y","style","ref","injectIntl","icon_mapping","ContactBadges","badges","color","icon","secondsToTime","seconds","fixedMin","min","sec","bytesToHumanSize","bytes","sizes","bucket","log2","pow","round","toFixed","shortenFileName","filename","maxLength","idToColorClass","light","fg","userId","abs","hash","stringToColorHash","updateFavicon","oldIcon","getElementById","head","getElementsByTagName","newIcon","createElement","rel","href","removeChild","appendChild","theCard","imageUrl","imageMimeType","note","card","trim","mimeType","matches","exec","photo","substring","indexOf","arrayEqual","isArray","sort","l","sanitizeUrl","url","allowedSchemes","schemes","RegExp","sanitizeImageUrl","sanitizedUrl","deliveryMarker","received","cancelablePromise","hasCanceled","isCanceled","cancel","LetterTile","avatar","isGroup","iconColor","letter","deleted","authorizeURL","alt","src","UnreadBadge","LazyImage","state","padding","whenDone","setState","componentDidUpdate","prevProps","FileProgress","width","progress","onCancel","UploadingImage","onCancelUpload","SUPPORTED_IMAGE_FORMATS","MIME_EXTENSIONS","makeImageUrl","startsWith","fitImageSize","height","maxWidth","maxHeight","forceSquare","scale","dstWidth","dstHeight","srcWidth","srcHeight","xoffset","yoffset","fileNameForMime","fname","mime","idx","ext","at","lastIndexOf","imageScaled","fileOrBlob","maxSize","img","Image","crossOrigin","onload","URL","revokeObjectURL","dim","canvas","ctx","getContext","imageSmoothingEnabled","drawImage","blob","toBlob","clearRect","createObjectURL","blobToBase64","reader","FileReader","evt","bits","split","readAsDataURL","drafty_form","drafty_attachment","drafty_image","handleImageData","el","attr","viewportWidth","minWidth","minHeight","Drafty","max","onImagePreview","loading","fullFormatter","stack","quoteFormatter","onFormButtonClick","inner","child","onQuoteClick","concat","previewFormatter","inlineImageAttr","quoteImage","bin","buf","ArrayBuffer","Blob","base64ToBlob","statusText","scaled","b64","replyFormatter","loadedPromise","Contact","handleContextClick","onSelected","showContextMenu","pageY","pageX","online","icon_badges","preview","isVerified","isStaff","isDangerous","acs","showMode","getMode","isMuted","isJoiner","marker","titleClass","showCheckmark","selected","showOnline","isChannel","unread","small","comment","ContactAction","action","badge_you","badge_owner","ContactList","topicSelected","contactNodes","contactsCount","contacts","filterFunc","filterOn","private","toLowerCase","public","myUserId","isOwner","forwarded","deliveryStatus","latestMessage","_status","msgStatus","showUnread","connected","onTopicSelected","trusted","verified","staff","danger","_deleted","noScroll","dangerouslySetInnerHTML","__html","emptyListMessage","SearchContacts","edited","search","handleSearchChange","handleSearch","handleClear","handleKeyDown","onSearchContacts","placeholder","onChange","onKeyDown","required","autoFocus","ForwardDialog","handleClose","handleSearchContacts","handleContactSelected","onInitFind","uid","searchResults","isWriter","desription","search_placeholder","not_found_placeholder","LoadSpinner","large","centered","show","AvatarUpload","source","handleFileReceived","files","onImageUpdated","randId","random","readOnly","accept","htmlFor","uploading","badge_verified","badge_staff","badge_danger","BadgeList","trustedBadges","CheckBox","handleChange","checked","MenuCancel","ErrorPanel","level","onClearError","text","whiteSpace","actionText","Chip","handleCancel","invalid","noAvatar","ChipInput","deriveStateFromProps","input","focused","handleTextInput","removeChipAt","handleChipCancel","handleFocusGained","handleFocusLost","chips","prompt","sortedChips","sortChips","staticMembers","chipIndex","indexChips","prevState","keep","normal","removed","onChipRemoved","onFocusLost","onEnter","avatarDisabled","tabIndex","onFocus","onBlur","no_contacts","contacts_not_found_short","GroupManager","members","indexMembers","keepInitialMembers","requiredMember","contactFilter","noContactsMessage","selectedContacts","handleMemberRemoved","handleContactFilter","handleSubmit","m","delta","present","keepInitial","stat","sel","splice","added","onSubmit","doContactFiltering","joiner","writer","preser","approver","sharer","deleter","owner","PermissionsEditor","names","skip","compare","userTitle","userAvatar","modeTitle","compareTitle","Cropper","panX","panY","originX","originY","zoom","minZoom","maxZoom","overlay","cutout","boundingBox","imageWidth","imageHeight","mouseX","mouseY","prevDistance","cutoutRect","bBoxRect","initScaling","onZoom","handleZoom","mouseDown","mouseUp","mouseMove","mouseTouch","positionAll","translate","passive","getBoundingClientRect","currPan","nextDiff","imgRect","imgLeft","imgRight","coLeft","coRight","imgTop","imgBottom","coTop","coBottom","touches","dX","dY","checkBound","touch0","touch1","distance","sqrt","t3d","orig","transform","transformOrigin","onLoad","step","AvatarCrop","objURL","VisiblePassword","handleVisibility","handeTextChange","handleEditingFinished","onFinished","activeElement","autoComplete","InPlaceEdit","active","initialValue","handleStartEditing","handlePasswordFinished","focus","checkValidity","spanText","spanClass","multiline","element","rows","TagManager","tags","tagInput","activated","handleTagInput","handleAddTag","handleRemoveTag","nextProps","last","tag","maxTagCount","getServerLimit","onTagsChanged","minTagLength","maxTagLength","add_tags_prompt","TopicDescEdit","getAccessMode","isMe","fullName","description","newAvatar","newAvatarMime","previousOnTags","tnNewTags","handleFullNameUpdate","handleImageUpdated","handleAvatarCropped","handlePrivateUpdate","handleDescriptionUpdate","uploadAvatar","handleAvatarCropCancel","handleTagsUpdated","onTagsUpdated","onUpdateTopicDesc","desc","readyToUpload","uploader","getLargeFileHelper","upload","finally","du","onUpdateTags","editable","full_name_placeholder","group_name_placeholder","private_placeholder","title_tag_manager","TopicCommon","credentials","getCredentials","addCredActive","addCredInvalid","newCred","previousTagsUpdated","tnCredsUpdated","handleCredChange","handleCredKeyDown","handleCredEntered","onCredsUpdated","getType","creds","asPhone","asEmail","onCredAdd","onTopicTagsUpdateRequest","cred","meth","done","onCredConfirm","onCredDelete","delete_messages","leave_chat","leave_chat_warning","block_contact","block_contact_warning","report_chat","report_chat_warning","other_user","TopicSecurity","handleDeleteTopic","handleDeleteMessages","handleLeave","handleBlock","handleReport","onDeleteTopic","onDeleteMessages","onLeaveTopic","onBlockTopic","onReportTopic","groupTopic","onLaunchPermissionsEditor","access","anon","modeGiven2","general","security","crop","perm_want","perm_given","perm_auth","perm_anon","perm_user","_clip","InfoView","admin","muted","address","selectedContact","modeGiven","modeWant","modeWant2","contactList","previousMetaDesc","previousSubsUpdated","resetSubs","resetDesc","resetTags","onMetaDesc","onSubsUpdated","handleImageChanged","handleMuted","handleUnarchive","handlePermissionsChanged","handleLaunchPermissionsEditor","handleShowAddMembers","handleMemberUpdateRequest","handleMemberSelected","handleContextMenu","handleBackNavigate","newState","user2","subscriber","getGiven","getWant","subscribers","sub","defacs","getDefaultAccess","isAdmin","isSharer","isDeleter","archived","isArchived","isGroupType","isChannelType","chan","getMeta","startMetaQuery","withTags","build","subs","onTopicDescUpdate","ignored","onChangePermissions","ignored2","onTopicUnArchive","which","perm","onTopicDescUpdateRequest","userPermissionsEdited","toEdit","toCompare","toSkip","titleEdit","titleCompare","userPermissionsTitle","userPermissionsAvatar","editedPermissions","immutablePermissions","editedPermissionsTitle","immutablePermissionsTitle","editedPermissionsSkipped","onNavigate","onMemberUpdateRequest","panel","menuItems","view","shift","panelTitle","displayMobile","errorLevel","errorText","searchableContacts","no_members","Attachment","downloader","downloadFile","mimetype","download","loaded","helperFunc","isUrlRelative","downloadUrl","downloadWidget","message_sending","message_sending_failed","ReceivedMarker","timestamp","toLocaleTimeString","locale","timeStyle","BaseChatMessage","onProgress","handleProgress","handleImagePreview","handleFormButtonClick","handleCancelUpload","handleQuoteClick","formatterContext","resp","act","onFormResponse","MESSAGE_STATUS_FAILED","userIsWriter","MESSAGE_STATUS_DEL_RANGE","ratio","replyToSeq","sideClass","sequence","bubbleClass","fullDisplay","attachments","att","tree","innerRef","IntlChatMessage","MILLIS_PER_BAR","AudioRecorder","enabled","audioRecord","recording","paused","duration","visualize","initMediaRecording","handleResume","handlePause","handleDelete","handleDone","durationMillis","startedOn","viewBuffer","canvasRef","canvasContext","lineCap","stream","mediaRecorder","audioContext","sampleRate","audioInput","analyser","audioChunks","mediaDevices","getUserMedia","audio","video","pcmData","frequencyBinCount","viewLength","viewDuration","fillStyle","lineWidth","strokeStyle","prevBarCount","drawFrame","requestAnimationFrame","pause","getByteTimeDomainData","volume","amplitude","barCount","dx","fillRect","beginPath","moveTo","lineTo","stroke","resume","stop","MediaRecorder","AudioContext","createMediaStreamSource","createAnalyser","fftSize","connect","onstop","onDeleted","ondataavailable","start","resumeClass","messaging_disabled","type_new_message","add_image_caption","file_attachment_too_large","cannot_initiate_upload","SendMessage","quote","audioRec","audioEnabled","keypressTimestamp","getTime","handlePasteEvent","handleAttachImage","handleAttachFile","handleAttachAudio","handleSend","handleKeyPress","handleMessageTyping","formatReply","messageEditArea","disabled","onImageSuccess","onAttachmentSuccess","clipboardData","originalEvent","kind","file","getAsFile","filePasted","onAttachImage","onAttachFile","acceptBlank","noInput","onSendMessage","shiftKey","onKeyPress","messagePrompt","onCancelReply","attachImage","click","attachFile","display","DocPreview","handleSendDoc","caption","onClose","mimeToIcon","default","iconFromMime","GroupSubs","usersOnline","totalCount","countToShow","some","overflow","ImagePreview","handleSendImage","assignWidth","node","maxlength","Invitation","handleButtonAction","LogoView","serverVersion","serverAddress","MetaMessage","date","HashNavigation","parts","path","part","decodeURIComponent","location","parsed","parseUrlHash","composeUrlHash","sidepanel","infopanel","online_now","last_seen","not_found","invalid_content","isUnconfirmed","ex","getExcessive","isPeerRestricted","getMissing","MessagesView","getDerivedStateFromProps","leave","sendMessage","retrySend","sendImageAttachment","sendFileAttachment","sendKeyPress","handleScrollReference","handleScrollEvent","handleDescChange","handleSubsUpdated","handleMessageUpdate","handleAllMessagesReceived","handleInfoReceipt","handleImagePostview","handleClosePreview","handleFormResponse","handleShowMessageContextMenu","handleNewChatAcceptance","handleEnablePeer","postReadNotification","clearNotificationQueue","goToLatestMessage","handlePickReply","handleCancelReply","chatMessageRefs","getOrCreateMessageRef","readNotificationQueue","readNotificationTimer","seqId","messagesScroller","messageCount","scrollPosition","scrollTop","scrollHeight","offsetHeight","onData","onAllMessagesReceived","onInfo","onPres","applicationVisible","ready","_new","nextState","forwardMessage","docPreview","imagePreview","imagePostview","typingIndicator","fetchingMessages","showGoToLastButton","onlineSubs","peer","p2pPeerDesc","peerMessagingDisabled","latestClearId","maxClearId","isReader","readingBlocked","unconformed","unconfirmed","isSubscribed","newTopic","newTopicParams","_topicName","getQuery","withLaterDesc","withLaterSub","withLaterData","withLaterDel","setQuery","onNewTopicCreated","queuedMessages","pub","_sending","navigateTo","setUrlTopic","blankState","oldTopicName","isTopicCached","oldTopic","pos","msgHasMoreMessages","getMessagesPage","setInterval","clearInterval","sendAt","noteRead","setMilliseconds","getMilliseconds","clearTimeout","keyPressTimer","isNewMessage","what","forceUpdate","attachJSON","parse","searchParams","messageSpecificMenuItems","onNewChat","noteKeyPress","uploadCompletionPromise","sanitizeEntities","maxInbandAttachmentSize","urlPromise","fileToBase64","maxExternAttachmentSize","limit","insertImage","_tempPreview","appendLineBreak","found","findMessage","_cancelled","senderName","init","isValid","replyContent","wrapInto","onCancelForwardMessage","scrollIntoView","behavior","classList","hideSelf","component2","messageNodes","previousFrom","chatBoxClass","dateFmt","Intl","DateTimeFormat","prev","nextFrom","thisFrom","isReply","userDesc","parseInt","isNaN","ts","toDateString","format","hi","isChan","_uploader","lastSeen","cont","getMeTopic","getContact","seen","userLanguage","language","getFullYear","getMonth","getDate","hour12","hour","minute","toLocaleDateString","month","day","year","shortDateFormat","when","onHideMessagesView","ButtonBack","onBack","MenuContacts","onNewTopic","onSettings","MenuStart","onSignUp","SideNavbar","AccountSettingsView","me","archived_contacts_title","ContactsView","handleAction","unreadThreads","archivedCount","chatList","blocked","arch","touched","action_ignored","onShowArchive","LocalStorageUtil","localStorage","setItem","getItem","oldVal","getObject","setObject","removeItem","CreateAccountView","login","password","email","imageDataUrl","errorCleared","saveToken","handleLoginChange","handlePasswordChange","handleEmailChange","handleFnChange","handleToggleSaveToken","onCreateAccount","submitClasses","login_prompt","password_prompt","onImageChanged","full_name_prompt","email_prompt","stay_logged_in","EditAccountView","handleCheckboxClick","onToggleMessageSounds","onTogglePushNotifications","onToggleIncognitoMode","messageSounds","desktopAlertsEnabled","desktopAlerts","incognitoMode","delete_account","delete_account_warning","AccSecurityView","blockedCount","showPermissionEditorFor","handlePasswordUpdate","handleHidePermissionsEditor","handleDeleteAccount","pwd","onUpdatePassword","onUpdateAccountDesc","onDeleteAccount","password_unchanged","onLogout","onShowBlocked","AccSupportView","LoginView","hostName","persist","onPersistenceChange","onLogin","autoCorrect","autoCapitalize","invalid_id","NewTopicById","groupId","prefix","NewTopicGroup","handleFieldEdit","handleTagsChanged","handleChannelToggle","search_for_contacts","search_no_results","NewTopicView","tabSelected","searchQuery","handleTabClick","handleSearchResultSelected","handleNewGroupSubmit","handleGroupByID","addUrlParam","removeUrlParam","onCreateTopic","dataUrl","priv","no_contacts_placeholder","PasswordResetView","sent","scheme","onReset","onRequest","HostSelector","changed","handleHostNameChange","onServerAddressChange","hostOptions","list","SettingsView","transport","handleTransportSelected","handleServerAddressChange","onUpdate","def","ws","lp","transportOptions","phone","ValidationView","credCode","credMethod","numbers_only","settings","edit","notif","support","newtpk","reset","SidepanelView","handleNewTopic","errorAction","errorActionText","loadSpinnerVisible","loginDisabled","onLoginRequest","onGlobalSettings","onUpdateAccountTags","onValidateCredentials","onPasswordResetRequest","onResetPassword","detectServerAddress","host","protocol","hostname","port","isSecureConnection","POP_SOUND","Audio","reconnect_countdown","reconnect_now","push_init_failed","invalid_security_token","no_connection","code_doesnot_match","menu_item_info","TinodeWeb","getBlankState","handleResize","handleHashRoute","handleOnline","checkForAppUpdate","handleVisibilityEvent","handleError","handleLoginRequest","handlePersistenceChange","handleConnected","handleAutoreconnectIteration","doLogin","handleCredentialsRequest","handleLoginSuccessful","handleDisconnect","tnMeMetaDesc","tnMeContactUpdate","tnMeSubsUpdated","resetContactList","tnData","tnInitFind","tnFndSubsUpdated","handleTopicSelected","handleHideMessagesView","handleSendMessage","handleNewChatInvitation","handleNewAccount","handleNewAccountRequest","handleUpdatePasswordRequest","handleUpdateAccountTagsRequest","handleToggleIncognitoMode","handleSettings","handleGlobalSettings","handleShowArchive","handleShowBlocked","handleToggleMessageSounds","handleCredAdd","handleCredDelete","handleCredConfirm","initFCMessaging","toggleFCMToken","handlePushMessage","handleSidepanelCancel","handleStartTopicRequest","handleNewTopicCreated","handleTopicUpdateRequest","handleChangePermissions","handleTagsUpdateRequest","handleLogout","handleDeleteTopicRequest","handleDeleteMessagesRequest","handleLeaveUnsubRequest","handleBlockTopicRequest","handleReportTopic","handleShowContextMenu","defaultTopicContextMenu","handleHideContextMenu","handleShowAlert","handleShowInfoView","handleValidateCredentialsRequest","handlePasswordResetRequest","handleResetPassword","handleContextMenuAction","handleShowForwardDialog","handleHideForwardDialog","sendMessageToTopic","autoLogin","messageSoundsOff","FIREBASE_INIT","firebaseToken","hidden","sidePanelSelected","sidePanelTitle","sidePanelAvatar","myTrustedBadges","liveConnection","topicSelectedOnline","topicSelectedAcs","innerWidth","infoPanel","mobilePanel","contextMenuVisible","contextMenuBounds","contextMenuClickAt","contextMenuParams","contextMenuItems","forwardDialogVisible","alertVisible","alertParams","requestedTopic","documentElement","clientWidth","viewportHeight","clientHeight","tnSetup","onConnect","onDisconnect","onAutoreconnectIteration","setDeviceToken","parsedNav","expires","setAuthToken","readTimer","readTimerCallback","persistentCache","onSetupCompleted","secure","setHumanLanguage","enableLogging","oobNotification","updateObject","fcm","firebaseGetMessaging","firebaseInitApp","reg","requestFCMToken","firebaseOnMessage","sw","firebaseGetToken","messagingVapidKey","mobile","onupdatefound","installingWorker","installing","onstatechange","controller","getTopicAccessMode","newTopicTabSelected","tab","reconnectCountdown","reconnect","isConnected","initStorage","clearStorage","getServerInfo","ver","prom","timeLeft","isAuthenticated","getAuthToken","loginBasic","loginToken","goToTopic","onContactUpdate","getCurrentUserID","withDesc","withCred","disconnect","urlHash","setUrlSidePanel","isPresencer","play","unused","foundContacts","merged","updated","past","prepareSearchableContacts","receivedTimer","noteRecv","fnd","getFndTopic","withSub","s","setMeta","isTopicOnline","isP2PType","am","updateWant","login_","password_","public_","cred_","tags_","createAccountBasic","on","firebaseDelToken","delCredential","basicNavigator","infoNavigator","setUrlInfoPanel","newGroupTopicName","oldName","newName","updateAccountBasic","getCurrentLogin","updateGiven","cleared","Promose","delCurrentUser","publish","header","msgPreview","self_blocked","subscribed","keepForwardedMessage","confirmText","rejectText","invite","requestResetAuthSecret","newPassword","base64ReEncode","myUserName","onAddMember","handleManageGroupMembers","hl","languages","baseLanguage","htmlLang","allMessages","langauge","setAttribute","IntlProvider","textComponent"],"sourceRoot":""}
\ No newline at end of file