From dc421f5d857b387a77ec30804a8ad73e3dbaa26f Mon Sep 17 00:00:00 2001 From: ericgaspar Date: Sun, 14 Nov 2021 18:31:05 +0100 Subject: [PATCH] Delete converse.js --- sources/dist/converse.js | 83469 ------------------------------------- 1 file changed, 83469 deletions(-) delete mode 100644 sources/dist/converse.js diff --git a/sources/dist/converse.js b/sources/dist/converse.js deleted file mode 100644 index aa27b88..0000000 --- a/sources/dist/converse.js +++ /dev/null @@ -1,83469 +0,0 @@ -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 7706: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -const atob = __webpack_require__(171); - -const btoa = __webpack_require__(8713); - -module.exports = { - atob, - btoa -}; - -/***/ }), - -/***/ 171: -/***/ ((module) => { - -"use strict"; - -/** - * Implementation of atob() according to the HTML and Infra specs, except that - * instead of throwing INVALID_CHARACTER_ERR we return null. - */ - -function atob(data) { - // Web IDL requires DOMStrings to just be converted using ECMAScript - // ToString, which in our case amounts to using a template literal. - data = `${data}`; // "Remove all ASCII whitespace from data." - - data = data.replace(/[ \t\n\f\r]/g, ""); // "If data's length divides by 4 leaving no remainder, then: if data ends - // with one or two U+003D (=) code points, then remove them from data." - - if (data.length % 4 === 0) { - data = data.replace(/==?$/, ""); - } // "If data's length divides by 4 leaving a remainder of 1, then return - // failure." - // - // "If data contains a code point that is not one of - // - // U+002B (+) - // U+002F (/) - // ASCII alphanumeric - // - // then return failure." - - - if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) { - return null; - } // "Let output be an empty byte sequence." - - - let output = ""; // "Let buffer be an empty buffer that can have bits appended to it." - // - // We append bits via left-shift and or. accumulatedBits is used to track - // when we've gotten to 24 bits. - - let buffer = 0; - let accumulatedBits = 0; // "Let position be a position variable for data, initially pointing at the - // start of data." - // - // "While position does not point past the end of data:" - - for (let i = 0; i < data.length; i++) { - // "Find the code point pointed to by position in the second column of - // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in - // the first cell of the same row. - // - // "Append to buffer the six bits corresponding to n, most significant bit - // first." - // - // atobLookup() implements the table from RFC 4648. - buffer <<= 6; - buffer |= atobLookup(data[i]); - accumulatedBits += 6; // "If buffer has accumulated 24 bits, interpret them as three 8-bit - // big-endian numbers. Append three bytes with values equal to those - // numbers to output, in the same order, and then empty buffer." - - if (accumulatedBits === 24) { - output += String.fromCharCode((buffer & 0xff0000) >> 16); - output += String.fromCharCode((buffer & 0xff00) >> 8); - output += String.fromCharCode(buffer & 0xff); - buffer = accumulatedBits = 0; - } // "Advance position by 1." - - } // "If buffer is not empty, it contains either 12 or 18 bits. If it contains - // 12 bits, then discard the last four and interpret the remaining eight as - // an 8-bit big-endian number. If it contains 18 bits, then discard the last - // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append - // the one or two bytes with values equal to those one or two numbers to - // output, in the same order." - - - if (accumulatedBits === 12) { - buffer >>= 4; - output += String.fromCharCode(buffer); - } else if (accumulatedBits === 18) { - buffer >>= 2; - output += String.fromCharCode((buffer & 0xff00) >> 8); - output += String.fromCharCode(buffer & 0xff); - } // "Return output." - - - return output; -} -/** - * A lookup table for atob(), which converts an ASCII character to the - * corresponding six-bit number. - */ - - -const keystr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -function atobLookup(chr) { - const index = keystr.indexOf(chr); // Throw exception if character is not in the lookup string; should not be hit in tests - - return index < 0 ? undefined : index; -} - -module.exports = atob; - -/***/ }), - -/***/ 8713: -/***/ ((module) => { - -"use strict"; - -/** - * btoa() as defined by the HTML and Infra specs, which mostly just references - * RFC 4648. - */ - -function btoa(s) { - let i; // String conversion as required by Web IDL. - - s = `${s}`; // "The btoa() method must throw an "InvalidCharacterError" DOMException if - // data contains any character whose code point is greater than U+00FF." - - for (i = 0; i < s.length; i++) { - if (s.charCodeAt(i) > 255) { - return null; - } - } - - let out = ""; - - for (i = 0; i < s.length; i += 3) { - const groupsOfSix = [undefined, undefined, undefined, undefined]; - groupsOfSix[0] = s.charCodeAt(i) >> 2; - groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; - - if (s.length > i + 1) { - groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; - groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; - } - - if (s.length > i + 2) { - groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; - groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; - } - - for (let j = 0; j < groupsOfSix.length; j++) { - if (typeof groupsOfSix[j] === "undefined") { - out += "="; - } else { - out += btoaLookup(groupsOfSix[j]); - } - } - } - - return out; -} -/** - * Lookup table for btoa(), which converts a six-bit number into the - * corresponding ASCII character. - */ - - -const keystr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -function btoaLookup(index) { - if (index >= 0 && index < 64) { - return keystr[index]; - } // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. - - - return undefined; -} - -module.exports = btoa; - -/***/ }), - -/***/ 8826: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = { - "default": __webpack_require__(5820), - __esModule: true -}; - -/***/ }), - -/***/ 1060: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = { - "default": __webpack_require__(3248), - __esModule: true -}; - -/***/ }), - -/***/ 3415: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = { - "default": __webpack_require__(9830), - __esModule: true -}; - -/***/ }), - -/***/ 8681: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - - -exports.__esModule = true; - -var _promise = __webpack_require__(3415); - -var _promise2 = _interopRequireDefault(_promise); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} - -exports.default = function (fn) { - return function () { - var gen = fn.apply(this, arguments); - return new _promise2.default(function (resolve, reject) { - function step(key, arg) { - try { - var info = gen[key](arg); - var value = info.value; - } catch (error) { - reject(error); - return; - } - - if (info.done) { - resolve(value); - } else { - return _promise2.default.resolve(value).then(function (value) { - step("next", value); - }, function (err) { - step("throw", err); - }); - } - } - - return step("next"); - }); - }; -}; - -/***/ }), - -/***/ 8584: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -"use strict"; - - -exports.__esModule = true; - -var _defineProperty = __webpack_require__(8826); - -var _defineProperty2 = _interopRequireDefault(_defineProperty); - -function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; -} - -exports.default = function (obj, key, value) { - if (key in obj) { - (0, _defineProperty2.default)(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; -}; - -/***/ }), - -/***/ 7034: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -// This method of obtaining a reference to the global object needs to be -// kept identical to the way it is obtained in runtime.js -var g = function () { - return this; -}() || Function("return this")(); // Use `getOwnPropertyNames` because not all browsers support calling -// `hasOwnProperty` on the global `self` object in a worker. See #183. - - -var hadRuntime = g.regeneratorRuntime && Object.getOwnPropertyNames(g).indexOf("regeneratorRuntime") >= 0; // Save the old regeneratorRuntime in case it needs to be restored later. - -var oldRuntime = hadRuntime && g.regeneratorRuntime; // Force reevalutation of runtime.js. - -g.regeneratorRuntime = undefined; -module.exports = __webpack_require__(4267); - -if (hadRuntime) { - // Restore the original runtime. - g.regeneratorRuntime = oldRuntime; -} else { - // Remove the global property added by runtime.js. - try { - delete g.regeneratorRuntime; - } catch (e) { - g.regeneratorRuntime = undefined; - } -} - -/***/ }), - -/***/ 4267: -/***/ ((module) => { - -/** - * Copyright (c) 2014-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -!function (global) { - "use strict"; - - var Op = Object.prototype; - var hasOwn = Op.hasOwnProperty; - var undefined; // More compressible than void 0. - - var $Symbol = typeof Symbol === "function" ? Symbol : {}; - var iteratorSymbol = $Symbol.iterator || "@@iterator"; - var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator"; - var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag"; - var inModule = "object" === "object"; - var runtime = global.regeneratorRuntime; - - if (runtime) { - if (inModule) { - // If regeneratorRuntime is defined globally and we're in a module, - // make the exports object identical to regeneratorRuntime. - module.exports = runtime; - } // Don't bother evaluating the rest of this file if the runtime was - // already defined globally. - - - return; - } // Define the runtime globally (as expected by generated code) as either - // module.exports (if we're in a module) or a new, empty object. - - - runtime = global.regeneratorRuntime = inModule ? module.exports : {}; - - function wrap(innerFn, outerFn, self, tryLocsList) { - // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator. - var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator; - var generator = Object.create(protoGenerator.prototype); - var context = new Context(tryLocsList || []); // The ._invoke method unifies the implementations of the .next, - // .throw, and .return methods. - - generator._invoke = makeInvokeMethod(innerFn, self, context); - return generator; - } - - runtime.wrap = wrap; // Try/catch helper to minimize deoptimizations. Returns a completion - // record like context.tryEntries[i].completion. This interface could - // have been (and was previously) designed to take a closure to be - // invoked without arguments, but in all the cases we care about we - // already have an existing method we want to call, so there's no need - // to create a new function object. We can even get away with assuming - // the method takes exactly one argument, since that happens to be true - // in every case, so we don't have to touch the arguments object. The - // only additional allocation required is the completion record, which - // has a stable shape and so hopefully should be cheap to allocate. - - function tryCatch(fn, obj, arg) { - try { - return { - type: "normal", - arg: fn.call(obj, arg) - }; - } catch (err) { - return { - type: "throw", - arg: err - }; - } - } - - var GenStateSuspendedStart = "suspendedStart"; - var GenStateSuspendedYield = "suspendedYield"; - var GenStateExecuting = "executing"; - var GenStateCompleted = "completed"; // Returning this object from the innerFn has the same effect as - // breaking out of the dispatch switch statement. - - var ContinueSentinel = {}; // Dummy constructor functions that we use as the .constructor and - // .constructor.prototype properties for functions that return Generator - // objects. For full spec compliance, you may wish to configure your - // minifier not to mangle the names of these two functions. - - function Generator() {} - - function GeneratorFunction() {} - - function GeneratorFunctionPrototype() {} // This is a polyfill for %IteratorPrototype% for environments that - // don't natively support it. - - - var IteratorPrototype = {}; - - IteratorPrototype[iteratorSymbol] = function () { - return this; - }; - - var getProto = Object.getPrototypeOf; - var NativeIteratorPrototype = getProto && getProto(getProto(values([]))); - - if (NativeIteratorPrototype && NativeIteratorPrototype !== Op && hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) { - // This environment has a native %IteratorPrototype%; use it instead - // of the polyfill. - IteratorPrototype = NativeIteratorPrototype; - } - - var Gp = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(IteratorPrototype); - GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype; - GeneratorFunctionPrototype.constructor = GeneratorFunction; - GeneratorFunctionPrototype[toStringTagSymbol] = GeneratorFunction.displayName = "GeneratorFunction"; // Helper for defining the .next, .throw, and .return methods of the - // Iterator interface in terms of a single ._invoke method. - - function defineIteratorMethods(prototype) { - ["next", "throw", "return"].forEach(function (method) { - prototype[method] = function (arg) { - return this._invoke(method, arg); - }; - }); - } - - runtime.isGeneratorFunction = function (genFun) { - var ctor = typeof genFun === "function" && genFun.constructor; - return ctor ? ctor === GeneratorFunction || // For the native GeneratorFunction constructor, the best we can - // do is to check its .name property. - (ctor.displayName || ctor.name) === "GeneratorFunction" : false; - }; - - runtime.mark = function (genFun) { - if (Object.setPrototypeOf) { - Object.setPrototypeOf(genFun, GeneratorFunctionPrototype); - } else { - genFun.__proto__ = GeneratorFunctionPrototype; - - if (!(toStringTagSymbol in genFun)) { - genFun[toStringTagSymbol] = "GeneratorFunction"; - } - } - - genFun.prototype = Object.create(Gp); - return genFun; - }; // Within the body of any async function, `await x` is transformed to - // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test - // `hasOwn.call(value, "__await")` to determine if the yielded value is - // meant to be awaited. - - - runtime.awrap = function (arg) { - return { - __await: arg - }; - }; - - function AsyncIterator(generator) { - function invoke(method, arg, resolve, reject) { - var record = tryCatch(generator[method], generator, arg); - - if (record.type === "throw") { - reject(record.arg); - } else { - var result = record.arg; - var value = result.value; - - if (value && typeof value === "object" && hasOwn.call(value, "__await")) { - return Promise.resolve(value.__await).then(function (value) { - invoke("next", value, resolve, reject); - }, function (err) { - invoke("throw", err, resolve, reject); - }); - } - - return Promise.resolve(value).then(function (unwrapped) { - // When a yielded Promise is resolved, its final value becomes - // the .value of the Promise<{value,done}> result for the - // current iteration. If the Promise is rejected, however, the - // result for this iteration will be rejected with the same - // reason. Note that rejections of yielded Promises are not - // thrown back into the generator function, as is the case - // when an awaited Promise is rejected. This difference in - // behavior between yield and await is important, because it - // allows the consumer to decide what to do with the yielded - // rejection (swallow it and continue, manually .throw it back - // into the generator, abandon iteration, whatever). With - // await, by contrast, there is no opportunity to examine the - // rejection reason outside the generator function, so the - // only option is to throw it from the await expression, and - // let the generator function handle the exception. - result.value = unwrapped; - resolve(result); - }, reject); - } - } - - var previousPromise; - - function enqueue(method, arg) { - function callInvokeWithMethodAndArg() { - return new Promise(function (resolve, reject) { - invoke(method, arg, resolve, reject); - }); - } - - return previousPromise = // If enqueue has been called before, then we want to wait until - // all previous Promises have been resolved before calling invoke, - // so that results are always delivered in the correct order. If - // enqueue has not been called before, then it is important to - // call invoke immediately, without waiting on a callback to fire, - // so that the async generator function has the opportunity to do - // any necessary setup in a predictable way. This predictability - // is why the Promise constructor synchronously invokes its - // executor callback, and why async functions synchronously - // execute code before the first await. Since we implement simple - // async functions in terms of async generators, it is especially - // important to get this right, even though it requires care. - previousPromise ? previousPromise.then(callInvokeWithMethodAndArg, // Avoid propagating failures to Promises returned by later - // invocations of the iterator. - callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); - } // Define the unified helper method that is used to implement .next, - // .throw, and .return (see defineIteratorMethods). - - - this._invoke = enqueue; - } - - defineIteratorMethods(AsyncIterator.prototype); - - AsyncIterator.prototype[asyncIteratorSymbol] = function () { - return this; - }; - - runtime.AsyncIterator = AsyncIterator; // Note that simple async functions are implemented on top of - // AsyncIterator objects; they just return a Promise for the value of - // the final result produced by the iterator. - - runtime.async = function (innerFn, outerFn, self, tryLocsList) { - var iter = new AsyncIterator(wrap(innerFn, outerFn, self, tryLocsList)); - return runtime.isGeneratorFunction(outerFn) ? iter // If outerFn is a generator, return the full iterator. - : iter.next().then(function (result) { - return result.done ? result.value : iter.next(); - }); - }; - - function makeInvokeMethod(innerFn, self, context) { - var state = GenStateSuspendedStart; - return function invoke(method, arg) { - if (state === GenStateExecuting) { - throw new Error("Generator is already running"); - } - - if (state === GenStateCompleted) { - if (method === "throw") { - throw arg; - } // Be forgiving, per 25.3.3.3.3 of the spec: - // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume - - - return doneResult(); - } - - context.method = method; - context.arg = arg; - - while (true) { - var delegate = context.delegate; - - if (delegate) { - var delegateResult = maybeInvokeDelegate(delegate, context); - - if (delegateResult) { - if (delegateResult === ContinueSentinel) continue; - return delegateResult; - } - } - - if (context.method === "next") { - // Setting context._sent for legacy support of Babel's - // function.sent implementation. - context.sent = context._sent = context.arg; - } else if (context.method === "throw") { - if (state === GenStateSuspendedStart) { - state = GenStateCompleted; - throw context.arg; - } - - context.dispatchException(context.arg); - } else if (context.method === "return") { - context.abrupt("return", context.arg); - } - - state = GenStateExecuting; - var record = tryCatch(innerFn, self, context); - - if (record.type === "normal") { - // If an exception is thrown from innerFn, we leave state === - // GenStateExecuting and loop back for another invocation. - state = context.done ? GenStateCompleted : GenStateSuspendedYield; - - if (record.arg === ContinueSentinel) { - continue; - } - - return { - value: record.arg, - done: context.done - }; - } else if (record.type === "throw") { - state = GenStateCompleted; // Dispatch the exception by looping back around to the - // context.dispatchException(context.arg) call above. - - context.method = "throw"; - context.arg = record.arg; - } - } - }; - } // Call delegate.iterator[context.method](context.arg) and handle the - // result, either by returning a { value, done } result from the - // delegate iterator, or by modifying context.method and context.arg, - // setting context.delegate to null, and returning the ContinueSentinel. - - - function maybeInvokeDelegate(delegate, context) { - var method = delegate.iterator[context.method]; - - if (method === undefined) { - // A .throw or .return when the delegate iterator has no .throw - // method always terminates the yield* loop. - context.delegate = null; - - if (context.method === "throw") { - if (delegate.iterator.return) { - // If the delegate iterator has a return method, give it a - // chance to clean up. - context.method = "return"; - context.arg = undefined; - maybeInvokeDelegate(delegate, context); - - if (context.method === "throw") { - // If maybeInvokeDelegate(context) changed context.method from - // "return" to "throw", let that override the TypeError below. - return ContinueSentinel; - } - } - - context.method = "throw"; - context.arg = new TypeError("The iterator does not provide a 'throw' method"); - } - - return ContinueSentinel; - } - - var record = tryCatch(method, delegate.iterator, context.arg); - - if (record.type === "throw") { - context.method = "throw"; - context.arg = record.arg; - context.delegate = null; - return ContinueSentinel; - } - - var info = record.arg; - - if (!info) { - context.method = "throw"; - context.arg = new TypeError("iterator result is not an object"); - context.delegate = null; - return ContinueSentinel; - } - - if (info.done) { - // Assign the result of the finished delegate to the temporary - // variable specified by delegate.resultName (see delegateYield). - context[delegate.resultName] = info.value; // Resume execution at the desired location (see delegateYield). - - context.next = delegate.nextLoc; // If context.method was "throw" but the delegate handled the - // exception, let the outer generator proceed normally. If - // context.method was "next", forget context.arg since it has been - // "consumed" by the delegate iterator. If context.method was - // "return", allow the original .return call to continue in the - // outer generator. - - if (context.method !== "return") { - context.method = "next"; - context.arg = undefined; - } - } else { - // Re-yield the result returned by the delegate method. - return info; - } // The delegate iterator is finished, so forget it and continue with - // the outer generator. - - - context.delegate = null; - return ContinueSentinel; - } // Define Generator.prototype.{next,throw,return} in terms of the - // unified ._invoke helper method. - - - defineIteratorMethods(Gp); - Gp[toStringTagSymbol] = "Generator"; // A Generator should always return itself as the iterator object when the - // @@iterator function is called on it. Some browsers' implementations of the - // iterator prototype chain incorrectly implement this, causing the Generator - // object to not be returned from this call. This ensures that doesn't happen. - // See https://github.com/facebook/regenerator/issues/274 for more details. - - Gp[iteratorSymbol] = function () { - return this; - }; - - Gp.toString = function () { - return "[object Generator]"; - }; - - function pushTryEntry(locs) { - var entry = { - tryLoc: locs[0] - }; - - if (1 in locs) { - entry.catchLoc = locs[1]; - } - - if (2 in locs) { - entry.finallyLoc = locs[2]; - entry.afterLoc = locs[3]; - } - - this.tryEntries.push(entry); - } - - function resetTryEntry(entry) { - var record = entry.completion || {}; - record.type = "normal"; - delete record.arg; - entry.completion = record; - } - - function Context(tryLocsList) { - // The root entry object (effectively a try statement without a catch - // or a finally block) gives us a place to store values thrown from - // locations where there is no enclosing try statement. - this.tryEntries = [{ - tryLoc: "root" - }]; - tryLocsList.forEach(pushTryEntry, this); - this.reset(true); - } - - runtime.keys = function (object) { - var keys = []; - - for (var key in object) { - keys.push(key); - } - - keys.reverse(); // Rather than returning an object with a next method, we keep - // things simple and return the next function itself. - - return function next() { - while (keys.length) { - var key = keys.pop(); - - if (key in object) { - next.value = key; - next.done = false; - return next; - } - } // To avoid creating an additional object, we just hang the .value - // and .done properties off the next function object itself. This - // also ensures that the minifier will not anonymize the function. - - - next.done = true; - return next; - }; - }; - - function values(iterable) { - if (iterable) { - var iteratorMethod = iterable[iteratorSymbol]; - - if (iteratorMethod) { - return iteratorMethod.call(iterable); - } - - if (typeof iterable.next === "function") { - return iterable; - } - - if (!isNaN(iterable.length)) { - var i = -1, - next = function next() { - while (++i < iterable.length) { - if (hasOwn.call(iterable, i)) { - next.value = iterable[i]; - next.done = false; - return next; - } - } - - next.value = undefined; - next.done = true; - return next; - }; - - return next.next = next; - } - } // Return an iterator with no values. - - - return { - next: doneResult - }; - } - - runtime.values = values; - - function doneResult() { - return { - value: undefined, - done: true - }; - } - - Context.prototype = { - constructor: Context, - reset: function (skipTempReset) { - this.prev = 0; - this.next = 0; // Resetting context._sent for legacy support of Babel's - // function.sent implementation. - - this.sent = this._sent = undefined; - this.done = false; - this.delegate = null; - this.method = "next"; - this.arg = undefined; - this.tryEntries.forEach(resetTryEntry); - - if (!skipTempReset) { - for (var name in this) { - // Not sure about the optimal order of these conditions: - if (name.charAt(0) === "t" && hasOwn.call(this, name) && !isNaN(+name.slice(1))) { - this[name] = undefined; - } - } - } - }, - stop: function () { - this.done = true; - var rootEntry = this.tryEntries[0]; - var rootRecord = rootEntry.completion; - - if (rootRecord.type === "throw") { - throw rootRecord.arg; - } - - return this.rval; - }, - dispatchException: function (exception) { - if (this.done) { - throw exception; - } - - var context = this; - - function handle(loc, caught) { - record.type = "throw"; - record.arg = exception; - context.next = loc; - - if (caught) { - // If the dispatched exception was caught by a catch block, - // then let that catch block handle the exception normally. - context.method = "next"; - context.arg = undefined; - } - - return !!caught; - } - - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - var record = entry.completion; - - if (entry.tryLoc === "root") { - // Exception thrown outside of any try block that could handle - // it, so set the completion value of the entire function to - // throw the exception. - return handle("end"); - } - - if (entry.tryLoc <= this.prev) { - var hasCatch = hasOwn.call(entry, "catchLoc"); - var hasFinally = hasOwn.call(entry, "finallyLoc"); - - if (hasCatch && hasFinally) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } else if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - } else if (hasCatch) { - if (this.prev < entry.catchLoc) { - return handle(entry.catchLoc, true); - } - } else if (hasFinally) { - if (this.prev < entry.finallyLoc) { - return handle(entry.finallyLoc); - } - } else { - throw new Error("try statement without catch or finally"); - } - } - } - }, - abrupt: function (type, arg) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - - if (entry.tryLoc <= this.prev && hasOwn.call(entry, "finallyLoc") && this.prev < entry.finallyLoc) { - var finallyEntry = entry; - break; - } - } - - if (finallyEntry && (type === "break" || type === "continue") && finallyEntry.tryLoc <= arg && arg <= finallyEntry.finallyLoc) { - // Ignore the finally entry if control is not jumping to a - // location outside the try/catch block. - finallyEntry = null; - } - - var record = finallyEntry ? finallyEntry.completion : {}; - record.type = type; - record.arg = arg; - - if (finallyEntry) { - this.method = "next"; - this.next = finallyEntry.finallyLoc; - return ContinueSentinel; - } - - return this.complete(record); - }, - complete: function (record, afterLoc) { - if (record.type === "throw") { - throw record.arg; - } - - if (record.type === "break" || record.type === "continue") { - this.next = record.arg; - } else if (record.type === "return") { - this.rval = this.arg = record.arg; - this.method = "return"; - this.next = "end"; - } else if (record.type === "normal" && afterLoc) { - this.next = afterLoc; - } - - return ContinueSentinel; - }, - finish: function (finallyLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - - if (entry.finallyLoc === finallyLoc) { - this.complete(entry.completion, entry.afterLoc); - resetTryEntry(entry); - return ContinueSentinel; - } - } - }, - "catch": function (tryLoc) { - for (var i = this.tryEntries.length - 1; i >= 0; --i) { - var entry = this.tryEntries[i]; - - if (entry.tryLoc === tryLoc) { - var record = entry.completion; - - if (record.type === "throw") { - var thrown = record.arg; - resetTryEntry(entry); - } - - return thrown; - } - } // The context.catch method must only be called with a location - // argument that corresponds to a known catch block. - - - throw new Error("illegal catch attempt"); - }, - delegateYield: function (iterable, resultName, nextLoc) { - this.delegate = { - iterator: values(iterable), - resultName: resultName, - nextLoc: nextLoc - }; - - if (this.method === "next") { - // Deliberately forget the last sent value so that we don't - // accidentally pass it on to the delegate. - this.arg = undefined; - } - - return ContinueSentinel; - } - }; -}( // In sloppy mode, unbound `this` refers to the global object, fallback to -// Function constructor if we're in global strict mode. That is sadly a form -// of indirect eval which violates Content Security Policy. -function () { - return this; -}() || Function("return this")()); - -/***/ }), - -/***/ 1161: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = __webpack_require__(7034); - -/***/ }), - -/***/ 6434: -/***/ (function(module, exports, __webpack_require__) { - -var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Native Javascript for Bootstrap 4 v2.0.27 | © dnp_theme | MIT-License -(function (root, factory) { - if (true) { - // AMD support: - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), - __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? - (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), - __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else { var bsn; } -})(this, function () { - /* Native Javascript for Bootstrap 4 | Internal Utility Functions - ----------------------------------------------------------------*/ - "use strict"; // globals - - var globalObject = typeof __webpack_require__.g !== 'undefined' ? __webpack_require__.g : this || window, - DOC = document, - HTML = DOC.documentElement, - body = 'body', - // allow the library to be used in - // Native Javascript for Bootstrap Global Object - BSN = globalObject.BSN = {}, - supports = BSN.supports = [], - // function toggle attributes - dataToggle = 'data-toggle', - dataDismiss = 'data-dismiss', - dataSpy = 'data-spy', - dataRide = 'data-ride', - // components - stringAlert = 'Alert', - stringButton = 'Button', - stringCarousel = 'Carousel', - stringCollapse = 'Collapse', - stringDropdown = 'Dropdown', - stringModal = 'Modal', - stringPopover = 'Popover', - stringScrollSpy = 'ScrollSpy', - stringTab = 'Tab', - stringTooltip = 'Tooltip', - stringToast = 'Toast', - // options DATA API - dataAutohide = 'data-autohide', - databackdrop = 'data-backdrop', - dataKeyboard = 'data-keyboard', - dataTarget = 'data-target', - dataInterval = 'data-interval', - dataHeight = 'data-height', - dataPause = 'data-pause', - dataTitle = 'data-title', - dataOriginalTitle = 'data-original-title', - dataDismissible = 'data-dismissible', - dataTrigger = 'data-trigger', - dataAnimation = 'data-animation', - dataContainer = 'data-container', - dataPlacement = 'data-placement', - dataDelay = 'data-delay', - // option keys - backdrop = 'backdrop', - keyboard = 'keyboard', - delay = 'delay', - content = 'content', - target = 'target', - currentTarget = 'currentTarget', - interval = 'interval', - pause = 'pause', - animation = 'animation', - placement = 'placement', - container = 'container', - // box model - offsetTop = 'offsetTop', - offsetBottom = 'offsetBottom', - offsetLeft = 'offsetLeft', - scrollTop = 'scrollTop', - scrollLeft = 'scrollLeft', - clientWidth = 'clientWidth', - clientHeight = 'clientHeight', - offsetWidth = 'offsetWidth', - offsetHeight = 'offsetHeight', - innerWidth = 'innerWidth', - innerHeight = 'innerHeight', - scrollHeight = 'scrollHeight', - scrollWidth = 'scrollWidth', - height = 'height', - // aria - ariaExpanded = 'aria-expanded', - ariaHidden = 'aria-hidden', - ariaSelected = 'aria-selected', - // event names - clickEvent = 'click', - focusEvent = 'focus', - hoverEvent = 'hover', - keydownEvent = 'keydown', - keyupEvent = 'keyup', - resizeEvent = 'resize', - // passive - scrollEvent = 'scroll', - // passive - mouseHover = 'onmouseleave' in DOC ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout'], - // touch since 2.0.26 - touchEvents = { - start: 'touchstart', - end: 'touchend', - move: 'touchmove' - }, - // passive - // originalEvents - showEvent = 'show', - shownEvent = 'shown', - hideEvent = 'hide', - hiddenEvent = 'hidden', - closeEvent = 'close', - closedEvent = 'closed', - slidEvent = 'slid', - slideEvent = 'slide', - changeEvent = 'change', - // other - getAttribute = 'getAttribute', - setAttribute = 'setAttribute', - hasAttribute = 'hasAttribute', - createElement = 'createElement', - appendChild = 'appendChild', - innerHTML = 'innerHTML', - getElementsByTagName = 'getElementsByTagName', - preventDefault = 'preventDefault', - getBoundingClientRect = 'getBoundingClientRect', - querySelectorAll = 'querySelectorAll', - getElementsByCLASSNAME = 'getElementsByClassName', - getComputedStyle = 'getComputedStyle', - indexOf = 'indexOf', - parentNode = 'parentNode', - length = 'length', - toLowerCase = 'toLowerCase', - Transition = 'Transition', - Duration = 'Duration', - Webkit = 'Webkit', - style = 'style', - push = 'push', - tabindex = 'tabindex', - contains = 'contains', - active = 'active', - showClass = 'show', - collapsing = 'collapsing', - disabled = 'disabled', - loading = 'loading', - left = 'left', - right = 'right', - top = 'top', - bottom = 'bottom', - // tooltip / popover - tipPositions = /\b(top|bottom|left|right)+/, - // modal - modalOverlay = 0, - fixedTop = 'fixed-top', - fixedBottom = 'fixed-bottom', - // transitionEnd since 2.0.4 - supportTransitions = Webkit + Transition in HTML[style] || Transition[toLowerCase]() in HTML[style], - transitionEndEvent = Webkit + Transition in HTML[style] ? Webkit[toLowerCase]() + Transition + 'End' : Transition[toLowerCase]() + 'end', - transitionDuration = Webkit + Duration in HTML[style] ? Webkit[toLowerCase]() + Transition + Duration : Transition[toLowerCase]() + Duration, - // set new focus element since 2.0.3 - setFocus = function (element) { - element.focus ? element.focus() : element.setActive(); - }, - // class manipulation, since 2.0.0 requires polyfill.js - addClass = function (element, classNAME) { - element.classList.add(classNAME); - }, - removeClass = function (element, classNAME) { - element.classList.remove(classNAME); - }, - hasClass = function (element, classNAME) { - // since 2.0.0 - return element.classList[contains](classNAME); - }, - // selection methods - getElementsByClassName = function (element, classNAME) { - // returns Array - return [].slice.call(element[getElementsByCLASSNAME](classNAME)); - }, - queryElement = function (selector, parent) { - var lookUp = parent ? parent : DOC; - return typeof selector === 'object' ? selector : lookUp.querySelector(selector); - }, - getClosest = function (element, selector) { - //element is the element and selector is for the closest parent element to find - // source http://gomakethings.com/climbing-up-and-down-the-dom-tree-with-vanilla-javascript/ - var firstChar = selector.charAt(0), - selectorSubstring = selector.substr(1); - - if (firstChar === '.') { - // If selector is a class - for (; element && element !== DOC; element = element[parentNode]) { - // Get closest match - if (queryElement(selector, element[parentNode]) !== null && hasClass(element, selectorSubstring)) { - return element; - } - } - } else if (firstChar === '#') { - // If selector is an ID - for (; element && element !== DOC; element = element[parentNode]) { - // Get closest match - if (element.id === selectorSubstring) { - return element; - } - } - } - - return false; - }, - // event attach jQuery style / trigger since 1.2.0 - on = function (element, event, handler, options) { - options = options || false; - element.addEventListener(event, handler, options); - }, - off = function (element, event, handler, options) { - options = options || false; - element.removeEventListener(event, handler, options); - }, - one = function (element, event, handler, options) { - // one since 2.0.4 - on(element, event, function handlerWrapper(e) { - handler(e); - off(element, event, handlerWrapper, options); - }, options); - }, - // determine support for passive events - supportPassive = function () { - // Test via a getter in the options object to see if the passive property is accessed - var result = false; - - try { - var opts = Object.defineProperty({}, 'passive', { - get: function () { - result = true; - } - }); - one(globalObject, 'testPassive', null, opts); - } catch (e) {} - - return result; - }(), - // event options - // https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection - passiveHandler = supportPassive ? { - passive: true - } : false, - // transitions - getTransitionDurationFromElement = function (element) { - var duration = supportTransitions ? globalObject[getComputedStyle](element)[transitionDuration] : 0; - duration = parseFloat(duration); - duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0; - return duration; // we take a short offset to make sure we fire on the next frame after animation - }, - emulateTransitionEnd = function (element, handler) { - // emulateTransitionEnd since 2.0.4 - var called = 0, - duration = getTransitionDurationFromElement(element); - duration ? one(element, transitionEndEvent, function (e) { - !called && handler(e), called = 1; - }) : setTimeout(function () { - !called && handler(), called = 1; - }, 17); - }, - bootstrapCustomEvent = function (eventName, componentName, related) { - var OriginalCustomEvent = new CustomEvent(eventName + '.bs.' + componentName); - OriginalCustomEvent.relatedTarget = related; - this.dispatchEvent(OriginalCustomEvent); - }, - // tooltip / popover stuff - getScroll = function () { - // also Affix and ScrollSpy uses it - return { - y: globalObject.pageYOffset || HTML[scrollTop], - x: globalObject.pageXOffset || HTML[scrollLeft] - }; - }, - styleTip = function (link, element, position, parent) { - // both popovers and tooltips (target,tooltip,placement,elementToAppendTo) - var elementDimensions = { - w: element[offsetWidth], - h: element[offsetHeight] - }, - windowWidth = HTML[clientWidth] || DOC[body][clientWidth], - windowHeight = HTML[clientHeight] || DOC[body][clientHeight], - rect = link[getBoundingClientRect](), - scroll = parent === DOC[body] ? getScroll() : { - x: parent[offsetLeft] + parent[scrollLeft], - y: parent[offsetTop] + parent[scrollTop] - }, - linkDimensions = { - w: rect[right] - rect[left], - h: rect[bottom] - rect[top] - }, - isPopover = hasClass(element, 'popover'), - topPosition, - leftPosition, - arrow = queryElement('.arrow', element), - arrowTop, - arrowLeft, - arrowWidth, - arrowHeight, - halfTopExceed = rect[top] + linkDimensions.h / 2 - elementDimensions.h / 2 < 0, - halfLeftExceed = rect[left] + linkDimensions.w / 2 - elementDimensions.w / 2 < 0, - halfRightExceed = rect[left] + elementDimensions.w / 2 + linkDimensions.w / 2 >= windowWidth, - halfBottomExceed = rect[top] + elementDimensions.h / 2 + linkDimensions.h / 2 >= windowHeight, - topExceed = rect[top] - elementDimensions.h < 0, - leftExceed = rect[left] - elementDimensions.w < 0, - bottomExceed = rect[top] + elementDimensions.h + linkDimensions.h >= windowHeight, - rightExceed = rect[left] + elementDimensions.w + linkDimensions.w >= windowWidth; // recompute position - - position = (position === left || position === right) && leftExceed && rightExceed ? top : position; // first, when both left and right limits are exceeded, we fall back to top|bottom - - position = position === top && topExceed ? bottom : position; - position = position === bottom && bottomExceed ? top : position; - position = position === left && leftExceed ? right : position; - position = position === right && rightExceed ? left : position; // update tooltip/popover class - - element.className[indexOf](position) === -1 && (element.className = element.className.replace(tipPositions, position)); // we check the computed width & height and update here - - arrowWidth = arrow[offsetWidth]; - arrowHeight = arrow[offsetHeight]; // apply styling to tooltip or popover - - if (position === left || position === right) { - // secondary|side positions - if (position === left) { - // LEFT - leftPosition = rect[left] + scroll.x - elementDimensions.w - (isPopover ? arrowWidth : 0); - } else { - // RIGHT - leftPosition = rect[left] + scroll.x + linkDimensions.w; - } // adjust top and arrow - - - if (halfTopExceed) { - topPosition = rect[top] + scroll.y; - arrowTop = linkDimensions.h / 2 - arrowWidth; - } else if (halfBottomExceed) { - topPosition = rect[top] + scroll.y - elementDimensions.h + linkDimensions.h; - arrowTop = elementDimensions.h - linkDimensions.h / 2 - arrowWidth; - } else { - topPosition = rect[top] + scroll.y - elementDimensions.h / 2 + linkDimensions.h / 2; - arrowTop = elementDimensions.h / 2 - (isPopover ? arrowHeight * 0.9 : arrowHeight / 2); - } - } else if (position === top || position === bottom) { - // primary|vertical positions - if (position === top) { - // TOP - topPosition = rect[top] + scroll.y - elementDimensions.h - (isPopover ? arrowHeight : 0); - } else { - // BOTTOM - topPosition = rect[top] + scroll.y + linkDimensions.h; - } // adjust left | right and also the arrow - - - if (halfLeftExceed) { - leftPosition = 0; - arrowLeft = rect[left] + linkDimensions.w / 2 - arrowWidth; - } else if (halfRightExceed) { - leftPosition = windowWidth - elementDimensions.w * 1.01; - arrowLeft = elementDimensions.w - (windowWidth - rect[left]) + linkDimensions.w / 2 - arrowWidth / 2; - } else { - leftPosition = rect[left] + scroll.x - elementDimensions.w / 2 + linkDimensions.w / 2; - arrowLeft = elementDimensions.w / 2 - (isPopover ? arrowWidth : arrowWidth / 2); - } - } // apply style to tooltip/popover and its arrow - - - element[style][top] = topPosition + 'px'; - element[style][left] = leftPosition + 'px'; - arrowTop && (arrow[style][top] = arrowTop + 'px'); - arrowLeft && (arrow[style][left] = arrowLeft + 'px'); - }; - - BSN.version = '2.0.27'; - /* Native Javascript for Bootstrap 4 | Alert - -------------------------------------------*/ - // ALERT DEFINITION - // ================ - - var Alert = function (element) { - // initialization element - element = queryElement(element); // bind, target alert, duration and stuff - - var self = this, - component = 'alert', - alert = getClosest(element, '.' + component), - triggerHandler = function () { - hasClass(alert, 'fade') ? emulateTransitionEnd(alert, transitionEndHandler) : transitionEndHandler(); - }, - // handlers - clickHandler = function (e) { - alert = getClosest(e[target], '.' + component); - element = queryElement('[' + dataDismiss + '="' + component + '"]', alert); - element && alert && (element === e[target] || element[contains](e[target])) && self.close(); - }, - transitionEndHandler = function () { - bootstrapCustomEvent.call(alert, closedEvent, component); - off(element, clickEvent, clickHandler); // detach it's listener - - alert[parentNode].removeChild(alert); - }; // public method - - - this.close = function () { - if (alert && element && hasClass(alert, showClass)) { - bootstrapCustomEvent.call(alert, closeEvent, component); - removeClass(alert, showClass); - alert && triggerHandler(); - } - }; // init - - - if (!(stringAlert in element)) { - // prevent adding event handlers twice - on(element, clickEvent, clickHandler); - } - - element[stringAlert] = self; - }; // ALERT DATA API - // ============== - - - supports[push]([stringAlert, Alert, '[' + dataDismiss + '="alert"]']); - /* Native Javascript for Bootstrap 4 | Button - ---------------------------------------------*/ - // BUTTON DEFINITION - // =================== - - var Button = function (element) { - // initialization element - element = queryElement(element); // constant - - var toggled = false, - // toggled makes sure to prevent triggering twice the change.bs.button events - // strings - component = 'button', - checked = 'checked', - LABEL = 'LABEL', - INPUT = 'INPUT', - // private methods - keyHandler = function (e) { - var key = e.which || e.keyCode; - key === 32 && e[target] === DOC.activeElement && toggle(e); - }, - preventScroll = function (e) { - var key = e.which || e.keyCode; - key === 32 && e[preventDefault](); - }, - toggle = function (e) { - var label = e[target].tagName === LABEL ? e[target] : e[target][parentNode].tagName === LABEL ? e[target][parentNode] : null; // the .btn label - - if (!label) return; //react if a label or its immediate child is clicked - - var labels = getElementsByClassName(label[parentNode], 'btn'), - // all the button group buttons - input = label[getElementsByTagName](INPUT)[0]; - if (!input) return; // return if no input found - // manage the dom manipulation - - if (input.type === 'checkbox') { - //checkboxes - if (!input[checked]) { - addClass(label, active); - input[getAttribute](checked); - input[setAttribute](checked, checked); - input[checked] = true; - } else { - removeClass(label, active); - input[getAttribute](checked); - input.removeAttribute(checked); - input[checked] = false; - } - - if (!toggled) { - // prevent triggering the event twice - toggled = true; - bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input - - bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group - } - } - - if (input.type === 'radio' && !toggled) { - // radio buttons - // don't trigger if already active (the OR condition is a hack to check if the buttons were selected with key press and NOT mouse click) - if (!input[checked] || e.screenX === 0 && e.screenY == 0) { - addClass(label, active); - addClass(label, focusEvent); - input[setAttribute](checked, checked); - input[checked] = true; - bootstrapCustomEvent.call(input, changeEvent, component); //trigger the change for the input - - bootstrapCustomEvent.call(element, changeEvent, component); //trigger the change for the btn-group - - toggled = true; - - for (var i = 0, ll = labels[length]; i < ll; i++) { - var otherLabel = labels[i], - otherInput = otherLabel[getElementsByTagName](INPUT)[0]; - - if (otherLabel !== label && hasClass(otherLabel, active)) { - removeClass(otherLabel, active); - otherInput.removeAttribute(checked); - otherInput[checked] = false; - bootstrapCustomEvent.call(otherInput, changeEvent, component); // trigger the change - } - } - } - } - - setTimeout(function () { - toggled = false; - }, 50); - }, - focusHandler = function (e) { - addClass(e[target][parentNode], focusEvent); - }, - blurHandler = function (e) { - removeClass(e[target][parentNode], focusEvent); - }; // init - - - if (!(stringButton in element)) { - // prevent adding event handlers twice - on(element, clickEvent, toggle); - on(element, keyupEvent, keyHandler), on(element, keydownEvent, preventScroll); - var allBtns = getElementsByClassName(element, 'btn'); - - for (var i = 0; i < allBtns.length; i++) { - var input = allBtns[i][getElementsByTagName](INPUT)[0]; - on(input, focusEvent, focusHandler), on(input, 'blur', blurHandler); - } - } // activate items on load - - - var labelsToACtivate = getElementsByClassName(element, 'btn'), - lbll = labelsToACtivate[length]; - - for (var i = 0; i < lbll; i++) { - !hasClass(labelsToACtivate[i], active) && queryElement('input:checked', labelsToACtivate[i]) && addClass(labelsToACtivate[i], active); - } - - element[stringButton] = this; - }; // BUTTON DATA API - // ================= - - - supports[push]([stringButton, Button, '[' + dataToggle + '="buttons"]']); - /* Native Javascript for Bootstrap 4 | Collapse - -----------------------------------------------*/ - // COLLAPSE DEFINITION - // =================== - - var Collapse = function (element, options) { - // initialization element - element = queryElement(element); // set options - - options = options || {}; // event targets and constants - - var accordion = null, - collapse = null, - self = this, - accordionData = element[getAttribute]('data-parent'), - activeCollapse, - activeElement, - // component strings - component = 'collapse', - collapsed = 'collapsed', - isAnimating = 'isAnimating', - // private methods - openAction = function (collapseElement, toggle) { - bootstrapCustomEvent.call(collapseElement, showEvent, component); - collapseElement[isAnimating] = true; - addClass(collapseElement, collapsing); - removeClass(collapseElement, component); - collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; - emulateTransitionEnd(collapseElement, function () { - collapseElement[isAnimating] = false; - collapseElement[setAttribute](ariaExpanded, 'true'); - toggle[setAttribute](ariaExpanded, 'true'); - removeClass(collapseElement, collapsing); - addClass(collapseElement, component); - addClass(collapseElement, showClass); - collapseElement[style][height] = ''; - bootstrapCustomEvent.call(collapseElement, shownEvent, component); - }); - }, - closeAction = function (collapseElement, toggle) { - bootstrapCustomEvent.call(collapseElement, hideEvent, component); - collapseElement[isAnimating] = true; - collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first - - removeClass(collapseElement, component); - removeClass(collapseElement, showClass); - addClass(collapseElement, collapsing); - collapseElement[offsetWidth]; // force reflow to enable transition - - collapseElement[style][height] = '0px'; - emulateTransitionEnd(collapseElement, function () { - collapseElement[isAnimating] = false; - collapseElement[setAttribute](ariaExpanded, 'false'); - toggle[setAttribute](ariaExpanded, 'false'); - removeClass(collapseElement, collapsing); - addClass(collapseElement, component); - collapseElement[style][height] = ''; - bootstrapCustomEvent.call(collapseElement, hiddenEvent, component); - }); - }, - getTarget = function () { - var href = element.href && element[getAttribute]('href'), - parent = element[getAttribute](dataTarget), - id = href || parent && parent.charAt(0) === '#' && parent; - return id && queryElement(id); - }; // public methods - - - this.toggle = function (e) { - e[preventDefault](); - - if (!hasClass(collapse, showClass)) { - self.show(); - } else { - self.hide(); - } - }; - - this.hide = function () { - if (collapse[isAnimating]) return; - closeAction(collapse, element); - addClass(element, collapsed); - }; - - this.show = function () { - if (accordion) { - activeCollapse = queryElement('.' + component + '.' + showClass, accordion); - activeElement = activeCollapse && (queryElement('[' + dataTarget + '="#' + activeCollapse.id + '"]', accordion) || queryElement('[href="#' + activeCollapse.id + '"]', accordion)); - } - - if (!collapse[isAnimating] || activeCollapse && !activeCollapse[isAnimating]) { - if (activeElement && activeCollapse !== collapse) { - closeAction(activeCollapse, activeElement); - addClass(activeElement, collapsed); - } - - openAction(collapse, element); - removeClass(element, collapsed); - } - }; // init - - - if (!(stringCollapse in element)) { - // prevent adding event handlers twice - on(element, clickEvent, self.toggle); - } - - collapse = getTarget(); - collapse[isAnimating] = false; // when true it will prevent click handlers - - accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData); - element[stringCollapse] = self; - }; // COLLAPSE DATA API - // ================= - - - supports[push]([stringCollapse, Collapse, '[' + dataToggle + '="collapse"]']); - /* Native Javascript for Bootstrap 4 | Dropdown - ----------------------------------------------*/ - // DROPDOWN DEFINITION - // =================== - - var Dropdown = function (element, option) { - // initialization element - element = queryElement(element); // set option - - this.persist = option === true || element[getAttribute]('data-persist') === 'true' || false; // constants, event targets, strings - - var self = this, - children = 'children', - parent = element[parentNode], - component = 'dropdown', - open = 'open', - relatedTarget = null, - menu = queryElement('.dropdown-menu', parent), - menuItems = function () { - var set = menu[children], - newSet = []; - - for (var i = 0; i < set[length]; i++) { - set[i][children][length] && set[i][children][0].tagName === 'A' && newSet[push](set[i][children][0]); - set[i].tagName === 'A' && newSet[push](set[i]); - } - - return newSet; - }(), - // preventDefault on empty anchor links - preventEmptyAnchor = function (anchor) { - (anchor.href && anchor.href.slice(-1) === '#' || anchor[parentNode] && anchor[parentNode].href && anchor[parentNode].href.slice(-1) === '#') && this[preventDefault](); - }, - // toggle dismissible events - toggleDismiss = function () { - var type = element[open] ? on : off; - type(DOC, clickEvent, dismissHandler); - type(DOC, keydownEvent, preventScroll); - type(DOC, keyupEvent, keyHandler); - type(DOC, focusEvent, dismissHandler, true); - }, - // handlers - dismissHandler = function (e) { - var eventTarget = e[target], - hasData = eventTarget && (eventTarget[getAttribute](dataToggle) || eventTarget[parentNode] && getAttribute in eventTarget[parentNode] && eventTarget[parentNode][getAttribute](dataToggle)); - - if (e.type === focusEvent && (eventTarget === element || eventTarget === menu || menu[contains](eventTarget))) { - return; - } - - if ((eventTarget === menu || menu[contains](eventTarget)) && (self.persist || hasData)) { - return; - } else { - relatedTarget = eventTarget === element || element[contains](eventTarget) ? element : null; - hide(); - } - - preventEmptyAnchor.call(e, eventTarget); - }, - clickHandler = function (e) { - relatedTarget = element; - show(); - preventEmptyAnchor.call(e, e[target]); - }, - preventScroll = function (e) { - var key = e.which || e.keyCode; - - if (key === 38 || key === 40) { - e[preventDefault](); - } - }, - keyHandler = function (e) { - var key = e.which || e.keyCode, - activeItem = DOC.activeElement, - idx = menuItems[indexOf](activeItem), - isSameElement = activeItem === element, - isInsideMenu = menu[contains](activeItem), - isMenuItem = activeItem[parentNode] === menu || activeItem[parentNode][parentNode] === menu; - - if (isMenuItem) { - // navigate up | down - idx = isSameElement ? 0 : key === 38 ? idx > 1 ? idx - 1 : 0 : key === 40 ? idx < menuItems[length] - 1 ? idx + 1 : idx : idx; - menuItems[idx] && setFocus(menuItems[idx]); - } - - if ((menuItems[length] && isMenuItem // menu has items - || !menuItems[length] && (isInsideMenu || isSameElement) // menu might be a form - || !isInsideMenu) && // or the focused element is not in the menu at all - element[open] && key === 27 // menu must be open - ) { - self.toggle(); - relatedTarget = null; - } - }, - // private methods - show = function () { - bootstrapCustomEvent.call(parent, showEvent, component, relatedTarget); - addClass(menu, showClass); - addClass(parent, showClass); - element[setAttribute](ariaExpanded, true); - bootstrapCustomEvent.call(parent, shownEvent, component, relatedTarget); - element[open] = true; - off(element, clickEvent, clickHandler); - setTimeout(function () { - setFocus(menu[getElementsByTagName]('INPUT')[0] || element); // focus the first input item | element - - toggleDismiss(); - }, 1); - }, - hide = function () { - bootstrapCustomEvent.call(parent, hideEvent, component, relatedTarget); - removeClass(menu, showClass); - removeClass(parent, showClass); - element[setAttribute](ariaExpanded, false); - bootstrapCustomEvent.call(parent, hiddenEvent, component, relatedTarget); - element[open] = false; - toggleDismiss(); - setFocus(element); - setTimeout(function () { - on(element, clickEvent, clickHandler); - }, 1); - }; // set initial state to closed - - - element[open] = false; // public methods - - this.toggle = function () { - if (hasClass(parent, showClass) && element[open]) { - hide(); - } else { - show(); - } - }; // init - - - if (!(stringDropdown in element)) { - // prevent adding event handlers twice - !tabindex in menu && menu[setAttribute](tabindex, '0'); // Fix onblur on Chrome | Safari - - on(element, clickEvent, clickHandler); - } - - element[stringDropdown] = self; - }; // DROPDOWN DATA API - // ================= - - - supports[push]([stringDropdown, Dropdown, '[' + dataToggle + '="dropdown"]']); - /* Native Javascript for Bootstrap 4 | Modal - -------------------------------------------*/ - // MODAL DEFINITION - // =============== - - var Modal = function (element, options) { - // element can be the modal/triggering button - // the modal (both JavaScript / DATA API init) / triggering button element (DATA API) - element = queryElement(element); // strings - - var component = 'modal', - staticString = 'static', - modalTrigger = 'modalTrigger', - paddingRight = 'paddingRight', - modalBackdropString = 'modal-backdrop', - isAnimating = 'isAnimating', - // determine modal, triggering element - btnCheck = element[getAttribute](dataTarget) || element[getAttribute]('href'), - checkModal = queryElement(btnCheck), - modal = hasClass(element, component) ? element : checkModal; - - if (hasClass(element, component)) { - element = null; - } // modal is now independent of it's triggering element - - - if (!modal) { - return; - } // invalidate - // set options - - - options = options || {}; - this[keyboard] = options[keyboard] === false || modal[getAttribute](dataKeyboard) === 'false' ? false : true; - this[backdrop] = options[backdrop] === staticString || modal[getAttribute](databackdrop) === staticString ? staticString : true; - this[backdrop] = options[backdrop] === false || modal[getAttribute](databackdrop) === 'false' ? false : this[backdrop]; - this[animation] = hasClass(modal, 'fade') ? true : false; - this[content] = options[content]; // JavaScript only - // set an initial state of the modal - - modal[isAnimating] = false; // bind, constants, event targets and other vars - - var self = this, - relatedTarget = null, - bodyIsOverflowing, - scrollBarWidth, - overlay, - overlayDelay, - modalTimer, - // also find fixed-top / fixed-bottom items - fixedItems = getElementsByClassName(HTML, fixedTop).concat(getElementsByClassName(HTML, fixedBottom)), - // private methods - getWindowWidth = function () { - var htmlRect = HTML[getBoundingClientRect](); - return globalObject[innerWidth] || htmlRect[right] - Math.abs(htmlRect[left]); - }, - setScrollbar = function () { - var bodyStyle = globalObject[getComputedStyle](DOC[body]), - bodyPad = parseInt(bodyStyle[paddingRight], 10), - itemPad; - - if (bodyIsOverflowing) { - DOC[body][style][paddingRight] = bodyPad + scrollBarWidth + 'px'; - modal[style][paddingRight] = scrollBarWidth + 'px'; - - if (fixedItems[length]) { - for (var i = 0; i < fixedItems[length]; i++) { - itemPad = globalObject[getComputedStyle](fixedItems[i])[paddingRight]; - fixedItems[i][style][paddingRight] = parseInt(itemPad) + scrollBarWidth + 'px'; - } - } - } - }, - resetScrollbar = function () { - DOC[body][style][paddingRight] = ''; - modal[style][paddingRight] = ''; - - if (fixedItems[length]) { - for (var i = 0; i < fixedItems[length]; i++) { - fixedItems[i][style][paddingRight] = ''; - } - } - }, - measureScrollbar = function () { - // thx walsh - var scrollDiv = DOC[createElement]('div'), - widthValue; - scrollDiv.className = component + '-scrollbar-measure'; // this is here to stay - - DOC[body][appendChild](scrollDiv); - widthValue = scrollDiv[offsetWidth] - scrollDiv[clientWidth]; - DOC[body].removeChild(scrollDiv); - return widthValue; - }, - checkScrollbar = function () { - bodyIsOverflowing = DOC[body][clientWidth] < getWindowWidth(); - scrollBarWidth = measureScrollbar(); - }, - createOverlay = function () { - var newOverlay = DOC[createElement]('div'); - overlay = queryElement('.' + modalBackdropString); - - if (overlay === null) { - newOverlay[setAttribute]('class', modalBackdropString + (self[animation] ? ' fade' : '')); - overlay = newOverlay; - DOC[body][appendChild](overlay); - } - - modalOverlay = 1; - }, - removeOverlay = function () { - overlay = queryElement('.' + modalBackdropString); - - if (overlay && overlay !== null && typeof overlay === 'object') { - modalOverlay = 0; - DOC[body].removeChild(overlay); - overlay = null; - } - }, - // triggers - triggerShow = function () { - setFocus(modal); - modal[isAnimating] = false; - bootstrapCustomEvent.call(modal, shownEvent, component, relatedTarget); - on(globalObject, resizeEvent, self.update, passiveHandler); - on(modal, clickEvent, dismissHandler); - on(DOC, keydownEvent, keyHandler); - }, - triggerHide = function () { - modal[style].display = ''; - element && setFocus(element); - bootstrapCustomEvent.call(modal, hiddenEvent, component); - - (function () { - if (!getElementsByClassName(DOC, component + ' ' + showClass)[0]) { - resetScrollbar(); - removeClass(DOC[body], component + '-open'); - overlay && hasClass(overlay, 'fade') ? (removeClass(overlay, showClass), emulateTransitionEnd(overlay, removeOverlay)) : removeOverlay(); - off(globalObject, resizeEvent, self.update, passiveHandler); - off(modal, clickEvent, dismissHandler); - off(DOC, keydownEvent, keyHandler); - } - })(); - - modal[isAnimating] = false; - }, - // handlers - clickHandler = function (e) { - if (modal[isAnimating]) return; - var clickTarget = e[target]; - clickTarget = clickTarget[hasAttribute](dataTarget) || clickTarget[hasAttribute]('href') ? clickTarget : clickTarget[parentNode]; - - if (clickTarget === element && !hasClass(modal, showClass)) { - modal[modalTrigger] = element; - relatedTarget = element; - self.show(); - e[preventDefault](); - } - }, - keyHandler = function (e) { - if (modal[isAnimating]) return; - - if (self[keyboard] && e.which == 27 && hasClass(modal, showClass)) { - self.hide(); - } - }, - dismissHandler = function (e) { - if (modal[isAnimating]) return; - var clickTarget = e[target]; - - if (hasClass(modal, showClass) && (clickTarget[parentNode][getAttribute](dataDismiss) === component || clickTarget[getAttribute](dataDismiss) === component || clickTarget === modal && self[backdrop] !== staticString)) { - self.hide(); - relatedTarget = null; - e[preventDefault](); - } - }; // public methods - - - this.toggle = function () { - if (hasClass(modal, showClass)) { - this.hide(); - } else { - this.show(); - } - }; - - this.show = function () { - if (hasClass(modal, showClass) || modal[isAnimating]) { - return; - } - - clearTimeout(modalTimer); - modalTimer = setTimeout(function () { - modal[isAnimating] = true; - bootstrapCustomEvent.call(modal, showEvent, component, relatedTarget); // we elegantly hide any opened modal - - var currentOpen = getElementsByClassName(DOC, component + ' ' + showClass)[0]; - - if (currentOpen && currentOpen !== modal) { - modalTrigger in currentOpen && currentOpen[modalTrigger][stringModal].hide(); - stringModal in currentOpen && currentOpen[stringModal].hide(); - } - - if (self[backdrop]) { - !modalOverlay && !overlay && createOverlay(); - } - - if (overlay && !hasClass(overlay, showClass)) { - overlay[offsetWidth]; // force reflow to enable trasition - - overlayDelay = getTransitionDurationFromElement(overlay); - addClass(overlay, showClass); - } - - setTimeout(function () { - modal[style].display = 'block'; - checkScrollbar(); - setScrollbar(); - addClass(DOC[body], component + '-open'); - addClass(modal, showClass); - modal[setAttribute](ariaHidden, false); - hasClass(modal, 'fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow(); - }, supportTransitions && overlay && overlayDelay ? overlayDelay : 1); - }, 1); - }; - - this.hide = function () { - if (modal[isAnimating] || !hasClass(modal, showClass)) { - return; - } - - clearTimeout(modalTimer); - modalTimer = setTimeout(function () { - modal[isAnimating] = true; - bootstrapCustomEvent.call(modal, hideEvent, component); - overlay = queryElement('.' + modalBackdropString); - overlayDelay = overlay && getTransitionDurationFromElement(overlay); - removeClass(modal, showClass); - modal[setAttribute](ariaHidden, true); - setTimeout(function () { - hasClass(modal, 'fade') ? emulateTransitionEnd(modal, triggerHide) : triggerHide(); - }, supportTransitions && overlay && overlayDelay ? overlayDelay : 2); - }, 2); - }; - - this.setContent = function (content) { - queryElement('.' + component + '-content', modal)[innerHTML] = content; - }; - - this.update = function () { - if (hasClass(modal, showClass)) { - checkScrollbar(); - setScrollbar(); - } - }; // init - // prevent adding event handlers over and over - // modal is independent of a triggering element - - - if (!!element && !(stringModal in element)) { - on(element, clickEvent, clickHandler); - } - - if (!!self[content]) { - self.setContent(self[content]); - } - - if (element) { - element[stringModal] = self; - modal[modalTrigger] = element; - } else { - modal[stringModal] = self; - } - }; // DATA API - - - supports[push]([stringModal, Modal, '[' + dataToggle + '="modal"]']); - /* Native Javascript for Bootstrap 4 | Popover - ----------------------------------------------*/ - // POPOVER DEFINITION - // ================== - - var Popover = function (element, options) { - // initialization element - element = queryElement(element); // set options - - options = options || {}; // DATA API - - var triggerData = element[getAttribute](dataTrigger), - // click / hover / focus - animationData = element[getAttribute](dataAnimation), - // true / false - placementData = element[getAttribute](dataPlacement), - dismissibleData = element[getAttribute](dataDismissible), - delayData = element[getAttribute](dataDelay), - containerData = element[getAttribute](dataContainer), - // internal strings - component = 'popover', - template = 'template', - trigger = 'trigger', - classString = 'class', - div = 'div', - fade = 'fade', - dataContent = 'data-content', - dismissible = 'dismissible', - closeBtn = '', - // check container - containerElement = queryElement(options[container]), - containerDataElement = queryElement(containerData), - // maybe the element is inside a modal - modal = getClosest(element, '.modal'), - // maybe the element is inside a fixed navbar - navbarFixedTop = getClosest(element, '.' + fixedTop), - navbarFixedBottom = getClosest(element, '.' + fixedBottom); // set instance options - - this[template] = options[template] ? options[template] : null; // JavaScript only - - this[trigger] = options[trigger] ? options[trigger] : triggerData || hoverEvent; - this[animation] = options[animation] && options[animation] !== fade ? options[animation] : animationData || fade; - this[placement] = options[placement] ? options[placement] : placementData || top; - this[delay] = parseInt(options[delay] || delayData) || 200; - this[dismissible] = options[dismissible] || dismissibleData === 'true' ? true : false; - this[container] = containerElement ? containerElement : containerDataElement ? containerDataElement : navbarFixedTop ? navbarFixedTop : navbarFixedBottom ? navbarFixedBottom : modal ? modal : DOC[body]; // bind, content - - var self = this, - titleString = options.title || element[getAttribute](dataTitle) || null, - contentString = options.content || element[getAttribute](dataContent) || null; - if (!contentString && !this[template]) return; // invalidate - // constants, vars - - var popover = null, - timer = 0, - placementSetting = this[placement], - // handlers - dismissibleHandler = function (e) { - if (popover !== null && e[target] === queryElement('.close', popover)) { - self.hide(); - } - }, - // private methods - removePopover = function () { - self[container].removeChild(popover); - timer = null; - popover = null; - }, - createPopover = function () { - titleString = options.title || element[getAttribute](dataTitle); - contentString = options.content || element[getAttribute](dataContent); // fixing https://github.com/thednp/bootstrap.native/issues/233 - - contentString = !!contentString ? contentString.trim() : null; - popover = DOC[createElement](div); // popover arrow - - var popoverArrow = DOC[createElement](div); - popoverArrow[setAttribute](classString, 'arrow'); - popover[appendChild](popoverArrow); - - if (contentString !== null && self[template] === null) { - //create the popover from data attributes - popover[setAttribute]('role', 'tooltip'); - - if (titleString !== null) { - var popoverTitle = DOC[createElement]('h3'); - popoverTitle[setAttribute](classString, component + '-header'); - popoverTitle[innerHTML] = self[dismissible] ? titleString + closeBtn : titleString; - popover[appendChild](popoverTitle); - } //set popover content - - - var popoverContent = DOC[createElement](div); - popoverContent[setAttribute](classString, component + '-body'); - popoverContent[innerHTML] = self[dismissible] && titleString === null ? contentString + closeBtn : contentString; - popover[appendChild](popoverContent); - } else { - // or create the popover from template - var popoverTemplate = DOC[createElement](div); - self[template] = self[template].trim(); - popoverTemplate[innerHTML] = self[template]; - popover[innerHTML] = popoverTemplate.firstChild[innerHTML]; - } //append to the container - - - self[container][appendChild](popover); - popover[style].display = 'block'; - popover[setAttribute](classString, component + ' bs-' + component + '-' + placementSetting + ' ' + self[animation]); - }, - showPopover = function () { - !hasClass(popover, showClass) && addClass(popover, showClass); - }, - updatePopover = function () { - styleTip(element, popover, placementSetting, self[container]); - }, - // event toggle - dismissHandlerToggle = function (type) { - if (clickEvent == self[trigger] || 'focus' == self[trigger]) { - !self[dismissible] && type(element, 'blur', self.hide); - } - - self[dismissible] && type(DOC, clickEvent, dismissibleHandler); - type(globalObject, resizeEvent, self.hide, passiveHandler); - }, - // triggers - showTrigger = function () { - dismissHandlerToggle(on); - bootstrapCustomEvent.call(element, shownEvent, component); - }, - hideTrigger = function () { - dismissHandlerToggle(off); - removePopover(); - bootstrapCustomEvent.call(element, hiddenEvent, component); - }; // public methods / handlers - - - this.toggle = function () { - if (popover === null) { - self.show(); - } else { - self.hide(); - } - }; - - this.show = function () { - clearTimeout(timer); - timer = setTimeout(function () { - if (popover === null) { - placementSetting = self[placement]; // we reset placement in all cases - - createPopover(); - updatePopover(); - showPopover(); - bootstrapCustomEvent.call(element, showEvent, component); - !!self[animation] ? emulateTransitionEnd(popover, showTrigger) : showTrigger(); - } - }, 20); - }; - - this.hide = function () { - clearTimeout(timer); - timer = setTimeout(function () { - if (popover && popover !== null && hasClass(popover, showClass)) { - bootstrapCustomEvent.call(element, hideEvent, component); - removeClass(popover, showClass); - !!self[animation] ? emulateTransitionEnd(popover, hideTrigger) : hideTrigger(); - } - }, self[delay]); - }; // init - - - if (!(stringPopover in element)) { - // prevent adding event handlers twice - if (self[trigger] === hoverEvent) { - on(element, mouseHover[0], self.show); - - if (!self[dismissible]) { - on(element, mouseHover[1], self.hide); - } - } else if (clickEvent == self[trigger] || 'focus' == self[trigger]) { - on(element, self[trigger], self.toggle); - } - } - - element[stringPopover] = self; - }; // POPOVER DATA API - // ================ - - - supports[push]([stringPopover, Popover, '[' + dataToggle + '="popover"]']); - /* Native Javascript for Bootstrap 4 | Tab - -----------------------------------------*/ - // TAB DEFINITION - // ============== - - var Tab = function (element, options) { - // initialization element - element = queryElement(element); // DATA API - - var heightData = element[getAttribute](dataHeight), - // strings - component = 'tab', - height = 'height', - float = 'float', - isAnimating = 'isAnimating'; // set options - - options = options || {}; - this[height] = supportTransitions ? options[height] || heightData === 'true' : false; // bind, event targets - - var self = this, - next, - tabs = getClosest(element, '.nav'), - tabsContentContainer = false, - dropdown = tabs && queryElement('.dropdown-toggle', tabs), - activeTab, - activeContent, - nextContent, - containerHeight, - equalContents, - nextHeight, - // trigger - triggerEnd = function () { - tabsContentContainer[style][height] = ''; - removeClass(tabsContentContainer, collapsing); - tabs[isAnimating] = false; - }, - triggerShow = function () { - if (tabsContentContainer) { - // height animation - if (equalContents) { - triggerEnd(); - } else { - setTimeout(function () { - // enables height animation - tabsContentContainer[style][height] = nextHeight + 'px'; // height animation - - tabsContentContainer[offsetWidth]; - emulateTransitionEnd(tabsContentContainer, triggerEnd); - }, 50); - } - } else { - tabs[isAnimating] = false; - } - - bootstrapCustomEvent.call(next, shownEvent, component, activeTab); - }, - triggerHide = function () { - if (tabsContentContainer) { - activeContent[style][float] = left; - nextContent[style][float] = left; - containerHeight = activeContent[scrollHeight]; - } - - addClass(nextContent, active); - bootstrapCustomEvent.call(next, showEvent, component, activeTab); - removeClass(activeContent, active); - bootstrapCustomEvent.call(activeTab, hiddenEvent, component, next); - - if (tabsContentContainer) { - nextHeight = nextContent[scrollHeight]; - equalContents = nextHeight === containerHeight; - addClass(tabsContentContainer, collapsing); - tabsContentContainer[style][height] = containerHeight + 'px'; // height animation - - tabsContentContainer[offsetHeight]; - activeContent[style][float] = ''; - nextContent[style][float] = ''; - } - - if (hasClass(nextContent, 'fade')) { - setTimeout(function () { - addClass(nextContent, showClass); - emulateTransitionEnd(nextContent, triggerShow); - }, 20); - } else { - triggerShow(); - } - }; - - if (!tabs) return; // invalidate - // set default animation state - - tabs[isAnimating] = false; // private methods - - var getActiveTab = function () { - var activeTabs = getElementsByClassName(tabs, active), - activeTab; - - if (activeTabs[length] === 1 && !hasClass(activeTabs[0][parentNode], 'dropdown')) { - activeTab = activeTabs[0]; - } else if (activeTabs[length] > 1) { - activeTab = activeTabs[activeTabs[length] - 1]; - } - - return activeTab; - }, - getActiveContent = function () { - return queryElement(getActiveTab()[getAttribute]('href')); - }, - // handler - clickHandler = function (e) { - e[preventDefault](); - next = e[currentTarget]; - !tabs[isAnimating] && !hasClass(next, active) && self.show(); - }; // public method - - - this.show = function () { - // the tab we clicked is now the next tab - next = next || element; - nextContent = queryElement(next[getAttribute]('href')); //this is the actual object, the next tab content to activate - - activeTab = getActiveTab(); - activeContent = getActiveContent(); - tabs[isAnimating] = true; - removeClass(activeTab, active); - activeTab[setAttribute](ariaSelected, 'false'); - addClass(next, active); - next[setAttribute](ariaSelected, 'true'); - - if (dropdown) { - if (!hasClass(element[parentNode], 'dropdown-menu')) { - if (hasClass(dropdown, active)) removeClass(dropdown, active); - } else { - if (!hasClass(dropdown, active)) addClass(dropdown, active); - } - } - - bootstrapCustomEvent.call(activeTab, hideEvent, component, next); - - if (hasClass(activeContent, 'fade')) { - removeClass(activeContent, showClass); - emulateTransitionEnd(activeContent, triggerHide); - } else { - triggerHide(); - } - }; // init - - - if (!(stringTab in element)) { - // prevent adding event handlers twice - on(element, clickEvent, clickHandler); - } - - if (self[height]) { - tabsContentContainer = getActiveContent()[parentNode]; - } - - element[stringTab] = self; - }; // TAB DATA API - // ============ - - - supports[push]([stringTab, Tab, '[' + dataToggle + '="tab"]']); - /* Native Javascript for Bootstrap | Initialize Data API - --------------------------------------------------------*/ - - var initializeDataAPI = function (constructor, collection) { - for (var i = 0, l = collection[length]; i < l; i++) { - new constructor(collection[i]); - } - }, - initCallback = BSN.initCallback = function (lookUp) { - lookUp = lookUp || DOC; - - for (var i = 0, l = supports[length]; i < l; i++) { - initializeDataAPI(supports[i][1], lookUp[querySelectorAll](supports[i][2])); - } - }; // bulk initialize all components - - - DOC[body] ? initCallback() : on(DOC, 'DOMContentLoaded', function () { - initCallback(); - }); - return { - Alert: Alert, - Button: Button, - Collapse: Collapse, - Dropdown: Dropdown, - Modal: Modal, - Popover: Popover, - Tab: Tab - }; -}); - -/***/ }), - -/***/ 5820: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -__webpack_require__(964); - -var $Object = __webpack_require__(7913).Object; - -module.exports = function defineProperty(it, key, desc) { - return $Object.defineProperty(it, key, desc); -}; - -/***/ }), - -/***/ 3248: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -__webpack_require__(4180); - -module.exports = __webpack_require__(7913).Object.keys; - -/***/ }), - -/***/ 9830: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -__webpack_require__(6542); - -__webpack_require__(4815); - -__webpack_require__(8160); - -__webpack_require__(8314); - -__webpack_require__(8245); - -__webpack_require__(5953); - -module.exports = __webpack_require__(7913).Promise; - -/***/ }), - -/***/ 472: -/***/ ((module) => { - -module.exports = function (it) { - if (typeof it != 'function') throw TypeError(it + ' is not a function!'); - return it; -}; - -/***/ }), - -/***/ 5057: -/***/ ((module) => { - -module.exports = function () { - /* empty */ -}; - -/***/ }), - -/***/ 9945: -/***/ ((module) => { - -module.exports = function (it, Constructor, name, forbiddenField) { - if (!(it instanceof Constructor) || forbiddenField !== undefined && forbiddenField in it) { - throw TypeError(name + ': incorrect invocation!'); - } - - return it; -}; - -/***/ }), - -/***/ 7237: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var isObject = __webpack_require__(9900); - -module.exports = function (it) { - if (!isObject(it)) throw TypeError(it + ' is not an object!'); - return it; -}; - -/***/ }), - -/***/ 7097: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// false -> Array#indexOf -// true -> Array#includes -var toIObject = __webpack_require__(6591); - -var toLength = __webpack_require__(1872); - -var toAbsoluteIndex = __webpack_require__(8367); - -module.exports = function (IS_INCLUDES) { - return function ($this, el, fromIndex) { - var O = toIObject($this); - var length = toLength(O.length); - var index = toAbsoluteIndex(fromIndex, length); - var value; // Array#includes uses SameValueZero equality algorithm - // eslint-disable-next-line no-self-compare - - if (IS_INCLUDES && el != el) while (length > index) { - value = O[index++]; // eslint-disable-next-line no-self-compare - - if (value != value) return true; // Array#indexOf ignores holes, Array#includes - not - } else for (; length > index; index++) if (IS_INCLUDES || index in O) { - if (O[index] === el) return IS_INCLUDES || index || 0; - } - return !IS_INCLUDES && -1; - }; -}; - -/***/ }), - -/***/ 8874: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// getting tag from 19.1.3.6 Object.prototype.toString() -var cof = __webpack_require__(4606); - -var TAG = __webpack_require__(9506)('toStringTag'); // ES3 wrong here - - -var ARG = cof(function () { - return arguments; -}()) == 'Arguments'; // fallback for IE11 Script Access Denied error - -var tryGet = function (it, key) { - try { - return it[key]; - } catch (e) { - /* empty */ - } -}; - -module.exports = function (it) { - var O, T, B; - return it === undefined ? 'Undefined' : it === null ? 'Null' // @@toStringTag case - : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T // builtinTag case - : ARG ? cof(O) // ES3 arguments fallback - : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; -}; - -/***/ }), - -/***/ 4606: -/***/ ((module) => { - -var toString = {}.toString; - -module.exports = function (it) { - return toString.call(it).slice(8, -1); -}; - -/***/ }), - -/***/ 7913: -/***/ ((module) => { - -var core = module.exports = { - version: '2.6.12' -}; -if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef - -/***/ }), - -/***/ 8773: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// optional / simple context binding -var aFunction = __webpack_require__(472); - -module.exports = function (fn, that, length) { - aFunction(fn); - if (that === undefined) return fn; - - switch (length) { - case 1: - return function (a) { - return fn.call(that, a); - }; - - case 2: - return function (a, b) { - return fn.call(that, a, b); - }; - - case 3: - return function (a, b, c) { - return fn.call(that, a, b, c); - }; - } - - return function () - /* ...args */ - { - return fn.apply(that, arguments); - }; -}; - -/***/ }), - -/***/ 1011: -/***/ ((module) => { - -// 7.2.1 RequireObjectCoercible(argument) -module.exports = function (it) { - if (it == undefined) throw TypeError("Can't call method on " + it); - return it; -}; - -/***/ }), - -/***/ 6580: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// Thank's IE8 for his funny defineProperty -module.exports = !__webpack_require__(7287)(function () { - return Object.defineProperty({}, 'a', { - get: function () { - return 7; - } - }).a != 7; -}); - -/***/ }), - -/***/ 6590: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var isObject = __webpack_require__(9900); - -var document = __webpack_require__(5677).document; // typeof document.createElement is 'object' in old IE - - -var is = isObject(document) && isObject(document.createElement); - -module.exports = function (it) { - return is ? document.createElement(it) : {}; -}; - -/***/ }), - -/***/ 4330: -/***/ ((module) => { - -// IE 8- don't enum bug keys -module.exports = 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'.split(','); - -/***/ }), - -/***/ 9749: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var global = __webpack_require__(5677); - -var core = __webpack_require__(7913); - -var ctx = __webpack_require__(8773); - -var hide = __webpack_require__(51); - -var has = __webpack_require__(3945); - -var PROTOTYPE = 'prototype'; - -var $export = function (type, name, source) { - var IS_FORCED = type & $export.F; - var IS_GLOBAL = type & $export.G; - var IS_STATIC = type & $export.S; - var IS_PROTO = type & $export.P; - var IS_BIND = type & $export.B; - var IS_WRAP = type & $export.W; - var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); - var expProto = exports[PROTOTYPE]; - var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; - var key, own, out; - if (IS_GLOBAL) source = name; - - for (key in source) { - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if (own && has(exports, key)) continue; // export native or passed - - out = own ? target[key] : source[key]; // prevent global pollution for namespaces - - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? function (C) { - var F = function (a, b, c) { - if (this instanceof C) { - switch (arguments.length) { - case 0: - return new C(); - - case 1: - return new C(a); - - case 2: - return new C(a, b); - } - - return new C(a, b, c); - } - - return C.apply(this, arguments); - }; - - F[PROTOTYPE] = C[PROTOTYPE]; - return F; // make static versions for prototype methods - }(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - - if (IS_PROTO) { - (exports.virtual || (exports.virtual = {}))[key] = out; // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - - if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); - } - } -}; // type bitmap - - -$export.F = 1; // forced - -$export.G = 2; // global - -$export.S = 4; // static - -$export.P = 8; // proto - -$export.B = 16; // bind - -$export.W = 32; // wrap - -$export.U = 64; // safe - -$export.R = 128; // real proto method for `library` - -module.exports = $export; - -/***/ }), - -/***/ 7287: -/***/ ((module) => { - -module.exports = function (exec) { - try { - return !!exec(); - } catch (e) { - return true; - } -}; - -/***/ }), - -/***/ 5614: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var ctx = __webpack_require__(8773); - -var call = __webpack_require__(352); - -var isArrayIter = __webpack_require__(2887); - -var anObject = __webpack_require__(7237); - -var toLength = __webpack_require__(1872); - -var getIterFn = __webpack_require__(7670); - -var BREAK = {}; -var RETURN = {}; - -var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) { - var iterFn = ITERATOR ? function () { - return iterable; - } : getIterFn(iterable); - var f = ctx(fn, that, entries ? 2 : 1); - var index = 0; - var length, step, iterator, result; - if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!'); // fast case for arrays with default iterator - - if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) { - result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); - if (result === BREAK || result === RETURN) return result; - } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) { - result = call(iterator, f, step.value, entries); - if (result === BREAK || result === RETURN) return result; - } -}; - -exports.BREAK = BREAK; -exports.RETURN = RETURN; - -/***/ }), - -/***/ 5677: -/***/ ((module) => { - -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math ? window : typeof self != 'undefined' && self.Math == Math ? self // eslint-disable-next-line no-new-func -: Function('return this')(); -if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef - -/***/ }), - -/***/ 3945: -/***/ ((module) => { - -var hasOwnProperty = {}.hasOwnProperty; - -module.exports = function (it, key) { - return hasOwnProperty.call(it, key); -}; - -/***/ }), - -/***/ 51: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var dP = __webpack_require__(8301); - -var createDesc = __webpack_require__(7269); - -module.exports = __webpack_require__(6580) ? function (object, key, value) { - return dP.f(object, key, createDesc(1, value)); -} : function (object, key, value) { - object[key] = value; - return object; -}; - -/***/ }), - -/***/ 8387: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var document = __webpack_require__(5677).document; - -module.exports = document && document.documentElement; - -/***/ }), - -/***/ 1430: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = !__webpack_require__(6580) && !__webpack_require__(7287)(function () { - return Object.defineProperty(__webpack_require__(6590)('div'), 'a', { - get: function () { - return 7; - } - }).a != 7; -}); - -/***/ }), - -/***/ 2229: -/***/ ((module) => { - -// fast apply, http://jsperf.lnkit.com/fast-apply/5 -module.exports = function (fn, args, that) { - var un = that === undefined; - - switch (args.length) { - case 0: - return un ? fn() : fn.call(that); - - case 1: - return un ? fn(args[0]) : fn.call(that, args[0]); - - case 2: - return un ? fn(args[0], args[1]) : fn.call(that, args[0], args[1]); - - case 3: - return un ? fn(args[0], args[1], args[2]) : fn.call(that, args[0], args[1], args[2]); - - case 4: - return un ? fn(args[0], args[1], args[2], args[3]) : fn.call(that, args[0], args[1], args[2], args[3]); - } - - return fn.apply(that, args); -}; - -/***/ }), - -/***/ 9734: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// fallback for non-array-like ES3 and non-enumerable old V8 strings -var cof = __webpack_require__(4606); // eslint-disable-next-line no-prototype-builtins - - -module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { - return cof(it) == 'String' ? it.split('') : Object(it); -}; - -/***/ }), - -/***/ 2887: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// check on default Array iterator -var Iterators = __webpack_require__(3829); - -var ITERATOR = __webpack_require__(9506)('iterator'); - -var ArrayProto = Array.prototype; - -module.exports = function (it) { - return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); -}; - -/***/ }), - -/***/ 9900: -/***/ ((module) => { - -module.exports = function (it) { - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; - -/***/ }), - -/***/ 352: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// call something on iterator step with safe closing on error -var anObject = __webpack_require__(7237); - -module.exports = function (iterator, fn, value, entries) { - try { - return entries ? fn(anObject(value)[0], value[1]) : fn(value); // 7.4.6 IteratorClose(iterator, completion) - } catch (e) { - var ret = iterator['return']; - if (ret !== undefined) anObject(ret.call(iterator)); - throw e; - } -}; - -/***/ }), - -/***/ 4419: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var create = __webpack_require__(8933); - -var descriptor = __webpack_require__(7269); - -var setToStringTag = __webpack_require__(8536); - -var IteratorPrototype = {}; // 25.1.2.1.1 %IteratorPrototype%[@@iterator]() - -__webpack_require__(51)(IteratorPrototype, __webpack_require__(9506)('iterator'), function () { - return this; -}); - -module.exports = function (Constructor, NAME, next) { - Constructor.prototype = create(IteratorPrototype, { - next: descriptor(1, next) - }); - setToStringTag(Constructor, NAME + ' Iterator'); -}; - -/***/ }), - -/***/ 2361: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var LIBRARY = __webpack_require__(7560); - -var $export = __webpack_require__(9749); - -var redefine = __webpack_require__(836); - -var hide = __webpack_require__(51); - -var Iterators = __webpack_require__(3829); - -var $iterCreate = __webpack_require__(4419); - -var setToStringTag = __webpack_require__(8536); - -var getPrototypeOf = __webpack_require__(2008); - -var ITERATOR = __webpack_require__(9506)('iterator'); - -var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` - -var FF_ITERATOR = '@@iterator'; -var KEYS = 'keys'; -var VALUES = 'values'; - -var returnThis = function () { - return this; -}; - -module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { - $iterCreate(Constructor, NAME, next); - - var getMethod = function (kind) { - if (!BUGGY && kind in proto) return proto[kind]; - - switch (kind) { - case KEYS: - return function keys() { - return new Constructor(this, kind); - }; - - case VALUES: - return function values() { - return new Constructor(this, kind); - }; - } - - return function entries() { - return new Constructor(this, kind); - }; - }; - - var TAG = NAME + ' Iterator'; - var DEF_VALUES = DEFAULT == VALUES; - var VALUES_BUG = false; - var proto = Base.prototype; - var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; - var $default = $native || getMethod(DEFAULT); - var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; - var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; - var methods, key, IteratorPrototype; // Fix native - - if ($anyNative) { - IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); - - if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { - // Set @@toStringTag to native iterators - setToStringTag(IteratorPrototype, TAG, true); // fix for some old engines - - if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis); - } - } // fix Array#{values, @@iterator}.name in V8 / FF - - - if (DEF_VALUES && $native && $native.name !== VALUES) { - VALUES_BUG = true; - - $default = function values() { - return $native.call(this); - }; - } // Define iterator - - - if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { - hide(proto, ITERATOR, $default); - } // Plug for library - - - Iterators[NAME] = $default; - Iterators[TAG] = returnThis; - - if (DEFAULT) { - methods = { - values: DEF_VALUES ? $default : getMethod(VALUES), - keys: IS_SET ? $default : getMethod(KEYS), - entries: $entries - }; - if (FORCED) for (key in methods) { - if (!(key in proto)) redefine(proto, key, methods[key]); - } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); - } - - return methods; -}; - -/***/ }), - -/***/ 3540: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var ITERATOR = __webpack_require__(9506)('iterator'); - -var SAFE_CLOSING = false; - -try { - var riter = [7][ITERATOR](); - - riter['return'] = function () { - SAFE_CLOSING = true; - }; // eslint-disable-next-line no-throw-literal - - - Array.from(riter, function () { - throw 2; - }); -} catch (e) { - /* empty */ -} - -module.exports = function (exec, skipClosing) { - if (!skipClosing && !SAFE_CLOSING) return false; - var safe = false; - - try { - var arr = [7]; - var iter = arr[ITERATOR](); - - iter.next = function () { - return { - done: safe = true - }; - }; - - arr[ITERATOR] = function () { - return iter; - }; - - exec(arr); - } catch (e) { - /* empty */ - } - - return safe; -}; - -/***/ }), - -/***/ 4362: -/***/ ((module) => { - -module.exports = function (done, value) { - return { - value: value, - done: !!done - }; -}; - -/***/ }), - -/***/ 3829: -/***/ ((module) => { - -module.exports = {}; - -/***/ }), - -/***/ 7560: -/***/ ((module) => { - -module.exports = true; - -/***/ }), - -/***/ 7231: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var global = __webpack_require__(5677); - -var macrotask = __webpack_require__(8226).set; - -var Observer = global.MutationObserver || global.WebKitMutationObserver; -var process = global.process; -var Promise = global.Promise; -var isNode = __webpack_require__(4606)(process) == 'process'; - -module.exports = function () { - var head, last, notify; - - var flush = function () { - var parent, fn; - if (isNode && (parent = process.domain)) parent.exit(); - - while (head) { - fn = head.fn; - head = head.next; - - try { - fn(); - } catch (e) { - if (head) notify();else last = undefined; - throw e; - } - } - - last = undefined; - if (parent) parent.enter(); - }; // Node.js - - - if (isNode) { - notify = function () { - process.nextTick(flush); - }; // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339 - - } else if (Observer && !(global.navigator && global.navigator.standalone)) { - var toggle = true; - var node = document.createTextNode(''); - new Observer(flush).observe(node, { - characterData: true - }); // eslint-disable-line no-new - - notify = function () { - node.data = toggle = !toggle; - }; // environments with maybe non-completely correct, but existent Promise - - } else if (Promise && Promise.resolve) { - // Promise.resolve without an argument throws an error in LG WebOS 2 - var promise = Promise.resolve(undefined); - - notify = function () { - promise.then(flush); - }; // for other environments - macrotask based on: - // - setImmediate - // - MessageChannel - // - window.postMessag - // - onreadystatechange - // - setTimeout - - } else { - notify = function () { - // strange IE + webpack dev server bug - use .call(global) - macrotask.call(global, flush); - }; - } - - return function (fn) { - var task = { - fn: fn, - next: undefined - }; - if (last) last.next = task; - - if (!head) { - head = task; - notify(); - } - - last = task; - }; -}; - -/***/ }), - -/***/ 7789: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - // 25.4.1.5 NewPromiseCapability(C) - -var aFunction = __webpack_require__(472); - -function PromiseCapability(C) { - var resolve, reject; - this.promise = new C(function ($$resolve, $$reject) { - if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); - resolve = $$resolve; - reject = $$reject; - }); - this.resolve = aFunction(resolve); - this.reject = aFunction(reject); -} - -module.exports.f = function (C) { - return new PromiseCapability(C); -}; - -/***/ }), - -/***/ 8933: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) -var anObject = __webpack_require__(7237); - -var dPs = __webpack_require__(8138); - -var enumBugKeys = __webpack_require__(4330); - -var IE_PROTO = __webpack_require__(4261)('IE_PROTO'); - -var Empty = function () { - /* empty */ -}; - -var PROTOTYPE = 'prototype'; // Create object with fake `null` prototype: use iframe Object with cleared prototype - -var createDict = function () { - // Thrash, waste and sodomy: IE GC bug - var iframe = __webpack_require__(6590)('iframe'); - - var i = enumBugKeys.length; - var lt = '<'; - var gt = '>'; - var iframeDocument; - iframe.style.display = 'none'; - - __webpack_require__(8387).appendChild(iframe); - - iframe.src = 'javascript:'; // eslint-disable-line no-script-url - // createDict = iframe.contentWindow.Object; - // html.removeChild(iframe); - - iframeDocument = iframe.contentWindow.document; - iframeDocument.open(); - iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); - iframeDocument.close(); - createDict = iframeDocument.F; - - while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; - - return createDict(); -}; - -module.exports = Object.create || function create(O, Properties) { - var result; - - if (O !== null) { - Empty[PROTOTYPE] = anObject(O); - result = new Empty(); - Empty[PROTOTYPE] = null; // add "__proto__" for Object.getPrototypeOf polyfill - - result[IE_PROTO] = O; - } else result = createDict(); - - return Properties === undefined ? result : dPs(result, Properties); -}; - -/***/ }), - -/***/ 8301: -/***/ ((__unused_webpack_module, exports, __webpack_require__) => { - -var anObject = __webpack_require__(7237); - -var IE8_DOM_DEFINE = __webpack_require__(1430); - -var toPrimitive = __webpack_require__(3216); - -var dP = Object.defineProperty; -exports.f = __webpack_require__(6580) ? Object.defineProperty : function defineProperty(O, P, Attributes) { - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if (IE8_DOM_DEFINE) try { - return dP(O, P, Attributes); - } catch (e) { - /* empty */ - } - if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); - if ('value' in Attributes) O[P] = Attributes.value; - return O; -}; - -/***/ }), - -/***/ 8138: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var dP = __webpack_require__(8301); - -var anObject = __webpack_require__(7237); - -var getKeys = __webpack_require__(1302); - -module.exports = __webpack_require__(6580) ? Object.defineProperties : function defineProperties(O, Properties) { - anObject(O); - var keys = getKeys(Properties); - var length = keys.length; - var i = 0; - var P; - - while (length > i) dP.f(O, P = keys[i++], Properties[P]); - - return O; -}; - -/***/ }), - -/***/ 2008: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) -var has = __webpack_require__(3945); - -var toObject = __webpack_require__(6581); - -var IE_PROTO = __webpack_require__(4261)('IE_PROTO'); - -var ObjectProto = Object.prototype; - -module.exports = Object.getPrototypeOf || function (O) { - O = toObject(O); - if (has(O, IE_PROTO)) return O[IE_PROTO]; - - if (typeof O.constructor == 'function' && O instanceof O.constructor) { - return O.constructor.prototype; - } - - return O instanceof Object ? ObjectProto : null; -}; - -/***/ }), - -/***/ 9483: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var has = __webpack_require__(3945); - -var toIObject = __webpack_require__(6591); - -var arrayIndexOf = __webpack_require__(7097)(false); - -var IE_PROTO = __webpack_require__(4261)('IE_PROTO'); - -module.exports = function (object, names) { - var O = toIObject(object); - var i = 0; - var result = []; - var key; - - for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); // Don't enum bug & hidden keys - - - while (names.length > i) if (has(O, key = names[i++])) { - ~arrayIndexOf(result, key) || result.push(key); - } - - return result; -}; - -/***/ }), - -/***/ 1302: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = __webpack_require__(9483); - -var enumBugKeys = __webpack_require__(4330); - -module.exports = Object.keys || function keys(O) { - return $keys(O, enumBugKeys); -}; - -/***/ }), - -/***/ 8438: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// most Object methods by ES6 should accept primitives -var $export = __webpack_require__(9749); - -var core = __webpack_require__(7913); - -var fails = __webpack_require__(7287); - -module.exports = function (KEY, exec) { - var fn = (core.Object || {})[KEY] || Object[KEY]; - var exp = {}; - exp[KEY] = exec(fn); - $export($export.S + $export.F * fails(function () { - fn(1); - }), 'Object', exp); -}; - -/***/ }), - -/***/ 5472: -/***/ ((module) => { - -module.exports = function (exec) { - try { - return { - e: false, - v: exec() - }; - } catch (e) { - return { - e: true, - v: e - }; - } -}; - -/***/ }), - -/***/ 3243: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var anObject = __webpack_require__(7237); - -var isObject = __webpack_require__(9900); - -var newPromiseCapability = __webpack_require__(7789); - -module.exports = function (C, x) { - anObject(C); - if (isObject(x) && x.constructor === C) return x; - var promiseCapability = newPromiseCapability.f(C); - var resolve = promiseCapability.resolve; - resolve(x); - return promiseCapability.promise; -}; - -/***/ }), - -/***/ 7269: -/***/ ((module) => { - -module.exports = function (bitmap, value) { - return { - enumerable: !(bitmap & 1), - configurable: !(bitmap & 2), - writable: !(bitmap & 4), - value: value - }; -}; - -/***/ }), - -/***/ 2853: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var hide = __webpack_require__(51); - -module.exports = function (target, src, safe) { - for (var key in src) { - if (safe && target[key]) target[key] = src[key];else hide(target, key, src[key]); - } - - return target; -}; - -/***/ }), - -/***/ 836: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -module.exports = __webpack_require__(51); - -/***/ }), - -/***/ 5003: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var global = __webpack_require__(5677); - -var core = __webpack_require__(7913); - -var dP = __webpack_require__(8301); - -var DESCRIPTORS = __webpack_require__(6580); - -var SPECIES = __webpack_require__(9506)('species'); - -module.exports = function (KEY) { - var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; - if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, { - configurable: true, - get: function () { - return this; - } - }); -}; - -/***/ }), - -/***/ 8536: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var def = __webpack_require__(8301).f; - -var has = __webpack_require__(3945); - -var TAG = __webpack_require__(9506)('toStringTag'); - -module.exports = function (it, tag, stat) { - if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { - configurable: true, - value: tag - }); -}; - -/***/ }), - -/***/ 4261: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var shared = __webpack_require__(7626)('keys'); - -var uid = __webpack_require__(1497); - -module.exports = function (key) { - return shared[key] || (shared[key] = uid(key)); -}; - -/***/ }), - -/***/ 7626: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var core = __webpack_require__(7913); - -var global = __webpack_require__(5677); - -var SHARED = '__core-js_shared__'; -var store = global[SHARED] || (global[SHARED] = {}); -(module.exports = function (key, value) { - return store[key] || (store[key] = value !== undefined ? value : {}); -})('versions', []).push({ - version: core.version, - mode: __webpack_require__(7560) ? 'pure' : 'global', - copyright: '© 2020 Denis Pushkarev (zloirock.ru)' -}); - -/***/ }), - -/***/ 6647: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 7.3.20 SpeciesConstructor(O, defaultConstructor) -var anObject = __webpack_require__(7237); - -var aFunction = __webpack_require__(472); - -var SPECIES = __webpack_require__(9506)('species'); - -module.exports = function (O, D) { - var C = anObject(O).constructor; - var S; - return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); -}; - -/***/ }), - -/***/ 4757: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var toInteger = __webpack_require__(1517); - -var defined = __webpack_require__(1011); // true -> String#at -// false -> String#codePointAt - - -module.exports = function (TO_STRING) { - return function (that, pos) { - var s = String(defined(that)); - var i = toInteger(pos); - var l = s.length; - var a, b; - if (i < 0 || i >= l) return TO_STRING ? '' : undefined; - a = s.charCodeAt(i); - return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff ? TO_STRING ? s.charAt(i) : a : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; - }; -}; - -/***/ }), - -/***/ 8226: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var ctx = __webpack_require__(8773); - -var invoke = __webpack_require__(2229); - -var html = __webpack_require__(8387); - -var cel = __webpack_require__(6590); - -var global = __webpack_require__(5677); - -var process = global.process; -var setTask = global.setImmediate; -var clearTask = global.clearImmediate; -var MessageChannel = global.MessageChannel; -var Dispatch = global.Dispatch; -var counter = 0; -var queue = {}; -var ONREADYSTATECHANGE = 'onreadystatechange'; -var defer, channel, port; - -var run = function () { - var id = +this; // eslint-disable-next-line no-prototype-builtins - - if (queue.hasOwnProperty(id)) { - var fn = queue[id]; - delete queue[id]; - fn(); - } -}; - -var listener = function (event) { - run.call(event.data); -}; // Node.js 0.9+ & IE10+ has setImmediate, otherwise: - - -if (!setTask || !clearTask) { - setTask = function setImmediate(fn) { - var args = []; - var i = 1; - - while (arguments.length > i) args.push(arguments[i++]); - - queue[++counter] = function () { - // eslint-disable-next-line no-new-func - invoke(typeof fn == 'function' ? fn : Function(fn), args); - }; - - defer(counter); - return counter; - }; - - clearTask = function clearImmediate(id) { - delete queue[id]; - }; // Node.js 0.8- - - - if (__webpack_require__(4606)(process) == 'process') { - defer = function (id) { - process.nextTick(ctx(run, id, 1)); - }; // Sphere (JS game engine) Dispatch API - - } else if (Dispatch && Dispatch.now) { - defer = function (id) { - Dispatch.now(ctx(run, id, 1)); - }; // Browsers with MessageChannel, includes WebWorkers - - } else if (MessageChannel) { - channel = new MessageChannel(); - port = channel.port2; - channel.port1.onmessage = listener; - defer = ctx(port.postMessage, port, 1); // Browsers with postMessage, skip WebWorkers - // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' - } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) { - defer = function (id) { - global.postMessage(id + '', '*'); - }; - - global.addEventListener('message', listener, false); // IE8- - } else if (ONREADYSTATECHANGE in cel('script')) { - defer = function (id) { - html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () { - html.removeChild(this); - run.call(id); - }; - }; // Rest old browsers - - } else { - defer = function (id) { - setTimeout(ctx(run, id, 1), 0); - }; - } -} - -module.exports = { - set: setTask, - clear: clearTask -}; - -/***/ }), - -/***/ 8367: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var toInteger = __webpack_require__(1517); - -var max = Math.max; -var min = Math.min; - -module.exports = function (index, length) { - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); -}; - -/***/ }), - -/***/ 1517: -/***/ ((module) => { - -// 7.1.4 ToInteger -var ceil = Math.ceil; -var floor = Math.floor; - -module.exports = function (it) { - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); -}; - -/***/ }), - -/***/ 6591: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = __webpack_require__(9734); - -var defined = __webpack_require__(1011); - -module.exports = function (it) { - return IObject(defined(it)); -}; - -/***/ }), - -/***/ 1872: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 7.1.15 ToLength -var toInteger = __webpack_require__(1517); - -var min = Math.min; - -module.exports = function (it) { - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 -}; - -/***/ }), - -/***/ 6581: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 7.1.13 ToObject(argument) -var defined = __webpack_require__(1011); - -module.exports = function (it) { - return Object(defined(it)); -}; - -/***/ }), - -/***/ 3216: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -// 7.1.1 ToPrimitive(input [, PreferredType]) -var isObject = __webpack_require__(9900); // instead of the ES6 spec version, we didn't implement @@toPrimitive case -// and the second argument - flag - preferred type is a string - - -module.exports = function (it, S) { - if (!isObject(it)) return it; - var fn, val; - if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; - if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - throw TypeError("Can't convert object to primitive value"); -}; - -/***/ }), - -/***/ 1497: -/***/ ((module) => { - -var id = 0; -var px = Math.random(); - -module.exports = function (key) { - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); -}; - -/***/ }), - -/***/ 8877: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var global = __webpack_require__(5677); - -var navigator = global.navigator; -module.exports = navigator && navigator.userAgent || ''; - -/***/ }), - -/***/ 9506: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var store = __webpack_require__(7626)('wks'); - -var uid = __webpack_require__(1497); - -var Symbol = __webpack_require__(5677).Symbol; - -var USE_SYMBOL = typeof Symbol == 'function'; - -var $exports = module.exports = function (name) { - return store[name] || (store[name] = USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); -}; - -$exports.store = store; - -/***/ }), - -/***/ 7670: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var classof = __webpack_require__(8874); - -var ITERATOR = __webpack_require__(9506)('iterator'); - -var Iterators = __webpack_require__(3829); - -module.exports = __webpack_require__(7913).getIteratorMethod = function (it) { - if (it != undefined) return it[ITERATOR] || it['@@iterator'] || Iterators[classof(it)]; -}; - -/***/ }), - -/***/ 66: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var addToUnscopables = __webpack_require__(5057); - -var step = __webpack_require__(4362); - -var Iterators = __webpack_require__(3829); - -var toIObject = __webpack_require__(6591); // 22.1.3.4 Array.prototype.entries() -// 22.1.3.13 Array.prototype.keys() -// 22.1.3.29 Array.prototype.values() -// 22.1.3.30 Array.prototype[@@iterator]() - - -module.exports = __webpack_require__(2361)(Array, 'Array', function (iterated, kind) { - this._t = toIObject(iterated); // target - - this._i = 0; // next index - - this._k = kind; // kind - // 22.1.5.2.1 %ArrayIteratorPrototype%.next() -}, function () { - var O = this._t; - var kind = this._k; - var index = this._i++; - - if (!O || index >= O.length) { - this._t = undefined; - return step(1); - } - - if (kind == 'keys') return step(0, index); - if (kind == 'values') return step(0, O[index]); - return step(0, [index, O[index]]); -}, 'values'); // argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) - -Iterators.Arguments = Iterators.Array; -addToUnscopables('keys'); -addToUnscopables('values'); -addToUnscopables('entries'); - -/***/ }), - -/***/ 964: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -var $export = __webpack_require__(9749); // 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes) - - -$export($export.S + $export.F * !__webpack_require__(6580), 'Object', { - defineProperty: __webpack_require__(8301).f -}); - -/***/ }), - -/***/ 4180: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -// 19.1.2.14 Object.keys(O) -var toObject = __webpack_require__(6581); - -var $keys = __webpack_require__(1302); - -__webpack_require__(8438)('keys', function () { - return function keys(it) { - return $keys(toObject(it)); - }; -}); - -/***/ }), - -/***/ 6542: -/***/ (() => { - - - -/***/ }), - -/***/ 8314: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var LIBRARY = __webpack_require__(7560); - -var global = __webpack_require__(5677); - -var ctx = __webpack_require__(8773); - -var classof = __webpack_require__(8874); - -var $export = __webpack_require__(9749); - -var isObject = __webpack_require__(9900); - -var aFunction = __webpack_require__(472); - -var anInstance = __webpack_require__(9945); - -var forOf = __webpack_require__(5614); - -var speciesConstructor = __webpack_require__(6647); - -var task = __webpack_require__(8226).set; - -var microtask = __webpack_require__(7231)(); - -var newPromiseCapabilityModule = __webpack_require__(7789); - -var perform = __webpack_require__(5472); - -var userAgent = __webpack_require__(8877); - -var promiseResolve = __webpack_require__(3243); - -var PROMISE = 'Promise'; -var TypeError = global.TypeError; -var process = global.process; -var versions = process && process.versions; -var v8 = versions && versions.v8 || ''; -var $Promise = global[PROMISE]; -var isNode = classof(process) == 'process'; - -var empty = function () { - /* empty */ -}; - -var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; -var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; -var USE_NATIVE = !!function () { - try { - // correct subclassing with @@species support - var promise = $Promise.resolve(1); - - var FakePromise = (promise.constructor = {})[__webpack_require__(9506)('species')] = function (exec) { - exec(empty, empty); - }; // unhandled rejections tracking support, NodeJS Promise without it fails @@species test - - - return (isNode || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables - // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 - // we can't detect it synchronously, so just check versions - && v8.indexOf('6.6') !== 0 && userAgent.indexOf('Chrome/66') === -1; - } catch (e) { - /* empty */ - } -}(); // helpers - -var isThenable = function (it) { - var then; - return isObject(it) && typeof (then = it.then) == 'function' ? then : false; -}; - -var notify = function (promise, isReject) { - if (promise._n) return; - promise._n = true; - var chain = promise._c; - microtask(function () { - var value = promise._v; - var ok = promise._s == 1; - var i = 0; - - var run = function (reaction) { - var handler = ok ? reaction.ok : reaction.fail; - var resolve = reaction.resolve; - var reject = reaction.reject; - var domain = reaction.domain; - var result, then, exited; - - try { - if (handler) { - if (!ok) { - if (promise._h == 2) onHandleUnhandled(promise); - promise._h = 1; - } - - if (handler === true) result = value;else { - if (domain) domain.enter(); - result = handler(value); // may throw - - if (domain) { - domain.exit(); - exited = true; - } - } - - if (result === reaction.promise) { - reject(TypeError('Promise-chain cycle')); - } else if (then = isThenable(result)) { - then.call(result, resolve, reject); - } else resolve(result); - } else reject(value); - } catch (e) { - if (domain && !exited) domain.exit(); - reject(e); - } - }; - - while (chain.length > i) run(chain[i++]); // variable length - can't use forEach - - - promise._c = []; - promise._n = false; - if (isReject && !promise._h) onUnhandled(promise); - }); -}; - -var onUnhandled = function (promise) { - task.call(global, function () { - var value = promise._v; - var unhandled = isUnhandled(promise); - var result, handler, console; - - if (unhandled) { - result = perform(function () { - if (isNode) { - process.emit('unhandledRejection', value, promise); - } else if (handler = global.onunhandledrejection) { - handler({ - promise: promise, - reason: value - }); - } else if ((console = global.console) && console.error) { - console.error('Unhandled promise rejection', value); - } - }); // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should - - promise._h = isNode || isUnhandled(promise) ? 2 : 1; - } - - promise._a = undefined; - if (unhandled && result.e) throw result.v; - }); -}; - -var isUnhandled = function (promise) { - return promise._h !== 1 && (promise._a || promise._c).length === 0; -}; - -var onHandleUnhandled = function (promise) { - task.call(global, function () { - var handler; - - if (isNode) { - process.emit('rejectionHandled', promise); - } else if (handler = global.onrejectionhandled) { - handler({ - promise: promise, - reason: promise._v - }); - } - }); -}; - -var $reject = function (value) { - var promise = this; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap - - promise._v = value; - promise._s = 2; - if (!promise._a) promise._a = promise._c.slice(); - notify(promise, true); -}; - -var $resolve = function (value) { - var promise = this; - var then; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap - - try { - if (promise === value) throw TypeError("Promise can't be resolved itself"); - - if (then = isThenable(value)) { - microtask(function () { - var wrapper = { - _w: promise, - _d: false - }; // wrap - - try { - then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); - } catch (e) { - $reject.call(wrapper, e); - } - }); - } else { - promise._v = value; - promise._s = 1; - notify(promise, false); - } - } catch (e) { - $reject.call({ - _w: promise, - _d: false - }, e); // wrap - } -}; // constructor polyfill - - -if (!USE_NATIVE) { - // 25.4.3.1 Promise(executor) - $Promise = function Promise(executor) { - anInstance(this, $Promise, PROMISE, '_h'); - aFunction(executor); - Internal.call(this); - - try { - executor(ctx($resolve, this, 1), ctx($reject, this, 1)); - } catch (err) { - $reject.call(this, err); - } - }; // eslint-disable-next-line no-unused-vars - - - Internal = function Promise(executor) { - this._c = []; // <- awaiting reactions - - this._a = undefined; // <- checked in isUnhandled reactions - - this._s = 0; // <- state - - this._d = false; // <- done - - this._v = undefined; // <- value - - this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled - - this._n = false; // <- notify - }; - - Internal.prototype = __webpack_require__(2853)($Promise.prototype, { - // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) - then: function then(onFulfilled, onRejected) { - var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); - reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; - reaction.fail = typeof onRejected == 'function' && onRejected; - reaction.domain = isNode ? process.domain : undefined; - - this._c.push(reaction); - - if (this._a) this._a.push(reaction); - if (this._s) notify(this, false); - return reaction.promise; - }, - // 25.4.5.1 Promise.prototype.catch(onRejected) - 'catch': function (onRejected) { - return this.then(undefined, onRejected); - } - }); - - OwnPromiseCapability = function () { - var promise = new Internal(); - this.promise = promise; - this.resolve = ctx($resolve, promise, 1); - this.reject = ctx($reject, promise, 1); - }; - - newPromiseCapabilityModule.f = newPromiseCapability = function (C) { - return C === $Promise || C === Wrapper ? new OwnPromiseCapability(C) : newGenericPromiseCapability(C); - }; -} - -$export($export.G + $export.W + $export.F * !USE_NATIVE, { - Promise: $Promise -}); - -__webpack_require__(8536)($Promise, PROMISE); - -__webpack_require__(5003)(PROMISE); - -Wrapper = __webpack_require__(7913)[PROMISE]; // statics - -$export($export.S + $export.F * !USE_NATIVE, PROMISE, { - // 25.4.4.5 Promise.reject(r) - reject: function reject(r) { - var capability = newPromiseCapability(this); - var $$reject = capability.reject; - $$reject(r); - return capability.promise; - } -}); -$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { - // 25.4.4.6 Promise.resolve(x) - resolve: function resolve(x) { - return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x); - } -}); -$export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(3540)(function (iter) { - $Promise.all(iter)['catch'](empty); -})), PROMISE, { - // 25.4.4.1 Promise.all(iterable) - all: function all(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var resolve = capability.resolve; - var reject = capability.reject; - var result = perform(function () { - var values = []; - var index = 0; - var remaining = 1; - forOf(iterable, false, function (promise) { - var $index = index++; - var alreadyCalled = false; - values.push(undefined); - remaining++; - C.resolve(promise).then(function (value) { - if (alreadyCalled) return; - alreadyCalled = true; - values[$index] = value; - --remaining || resolve(values); - }, reject); - }); - --remaining || resolve(values); - }); - if (result.e) reject(result.v); - return capability.promise; - }, - // 25.4.4.4 Promise.race(iterable) - race: function race(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var reject = capability.reject; - var result = perform(function () { - forOf(iterable, false, function (promise) { - C.resolve(promise).then(capability.resolve, reject); - }); - }); - if (result.e) reject(result.v); - return capability.promise; - } -}); - -/***/ }), - -/***/ 4815: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -var $at = __webpack_require__(4757)(true); // 21.1.3.27 String.prototype[@@iterator]() - - -__webpack_require__(2361)(String, 'String', function (iterated) { - this._t = String(iterated); // target - - this._i = 0; // next index - // 21.1.5.2.1 %StringIteratorPrototype%.next() -}, function () { - var O = this._t; - var index = this._i; - var point; - if (index >= O.length) return { - value: undefined, - done: true - }; - point = $at(O, index); - this._i += point.length; - return { - value: point, - done: false - }; -}); - -/***/ }), - -/***/ 8245: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; -// https://github.com/tc39/proposal-promise-finally - - -var $export = __webpack_require__(9749); - -var core = __webpack_require__(7913); - -var global = __webpack_require__(5677); - -var speciesConstructor = __webpack_require__(6647); - -var promiseResolve = __webpack_require__(3243); - -$export($export.P + $export.R, 'Promise', { - 'finally': function (onFinally) { - var C = speciesConstructor(this, core.Promise || global.Promise); - var isFunction = typeof onFinally == 'function'; - return this.then(isFunction ? function (x) { - return promiseResolve(C, onFinally()).then(function () { - return x; - }); - } : onFinally, isFunction ? function (e) { - return promiseResolve(C, onFinally()).then(function () { - throw e; - }); - } : onFinally); - } -}); - -/***/ }), - -/***/ 5953: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - // https://github.com/tc39/proposal-promise-try - -var $export = __webpack_require__(9749); - -var newPromiseCapability = __webpack_require__(7789); - -var perform = __webpack_require__(5472); - -$export($export.S, 'Promise', { - 'try': function (callbackfn) { - var promiseCapability = newPromiseCapability.f(this); - var result = perform(callbackfn); - (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v); - return promiseCapability.promise; - } -}); - -/***/ }), - -/***/ 8160: -/***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { - -__webpack_require__(66); - -var global = __webpack_require__(5677); - -var hide = __webpack_require__(51); - -var Iterators = __webpack_require__(3829); - -var TO_STRING_TAG = __webpack_require__(9506)('toStringTag'); - -var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + 'TextTrackList,TouchList').split(','); - -for (var i = 0; i < DOMIterables.length; i++) { - var NAME = DOMIterables[i]; - var Collection = global[NAME]; - var proto = Collection && Collection.prototype; - if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); - Iterators[NAME] = Iterators.Array; -} - -/***/ }), - -/***/ 1246: -/***/ ((module) => { - -"use strict"; - -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -// css base code, injected by the css-loader -// eslint-disable-next-line func-names - -module.exports = function (cssWithMappingToString) { - var list = []; // return the list of modules as css string - - list.toString = function toString() { - return this.map(function (item) { - var content = cssWithMappingToString(item); - - if (item[2]) { - return "@media ".concat(item[2], " {").concat(content, "}"); - } - - return content; - }).join(""); - }; // import a list of modules into the list - // eslint-disable-next-line func-names - - - list.i = function (modules, mediaQuery, dedupe) { - if (typeof modules === "string") { - // eslint-disable-next-line no-param-reassign - modules = [[null, modules, ""]]; - } - - var alreadyImportedModules = {}; - - if (dedupe) { - for (var i = 0; i < this.length; i++) { - // eslint-disable-next-line prefer-destructuring - var id = this[i][0]; - - if (id != null) { - alreadyImportedModules[id] = true; - } - } - } - - for (var _i = 0; _i < modules.length; _i++) { - var item = [].concat(modules[_i]); - - if (dedupe && alreadyImportedModules[item[0]]) { - // eslint-disable-next-line no-continue - continue; - } - - if (mediaQuery) { - if (!item[2]) { - item[2] = mediaQuery; - } else { - item[2] = "".concat(mediaQuery, " and ").concat(item[2]); - } - } - - list.push(item); - } - }; - - return list; -}; - -/***/ }), - -/***/ 7620: -/***/ ((module) => { - -"use strict"; - - -function _slicedToArray(arr, i) { - return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); -} - -function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); -} - -function _unsupportedIterableToArray(o, minLen) { - if (!o) return; - if (typeof o === "string") return _arrayLikeToArray(o, minLen); - var n = Object.prototype.toString.call(o).slice(8, -1); - if (n === "Object" && o.constructor) n = o.constructor.name; - if (n === "Map" || n === "Set") return Array.from(o); - if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); -} - -function _arrayLikeToArray(arr, len) { - if (len == null || len > arr.length) len = arr.length; - - for (var i = 0, arr2 = new Array(len); i < len; i++) { - arr2[i] = arr[i]; - } - - return arr2; -} - -function _iterableToArrayLimit(arr, i) { - var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); - - if (_i == null) return; - var _arr = []; - var _n = true; - var _d = false; - - var _s, _e; - - try { - for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { - _arr.push(_s.value); - - if (i && _arr.length === i) break; - } - } catch (err) { - _d = true; - _e = err; - } finally { - try { - if (!_n && _i["return"] != null) _i["return"](); - } finally { - if (_d) throw _e; - } - } - - return _arr; -} - -function _arrayWithHoles(arr) { - if (Array.isArray(arr)) return arr; -} - -module.exports = function cssWithMappingToString(item) { - var _item = _slicedToArray(item, 4), - content = _item[1], - cssMapping = _item[3]; - - if (!cssMapping) { - return content; - } - - if (typeof btoa === "function") { - // eslint-disable-next-line no-undef - var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping)))); - var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64); - var sourceMapping = "/*# ".concat(data, " */"); - var sourceURLs = cssMapping.sources.map(function (source) { - return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */"); - }); - return [content].concat(sourceURLs).concat([sourceMapping]).join("\n"); - } - - return [content].join("\n"); -}; - -/***/ }), - -/***/ 8169: -/***/ (function(module) { - -!function (t, e) { - true ? module.exports = e() : 0; -}(this, function () { - "use strict"; - - var t = 1e3, - e = 6e4, - n = 36e5, - r = "millisecond", - i = "second", - s = "minute", - u = "hour", - a = "day", - o = "week", - f = "month", - h = "quarter", - c = "year", - d = "date", - $ = "Invalid Date", - l = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, - y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, - M = { - name: "en", - weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), - months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") - }, - m = function (t, e, n) { - var r = String(t); - return !r || r.length >= e ? t : "" + Array(e + 1 - r.length).join(n) + t; - }, - g = { - s: m, - z: function (t) { - var e = -t.utcOffset(), - n = Math.abs(e), - r = Math.floor(n / 60), - i = n % 60; - return (e <= 0 ? "+" : "-") + m(r, 2, "0") + ":" + m(i, 2, "0"); - }, - m: function t(e, n) { - if (e.date() < n.date()) return -t(n, e); - var r = 12 * (n.year() - e.year()) + (n.month() - e.month()), - i = e.clone().add(r, f), - s = n - i < 0, - u = e.clone().add(r + (s ? -1 : 1), f); - return +(-(r + (n - i) / (s ? i - u : u - i)) || 0); - }, - a: function (t) { - return t < 0 ? Math.ceil(t) || 0 : Math.floor(t); - }, - p: function (t) { - return { - M: f, - y: c, - w: o, - d: a, - D: d, - h: u, - m: s, - s: i, - ms: r, - Q: h - }[t] || String(t || "").toLowerCase().replace(/s$/, ""); - }, - u: function (t) { - return void 0 === t; - } - }, - D = "en", - v = {}; - - v[D] = M; - - var p = function (t) { - return t instanceof _; - }, - S = function (t, e, n) { - var r; - if (!t) return D; - if ("string" == typeof t) v[t] && (r = t), e && (v[t] = e, r = t);else { - var i = t.name; - v[i] = t, r = i; - } - return !n && r && (D = r), r || !n && D; - }, - w = function (t, e) { - if (p(t)) return t.clone(); - var n = "object" == typeof e ? e : {}; - return n.date = t, n.args = arguments, new _(n); - }, - O = g; - - O.l = S, O.i = p, O.w = function (t, e) { - return w(t, { - locale: e.$L, - utc: e.$u, - x: e.$x, - $offset: e.$offset - }); - }; - - var _ = function () { - function M(t) { - this.$L = S(t.locale, null, !0), this.parse(t); - } - - var m = M.prototype; - return m.parse = function (t) { - this.$d = function (t) { - var e = t.date, - n = t.utc; - if (null === e) return new Date(NaN); - if (O.u(e)) return new Date(); - if (e instanceof Date) return new Date(e); - - if ("string" == typeof e && !/Z$/i.test(e)) { - var r = e.match(l); - - if (r) { - var i = r[2] - 1 || 0, - s = (r[7] || "0").substring(0, 3); - return n ? new Date(Date.UTC(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s)) : new Date(r[1], i, r[3] || 1, r[4] || 0, r[5] || 0, r[6] || 0, s); - } - } - - return new Date(e); - }(t), this.$x = t.x || {}, this.init(); - }, m.init = function () { - var t = this.$d; - this.$y = t.getFullYear(), this.$M = t.getMonth(), this.$D = t.getDate(), this.$W = t.getDay(), this.$H = t.getHours(), this.$m = t.getMinutes(), this.$s = t.getSeconds(), this.$ms = t.getMilliseconds(); - }, m.$utils = function () { - return O; - }, m.isValid = function () { - return !(this.$d.toString() === $); - }, m.isSame = function (t, e) { - var n = w(t); - return this.startOf(e) <= n && n <= this.endOf(e); - }, m.isAfter = function (t, e) { - return w(t) < this.startOf(e); - }, m.isBefore = function (t, e) { - return this.endOf(e) < w(t); - }, m.$g = function (t, e, n) { - return O.u(t) ? this[e] : this.set(n, t); - }, m.unix = function () { - return Math.floor(this.valueOf() / 1e3); - }, m.valueOf = function () { - return this.$d.getTime(); - }, m.startOf = function (t, e) { - var n = this, - r = !!O.u(e) || e, - h = O.p(t), - $ = function (t, e) { - var i = O.w(n.$u ? Date.UTC(n.$y, e, t) : new Date(n.$y, e, t), n); - return r ? i : i.endOf(a); - }, - l = function (t, e) { - return O.w(n.toDate()[t].apply(n.toDate("s"), (r ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e)), n); - }, - y = this.$W, - M = this.$M, - m = this.$D, - g = "set" + (this.$u ? "UTC" : ""); - - switch (h) { - case c: - return r ? $(1, 0) : $(31, 11); - - case f: - return r ? $(1, M) : $(0, M + 1); - - case o: - var D = this.$locale().weekStart || 0, - v = (y < D ? y + 7 : y) - D; - return $(r ? m - v : m + (6 - v), M); - - case a: - case d: - return l(g + "Hours", 0); - - case u: - return l(g + "Minutes", 1); - - case s: - return l(g + "Seconds", 2); - - case i: - return l(g + "Milliseconds", 3); - - default: - return this.clone(); - } - }, m.endOf = function (t) { - return this.startOf(t, !1); - }, m.$set = function (t, e) { - var n, - o = O.p(t), - h = "set" + (this.$u ? "UTC" : ""), - $ = (n = {}, n[a] = h + "Date", n[d] = h + "Date", n[f] = h + "Month", n[c] = h + "FullYear", n[u] = h + "Hours", n[s] = h + "Minutes", n[i] = h + "Seconds", n[r] = h + "Milliseconds", n)[o], - l = o === a ? this.$D + (e - this.$W) : e; - - if (o === f || o === c) { - var y = this.clone().set(d, 1); - y.$d[$](l), y.init(), this.$d = y.set(d, Math.min(this.$D, y.daysInMonth())).$d; - } else $ && this.$d[$](l); - - return this.init(), this; - }, m.set = function (t, e) { - return this.clone().$set(t, e); - }, m.get = function (t) { - return this[O.p(t)](); - }, m.add = function (r, h) { - var d, - $ = this; - r = Number(r); - - var l = O.p(h), - y = function (t) { - var e = w($); - return O.w(e.date(e.date() + Math.round(t * r)), $); - }; - - if (l === f) return this.set(f, this.$M + r); - if (l === c) return this.set(c, this.$y + r); - if (l === a) return y(1); - if (l === o) return y(7); - var M = (d = {}, d[s] = e, d[u] = n, d[i] = t, d)[l] || 1, - m = this.$d.getTime() + r * M; - return O.w(m, this); - }, m.subtract = function (t, e) { - return this.add(-1 * t, e); - }, m.format = function (t) { - var e = this, - n = this.$locale(); - if (!this.isValid()) return n.invalidDate || $; - - var r = t || "YYYY-MM-DDTHH:mm:ssZ", - i = O.z(this), - s = this.$H, - u = this.$m, - a = this.$M, - o = n.weekdays, - f = n.months, - h = function (t, n, i, s) { - return t && (t[n] || t(e, r)) || i[n].substr(0, s); - }, - c = function (t) { - return O.s(s % 12 || 12, t, "0"); - }, - d = n.meridiem || function (t, e, n) { - var r = t < 12 ? "AM" : "PM"; - return n ? r.toLowerCase() : r; - }, - l = { - YY: String(this.$y).slice(-2), - YYYY: this.$y, - M: a + 1, - MM: O.s(a + 1, 2, "0"), - MMM: h(n.monthsShort, a, f, 3), - MMMM: h(f, a), - D: this.$D, - DD: O.s(this.$D, 2, "0"), - d: String(this.$W), - dd: h(n.weekdaysMin, this.$W, o, 2), - ddd: h(n.weekdaysShort, this.$W, o, 3), - dddd: o[this.$W], - H: String(s), - HH: O.s(s, 2, "0"), - h: c(1), - hh: c(2), - a: d(s, u, !0), - A: d(s, u, !1), - m: String(u), - mm: O.s(u, 2, "0"), - s: String(this.$s), - ss: O.s(this.$s, 2, "0"), - SSS: O.s(this.$ms, 3, "0"), - Z: i - }; - - return r.replace(y, function (t, e) { - return e || l[t] || i.replace(":", ""); - }); - }, m.utcOffset = function () { - return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); - }, m.diff = function (r, d, $) { - var l, - y = O.p(d), - M = w(r), - m = (M.utcOffset() - this.utcOffset()) * e, - g = this - M, - D = O.m(this, M); - return D = (l = {}, l[c] = D / 12, l[f] = D, l[h] = D / 3, l[o] = (g - m) / 6048e5, l[a] = (g - m) / 864e5, l[u] = g / n, l[s] = g / e, l[i] = g / t, l)[y] || g, $ ? D : O.a(D); - }, m.daysInMonth = function () { - return this.endOf(f).$D; - }, m.$locale = function () { - return v[this.$L]; - }, m.locale = function (t, e) { - if (!t) return this.$L; - var n = this.clone(), - r = S(t, e, !0); - return r && (n.$L = r), n; - }, m.clone = function () { - return O.w(this.$d, this); - }, m.toDate = function () { - return new Date(this.valueOf()); - }, m.toJSON = function () { - return this.isValid() ? this.toISOString() : null; - }, m.toISOString = function () { - return this.$d.toISOString(); - }, m.toString = function () { - return this.$d.toUTCString(); - }, M; - }(), - b = _.prototype; - - return w.prototype = b, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", f], ["$y", c], ["$D", d]].forEach(function (t) { - b[t[1]] = function (e) { - return this.$g(e, t[0], t[1]); - }; - }), w.extend = function (t, e) { - return t.$i || (t(e, _, w), t.$i = !0), w; - }, w.locale = S, w.isDayjs = p, w.unix = function (t) { - return w(1e3 * t); - }, w.en = v[D], w.Ls = v, w.p = {}, w; -}); - -/***/ }), - -/***/ 8674: -/***/ (function(module) { - -!function (e, t) { - true ? module.exports = t() : 0; -}(this, function () { - "use strict"; - - return function (e, t, r) { - var n = t.prototype, - s = n.format; - r.en.ordinal = function (e) { - var t = ["th", "st", "nd", "rd"], - r = e % 100; - return "[" + e + (t[(r - 20) % 10] || t[r] || t[0]) + "]"; - }, n.format = function (e) { - var t = this, - r = this.$locale(), - n = this.$utils(), - a = (e || "YYYY-MM-DDTHH:mm:ssZ").replace(/\[([^\]]+)]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g, function (e) { - switch (e) { - case "Q": - return Math.ceil((t.$M + 1) / 3); - - case "Do": - return r.ordinal(t.$D); - - case "gggg": - return t.weekYear(); - - case "GGGG": - return t.isoWeekYear(); - - case "wo": - return r.ordinal(t.week(), "W"); - - case "w": - case "ww": - return n.s(t.week(), "w" === e ? 1 : 2, "0"); - - case "W": - case "WW": - return n.s(t.isoWeek(), "W" === e ? 1 : 2, "0"); - - case "k": - case "kk": - return n.s(String(0 === t.$H ? 24 : t.$H), "k" === e ? 1 : 2, "0"); - - case "X": - return Math.floor(t.$d.getTime() / 1e3); - - case "x": - return t.$d.getTime(); - - case "z": - return "[" + t.offsetName() + "]"; - - case "zzz": - return "[" + t.offsetName("long") + "]"; - - default: - return e; - } - }); - return s.bind(this)(a); - }; - }; -}); - -/***/ }), - -/***/ 1041: -/***/ (function(module) { - -/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */ -(function (global, factory) { - true ? module.exports = factory() : 0; -})(this, function () { - 'use strict'; - - function _toConsumableArray(arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { - arr2[i] = arr[i]; - } - - return arr2; - } else { - return Array.from(arr); - } - } - - var hasOwnProperty = Object.hasOwnProperty, - setPrototypeOf = Object.setPrototypeOf, - isFrozen = Object.isFrozen, - getPrototypeOf = Object.getPrototypeOf, - getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; - var freeze = Object.freeze, - seal = Object.seal, - create = Object.create; // eslint-disable-line import/no-mutable-exports - - var _ref = typeof Reflect !== 'undefined' && Reflect, - apply = _ref.apply, - construct = _ref.construct; - - if (!apply) { - apply = function apply(fun, thisValue, args) { - return fun.apply(thisValue, args); - }; - } - - if (!freeze) { - freeze = function freeze(x) { - return x; - }; - } - - if (!seal) { - seal = function seal(x) { - return x; - }; - } - - if (!construct) { - construct = function construct(Func, args) { - return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); - }; - } - - var arrayForEach = unapply(Array.prototype.forEach); - var arrayPop = unapply(Array.prototype.pop); - var arrayPush = unapply(Array.prototype.push); - var stringToLowerCase = unapply(String.prototype.toLowerCase); - var stringMatch = unapply(String.prototype.match); - var stringReplace = unapply(String.prototype.replace); - var stringIndexOf = unapply(String.prototype.indexOf); - var stringTrim = unapply(String.prototype.trim); - var regExpTest = unapply(RegExp.prototype.test); - var typeErrorCreate = unconstruct(TypeError); - - function unapply(func) { - return function (thisArg) { - for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; - } - - return apply(func, thisArg, args); - }; - } - - function unconstruct(func) { - return function () { - for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { - args[_key2] = arguments[_key2]; - } - - return construct(func, args); - }; - } - /* Add properties to a lookup table */ - - - function addToSet(set, array) { - if (setPrototypeOf) { - // Make 'in' and truthy checks like Boolean(set.constructor) - // independent of any properties defined on Object.prototype. - // Prevent prototype setters from intercepting set as a this value. - setPrototypeOf(set, null); - } - - var l = array.length; - - while (l--) { - var element = array[l]; - - if (typeof element === 'string') { - var lcElement = stringToLowerCase(element); - - if (lcElement !== element) { - // Config presets (e.g. tags.js, attrs.js) are immutable. - if (!isFrozen(array)) { - array[l] = lcElement; - } - - element = lcElement; - } - } - - set[element] = true; - } - - return set; - } - /* Shallow clone an object */ - - - function clone(object) { - var newObject = create(null); - var property = void 0; - - for (property in object) { - if (apply(hasOwnProperty, object, [property])) { - newObject[property] = object[property]; - } - } - - return newObject; - } - /* IE10 doesn't support __lookupGetter__ so lets' - * simulate it. It also automatically checks - * if the prop is function or getter and behaves - * accordingly. */ - - - function lookupGetter(object, prop) { - while (object !== null) { - var desc = getOwnPropertyDescriptor(object, prop); - - if (desc) { - if (desc.get) { - return unapply(desc.get); - } - - if (typeof desc.value === 'function') { - return unapply(desc.value); - } - } - - object = getPrototypeOf(object); - } - - function fallbackValue(element) { - console.warn('fallback value for', element); - return null; - } - - return fallbackValue; - } - - var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']); // SVG - - var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']); - var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']); // List of SVG elements that are disallowed by default. - // We still need to know them so that we can do namespace - // checks properly in case one wants to add them to - // allow-list. - - var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']); - var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']); // Similarly to SVG, we want to know all MathML elements, - // even those that we disallow by default. - - var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']); - var text = freeze(['#text']); - var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns', 'slot']); - var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']); - var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']); - var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']); // eslint-disable-next-line unicorn/better-regex - - var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode - - var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm); - var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape - - var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape - - var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape - ); - var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i); - var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex - ); - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; - }; - - function _toConsumableArray$1(arr) { - if (Array.isArray(arr)) { - for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { - arr2[i] = arr[i]; - } - - return arr2; - } else { - return Array.from(arr); - } - } - - var getGlobal = function getGlobal() { - return typeof window === 'undefined' ? null : window; - }; - /** - * Creates a no-op policy for internal use only. - * Don't export this function outside this module! - * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory. - * @param {Document} document The document object (to determine policy name suffix) - * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types - * are not supported). - */ - - - var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { - if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { - return null; - } // Allow the callers to control the unique policy name - // by adding a data-tt-policy-suffix to the script element with the DOMPurify. - // Policy creation with duplicate names throws in Trusted Types. - - - var suffix = null; - var ATTR_NAME = 'data-tt-policy-suffix'; - - if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { - suffix = document.currentScript.getAttribute(ATTR_NAME); - } - - var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); - - try { - return trustedTypes.createPolicy(policyName, { - createHTML: function createHTML(html$$1) { - return html$$1; - } - }); - } catch (_) { - // Policy creation failed (most likely another DOMPurify script has - // already run). Skip creating the policy, as this will only cause errors - // if TT are enforced. - console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); - return null; - } - }; - - function createDOMPurify() { - var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); - - var DOMPurify = function DOMPurify(root) { - return createDOMPurify(root); - }; - /** - * Version label, exposed for easier checks - * if DOMPurify is up to date or not - */ - - - DOMPurify.version = '2.3.1'; - /** - * Array of elements that DOMPurify removed during sanitation. - * Empty if nothing was removed. - */ - - DOMPurify.removed = []; - - if (!window || !window.document || window.document.nodeType !== 9) { - // Not running in a browser, provide a factory function - // so that you can pass your own Window - DOMPurify.isSupported = false; - return DOMPurify; - } - - var originalDocument = window.document; - var document = window.document; - var DocumentFragment = window.DocumentFragment, - HTMLTemplateElement = window.HTMLTemplateElement, - Node = window.Node, - Element = window.Element, - NodeFilter = window.NodeFilter, - _window$NamedNodeMap = window.NamedNodeMap, - NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, - Text = window.Text, - Comment = window.Comment, - DOMParser = window.DOMParser, - trustedTypes = window.trustedTypes; - var ElementPrototype = Element.prototype; - var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); - var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling'); - var getChildNodes = lookupGetter(ElementPrototype, 'childNodes'); - var getParentNode = lookupGetter(ElementPrototype, 'parentNode'); // As per issue #47, the web-components registry is inherited by a - // new document created via createHTMLDocument. As per the spec - // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) - // a new empty registry is used when creating a template contents owner - // document, so we use that as our parent document to ensure nothing - // is inherited. - - if (typeof HTMLTemplateElement === 'function') { - var template = document.createElement('template'); - - if (template.content && template.content.ownerDocument) { - document = template.content.ownerDocument; - } - } - - var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); - - var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; - var _document = document, - implementation = _document.implementation, - createNodeIterator = _document.createNodeIterator, - createDocumentFragment = _document.createDocumentFragment, - getElementsByTagName = _document.getElementsByTagName; - var importNode = originalDocument.importNode; - var documentMode = {}; - - try { - documentMode = clone(document).documentMode ? document.documentMode : {}; - } catch (_) {} - - var hooks = {}; - /** - * Expose whether this browser supports running the full DOMPurify. - */ - - DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; - var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, - ERB_EXPR$$1 = ERB_EXPR, - DATA_ATTR$$1 = DATA_ATTR, - ARIA_ATTR$$1 = ARIA_ATTR, - IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, - ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; - var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; - /** - * We consider the elements and attributes below to be safe. Ideally - * don't add any new ones but feel free to remove unwanted ones. - */ - - /* allowed element names */ - - var ALLOWED_TAGS = null; - var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text))); - /* Allowed attribute names */ - - var ALLOWED_ATTR = null; - var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml))); - /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */ - - var FORBID_TAGS = null; - /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */ - - var FORBID_ATTR = null; - /* Decide if ARIA attributes are okay */ - - var ALLOW_ARIA_ATTR = true; - /* Decide if custom data attributes are okay */ - - var ALLOW_DATA_ATTR = true; - /* Decide if unknown protocols are okay */ - - var ALLOW_UNKNOWN_PROTOCOLS = false; - /* Output should be safe for common template engines. - * This means, DOMPurify removes data attributes, mustaches and ERB - */ - - var SAFE_FOR_TEMPLATES = false; - /* Decide if document with ... should be returned */ - - var WHOLE_DOCUMENT = false; - /* Track whether config is already set on this instance of DOMPurify. */ - - var SET_CONFIG = false; - /* Decide if all elements (e.g. style, script) must be children of - * document.body. By default, browsers might move them to document.head */ - - var FORCE_BODY = false; - /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html - * string (or a TrustedHTML object if Trusted Types are supported). - * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead - */ - - var RETURN_DOM = false; - /* Decide if a DOM `DocumentFragment` should be returned, instead of a html - * string (or a TrustedHTML object if Trusted Types are supported) */ - - var RETURN_DOM_FRAGMENT = false; - /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM - * `Node` is imported into the current `Document`. If this flag is not enabled the - * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by - * DOMPurify. - * - * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false` - * might cause XSS from attacks hidden in closed shadowroots in case the browser - * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/ - */ - - var RETURN_DOM_IMPORT = true; - /* Try to return a Trusted Type object instead of a string, return a string in - * case Trusted Types are not supported */ - - var RETURN_TRUSTED_TYPE = false; - /* Output should be free from DOM clobbering attacks? */ - - var SANITIZE_DOM = true; - /* Keep element content when removing element? */ - - var KEEP_CONTENT = true; - /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead - * of importing it into a new Document and returning a sanitized copy */ - - var IN_PLACE = false; - /* Allow usage of profiles like html, svg and mathMl */ - - var USE_PROFILES = {}; - /* Tags to ignore content of when KEEP_CONTENT is true */ - - var FORBID_CONTENTS = null; - var DEFAULT_FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']); - /* Tags that are safe for data: URIs */ - - var DATA_URI_TAGS = null; - var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']); - /* Attributes safe for values like "javascript:" */ - - var URI_SAFE_ATTRIBUTES = null; - var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'role', 'summary', 'title', 'value', 'style', 'xmlns']); - var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; - var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; - var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; - /* Document namespace */ - - var NAMESPACE = HTML_NAMESPACE; - var IS_EMPTY_INPUT = false; - /* Keep a reference to config to pass to hooks */ - - var CONFIG = null; - /* Ideally, do not touch anything below this line */ - - /* ______________________________________________ */ - - var formElement = document.createElement('form'); - /** - * _parseConfig - * - * @param {Object} cfg optional config literal - */ - // eslint-disable-next-line complexity - - var _parseConfig = function _parseConfig(cfg) { - if (CONFIG && CONFIG === cfg) { - return; - } - /* Shield configuration object from tampering */ - - - if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { - cfg = {}; - } - /* Shield configuration object from prototype pollution */ - - - cfg = clone(cfg); - /* Set configuration parameters */ - - ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; - ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; - URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; - DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; - FORBID_CONTENTS = 'FORBID_CONTENTS' in cfg ? addToSet({}, cfg.FORBID_CONTENTS) : DEFAULT_FORBID_CONTENTS; - FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; - FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; - USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; - ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true - - ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true - - ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false - - SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false - - WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false - - RETURN_DOM = cfg.RETURN_DOM || false; // Default false - - RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false - - RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true - - RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false - - FORCE_BODY = cfg.FORCE_BODY || false; // Default false - - SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true - - KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true - - IN_PLACE = cfg.IN_PLACE || false; // Default false - - IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; - NAMESPACE = cfg.NAMESPACE || HTML_NAMESPACE; - - if (SAFE_FOR_TEMPLATES) { - ALLOW_DATA_ATTR = false; - } - - if (RETURN_DOM_FRAGMENT) { - RETURN_DOM = true; - } - /* Parse profile info */ - - - if (USE_PROFILES) { - ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); - ALLOWED_ATTR = []; - - if (USE_PROFILES.html === true) { - addToSet(ALLOWED_TAGS, html); - addToSet(ALLOWED_ATTR, html$1); - } - - if (USE_PROFILES.svg === true) { - addToSet(ALLOWED_TAGS, svg); - addToSet(ALLOWED_ATTR, svg$1); - addToSet(ALLOWED_ATTR, xml); - } - - if (USE_PROFILES.svgFilters === true) { - addToSet(ALLOWED_TAGS, svgFilters); - addToSet(ALLOWED_ATTR, svg$1); - addToSet(ALLOWED_ATTR, xml); - } - - if (USE_PROFILES.mathMl === true) { - addToSet(ALLOWED_TAGS, mathMl); - addToSet(ALLOWED_ATTR, mathMl$1); - addToSet(ALLOWED_ATTR, xml); - } - } - /* Merge configuration parameters */ - - - if (cfg.ADD_TAGS) { - if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { - ALLOWED_TAGS = clone(ALLOWED_TAGS); - } - - addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); - } - - if (cfg.ADD_ATTR) { - if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { - ALLOWED_ATTR = clone(ALLOWED_ATTR); - } - - addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); - } - - if (cfg.ADD_URI_SAFE_ATTR) { - addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); - } - - if (cfg.FORBID_CONTENTS) { - if (FORBID_CONTENTS === DEFAULT_FORBID_CONTENTS) { - FORBID_CONTENTS = clone(FORBID_CONTENTS); - } - - addToSet(FORBID_CONTENTS, cfg.FORBID_CONTENTS); - } - /* Add #text in case KEEP_CONTENT is set to true */ - - - if (KEEP_CONTENT) { - ALLOWED_TAGS['#text'] = true; - } - /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ - - - if (WHOLE_DOCUMENT) { - addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); - } - /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ - - - if (ALLOWED_TAGS.table) { - addToSet(ALLOWED_TAGS, ['tbody']); - delete FORBID_TAGS.tbody; - } // Prevent further manipulation of configuration. - // Not available in IE8, Safari 5, etc. - - - if (freeze) { - freeze(cfg); - } - - CONFIG = cfg; - }; - - var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); - var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); - /* Keep track of all possible SVG and MathML tags - * so that we can perform the namespace checks - * correctly. */ - - var ALL_SVG_TAGS = addToSet({}, svg); - addToSet(ALL_SVG_TAGS, svgFilters); - addToSet(ALL_SVG_TAGS, svgDisallowed); - var ALL_MATHML_TAGS = addToSet({}, mathMl); - addToSet(ALL_MATHML_TAGS, mathMlDisallowed); - /** - * - * - * @param {Element} element a DOM element whose namespace is being checked - * @returns {boolean} Return false if the element has a - * namespace that a spec-compliant parser would never - * return. Return true otherwise. - */ - - var _checkValidNamespace = function _checkValidNamespace(element) { - var parent = getParentNode(element); // In JSDOM, if we're inside shadow DOM, then parentNode - // can be null. We just simulate parent in this case. - - if (!parent || !parent.tagName) { - parent = { - namespaceURI: HTML_NAMESPACE, - tagName: 'template' - }; - } - - var tagName = stringToLowerCase(element.tagName); - var parentTagName = stringToLowerCase(parent.tagName); - - if (element.namespaceURI === SVG_NAMESPACE) { - // The only way to switch from HTML namespace to SVG - // is via . If it happens via any other tag, then - // it should be killed. - if (parent.namespaceURI === HTML_NAMESPACE) { - return tagName === 'svg'; - } // The only way to switch from MathML to SVG is via - // svg if parent is either or MathML - // text integration points. - - - if (parent.namespaceURI === MATHML_NAMESPACE) { - return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); - } // We only allow elements that are defined in SVG - // spec. All others are disallowed in SVG namespace. - - - return Boolean(ALL_SVG_TAGS[tagName]); - } - - if (element.namespaceURI === MATHML_NAMESPACE) { - // The only way to switch from HTML namespace to MathML - // is via . If it happens via any other tag, then - // it should be killed. - if (parent.namespaceURI === HTML_NAMESPACE) { - return tagName === 'math'; - } // The only way to switch from SVG to MathML is via - // and HTML integration points - - - if (parent.namespaceURI === SVG_NAMESPACE) { - return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; - } // We only allow elements that are defined in MathML - // spec. All others are disallowed in MathML namespace. - - - return Boolean(ALL_MATHML_TAGS[tagName]); - } - - if (element.namespaceURI === HTML_NAMESPACE) { - // The only way to switch from SVG to HTML is via - // HTML integration points, and from MathML to HTML - // is via MathML text integration points - if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { - return false; - } - - if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { - return false; - } // Certain elements are allowed in both SVG and HTML - // namespace. We need to specify them explicitly - // so that they don't get erronously deleted from - // HTML namespace. - - - var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']); // We disallow tags that are specific for MathML - // or SVG and should never appear in HTML namespace - - return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]); - } // The code should never reach this place (this means - // that the element somehow got namespace that is not - // HTML, SVG or MathML). Return false just in case. - - - return false; - }; - /** - * _forceRemove - * - * @param {Node} node a DOM node - */ - - - var _forceRemove = function _forceRemove(node) { - arrayPush(DOMPurify.removed, { - element: node - }); - - try { - // eslint-disable-next-line unicorn/prefer-dom-node-remove - node.parentNode.removeChild(node); - } catch (_) { - try { - node.outerHTML = emptyHTML; - } catch (_) { - node.remove(); - } - } - }; - /** - * _removeAttribute - * - * @param {String} name an Attribute name - * @param {Node} node a DOM node - */ - - - var _removeAttribute = function _removeAttribute(name, node) { - try { - arrayPush(DOMPurify.removed, { - attribute: node.getAttributeNode(name), - from: node - }); - } catch (_) { - arrayPush(DOMPurify.removed, { - attribute: null, - from: node - }); - } - - node.removeAttribute(name); // We void attribute values for unremovable "is"" attributes - - if (name === 'is' && !ALLOWED_ATTR[name]) { - if (RETURN_DOM || RETURN_DOM_FRAGMENT) { - try { - _forceRemove(node); - } catch (_) {} - } else { - try { - node.setAttribute(name, ''); - } catch (_) {} - } - } - }; - /** - * _initDocument - * - * @param {String} dirty a string of dirty markup - * @return {Document} a DOM, filled with the dirty markup - */ - - - var _initDocument = function _initDocument(dirty) { - /* Create a HTML document */ - var doc = void 0; - var leadingWhitespace = void 0; - - if (FORCE_BODY) { - dirty = '' + dirty; - } else { - /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ - var matches = stringMatch(dirty, /^[\r\n\t ]+/); - leadingWhitespace = matches && matches[0]; - } - - var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; - /* - * Use the DOMParser API by default, fallback later if needs be - * DOMParser not work for svg when has multiple root element. - */ - - if (NAMESPACE === HTML_NAMESPACE) { - try { - doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); - } catch (_) {} - } - /* Use createHTMLDocument in case DOMParser is not available */ - - - if (!doc || !doc.documentElement) { - doc = implementation.createDocument(NAMESPACE, 'template', null); - - try { - doc.documentElement.innerHTML = IS_EMPTY_INPUT ? '' : dirtyPayload; - } catch (_) {// Syntax error if dirtyPayload is invalid xml - } - } - - var body = doc.body || doc.documentElement; - - if (dirty && leadingWhitespace) { - body.insertBefore(document.createTextNode(leadingWhitespace), body.childNodes[0] || null); - } - /* Work on whole document or just its body */ - - - if (NAMESPACE === HTML_NAMESPACE) { - return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; - } - - return WHOLE_DOCUMENT ? doc.documentElement : body; - }; - /** - * _createIterator - * - * @param {Document} root document/fragment to create iterator for - * @return {Iterator} iterator instance - */ - - - var _createIterator = function _createIterator(root) { - return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, null, false); - }; - /** - * _isClobbered - * - * @param {Node} elm element to check for clobbering attacks - * @return {Boolean} true if clobbered, false if safe - */ - - - var _isClobbered = function _isClobbered(elm) { - if (elm instanceof Text || elm instanceof Comment) { - return false; - } - - if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') { - return true; - } - - return false; - }; - /** - * _isNode - * - * @param {Node} obj object to check whether it's a DOM node - * @return {Boolean} true is object is a DOM node - */ - - - var _isNode = function _isNode(object) { - return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; - }; - /** - * _executeHook - * Execute user configurable hooks - * - * @param {String} entryPoint Name of the hook's entry point - * @param {Node} currentNode node to work on with the hook - * @param {Object} data additional hook parameters - */ - - - var _executeHook = function _executeHook(entryPoint, currentNode, data) { - if (!hooks[entryPoint]) { - return; - } - - arrayForEach(hooks[entryPoint], function (hook) { - hook.call(DOMPurify, currentNode, data, CONFIG); - }); - }; - /** - * _sanitizeElements - * - * @protect nodeName - * @protect textContent - * @protect removeChild - * - * @param {Node} currentNode to check for permission to exist - * @return {Boolean} true if node was killed, false if left alive - */ - - - var _sanitizeElements = function _sanitizeElements(currentNode) { - var content = void 0; - /* Execute a hook if present */ - - _executeHook('beforeSanitizeElements', currentNode, null); - /* Check if element is clobbered or can clobber */ - - - if (_isClobbered(currentNode)) { - _forceRemove(currentNode); - - return true; - } - /* Check if tagname contains Unicode */ - - - if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { - _forceRemove(currentNode); - - return true; - } - /* Now let's check the element's type and name */ - - - var tagName = stringToLowerCase(currentNode.nodeName); - /* Execute a hook if present */ - - _executeHook('uponSanitizeElement', currentNode, { - tagName: tagName, - allowedTags: ALLOWED_TAGS - }); - /* Detect mXSS attempts abusing namespace confusion */ - - - if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { - _forceRemove(currentNode); - - return true; - } - /* Mitigate a problem with templates inside select */ - - - if (tagName === 'select' && regExpTest(/ tag of the message stanza - * @property { String } chat_state - The XEP-0085 chat state notification contained in this message - * @property { String } contact_jid - The JID of the other person or entity - * @property { String } edited - An ISO8601 string recording the time that the message was edited per XEP-0308 - * @property { String } error_condition - The defined error condition - * @property { String } error_text - The error text received from the server - * @property { String } error_type - The type of error received from the server - * @property { String } from - The sender JID - * @property { String } fullname - The full name of the sender - * @property { String } marker - The XEP-0333 Chat Marker value - * @property { String } marker_id - The `id` attribute of a XEP-0333 chat marker - * @property { String } msgid - The root `id` attribute of the stanza - * @property { String } nick - The roster nickname of the sender - * @property { String } oob_desc - The description of the XEP-0066 out of band data - * @property { String } oob_url - The URL of the XEP-0066 out of band data - * @property { String } origin_id - The XEP-0359 Origin ID - * @property { String } receipt_id - The `id` attribute of a XEP-0184 element - * @property { String } received - An ISO8601 string recording the time that the message was received - * @property { String } replace_id - The `id` attribute of a XEP-0308 element - * @property { String } retracted - An ISO8601 string recording the time that the message was retracted - * @property { String } retracted_id - The `id` attribute of a XEP-424 element - * @property { String } spoiler_hint The XEP-0382 spoiler hint - * @property { String } stanza_id - The XEP-0359 Stanza ID. Note: the key is actualy `stanza_id ${by_jid}` and there can be multiple. - * @property { String } subject - The element value - * @property { String } thread - The element value - * @property { String } time - The time (in ISO8601 format), either given by the XEP-0203 element, or of receipt. - * @property { String } to - The recipient JID - * @property { String } type - The type of message - */ - - - const delay = parsers_sizzle(`delay[xmlns="${parsers_Strophe.NS.DELAY}"]`, original_stanza).pop(); - const marker = getChatMarker(stanza); - const now = new Date().toISOString(); - let attrs = Object.assign({ - contact_jid, - is_archived, - is_headline, - is_server_message, - 'body': (_stanza$querySelector = stanza.querySelector('body')) === null || _stanza$querySelector === void 0 ? void 0 : (_stanza$querySelector2 = _stanza$querySelector.textContent) === null || _stanza$querySelector2 === void 0 ? void 0 : _stanza$querySelector2.trim(), - 'chat_state': getChatState(stanza), - 'from': parsers_Strophe.getBareJidFromJid(stanza.getAttribute('from')), - 'is_carbon': isCarbon(original_stanza), - 'is_delayed': !!delay, - 'is_markable': !!parsers_sizzle(`markable[xmlns="${parsers_Strophe.NS.MARKERS}"]`, stanza).length, - 'is_marker': !!marker, - 'is_unstyled': !!parsers_sizzle(`unstyled[xmlns="${parsers_Strophe.NS.STYLING}"]`, stanza).length, - 'marker_id': marker && marker.getAttribute('id'), - 'msgid': stanza.getAttribute('id') || original_stanza.getAttribute('id'), - 'nick': (_contact = contact) === null || _contact === void 0 ? void 0 : (_contact$attributes = _contact.attributes) === null || _contact$attributes === void 0 ? void 0 : _contact$attributes.nickname, - 'receipt_id': getReceiptId(stanza), - 'received': new Date().toISOString(), - 'references': getReferences(stanza), - 'sender': is_me ? 'me' : 'them', - 'subject': (_stanza$querySelector3 = stanza.querySelector('subject')) === null || _stanza$querySelector3 === void 0 ? void 0 : _stanza$querySelector3.textContent, - 'thread': (_stanza$querySelector4 = stanza.querySelector('thread')) === null || _stanza$querySelector4 === void 0 ? void 0 : _stanza$querySelector4.textContent, - 'time': delay ? dayjs_min_default()(delay.getAttribute('stamp')).toISOString() : now, - 'to': stanza.getAttribute('to'), - 'type': stanza.getAttribute('type') - }, getErrorAttributes(stanza), getOutOfBandAttributes(stanza), getSpoilerAttributes(stanza), getCorrectionAttributes(stanza, original_stanza), getStanzaIDs(stanza, original_stanza), getRetractionAttributes(stanza, original_stanza), getEncryptionAttributes(stanza, _converse)); - - if (attrs.is_archived) { - const from = original_stanza.getAttribute('from'); - - if (from && from !== _converse.bare_jid) { - return new StanzaParseError(`Invalid Stanza: Forged MAM message from ${from}`, stanza); - } - } - - await api.emojis.initialize(); - attrs = Object.assign({ - 'message': attrs.body || attrs.error, - // TODO: Remove and use body and error attributes instead - 'is_only_emojis': attrs.body ? utils_core.isOnlyEmojis(attrs.body) : false, - 'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs) - }, attrs); // We prefer to use one of the XEP-0359 unique and stable stanza IDs - // as the Model id, to avoid duplicates. - - attrs['id'] = attrs['origin_id'] || attrs[`stanza_id ${attrs.from}`] || utils_core.getUniqueId(); - /** - * *Hook* which allows plugins to add additional parsing - * @event _converse#parseMessage - */ - - attrs = await api.hook('parseMessage', stanza, attrs); // We call this after the hook, to allow plugins to decrypt encrypted - // messages, since we need to parse the message text to determine whether - // there are media urls. - - return Object.assign(attrs, getMediaURLs(attrs.is_encrypted ? attrs.plaintext : attrs.body)); -} -;// CONCATENATED MODULE: ./src/headless/plugins/chat/model.js - - - - - - - - - - - - - - -const { - Strophe: model_Strophe, - $msg: model_$msg -} = core_converse.env; -const model_u = core_converse.env.utils; -/** - * Represents an open/ongoing chat conversation. - * - * @class - * @namespace _converse.ChatBox - * @memberOf _converse - */ - -const ChatBox = model_with_contact.extend({ - defaults() { - return { - 'bookmarked': false, - 'chat_state': undefined, - 'hidden': shared_converse.isUniView() && !api.settings.get('singleton'), - 'message_type': 'chat', - 'nickname': undefined, - 'num_unread': 0, - 'time_opened': this.get('time_opened') || new Date().getTime(), - 'time_sent': new Date(0).toISOString(), - 'type': shared_converse.PRIVATE_CHAT_TYPE, - 'url': '' - }; - }, - - async initialize() { - this.initialized = getOpenPromise(); - model_with_contact.prototype.initialize.apply(this, arguments); - const jid = this.get('jid'); - - if (!jid) { - // XXX: The `validate` method will prevent this model - // from being persisted if there's no jid, but that gets - // called after model instantiation, so we have to deal - // with invalid models here also. - // This happens when the controlbox is in browser storage, - // but we're in embedded mode. - return; - } - - this.set({ - 'box_id': `box-${jid}` - }); - this.initNotifications(); - this.initUI(); - this.initMessages(); - - if (this.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - this.presence = shared_converse.presences.findWhere({ - 'jid': jid - }) || shared_converse.presences.create({ - 'jid': jid - }); - await this.setRosterContact(jid); - this.presence.on('change:show', item => this.onPresenceChanged(item)); - } - - this.on('change:chat_state', this.sendChatState, this); - this.ui.on('change:scrolled', this.onScrolledChanged, this); - await this.fetchMessages(); - /** - * Triggered once a {@link _converse.ChatBox} has been created and initialized. - * @event _converse#chatBoxInitialized - * @type { _converse.ChatBox} - * @example _converse.api.listen.on('chatBoxInitialized', model => { ... }); - */ - - await api.trigger('chatBoxInitialized', this, { - 'Synchronous': true - }); - this.initialized.resolve(); - }, - - getMessagesCollection() { - return new shared_converse.Messages(); - }, - - getMessagesCacheKey() { - return `converse.messages-${this.get('jid')}-${shared_converse.bare_jid}`; - }, - - initMessages() { - this.messages = this.getMessagesCollection(); - this.messages.fetched = getOpenPromise(); - this.messages.fetched.then(() => { - this.pruneHistoryWhenScrolledDown(); - /** - * Triggered whenever a `_converse.ChatBox` instance has fetched its messages from - * `sessionStorage` but **NOT** from the server. - * @event _converse#afterMessagesFetched - * @type {_converse.ChatBoxView | _converse.ChatRoomView} - * @example _converse.api.listen.on('afterMessagesFetched', view => { ... }); - */ - - api.trigger('afterMessagesFetched', this); - }); - this.messages.chatbox = this; - initStorage(this.messages, this.getMessagesCacheKey()); - this.listenTo(this.messages, 'change:upload', this.onMessageUploadChanged, this); - this.listenTo(this.messages, 'add', this.onMessageAdded, this); - }, - - initUI() { - this.ui = new Model(); - }, - - initNotifications() { - this.notifications = new Model(); - }, - - getNotificationsText() { - var _this$notifications, _this$notifications2, _this$notifications3; - - const { - __ - } = shared_converse; - - if (((_this$notifications = this.notifications) === null || _this$notifications === void 0 ? void 0 : _this$notifications.get('chat_state')) === shared_converse.COMPOSING) { - return __('%1$s is typing', this.getDisplayName()); - } else if (((_this$notifications2 = this.notifications) === null || _this$notifications2 === void 0 ? void 0 : _this$notifications2.get('chat_state')) === shared_converse.PAUSED) { - return __('%1$s has stopped typing', this.getDisplayName()); - } else if (((_this$notifications3 = this.notifications) === null || _this$notifications3 === void 0 ? void 0 : _this$notifications3.get('chat_state')) === shared_converse.GONE) { - return __('%1$s has gone away', this.getDisplayName()); - } else { - return ''; - } - }, - - afterMessagesFetched(messages) { - this.most_recent_cached_message = messages ? this.getMostRecentMessage(messages) : null; - /** - * Triggered whenever a `_converse.ChatBox` instance has fetched its messages from - * `sessionStorage` but **NOT** from the server. - * @event _converse#afterMessagesFetched - * @type {_converse.ChatBox | _converse.ChatRoom} - * @example _converse.api.listen.on('afterMessagesFetched', view => { ... }); - */ - - api.trigger('afterMessagesFetched', this); - }, - - fetchMessages() { - if (this.messages.fetched_flag) { - headless_log.info(`Not re-fetching messages for ${this.get('jid')}`); - return; - } - - this.most_recent_cached_message = null; - this.messages.fetched_flag = true; - const resolve = this.messages.fetched.resolve; - this.messages.fetch({ - 'add': true, - 'success': msgs => { - this.afterMessagesFetched(msgs); - resolve(); - }, - 'error': () => { - this.afterMessagesFetched(); - resolve(); - } - }); - return this.messages.fetched; - }, - - async handleErrorMessageStanza(stanza) { - const { - __ - } = shared_converse; - const attrs = await parseMessage(stanza, shared_converse); - - if (!(await this.shouldShowErrorMessage(attrs))) { - return; - } - - const message = this.getMessageReferencedByError(attrs); - - if (message) { - const new_attrs = { - 'error': attrs.error, - 'error_condition': attrs.error_condition, - 'error_text': attrs.error_text, - 'error_type': attrs.error_type, - 'editable': false - }; - - if (attrs.msgid === message.get('retraction_id')) { - // The error message refers to a retraction - new_attrs.retraction_id = undefined; - - if (!attrs.error) { - if (attrs.error_condition === 'forbidden') { - new_attrs.error = __("You're not allowed to retract your message."); - } else { - new_attrs.error = __('Sorry, an error occurred while trying to retract your message.'); - } - } - } else if (!attrs.error) { - if (attrs.error_condition === 'forbidden') { - new_attrs.error = __("You're not allowed to send a message."); - } else { - new_attrs.error = __('Sorry, an error occurred while trying to send your message.'); - } - } - - message.save(new_attrs); - } else { - this.createMessage(attrs); - } - }, - - /** - * Queue an incoming `chat` message stanza for processing. - * @async - * @private - * @method _converse.ChatBox#queueMessage - * @param { Promise } attrs - A promise which resolves to the message attributes - */ - queueMessage(attrs) { - this.msg_chain = (this.msg_chain || this.messages.fetched).then(() => this.onMessage(attrs)).catch(e => headless_log.error(e)); - return this.msg_chain; - }, - - /** - * @async - * @private - * @method _converse.ChatBox#onMessage - * @param { MessageAttributes } attrs_promse - A promise which resolves to the message attributes. - */ - async onMessage(attrs) { - attrs = await attrs; - - if (model_u.isErrorObject(attrs)) { - attrs.stanza && headless_log.error(attrs.stanza); - return headless_log.error(attrs.message); - } - - const message = this.getDuplicateMessage(attrs); - - if (message) { - this.updateMessage(message, attrs); - } else if (!this.handleReceipt(attrs) && !this.handleChatMarker(attrs) && !(await this.handleRetraction(attrs))) { - this.setEditable(attrs, attrs.time); - - if (attrs['chat_state'] && attrs.sender === 'them') { - this.notifications.set('chat_state', attrs.chat_state); - } - - if (model_u.shouldCreateMessage(attrs)) { - const msg = this.handleCorrection(attrs) || (await this.createMessage(attrs)); - this.notifications.set({ - 'chat_state': null - }); - this.handleUnreadMessage(msg); - } - } - }, - - async onMessageUploadChanged(message) { - if (message.get('upload') === shared_converse.SUCCESS) { - const attrs = { - 'body': message.get('message'), - 'spoiler_hint': message.get('spoiler_hint'), - 'oob_url': message.get('oob_url') - }; - await this.sendMessage(attrs); - message.destroy(); - } - }, - - onMessageAdded(message) { - if (api.settings.get('prune_messages_above') && (api.settings.get('pruning_behavior') === 'scrolled' || !this.ui.get('scrolled')) && !model_u.isEmptyMessage(message)) { - debouncedPruneHistory(this); - } - }, - - async clearMessages() { - try { - await this.messages.clearStore(); - } catch (e) { - this.messages.trigger('reset'); - headless_log.error(e); - } finally { - delete this.msg_chain; - delete this.messages.fetched_flag; - this.messages.fetched = getOpenPromise(); - } - }, - - async close() { - if (api.connection.connected()) { - // Immediately sending the chat state, because the - // model is going to be destroyed afterwards. - this.setChatState(shared_converse.INACTIVE); - this.sendChatState(); - } - - try { - await new Promise((success, reject) => { - return this.destroy({ - success, - 'error': (m, e) => reject(e) - }); - }); - } catch (e) { - headless_log.error(e); - } finally { - if (api.settings.get('clear_messages_on_reconnection')) { - await this.clearMessages(); - } - } - /** - * Triggered once a chatbox has been closed. - * @event _converse#chatBoxClosed - * @type {_converse.ChatBox | _converse.ChatRoom} - * @example _converse.api.listen.on('chatBoxClosed', chat => { ... }); - */ - - - api.trigger('chatBoxClosed', this); - }, - - announceReconnection() { - /** - * Triggered whenever a `_converse.ChatBox` instance has reconnected after an outage - * @event _converse#onChatReconnected - * @type {_converse.ChatBox | _converse.ChatRoom} - * @example _converse.api.listen.on('onChatReconnected', chat => { ... }); - */ - api.trigger('chatReconnected', this); - }, - - async onReconnection() { - if (api.settings.get('clear_messages_on_reconnection')) { - await this.clearMessages(); - } - - this.announceReconnection(); - }, - - onPresenceChanged(item) { - const { - __ - } = shared_converse; - const show = item.get('show'); - const fullname = this.getDisplayName(); - let text; - - if (show === 'offline') { - text = __('%1$s has gone offline', fullname); - } else if (show === 'away') { - text = __('%1$s has gone away', fullname); - } else if (show === 'dnd') { - text = __('%1$s is busy', fullname); - } else if (show === 'online') { - text = __('%1$s is online', fullname); - } - - text && this.createMessage({ - 'message': text, - 'type': 'info' - }); - }, - - onScrolledChanged() { - if (!this.ui.get('scrolled')) { - this.clearUnreadMsgCounter(); - this.pruneHistoryWhenScrolledDown(); - } - }, - - pruneHistoryWhenScrolledDown() { - if (api.settings.get('prune_messages_above') && api.settings.get('pruning_behavior') === 'unscrolled' && !this.ui.get('scrolled')) { - pruneHistory(this); - } - }, - - validate(attrs) { - if (!attrs.jid) { - return 'Ignored ChatBox without JID'; - } - - const room_jids = api.settings.get('auto_join_rooms').map(s => lodash_es_isObject(s) ? s.jid : s); - const auto_join = api.settings.get('auto_join_private_chats').concat(room_jids); - - if (api.settings.get("singleton") && !auto_join.includes(attrs.jid) && !api.settings.get('auto_join_on_invite')) { - const msg = `${attrs.jid} is not allowed because singleton is true and it's not being auto_joined`; - headless_log.warn(msg); - return msg; - } - }, - - getDisplayName() { - if (this.contact) { - return this.contact.getDisplayName(); - } else if (this.vcard) { - return this.vcard.getDisplayName(); - } else { - return this.get('jid'); - } - }, - - async createMessageFromError(error) { - if (error instanceof shared_converse.TimeoutError) { - const msg = await this.createMessage({ - 'type': 'error', - 'message': error.message, - 'retry_event_id': error.retry_event_id - }); - msg.error = error; - } - }, - - editEarlierMessage() { - let message; - let idx = this.messages.findLastIndex('correcting'); - - if (idx >= 0) { - this.messages.at(idx).save('correcting', false); - - while (idx > 0) { - idx -= 1; - const candidate = this.messages.at(idx); - - if (candidate.get('editable')) { - message = candidate; - break; - } - } - } - - message = message || this.messages.filter({ - 'sender': 'me' - }).reverse().find(m => m.get('editable')); - - if (message) { - message.save('correcting', true); - } - }, - - editLaterMessage() { - let message; - let idx = this.messages.findLastIndex('correcting'); - - if (idx >= 0) { - this.messages.at(idx).save('correcting', false); - - while (idx < this.messages.length - 1) { - idx += 1; - const candidate = this.messages.at(idx); - - if (candidate.get('editable')) { - message = candidate; - message.save('correcting', true); - break; - } - } - } - - return message; - }, - - getOldestMessage() { - for (let i = 0; i < this.messages.length; i++) { - const message = this.messages.at(i); - - if (message.get('type') === this.get('message_type')) { - return message; - } - } - }, - - getMostRecentMessage(messages) { - messages = messages || this.messages; - - for (let i = messages.length - 1; i >= 0; i--) { - const message = messages.at(i); - - if (message.get('type') === this.get('message_type')) { - return message; - } - } - }, - - getUpdatedMessageAttributes(message, attrs) { - // Filter the attrs object, restricting it to only the `is_archived` key. - return (({ - is_archived - }) => ({ - is_archived - }))(attrs); - }, - - updateMessage(message, attrs) { - const new_attrs = this.getUpdatedMessageAttributes(message, attrs); - new_attrs && message.save(new_attrs); - }, - - /** - * Mutator for setting the chat state of this chat session. - * Handles clearing of any chat state notification timeouts and - * setting new ones if necessary. - * Timeouts are set when the state being set is COMPOSING or PAUSED. - * After the timeout, COMPOSING will become PAUSED and PAUSED will become INACTIVE. - * See XEP-0085 Chat State Notifications. - * @private - * @method _converse.ChatBox#setChatState - * @param { string } state - The chat state (consts ACTIVE, COMPOSING, PAUSED, INACTIVE, GONE) - */ - setChatState(state, options) { - if (this.chat_state_timeout !== undefined) { - window.clearTimeout(this.chat_state_timeout); - delete this.chat_state_timeout; - } - - if (state === shared_converse.COMPOSING) { - this.chat_state_timeout = window.setTimeout(this.setChatState.bind(this), shared_converse.TIMEOUTS.PAUSED, shared_converse.PAUSED); - } else if (state === shared_converse.PAUSED) { - this.chat_state_timeout = window.setTimeout(this.setChatState.bind(this), shared_converse.TIMEOUTS.INACTIVE, shared_converse.INACTIVE); - } - - this.set('chat_state', state, options); - return this; - }, - - /** - * Given an error `` stanza's attributes, find the saved message model which is - * referenced by that error. - * @param { Object } attrs - */ - getMessageReferencedByError(attrs) { - const id = attrs.msgid; - return id && this.messages.models.find(m => [m.get('msgid'), m.get('retraction_id')].includes(id)); - }, - - /** - * @private - * @method _converse.ChatBox#shouldShowErrorMessage - * @returns {boolean} - */ - shouldShowErrorMessage(attrs) { - const msg = this.getMessageReferencedByError(attrs); - - if (!msg && !attrs.body) { - // If the error refers to a message not included in our store, - // and it doesn't have a tag, we assume that this was a - // CSI message (which we don't store). - // See https://github.com/conversejs/converse.js/issues/1317 - return; - } // Gets overridden in ChatRoom - - - return true; - }, - - isSameUser(jid1, jid2) { - return model_u.isSameBareJID(jid1, jid2); - }, - - /** - * Looks whether we already have a retraction for this - * incoming message. If so, it's considered "dangling" because it - * probably hasn't been applied to anything yet, given that the - * relevant message is only coming in now. - * @private - * @method _converse.ChatBox#findDanglingRetraction - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMessage} - * @returns { _converse.Message } - */ - findDanglingRetraction(attrs) { - if (!attrs.origin_id || !this.messages.length) { - return null; - } // Only look for dangling retractions if there are newer - // messages than this one, since retractions come after. - - - if (this.messages.last().get('time') > attrs.time) { - // Search from latest backwards - const messages = Array.from(this.messages.models); - messages.reverse(); - return messages.find(({ - attributes - }) => attributes.retracted_id === attrs.origin_id && attributes.from === attrs.from && !attributes.moderated_by); - } - }, - - /** - * Handles message retraction based on the passed in attributes. - * @private - * @method _converse.ChatBox#handleRetraction - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMessage} - * @returns { Boolean } Returns `true` or `false` depending on - * whether a message was retracted or not. - */ - async handleRetraction(attrs) { - const RETRACTION_ATTRIBUTES = ['retracted', 'retracted_id', 'editable']; - - if (attrs.retracted) { - if (attrs.is_tombstone) { - return false; - } - - const message = this.messages.findWhere({ - 'origin_id': attrs.retracted_id, - 'from': attrs.from - }); - - if (!message) { - attrs['dangling_retraction'] = true; - await this.createMessage(attrs); - return true; - } - - message.save(lodash_es_pick(attrs, RETRACTION_ATTRIBUTES)); - return true; - } else { - // Check if we have dangling retraction - const message = this.findDanglingRetraction(attrs); - - if (message) { - const retraction_attrs = lodash_es_pick(message.attributes, RETRACTION_ATTRIBUTES); - const new_attrs = Object.assign({ - 'dangling_retraction': false - }, attrs, retraction_attrs); - delete new_attrs['id']; // Delete id, otherwise a new cache entry gets created - - message.save(new_attrs); - return true; - } - } - - return false; - }, - - /** - * Determines whether the passed in message attributes represent a - * message which corrects a previously received message, or an - * older message which has already been corrected. - * In both cases, update the corrected message accordingly. - * @private - * @method _converse.ChatBox#handleCorrection - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMessage} - * @returns { _converse.Message|undefined } Returns the corrected - * message or `undefined` if not applicable. - */ - handleCorrection(attrs) { - if (!attrs.replace_id || !attrs.from) { - return; - } - - const message = this.messages.findWhere({ - 'msgid': attrs.replace_id, - 'from': attrs.from - }); - - if (!message) { - return; - } - - const older_versions = message.get('older_versions') || {}; - - if (attrs.time < message.get('time') && message.get('edited')) { - // This is an older message which has been corrected afterwards - older_versions[attrs.time] = attrs['message']; - message.save({ - 'older_versions': older_versions - }); - } else { - // This is a correction of an earlier message we already received - if (Object.keys(older_versions).length) { - older_versions[message.get('edited')] = message.get('message'); - } else { - older_versions[message.get('time')] = message.get('message'); - } - - attrs = Object.assign(attrs, { - 'older_versions': older_versions - }); - delete attrs['id']; // Delete id, otherwise a new cache entry gets created - - attrs['time'] = message.get('time'); - message.save(attrs); - } - - return message; - }, - - /** - * Returns an already cached message (if it exists) based on the - * passed in attributes map. - * @private - * @method _converse.ChatBox#getDuplicateMessage - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMessage} - * @returns {Promise<_converse.Message>} - */ - getDuplicateMessage(attrs) { - const queries = [...this.getStanzaIdQueryAttrs(attrs), this.getOriginIdQueryAttrs(attrs), this.getMessageBodyQueryAttrs(attrs)].filter(s => s); - const msgs = this.messages.models; - return msgs.find(m => queries.reduce((out, q) => out || lodash_es_isMatch(m.attributes, q), false)); - }, - - getOriginIdQueryAttrs(attrs) { - return attrs.origin_id && { - 'origin_id': attrs.origin_id, - 'from': attrs.from - }; - }, - - getStanzaIdQueryAttrs(attrs) { - const keys = Object.keys(attrs).filter(k => k.startsWith('stanza_id ')); - return keys.map(key => { - const by_jid = key.replace(/^stanza_id /, ''); - const query = {}; - query[`stanza_id ${by_jid}`] = attrs[key]; - return query; - }); - }, - - getMessageBodyQueryAttrs(attrs) { - if (attrs.message && attrs.msgid) { - const query = { - 'from': attrs.from, - 'msgid': attrs.msgid - }; - - if (!attrs.is_encrypted) { - // We can't match the message if it's a reflected - // encrypted message (e.g. via MAM or in a MUC) - query['message'] = attrs.message; - } - - return query; - } - }, - - /** - * Retract one of your messages in this chat - * @private - * @method _converse.ChatBoxView#retractOwnMessage - * @param { _converse.Message } message - The message which we're retracting. - */ - retractOwnMessage(message) { - this.sendRetractionMessage(message); - message.save({ - 'retracted': new Date().toISOString(), - 'retracted_id': message.get('origin_id'), - 'retraction_id': message.get('id'), - 'is_ephemeral': true, - 'editable': false - }); - }, - - /** - * Sends a message stanza to retract a message in this chat - * @private - * @method _converse.ChatBox#sendRetractionMessage - * @param { _converse.Message } message - The message which we're retracting. - */ - sendRetractionMessage(message) { - const origin_id = message.get('origin_id'); - - if (!origin_id) { - throw new Error("Can't retract message without a XEP-0359 Origin ID"); - } - - const msg = model_$msg({ - 'id': model_u.getUniqueId(), - 'to': this.get('jid'), - 'type': "chat" - }).c('store', { - xmlns: model_Strophe.NS.HINTS - }).up().c("apply-to", { - 'id': origin_id, - 'xmlns': model_Strophe.NS.FASTEN - }).c('retract', { - xmlns: model_Strophe.NS.RETRACT - }); - return shared_converse.connection.send(msg); - }, - - /** - * Finds the last eligible message and then sends a XEP-0333 chat marker for it. - * @param { ('received'|'displayed'|'acknowledged') } [type='displayed'] - * @param { Boolean } force - Whether a marker should be sent for the - * message, even if it didn't include a `markable` element. - */ - sendMarkerForLastMessage(type = 'displayed', force = false) { - const msgs = Array.from(this.messages.models); - msgs.reverse(); - const msg = msgs.find(m => m.get('sender') === 'them' && (force || m.get('is_markable'))); - msg && this.sendMarkerForMessage(msg, type, force); - }, - - /** - * Given the passed in message object, send a XEP-0333 chat marker. - * @param { _converse.Message } msg - * @param { ('received'|'displayed'|'acknowledged') } [type='displayed'] - * @param { Boolean } force - Whether a marker should be sent for the - * message, even if it didn't include a `markable` element. - */ - sendMarkerForMessage(msg, type = 'displayed', force = false) { - if (!msg || !api.settings.get('send_chat_markers').includes(type)) { - return; - } - - if (msg !== null && msg !== void 0 && msg.get('is_markable') || force) { - const from_jid = model_Strophe.getBareJidFromJid(msg.get('from')); - sendMarker(from_jid, msg.get('msgid'), type, msg.get('type')); - } - }, - - handleChatMarker(attrs) { - const to_bare_jid = model_Strophe.getBareJidFromJid(attrs.to); - - if (to_bare_jid !== shared_converse.bare_jid) { - return false; - } - - if (attrs.is_markable) { - if (this.contact && !attrs.is_archived && !attrs.is_carbon) { - sendMarker(attrs.from, attrs.msgid, 'received'); - } - - return false; - } else if (attrs.marker_id) { - const message = this.messages.findWhere({ - 'msgid': attrs.marker_id - }); - const field_name = `marker_${attrs.marker}`; - - if (message && !message.get(field_name)) { - message.save({ - field_name: new Date().toISOString() - }); - } - - return true; - } - }, - - sendReceiptStanza(to_jid, id) { - const receipt_stanza = model_$msg({ - 'from': shared_converse.connection.jid, - 'id': model_u.getUniqueId(), - 'to': to_jid, - 'type': 'chat' - }).c('received', { - 'xmlns': model_Strophe.NS.RECEIPTS, - 'id': id - }).up().c('store', { - 'xmlns': model_Strophe.NS.HINTS - }).up(); - api.send(receipt_stanza); - }, - - handleReceipt(attrs) { - if (attrs.sender === 'them') { - if (attrs.is_valid_receipt_request) { - this.sendReceiptStanza(attrs.from, attrs.msgid); - } else if (attrs.receipt_id) { - const message = this.messages.findWhere({ - 'msgid': attrs.receipt_id - }); - - if (message && !message.get('received')) { - message.save({ - 'received': new Date().toISOString() - }); - } - - return true; - } - } - - return false; - }, - - /** - * Given a {@link _converse.Message} return the XML stanza that represents it. - * @private - * @method _converse.ChatBox#createMessageStanza - * @param { _converse.Message } message - The message object - */ - createMessageStanza(message) { - const stanza = model_$msg({ - 'from': shared_converse.connection.jid, - 'to': this.get('jid'), - 'type': this.get('message_type'), - 'id': message.get('edited') && model_u.getUniqueId() || message.get('msgid') - }).c('body').t(message.get('message')).up().c(shared_converse.ACTIVE, { - 'xmlns': model_Strophe.NS.CHATSTATES - }).root(); - - if (message.get('type') === 'chat') { - stanza.c('request', { - 'xmlns': model_Strophe.NS.RECEIPTS - }).root(); - } - - if (message.get('is_spoiler')) { - if (message.get('spoiler_hint')) { - stanza.c('spoiler', { - 'xmlns': model_Strophe.NS.SPOILER - }, message.get('spoiler_hint')).root(); - } else { - stanza.c('spoiler', { - 'xmlns': model_Strophe.NS.SPOILER - }).root(); - } - } - - (message.get('references') || []).forEach(reference => { - const attrs = { - 'xmlns': model_Strophe.NS.REFERENCE, - 'begin': reference.begin, - 'end': reference.end, - 'type': reference.type - }; - - if (reference.uri) { - attrs.uri = reference.uri; - } - - stanza.c('reference', attrs).root(); - }); - - if (message.get('oob_url')) { - stanza.c('x', { - 'xmlns': model_Strophe.NS.OUTOFBAND - }).c('url').t(message.get('oob_url')).root(); - } - - if (message.get('edited')) { - stanza.c('replace', { - 'xmlns': model_Strophe.NS.MESSAGE_CORRECT, - 'id': message.get('msgid') - }).root(); - } - - if (message.get('origin_id')) { - stanza.c('origin-id', { - 'xmlns': model_Strophe.NS.SID, - 'id': message.get('origin_id') - }).root(); - } - - return stanza; - }, - - getOutgoingMessageAttributes(attrs) { - const is_spoiler = !!this.get('composing_spoiler'); - const origin_id = model_u.getUniqueId(); - const text = attrs === null || attrs === void 0 ? void 0 : attrs.body; - const body = text ? model_u.httpToGeoUri(model_u.shortnamesToUnicode(text), shared_converse) : undefined; - return Object.assign({}, attrs, { - 'from': shared_converse.bare_jid, - 'fullname': shared_converse.xmppstatus.get('fullname'), - 'id': origin_id, - 'is_only_emojis': text ? model_u.isOnlyEmojis(text) : false, - 'jid': this.get('jid'), - 'message': body, - 'msgid': origin_id, - 'nickname': this.get('nickname'), - 'sender': 'me', - 'time': new Date().toISOString(), - 'type': this.get('message_type'), - body, - is_spoiler, - origin_id - }, getMediaURLs(text)); - }, - - /** - * Responsible for setting the editable attribute of messages. - * If api.settings.get('allow_message_corrections') is "last", then only the last - * message sent from me will be editable. If set to "all" all messages - * will be editable. Otherwise no messages will be editable. - * @method _converse.ChatBox#setEditable - * @memberOf _converse.ChatBox - * @param { Object } attrs An object containing message attributes. - * @param { String } send_time - time when the message was sent - */ - setEditable(attrs, send_time) { - if (attrs.is_headline || model_u.isEmptyMessage(attrs) || attrs.sender !== 'me') { - return; - } - - if (api.settings.get('allow_message_corrections') === 'all') { - attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs); - } else if (api.settings.get('allow_message_corrections') === 'last' && send_time > this.get('time_sent')) { - this.set({ - 'time_sent': send_time - }); - const msg = this.messages.findWhere({ - 'editable': true - }); - - if (msg) { - msg.save({ - 'editable': false - }); - } - - attrs.editable = !(attrs.file || attrs.retracted || 'oob_url' in attrs); - } - }, - - /** - * Queue the creation of a message, to make sure that we don't run - * into a race condition whereby we're creating a new message - * before the collection has been fetched. - * @async - * @private - * @method _converse.ChatBox#queueMessageCreation - * @param { Object } attrs - */ - async createMessage(attrs, options) { - attrs.time = attrs.time || new Date().toISOString(); - await this.messages.fetched; - return this.messages.create(attrs, options); - }, - - /** - * Responsible for sending off a text message inside an ongoing chat conversation. - * @private - * @method _converse.ChatBox#sendMessage - * @memberOf _converse.ChatBox - * @param { Object } [attrs] - A map of attributes to be saved on the message - * @returns { _converse.Message } - * @example - * const chat = api.chats.get('buddy1@example.org'); - * chat.sendMessage({'body': 'hello world'}); - */ - async sendMessage(attrs) { - attrs = this.getOutgoingMessageAttributes(attrs); - let message = this.messages.findWhere('correcting'); - - if (message) { - const older_versions = message.get('older_versions') || {}; - older_versions[message.get('time')] = message.get('message'); - message.save({ - 'correcting': false, - 'edited': new Date().toISOString(), - 'message': attrs.message, - 'older_versions': older_versions, - 'references': attrs.references, - 'is_only_emojis': attrs.is_only_emojis, - 'origin_id': model_u.getUniqueId(), - 'received': undefined - }); - } else { - this.setEditable(attrs, new Date().toISOString()); - message = await this.createMessage(attrs); - } - - api.send(this.createMessageStanza(message)); - /** - * Triggered when a message is being sent out - * @event _converse#sendMessage - * @type { Object } - * @param { Object } data - * @property { (_converse.ChatBox | _converse.ChatRoom) } data.chatbox - * @property { (_converse.Message | _converse.ChatRoomMessage) } data.message - */ - - api.trigger('sendMessage', { - 'chatbox': this, - message - }); - return message; - }, - - /** - * Sends a message with the current XEP-0085 chat state of the user - * as taken from the `chat_state` attribute of the {@link _converse.ChatBox}. - * @private - * @method _converse.ChatBox#sendChatState - */ - sendChatState() { - if (api.settings.get('send_chat_state_notifications') && this.get('chat_state')) { - const allowed = api.settings.get('send_chat_state_notifications'); - - if (Array.isArray(allowed) && !allowed.includes(this.get('chat_state'))) { - return; - } - - api.send(model_$msg({ - 'id': model_u.getUniqueId(), - 'to': this.get('jid'), - 'type': 'chat' - }).c(this.get('chat_state'), { - 'xmlns': model_Strophe.NS.CHATSTATES - }).up().c('no-store', { - 'xmlns': model_Strophe.NS.HINTS - }).up().c('no-permanent-store', { - 'xmlns': model_Strophe.NS.HINTS - })); - } - }, - - async sendFiles(files) { - var _maxFileSize; - - const { - __ - } = shared_converse; - const result = await api.disco.features.get(model_Strophe.NS.HTTPUPLOAD, shared_converse.domain); - const item = result.pop(); - - if (!item) { - this.createMessage({ - 'message': __("Sorry, looks like file upload is not supported by your server."), - 'type': 'error', - 'is_ephemeral': true - }); - return; - } - - const data = item.dataforms.where({ - 'FORM_TYPE': { - 'value': model_Strophe.NS.HTTPUPLOAD, - 'type': "hidden" - } - }).pop(); - const max_file_size = window.parseInt((_maxFileSize = ((data === null || data === void 0 ? void 0 : data.attributes) || {})['max-file-size']) === null || _maxFileSize === void 0 ? void 0 : _maxFileSize.value); - const slot_request_url = item === null || item === void 0 ? void 0 : item.id; - - if (!slot_request_url) { - this.createMessage({ - 'message': __("Sorry, looks like file upload is not supported by your server."), - 'type': 'error', - 'is_ephemeral': true - }); - return; - } - - Array.from(files).forEach(async file => { - /** - * *Hook* which allows plugins to transform files before they'll be - * uploaded. The main use-case is to encrypt the files. - * @event _converse#beforeFileUpload - */ - file = await api.hook('beforeFileUpload', this, file); - - if (!window.isNaN(max_file_size) && window.parseInt(file.size) > max_file_size) { - return this.createMessage({ - 'message': __('The size of your file, %1$s, exceeds the maximum allowed by your server, which is %2$s.', file.name, filesize_min_default()(max_file_size)), - 'type': 'error', - 'is_ephemeral': true - }); - } else { - const attrs = Object.assign(this.getOutgoingMessageAttributes(), { - 'file': true, - 'progress': 0, - 'slot_request_url': slot_request_url - }); - this.setEditable(attrs, new Date().toISOString()); - const message = await this.createMessage(attrs, { - 'silent': true - }); - message.file = file; - this.messages.trigger('add', message); - message.getRequestSlotURL(); - } - }); - }, - - maybeShow(force) { - if (shared_converse.isUniView()) { - const filter = c => !c.get('hidden') && c.get('jid') !== this.get('jid') && c.get('id') !== 'controlbox'; - - const other_chats = shared_converse.chatboxes.filter(filter); - - if (force || other_chats.length === 0) { - // We only have one chat visible at any one time. - // So before opening a chat, we make sure all other chats are hidden. - other_chats.forEach(c => model_u.safeSave(c, { - 'hidden': true - })); - model_u.safeSave(this, { - 'hidden': false - }); - } - - return; - } - - model_u.safeSave(this, { - 'hidden': false - }); - this.trigger('show'); - return this; - }, - - /** - * Indicates whether the chat is hidden and therefore - * whether a newly received message will be visible - * to the user or not. - * @returns {boolean} - */ - isHidden() { - // Note: This methods gets overridden by converse-minimize - return this.get('hidden') || this.isScrolledUp() || shared_converse.windowState === 'hidden'; - }, - - /** - * Given a newly received {@link _converse.Message} instance, - * update the unread counter if necessary. - * @private - * @method _converse.ChatBox#handleUnreadMessage - * @param {_converse.Message} message - */ - handleUnreadMessage(message) { - if (!(message !== null && message !== void 0 && message.get('body'))) { - return; - } - - if (model_u.isNewMessage(message)) { - if (message.get('sender') === 'me') { - // We remove the "scrolled" flag so that the chat area - // gets scrolled down. We always want to scroll down - // when the user writes a message as opposed to when a - // message is received. - this.ui.set('scrolled', false); - } else if (this.isHidden()) { - this.incrementUnreadMsgsCounter(message); - } else { - this.sendMarkerForMessage(message); - } - } - }, - - incrementUnreadMsgsCounter(message) { - const settings = { - 'num_unread': this.get('num_unread') + 1 - }; - - if (this.get('num_unread') === 0) { - settings['first_unread_id'] = message.get('id'); - } - - this.save(settings); - }, - - clearUnreadMsgCounter() { - if (this.get('num_unread') > 0) { - this.sendMarkerForMessage(this.messages.last()); - } - - model_u.safeSave(this, { - 'num_unread': 0 - }); - }, - - isScrolledUp() { - return this.ui.get('scrolled'); - } - -}); -/* harmony default export */ const model = (ChatBox); -;// CONCATENATED MODULE: ./src/headless/plugins/chat/message.js - - - - - -const message_u = core_converse.env.utils; -const { - Strophe: message_Strophe -} = core_converse.env; -/** - * Mixin which turns a `ModelWithContact` model into a non-MUC message. These can be either `chat` messages or `headline` messages. - * @mixin - * @namespace _converse.Message - * @memberOf _converse - * @example const msg = new _converse.Message({'message': 'hello world!'}); - */ - -const MessageMixin = { - defaults() { - return { - 'msgid': message_u.getUniqueId(), - 'time': new Date().toISOString(), - 'is_ephemeral': false - }; - }, - - async initialize() { - if (!this.checkValidity()) { - return; - } - - this.initialized = getOpenPromise(); - - if (this.get('type') === 'chat') { - model_with_contact.prototype.initialize.apply(this, arguments); - this.setRosterContact(message_Strophe.getBareJidFromJid(this.get('from'))); - } - - if (this.get('file')) { - this.on('change:put', this.uploadFile, this); - } - - this.setTimerForEphemeralMessage(); - /** - * Triggered once a {@link _converse.Message} has been created and initialized. - * @event _converse#messageInitialized - * @type { _converse.Message} - * @example _converse.api.listen.on('messageInitialized', model => { ... }); - */ - - await api.trigger('messageInitialized', this, { - 'Synchronous': true - }); - this.initialized.resolve(); - }, - - /** - * Sets an auto-destruct timer for this message, if it's is_ephemeral. - * @private - * @method _converse.Message#setTimerForEphemeralMessage - * @returns { Boolean } - Indicates whether the message is - * ephemeral or not, and therefore whether the timer was set or not. - */ - setTimerForEphemeralMessage() { - const setTimer = () => { - this.ephemeral_timer = window.setTimeout(this.safeDestroy.bind(this), 10000); - }; - - if (this.isEphemeral()) { - setTimer(); - return true; - } else { - this.on('change:is_ephemeral', () => this.isEphemeral() ? setTimer() : clearTimeout(this.ephemeral_timer)); - return false; - } - }, - - checkValidity() { - if (Object.keys(this.attributes).length === 3) { - // XXX: This is an empty message with only the 3 default values. - // This seems to happen when saving a newly created message - // fails for some reason. - // TODO: This is likely fixable by setting `wait` when - // creating messages. See the wait-for-messages branch. - this.validationError = 'Empty message'; - this.safeDestroy(); - return false; - } - - return true; - }, - - /** - * Determines whether this messsage may be retracted by the current user. - * @private - * @method _converse.Messages#mayBeRetracted - * @returns { Boolean } - */ - mayBeRetracted() { - const is_own_message = this.get('sender') === 'me'; - const not_canceled = this.get('error_type') !== 'cancel'; - return is_own_message && not_canceled && ['all', 'own'].includes(api.settings.get('allow_message_retraction')); - }, - - safeDestroy() { - try { - this.destroy(); - } catch (e) { - headless_log.error(e); - } - }, - - /** - * Returns a boolean indicating whether this message is ephemeral, - * meaning it will get automatically removed after ten seconds. - * @returns { boolean } - */ - isEphemeral() { - return this.get('is_ephemeral'); - }, - - /** - * Returns a boolean indicating whether this message is a XEP-0245 /me command. - * @returns { boolean } - */ - isMeCommand() { - const text = this.getMessageText(); - - if (!text) { - return false; - } - - return text.startsWith('/me '); - }, - - /** - * Returns a boolean indicating whether this message is considered a followup - * message from the previous one. Followup messages are shown grouped together - * under one author heading. - * A message is considered a followup of it's predecessor when it's a chat - * message from the same author, within 10 minutes. - * @returns { boolean } - */ - isFollowup() { - const messages = this.collection.models; - const idx = messages.indexOf(this); - const prev_model = idx ? messages[idx - 1] : null; - - if (prev_model === null) { - return false; - } - - const date = dayjs_min_default()(this.get('time')); - return this.get('from') === prev_model.get('from') && !this.isMeCommand() && !prev_model.isMeCommand() && this.get('type') !== 'info' && prev_model.get('type') !== 'info' && date.isBefore(dayjs_min_default()(prev_model.get('time')).add(10, 'minutes')) && !!this.get('is_encrypted') === !!prev_model.get('is_encrypted'); - }, - - getDisplayName() { - if (this.get('type') === 'groupchat') { - return this.get('nick'); - } else if (this.contact) { - return this.contact.getDisplayName(); - } else if (this.vcard) { - return this.vcard.getDisplayName(); - } else { - return this.get('from'); - } - }, - - getMessageText() { - const { - __ - } = shared_converse; - - if (this.get('is_encrypted')) { - return this.get('plaintext') || this.get('body') || __('Undecryptable OMEMO message'); - } - - return this.get('message'); - }, - - /** - * Send out an IQ stanza to request a file upload slot. - * https://xmpp.org/extensions/xep-0363.html#request - * @private - * @method _converse.Message#sendSlotRequestStanza - */ - sendSlotRequestStanza() { - if (!this.file) { - return Promise.reject(new Error('file is undefined')); - } - - const iq = core_converse.env.$iq({ - 'from': shared_converse.jid, - 'to': this.get('slot_request_url'), - 'type': 'get' - }).c('request', { - 'xmlns': message_Strophe.NS.HTTPUPLOAD, - 'filename': this.file.name, - 'size': this.file.size, - 'content-type': this.file.type - }); - return api.sendIQ(iq); - }, - - async getRequestSlotURL() { - const { - __ - } = shared_converse; - let stanza; - - try { - stanza = await this.sendSlotRequestStanza(); - } catch (e) { - headless_log.error(e); - return this.save({ - 'type': 'error', - 'message': __('Sorry, could not determine upload URL.'), - 'is_ephemeral': true - }); - } - - const slot = stanza.querySelector('slot'); - - if (slot) { - this.save({ - 'get': slot.querySelector('get').getAttribute('url'), - 'put': slot.querySelector('put').getAttribute('url') - }); - } else { - return this.save({ - 'type': 'error', - 'message': __('Sorry, could not determine file upload URL.'), - 'is_ephemeral': true - }); - } - }, - - uploadFile() { - const xhr = new XMLHttpRequest(); - - xhr.onreadystatechange = async () => { - if (xhr.readyState === XMLHttpRequest.DONE) { - headless_log.info('Status: ' + xhr.status); - - if (xhr.status === 200 || xhr.status === 201) { - let attrs = { - 'upload': shared_converse.SUCCESS, - 'oob_url': this.get('get'), - 'message': this.get('get'), - 'body': this.get('get') - }; - /** - * *Hook* which allows plugins to change the attributes - * saved on the message once a file has been uploaded. - * @event _converse#afterFileUploaded - */ - - attrs = await api.hook('afterFileUploaded', this, attrs); - this.save(attrs); - } else { - xhr.onerror(); - } - } - }; - - xhr.upload.addEventListener('progress', evt => { - if (evt.lengthComputable) { - this.set('progress', evt.loaded / evt.total); - } - }, false); - - xhr.onerror = () => { - const { - __ - } = shared_converse; - let message; - - if (xhr.responseText) { - message = __('Sorry, could not succesfully upload your file. Your server’s response: "%1$s"', xhr.responseText); - } else { - message = __('Sorry, could not succesfully upload your file.'); - } - - this.save({ - 'type': 'error', - 'upload': shared_converse.FAILURE, - 'message': message, - 'is_ephemeral': true - }); - }; - - xhr.open('PUT', this.get('put'), true); - xhr.setRequestHeader('Content-type', this.file.type); - xhr.send(this.file); - } - -}; -/* harmony default export */ const message = (MessageMixin); -;// CONCATENATED MODULE: ./src/headless/plugins/chat/api.js - - -/* harmony default export */ const chat_api = ({ - /** - * The "chats" namespace (used for one-on-one chats) - * - * @namespace api.chats - * @memberOf api - */ - chats: { - /** - * @method api.chats.create - * @param {string|string[]} jid|jids An jid or array of jids - * @param {object} [attrs] An object containing configuration attributes. - */ - async create(jids, attrs) { - if (typeof jids === 'string') { - if (attrs && !(attrs !== null && attrs !== void 0 && attrs.fullname)) { - var _contact$attributes; - - const contact = await api.contacts.get(jids); - attrs.fullname = contact === null || contact === void 0 ? void 0 : (_contact$attributes = contact.attributes) === null || _contact$attributes === void 0 ? void 0 : _contact$attributes.fullname; - } - - const chatbox = api.chats.get(jids, attrs, true); - - if (!chatbox) { - headless_log.error("Could not open chatbox for JID: " + jids); - return; - } - - return chatbox; - } - - if (Array.isArray(jids)) { - return Promise.all(jids.forEach(async jid => { - var _contact$attributes2; - - const contact = await api.contacts.get(jids); - attrs.fullname = contact === null || contact === void 0 ? void 0 : (_contact$attributes2 = contact.attributes) === null || _contact$attributes2 === void 0 ? void 0 : _contact$attributes2.fullname; - return api.chats.get(jid, attrs, true).maybeShow(); - })); - } - - headless_log.error("chats.create: You need to provide at least one JID"); - return null; - }, - - /** - * Opens a new one-on-one chat. - * - * @method api.chats.open - * @param {String|string[]} name - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] - * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. - * @param {Boolean} [attrs.minimized] - Should the chat be created in minimized state. - * @param {Boolean} [force=false] - By default, a minimized - * chat won't be maximized (in `overlayed` view mode) and in - * `fullscreen` view mode a newly opened chat won't replace - * another chat already in the foreground. - * Set `force` to `true` if you want to force the chat to be - * maximized or shown. - * @returns {Promise} Promise which resolves with the - * _converse.ChatBox representing the chat. - * - * @example - * // To open a single chat, provide the JID of the contact you're chatting with in that chat: - * converse.plugins.add('myplugin', { - * initialize: function() { - * const _converse = this._converse; - * // Note, buddy@example.org must be in your contacts roster! - * api.chats.open('buddy@example.com').then(chat => { - * // Now you can do something with the chat model - * }); - * } - * }); - * - * @example - * // To open an array of chats, provide an array of JIDs: - * converse.plugins.add('myplugin', { - * initialize: function () { - * const _converse = this._converse; - * // Note, these users must first be in your contacts roster! - * api.chats.open(['buddy1@example.com', 'buddy2@example.com']).then(chats => { - * // Now you can do something with the chat models - * }); - * } - * }); - */ - async open(jids, attrs, force) { - if (typeof jids === 'string') { - const chat = await api.chats.get(jids, attrs, true); - - if (chat) { - return chat.maybeShow(force); - } - - return chat; - } else if (Array.isArray(jids)) { - return Promise.all(jids.map(j => api.chats.get(j, attrs, true).then(c => c && c.maybeShow(force))).filter(c => c)); - } - - const err_msg = "chats.open: You need to provide at least one JID"; - headless_log.error(err_msg); - throw new Error(err_msg); - }, - - /** - * Retrieves a chat or all chats. - * - * @method api.chats.get - * @param {String|string[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] - * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. - * @param {Boolean} [create=false] - Whether the chat should be created if it's not found. - * @returns { Promise<_converse.ChatBox> } - * - * @example - * // To return a single chat, provide the JID of the contact you're chatting with in that chat: - * const model = await api.chats.get('buddy@example.com'); - * - * @example - * // To return an array of chats, provide an array of JIDs: - * const models = await api.chats.get(['buddy1@example.com', 'buddy2@example.com']); - * - * @example - * // To return all open chats, call the method without any parameters:: - * const models = await api.chats.get(); - * - */ - async get(jids, attrs = {}, create = false) { - await api.waitUntil('chatBoxesFetched'); - - async function _get(jid) { - let model = await api.chatboxes.get(jid); - - if (!model && create) { - model = await api.chatboxes.create(jid, attrs, shared_converse.ChatBox); - } else { - model = model && model.get('type') === shared_converse.PRIVATE_CHAT_TYPE ? model : null; - - if (model && Object.keys(attrs).length) { - model.save(attrs); - } - } - - return model; - } - - if (jids === undefined) { - const chats = await api.chatboxes.get(); - return chats.filter(c => c.get('type') === shared_converse.PRIVATE_CHAT_TYPE); - } else if (typeof jids === 'string') { - return _get(jids); - } - - return Promise.all(jids.map(jid => _get(jid))); - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/chat/utils.js - - - - -const { - Strophe: utils_Strophe, - sizzle: utils_sizzle, - u: chat_utils_u -} = core_converse.env; -function openChat(jid) { - if (!chat_utils_u.isValidJID(jid)) { - return headless_log.warn(`Invalid JID "${jid}" provided in URL fragment`); - } - - api.chats.open(jid); -} -async function onClearSession() { - if (shared_converse.shouldClearCache()) { - await Promise.all(shared_converse.chatboxes.map(c => c.messages && c.messages.clearStore({ - 'silent': true - }))); - - const filter = o => o.get('type') !== shared_converse.CONTROLBOX_TYPE; - - shared_converse.chatboxes.clearStore({ - 'silent': true - }, filter); - } -} - -async function handleErrorMessage(stanza) { - const from_jid = utils_Strophe.getBareJidFromJid(stanza.getAttribute('from')); - - if (chat_utils_u.isSameBareJID(from_jid, shared_converse.bare_jid)) { - return; - } - - const chatbox = await api.chatboxes.get(from_jid); - - if (chatbox.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - chatbox === null || chatbox === void 0 ? void 0 : chatbox.handleErrorMessageStanza(stanza); - } -} - -function autoJoinChats() { - // Automatically join private chats, based on the - // "auto_join_private_chats" configuration setting. - api.settings.get('auto_join_private_chats').forEach(jid => { - if (shared_converse.chatboxes.where({ - 'jid': jid - }).length) { - return; - } - - if (typeof jid === 'string') { - api.chats.open(jid); - } else { - headless_log.error('Invalid jid criteria specified for "auto_join_private_chats"'); - } - }); - /** - * Triggered once any private chats have been automatically joined as - * specified by the `auto_join_private_chats` setting. - * See: https://conversejs.org/docs/html/configuration.html#auto-join-private-chats - * @event _converse#privateChatsAutoJoined - * @example _converse.api.listen.on('privateChatsAutoJoined', () => { ... }); - * @example _converse.api.waitUntil('privateChatsAutoJoined').then(() => { ... }); - */ - - api.trigger('privateChatsAutoJoined'); -} -function registerMessageHandlers() { - shared_converse.connection.addHandler(stanza => { - if (utils_sizzle(`message > result[xmlns="${utils_Strophe.NS.MAM}"]`, stanza).pop()) { - // MAM messages are handled in converse-mam. - // We shouldn't get MAM messages here because - // they shouldn't have a `type` attribute. - headless_log.warn(`Received a MAM message with type "chat".`); - return true; - } - - shared_converse.handleMessageStanza(stanza); - - return true; - }, null, 'message', 'chat'); - - shared_converse.connection.addHandler(stanza => { - // Message receipts are usually without the `type` attribute. See #1353 - if (stanza.getAttribute('type') !== null) { - // TODO: currently Strophe has no way to register a handler - // for stanzas without a `type` attribute. - // We could update it to accept null to mean no attribute, - // but that would be a backward-incompatible change - return true; // Gets handled above. - } - - shared_converse.handleMessageStanza(stanza); - - return true; - }, utils_Strophe.NS.RECEIPTS, 'message'); - - shared_converse.connection.addHandler(stanza => { - handleErrorMessage(stanza); - return true; - }, null, 'message', 'error'); -} -/** - * Handler method for all incoming single-user chat "message" stanzas. - * @private - * @param { MessageAttributes } attrs - The message attributes - */ - -async function handleMessageStanza(stanza) { - if (isServerMessage(stanza)) { - // Prosody sends headline messages with type `chat`, so we need to filter them out here. - const from = stanza.getAttribute('from'); - return headless_log.info(`handleMessageStanza: Ignoring incoming server message from JID: ${from}`); - } - - const attrs = await parseMessage(stanza, shared_converse); - - if (chat_utils_u.isErrorObject(attrs)) { - attrs.stanza && headless_log.error(attrs.stanza); - return headless_log.error(attrs.message); - } - - const has_body = !!utils_sizzle(`body, encrypted[xmlns="${utils_Strophe.NS.OMEMO}"]`, stanza).length; - const chatbox = await api.chats.get(attrs.contact_jid, { - 'nickname': attrs.nick - }, has_body); - await (chatbox === null || chatbox === void 0 ? void 0 : chatbox.queueMessage(attrs)); - /** - * @typedef { Object } MessageData - * An object containing the original message stanza, as well as the - * parsed attributes. - * @property { XMLElement } stanza - * @property { MessageAttributes } stanza - * @property { ChatBox } chatbox - */ - - const data = { - stanza, - attrs, - chatbox - }; - /** - * Triggered when a message stanza is been received and processed. - * @event _converse#message - * @type { object } - * @property { module:converse-chat~MessageData } data - */ - - api.trigger('message', data); -} -;// CONCATENATED MODULE: ./src/headless/plugins/chat/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - -core_converse.plugins.add('converse-chat', { - /* Optional dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. They are called "optional" because they might not be - * available, in which case any overrides applicable to them will be - * ignored. - * - * It's possible however to make optional dependencies non-optional. - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ['converse-chatboxes', 'converse-disco'], - - initialize() { - // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - api.settings.extend({ - 'allow_message_corrections': 'all', - 'allow_message_retraction': 'all', - 'allow_message_styling': true, - 'auto_join_private_chats': [], - 'clear_messages_on_reconnection': false, - 'filter_by_resource': false, - 'prune_messages_above': undefined, - 'pruning_behavior': 'unscrolled', - 'send_chat_markers': ["received", "displayed", "acknowledged"], - 'send_chat_state_notifications': true - }); - shared_converse.Message = model_with_contact.extend(message); - shared_converse.Messages = Collection.extend({ - model: shared_converse.Message, - comparator: 'time' - }); - Object.assign(shared_converse, { - ChatBox: model, - handleMessageStanza: handleMessageStanza - }); - Object.assign(api, chat_api); - - shared_converse.router.route('converse/chat?jid=:jid', openChat); - - api.listen.on('chatBoxesFetched', autoJoinChats); - api.listen.on('presencesInitialized', registerMessageHandlers); - api.listen.on('clearSession', onClearSession); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/disco/entity.js - - - - - - -const { - Strophe: entity_Strophe -} = core_converse.env; -/** - * @class - * @namespace _converse.DiscoEntity - * @memberOf _converse - * - * A Disco Entity is a JID addressable entity that can be queried for features. - * - * See XEP-0030: https://xmpp.org/extensions/xep-0030.html - */ - -const DiscoEntity = Model.extend({ - idAttribute: 'jid', - - initialize(attrs, options) { - this.waitUntilFeaturesDiscovered = getOpenPromise(); - this.dataforms = new Collection(); - let id = `converse.dataforms-${this.get('jid')}`; - this.dataforms.browserStorage = shared_converse.createStore(id, 'session'); - this.features = new Collection(); - id = `converse.features-${this.get('jid')}`; - this.features.browserStorage = shared_converse.createStore(id, 'session'); - this.listenTo(this.features, 'add', this.onFeatureAdded); - this.fields = new Collection(); - id = `converse.fields-${this.get('jid')}`; - this.fields.browserStorage = shared_converse.createStore(id, 'session'); - this.listenTo(this.fields, 'add', this.onFieldAdded); - this.identities = new Collection(); - id = `converse.identities-${this.get('jid')}`; - this.identities.browserStorage = shared_converse.createStore(id, 'session'); - this.fetchFeatures(options); - this.items = new shared_converse.DiscoEntities(); - id = `converse.disco-items-${this.get('jid')}`; - this.items.browserStorage = shared_converse.createStore(id, 'session'); - this.items.fetch(); - }, - - /** - * Returns a Promise which resolves with a map indicating - * whether a given identity is provided by this entity. - * @private - * @method _converse.DiscoEntity#getIdentity - * @param { String } category - The identity category - * @param { String } type - The identity type - */ - async getIdentity(category, type) { - await this.waitUntilFeaturesDiscovered; - return this.identities.findWhere({ - 'category': category, - 'type': type - }); - }, - - /** - * Returns a Promise which resolves with a map indicating - * whether a given feature is supported. - * @private - * @method _converse.DiscoEntity#hasFeature - * @param { String } feature - The feature that might be supported. - */ - async hasFeature(feature) { - await this.waitUntilFeaturesDiscovered; - - if (this.features.findWhere({ - 'var': feature - })) { - return this; - } - }, - - onFeatureAdded(feature) { - feature.entity = this; - /** - * Triggered when Converse has learned of a service provided by the XMPP server. - * See XEP-0030. - * @event _converse#serviceDiscovered - * @type { Model } - * @example _converse.api.listen.on('featuresDiscovered', feature => { ... }); - */ - - api.trigger('serviceDiscovered', feature); - }, - - onFieldAdded(field) { - field.entity = this; - /** - * Triggered when Converse has learned of a disco extension field. - * See XEP-0030. - * @event _converse#discoExtensionFieldDiscovered - * @example _converse.api.listen.on('discoExtensionFieldDiscovered', () => { ... }); - */ - - api.trigger('discoExtensionFieldDiscovered', field); - }, - - async fetchFeatures(options) { - if (options.ignore_cache) { - this.queryInfo(); - } else { - const store_id = this.features.browserStorage.name; - const result = await this.features.browserStorage.store.getItem(store_id); - - if (result && result.length === 0 || result === null) { - this.queryInfo(); - } else { - this.features.fetch({ - add: true, - success: () => { - this.waitUntilFeaturesDiscovered.resolve(this); - this.trigger('featuresDiscovered'); - } - }); - this.identities.fetch({ - add: true - }); - } - } - }, - - async queryInfo() { - let stanza; - - try { - stanza = await api.disco.info(this.get('jid'), null); - } catch (iq) { - iq === null ? headless_log.error(`Timeout for disco#info query for ${this.get('jid')}`) : headless_log.error(iq); - this.waitUntilFeaturesDiscovered.resolve(this); - return; - } - - this.onInfo(stanza); - }, - - onDiscoItems(stanza) { - sizzle_default()(`query[xmlns="${entity_Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => { - if (item.getAttribute("node")) { - // XXX: Ignore nodes for now. - // See: https://xmpp.org/extensions/xep-0030.html#items-nodes - return; - } - - const jid = item.getAttribute('jid'); - - if (this.items.get(jid) === undefined) { - const entity = shared_converse.disco_entities.get(jid); - - if (entity) { - this.items.add(entity); - } else { - this.items.create({ - 'jid': jid - }); - } - } - }); - }, - - async queryForItems() { - if (this.identities.where({ - 'category': 'server' - }).length === 0) { - // Don't fetch features and items if this is not a - // server or a conference component. - return; - } - - const stanza = await api.disco.items(this.get('jid')); - this.onDiscoItems(stanza); - }, - - onInfo(stanza) { - Array.from(stanza.querySelectorAll('identity')).forEach(identity => { - this.identities.create({ - 'category': identity.getAttribute('category'), - 'type': identity.getAttribute('type'), - 'name': identity.getAttribute('name') - }); - }); - sizzle_default()(`x[type="result"][xmlns="${entity_Strophe.NS.XFORM}"]`, stanza).forEach(form => { - const data = {}; - sizzle_default()('field', form).forEach(field => { - var _field$querySelector; - - data[field.getAttribute('var')] = { - 'value': (_field$querySelector = field.querySelector('value')) === null || _field$querySelector === void 0 ? void 0 : _field$querySelector.textContent, - 'type': field.getAttribute('type') - }; - }); - this.dataforms.create(data); - }); - - if (stanza.querySelector(`feature[var="${entity_Strophe.NS.DISCO_ITEMS}"]`)) { - this.queryForItems(); - } - - Array.from(stanza.querySelectorAll('feature')).forEach(feature => { - this.features.create({ - 'var': feature.getAttribute('var'), - 'from': stanza.getAttribute('from') - }); - }); // XEP-0128 Service Discovery Extensions - - sizzle_default()('x[type="result"][xmlns="jabber:x:data"] field', stanza).forEach(field => { - var _field$querySelector2; - - this.fields.create({ - 'var': field.getAttribute('var'), - 'value': (_field$querySelector2 = field.querySelector('value')) === null || _field$querySelector2 === void 0 ? void 0 : _field$querySelector2.textContent, - 'from': stanza.getAttribute('from') - }); - }); - this.waitUntilFeaturesDiscovered.resolve(this); - this.trigger('featuresDiscovered'); - } - -}); -/* harmony default export */ const entity = (DiscoEntity); -;// CONCATENATED MODULE: ./src/headless/plugins/disco/entities.js - - - -const DiscoEntities = Collection.extend({ - model: entity, - - fetchEntities() { - return new Promise((resolve, reject) => { - this.fetch({ - add: true, - success: resolve, - - error(m, e) { - headless_log.error(e); - reject(new Error("Could not fetch disco entities")); - } - - }); - }); - } - -}); -/* harmony default export */ const entities = (DiscoEntities); -;// CONCATENATED MODULE: ./src/headless/plugins/disco/utils.js - - -const { - Strophe: disco_utils_Strophe, - $iq: utils_$iq -} = core_converse.env; - -function onDiscoInfoRequest(stanza) { - const node = stanza.getElementsByTagName('query')[0].getAttribute('node'); - const attrs = { - xmlns: disco_utils_Strophe.NS.DISCO_INFO - }; - - if (node) { - attrs.node = node; - } - - const iqresult = utils_$iq({ - 'type': 'result', - 'id': stanza.getAttribute('id') - }); - const from = stanza.getAttribute('from'); - - if (from !== null) { - iqresult.attrs({ - 'to': from - }); - } - - iqresult.c('query', attrs); - - shared_converse.disco._identities.forEach(identity => { - const attrs = { - 'category': identity.category, - 'type': identity.type - }; - - if (identity.name) { - attrs.name = identity.name; - } - - if (identity.lang) { - attrs['xml:lang'] = identity.lang; - } - - iqresult.c('identity', attrs).up(); - }); - - shared_converse.disco._features.forEach(feature => iqresult.c('feature', { - 'var': feature - }).up()); - - api.send(iqresult.tree()); - return true; -} - -function addClientFeatures() { - // See https://xmpp.org/registrar/disco-categories.html - api.disco.own.identities.add('client', 'web', 'Converse'); - api.disco.own.features.add(disco_utils_Strophe.NS.CHATSTATES); - api.disco.own.features.add(disco_utils_Strophe.NS.DISCO_INFO); - api.disco.own.features.add(disco_utils_Strophe.NS.ROSTERX); // Limited support - - if (api.settings.get("message_carbons")) { - api.disco.own.features.add(disco_utils_Strophe.NS.CARBONS); - } - /** - * Triggered in converse-disco once the core disco features of - * Converse have been added. - * @event _converse#addClientFeatures - * @example _converse.api.listen.on('addClientFeatures', () => { ... }); - */ - - - api.trigger('addClientFeatures'); - return this; -} - -async function initializeDisco() { - addClientFeatures(); - - shared_converse.connection.addHandler(stanza => onDiscoInfoRequest(stanza), disco_utils_Strophe.NS.DISCO_INFO, 'iq', 'get', null, null); - - shared_converse.disco_entities = new shared_converse.DiscoEntities(); - const id = `converse.disco-entities-${shared_converse.bare_jid}`; - shared_converse.disco_entities.browserStorage = shared_converse.createStore(id, 'session'); - const collection = await shared_converse.disco_entities.fetchEntities(); - - if (collection.length === 0 || !collection.get(shared_converse.domain)) { - // If we don't have an entity for our own XMPP server, - // create one. - shared_converse.disco_entities.create({ - 'jid': shared_converse.domain - }); - } - /** - * Triggered once the `converse-disco` plugin has been initialized and the - * `_converse.disco_entities` collection will be available and populated with at - * least the service discovery features of the user's own server. - * @event _converse#discoInitialized - * @example _converse.api.listen.on('discoInitialized', () => { ... }); - */ - - - api.trigger('discoInitialized'); -} -function initStreamFeatures() { - // Initialize the stream_features collection, and if we're - // re-attaching to a pre-existing BOSH session, we restore the - // features from cache. - // Otherwise the features will be created once we've received them - // from the server (see populateStreamFeatures). - if (!shared_converse.stream_features) { - const bare_jid = disco_utils_Strophe.getBareJidFromJid(shared_converse.jid); - const id = `converse.stream-features-${bare_jid}`; - api.promises.add('streamFeaturesAdded'); - shared_converse.stream_features = new Collection(); - shared_converse.stream_features.browserStorage = shared_converse.createStore(id, "session"); - } -} -function notifyStreamFeaturesAdded() { - /** - * Triggered as soon as the stream features are known. - * If you want to check whether a stream feature is supported before proceeding, - * then you'll first want to wait for this event. - * @event _converse#streamFeaturesAdded - * @example _converse.api.listen.on('streamFeaturesAdded', () => { ... }); - */ - api.trigger('streamFeaturesAdded'); -} -function populateStreamFeatures() { - // Strophe.js sets the element on the - // Strophe.Connection instance (_converse.connection). - // - // Once this is we populate the _converse.stream_features collection - // and trigger streamFeaturesAdded. - initStreamFeatures(); - Array.from(shared_converse.connection.features.childNodes).forEach(feature => { - shared_converse.stream_features.create({ - 'name': feature.nodeName, - 'xmlns': feature.getAttribute('xmlns') - }); - }); - notifyStreamFeaturesAdded(); -} -function utils_clearSession() { - var _converse$disco_entit, _converse$disco_entit2, _converse$disco_entit3, _converse$disco_entit4, _converse$disco_entit5; - - (_converse$disco_entit = shared_converse.disco_entities) === null || _converse$disco_entit === void 0 ? void 0 : _converse$disco_entit.forEach(e => e.features.clearStore()); - (_converse$disco_entit2 = shared_converse.disco_entities) === null || _converse$disco_entit2 === void 0 ? void 0 : _converse$disco_entit2.forEach(e => e.identities.clearStore()); - (_converse$disco_entit3 = shared_converse.disco_entities) === null || _converse$disco_entit3 === void 0 ? void 0 : _converse$disco_entit3.forEach(e => e.dataforms.clearStore()); - (_converse$disco_entit4 = shared_converse.disco_entities) === null || _converse$disco_entit4 === void 0 ? void 0 : _converse$disco_entit4.forEach(e => e.fields.clearStore()); - (_converse$disco_entit5 = shared_converse.disco_entities) === null || _converse$disco_entit5 === void 0 ? void 0 : _converse$disco_entit5.clearStore(); - delete shared_converse.disco_entities; -} -;// CONCATENATED MODULE: ./src/headless/plugins/disco/api.js - - - - -const { - Strophe: api_Strophe, - $iq: api_$iq -} = core_converse.env; -/* harmony default export */ const disco_api = ({ - /** - * The XEP-0030 service discovery API - * - * This API lets you discover information about entities on the - * XMPP network. - * - * @namespace api.disco - * @memberOf api - */ - disco: { - /** - * @namespace api.disco.stream - * @memberOf api.disco - */ - stream: { - /** - * @method api.disco.stream.getFeature - * @param {String} name The feature name - * @param {String} xmlns The XML namespace - * @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') - */ - async getFeature(name, xmlns) { - await api.waitUntil('streamFeaturesAdded'); - - if (!name || !xmlns) { - throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature"); - } - - if (shared_converse.stream_features === undefined && !api.connection.connected()) { - // Happens during tests when disco lookups happen asynchronously after teardown. - const msg = `Tried to get feature ${name} ${xmlns} but _converse.stream_features has been torn down`; - headless_log.warn(msg); - return; - } - - return shared_converse.stream_features.findWhere({ - 'name': name, - 'xmlns': xmlns - }); - } - - }, - - /** - * @namespace api.disco.own - * @memberOf api.disco - */ - own: { - /** - * @namespace api.disco.own.identities - * @memberOf api.disco.own - */ - identities: { - /** - * Lets you add new identities for this client (i.e. instance of Converse) - * @method api.disco.own.identities.add - * - * @param {String} category - server, client, gateway, directory, etc. - * @param {String} type - phone, pc, web, etc. - * @param {String} name - "Converse" - * @param {String} lang - en, el, de, etc. - * - * @example _converse.api.disco.own.identities.clear(); - */ - add(category, type, name, lang) { - for (var i = 0; i < shared_converse.disco._identities.length; i++) { - if (shared_converse.disco._identities[i].category == category && shared_converse.disco._identities[i].type == type && shared_converse.disco._identities[i].name == name && shared_converse.disco._identities[i].lang == lang) { - return false; - } - } - - shared_converse.disco._identities.push({ - category: category, - type: type, - name: name, - lang: lang - }); - }, - - /** - * Clears all previously registered identities. - * @method api.disco.own.identities.clear - * @example _converse.api.disco.own.identities.clear(); - */ - clear() { - shared_converse.disco._identities = []; - }, - - /** - * Returns all of the identities registered for this client - * (i.e. instance of Converse). - * @method api.disco.identities.get - * @example const identities = api.disco.own.identities.get(); - */ - get() { - return shared_converse.disco._identities; - } - - }, - - /** - * @namespace api.disco.own.features - * @memberOf api.disco.own - */ - features: { - /** - * Lets you register new disco features for this client (i.e. instance of Converse) - * @method api.disco.own.features.add - * @param {String} name - e.g. http://jabber.org/protocol/caps - * @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps"); - */ - add(name) { - for (var i = 0; i < shared_converse.disco._features.length; i++) { - if (shared_converse.disco._features[i] == name) { - return false; - } - } - - shared_converse.disco._features.push(name); - }, - - /** - * Clears all previously registered features. - * @method api.disco.own.features.clear - * @example _converse.api.disco.own.features.clear(); - */ - clear() { - shared_converse.disco._features = []; - }, - - /** - * Returns all of the features registered for this client (i.e. instance of Converse). - * @method api.disco.own.features.get - * @example const features = api.disco.own.features.get(); - */ - get() { - return shared_converse.disco._features; - } - - } - }, - - /** - * Query for information about an XMPP entity - * - * @method api.disco.info - * @param {string} jid The Jabber ID of the entity to query - * @param {string} [node] A specific node identifier associated with the JID - * @returns {promise} Promise which resolves once we have a result from the server. - */ - info(jid, node) { - const attrs = { - xmlns: api_Strophe.NS.DISCO_INFO - }; - - if (node) { - attrs.node = node; - } - - const info = api_$iq({ - 'from': shared_converse.connection.jid, - 'to': jid, - 'type': 'get' - }).c('query', attrs); - return api.sendIQ(info); - }, - - /** - * Query for items associated with an XMPP entity - * - * @method api.disco.items - * @param {string} jid The Jabber ID of the entity to query for items - * @param {string} [node] A specific node identifier associated with the JID - * @returns {promise} Promise which resolves once we have a result from the server. - */ - items(jid, node) { - const attrs = { - 'xmlns': api_Strophe.NS.DISCO_ITEMS - }; - - if (node) { - attrs.node = node; - } - - return api.sendIQ(api_$iq({ - 'from': shared_converse.connection.jid, - 'to': jid, - 'type': 'get' - }).c('query', attrs)); - }, - - /** - * Namespace for methods associated with disco entities - * - * @namespace api.disco.entities - * @memberOf api.disco - */ - entities: { - /** - * Get the corresponding `DiscoEntity` instance. - * - * @method api.disco.entities.get - * @param {string} jid The Jabber ID of the entity - * @param {boolean} [create] Whether the entity should be created if it doesn't exist. - * @example _converse.api.disco.entities.get(jid); - */ - async get(jid, create = false) { - await api.waitUntil('discoInitialized'); - - if (!jid) { - return shared_converse.disco_entities; - } - - if (shared_converse.disco_entities === undefined && !api.connection.connected()) { - // Happens during tests when disco lookups happen asynchronously after teardown. - const msg = `Tried to look up entity ${jid} but _converse.disco_entities has been torn down`; - headless_log.warn(msg); - return; - } - - const entity = shared_converse.disco_entities.get(jid); - - if (entity || !create) { - return entity; - } - - return api.disco.entities.create(jid); - }, - - /** - * Create a new disco entity. It's identity and features - * will automatically be fetched from cache or from the - * XMPP server. - * - * Fetching from cache can be disabled by passing in - * `ignore_cache: true` in the options parameter. - * - * @method api.disco.entities.create - * @param {string} jid The Jabber ID of the entity - * @param {object} [options] Additional options - * @param {boolean} [options.ignore_cache] - * If true, fetch all features from the XMPP server instead of restoring them from cache - * @example _converse.api.disco.entities.create(jid, {'ignore_cache': true}); - */ - create(jid, options) { - return shared_converse.disco_entities.create({ - 'jid': jid - }, options); - } - - }, - - /** - * @namespace api.disco.features - * @memberOf api.disco - */ - features: { - /** - * Return a given feature of a disco entity - * - * @method api.disco.features.get - * @param {string} feature The feature that might be - * supported. In the XML stanza, this is the `var` - * attribute of the `` element. For - * example: `http://jabber.org/protocol/muc` - * @param {string} jid The JID of the entity - * (and its associated items) which should be queried - * @returns {promise} A promise which resolves with a list containing - * _converse.Entity instances representing the entity - * itself or those items associated with the entity if - * they support the given feature. - * @example - * api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid); - */ - async get(feature, jid) { - if (!jid) { - throw new TypeError('You need to provide an entity JID'); - } - - await api.waitUntil('discoInitialized'); - let entity = await api.disco.entities.get(jid, true); - - if (shared_converse.disco_entities === undefined && !api.connection.connected()) { - // Happens during tests when disco lookups happen asynchronously after teardown. - const msg = `Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`; - headless_log.warn(msg); - return; - } - - entity = await entity.waitUntilFeaturesDiscovered; - const promises = [...entity.items.map(i => i.hasFeature(feature)), entity.hasFeature(feature)]; - const result = await Promise.all(promises); - return result.filter(lodash_es_isObject); - } - - }, - - /** - * Used to determine whether an entity supports a given feature. - * - * @method api.disco.supports - * @param {string} feature The feature that might be - * supported. In the XML stanza, this is the `var` - * attribute of the `` element. For - * example: `http://jabber.org/protocol/muc` - * @param {string} jid The JID of the entity - * (and its associated items) which should be queried - * @returns {promise} A promise which resolves with `true` or `false`. - * @example - * if (await api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) { - * // The feature is supported - * } else { - * // The feature is not supported - * } - */ - async supports(feature, jid) { - const features = (await api.disco.features.get(feature, jid)) || []; - return features.length > 0; - }, - - /** - * Refresh the features, fields and identities associated with a - * disco entity by refetching them from the server - * @method api.disco.refresh - * @param {string} jid The JID of the entity whose features are refreshed. - * @returns {promise} A promise which resolves once the features have been refreshed - * @example - * await api.disco.refresh('room@conference.example.org'); - */ - async refresh(jid) { - if (!jid) { - throw new TypeError('api.disco.refresh: You need to provide an entity JID'); - } - - await api.waitUntil('discoInitialized'); - let entity = await api.disco.entities.get(jid); - - if (entity) { - entity.features.reset(); - entity.fields.reset(); - entity.identities.reset(); - - if (!entity.waitUntilFeaturesDiscovered.isPending) { - entity.waitUntilFeaturesDiscovered = getOpenPromise(); - } - - entity.queryInfo(); - } else { - // Create it if it doesn't exist - entity = await api.disco.entities.create(jid, { - 'ignore_cache': true - }); - } - - return entity.waitUntilFeaturesDiscovered; - }, - - /** - * @deprecated Use {@link api.disco.refresh} instead. - * @method api.disco.refreshFeatures - */ - refreshFeatures(jid) { - return api.refresh(jid); - }, - - /** - * Return all the features associated with a disco entity - * - * @method api.disco.getFeatures - * @param {string} jid The JID of the entity whose features are returned. - * @returns {promise} A promise which resolves with the returned features - * @example - * const features = await api.disco.getFeatures('room@conference.example.org'); - */ - async getFeatures(jid) { - if (!jid) { - throw new TypeError('api.disco.getFeatures: You need to provide an entity JID'); - } - - await api.waitUntil('discoInitialized'); - let entity = await api.disco.entities.get(jid, true); - entity = await entity.waitUntilFeaturesDiscovered; - return entity.features; - }, - - /** - * Return all the service discovery extensions fields - * associated with an entity. - * - * See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html) - * - * @method api.disco.getFields - * @param {string} jid The JID of the entity whose fields are returned. - * @example - * const fields = await api.disco.getFields('room@conference.example.org'); - */ - async getFields(jid) { - if (!jid) { - throw new TypeError('api.disco.getFields: You need to provide an entity JID'); - } - - await api.waitUntil('discoInitialized'); - let entity = await api.disco.entities.get(jid, true); - entity = await entity.waitUntilFeaturesDiscovered; - return entity.fields; - }, - - /** - * Get the identity (with the given category and type) for a given disco entity. - * - * For example, when determining support for PEP (personal eventing protocol), you - * want to know whether the user's own JID has an identity with - * `category='pubsub'` and `type='pep'` as explained in this section of - * XEP-0163: https://xmpp.org/extensions/xep-0163.html#support - * - * @method api.disco.getIdentity - * @param {string} The identity category. - * In the XML stanza, this is the `category` - * attribute of the `` element. - * For example: 'pubsub' - * @param {string} type The identity type. - * In the XML stanza, this is the `type` - * attribute of the `` element. - * For example: 'pep' - * @param {string} jid The JID of the entity which might have the identity - * @returns {promise} A promise which resolves with a map indicating - * whether an identity with a given type is provided by the entity. - * @example - * api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid).then( - * function (identity) { - * if (identity) { - * // The entity DOES have this identity - * } else { - * // The entity DOES NOT have this identity - * } - * } - * ).catch(e => log.error(e)); - */ - async getIdentity(category, type, jid) { - const e = await api.disco.entities.get(jid, true); - - if (e === undefined && !api.connection.connected()) { - // Happens during tests when disco lookups happen asynchronously after teardown. - const msg = `Tried to look up category ${category} for ${jid} but _converse.disco_entities has been torn down`; - headless_log.warn(msg); - return; - } - - return e.getIdentity(category, type); - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/disco/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Converse plugin which add support for XEP-0030: Service Discovery - */ - - - - - -const { - Strophe: disco_Strophe -} = core_converse.env; -core_converse.plugins.add('converse-disco', { - initialize() { - Object.assign(api, disco_api); - api.promises.add('discoInitialized'); - api.promises.add('streamFeaturesAdded'); - shared_converse.DiscoEntity = entity; - shared_converse.DiscoEntities = entities; - shared_converse.disco = { - _identities: [], - _features: [] - }; - api.listen.on('userSessionInitialized', async () => { - initStreamFeatures(); - - if (shared_converse.connfeedback.get('connection_status') === disco_Strophe.Status.ATTACHED) { - // When re-attaching to a BOSH session, we fetch the stream features from the cache. - await new Promise((success, error) => shared_converse.stream_features.fetch({ - success, - error - })); - notifyStreamFeaturesAdded(); - } - }); - api.listen.on('beforeResourceBinding', populateStreamFeatures); - api.listen.on('reconnected', initializeDisco); - api.listen.on('connected', initializeDisco); - api.listen.on('beforeTearDown', async () => { - api.promises.add('streamFeaturesAdded'); - - if (shared_converse.stream_features) { - await shared_converse.stream_features.clearStore(); - delete shared_converse.stream_features; - } - }); // All disco entities stored in sessionStorage and are refetched - // upon login or reconnection and then stored with new ids, so to - // avoid sessionStorage filling up, we remove them. - - api.listen.on('will-reconnect', utils_clearSession); - api.listen.on('clearSession', utils_clearSession); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/emoji/regexes.js -const ASCII_REGEX = '(\\*\\\\0\\/\\*|\\*\\\\O\\/\\*|\\-___\\-|\\:\'\\-\\)|\'\\:\\-\\)|\'\\:\\-D|\\>\\:\\-\\)|>\\:\\-\\)|\'\\:\\-\\(|\\>\\:\\-\\(|>\\:\\-\\(|\\:\'\\-\\(|O\\:\\-\\)|0\\:\\-3|0\\:\\-\\)|0;\\^\\)|O;\\-\\)|0;\\-\\)|O\\:\\-3|\\-__\\-|\\:\\-Þ|\\:\\-Þ|\\<\\/3|<\\/3|\\:\'\\)|\\:\\-D|\'\\:\\)|\'\\=\\)|\'\\:D|\'\\=D|\\>\\:\\)|>\\:\\)|\\>;\\)|>;\\)|\\>\\=\\)|>\\=\\)|;\\-\\)|\\*\\-\\)|;\\-\\]|;\\^\\)|\'\\:\\(|\'\\=\\(|\\:\\-\\*|\\:\\^\\*|\\>\\:P|>\\:P|X\\-P|\\>\\:\\[|>\\:\\[|\\:\\-\\(|\\:\\-\\[|\\>\\:\\(|>\\:\\(|\\:\'\\(|;\\-\\(|\\>\\.\\<|>\\.<|#\\-\\)|%\\-\\)|X\\-\\)|\\\\0\\/|\\\\O\\/|0\\:3|0\\:\\)|O\\:\\)|O\\=\\)|O\\:3|B\\-\\)|8\\-\\)|B\\-D|8\\-D|\\-_\\-|\\>\\:\\\\|>\\:\\\\|\\>\\:\\/|>\\:\\/|\\:\\-\\/|\\:\\-\\.|\\:\\-P|\\:Þ|\\:Þ|\\:\\-b|\\:\\-O|O_O|\\>\\:O|>\\:O|\\:\\-X|\\:\\-#|\\:\\-\\)|\\(y\\)|\\<3|<3|\\:D|\\=D|;\\)|\\*\\)|;\\]|;D|\\:\\*|\\=\\*|\\:\\(|\\:\\[|\\=\\(|\\:@|;\\(|D\\:|\\:\\$|\\=\\$|#\\)|%\\)|X\\)|B\\)|8\\)|\\:\\/|\\:\\\\|\\=\\/|\\=\\\\|\\:L|\\=L|\\:P|\\=P|\\:b|\\:O|\\:X|\\:#|\\=X|\\=#|\\:\\)|\\=\\]|\\=\\)|\\:\\])'; -const ASCII_REPLACE_REGEX = new RegExp("]*>.*?<\/object>|]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|((\\s|^)" + ASCII_REGEX + "(?=\\s|$|[!,.?]))", "gi"); -const CODEPOINTS_REGEX = /(?:\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d])|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5\udeeb\udeec\udef4-\udefa\udfe0-\udfeb]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd1d\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd71\udd73-\udd76\udd7a-\udda2\udda5-\uddaa\uddae-\uddb4\uddb7\uddba\uddbc-\uddca\uddd0\uddde-\uddff\ude70-\ude73\ude78-\ude7a\ude80-\ude82\ude90-\ude95]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g; -;// CONCATENATED MODULE: ./src/headless/plugins/emoji/index.js -/** - * @module converse-emoji - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - -const emoji_u = core_converse.env.utils; -core_converse.emojis = { - 'initialized': false, - 'initialized_promise': getOpenPromise() -}; -const ASCII_LIST = { - '*\\0/*': '1f646', - '*\\O/*': '1f646', - '-___-': '1f611', - ':\'-)': '1f602', - '\':-)': '1f605', - '\':-D': '1f605', - '>:-)': '1f606', - '\':-(': '1f613', - '>:-(': '1f620', - ':\'-(': '1f622', - 'O:-)': '1f607', - '0:-3': '1f607', - '0:-)': '1f607', - '0;^)': '1f607', - 'O;-)': '1f607', - '0;-)': '1f607', - 'O:-3': '1f607', - '-__-': '1f611', - ':-Þ': '1f61b', - ':)': '1f606', - '>;)': '1f606', - '>=)': '1f606', - ';-)': '1f609', - '*-)': '1f609', - ';-]': '1f609', - ';^)': '1f609', - '\':(': '1f613', - '\'=(': '1f613', - ':-*': '1f618', - ':^*': '1f618', - '>:P': '1f61c', - 'X-P': '1f61c', - '>:[': '1f61e', - ':-(': '1f61e', - ':-[': '1f61e', - '>:(': '1f620', - ':\'(': '1f622', - ';-(': '1f622', - '>.<': '1f623', - '#-)': '1f635', - '%-)': '1f635', - 'X-)': '1f635', - '\\0/': '1f646', - '\\O/': '1f646', - '0:3': '1f607', - '0:)': '1f607', - 'O:)': '1f607', - 'O=)': '1f607', - 'O:3': '1f607', - 'B-)': '1f60e', - '8-)': '1f60e', - 'B-D': '1f60e', - '8-D': '1f60e', - '-_-': '1f611', - '>:\\': '1f615', - '>:/': '1f615', - ':-/': '1f615', - ':-.': '1f615', - ':-P': '1f61b', - ':Þ': '1f61b', - ':-b': '1f61b', - ':-O': '1f62e', - 'O_O': '1f62e', - '>:O': '1f62e', - ':-X': '1f636', - ':-#': '1f636', - ':-)': '1f642', - '(y)': '1f44d', - '<3': '2764', - ':D': '1f603', - '=D': '1f603', - ';)': '1f609', - '*)': '1f609', - ';]': '1f609', - ';D': '1f609', - ':*': '1f618', - '=*': '1f618', - ':(': '1f61e', - ':[': '1f61e', - '=(': '1f61e', - ':@': '1f620', - ';(': '1f622', - 'D:': '1f628', - ':$': '1f633', - '=$': '1f633', - '#)': '1f635', - '%)': '1f635', - 'X)': '1f635', - 'B)': '1f60e', - '8)': '1f60e', - ':/': '1f615', - ':\\': '1f615', - '=/': '1f615', - '=\\': '1f615', - ':L': '1f615', - '=L': '1f615', - ':P': '1f61b', - '=P': '1f61b', - ':b': '1f61b', - ':O': '1f62e', - ':X': '1f636', - ':#': '1f636', - '=X': '1f636', - '=#': '1f636', - ':)': '1f642', - '=]': '1f642', - '=)': '1f642', - ':]': '1f642' -}; - -function toCodePoint(unicode_surrogates) { - const r = []; - let p = 0; - let i = 0; - - while (i < unicode_surrogates.length) { - const c = unicode_surrogates.charCodeAt(i++); - - if (p) { - r.push((0x10000 + (p - 0xD800 << 10) + (c - 0xDC00)).toString(16)); - p = 0; - } else if (0xD800 <= c && c <= 0xDBFF) { - p = c; - } else { - r.push(c.toString(16)); - } - } - - return r.join('-'); -} - -function fromCodePoint(codepoint) { - let code = typeof codepoint === 'string' ? parseInt(codepoint, 16) : codepoint; - - if (code < 0x10000) { - return String.fromCharCode(code); - } - - code -= 0x10000; - return String.fromCharCode(0xD800 + (code >> 10), 0xDC00 + (code & 0x3FF)); -} - -function convert(unicode) { - // Converts unicode code points and code pairs to their respective characters - if (unicode.indexOf("-") > -1) { - const parts = [], - s = unicode.split('-'); - - for (let i = 0; i < s.length; i++) { - let part = parseInt(s[i], 16); - - if (part >= 0x10000 && part <= 0x10FFFF) { - const hi = Math.floor((part - 0x10000) / 0x400) + 0xD800; - const lo = (part - 0x10000) % 0x400 + 0xDC00; - part = String.fromCharCode(hi) + String.fromCharCode(lo); - } else { - part = String.fromCharCode(part); - } - - parts.push(part); - } - - return parts.join(''); - } - - return fromCodePoint(unicode); -} - -function unique(arr) { - return [...new Set(arr)]; -} - -function getTonedEmojis() { - if (!core_converse.emojis.toned) { - core_converse.emojis.toned = unique(Object.values(core_converse.emojis.json.people).filter(person => person.sn.includes('_tone')).map(person => person.sn.replace(/_tone[1-5]/, ''))); - } - - return core_converse.emojis.toned; -} - -function convertASCII2Emoji(str) { - // Replace ASCII smileys - return str.replace(ASCII_REPLACE_REGEX, (entire, m1, m2, m3) => { - if (typeof m3 === 'undefined' || m3 === '' || !(emoji_u.unescapeHTML(m3) in ASCII_LIST)) { - // if the ascii doesnt exist just return the entire match - return entire; - } - - m3 = emoji_u.unescapeHTML(m3); - const unicode = ASCII_LIST[m3].toUpperCase(); - return m2 + convert(unicode); - }); -} -function getEmojiMarkup(data, options = { - unicode_only: false, - add_title_wrapper: false -}) { - const emoji = data.emoji; - const shortname = data.shortname; - - if (emoji) { - if (options.unicode_only) { - return emoji; - } else if (api.settings.get('use_system_emojis')) { - if (options.add_title_wrapper) { - return shortname ? T`${emoji}` : emoji; - } else { - return emoji; - } - } else { - const path = api.settings.get('emoji_image_path'); - return T`${emoji}`; - } - } else if (options.unicode_only) { - return shortname; - } else { - return T`${shortname}`; - } -} -function getShortnameReferences(text) { - if (!core_converse.emojis.initialized) { - throw new Error('getShortnameReferences called before emojis are initialized. ' + 'To avoid this problem, first await the converse.emojis.initilaized_promise.'); - } - - const references = [...text.matchAll(core_converse.emojis.shortnames_regex)].filter(ref => ref[0].length > 0); - return references.map(ref => { - const cp = core_converse.emojis.by_sn[ref[0]].cp; - return { - cp, - 'begin': ref.index, - 'end': ref.index + ref[0].length, - 'shortname': ref[0], - 'emoji': cp ? convert(cp) : null - }; - }); -} - -function parseStringForEmojis(str, callback) { - const UFE0Fg = /\uFE0F/g; - const U200D = String.fromCharCode(0x200D); - return String(str).replace(CODEPOINTS_REGEX, (emoji, _, offset) => { - const icon_id = toCodePoint(emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0Fg, '') : emoji); - if (icon_id) callback(icon_id, emoji, offset); - }); -} - -function getCodePointReferences(text) { - const references = []; - parseStringForEmojis(text, (icon_id, emoji, offset) => { - var _u$getEmojisByAtrribu; - - references.push({ - 'begin': offset, - 'cp': icon_id, - 'emoji': emoji, - 'end': offset + emoji.length, - 'shortname': ((_u$getEmojisByAtrribu = emoji_u.getEmojisByAtrribute('cp')[icon_id]) === null || _u$getEmojisByAtrribu === void 0 ? void 0 : _u$getEmojisByAtrribu.sn) || '' - }); - }); - return references; -} - -function addEmojisMarkup(text, options) { - let list = [text]; - [...getShortnameReferences(text), ...getCodePointReferences(text)].sort((a, b) => b.begin - a.begin).forEach(ref => { - const text = list.shift(); - const emoji = getEmojiMarkup(ref, options); - - if (typeof emoji === 'string') { - list = [text.slice(0, ref.begin) + emoji + text.slice(ref.end), ...list]; - } else { - list = [text.slice(0, ref.begin), emoji, text.slice(ref.end), ...list]; - } - }); - return list; -} - -core_converse.plugins.add('converse-emoji', { - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - const { - ___ - } = shared_converse; - api.settings.extend({ - 'emoji_image_path': 'https://twemoji.maxcdn.com/v/12.1.6/', - 'emoji_categories': { - "smileys": ":grinning:", - "people": ":thumbsup:", - "activity": ":soccer:", - "travel": ":motorcycle:", - "objects": ":bomb:", - "nature": ":rainbow:", - "food": ":hotdog:", - "symbols": ":musical_note:", - "flags": ":flag_ac:", - "custom": null - }, - // We use the triple-underscore method which doesn't actually - // translate but does signify to gettext that these strings should - // go into the POT file. The translation then happens in the - // template. We do this so that users can pass in their own - // strings via converse.initialize, which is before __ is - // available. - 'emoji_category_labels': { - "smileys": ___("Smileys and emotions"), - "people": ___("People"), - "activity": ___("Activities"), - "travel": ___("Travel"), - "objects": ___("Objects"), - "nature": ___("Animals and nature"), - "food": ___("Food and drink"), - "symbols": ___("Symbols"), - "flags": ___("Flags"), - "custom": ___("Stickers") - } - }); - /** - * Model for storing data related to the Emoji picker widget - * @class - * @namespace _converse.EmojiPicker - * @memberOf _converse - */ - - shared_converse.EmojiPicker = Model.extend({ - defaults: { - 'current_category': 'smileys', - 'current_skintone': '', - 'scroll_position': 0 - } - }); - /************************ BEGIN Utils ************************/ - // Closured cache - - const emojis_by_attribute = {}; - Object.assign(emoji_u, { - /** - * Returns an emoji represented by the passed in shortname. - * Scans the passed in text for shortnames and replaces them with - * emoji unicode glyphs or alternatively if it's a custom emoji - * without unicode representation then a lit TemplateResult - * which represents image tag markup is returned. - * - * The shortname needs to be defined in `emojis.json` - * and needs to have either a `cp` attribute for the codepoint, or - * an `url` attribute which points to the source for the image. - * - * @method u.shortnamesToEmojis - * @param { String } str - String containg the shortname(s) - * @param { Object } options - * @param { Boolean } options.unicode_only - Whether emojis are rendered as - * unicode codepoints. If so, the returned result will be an array - * with containing one string, because the emojis themselves will - * also be strings. If set to false, emojis will be represented by - * lit TemplateResult objects. - * @param { Boolean } options.add_title_wrapper - Whether unicode - * codepoints should be wrapped with a `` element with a - * title, so that the shortname is shown upon hovering with the - * mouse. - * @returns {Array} An array of at least one string, or otherwise - * strings and lit TemplateResult objects. - */ - shortnamesToEmojis(str, options = { - unicode_only: false, - add_title_wrapper: false - }) { - str = convertASCII2Emoji(str); - return addEmojisMarkup(str, options); - }, - - /** - * Replaces all shortnames in the passed in string with their - * unicode (emoji) representation. - * @method u.shortnamesToUnicode - * @param { String } str - String containing the shortname(s) - * @returns { String } - */ - shortnamesToUnicode(str) { - return emoji_u.shortnamesToEmojis(str, { - 'unicode_only': true - })[0]; - }, - - /** - * Determines whether the passed in string is just a single emoji shortname; - * @method u.isOnlyEmojis - * @param { String } shortname - A string which migh be just an emoji shortname - * @returns { Boolean } - */ - isOnlyEmojis(text) { - const words = text.trim().split(/\s+/); - - if (words.length === 0 || words.length > 3) { - return false; - } - - const emojis = words.filter(text => { - const refs = getCodePointReferences(emoji_u.shortnamesToUnicode(text)); - return refs.length === 1 && (text === refs[0]['shortname'] || text === refs[0]['emoji']); - }); - return emojis.length === words.length; - }, - - /** - * @method u.getEmojisByAtrribute - * @param { String } attr - The attribute according to which the - * returned map should be keyed. - * @returns { Object } - Map of emojis with the passed in attribute values - * as keys and a list of emojis for a particular category as values. - */ - getEmojisByAtrribute(attr) { - if (emojis_by_attribute[attr]) { - return emojis_by_attribute[attr]; - } - - if (attr === 'category') { - return core_converse.emojis.json; - } - - const all_variants = core_converse.emojis.list.map(e => e[attr]).filter((c, i, arr) => arr.indexOf(c) == i); - emojis_by_attribute[attr] = {}; - all_variants.forEach(v => emojis_by_attribute[attr][v] = core_converse.emojis.list.find(i => i[attr] === v)); - return emojis_by_attribute[attr]; - } - - }); - /************************ END Utils ************************/ - - /************************ BEGIN API ************************/ - // We extend the default converse.js API to add methods specific to MUC groupchats. - - Object.assign(api, { - /** - * @namespace api.emojis - * @memberOf api - */ - emojis: { - /** - * Initializes Emoji support by downloading the emojis JSON (and any applicable images). - * @method api.emojis.initialize - * @returns {Promise} - */ - async initialize() { - if (!core_converse.emojis.initialized) { - core_converse.emojis.initialized = true; - const { - default: json - } = await __webpack_require__.e(/* import() | emojis */ 4610).then(__webpack_require__.t.bind(__webpack_require__, 7530, 19)); - core_converse.emojis.json = json; - core_converse.emojis.by_sn = Object.keys(json).reduce((result, cat) => Object.assign(result, json[cat]), {}); - core_converse.emojis.list = Object.values(core_converse.emojis.by_sn); - core_converse.emojis.list.sort((a, b) => a.sn < b.sn ? -1 : a.sn > b.sn ? 1 : 0); - core_converse.emojis.shortnames = core_converse.emojis.list.map(m => m.sn); - - const getShortNames = () => core_converse.emojis.shortnames.map(s => s.replace(/[+]/g, "\\$&")).join('|'); - - core_converse.emojis.shortnames_regex = new RegExp(getShortNames(), "gi"); - core_converse.emojis.toned = getTonedEmojis(); - core_converse.emojis.initialized_promise.resolve(); - } - - return core_converse.emojis.initialized_promise; - } - - } - }); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/message.js - - - -/** - * Mixing that turns a Message model into a ChatRoomMessage model. - * @class - * @namespace _converse.ChatRoomMessage - * @memberOf _converse - */ - -const ChatRoomMessageMixin = { - initialize() { - if (!this.checkValidity()) { - return; - } - - if (this.get('file')) { - this.on('change:put', this.uploadFile, this); - } - - if (!this.setTimerForEphemeralMessage()) { - this.setOccupant(); - } - /** - * Triggered once a {@link _converse.ChatRoomMessageInitialized} has been created and initialized. - * @event _converse#chatRoomMessageInitialized - * @type { _converse.ChatRoomMessages} - * @example _converse.api.listen.on('chatRoomMessageInitialized', model => { ... }); - */ - - - api.trigger('chatRoomMessageInitialized', this); - }, - - /** - * Determines whether this messsage may be moderated, - * based on configuration settings and server support. - * @async - * @private - * @method _converse.ChatRoomMessages#mayBeModerated - * @returns { Boolean } - */ - mayBeModerated() { - return ['all', 'moderator'].includes(api.settings.get('allow_message_retraction')) && this.collection.chatbox.canModerateMessages(); - }, - - checkValidity() { - const result = shared_converse.Message.prototype.checkValidity.call(this); - - !result && this.collection.chatbox.debouncedRejoin(); - return result; - }, - - onOccupantRemoved() { - var _this$collection; - - this.stopListening(this.occupant); - delete this.occupant; - const chatbox = this === null || this === void 0 ? void 0 : (_this$collection = this.collection) === null || _this$collection === void 0 ? void 0 : _this$collection.chatbox; - - if (!chatbox) { - return headless_log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`); - } - - this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded); - }, - - onOccupantAdded(occupant) { - if (occupant.get('nick') === Strophe.getResourceFromJid(this.get('from'))) { - var _this$collection2; - - this.occupant = occupant; - this.trigger('occupantAdded'); - this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved); - const chatbox = this === null || this === void 0 ? void 0 : (_this$collection2 = this.collection) === null || _this$collection2 === void 0 ? void 0 : _this$collection2.chatbox; - - if (!chatbox) { - return headless_log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`); - } - - this.stopListening(chatbox.occupants, 'add', this.onOccupantAdded); - } - }, - - setOccupant() { - var _this$collection3; - - if (this.get('type') !== 'groupchat') { - return; - } - - const chatbox = this === null || this === void 0 ? void 0 : (_this$collection3 = this.collection) === null || _this$collection3 === void 0 ? void 0 : _this$collection3.chatbox; - - if (!chatbox) { - return headless_log.error(`Could not get collection.chatbox for message: ${JSON.stringify(this.toJSON())}`); - } - - const nick = Strophe.getResourceFromJid(this.get('from')); - this.occupant = chatbox.occupants.findWhere({ - nick - }); - - if (!this.occupant && api.settings.get('muc_send_probes')) { - this.occupant = chatbox.occupants.create({ - nick, - 'type': 'unavailable' - }); - const jid = `${chatbox.get('jid')}/${nick}`; - api.user.presence.send('probe', jid); - } - - if (this.occupant) { - this.listenTo(this.occupant, 'destroy', this.onOccupantRemoved); - } else { - this.listenTo(chatbox.occupants, 'add', this.onOccupantAdded); - } - } - -}; -/* harmony default export */ const muc_message = (ChatRoomMessageMixin); -;// CONCATENATED MODULE: ./src/headless/utils/parse-helpers.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Pure functions to help functionally parse messages. - * @todo Other parsing helpers can be made more abstract and placed here. - */ -const helpers = {}; - -const escapeRegexChars = (string, char) => string.replace(RegExp('\\' + char, 'ig'), '\\' + char); - -helpers.escapeCharacters = characters => string => characters.split('').reduce(escapeRegexChars, string); - -helpers.escapeRegexString = helpers.escapeCharacters('[\\^$.?*+(){}|'); // `for` is ~25% faster than using `Array.find()` - -helpers.findFirstMatchInArray = array => text => { - for (let i = 0; i < array.length; i++) { - if (text.localeCompare(array[i], undefined, { - sensitivity: 'base' - }) === 0) { - return array[i]; - } - } - - return null; -}; - -const reduceReferences = ([text, refs], ref, index) => { - let updated_text = text; - let { - begin, - end - } = ref; - const { - value - } = ref; - begin = begin - index; - end = end - index - 1; // -1 to compensate for the removed @ - - updated_text = `${updated_text.slice(0, begin)}${value}${updated_text.slice(end + 1)}`; - return [updated_text, [...refs, { ...ref, - begin, - end - }]]; -}; - -helpers.reduceTextFromReferences = (text, refs) => refs.reduce(reduceReferences, [text, []]); - -/* harmony default export */ const parse_helpers = (helpers); -;// CONCATENATED MODULE: ./src/headless/utils/form.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description This is the form utilities module. - */ - -/** - * Takes an HTML DOM and turns it into an XForm field. - * @private - * @method u#webForm2xForm - * @param { DOMElement } field - the field to convert - */ - -utils_core.webForm2xForm = function (field) { - const name = field.getAttribute('name'); - - if (!name) { - return null; // See #1924 - } - - let value; - - if (field.getAttribute('type') === 'checkbox') { - value = field.checked && 1 || 0; - } else if (field.tagName == "TEXTAREA") { - value = field.value.split('\n').filter(s => s.trim()); - } else if (field.tagName == "SELECT") { - value = utils_core.getSelectValues(field); - } else { - value = field.value; - } - - return utils_core.toStanza(` - - ${value.constructor === Array ? value.map(v => `${v}`) : `${value}`} - `); -}; - -/* harmony default export */ const utils_form = (utils_core); -;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseZipObject.js -/** - * This base implementation of `_.zipObject` which assigns values using `assignFunc`. - * - * @private - * @param {Array} props The property identifiers. - * @param {Array} values The property values. - * @param {Function} assignFunc The function to assign values. - * @returns {Object} Returns the new object. - */ -function baseZipObject(props, values, assignFunc) { - var index = -1, - length = props.length, - valsLength = values.length, - result = {}; - - while (++index < length) { - var value = index < valsLength ? values[index] : undefined; - assignFunc(result, props[index], value); - } - - return result; -} - -/* harmony default export */ const _baseZipObject = (baseZipObject); -;// CONCATENATED MODULE: ./node_modules/lodash-es/zipObject.js - - -/** - * This method is like `_.fromPairs` except that it accepts two arrays, - * one of property identifiers and one of corresponding values. - * - * @static - * @memberOf _ - * @since 0.4.0 - * @category Array - * @param {Array} [props=[]] The property identifiers. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @example - * - * _.zipObject(['a', 'b'], [1, 2]); - * // => { 'a': 1, 'b': 2 } - */ - -function zipObject(props, values) { - return _baseZipObject(props || [], values || [], _assignValue); -} - -/* harmony default export */ const lodash_es_zipObject = (zipObject); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/parsers.js - - - -const { - Strophe: muc_parsers_Strophe, - sizzle: muc_parsers_sizzle, - u: parsers_u -} = core_converse.env; -const { - NS: parsers_NS -} = muc_parsers_Strophe; -/** - * Parses a message stanza for XEP-0317 MEP notification data - * @param { XMLElement } stanza - The message stanza - * @returns { Array } Returns an array of objects representing elements. - */ - -function getMEPActivities(stanza) { - const items_el = muc_parsers_sizzle(`items[node="${muc_parsers_Strophe.NS.CONFINFO}"]`, stanza).pop(); - - if (!items_el) { - return null; - } - - const from = stanza.getAttribute('from'); - const msgid = stanza.getAttribute('id'); - const selector = `item ` + `conference-info[xmlns="${muc_parsers_Strophe.NS.CONFINFO}"] ` + `activity[xmlns="${muc_parsers_Strophe.NS.ACTIVITY}"]`; - return muc_parsers_sizzle(selector, items_el).map(el => { - var _el$querySelector; - - const message = (_el$querySelector = el.querySelector('text')) === null || _el$querySelector === void 0 ? void 0 : _el$querySelector.textContent; - - if (message) { - var _el$querySelector2; - - const references = getReferences(stanza); - const reason = (_el$querySelector2 = el.querySelector('reason')) === null || _el$querySelector2 === void 0 ? void 0 : _el$querySelector2.textContent; - return { - from, - msgid, - message, - reason, - references, - 'type': 'info' - }; - } - - return {}; - }); -} -/** - * @private - * @param { XMLElement } stanza - The message stanza - * @param { XMLElement } original_stanza - The original stanza, that contains the - * message stanza, if it was contained, otherwise it's the message stanza itself. - * @returns { Object } - */ - -function getModerationAttributes(stanza) { - const fastening = muc_parsers_sizzle(`apply-to[xmlns="${muc_parsers_Strophe.NS.FASTEN}"]`, stanza).pop(); - - if (fastening) { - const applies_to_id = fastening.getAttribute('id'); - const moderated = muc_parsers_sizzle(`moderated[xmlns="${muc_parsers_Strophe.NS.MODERATE}"]`, fastening).pop(); - - if (moderated) { - const retracted = muc_parsers_sizzle(`retract[xmlns="${muc_parsers_Strophe.NS.RETRACT}"]`, moderated).pop(); - - if (retracted) { - var _moderated$querySelec; - - return { - 'editable': false, - 'moderated': 'retracted', - 'moderated_by': moderated.getAttribute('by'), - 'moderated_id': applies_to_id, - 'moderation_reason': (_moderated$querySelec = moderated.querySelector('reason')) === null || _moderated$querySelec === void 0 ? void 0 : _moderated$querySelec.textContent - }; - } - } - } else { - const tombstone = muc_parsers_sizzle(`> moderated[xmlns="${muc_parsers_Strophe.NS.MODERATE}"]`, stanza).pop(); - - if (tombstone) { - const retracted = muc_parsers_sizzle(`retracted[xmlns="${muc_parsers_Strophe.NS.RETRACT}"]`, tombstone).pop(); - - if (retracted) { - var _tombstone$querySelec; - - return { - 'editable': false, - 'is_tombstone': true, - 'moderated_by': tombstone.getAttribute('by'), - 'retracted': tombstone.getAttribute('stamp'), - 'moderation_reason': (_tombstone$querySelec = tombstone.querySelector('reason')) === null || _tombstone$querySelec === void 0 ? void 0 : _tombstone$querySelec.textContent - }; - } - } - } - - return {}; -} -/** - * Parses a passed in message stanza and returns an object of attributes. - * @param { XMLElement } stanza - The message stanza - * @param { XMLElement } original_stanza - The original stanza, that contains the - * message stanza, if it was contained, otherwise it's the message stanza itself. - * @param { _converse.ChatRoom } chatbox - * @param { _converse } _converse - * @returns { Promise } - */ - - -async function parseMUCMessage(stanza, chatbox, _converse) { - var _stanza, _stanza$querySelector, _stanza$querySelector2, _chatbox$occupants$fi, _stanza$querySelector3, _stanza$querySelector4; - - throwErrorIfInvalidForward(stanza); - const selector = `[xmlns="${parsers_NS.MAM}"] > forwarded[xmlns="${parsers_NS.FORWARD}"] > message`; - const original_stanza = stanza; - stanza = muc_parsers_sizzle(selector, stanza).pop() || stanza; - - if (muc_parsers_sizzle(`message > forwarded[xmlns="${muc_parsers_Strophe.NS.FORWARD}"]`, stanza).length) { - return new StanzaParseError(`Invalid Stanza: Forged MAM groupchat message from ${stanza.getAttribute('from')}`, stanza); - } - - const delay = muc_parsers_sizzle(`delay[xmlns="${muc_parsers_Strophe.NS.DELAY}"]`, original_stanza).pop(); - const from = stanza.getAttribute('from'); - const nick = muc_parsers_Strophe.unescapeNode(muc_parsers_Strophe.getResourceFromJid(from)); - const marker = getChatMarker(stanza); - const now = new Date().toISOString(); - /** - * @typedef { Object } MUCMessageAttributes - * The object which {@link parseMUCMessage} returns - * @property { ('me'|'them') } sender - Whether the message was sent by the current user or someone else - * @property { Array } activities - A list of objects representing XEP-0316 MEP notification data - * @property { Array } references - A list of objects representing XEP-0372 references - * @property { Boolean } editable - Is this message editable via XEP-0308? - * @property { Boolean } is_archived - Is this message from a XEP-0313 MAM archive? - * @property { Boolean } is_carbon - Is this message a XEP-0280 Carbon? - * @property { Boolean } is_delayed - Was delivery of this message was delayed as per XEP-0203? - * @property { Boolean } is_encrypted - Is this message XEP-0384 encrypted? - * @property { Boolean } is_error - Whether an error was received for this message - * @property { Boolean } is_headline - Is this a "headline" message? - * @property { Boolean } is_markable - Can this message be marked with a XEP-0333 chat marker? - * @property { Boolean } is_marker - Is this message a XEP-0333 Chat Marker? - * @property { Boolean } is_only_emojis - Does the message body contain only emojis? - * @property { Boolean } is_spoiler - Is this a XEP-0382 spoiler message? - * @property { Boolean } is_tombstone - Is this a XEP-0424 tombstone? - * @property { Boolean } is_unstyled - Whether XEP-0393 styling hints should be ignored - * @property { Boolean } is_valid_receipt_request - Does this message request a XEP-0184 receipt (and is not from us or a carbon or archived message) - * @property { Object } encrypted - XEP-0384 encryption payload attributes - * @property { String } body - The contents of the tag of the message stanza - * @property { String } chat_state - The XEP-0085 chat state notification contained in this message - * @property { String } edited - An ISO8601 string recording the time that the message was edited per XEP-0308 - * @property { String } error_condition - The defined error condition - * @property { String } error_text - The error text received from the server - * @property { String } error_type - The type of error received from the server - * @property { String } from - The sender JID (${muc_jid}/${nick}) - * @property { String } from_muc - The JID of the MUC from which this message was sent - * @property { String } from_real_jid - The real JID of the sender, if available - * @property { String } fullname - The full name of the sender - * @property { String } marker - The XEP-0333 Chat Marker value - * @property { String } marker_id - The `id` attribute of a XEP-0333 chat marker - * @property { String } moderated - The type of XEP-0425 moderation (if any) that was applied - * @property { String } moderated_by - The JID of the user that moderated this message - * @property { String } moderated_id - The XEP-0359 Stanza ID of the message that this one moderates - * @property { String } moderation_reason - The reason provided why this message moderates another - * @property { String } msgid - The root `id` attribute of the stanza - * @property { String } nick - The MUC nickname of the sender - * @property { String } oob_desc - The description of the XEP-0066 out of band data - * @property { String } oob_url - The URL of the XEP-0066 out of band data - * @property { String } origin_id - The XEP-0359 Origin ID - * @property { String } receipt_id - The `id` attribute of a XEP-0184 element - * @property { String } received - An ISO8601 string recording the time that the message was received - * @property { String } replace_id - The `id` attribute of a XEP-0308 element - * @property { String } retracted - An ISO8601 string recording the time that the message was retracted - * @property { String } retracted_id - The `id` attribute of a XEP-424 element - * @property { String } spoiler_hint The XEP-0382 spoiler hint - * @property { String } stanza_id - The XEP-0359 Stanza ID. Note: the key is actualy `stanza_id ${by_jid}` and there can be multiple. - * @property { String } subject - The element value - * @property { String } thread - The element value - * @property { String } time - The time (in ISO8601 format), either given by the XEP-0203 element, or of receipt. - * @property { String } to - The recipient JID - * @property { String } type - The type of message - */ - - let attrs = Object.assign({ - from, - nick, - 'is_forwarded': !!((_stanza = stanza) !== null && _stanza !== void 0 && _stanza.querySelector('forwarded')), - 'activities': getMEPActivities(stanza), - 'body': (_stanza$querySelector = stanza.querySelector('body')) === null || _stanza$querySelector === void 0 ? void 0 : (_stanza$querySelector2 = _stanza$querySelector.textContent) === null || _stanza$querySelector2 === void 0 ? void 0 : _stanza$querySelector2.trim(), - 'chat_state': getChatState(stanza), - 'from_muc': muc_parsers_Strophe.getBareJidFromJid(from), - 'from_real_jid': (_chatbox$occupants$fi = chatbox.occupants.findOccupant({ - nick - })) === null || _chatbox$occupants$fi === void 0 ? void 0 : _chatbox$occupants$fi.get('jid'), - 'is_archived': isArchived(original_stanza), - 'is_carbon': isCarbon(original_stanza), - 'is_delayed': !!delay, - 'is_headline': isHeadline(stanza), - 'is_markable': !!muc_parsers_sizzle(`markable[xmlns="${muc_parsers_Strophe.NS.MARKERS}"]`, stanza).length, - 'is_marker': !!marker, - 'is_unstyled': !!muc_parsers_sizzle(`unstyled[xmlns="${muc_parsers_Strophe.NS.STYLING}"]`, stanza).length, - 'marker_id': marker && marker.getAttribute('id'), - 'msgid': stanza.getAttribute('id') || original_stanza.getAttribute('id'), - 'receipt_id': getReceiptId(stanza), - 'received': new Date().toISOString(), - 'references': getReferences(stanza), - 'subject': (_stanza$querySelector3 = stanza.querySelector('subject')) === null || _stanza$querySelector3 === void 0 ? void 0 : _stanza$querySelector3.textContent, - 'thread': (_stanza$querySelector4 = stanza.querySelector('thread')) === null || _stanza$querySelector4 === void 0 ? void 0 : _stanza$querySelector4.textContent, - 'time': delay ? dayjs_min_default()(delay.getAttribute('stamp')).toISOString() : now, - 'to': stanza.getAttribute('to'), - 'type': stanza.getAttribute('type') - }, getErrorAttributes(stanza), getOutOfBandAttributes(stanza), getSpoilerAttributes(stanza), getCorrectionAttributes(stanza, original_stanza), getStanzaIDs(stanza, original_stanza), getOpenGraphMetadata(stanza), getRetractionAttributes(stanza, original_stanza), getModerationAttributes(stanza), getEncryptionAttributes(stanza, _converse)); - await api.emojis.initialize(); - attrs = Object.assign({ - 'is_only_emojis': attrs.body ? parsers_u.isOnlyEmojis(attrs.body) : false, - 'is_valid_receipt_request': isValidReceiptRequest(stanza, attrs), - 'message': attrs.body || attrs.error, - // TODO: Remove and use body and error attributes instead - 'sender': attrs.nick === chatbox.get('nick') ? 'me' : 'them' - }, attrs); - - if (attrs.is_archived && original_stanza.getAttribute('from') !== attrs.from_muc) { - return new StanzaParseError(`Invalid Stanza: Forged MAM message from ${original_stanza.getAttribute('from')}`, stanza); - } else if (attrs.is_archived && original_stanza.getAttribute('from') !== chatbox.get('jid')) { - return new StanzaParseError(`Invalid Stanza: Forged MAM groupchat message from ${stanza.getAttribute('from')}`, stanza); - } else if (attrs.is_carbon) { - return new StanzaParseError('Invalid Stanza: MUC messages SHOULD NOT be XEP-0280 carbon copied', stanza); - } // We prefer to use one of the XEP-0359 unique and stable stanza IDs as the Model id, to avoid duplicates. - - - attrs['id'] = attrs['origin_id'] || attrs[`stanza_id ${attrs.from_muc || attrs.from}`] || parsers_u.getUniqueId(); - /** - * *Hook* which allows plugins to add additional parsing - * @event _converse#parseMUCMessage - */ - - attrs = await api.hook('parseMUCMessage', stanza, attrs); // We call this after the hook, to allow plugins to decrypt encrypted - // messages, since we need to parse the message text to determine whether - // there are media urls. - - return Object.assign(attrs, getMediaURLs(attrs.is_encrypted ? attrs.plaintext : attrs.body)); -} -/** - * Given an IQ stanza with a member list, create an array of objects containing - * known member data (e.g. jid, nick, role, affiliation). - * @private - * @method muc_utils#parseMemberListIQ - * @returns { MemberListItem[] } - */ - -function parseMemberListIQ(iq) { - return muc_parsers_sizzle(`query[xmlns="${muc_parsers_Strophe.NS.MUC_ADMIN}"] item`, iq).map(item => { - /** - * @typedef {Object} MemberListItem - * Either the JID or the nickname (or both) will be available. - * @property {string} affiliation - * @property {string} [role] - * @property {string} [jid] - * @property {string} [nick] - */ - const data = { - 'affiliation': item.getAttribute('affiliation') - }; - const jid = item.getAttribute('jid'); - - if (parsers_u.isValidJID(jid)) { - data['jid'] = jid; - } else { - // XXX: Prosody sends nick for the jid attribute value - // Perhaps for anonymous room? - data['nick'] = jid; - } - - const nick = item.getAttribute('nick'); - - if (nick) { - data['nick'] = nick; - } - - const role = item.getAttribute('role'); - - if (role) { - data['role'] = nick; - } - - return data; - }); -} -/** - * Parses a passed in MUC presence stanza and returns an object of attributes. - * @method parseMUCPresence - * @param { XMLElement } stanza - The presence stanza - * @returns { Object } - */ - -function parseMUCPresence(stanza) { - const from = stanza.getAttribute('from'); - const type = stanza.getAttribute('type'); - const data = { - 'from': from, - 'nick': muc_parsers_Strophe.getResourceFromJid(from), - 'type': type, - 'states': [], - 'hats': [], - 'show': type !== 'unavailable' ? 'online' : 'offline' - }; - Array.from(stanza.children).forEach(child => { - if (child.matches('status')) { - data.status = child.textContent || null; - } else if (child.matches('show')) { - data.show = child.textContent || 'online'; - } else if (child.matches('x') && child.getAttribute('xmlns') === muc_parsers_Strophe.NS.MUC_USER) { - Array.from(child.children).forEach(item => { - if (item.nodeName === 'item') { - data.affiliation = item.getAttribute('affiliation'); - data.role = item.getAttribute('role'); - data.jid = item.getAttribute('jid'); - data.nick = item.getAttribute('nick') || data.nick; - } else if (item.nodeName == 'status' && item.getAttribute('code')) { - data.states.push(item.getAttribute('code')); - } - }); - } else if (child.matches('x') && child.getAttribute('xmlns') === muc_parsers_Strophe.NS.VCARDUPDATE) { - var _child$querySelector; - - data.image_hash = (_child$querySelector = child.querySelector('photo')) === null || _child$querySelector === void 0 ? void 0 : _child$querySelector.textContent; - } else if (child.matches('hats') && child.getAttribute('xmlns') === muc_parsers_Strophe.NS.MUC_HATS) { - data['hats'] = Array.from(child.children).map(c => c.matches('hat') && { - 'title': c.getAttribute('title'), - 'uri': c.getAttribute('uri') - }); - } - }); - return data; -} -;// CONCATENATED MODULE: ./src/headless/plugins/muc/affiliations/utils.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - -const { - Strophe: affiliations_utils_Strophe, - $iq: affiliations_utils_$iq, - u: affiliations_utils_u -} = core_converse.env; -/** - * Sends an IQ stanza to the server, asking it for the relevant affiliation list . - * Returns an array of {@link MemberListItem} objects, representing occupants - * that have the given affiliation. - * See: https://xmpp.org/extensions/xep-0045.html#modifymember - * @param { ("admin"|"owner"|"member") } affiliation - * @param { String } muc_jid - The JID of the MUC for which the affiliation list should be fetched - * @returns { Promise } - */ - -async function getAffiliationList(affiliation, muc_jid) { - const { - __ - } = shared_converse; - const iq = affiliations_utils_$iq({ - 'to': muc_jid, - 'type': 'get' - }).c('query', { - xmlns: affiliations_utils_Strophe.NS.MUC_ADMIN - }).c('item', { - 'affiliation': affiliation - }); - const result = await api.sendIQ(iq, null, false); - - if (result === null) { - const err_msg = __('Error: timeout while fetching %1s list for MUC %2s', affiliation, muc_jid); - - const err = new Error(err_msg); - headless_log.warn(err_msg); - headless_log.warn(result); - return err; - } - - if (affiliations_utils_u.isErrorStanza(result)) { - const err_msg = __('Error: not allowed to fetch %1s list for MUC %2s', affiliation, muc_jid); - - const err = new Error(err_msg); - headless_log.warn(err_msg); - headless_log.warn(result); - return err; - } - - return parseMemberListIQ(result).filter(p => p).sort((a, b) => a.nick < b.nick ? -1 : a.nick > b.nick ? 1 : 0); -} -/** - * Given an occupant model, see which affiliations may be assigned to that user. - * @param { Model } occupant - * @returns { ('owner', 'admin', 'member', 'outcast', 'none')[] } - An array of assignable affiliations - */ - -function getAssignableAffiliations(occupant) { - let disabled = api.settings.get('modtools_disable_assign'); - - if (!Array.isArray(disabled)) { - disabled = disabled ? AFFILIATIONS : []; - } - - if (occupant.get('affiliation') === 'owner') { - return AFFILIATIONS.filter(a => !disabled.includes(a)); - } else if (occupant.get('affiliation') === 'admin') { - return AFFILIATIONS.filter(a => !['owner', 'admin', ...disabled].includes(a)); - } else { - return []; - } -} // Necessary for tests - -shared_converse.getAssignableAffiliations = getAssignableAffiliations; -/** - * Send IQ stanzas to the server to modify affiliations for users in this groupchat. - * See: https://xmpp.org/extensions/xep-0045.html#modifymember - * @param { Object[] } users - * @param { string } users[].jid - The JID of the user whose affiliation will change - * @param { Array } users[].affiliation - The new affiliation for this user - * @param { string } [users[].reason] - An optional reason for the affiliation change - * @returns { Promise } - */ - -function setAffiliations(muc_jid, users) { - const affiliations = [...new Set(users.map(u => u.affiliation))]; - return Promise.all(affiliations.map(a => setAffiliation(a, muc_jid, users))); -} -/** - * Send IQ stanzas to the server to set an affiliation for - * the provided JIDs. - * See: https://xmpp.org/extensions/xep-0045.html#modifymember - * - * Prosody doesn't accept multiple JIDs' affiliations - * being set in one IQ stanza, so as a workaround we send - * a separate stanza for each JID. - * Related ticket: https://issues.prosody.im/345 - * - * @param { ('outcast'|'member'|'admin'|'owner') } affiliation - The affiliation to be set - * @param { String|Array } jids - The JID(s) of the MUCs in which the - * affiliations need to be set. - * @param { object } members - A map of jids, affiliations and - * optionally reasons. Only those entries with the - * same affiliation as being currently set will be considered. - * @returns { Promise } A promise which resolves and fails depending on the XMPP server response. - */ - -function setAffiliation(affiliation, muc_jids, members) { - if (!Array.isArray(muc_jids)) { - muc_jids = [muc_jids]; - } - - members = members.filter(m => [undefined, affiliation].includes(m.affiliation)); - return Promise.all(muc_jids.reduce((acc, jid) => [...acc, ...members.map(m => sendAffiliationIQ(affiliation, jid, m))], [])); -} -/** - * Send an IQ stanza specifying an affiliation change. - * @private - * @param { String } affiliation: affiliation (could also be stored on the member object). - * @param { String } muc_jid: The JID of the MUC in which the affiliation should be set. - * @param { Object } member: Map containing the member's jid and optionally a reason and affiliation. - */ - -function sendAffiliationIQ(affiliation, muc_jid, member) { - const iq = affiliations_utils_$iq({ - to: muc_jid, - type: 'set' - }).c('query', { - xmlns: affiliations_utils_Strophe.NS.MUC_ADMIN - }).c('item', { - 'affiliation': member.affiliation || affiliation, - 'nick': member.nick, - 'jid': member.jid - }); - - if (member.reason !== undefined) { - iq.c('reason', member.reason); - } - - return api.sendIQ(iq); -} -/** - * Given two lists of objects with 'jid', 'affiliation' and - * 'reason' properties, return a new list containing - * those objects that are new, changed or removed - * (depending on the 'remove_absentees' boolean). - * - * The affiliations for new and changed members stay the - * same, for removed members, the affiliation is set to 'none'. - * - * The 'reason' property is not taken into account when - * comparing whether affiliations have been changed. - * @param { boolean } exclude_existing - Indicates whether JIDs from - * the new list which are also in the old list - * (regardless of affiliation) should be excluded - * from the delta. One reason to do this - * would be when you want to add a JID only if it - * doesn't have *any* existing affiliation at all. - * @param { boolean } remove_absentees - Indicates whether JIDs - * from the old list which are not in the new list - * should be considered removed and therefore be - * included in the delta with affiliation set - * to 'none'. - * @param { array } new_list - Array containing the new affiliations - * @param { array } old_list - Array containing the old affiliations - * @returns { array } - */ - - -function computeAffiliationsDelta(exclude_existing, remove_absentees, new_list, old_list) { - const new_jids = new_list.map(o => o.jid); - const old_jids = old_list.map(o => o.jid); // Get the new affiliations - - let delta = lodash_es_difference(new_jids, old_jids).map(jid => new_list[lodash_es_indexOf(new_jids, jid)]); - - if (!exclude_existing) { - // Get the changed affiliations - delta = delta.concat(new_list.filter(item => { - const idx = lodash_es_indexOf(old_jids, item.jid); - return idx >= 0 ? item.affiliation !== old_list[idx].affiliation : false; - })); - } - - if (remove_absentees) { - // Get the removed affiliations - delta = delta.concat(lodash_es_difference(old_jids, new_jids).map(jid => ({ - 'jid': jid, - 'affiliation': 'none' - }))); - } - - return delta; -} -;// CONCATENATED MODULE: ./src/headless/plugins/muc/muc.js - - - - - - - - - - - - - - - - - - -const OWNER_COMMANDS = ['owner']; -const ADMIN_COMMANDS = ['admin', 'ban', 'deop', 'destroy', 'member', 'op', 'revoke']; -const MODERATOR_COMMANDS = ['kick', 'mute', 'voice', 'modtools']; -const VISITOR_COMMANDS = ['nick']; -const METADATA_ATTRIBUTES = ["og:article:author", "og:article:published_time", "og:description", "og:image", "og:image:height", "og:image:width", "og:site_name", "og:title", "og:type", "og:url", "og:video:height", "og:video:secure_url", "og:video:tag", "og:video:type", "og:video:url", "og:video:width"]; -const ACTION_INFO_CODES = ['301', '303', '333', '307', '321', '322']; -const MUCSession = Model.extend({ - defaults() { - return { - 'connection_status': core_converse.ROOMSTATUS.DISCONNECTED - }; - } - -}); -/** - * Represents an open/ongoing groupchat conversation. - * @mixin - * @namespace _converse.ChatRoom - * @memberOf _converse - */ - -const ChatRoomMixin = { - defaults() { - return { - 'bookmarked': false, - 'chat_state': undefined, - 'has_activity': false, - // XEP-437 - 'hidden': shared_converse.isUniView() && !api.settings.get('singleton'), - 'hidden_occupants': !!api.settings.get('hide_muc_participants'), - 'message_type': 'groupchat', - 'name': '', - // For group chats, we distinguish between generally unread - // messages and those ones that specifically mention the - // user. - // - // To keep things simple, we reuse `num_unread` from - // _converse.ChatBox to indicate unread messages which - // mention the user and `num_unread_general` to indicate - // generally unread messages (which *includes* mentions!). - 'num_unread_general': 0, - 'num_unread': 0, - 'roomconfig': {}, - 'time_opened': this.get('time_opened') || new Date().getTime(), - 'time_sent': new Date(0).toISOString(), - 'type': shared_converse.CHATROOMS_TYPE - }; - }, - - async initialize() { - this.initialized = getOpenPromise(); - this.debouncedRejoin = lodash_es_debounce(this.rejoin, 250); - this.set('box_id', `box-${this.get('jid')}`); - this.initNotifications(); - this.initMessages(); - this.initUI(); - this.initOccupants(); - this.initDiscoModels(); // sendChatState depends on this.features - - this.registerHandlers(); - this.on('change:chat_state', this.sendChatState, this); - this.on('change:hidden', this.onHiddenChange, this); - this.on('destroy', this.removeHandlers, this); - this.ui.on('change:scrolled', this.onScrolledChanged, this); - await this.restoreSession(); - this.session.on('change:connection_status', this.onConnectionStatusChanged, this); - this.listenTo(this.occupants, 'add', this.onOccupantAdded); - this.listenTo(this.occupants, 'remove', this.onOccupantRemoved); - this.listenTo(this.occupants, 'change:show', this.onOccupantShowChanged); - this.listenTo(this.occupants, 'change:affiliation', this.createAffiliationChangeMessage); - this.listenTo(this.occupants, 'change:role', this.createRoleChangeMessage); - const restored = await this.restoreFromCache(); - - if (!restored) { - this.join(); - } - /** - * Triggered once a {@link _converse.ChatRoom} has been created and initialized. - * @event _converse#chatRoomInitialized - * @type { _converse.ChatRoom } - * @example _converse.api.listen.on('chatRoomInitialized', model => { ... }); - */ - - - await api.trigger('chatRoomInitialized', this, { - 'Synchronous': true - }); - this.initialized.resolve(); - }, - - /** - * Checks whether we're still joined and if so, restores the MUC state from cache. - * @private - * @method _converse.ChatRoom#restoreFromCache - * @returns { Boolean } Returns `true` if we're still joined, otherwise returns `false`. - */ - async restoreFromCache() { - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED && (await this.isJoined())) { - // We've restored the room from cache and we're still joined. - await new Promise(resolve => this.features.fetch({ - 'success': resolve, - 'error': resolve - })); - await this.fetchOccupants().catch(e => headless_log.error(e)); - await this.fetchMessages().catch(e => headless_log.error(e)); - return true; - } else { - this.session.save('connection_status', core_converse.ROOMSTATUS.DISCONNECTED); - this.clearOccupantsCache(); - return false; - } - }, - - /** - * Join the MUC - * @private - * @method _converse.ChatRoom#join - * @param { String } nick - The user's nickname - * @param { String } [password] - Optional password, if required by the groupchat. - * Will fall back to the `password` value stored in the room - * model (if available). - */ - async join(nick, password) { - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED) { - // We have restored a groupchat from session storage, - // so we don't send out a presence stanza again. - return this; - } // Set this early, so we don't rejoin in onHiddenChange - - - this.session.save('connection_status', core_converse.ROOMSTATUS.CONNECTING); - await this.refreshDiscoInfo(); - nick = await this.getAndPersistNickname(nick); - - if (!nick) { - utils_form.safeSave(this.session, { - 'connection_status': core_converse.ROOMSTATUS.NICKNAME_REQUIRED - }); - - if (api.settings.get('muc_show_logs_before_join')) { - await this.fetchMessages(); - } - - return this; - } - - api.send(await this.constructPresence(password)); - return this; - }, - - /** - * Clear stale cache and re-join a MUC we've been in before. - * @private - * @method _converse.ChatRoom#rejoin - */ - rejoin() { - this.session.save('connection_status', core_converse.ROOMSTATUS.DISCONNECTED); - this.registerHandlers(); - this.clearOccupantsCache(); - return this.join(); - }, - - async constructPresence(password) { - let stanza = $pres({ - 'from': shared_converse.connection.jid, - 'to': this.getRoomJIDAndNick() - }).c('x', { - 'xmlns': Strophe.NS.MUC - }).c('history', { - 'maxstanzas': this.features.get('mam_enabled') ? 0 : api.settings.get('muc_history_max_stanzas') - }).up(); - password = password || this.get('password'); - - if (password) { - stanza.cnode(Strophe.xmlElement('password', [], password)); - } - - stanza = await api.hook('constructedMUCPresence', null, stanza); - return stanza; - }, - - clearOccupantsCache() { - if (this.occupants.length) { - // Remove non-members when reconnecting - this.occupants.filter(o => !o.isMember()).forEach(o => o.destroy()); - } else { - // Looks like we haven't restored occupants from cache, so we clear it. - this.occupants.clearStore(); - } - }, - - /** - * Given the passed in MUC message, send a XEP-0333 chat marker. - * @param { _converse.MUCMessage } msg - * @param { ('received'|'displayed'|'acknowledged') } [type='displayed'] - * @param { Boolean } force - Whether a marker should be sent for the - * message, even if it didn't include a `markable` element. - */ - sendMarkerForMessage(msg, type = 'displayed', force = false) { - if (!msg || !api.settings.get('send_chat_markers').includes(type)) { - return; - } - - if (msg !== null && msg !== void 0 && msg.get('is_markable') || force) { - const key = `stanza_id ${this.get('jid')}`; - const id = msg.get(key); - - if (!id) { - headless_log.error(`Can't send marker for message without stanza ID: ${key}`); - return; - } - - const from_jid = Strophe.getBareJidFromJid(msg.get('from')); - sendMarker(from_jid, id, type, msg.get('type')); - } - }, - - /** - * Ensures that the user is subscribed to XEP-0437 Room Activity Indicators - * if `muc_subscribe_to_rai` is set to `true`. - * Only affiliated users can subscribe to RAI, but this method doesn't - * check whether the current user is affiliated because it's intended to be - * called after the MUC has been left and we don't have that information - * anymore. - * @private - * @method _converse.ChatRoom#enableRAI - */ - enableRAI() { - if (api.settings.get('muc_subscribe_to_rai')) { - const muc_domain = Strophe.getDomainFromJid(this.get('jid')); - api.user.presence.send(null, muc_domain, null, $build('rai', { - 'xmlns': Strophe.NS.RAI - })); - } - }, - - /** - * Handler that gets called when the 'hidden' flag is toggled. - * @private - * @method _converse.ChatRoom#onHiddenChange - */ - async onHiddenChange() { - const conn_status = this.session.get('connection_status'); - - if (this.get('hidden')) { - if (conn_status === core_converse.ROOMSTATUS.ENTERED && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') { - if (conn_status !== core_converse.ROOMSTATUS.DISCONNECTED) { - this.sendMarkerForLastMessage('received', true); - await this.leave(); - } - - this.enableRAI(); - } - } else { - if (conn_status === core_converse.ROOMSTATUS.DISCONNECTED) { - this.rejoin(); - } - - this.clearUnreadMsgCounter(); - } - }, - - onOccupantAdded(occupant) { - if (shared_converse.isInfoVisible(core_converse.MUC_TRAFFIC_STATES.ENTERED) && this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED && occupant.get('show') === 'online') { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_TRAFFIC_STATES.ENTERED); - } - }, - - onOccupantRemoved(occupant) { - if (shared_converse.isInfoVisible(core_converse.MUC_TRAFFIC_STATES.EXITED) && this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED && occupant.get('show') === 'online') { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_TRAFFIC_STATES.EXITED); - } - }, - - onOccupantShowChanged(occupant) { - if (occupant.get('states').includes('303')) { - return; - } - - if (occupant.get('show') === 'offline' && shared_converse.isInfoVisible(core_converse.MUC_TRAFFIC_STATES.EXITED)) { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_TRAFFIC_STATES.EXITED); - } else if (occupant.get('show') === 'online' && shared_converse.isInfoVisible(core_converse.MUC_TRAFFIC_STATES.ENTERED)) { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_TRAFFIC_STATES.ENTERED); - } - }, - - async onRoomEntered() { - await this.occupants.fetchMembers(); - - if (api.settings.get('clear_messages_on_reconnection')) { - // Don't call this.clearMessages because we don't want to - // recreate promises, since that will cause some existing - // awaiters to never proceed. - await this.messages.clearStore(); // A bit hacky. No need to fetch messages after clearing - - this.messages.fetched.resolve(); - } else { - await this.fetchMessages(); - } - /** - * Triggered when the user has entered a new MUC - * @event _converse#enteredNewRoom - * @type { _converse.ChatRoom} - * @example _converse.api.listen.on('enteredNewRoom', model => { ... }); - */ - - - api.trigger('enteredNewRoom', this); - - if (api.settings.get('auto_register_muc_nickname') && (await api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid')))) { - this.registerNickname(); - } - }, - - async onConnectionStatusChanged() { - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED) { - if (this.get('hidden') && api.settings.get('muc_subscribe_to_rai') && this.getOwnAffiliation() !== 'none') { - await this.leave(); - this.enableRAI(); - } else { - await this.onRoomEntered(); - } - } - }, - - async onReconnection() { - await this.rejoin(); - this.announceReconnection(); - }, - - getMessagesCollection() { - return new shared_converse.ChatRoomMessages(); - }, - - restoreSession() { - const id = `muc.session-${shared_converse.bare_jid}-${this.get('jid')}`; - this.session = new MUCSession({ - id - }); - initStorage(this.session, id, 'session'); - return new Promise(r => this.session.fetch({ - 'success': r, - 'error': r - })); - }, - - initDiscoModels() { - let id = `converse.muc-features-${shared_converse.bare_jid}-${this.get('jid')}`; - this.features = new Model(Object.assign({ - id - }, lodash_es_zipObject(core_converse.ROOM_FEATURES, core_converse.ROOM_FEATURES.map(() => false)))); - this.features.browserStorage = shared_converse.createStore(id, 'session'); - this.features.listenTo(shared_converse, 'beforeLogout', () => this.features.browserStorage.flush()); - id = `converse.muc-config-{_converse.bare_jid}-${this.get('jid')}`; - this.config = new Model(); - this.config.browserStorage = shared_converse.createStore(id, 'session'); - this.config.listenTo(shared_converse, 'beforeLogout', () => this.config.browserStorage.flush()); - }, - - initOccupants() { - this.occupants = new shared_converse.ChatRoomOccupants(); - const id = `converse.occupants-${shared_converse.bare_jid}${this.get('jid')}`; - this.occupants.browserStorage = shared_converse.createStore(id, 'session'); - this.occupants.chatroom = this; - this.occupants.listenTo(shared_converse, 'beforeLogout', () => this.occupants.browserStorage.flush()); - }, - - fetchOccupants() { - this.occupants.fetched = new Promise(resolve => { - this.occupants.fetch({ - 'add': true, - 'silent': true, - 'success': resolve, - 'error': resolve - }); - }); - return this.occupants.fetched; - }, - - handleAffiliationChangedMessage(stanza) { - const item = sizzle_default()(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop(); - - if (item) { - const from = stanza.getAttribute('from'); - const type = stanza.getAttribute('type'); - const affiliation = item.getAttribute('affiliation'); - const jid = item.getAttribute('jid'); - const data = { - from, - type, - affiliation, - 'states': [], - 'show': type == 'unavailable' ? 'offline' : 'online', - 'role': item.getAttribute('role'), - 'jid': Strophe.getBareJidFromJid(jid), - 'resource': Strophe.getResourceFromJid(jid) - }; - const occupant = this.occupants.findOccupant({ - 'jid': data.jid - }); - - if (occupant) { - occupant.save(data); - } else { - this.occupants.create(data); - } - } - }, - - async handleErrorMessageStanza(stanza) { - const { - __ - } = shared_converse; - const attrs = await parseMUCMessage(stanza, this, shared_converse); - - if (!(await this.shouldShowErrorMessage(attrs))) { - return; - } - - const message = this.getMessageReferencedByError(attrs); - - if (message) { - const new_attrs = { - 'error': attrs.error, - 'error_condition': attrs.error_condition, - 'error_text': attrs.error_text, - 'error_type': attrs.error_type, - 'editable': false - }; - - if (attrs.msgid === message.get('retraction_id')) { - // The error message refers to a retraction - new_attrs.retracted = undefined; - new_attrs.retraction_id = undefined; - new_attrs.retracted_id = undefined; - - if (!attrs.error) { - if (attrs.error_condition === 'forbidden') { - new_attrs.error = __("You're not allowed to retract your message."); - } else if (attrs.error_condition === 'not-acceptable') { - new_attrs.error = __("Your retraction was not delivered because you're not present in the groupchat."); - } else { - new_attrs.error = __('Sorry, an error occurred while trying to retract your message.'); - } - } - } else if (!attrs.error) { - if (attrs.error_condition === 'forbidden') { - new_attrs.error = __("Your message was not delivered because you weren't allowed to send it."); - } else if (attrs.error_condition === 'not-acceptable') { - new_attrs.error = __("Your message was not delivered because you're not present in the groupchat."); - } else { - new_attrs.error = __('Sorry, an error occurred while trying to send your message.'); - } - } - - message.save(new_attrs); - } else { - this.createMessage(attrs); - } - }, - - /** - * Handles incoming message stanzas from the service that hosts this MUC - * @private - * @method _converse.ChatRoom#handleMessageFromMUCHost - * @param { XMLElement } stanza - */ - handleMessageFromMUCHost(stanza) { - const conn_status = this.session.get('connection_status'); - - if (conn_status === core_converse.ROOMSTATUS.ENTERED) { - // We're not interested in activity indicators when already joined to the room - return; - } - - const rai = sizzle_default()(`rai[xmlns="${Strophe.NS.RAI}"]`, stanza).pop(); - const active_mucs = Array.from((rai === null || rai === void 0 ? void 0 : rai.querySelectorAll('activity')) || []).map(m => m.textContent); - - if (active_mucs.includes(this.get('jid'))) { - this.save({ - 'has_activity': true, - 'num_unread_general': 0 // Either/or between activity and unreads - - }); - } - }, - - /** - * Handles XEP-0452 MUC Mention Notification messages - * @private - * @method _converse.ChatRoom#handleForwardedMentions - * @param { XMLElement } stanza - */ - handleForwardedMentions(stanza) { - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED) { - // Avoid counting mentions twice - return; - } - - const msgs = sizzle_default()(`mentions[xmlns="${Strophe.NS.MENTIONS}"] forwarded[xmlns="${Strophe.NS.FORWARD}"] message[type="groupchat"]`, stanza); - const muc_jid = this.get('jid'); - const mentions = msgs.filter(m => Strophe.getBareJidFromJid(m.getAttribute('from')) === muc_jid); - - if (mentions.length) { - this.save({ - 'has_activity': true, - 'num_unread': this.get('num_unread') + mentions.length - }); - mentions.forEach(async stanza => { - const attrs = await parseMUCMessage(stanza, this, shared_converse); - const data = { - stanza, - attrs, - 'chatbox': this - }; - api.trigger('message', data); - }); - } - }, - - /** - * Parses an incoming message stanza and queues it for processing. - * @private - * @method _converse.ChatRoom#handleMessageStanza - * @param { XMLElement } stanza - */ - async handleMessageStanza(stanza) { - const type = stanza.getAttribute('type'); - - if (type === 'error') { - return this.handleErrorMessageStanza(stanza); - } - - if (type === 'groupchat') { - if (isArchived(stanza)) { - // MAM messages are handled in converse-mam. - // We shouldn't get MAM messages here because - // they shouldn't have a `type` attribute. - return headless_log.warn(`Received a MAM message with type "groupchat"`); - } - - this.createInfoMessages(stanza); - this.fetchFeaturesIfConfigurationChanged(stanza); - } else if (!type) { - return this.handleForwardedMentions(stanza); - } - /** - * @typedef { Object } MUCMessageData - * An object containing the parsed { @link MUCMessageAttributes } and - * current { @link ChatRoom }. - * @property { MUCMessageAttributes } attrs - * @property { ChatRoom } chatbox - */ - - - let attrs; - - try { - attrs = await parseMUCMessage(stanza, this, shared_converse); - } catch (e) { - return headless_log.error(e.message); - } - - const data = { - stanza, - attrs, - 'chatbox': this - }; - /** - * Triggered when a groupchat message stanza has been received and parsed. - * @event _converse#message - * @type { object } - * @property { module:converse-muc~MUCMessageData } data - */ - - api.trigger('message', data); - return attrs && this.queueMessage(attrs); - }, - - /** - * Register presence and message handlers relevant to this groupchat - * @private - * @method _converse.ChatRoom#registerHandlers - */ - registerHandlers() { - const muc_jid = this.get('jid'); - const muc_domain = Strophe.getDomainFromJid(muc_jid); - this.removeHandlers(); - this.presence_handler = shared_converse.connection.addHandler(stanza => this.onPresence(stanza) || true, null, 'presence', null, null, muc_jid, { - 'ignoreNamespaceFragment': true, - 'matchBareFromJid': true - }); - this.domain_presence_handler = shared_converse.connection.addHandler(stanza => this.onPresenceFromMUCHost(stanza) || true, null, 'presence', null, null, muc_domain); - this.message_handler = shared_converse.connection.addHandler(stanza => !!this.handleMessageStanza(stanza) || true, null, 'message', null, null, muc_jid, { - 'matchBareFromJid': true - }); - this.domain_message_handler = shared_converse.connection.addHandler(stanza => this.handleMessageFromMUCHost(stanza) || true, null, 'message', null, null, muc_domain); - this.affiliation_message_handler = shared_converse.connection.addHandler(stanza => this.handleAffiliationChangedMessage(stanza) || true, Strophe.NS.MUC_USER, 'message', null, null, muc_jid); - }, - - removeHandlers() { - // Remove the presence and message handlers that were - // registered for this groupchat. - if (this.message_handler) { - shared_converse.connection && shared_converse.connection.deleteHandler(this.message_handler); - delete this.message_handler; - } - - if (this.domain_message_handler) { - shared_converse.connection && shared_converse.connection.deleteHandler(this.domain_message_handler); - delete this.domain_message_handler; - } - - if (this.presence_handler) { - shared_converse.connection && shared_converse.connection.deleteHandler(this.presence_handler); - delete this.presence_handler; - } - - if (this.domain_presence_handler) { - shared_converse.connection && shared_converse.connection.deleteHandler(this.domain_presence_handler); - delete this.domain_presence_handler; - } - - if (this.affiliation_message_handler) { - shared_converse.connection && shared_converse.connection.deleteHandler(this.affiliation_message_handler); - delete this.affiliation_message_handler; - } - - return this; - }, - - invitesAllowed() { - return api.settings.get('allow_muc_invitations') && (this.features.get('open') || this.getOwnAffiliation() === 'owner'); - }, - - getDisplayName() { - const name = this.get('name'); - - if (name) { - return name; - } else if (api.settings.get('locked_muc_domain') === 'hidden') { - return Strophe.getNodeFromJid(this.get('jid')); - } else { - return this.get('jid'); - } - }, - - /** - * Sends a message stanza to the XMPP server and expects a reflection - * or error message within a specific timeout period. - * @private - * @method _converse.ChatRoom#sendTimedMessage - * @param { _converse.Message|XMLElement } message - * @returns { Promise|Promise<_converse.TimeoutError> } Returns a promise - * which resolves with the reflected message stanza or with an error stanza or {@link _converse.TimeoutError}. - */ - sendTimedMessage(el) { - if (typeof el.tree === 'function') { - el = el.tree(); - } - - let id = el.getAttribute('id'); - - if (!id) { - // inject id if not found - id = this.getUniqueId('sendIQ'); - el.setAttribute('id', id); - } - - const promise = getOpenPromise(); - - const timeoutHandler = shared_converse.connection.addTimedHandler(shared_converse.STANZA_TIMEOUT, () => { - shared_converse.connection.deleteHandler(handler); - - const err = new shared_converse.TimeoutError('Timeout Error: No response from server'); - promise.resolve(err); - return false; - }); - - const handler = shared_converse.connection.addHandler(stanza => { - timeoutHandler && shared_converse.connection.deleteTimedHandler(timeoutHandler); - promise.resolve(stanza); - }, null, 'message', ['error', 'groupchat'], id); - - api.send(el); - return promise; - }, - - /** - * Retract one of your messages in this groupchat - * @private - * @method _converse.ChatRoom#retractOwnMessage - * @param { _converse.Message } message - The message which we're retracting. - */ - async retractOwnMessage(message) { - const __ = shared_converse.__; - const origin_id = message.get('origin_id'); - - if (!origin_id) { - throw new Error("Can't retract message without a XEP-0359 Origin ID"); - } - - const editable = message.get('editable'); - const stanza = $msg({ - 'id': utils_form.getUniqueId(), - 'to': this.get('jid'), - 'type': 'groupchat' - }).c('store', { - xmlns: Strophe.NS.HINTS - }).up().c('apply-to', { - 'id': origin_id, - 'xmlns': Strophe.NS.FASTEN - }).c('retract', { - xmlns: Strophe.NS.RETRACT - }); // Optimistic save - - message.set({ - 'retracted': new Date().toISOString(), - 'retracted_id': origin_id, - 'retraction_id': stanza.nodeTree.getAttribute('id'), - 'editable': false - }); - const result = await this.sendTimedMessage(stanza); - - if (utils_form.isErrorStanza(result)) { - headless_log.error(result); - } else if (result instanceof shared_converse.TimeoutError) { - headless_log.error(result); - message.save({ - editable, - 'error_type': 'timeout', - 'error': __('A timeout happened while while trying to retract your message.'), - 'retracted': undefined, - 'retracted_id': undefined, - 'retraction_id': undefined - }); - } - }, - - /** - * Retract someone else's message in this groupchat. - * @private - * @method _converse.ChatRoom#retractOtherMessage - * @param { _converse.Message } message - The message which we're retracting. - * @param { string } [reason] - The reason for retracting the message. - * @example - * const room = await api.rooms.get(jid); - * const message = room.messages.findWhere({'body': 'Get rich quick!'}); - * room.retractOtherMessage(message, 'spam'); - */ - async retractOtherMessage(message, reason) { - const editable = message.get('editable'); // Optimistic save - - message.save({ - 'moderated': 'retracted', - 'moderated_by': shared_converse.bare_jid, - 'moderated_id': message.get('msgid'), - 'moderation_reason': reason, - 'editable': false - }); - const result = await this.sendRetractionIQ(message, reason); - - if (result === null || utils_form.isErrorStanza(result)) { - // Undo the save if something went wrong - message.save({ - editable, - 'moderated': undefined, - 'moderated_by': undefined, - 'moderated_id': undefined, - 'moderation_reason': undefined - }); - } - - return result; - }, - - /** - * Sends an IQ stanza to the XMPP server to retract a message in this groupchat. - * @private - * @method _converse.ChatRoom#sendRetractionIQ - * @param { _converse.Message } message - The message which we're retracting. - * @param { string } [reason] - The reason for retracting the message. - */ - sendRetractionIQ(message, reason) { - const iq = $iq({ - 'to': this.get('jid'), - 'type': 'set' - }).c('apply-to', { - 'id': message.get(`stanza_id ${this.get('jid')}`), - 'xmlns': Strophe.NS.FASTEN - }).c('moderate', { - xmlns: Strophe.NS.MODERATE - }).c('retract', { - xmlns: Strophe.NS.RETRACT - }).up().c('reason').t(reason || ''); - return api.sendIQ(iq, null, false); - }, - - /** - * Sends an IQ stanza to the XMPP server to destroy this groupchat. Not - * to be confused with the {@link _converse.ChatRoom#destroy} - * method, which simply removes the room from the local browser storage cache. - * @private - * @method _converse.ChatRoom#sendDestroyIQ - * @param { string } [reason] - The reason for destroying the groupchat. - * @param { string } [new_jid] - The JID of the new groupchat which replaces this one. - */ - sendDestroyIQ(reason, new_jid) { - const destroy = $build('destroy'); - - if (new_jid) { - destroy.attrs({ - 'jid': new_jid - }); - } - - const iq = $iq({ - 'to': this.get('jid'), - 'type': 'set' - }).c('query', { - 'xmlns': Strophe.NS.MUC_OWNER - }).cnode(destroy.node); - - if (reason && reason.length > 0) { - iq.c('reason', reason); - } - - return api.sendIQ(iq); - }, - - /** - * Leave the groupchat. - * @private - * @method _converse.ChatRoom#leave - * @param { string } [exit_msg] - Message to indicate your reason for leaving - */ - async leave(exit_msg) { - var _converse$disco_entit; - - this.features.destroy(); - const disco_entity = (_converse$disco_entit = shared_converse.disco_entities) === null || _converse$disco_entit === void 0 ? void 0 : _converse$disco_entit.get(this.get('jid')); - - if (disco_entity) { - await new Promise((success, error) => disco_entity.destroy({ - success, - error - })); - } - - if (api.connection.connected()) { - api.user.presence.send('unavailable', this.getRoomJIDAndNick(), exit_msg); - } - - utils_form.safeSave(this.session, { - 'connection_status': core_converse.ROOMSTATUS.DISCONNECTED - }); - }, - - async close(ev) { - await this.leave(); - - if (api.settings.get('auto_register_muc_nickname') === 'unregister' && (await api.disco.supports(Strophe.NS.MUC_REGISTER, this.get('jid')))) { - this.unregisterNickname(); - } - - this.occupants.clearStore(); - - if ((ev === null || ev === void 0 ? void 0 : ev.name) !== 'closeAllChatBoxes' && api.settings.get('muc_clear_messages_on_leave')) { - this.clearMessages(); - } // Delete the session model - - - await new Promise(resolve => this.session.destroy({ - 'success': resolve, - 'error': (m, e) => { - headless_log.error(e); - resolve(); - } - })); // Delete the features model - - await new Promise(resolve => this.features.destroy({ - 'success': resolve, - 'error': (m, e) => { - headless_log.error(e); - resolve(); - } - })); - return shared_converse.ChatBox.prototype.close.call(this); - }, - - canModerateMessages() { - const self = this.getOwnOccupant(); - return self && self.isModerator() && api.disco.supports(Strophe.NS.MODERATE, this.get('jid')); - }, - - /** - * Return an array of unique nicknames based on all occupants and messages in this MUC. - * @private - * @method _converse.ChatRoom#getAllKnownNicknames - * @returns { String[] } - */ - getAllKnownNicknames() { - return [...new Set([...this.occupants.map(o => o.get('nick')), ...this.messages.map(m => m.get('nick'))])].filter(n => n); - }, - - getAllKnownNicknamesRegex() { - const longNickString = this.getAllKnownNicknames().map(n => parse_helpers.escapeRegexString(n)).join('|'); - return RegExp(`(?:\\p{P}|\\p{Z}|^)@(${longNickString})(?![\\w@-])`, 'uig'); - }, - - getOccupantByJID(jid) { - return this.occupants.findOccupant({ - jid - }); - }, - - getOccupantByNickname(nick) { - return this.occupants.findOccupant({ - nick - }); - }, - - /** - * Given a text message, look for `@` mentions and turn them into - * XEP-0372 references - * @param { String } text - */ - parseTextForReferences(text) { - const mentions_regex = /(\p{P}|\p{Z}|^)([@][\w_-]+(?:\.\w+)*)/giu; - - if (!text || !mentions_regex.test(text)) { - return [text, []]; - } - - const getMatchingNickname = parse_helpers.findFirstMatchInArray(this.getAllKnownNicknames()); - - const uriFromNickname = nickname => { - const jid = this.get('jid'); - const occupant = this.getOccupant(nickname) || this.getOccupant(jid); - const uri = this.features.get('nonanonymous') && (occupant === null || occupant === void 0 ? void 0 : occupant.get('jid')) || `${jid}/${nickname}`; - return encodeURI(`xmpp:${uri}`); - }; - - const matchToReference = match => { - let at_sign_index = match[0].indexOf('@'); - - if (match[0][at_sign_index + 1] === '@') { - // edge-case - at_sign_index += 1; - } - - const begin = match.index + at_sign_index; - const end = begin + match[0].length - at_sign_index; - const value = getMatchingNickname(match[1]); - const type = 'mention'; - const uri = uriFromNickname(value); - return { - begin, - end, - value, - type, - uri - }; - }; - - const regex = this.getAllKnownNicknamesRegex(); - const mentions = [...text.matchAll(regex)].filter(m => !m[0].startsWith('/')); - const references = mentions.map(matchToReference); - const [updated_message, updated_references] = parse_helpers.reduceTextFromReferences(text, references); - return [updated_message, updated_references]; - }, - - getOutgoingMessageAttributes(attrs) { - const is_spoiler = this.get('composing_spoiler'); - let text = '', - references; - - if (attrs !== null && attrs !== void 0 && attrs.body) { - [text, references] = this.parseTextForReferences(attrs.body); - } - - const origin_id = utils_form.getUniqueId(); - const body = text ? utils_form.httpToGeoUri(utils_form.shortnamesToUnicode(text), shared_converse) : undefined; - return Object.assign({}, attrs, { - body, - is_spoiler, - origin_id, - references, - 'id': origin_id, - 'msgid': origin_id, - 'from': `${this.get('jid')}/${this.get('nick')}`, - 'fullname': this.get('nick'), - 'is_only_emojis': text ? utils_form.isOnlyEmojis(text) : false, - 'message': body, - 'nick': this.get('nick'), - 'sender': 'me', - 'type': 'groupchat' - }, getMediaURLs(text)); - }, - - /** - * Utility method to construct the JID for the current user as occupant of the groupchat. - * @private - * @method _converse.ChatRoom#getRoomJIDAndNick - * @returns {string} - The groupchat JID with the user's nickname added at the end. - * @example groupchat@conference.example.org/nickname - */ - getRoomJIDAndNick() { - const nick = this.get('nick'); - const jid = Strophe.getBareJidFromJid(this.get('jid')); - return jid + (nick !== null ? `/${nick}` : ''); - }, - - /** - * Sends a message with the current XEP-0085 chat state of the user - * as taken from the `chat_state` attribute of the {@link _converse.ChatRoom}. - * @private - * @method _converse.ChatRoom#sendChatState - */ - sendChatState() { - if (!api.settings.get('send_chat_state_notifications') || !this.get('chat_state') || this.session.get('connection_status') !== core_converse.ROOMSTATUS.ENTERED || this.features.get('moderated') && this.getOwnRole() === 'visitor') { - return; - } - - const allowed = api.settings.get('send_chat_state_notifications'); - - if (Array.isArray(allowed) && !allowed.includes(this.get('chat_state'))) { - return; - } - - const chat_state = this.get('chat_state'); - - if (chat_state === shared_converse.GONE) { - // is not applicable within MUC context - return; - } - - api.send($msg({ - 'to': this.get('jid'), - 'type': 'groupchat' - }).c(chat_state, { - 'xmlns': Strophe.NS.CHATSTATES - }).up().c('no-store', { - 'xmlns': Strophe.NS.HINTS - }).up().c('no-permanent-store', { - 'xmlns': Strophe.NS.HINTS - })); - }, - - /** - * Send a direct invitation as per XEP-0249 - * @private - * @method _converse.ChatRoom#directInvite - * @param { String } recipient - JID of the person being invited - * @param { String } [reason] - Reason for the invitation - */ - directInvite(recipient, reason) { - if (this.features.get('membersonly')) { - // When inviting to a members-only groupchat, we first add - // the person to the member list by giving them an - // affiliation of 'member' otherwise they won't be able to join. - this.updateMemberLists([{ - 'jid': recipient, - 'affiliation': 'member', - 'reason': reason - }]); - } - - const attrs = { - 'xmlns': 'jabber:x:conference', - 'jid': this.get('jid') - }; - - if (reason !== null) { - attrs.reason = reason; - } - - if (this.get('password')) { - attrs.password = this.get('password'); - } - - const invitation = $msg({ - 'from': shared_converse.connection.jid, - 'to': recipient, - 'id': utils_form.getUniqueId() - }).c('x', attrs); - api.send(invitation); - /** - * After the user has sent out a direct invitation (as per XEP-0249), - * to a roster contact, asking them to join a room. - * @event _converse#chatBoxMaximized - * @type {object} - * @property {_converse.ChatRoom} room - * @property {string} recipient - The JID of the person being invited - * @property {string} reason - The original reason for the invitation - * @example _converse.api.listen.on('chatBoxMaximized', view => { ... }); - */ - - api.trigger('roomInviteSent', { - 'room': this, - 'recipient': recipient, - 'reason': reason - }); - }, - - /** - * Refresh the disco identity, features and fields for this {@link _converse.ChatRoom}. - * *features* are stored on the features {@link Model} attribute on this {@link _converse.ChatRoom}. - * *fields* are stored on the config {@link Model} attribute on this {@link _converse.ChatRoom}. - * @private - * @returns {Promise} - */ - refreshDiscoInfo() { - return api.disco.refresh(this.get('jid')).then(() => this.getDiscoInfo()).catch(e => headless_log.error(e)); - }, - - /** - * Fetch the *extended* MUC info from the server and cache it locally - * https://xmpp.org/extensions/xep-0045.html#disco-roominfo - * @private - * @method _converse.ChatRoom#getDiscoInfo - * @returns {Promise} - */ - getDiscoInfo() { - return api.disco.getIdentity('conference', 'text', this.get('jid')).then(identity => this.save({ - 'name': identity === null || identity === void 0 ? void 0 : identity.get('name') - })).then(() => this.getDiscoInfoFields()).then(() => this.getDiscoInfoFeatures()).catch(e => headless_log.error(e)); - }, - - /** - * Fetch the *extended* MUC info fields from the server and store them locally - * in the `config` {@link Model} attribute. - * See: https://xmpp.org/extensions/xep-0045.html#disco-roominfo - * @private - * @method _converse.ChatRoom#getDiscoInfoFields - * @returns {Promise} - */ - async getDiscoInfoFields() { - const fields = await api.disco.getFields(this.get('jid')); - const config = fields.reduce((config, f) => { - const name = f.get('var'); - - if (name && name.startsWith('muc#roominfo_')) { - config[name.replace('muc#roominfo_', '')] = f.get('value'); - } - - return config; - }, {}); - this.config.save(config); - }, - - /** - * Use converse-disco to populate the features {@link Model} which - * is stored as an attibute on this {@link _converse.ChatRoom}. - * The results may be cached. If you want to force fetching the features from the - * server, call {@link _converse.ChatRoom#refreshDiscoInfo} instead. - * @private - * @returns {Promise} - */ - async getDiscoInfoFeatures() { - const features = await api.disco.getFeatures(this.get('jid')); - const attrs = Object.assign(lodash_es_zipObject(core_converse.ROOM_FEATURES, core_converse.ROOM_FEATURES.map(() => false)), { - 'fetched': new Date().toISOString() - }); - features.each(feature => { - const fieldname = feature.get('var'); - - if (!fieldname.startsWith('muc_')) { - if (fieldname === Strophe.NS.MAM) { - attrs.mam_enabled = true; - } - - return; - } - - attrs[fieldname.replace('muc_', '')] = true; - }); - this.features.save(attrs); - }, - - /** - * Given a element, return a copy with a child if - * we can find a value for it in this rooms config. - * @private - * @method _converse.ChatRoom#addFieldValue - * @returns { Element } - */ - addFieldValue(field) { - const type = field.getAttribute('type'); - - if (type === 'fixed') { - return field; - } - - const fieldname = field.getAttribute('var').replace('muc#roomconfig_', ''); - const config = this.get('roomconfig'); - - if (fieldname in config) { - let values; - - switch (type) { - case 'boolean': - values = [config[fieldname] ? 1 : 0]; - break; - - case 'list-multi': - values = config[fieldname]; - break; - - default: - values = [config[fieldname]]; - } - - field.innerHTML = values.map(v => $build('value').t(v)).join(''); - } - - return field; - }, - - /** - * Automatically configure the groupchat based on this model's - * 'roomconfig' data. - * @private - * @method _converse.ChatRoom#autoConfigureChatRoom - * @returns { Promise } - * Returns a promise which resolves once a response IQ has - * been received. - */ - async autoConfigureChatRoom() { - const stanza = await this.fetchRoomConfiguration(); - const fields = sizzle_default()('field', stanza); - const configArray = fields.map(f => this.addFieldValue(f)); - - if (configArray.length) { - return this.sendConfiguration(configArray); - } - }, - - /** - * Send an IQ stanza to fetch the groupchat configuration data. - * Returns a promise which resolves once the response IQ - * has been received. - * @private - * @method _converse.ChatRoom#fetchRoomConfiguration - * @returns { Promise } - */ - fetchRoomConfiguration() { - return api.sendIQ($iq({ - 'to': this.get('jid'), - 'type': 'get' - }).c('query', { - xmlns: Strophe.NS.MUC_OWNER - })); - }, - - /** - * Sends an IQ stanza with the groupchat configuration. - * @private - * @method _converse.ChatRoom#sendConfiguration - * @param { Array } config - The groupchat configuration - * @returns { Promise } - A promise which resolves with - * the `result` stanza received from the XMPP server. - */ - sendConfiguration(config = []) { - const iq = $iq({ - to: this.get('jid'), - type: 'set' - }).c('query', { - xmlns: Strophe.NS.MUC_OWNER - }).c('x', { - xmlns: Strophe.NS.XFORM, - type: 'submit' - }); - config.forEach(node => iq.cnode(node).up()); - return api.sendIQ(iq); - }, - - onCommandError(err) { - const { - __ - } = shared_converse; - headless_log.fatal(err); - - const message = __('Sorry, an error happened while running the command.') + ' ' + __("Check your browser's developer console for details."); - - this.createMessage({ - message, - 'type': 'error' - }); - }, - - getNickOrJIDFromCommandArgs(args) { - const { - __ - } = shared_converse; - - if (utils_form.isValidJID(args.trim())) { - return args.trim(); - } - - if (!args.startsWith('@')) { - args = '@' + args; - } - - const [text, references] = this.parseTextForReferences(args); // eslint-disable-line no-unused-vars - - if (!references.length) { - const message = __("Error: couldn't find a groupchat participant based on your arguments"); - - this.createMessage({ - message, - 'type': 'error' - }); - return; - } - - if (references.length > 1) { - const message = __('Error: found multiple groupchat participant based on your arguments'); - - this.createMessage({ - message, - 'type': 'error' - }); - return; - } - - const nick_or_jid = references.pop().value; - const reason = args.split(nick_or_jid, 2)[1]; - - if (reason && !reason.startsWith(' ')) { - const message = __("Error: couldn't find a groupchat participant based on your arguments"); - - this.createMessage({ - message, - 'type': 'error' - }); - return; - } - - return nick_or_jid; - }, - - validateRoleOrAffiliationChangeArgs(command, args) { - const { - __ - } = shared_converse; - - if (!args) { - const message = __('Error: the "%1$s" command takes two arguments, the user\'s nickname and optionally a reason.', command); - - this.createMessage({ - message, - 'type': 'error' - }); - return false; - } - - return true; - }, - - getAllowedCommands() { - let allowed_commands = ['clear', 'help', 'me', 'nick', 'register']; - - if (this.config.get('changesubject') || ['owner', 'admin'].includes(this.getOwnAffiliation())) { - allowed_commands = [...allowed_commands, ...['subject', 'topic']]; - } - - const occupant = this.occupants.findWhere({ - 'jid': shared_converse.bare_jid - }); - - if (this.verifyAffiliations(['owner'], occupant, false)) { - allowed_commands = allowed_commands.concat(OWNER_COMMANDS).concat(ADMIN_COMMANDS); - } else if (this.verifyAffiliations(['admin'], occupant, false)) { - allowed_commands = allowed_commands.concat(ADMIN_COMMANDS); - } - - if (this.verifyRoles(['moderator'], occupant, false)) { - allowed_commands = allowed_commands.concat(MODERATOR_COMMANDS).concat(VISITOR_COMMANDS); - } else if (!this.verifyRoles(['visitor', 'participant', 'moderator'], occupant, false)) { - allowed_commands = allowed_commands.concat(VISITOR_COMMANDS); - } - - allowed_commands.sort(); - - if (Array.isArray(api.settings.get('muc_disable_slash_commands'))) { - return allowed_commands.filter(c => !api.settings.get('muc_disable_slash_commands').includes(c)); - } else { - return allowed_commands; - } - }, - - verifyAffiliations(affiliations, occupant, show_error = true) { - const { - __ - } = shared_converse; - - if (!Array.isArray(affiliations)) { - throw new TypeError('affiliations must be an Array'); - } - - if (!affiliations.length) { - return true; - } - - occupant = occupant || this.occupants.findWhere({ - 'jid': shared_converse.bare_jid - }); - - if (occupant) { - const a = occupant.get('affiliation'); - - if (affiliations.includes(a)) { - return true; - } - } - - if (show_error) { - const message = __('Forbidden: you do not have the necessary affiliation in order to do that.'); - - this.createMessage({ - message, - 'type': 'error' - }); - } - - return false; - }, - - verifyRoles(roles, occupant, show_error = true) { - const { - __ - } = shared_converse; - - if (!Array.isArray(roles)) { - throw new TypeError('roles must be an Array'); - } - - if (!roles.length) { - return true; - } - - occupant = occupant || this.occupants.findWhere({ - 'jid': shared_converse.bare_jid - }); - - if (occupant) { - const role = occupant.get('role'); - - if (roles.includes(role)) { - return true; - } - } - - if (show_error) { - const message = __('Forbidden: you do not have the necessary role in order to do that.'); - - this.createMessage({ - message, - 'type': 'error' - }); - } - - return false; - }, - - /** - * Returns the `role` which the current user has in this MUC - * @private - * @method _converse.ChatRoom#getOwnRole - * @returns { ('none'|'visitor'|'participant'|'moderator') } - */ - getOwnRole() { - var _this$getOwnOccupant, _this$getOwnOccupant$; - - return (_this$getOwnOccupant = this.getOwnOccupant()) === null || _this$getOwnOccupant === void 0 ? void 0 : (_this$getOwnOccupant$ = _this$getOwnOccupant.attributes) === null || _this$getOwnOccupant$ === void 0 ? void 0 : _this$getOwnOccupant$.role; - }, - - /** - * Returns the `affiliation` which the current user has in this MUC - * @private - * @method _converse.ChatRoom#getOwnAffiliation - * @returns { ('none'|'outcast'|'member'|'admin'|'owner') } - */ - getOwnAffiliation() { - var _this$getOwnOccupant2, _this$getOwnOccupant3; - - return ((_this$getOwnOccupant2 = this.getOwnOccupant()) === null || _this$getOwnOccupant2 === void 0 ? void 0 : (_this$getOwnOccupant3 = _this$getOwnOccupant2.attributes) === null || _this$getOwnOccupant3 === void 0 ? void 0 : _this$getOwnOccupant3.affiliation) || 'none'; - }, - - /** - * Get the {@link _converse.ChatRoomOccupant} instance which - * represents the current user. - * @private - * @method _converse.ChatRoom#getOwnOccupant - * @returns { _converse.ChatRoomOccupant } - */ - getOwnOccupant() { - return this.occupants.findWhere({ - 'jid': shared_converse.bare_jid - }); - }, - - /** - * Send an IQ stanza to modify an occupant's role - * @private - * @method _converse.ChatRoom#setRole - * @param { _converse.ChatRoomOccupant } occupant - * @param { String } role - * @param { String } reason - * @param { function } onSuccess - callback for a succesful response - * @param { function } onError - callback for an error response - */ - setRole(occupant, role, reason, onSuccess, onError) { - const item = $build('item', { - 'nick': occupant.get('nick'), - role - }); - const iq = $iq({ - 'to': this.get('jid'), - 'type': 'set' - }).c('query', { - xmlns: Strophe.NS.MUC_ADMIN - }).cnode(item.node); - - if (reason !== null) { - iq.c('reason', reason); - } - - return api.sendIQ(iq).then(onSuccess).catch(onError); - }, - - /** - * @private - * @method _converse.ChatRoom#getOccupant - * @param { String } nickname_or_jid - The nickname or JID of the occupant to be returned - * @returns { _converse.ChatRoomOccupant } - */ - getOccupant(nickname_or_jid) { - return utils_form.isValidJID(nickname_or_jid) ? this.getOccupantByJID(nickname_or_jid) : this.getOccupantByNickname(nickname_or_jid); - }, - - /** - * Return an array of occupant models that have the required role - * @private - * @method _converse.ChatRoom#getOccupantsWithRole - * @param { String } role - * @returns { _converse.ChatRoomOccupant[] } - */ - getOccupantsWithRole(role) { - return this.getOccupantsSortedBy('nick').filter(o => o.get('role') === role).map(item => { - return { - 'jid': item.get('jid'), - 'nick': item.get('nick'), - 'role': item.get('role') - }; - }); - }, - - /** - * Return an array of occupant models that have the required affiliation - * @private - * @method _converse.ChatRoom#getOccupantsWithAffiliation - * @param { String } affiliation - * @returns { _converse.ChatRoomOccupant[] } - */ - getOccupantsWithAffiliation(affiliation) { - return this.getOccupantsSortedBy('nick').filter(o => o.get('affiliation') === affiliation).map(item => { - return { - 'jid': item.get('jid'), - 'nick': item.get('nick'), - 'affiliation': item.get('affiliation') - }; - }); - }, - - /** - * Return an array of occupant models, sorted according to the passed-in attribute. - * @private - * @method _converse.ChatRoom#getOccupantsSortedBy - * @param { String } attr - The attribute to sort the returned array by - * @returns { _converse.ChatRoomOccupant[] } - */ - getOccupantsSortedBy(attr) { - return Array.from(this.occupants.models).sort((a, b) => a.get(attr) < b.get(attr) ? -1 : a.get(attr) > b.get(attr) ? 1 : 0); - }, - - /** - * Fetch the lists of users with the given affiliations. - * Then compute the delta between those users and - * the passed in members, and if it exists, send the delta - * to the XMPP server to update the member list. - * @private - * @method _converse.ChatRoom#updateMemberLists - * @param { object } members - Map of member jids and affiliations. - * @returns { Promise } - * A promise which is resolved once the list has been - * updated or once it's been established there's no need - * to update the list. - */ - async updateMemberLists(members) { - const muc_jid = this.get('jid'); - const all_affiliations = ['member', 'admin', 'owner']; - const aff_lists = await Promise.all(all_affiliations.map(a => getAffiliationList(a, muc_jid))); - const old_members = aff_lists.reduce((acc, val) => utils_form.isErrorObject(val) ? acc : [...val, ...acc], []); - await setAffiliations(muc_jid, computeAffiliationsDelta(true, false, members, old_members)); - await this.occupants.fetchMembers(); - }, - - /** - * Given a nick name, save it to the model state, otherwise, look - * for a server-side reserved nickname or default configured - * nickname and if found, persist that to the model state. - * @private - * @method _converse.ChatRoom#getAndPersistNickname - * @returns { Promise } A promise which resolves with the nickname - */ - async getAndPersistNickname(nick) { - nick = nick || this.get('nick') || (await this.getReservedNick()) || shared_converse.getDefaultMUCNickname(); - - if (nick) { - this.save({ - nick - }, { - 'silent': true - }); - } - - return nick; - }, - - /** - * Use service-discovery to ask the XMPP server whether - * this user has a reserved nickname for this groupchat. - * If so, we'll use that, otherwise we render the nickname form. - * @private - * @method _converse.ChatRoom#getReservedNick - * @returns { Promise } A promise which resolves with the reserved nick or null - */ - async getReservedNick() { - const stanza = $iq({ - 'to': this.get('jid'), - 'from': shared_converse.connection.jid, - 'type': 'get' - }).c('query', { - 'xmlns': Strophe.NS.DISCO_INFO, - 'node': 'x-roomuser-item' - }); - const result = await api.sendIQ(stanza, null, false); - - if (utils_form.isErrorObject(result)) { - throw result; - } // Result might be undefined due to a timeout - - - const identity_el = result === null || result === void 0 ? void 0 : result.querySelector('query[node="x-roomuser-item"] identity'); - return identity_el ? identity_el.getAttribute('name') : null; - }, - - /** - * Send an IQ stanza to the MUC to register this user's nickname. - * This sets the user's affiliation to 'member' (if they weren't affiliated - * before) and reserves the nickname for this user, thereby preventing other - * users from using it in this MUC. - * See https://xmpp.org/extensions/xep-0045.html#register - * @private - * @method _converse.ChatRoom#registerNickname - */ - async registerNickname() { - const { - __ - } = shared_converse; - const nick = this.get('nick'); - const jid = this.get('jid'); - let iq, err_msg; - - try { - iq = await api.sendIQ($iq({ - 'to': jid, - 'type': 'get' - }).c('query', { - 'xmlns': Strophe.NS.MUC_REGISTER - })); - } catch (e) { - if (sizzle_default()(`not-allowed[xmlns="${Strophe.NS.STANZAS}"]`, e).length) { - err_msg = __("You're not allowed to register yourself in this groupchat."); - } else if (sizzle_default()(`registration-required[xmlns="${Strophe.NS.STANZAS}"]`, e).length) { - err_msg = __("You're not allowed to register in this groupchat because it's members-only."); - } - - headless_log.error(e); - return err_msg; - } - - const required_fields = sizzle_default()('field required', iq).map(f => f.parentElement); - - if (required_fields.length > 1 && required_fields[0].getAttribute('var') !== 'muc#register_roomnick') { - return headless_log.error(`Can't register the user register in the groupchat ${jid} due to the required fields`); - } - - try { - await api.sendIQ($iq({ - 'to': jid, - 'type': 'set' - }).c('query', { - 'xmlns': Strophe.NS.MUC_REGISTER - }).c('x', { - 'xmlns': Strophe.NS.XFORM, - 'type': 'submit' - }).c('field', { - 'var': 'FORM_TYPE' - }).c('value').t('http://jabber.org/protocol/muc#register').up().up().c('field', { - 'var': 'muc#register_roomnick' - }).c('value').t(nick)); - } catch (e) { - if (sizzle_default()(`service-unavailable[xmlns="${Strophe.NS.STANZAS}"]`, e).length) { - err_msg = __("Can't register your nickname in this groupchat, it doesn't support registration."); - } else if (sizzle_default()(`bad-request[xmlns="${Strophe.NS.STANZAS}"]`, e).length) { - err_msg = __("Can't register your nickname in this groupchat, invalid data form supplied."); - } - - headless_log.error(err_msg); - headless_log.error(e); - return err_msg; - } - }, - - /** - * Send an IQ stanza to the MUC to unregister this user's nickname. - * If the user had a 'member' affiliation, it'll be removed and their - * nickname will no longer be reserved and can instead be used (and - * registered) by other users. - * @private - * @method _converse.ChatRoom#unregisterNickname - */ - unregisterNickname() { - const iq = $iq({ - 'to': this.get('jid'), - 'type': 'set' - }).c('query', { - 'xmlns': Strophe.NS.MUC_REGISTER - }).c('remove'); - return api.sendIQ(iq).catch(e => headless_log.error(e)); - }, - - /** - * Given a presence stanza, update the occupant model based on its contents. - * @private - * @method _converse.ChatRoom#updateOccupantsOnPresence - * @param { XMLElement } pres - The presence stanza - */ - updateOccupantsOnPresence(pres) { - var _occupant$attributes, _occupant$attributes2; - - const data = parseMUCPresence(pres); - - if (data.type === 'error' || !data.jid && !data.nick) { - return true; - } - - const occupant = this.occupants.findOccupant(data); // Destroy an unavailable occupant if this isn't a nick change operation and if they're not affiliated - - if (data.type === 'unavailable' && occupant && !data.states.includes(core_converse.MUC_NICK_CHANGED_CODE) && !['admin', 'owner', 'member'].includes(data['affiliation'])) { - // Before destroying we set the new data, so that we can show the disconnection message - occupant.set(data); - occupant.destroy(); - return; - } - - const jid = data.jid || ''; - const attributes = Object.assign(data, { - 'jid': Strophe.getBareJidFromJid(jid) || (occupant === null || occupant === void 0 ? void 0 : (_occupant$attributes = occupant.attributes) === null || _occupant$attributes === void 0 ? void 0 : _occupant$attributes.jid), - 'resource': Strophe.getResourceFromJid(jid) || (occupant === null || occupant === void 0 ? void 0 : (_occupant$attributes2 = occupant.attributes) === null || _occupant$attributes2 === void 0 ? void 0 : _occupant$attributes2.resource) - }); - - if (occupant) { - occupant.save(attributes); - } else { - this.occupants.create(attributes); - } - }, - - fetchFeaturesIfConfigurationChanged(stanza) { - // 104: configuration change - // 170: logging enabled - // 171: logging disabled - // 172: room no longer anonymous - // 173: room now semi-anonymous - // 174: room now fully anonymous - const codes = ['104', '170', '171', '172', '173', '174']; - - if (sizzle_default()('status', stanza).filter(e => codes.includes(e.getAttribute('status'))).length) { - this.refreshDiscoInfo(); - } - }, - - /** - * Given two JIDs, which can be either user JIDs or MUC occupant JIDs, - * determine whether they belong to the same user. - * @private - * @method _converse.ChatRoom#isSameUser - * @param { String } jid1 - * @param { String } jid2 - * @returns { Boolean } - */ - isSameUser(jid1, jid2) { - const bare_jid1 = Strophe.getBareJidFromJid(jid1); - const bare_jid2 = Strophe.getBareJidFromJid(jid2); - const resource1 = Strophe.getResourceFromJid(jid1); - const resource2 = Strophe.getResourceFromJid(jid2); - - if (utils_form.isSameBareJID(jid1, jid2)) { - if (bare_jid1 === this.get('jid')) { - // MUC JIDs - return resource1 === resource2; - } else { - return true; - } - } else { - const occupant1 = bare_jid1 === this.get('jid') ? this.occupants.findOccupant({ - 'nick': resource1 - }) : this.occupants.findOccupant({ - 'jid': bare_jid1 - }); - const occupant2 = bare_jid2 === this.get('jid') ? this.occupants.findOccupant({ - 'nick': resource2 - }) : this.occupants.findOccupant({ - 'jid': bare_jid2 - }); - return occupant1 === occupant2; - } - }, - - async isSubjectHidden() { - const jids = await api.user.settings.get('mucs_with_hidden_subject', []); - return jids.includes(this.get('jid')); - }, - - async toggleSubjectHiddenState() { - const muc_jid = this.get('jid'); - const jids = await api.user.settings.get('mucs_with_hidden_subject', []); - - if (jids.includes(this.get('jid'))) { - api.user.settings.set('mucs_with_hidden_subject', jids.filter(jid => jid !== muc_jid)); - } else { - api.user.settings.set('mucs_with_hidden_subject', [...jids, muc_jid]); - } - }, - - /** - * Handle a possible subject change and return `true` if so. - * @private - * @method _converse.ChatRoom#handleSubjectChange - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMUCMessage} - */ - async handleSubjectChange(attrs) { - const __ = shared_converse.__; - - if (typeof attrs.subject === 'string' && !attrs.thread && !attrs.message) { - // https://xmpp.org/extensions/xep-0045.html#subject-mod - // ----------------------------------------------------- - // The subject is changed by sending a message of type "groupchat" to the , - // where the MUST contain a element that specifies the new subject but - // MUST NOT contain a element (or a element). - const subject = attrs.subject; - const author = attrs.nick; - utils_form.safeSave(this, { - 'subject': { - author, - 'text': attrs.subject || '' - } - }); - - if (!attrs.is_delayed && author) { - const message = subject ? __('Topic set by %1$s', author) : __('Topic cleared by %1$s', author); - const prev_msg = this.messages.last(); - - if ((prev_msg === null || prev_msg === void 0 ? void 0 : prev_msg.get('nick')) !== attrs.nick || (prev_msg === null || prev_msg === void 0 ? void 0 : prev_msg.get('type')) !== 'info' || (prev_msg === null || prev_msg === void 0 ? void 0 : prev_msg.get('message')) !== message) { - this.createMessage({ - message, - 'nick': attrs.nick, - 'type': 'info' - }); - } - - if (await this.isSubjectHidden()) { - this.toggleSubjectHiddenState(); - } - } - - return true; - } - - return false; - }, - - /** - * Set the subject for this {@link _converse.ChatRoom} - * @private - * @method _converse.ChatRoom#setSubject - * @param { String } value - */ - setSubject(value = '') { - api.send($msg({ - to: this.get('jid'), - from: shared_converse.connection.jid, - type: 'groupchat' - }).c('subject', { - xmlns: 'jabber:client' - }).t(value).tree()); - }, - - /** - * Is this a chat state notification that can be ignored, - * because it's old or because it's from us. - * @private - * @method _converse.ChatRoom#ignorableCSN - * @param { Object } attrs - The message attributes - */ - ignorableCSN(attrs) { - return attrs.chat_state && !attrs.body && (attrs.is_delayed || this.isOwnMessage(attrs)); - }, - - /** - * Determines whether the message is from ourselves by checking - * the `from` attribute. Doesn't check the `type` attribute. - * @private - * @method _converse.ChatRoom#isOwnMessage - * @param { Object|XMLElement|_converse.Message } msg - * @returns { boolean } - */ - isOwnMessage(msg) { - let from; - - if (lodash_es_isElement(msg)) { - from = msg.getAttribute('from'); - } else if (msg instanceof shared_converse.Message) { - from = msg.get('from'); - } else { - from = msg.from; - } - - return Strophe.getResourceFromJid(from) == this.get('nick'); - }, - - getUpdatedMessageAttributes(message, attrs) { - const new_attrs = shared_converse.ChatBox.prototype.getUpdatedMessageAttributes.call(this, message, attrs); - - if (this.isOwnMessage(attrs)) { - const stanza_id_keys = Object.keys(attrs).filter(k => k.startsWith('stanza_id')); - Object.assign(new_attrs, lodash_es_pick(attrs, stanza_id_keys)); - - if (!message.get('received')) { - new_attrs.received = new Date().toISOString(); - } - } - - return new_attrs; - }, - - /** - * Send a MUC-0410 MUC Self-Ping stanza to room to determine - * whether we're still joined. - * @async - * @private - * @method _converse.ChatRoom#isJoined - * @returns {Promise} - */ - async isJoined() { - const jid = this.get('jid'); - const ping = $iq({ - 'to': `${jid}/${this.get('nick')}`, - 'type': 'get' - }).c('ping', { - 'xmlns': Strophe.NS.PING - }); - - try { - await api.sendIQ(ping); - } catch (e) { - if (e === null) { - headless_log.warn(`isJoined: Timeout error while checking whether we're joined to MUC: ${jid}`); - } else { - headless_log.warn(`isJoined: Apparently we're no longer connected to MUC: ${jid}`); - } - - return false; - } - - return true; - }, - - /** - * Check whether we're still joined and re-join if not - * @async - * @private - * @method _converse.ChatRoom#rejoinIfNecessary - */ - async rejoinIfNecessary() { - if (!(await this.isJoined())) { - this.rejoin(); - return true; - } - }, - - /** - * @private - * @method _converse.ChatRoom#shouldShowErrorMessage - * @returns {Promise} - */ - async shouldShowErrorMessage(attrs) { - if (attrs['error_condition'] === 'not-acceptable' && (await this.rejoinIfNecessary())) { - return false; - } - - return shared_converse.ChatBox.prototype.shouldShowErrorMessage.call(this, attrs); - }, - - /** - * Looks whether we already have a moderation message for this - * incoming message. If so, it's considered "dangling" because - * it probably hasn't been applied to anything yet, given that - * the relevant message is only coming in now. - * @private - * @method _converse.ChatRoom#findDanglingModeration - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMUCMessage} - * @returns { _converse.ChatRoomMessage } - */ - findDanglingModeration(attrs) { - if (!this.messages.length) { - return null; - } // Only look for dangling moderation if there are newer - // messages than this one, since moderation come after. - - - if (this.messages.last().get('time') > attrs.time) { - // Search from latest backwards - const messages = Array.from(this.messages.models); - const stanza_id = attrs[`stanza_id ${this.get('jid')}`]; - - if (!stanza_id) { - return null; - } - - messages.reverse(); - return messages.find(({ - attributes - }) => attributes.moderated === 'retracted' && attributes.moderated_id === stanza_id && attributes.moderated_by); - } - }, - - /** - * Handles message moderation based on the passed in attributes. - * @private - * @method _converse.ChatRoom#handleModeration - * @param { object } attrs - Attributes representing a received - * message, as returned by {@link parseMUCMessage} - * @returns { Boolean } Returns `true` or `false` depending on - * whether a message was moderated or not. - */ - async handleModeration(attrs) { - const MODERATION_ATTRIBUTES = ['editable', 'moderated', 'moderated_by', 'moderated_id', 'moderation_reason']; - - if (attrs.moderated === 'retracted') { - const query = {}; - const key = `stanza_id ${this.get('jid')}`; - query[key] = attrs.moderated_id; - const message = this.messages.findWhere(query); - - if (!message) { - attrs['dangling_moderation'] = true; - await this.createMessage(attrs); - return true; - } - - message.save(lodash_es_pick(attrs, MODERATION_ATTRIBUTES)); - return true; - } else { - // Check if we have dangling moderation message - const message = this.findDanglingModeration(attrs); - - if (message) { - const moderation_attrs = lodash_es_pick(message.attributes, MODERATION_ATTRIBUTES); - const new_attrs = Object.assign({ - 'dangling_moderation': false - }, attrs, moderation_attrs); - delete new_attrs['id']; // Delete id, otherwise a new cache entry gets created - - message.save(new_attrs); - return true; - } - } - - return false; - }, - - getNotificationsText() { - const { - __ - } = shared_converse; - const actors_per_state = this.notifications.toJSON(); - const role_changes = api.settings.get('muc_show_info_messages').filter(role_change => core_converse.MUC_ROLE_CHANGES_LIST.includes(role_change)); - const join_leave_events = api.settings.get('muc_show_info_messages').filter(join_leave_event => core_converse.MUC_TRAFFIC_STATES_LIST.includes(join_leave_event)); - const states = [...core_converse.CHAT_STATES, ...join_leave_events, ...role_changes]; - return states.reduce((result, state) => { - const existing_actors = actors_per_state[state]; - - if (!(existing_actors !== null && existing_actors !== void 0 && existing_actors.length)) { - return result; - } - - const actors = existing_actors.map(a => { - var _this$getOccupant; - - return ((_this$getOccupant = this.getOccupant(a)) === null || _this$getOccupant === void 0 ? void 0 : _this$getOccupant.getDisplayName()) || a; - }); - - if (actors.length === 1) { - if (state === 'composing') { - return `${result}${__('%1$s is typing', actors[0])}\n`; - } else if (state === 'paused') { - return `${result}${__('%1$s has stopped typing', actors[0])}\n`; - } else if (state === shared_converse.GONE) { - return `${result}${__('%1$s has gone away', actors[0])}\n`; - } else if (state === 'entered') { - return `${result}${__('%1$s has entered the groupchat', actors[0])}\n`; - } else if (state === 'exited') { - return `${result}${__('%1$s has left the groupchat', actors[0])}\n`; - } else if (state === 'op') { - return `${result}${__('%1$s is now a moderator', actors[0])}\n`; - } else if (state === 'deop') { - return `${result}${__('%1$s is no longer a moderator', actors[0])}\n`; - } else if (state === 'voice') { - return `${result}${__('%1$s has been given a voice', actors[0])}\n`; - } else if (state === 'mute') { - return `${result}${__('%1$s has been muted', actors[0])}\n`; - } - } else if (actors.length > 1) { - let actors_str; - - if (actors.length > 3) { - actors_str = `${Array.from(actors).slice(0, 2).join(', ')} and others`; - } else { - const last_actor = actors.pop(); - actors_str = __('%1$s and %2$s', actors.join(', '), last_actor); - } - - if (state === 'composing') { - return `${result}${__('%1$s are typing', actors_str)}\n`; - } else if (state === 'paused') { - return `${result}${__('%1$s have stopped typing', actors_str)}\n`; - } else if (state === shared_converse.GONE) { - return `${result}${__('%1$s have gone away', actors_str)}\n`; - } else if (state === 'entered') { - return `${result}${__('%1$s have entered the groupchat', actors_str)}\n`; - } else if (state === 'exited') { - return `${result}${__('%1$s have left the groupchat', actors_str)}\n`; - } else if (state === 'op') { - return `${result}${__('%1$s are now moderators', actors[0])}\n`; - } else if (state === 'deop') { - return `${result}${__('%1$s are no longer moderators', actors[0])}\n`; - } else if (state === 'voice') { - return `${result}${__('%1$s have been given voices', actors[0])}\n`; - } else if (state === 'mute') { - return `${result}${__('%1$s have been muted', actors[0])}\n`; - } - } - - return result; - }, ''); - }, - - /** - * @param {String} actor - The nickname of the actor that caused the notification - * @param {String|Array} states - The state or states representing the type of notificcation - */ - removeNotification(actor, states) { - const actors_per_state = this.notifications.toJSON(); - states = Array.isArray(states) ? states : [states]; - states.forEach(state => { - const existing_actors = Array.from(actors_per_state[state] || []); - - if (existing_actors.includes(actor)) { - const idx = existing_actors.indexOf(actor); - existing_actors.splice(idx, 1); - this.notifications.set(state, Array.from(existing_actors)); - } - }); - }, - - /** - * Update the notifications model by adding the passed in nickname - * to the array of nicknames that all match a particular state. - * - * Removes the nickname from any other states it might be associated with. - * - * The state can be a XEP-0085 Chat State or a XEP-0045 join/leave - * state. - * @param {String} actor - The nickname of the actor that causes the notification - * @param {String} state - The state representing the type of notificcation - */ - updateNotifications(actor, state) { - const actors_per_state = this.notifications.toJSON(); - const existing_actors = actors_per_state[state] || []; - - if (existing_actors.includes(actor)) { - return; - } - - const reducer = (out, s) => { - if (s === state) { - out[s] = [...existing_actors, actor]; - } else { - out[s] = (actors_per_state[s] || []).filter(a => a !== actor); - } - - return out; - }; - - const actors_per_chat_state = core_converse.CHAT_STATES.reduce(reducer, {}); - const actors_per_traffic_state = core_converse.MUC_TRAFFIC_STATES_LIST.reduce(reducer, {}); - const actors_per_role_change = core_converse.MUC_ROLE_CHANGES_LIST.reduce(reducer, {}); - this.notifications.set(Object.assign(actors_per_chat_state, actors_per_traffic_state, actors_per_role_change)); - window.setTimeout(() => this.removeNotification(actor, state), 10000); - }, - - handleMetadataFastening(attrs) { - if (!api.settings.get('muc_show_ogp_unfurls')) { - return false; - } - - if (attrs.ogp_for_id) { - if (attrs.from !== this.get('jid')) { - // For now we only allow metadata from the MUC itself and not - // from individual users who are deemed less trustworthy. - return false; - } - - const message = this.messages.findWhere({ - 'origin_id': attrs.ogp_for_id - }); - - if (message) { - const old_list = message.get('ogp_metadata') || []; - - if (old_list.filter(m => m['og:url'] === attrs['og:url']).length) { - // Don't add metadata for the same URL again - return false; - } - - const list = [...old_list, lodash_es_pick(attrs, METADATA_ATTRIBUTES)]; - message.save('ogp_metadata', list); - return true; - } - } - - return false; - }, - - /** - * Given { @link MessageAttributes } look for XEP-0316 Room Notifications and create info - * messages for them. - * @param { XMLElement } stanza - */ - handleMEPNotification(attrs) { - var _attrs$activities; - - if (attrs.from !== this.get('jid') || !attrs.activities) { - return false; - } - - (_attrs$activities = attrs.activities) === null || _attrs$activities === void 0 ? void 0 : _attrs$activities.forEach(activity_attrs => { - const data = Object.assign({ - 'msgid': attrs.msgid, - 'from_muc': attrs.from - }, activity_attrs); - this.createMessage(data); // Trigger so that notifications are shown - - api.trigger('message', { - 'attrs': data, - 'chatbox': this - }); - }); - return !!attrs.activities.length; - }, - - /** - * Returns an already cached message (if it exists) based on the - * passed in attributes map. - * @method _converse.ChatRoom#getDuplicateMessage - * @param { object } attrs - Attributes representing a received - * message, as returned by { @link parseMUCMessage } - * @returns {Promise<_converse.Message>} - */ - getDuplicateMessage(attrs) { - var _attrs$activities2; - - if ((_attrs$activities2 = attrs.activities) !== null && _attrs$activities2 !== void 0 && _attrs$activities2.length) { - return this.messages.findWhere({ - 'type': 'info', - 'msgid': attrs.msgid - }); - } else { - return shared_converse.ChatBox.prototype.getDuplicateMessage.call(this, attrs); - } - }, - - /** - * Handler for all MUC messages sent to this groupchat. This method - * shouldn't be called directly, instead {@link _converse.ChatRoom#queueMessage} - * should be called. - * @method _converse.ChatRoom#onMessage - * @param { MessageAttributes } attrs - A promise which resolves to the message attributes. - */ - async onMessage(attrs) { - attrs = await attrs; - - if (utils_form.isErrorObject(attrs)) { - attrs.stanza && headless_log.error(attrs.stanza); - return headless_log.error(attrs.message); - } - - const message = this.getDuplicateMessage(attrs); - - if (message) { - message.get('type') === 'groupchat' && this.updateMessage(message, attrs); - return; - } else if (attrs.is_valid_receipt_request || attrs.is_marker || this.ignorableCSN(attrs)) { - return; - } - - if (this.handleMetadataFastening(attrs) || this.handleMEPNotification(attrs) || (await this.handleRetraction(attrs)) || (await this.handleModeration(attrs)) || (await this.handleSubjectChange(attrs))) { - attrs.nick && this.removeNotification(attrs.nick, ['composing', 'paused']); - return; - } - - this.setEditable(attrs, attrs.time); - - if (attrs['chat_state']) { - this.updateNotifications(attrs.nick, attrs.chat_state); - } - - if (utils_form.shouldCreateGroupchatMessage(attrs)) { - const msg = this.handleCorrection(attrs) || (await this.createMessage(attrs)); - this.removeNotification(attrs.nick, ['composing', 'paused']); - this.handleUnreadMessage(msg); - } - }, - - handleModifyError(pres) { - var _pres$querySelector; - - const text = (_pres$querySelector = pres.querySelector('error text')) === null || _pres$querySelector === void 0 ? void 0 : _pres$querySelector.textContent; - - if (text) { - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.CONNECTING) { - this.setDisconnectionState(text); - } else { - const attrs = { - 'type': 'error', - 'message': text, - 'is_ephemeral': true - }; - this.createMessage(attrs); - } - } - }, - - /** - * Handle a presence stanza that disconnects the user from the MUC - * @param { XMLElement } stanza - */ - handleDisconnection(stanza) { - var _item$querySelector; - - const is_self = stanza.querySelector("status[code='110']") !== null; - const x = sizzle_default()(`x[xmlns="${Strophe.NS.MUC_USER}"]`, stanza).pop(); - - if (!x) { - return; - } - - const disconnection_codes = Object.keys(shared_converse.muc.disconnect_messages); - const codes = sizzle_default()('status', x).map(s => s.getAttribute('code')).filter(c => disconnection_codes.includes(c)); - const disconnected = is_self && codes.length > 0; - - if (!disconnected) { - return; - } // By using querySelector we assume here there is - // one per - // element. This appears to be a safe assumption, since - // each element pertains to a single user. - - - const item = x.querySelector('item'); - const reason = item ? (_item$querySelector = item.querySelector('reason')) === null || _item$querySelector === void 0 ? void 0 : _item$querySelector.textContent : undefined; - const actor = item ? lodash_es_invoke(item.querySelector('actor'), 'getAttribute', 'nick') : undefined; - const message = shared_converse.muc.disconnect_messages[codes[0]]; - const status = codes.includes('301') ? core_converse.ROOMSTATUS.BANNED : core_converse.ROOMSTATUS.DISCONNECTED; - this.setDisconnectionState(message, reason, actor, status); - }, - - getActionInfoMessage(code, nick, actor) { - const __ = shared_converse.__; - - if (code === '301') { - return actor ? __('%1$s has been banned by %2$s', nick, actor) : __('%1$s has been banned', nick); - } else if (code === '303') { - return __("%1$s's nickname has changed", nick); - } else if (code === '307') { - return actor ? __('%1$s has been kicked out by %2$s', nick, actor) : __('%1$s has been kicked out', nick); - } else if (code === '321') { - return __('%1$s has been removed because of an affiliation change', nick); - } else if (code === '322') { - return __('%1$s has been removed for not being a member', nick); - } - }, - - createAffiliationChangeMessage(occupant) { - const __ = shared_converse.__; - const previous_affiliation = occupant._previousAttributes.affiliation; - - if (!previous_affiliation) { - // If no previous affiliation was set, then we don't - // interpret this as an affiliation change. - // For example, if muc_send_probes is true, then occupants - // are created based on incoming messages, in which case - // we don't yet know the affiliation - return; - } - - const current_affiliation = occupant.get('affiliation'); - - if (previous_affiliation === 'admin' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.EXADMIN)) { - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is no longer an admin of this groupchat', occupant.get('nick')) - }); - } else if (previous_affiliation === 'owner' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.EXOWNER)) { - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is no longer an owner of this groupchat', occupant.get('nick')) - }); - } else if (previous_affiliation === 'outcast' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.EXOUTCAST)) { - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is no longer banned from this groupchat', occupant.get('nick')) - }); - } - - if (current_affiliation === 'none' && previous_affiliation === 'member' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.EXMEMBER)) { - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is no longer a member of this groupchat', occupant.get('nick')) - }); - } - - if (current_affiliation === 'member' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.MEMBER)) { - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is now a member of this groupchat', occupant.get('nick')) - }); - } else if (current_affiliation === 'admin' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.ADMIN) || current_affiliation == 'owner' && shared_converse.isInfoVisible(core_converse.AFFILIATION_CHANGES.OWNER)) { - // For example: AppleJack is now an (admin|owner) of this groupchat - this.createMessage({ - 'type': 'info', - 'message': __('%1$s is now an %2$s of this groupchat', occupant.get('nick'), current_affiliation) - }); - } - }, - - createRoleChangeMessage(occupant, changed) { - if (changed === 'none' || occupant.changed.affiliation) { - // We don't inform of role changes if they accompany affiliation changes. - return; - } - - const previous_role = occupant._previousAttributes.role; - - if (previous_role === 'moderator' && shared_converse.isInfoVisible(core_converse.MUC_ROLE_CHANGES.DEOP)) { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_ROLE_CHANGES.DEOP); - } else if (previous_role === 'visitor' && shared_converse.isInfoVisible(core_converse.MUC_ROLE_CHANGES.VOICE)) { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_ROLE_CHANGES.VOICE); - } - - if (occupant.get('role') === 'visitor' && shared_converse.isInfoVisible(core_converse.MUC_ROLE_CHANGES.MUTE)) { - this.updateNotifications(occupant.get('nick'), core_converse.MUC_ROLE_CHANGES.MUTE); - } else if (occupant.get('role') === 'moderator') { - if (!['owner', 'admin'].includes(occupant.get('affiliation')) && shared_converse.isInfoVisible(core_converse.MUC_ROLE_CHANGES.OP)) { - // Oly show this message if the user isn't already - // an admin or owner, otherwise this isn't new information. - this.updateNotifications(occupant.get('nick'), core_converse.MUC_ROLE_CHANGES.OP); - } - } - }, - - /** - * Create an info message based on a received MUC status code - * @private - * @method _converse.ChatRoom#createInfoMessage - * @param { string } code - The MUC status code - * @param { XMLElement } stanza - The original stanza that contains the code - * @param { Boolean } is_self - Whether this stanza refers to our own presence - */ - createInfoMessage(code, stanza, is_self) { - const __ = shared_converse.__; - const data = { - 'type': 'info' - }; - - if (!shared_converse.isInfoVisible(code)) { - return; - } - - if (code === '110' || code === '100' && !is_self) { - return; - } else if (code in shared_converse.muc.info_messages) { - data.message = shared_converse.muc.info_messages[code]; - } else if (!is_self && ACTION_INFO_CODES.includes(code)) { - var _item$querySelector2, _item$querySelector3; - - const nick = Strophe.getResourceFromJid(stanza.getAttribute('from')); - const item = sizzle_default()(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop(); - data.actor = item ? (_item$querySelector2 = item.querySelector('actor')) === null || _item$querySelector2 === void 0 ? void 0 : _item$querySelector2.getAttribute('nick') : undefined; - data.reason = item ? (_item$querySelector3 = item.querySelector('reason')) === null || _item$querySelector3 === void 0 ? void 0 : _item$querySelector3.textContent : undefined; - data.message = this.getActionInfoMessage(code, nick, data.actor); - } else if (is_self && code in shared_converse.muc.new_nickname_messages) { - // XXX: Side-effect of setting the nick. Should ideally be refactored out of this method - let nick; - - if (is_self && code === '210') { - nick = Strophe.getResourceFromJid(stanza.getAttribute('from')); - } else if (is_self && code === '303') { - nick = sizzle_default()(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, stanza).pop().getAttribute('nick'); - } - - this.save('nick', nick); - data.message = __(shared_converse.muc.new_nickname_messages[code], nick); - } - - if (data.message) { - if (code === '201' && this.messages.findWhere(data)) { - return; - } else if (code in shared_converse.muc.info_messages && this.messages.length && this.messages.pop().get('message') === data.message) { - // XXX: very naive duplication checking - return; - } - - this.createMessage(data); - } - }, - - /** - * Create info messages based on a received presence or message stanza - * @private - * @method _converse.ChatRoom#createInfoMessages - * @param { XMLElement } stanza - */ - createInfoMessages(stanza) { - const codes = sizzle_default()(`x[xmlns="${Strophe.NS.MUC_USER}"] status`, stanza).map(s => s.getAttribute('code')); - - if (codes.includes('333') && codes.includes('307')) { - // See: https://github.com/xsf/xeps/pull/969/files#diff-ac5113766e59219806793c1f7d967f1bR4966 - codes.splice(codes.indexOf('307'), 1); - } - - const is_self = codes.includes('110'); - codes.forEach(code => this.createInfoMessage(code, stanza, is_self)); - }, - - /** - * Set parameters regarding disconnection from this room. This helps to - * communicate to the user why they were disconnected. - * @param { String } message - The disconnection message, as received from (or - * implied by) the server. - * @param { String } reason - The reason provided for the disconnection - * @param { String } actor - The person (if any) responsible for this disconnection - * @param { Integer } status - The status code (see `converse.ROOMSTATUS`) - */ - setDisconnectionState(message, reason, actor, status = core_converse.ROOMSTATUS.DISCONNECTED) { - this.session.save({ - 'connection_status': status, - 'disconnection_actor': actor, - 'disconnection_message': message, - 'disconnection_reason': reason - }); - }, - - onNicknameClash(presence) { - const __ = shared_converse.__; - - if (api.settings.get('muc_nickname_from_jid')) { - const nick = presence.getAttribute('from').split('/')[1]; - - if (nick === shared_converse.getDefaultMUCNickname()) { - this.join(nick + '-2'); - } else { - const del = nick.lastIndexOf('-'); - const num = nick.substring(del + 1, nick.length); - this.join(nick.substring(0, del + 1) + String(Number(num) + 1)); - } - } else { - this.save({ - 'nickname_validation_message': __('The nickname you chose is reserved or ' + 'currently in use, please choose a different one.') - }); - this.session.save({ - 'connection_status': core_converse.ROOMSTATUS.NICKNAME_REQUIRED - }); - } - }, - - /** - * Parses a stanza with type "error" and sets the proper - * `connection_status` value for this {@link _converse.ChatRoom} as - * well as any additional output that can be shown to the user. - * @private - * @param { XMLElement } stanza - The presence stanza - */ - onErrorPresence(stanza) { - var _sizzle$pop; - - const __ = shared_converse.__; - const error = stanza.querySelector('error'); - const error_type = error.getAttribute('type'); - const reason = (_sizzle$pop = sizzle_default()(`text[xmlns="${Strophe.NS.STANZAS}"]`, error).pop()) === null || _sizzle$pop === void 0 ? void 0 : _sizzle$pop.textContent; - - if (error_type === 'modify') { - this.handleModifyError(stanza); - } else if (error_type === 'auth') { - if (sizzle_default()(`not-authorized[xmlns="${Strophe.NS.STANZAS}"]`, error).length) { - this.save({ - 'password_validation_message': reason || __('Password incorrect') - }); - this.session.save({ - 'connection_status': core_converse.ROOMSTATUS.PASSWORD_REQUIRED - }); - } - - if (error.querySelector('registration-required')) { - const message = __('You are not on the member list of this groupchat.'); - - this.setDisconnectionState(message, reason); - } else if (error.querySelector('forbidden')) { - this.setDisconnectionState(shared_converse.muc.disconnect_messages[301], reason, null, core_converse.ROOMSTATUS.BANNED); - } - } else if (error_type === 'cancel') { - if (error.querySelector('not-allowed')) { - const message = __('You are not allowed to create new groupchats.'); - - this.setDisconnectionState(message, reason); - } else if (error.querySelector('not-acceptable')) { - const message = __("Your nickname doesn't conform to this groupchat's policies."); - - this.setDisconnectionState(message, reason); - } else if (sizzle_default()(`gone[xmlns="${Strophe.NS.STANZAS}"]`, error).length) { - var _sizzle$pop2; - - const moved_jid = (_sizzle$pop2 = sizzle_default()(`gone[xmlns="${Strophe.NS.STANZAS}"]`, error).pop()) === null || _sizzle$pop2 === void 0 ? void 0 : _sizzle$pop2.textContent.replace(/^xmpp:/, '').replace(/\?join$/, ''); - this.save({ - moved_jid, - 'destroyed_reason': reason - }); - this.session.save({ - 'connection_status': core_converse.ROOMSTATUS.DESTROYED - }); - } else if (error.querySelector('conflict')) { - this.onNicknameClash(stanza); - } else if (error.querySelector('item-not-found')) { - const message = __('This groupchat does not (yet) exist.'); - - this.setDisconnectionState(message, reason); - } else if (error.querySelector('service-unavailable')) { - const message = __('This groupchat has reached its maximum number of participants.'); - - this.setDisconnectionState(message, reason); - } else if (error.querySelector('remote-server-not-found')) { - const message = __('Remote server not found'); - - const feedback = reason ? __('The explanation given is: "%1$s".', reason) : undefined; - this.setDisconnectionState(message, feedback); - } - } - }, - - /** - * Listens for incoming presence stanzas from the service that hosts this MUC - * @private - * @method _converse.ChatRoom#onPresenceFromMUCHost - * @param { XMLElement } stanza - The presence stanza - */ - onPresenceFromMUCHost(stanza) { - if (stanza.getAttribute('type') === 'error') { - const error = stanza.querySelector('error'); - - if ((error === null || error === void 0 ? void 0 : error.getAttribute('type')) === 'wait' && error !== null && error !== void 0 && error.querySelector('resource-constraint')) { - // If we get a error, we assume it's in context of XEP-0437 RAI. - // We remove this MUC's host from the list of enabled domains and rejoin the MUC. - if (this.session.get('connection_status') === core_converse.ROOMSTATUS.DISCONNECTED) { - this.rejoin(); - } - } - } - }, - - /** - * Handles incoming presence stanzas coming from the MUC - * @private - * @method _converse.ChatRoom#onPresence - * @param { XMLElement } stanza - */ - onPresence(stanza) { - if (stanza.getAttribute('type') === 'error') { - return this.onErrorPresence(stanza); - } - - this.createInfoMessages(stanza); - - if (stanza.querySelector("status[code='110']")) { - this.onOwnPresence(stanza); - - if (this.getOwnRole() !== 'none' && this.session.get('connection_status') === core_converse.ROOMSTATUS.CONNECTING) { - this.session.save('connection_status', core_converse.ROOMSTATUS.CONNECTED); - } - } else { - this.updateOccupantsOnPresence(stanza); - } - }, - - /** - * Handles a received presence relating to the current user. - * - * For locked groupchats (which are by definition "new"), the - * groupchat will either be auto-configured or created instantly - * (with default config) or a configuration groupchat will be - * rendered. - * - * If the groupchat is not locked, then the groupchat will be - * auto-configured only if applicable and if the current - * user is the groupchat's owner. - * @private - * @method _converse.ChatRoom#onOwnPresence - * @param { XMLElement } pres - The stanza - */ - async onOwnPresence(stanza) { - await this.occupants.fetched; - const old_status = this.session.get('connection_status'); - - if (stanza.getAttribute('type') !== 'unavailable' && old_status !== core_converse.ROOMSTATUS.ENTERED) { - // Set connection_status before creating the occupant, but - // only trigger afterwards, so that plugins can access the - // occupant in their event handlers. - this.session.save('connection_status', core_converse.ROOMSTATUS.ENTERED, { - 'silent': true - }); - this.updateOccupantsOnPresence(stanza); - this.session.trigger('change:connection_status', this.session, old_status); - } else { - this.updateOccupantsOnPresence(stanza); - } - - if (stanza.getAttribute('type') === 'unavailable') { - this.handleDisconnection(stanza); - return; - } else { - const locked_room = stanza.querySelector("status[code='201']"); - - if (locked_room) { - if (this.get('auto_configure')) { - this.autoConfigureChatRoom().then(() => this.refreshDiscoInfo()); - } else if (api.settings.get('muc_instant_rooms')) { - // Accept default configuration - this.sendConfiguration().then(() => this.refreshDiscoInfo()); - } else { - this.session.save({ - 'view': core_converse.MUC.VIEWS.CONFIG - }); - return; - } - } else if (!this.features.get('fetched')) { - // The features for this groupchat weren't fetched. - // That must mean it's a new groupchat without locking - // (in which case Prosody doesn't send a 201 status), - // otherwise the features would have been fetched in - // the "initialize" method already. - if (this.getOwnAffiliation() === 'owner' && this.get('auto_configure')) { - this.autoConfigureChatRoom().then(() => this.refreshDiscoInfo()); - } else { - this.getDiscoInfo(); - } - } - } - - this.session.save({ - 'connection_status': core_converse.ROOMSTATUS.ENTERED - }); - }, - - /** - * Returns a boolean to indicate whether the current user - * was mentioned in a message. - * @private - * @method _converse.ChatRoom#isUserMentioned - * @param { String } - The text message - */ - isUserMentioned(message) { - const nick = this.get('nick'); - - if (message.get('references').length) { - const mentions = message.get('references').filter(ref => ref.type === 'mention').map(ref => ref.value); - return mentions.includes(nick); - } else { - return new RegExp(`\\b${nick}\\b`).test(message.get('message')); - } - }, - - incrementUnreadMsgsCounter(message) { - const settings = { - 'num_unread_general': this.get('num_unread_general') + 1 - }; - - if (this.get('num_unread_general') === 0) { - settings['first_unread_id'] = message.get('id'); - } - - if (this.isUserMentioned(message)) { - settings.num_unread = this.get('num_unread') + 1; - } - - this.save(settings); - }, - - clearUnreadMsgCounter() { - if (this.get('num_unread_general') > 0 || this.get('num_unread') > 0 || this.get('has_activity')) { - this.sendMarkerForMessage(this.messages.last()); - } - - utils_form.safeSave(this, { - 'has_activity': false, - 'num_unread': 0, - 'num_unread_general': 0 - }); - } - -}; -/* harmony default export */ const muc = (ChatRoomMixin); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/occupant.js - - - -/** - * Represents a participant in a MUC - * @class - * @namespace _converse.ChatRoomOccupant - * @memberOf _converse - */ - -const ChatRoomOccupant = Model.extend({ - defaults: { - 'hats': [], - 'show': 'offline', - 'states': [] - }, - - initialize(attributes) { - this.set(Object.assign({ - 'id': utils_form.getUniqueId() - }, attributes)); - this.on('change:image_hash', this.onAvatarChanged, this); - }, - - onAvatarChanged() { - const hash = this.get('image_hash'); - const vcards = []; - - if (this.get('jid')) { - vcards.push(shared_converse.vcards.findWhere({ - 'jid': this.get('jid') - })); - } - - vcards.push(shared_converse.vcards.findWhere({ - 'jid': this.get('from') - })); - vcards.filter(v => v).forEach(vcard => { - if (hash && vcard.get('image_hash') !== hash) { - api.vcard.update(vcard, true); - } - }); - }, - - getDisplayName() { - return this.get('nick') || this.get('jid'); - }, - - isMember() { - return ['admin', 'owner', 'member'].includes(this.get('affiliation')); - }, - - isModerator() { - return ['admin', 'owner'].includes(this.get('affiliation')) || this.get('role') === 'moderator'; - }, - - isSelf() { - return this.get('states').includes('110'); - } - -}); -/* harmony default export */ const occupant = (ChatRoomOccupant); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/occupants.js - - - - - - -const MUC_ROLE_WEIGHTS = { - 'moderator': 1, - 'participant': 2, - 'visitor': 3, - 'none': 2 -}; -/** - * A list of {@link _converse.ChatRoomOccupant} instances, representing participants in a MUC. - * @class - * @namespace _converse.ChatRoomOccupants - * @memberOf _converse - */ - -const ChatRoomOccupants = Collection.extend({ - model: occupant, - - comparator(occupant1, occupant2) { - const role1 = occupant1.get('role') || 'none'; - const role2 = occupant2.get('role') || 'none'; - - if (MUC_ROLE_WEIGHTS[role1] === MUC_ROLE_WEIGHTS[role2]) { - const nick1 = occupant1.getDisplayName().toLowerCase(); - const nick2 = occupant2.getDisplayName().toLowerCase(); - return nick1 < nick2 ? -1 : nick1 > nick2 ? 1 : 0; - } else { - return MUC_ROLE_WEIGHTS[role1] < MUC_ROLE_WEIGHTS[role2] ? -1 : 1; - } - }, - - getAutoFetchedAffiliationLists() { - const affs = api.settings.get('muc_fetch_members'); - return Array.isArray(affs) ? affs : affs ? ['member', 'admin', 'owner'] : []; - }, - - async fetchMembers() { - const affiliations = this.getAutoFetchedAffiliationLists(); - - if (affiliations.length === 0) { - return; - } - - const muc_jid = this.chatroom.get('jid'); - const aff_lists = await Promise.all(affiliations.map(a => getAffiliationList(a, muc_jid))); - const new_members = aff_lists.reduce((acc, val) => utils_form.isErrorObject(val) ? acc : [...val, ...acc], []); - const known_affiliations = affiliations.filter(a => !utils_form.isErrorObject(aff_lists[affiliations.indexOf(a)])); - const new_jids = new_members.map(m => m.jid).filter(m => m !== undefined); - const new_nicks = new_members.map(m => !m.jid && m.nick || undefined).filter(m => m !== undefined); - const removed_members = this.filter(m => { - return known_affiliations.includes(m.get('affiliation')) && !new_nicks.includes(m.get('nick')) && !new_jids.includes(m.get('jid')); - }); - removed_members.forEach(occupant => { - if (occupant.get('jid') === shared_converse.bare_jid) { - return; - } - - if (occupant.get('show') === 'offline') { - occupant.destroy(); - } else { - occupant.save('affiliation', null); - } - }); - new_members.forEach(attrs => { - const occupant = attrs.jid ? this.findOccupant({ - 'jid': attrs.jid - }) : this.findOccupant({ - 'nick': attrs.nick - }); - - if (occupant) { - occupant.save(attrs); - } else { - this.create(attrs); - } - }); - /** - * Triggered once the member lists for this MUC have been fetched and processed. - * @event _converse#membersFetched - * @example _converse.api.listen.on('membersFetched', () => { ... }); - */ - - api.trigger('membersFetched'); - }, - - /** - * @typedef { Object} OccupantData - * @property { String } [jid] - * @property { String } [nick] - */ - - /** - * Try to find an existing occupant based on the passed in - * data object. - * - * If we have a JID, we use that as lookup variable, - * otherwise we use the nick. We don't always have both, - * but should have at least one or the other. - * @private - * @method _converse.ChatRoomOccupants#findOccupant - * @param { OccupantData } data - */ - findOccupant(data) { - const jid = Strophe.getBareJidFromJid(data.jid); - return jid && this.findWhere({ - jid - }) || this.findWhere({ - 'nick': data.nick - }); - } - -}); -/* harmony default export */ const occupants = (ChatRoomOccupants); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/affiliations/api.js - -/* harmony default export */ const affiliations_api = ({ - /** - * The "affiliations" namespace groups methods relevant to setting and - * getting MUC affiliations. - * - * @namespace api.rooms.affiliations - * @memberOf api.rooms - */ - affiliations: { - /** - * Set the given affliation for the given JIDs in the specified MUCs - * - * @param { String|Array } muc_jids - The JIDs of the MUCs in - * which the affiliation should be set. - * @param { Object[] } users - An array of objects representing users - * for whom the affiliation is to be set. - * @param { String } users[].jid - The JID of the user whose affiliation will change - * @param { ('outcast'|'member'|'admin'|'owner') } users[].affiliation - The new affiliation for this user - * @param { String } [users[].reason] - An optional reason for the affiliation change - */ - set(muc_jids, users) { - users = !Array.isArray(users) ? [users] : users; - muc_jids = !Array.isArray(muc_jids) ? [muc_jids] : muc_jids; - return setAffiliations(muc_jids, users); - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/api.js - - - - -/* harmony default export */ const muc_api = ({ - /** - * The "rooms" namespace groups methods relevant to chatrooms - * (aka groupchats). - * - * @namespace api.rooms - * @memberOf api - */ - rooms: { - /** - * Creates a new MUC chatroom (aka groupchat) - * - * Similar to {@link api.rooms.open}, but creates - * the chatroom in the background (i.e. doesn't cause a view to open). - * - * @method api.rooms.create - * @param {(string[]|string)} jid|jids The JID or array of - * JIDs of the chatroom(s) to create - * @param {object} [attrs] attrs The room attributes - * @returns {Promise} Promise which resolves with the Model representing the chat. - */ - create(jids, attrs = {}) { - attrs = typeof attrs === 'string' ? { - 'nick': attrs - } : attrs || {}; - - if (!attrs.nick && api.settings.get('muc_nickname_from_jid')) { - attrs.nick = Strophe.getNodeFromJid(shared_converse.bare_jid); - } - - if (jids === undefined) { - throw new TypeError('rooms.create: You need to provide at least one JID'); - } else if (typeof jids === 'string') { - return api.rooms.get(utils_form.getJIDFromURI(jids), attrs, true); - } - - return jids.map(jid => api.rooms.get(utils_form.getJIDFromURI(jid), attrs, true)); - }, - - /** - * Opens a MUC chatroom (aka groupchat) - * - * Similar to {@link api.chats.open}, but for groupchats. - * - * @method api.rooms.open - * @param {string} jid The room JID or JIDs (if not specified, all - * currently open rooms will be returned). - * @param {string} attrs A map containing any extra room attributes. - * @param {string} [attrs.nick] The current user's nickname for the MUC - * @param {boolean} [attrs.auto_configure] A boolean, indicating - * whether the room should be configured automatically or not. - * If set to `true`, then it makes sense to pass in configuration settings. - * @param {object} [attrs.roomconfig] A map of configuration settings to be used when the room gets - * configured automatically. Currently it doesn't make sense to specify - * `roomconfig` values if `auto_configure` is set to `false`. - * For a list of configuration values that can be passed in, refer to these values - * in the [XEP-0045 MUC specification](https://xmpp.org/extensions/xep-0045.html#registrar-formtype-owner). - * The values should be named without the `muc#roomconfig_` prefix. - * @param {boolean} [attrs.minimized] A boolean, indicating whether the room should be opened minimized or not. - * @param {boolean} [attrs.bring_to_foreground] A boolean indicating whether the room should be - * brought to the foreground and therefore replace the currently shown chat. - * If there is no chat currently open, then this option is ineffective. - * @param {Boolean} [force=false] - By default, a minimized - * room won't be maximized (in `overlayed` view mode) and in - * `fullscreen` view mode a newly opened room won't replace - * another chat already in the foreground. - * Set `force` to `true` if you want to force the room to be - * maximized or shown. - * @returns {Promise} Promise which resolves with the Model representing the chat. - * - * @example - * this.api.rooms.open('group@muc.example.com') - * - * @example - * // To return an array of rooms, provide an array of room JIDs: - * api.rooms.open(['group1@muc.example.com', 'group2@muc.example.com']) - * - * @example - * // To setup a custom nickname when joining the room, provide the optional nick argument: - * api.rooms.open('group@muc.example.com', {'nick': 'mycustomnick'}) - * - * @example - * // For example, opening a room with a specific default configuration: - * api.rooms.open( - * 'myroom@conference.example.org', - * { 'nick': 'coolguy69', - * 'auto_configure': true, - * 'roomconfig': { - * 'changesubject': false, - * 'membersonly': true, - * 'persistentroom': true, - * 'publicroom': true, - * 'roomdesc': 'Comfy room for hanging out', - * 'whois': 'anyone' - * } - * } - * ); - */ - async open(jids, attrs = {}, force = false) { - await api.waitUntil('chatBoxesFetched'); - - if (jids === undefined) { - const err_msg = 'rooms.open: You need to provide at least one JID'; - headless_log.error(err_msg); - throw new TypeError(err_msg); - } else if (typeof jids === 'string') { - const room = await api.rooms.get(jids, attrs, true); - !attrs.hidden && (room === null || room === void 0 ? void 0 : room.maybeShow(force)); - return room; - } else { - const rooms = await Promise.all(jids.map(jid => api.rooms.get(jid, attrs, true))); - rooms.forEach(r => !attrs.hidden && r.maybeShow(force)); - return rooms; - } - }, - - /** - * Fetches the object representing a MUC chatroom (aka groupchat) - * - * @method api.rooms.get - * @param { String } [jid] The room JID (if not specified, all rooms will be returned). - * @param { Object } [attrs] A map containing any extra room attributes - * to be set if `create` is set to `true` - * @param { String } [attrs.nick] Specify the nickname - * @param { String } [attrs.password ] Specify a password if needed to enter a new room - * @param { Boolean } create A boolean indicating whether the room should be created - * if not found (default: `false`) - * @returns { Promise<_converse.ChatRoom> } - * @example - * api.waitUntil('roomsAutoJoined').then(() => { - * const create_if_not_found = true; - * api.rooms.get( - * 'group@muc.example.com', - * {'nick': 'dread-pirate-roberts', 'password': 'secret'}, - * create_if_not_found - * ) - * }); - */ - async get(jids, attrs = {}, create = false) { - await api.waitUntil('chatBoxesFetched'); - - async function _get(jid) { - jid = utils_form.getJIDFromURI(jid); - let model = await api.chatboxes.get(jid); - - if (!model && create) { - model = await api.chatboxes.create(jid, attrs, shared_converse.ChatRoom); - } else { - model = model && model.get('type') === shared_converse.CHATROOMS_TYPE ? model : null; - - if (model && Object.keys(attrs).length) { - model.save(attrs); - } - } - - return model; - } - - if (jids === undefined) { - const chats = await api.chatboxes.get(); - return chats.filter(c => c.get('type') === shared_converse.CHATROOMS_TYPE); - } else if (typeof jids === 'string') { - return _get(jids); - } - - return Promise.all(jids.map(jid => _get(jid))); - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/utils.js - - - - - -const { - Strophe: muc_utils_Strophe, - sizzle: muc_utils_sizzle, - u: muc_utils_u -} = core_converse.env; -/** - * Given an occupant model, see which roles may be assigned to that user. - * @param { Model } occupant - * @returns { ('moderator', 'participant', 'visitor')[] } - An array of assignable roles - */ - -function getAssignableRoles(occupant) { - let disabled = api.settings.get('modtools_disable_assign'); - - if (!Array.isArray(disabled)) { - disabled = disabled ? ROLES : []; - } - - if (occupant.get('role') === 'moderator') { - return ROLES.filter(r => !disabled.includes(r)); - } else { - return []; - } -} -function registerDirectInvitationHandler() { - shared_converse.connection.addHandler(message => { - shared_converse.onDirectMUCInvitation(message); - - return true; - }, 'jabber:x:conference', 'message'); -} -function disconnectChatRooms() { - /* When disconnecting, mark all groupchats as - * disconnected, so that they will be properly entered again - * when fetched from session storage. - */ - return shared_converse.chatboxes.filter(m => m.get('type') === shared_converse.CHATROOMS_TYPE).forEach(m => m.session.save({ - 'connection_status': core_converse.ROOMSTATUS.DISCONNECTED - })); -} -async function onWindowStateChanged(data) { - if (data.state === 'visible' && api.connection.connected()) { - const rooms = await api.rooms.get(); - rooms.forEach(room => room.rejoinIfNecessary()); - } -} -async function routeToRoom(jid) { - if (!muc_utils_u.isValidMUCJID(jid)) { - return headless_log.warn(`invalid jid "${jid}" provided in url fragment`); - } - - await api.waitUntil('roomsAutoJoined'); - - if (api.settings.get('allow_bookmarks')) { - await api.waitUntil('bookmarksInitialized'); - } - - api.rooms.open(jid); -} -/* Opens a groupchat, making sure that certain attributes - * are correct, for example that the "type" is set to - * "chatroom". - */ - -async function openChatRoom(jid, settings) { - settings.type = shared_converse.CHATROOMS_TYPE; - settings.id = jid; - const chatbox = await api.rooms.get(jid, settings, true); - chatbox.maybeShow(true); - return chatbox; -} -/** - * A direct MUC invitation to join a groupchat has been received - * See XEP-0249: Direct MUC invitations. - * @private - * @method _converse.ChatRoom#onDirectMUCInvitation - * @param { XMLElement } message - The message stanza containing the invitation. - */ - -async function onDirectMUCInvitation(message) { - const { - __ - } = shared_converse; - const x_el = muc_utils_sizzle('x[xmlns="jabber:x:conference"]', message).pop(), - from = muc_utils_Strophe.getBareJidFromJid(message.getAttribute('from')), - room_jid = x_el.getAttribute('jid'), - reason = x_el.getAttribute('reason'); - let result; - - if (api.settings.get('auto_join_on_invite')) { - result = true; - } else { - // Invite request might come from someone not your roster list - let contact = shared_converse.roster.get(from); - - contact = contact ? contact.getDisplayName() : from; - - if (!reason) { - result = confirm(__('%1$s has invited you to join a groupchat: %2$s', contact, room_jid)); - } else { - result = confirm(__('%1$s has invited you to join a groupchat: %2$s, and left the following reason: "%3$s"', contact, room_jid, reason)); - } - } - - if (result === true) { - const chatroom = await openChatRoom(room_jid, { - 'password': x_el.getAttribute('password') - }); - - if (chatroom.session.get('connection_status') === core_converse.ROOMSTATUS.DISCONNECTED) { - shared_converse.chatboxes.get(room_jid).rejoin(); - } - } -} -function getDefaultMUCNickname() { - // XXX: if anything changes here, update the docs for the - // locked_muc_nickname setting. - if (!shared_converse.xmppstatus) { - throw new Error("Can't call _converse.getDefaultMUCNickname before the statusInitialized has been fired."); - } - - const nick = shared_converse.xmppstatus.getNickname(); - - if (nick) { - return nick; - } else if (api.settings.get('muc_nickname_from_jid')) { - return muc_utils_Strophe.unescapeNode(muc_utils_Strophe.getNodeFromJid(shared_converse.bare_jid)); - } -} -/** - * Determines info message visibility based on - * muc_show_info_messages configuration setting - * @param {*} code - * @memberOf _converse - */ - -function isInfoVisible(code) { - const info_messages = api.settings.get('muc_show_info_messages'); - - if (info_messages.includes(code)) { - return true; - } - - return false; -} -/* Automatically join groupchats, based on the - * "auto_join_rooms" configuration setting, which is an array - * of strings (groupchat JIDs) or objects (with groupchat JID and other settings). - */ - -async function autoJoinRooms() { - await Promise.all(api.settings.get('auto_join_rooms').map(muc => { - if (typeof muc === 'string') { - if (shared_converse.chatboxes.where({ - 'jid': muc - }).length) { - return Promise.resolve(); - } - - return api.rooms.open(muc); - } else if (lodash_es_isObject(muc)) { - return api.rooms.open(muc.jid, { ...muc - }); - } else { - headless_log.error('Invalid muc criteria specified for "auto_join_rooms"'); - return Promise.resolve(); - } - })); - /** - * Triggered once any rooms that have been configured to be automatically joined, - * specified via the _`auto_join_rooms` setting, have been entered. - * @event _converse#roomsAutoJoined - * @example _converse.api.listen.on('roomsAutoJoined', () => { ... }); - * @example _converse.api.waitUntil('roomsAutoJoined').then(() => { ... }); - */ - - api.trigger('roomsAutoJoined'); -} -function onAddClientFeatures() { - if (api.settings.get('allow_muc')) { - api.disco.own.features.add(muc_utils_Strophe.NS.MUC); - } - - if (api.settings.get('allow_muc_invitations')) { - api.disco.own.features.add('jabber:x:conference'); // Invites - } -} -function onBeforeTearDown() { - shared_converse.chatboxes.where({ - 'type': shared_converse.CHATROOMS_TYPE - }).forEach(muc => safeSave(muc.session, { - 'connection_status': core_converse.ROOMSTATUS.DISCONNECTED - })); -} -function onStatusInitialized() { - window.addEventListener(shared_converse.unloadevent, () => { - const using_websocket = api.connection.isType('websocket'); - - if (using_websocket && (!api.settings.get('enable_smacks') || !shared_converse.session.get('smacks_stream_id'))) { - // For non-SMACKS websocket connections, or non-resumeable - // connections, we disconnect all chatrooms when the page unloads. - // See issue #1111 - disconnectChatRooms(); - } - }); -} -function onBeforeResourceBinding() { - shared_converse.connection.addHandler(stanza => { - const muc_jid = muc_utils_Strophe.getBareJidFromJid(stanza.getAttribute('from')); - - if (!shared_converse.chatboxes.get(muc_jid)) { - api.waitUntil('chatBoxesFetched').then(async () => { - const muc = shared_converse.chatboxes.get(muc_jid); - - if (muc) { - await muc.initialized; - muc.message_handler.run(stanza); - } - }); - } - - return true; - }, null, 'message', 'groupchat'); -} -Object.assign(shared_converse, { - getAssignableRoles -}); -;// CONCATENATED MODULE: ./src/headless/plugins/muc/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Implements the non-view logic for XEP-0045 Multi-User Chat - */ - - - - - - - - - - - - - -const ROLES = ['moderator', 'participant', 'visitor']; -const AFFILIATIONS = ['owner', 'admin', 'member', 'outcast', 'none']; -core_converse.AFFILIATION_CHANGES = { - OWNER: 'owner', - ADMIN: 'admin', - MEMBER: 'member', - EXADMIN: 'exadmin', - EXOWNER: 'exowner', - EXOUTCAST: 'exoutcast', - EXMEMBER: 'exmember' -}; -core_converse.AFFILIATION_CHANGES_LIST = Object.values(core_converse.AFFILIATION_CHANGES); -core_converse.MUC_TRAFFIC_STATES = { - ENTERED: 'entered', - EXITED: 'exited' -}; -core_converse.MUC_TRAFFIC_STATES_LIST = Object.values(core_converse.MUC_TRAFFIC_STATES); -core_converse.MUC_ROLE_CHANGES = { - OP: 'op', - DEOP: 'deop', - VOICE: 'voice', - MUTE: 'mute' -}; -core_converse.MUC_ROLE_CHANGES_LIST = Object.values(core_converse.MUC_ROLE_CHANGES); -core_converse.MUC = {}; -core_converse.MUC.INFO_CODES = { - 'visibility_changes': ['100', '102', '103', '172', '173', '174'], - 'self': ['110'], - 'non_privacy_changes': ['104', '201'], - 'muc_logging_changes': ['170', '171'], - 'nickname_changes': ['210', '303'], - 'disconnected': ['301', '307', '321', '322', '332', '333'], - 'affiliation_changes': [...core_converse.AFFILIATION_CHANGES_LIST], - 'join_leave_events': [...core_converse.MUC_TRAFFIC_STATES_LIST], - 'role_changes': [...core_converse.MUC_ROLE_CHANGES_LIST] -}; -const { - Strophe: muc_Strophe -} = core_converse.env; // Add Strophe Namespaces - -muc_Strophe.addNamespace('MUC_ADMIN', muc_Strophe.NS.MUC + '#admin'); -muc_Strophe.addNamespace('MUC_OWNER', muc_Strophe.NS.MUC + '#owner'); -muc_Strophe.addNamespace('MUC_REGISTER', 'jabber:iq:register'); -muc_Strophe.addNamespace('MUC_ROOMCONF', muc_Strophe.NS.MUC + '#roomconfig'); -muc_Strophe.addNamespace('MUC_USER', muc_Strophe.NS.MUC + '#user'); -muc_Strophe.addNamespace('MUC_HATS', 'xmpp:prosody.im/protocol/hats:1'); -muc_Strophe.addNamespace('CONFINFO', 'urn:ietf:params:xml:ns:conference-info'); -core_converse.MUC_NICK_CHANGED_CODE = '303'; -core_converse.ROOM_FEATURES = ['passwordprotected', 'unsecured', 'hidden', 'publicroom', 'membersonly', 'open', 'persistent', 'temporary', 'nonanonymous', 'semianonymous', 'moderated', 'unmoderated', 'mam_enabled']; // No longer used in code, but useful as reference. -// -// const ROOM_FEATURES_MAP = { -// 'passwordprotected': 'unsecured', -// 'unsecured': 'passwordprotected', -// 'hidden': 'publicroom', -// 'publicroom': 'hidden', -// 'membersonly': 'open', -// 'open': 'membersonly', -// 'persistent': 'temporary', -// 'temporary': 'persistent', -// 'nonanonymous': 'semianonymous', -// 'semianonymous': 'nonanonymous', -// 'moderated': 'unmoderated', -// 'unmoderated': 'moderated' -// }; - -core_converse.ROOMSTATUS = { - CONNECTED: 0, - CONNECTING: 1, - NICKNAME_REQUIRED: 2, - PASSWORD_REQUIRED: 3, - DISCONNECTED: 4, - ENTERED: 5, - DESTROYED: 6, - BANNED: 7 -}; -core_converse.plugins.add('converse-muc', { - /* Optional dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. They are called "optional" because they might not be - * available, in which case any overrides applicable to them will be - * ignored. - * - * It's possible however to make optional dependencies non-optional. - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ['converse-chatboxes', 'converse-chat', 'converse-disco', 'converse-controlbox'], - overrides: { - ChatBoxes: { - model(attrs, options) { - const { - _converse - } = this.__super__; - - if (attrs && attrs.type == _converse.CHATROOMS_TYPE) { - return new _converse.ChatRoom(attrs, options); - } else { - return this.__super__.model.apply(this, arguments); - } - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - const { - __, - ___ - } = shared_converse; // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - - api.settings.extend({ - 'allow_muc': true, - 'allow_muc_invitations': true, - 'auto_join_on_invite': false, - 'auto_join_rooms': [], - 'auto_register_muc_nickname': false, - 'hide_muc_participants': false, - 'locked_muc_domain': false, - 'modtools_disable_assign': false, - 'muc_clear_messages_on_leave': true, - 'muc_domain': undefined, - 'muc_fetch_members': true, - 'muc_history_max_stanzas': undefined, - 'muc_instant_rooms': true, - 'muc_nickname_from_jid': false, - 'muc_send_probes': false, - 'muc_show_info_messages': [...core_converse.MUC.INFO_CODES.visibility_changes, ...core_converse.MUC.INFO_CODES.self, ...core_converse.MUC.INFO_CODES.non_privacy_changes, ...core_converse.MUC.INFO_CODES.muc_logging_changes, ...core_converse.MUC.INFO_CODES.nickname_changes, ...core_converse.MUC.INFO_CODES.disconnected, ...core_converse.MUC.INFO_CODES.affiliation_changes, ...core_converse.MUC.INFO_CODES.join_leave_events, ...core_converse.MUC.INFO_CODES.role_changes], - 'muc_show_logs_before_join': false, - 'muc_show_ogp_unfurls': true, - 'muc_subscribe_to_rai': false - }); - api.promises.add(['roomsAutoJoined']); - - if (api.settings.get('locked_muc_domain') && typeof api.settings.get('muc_domain') !== 'string') { - throw new Error('Config Error: it makes no sense to set locked_muc_domain ' + 'to true when muc_domain is not set'); - } // This is for tests (at least until we can import modules inside tests) - - - core_converse.env.muc_utils = { - computeAffiliationsDelta: computeAffiliationsDelta - }; - Object.assign(api, muc_api); - Object.assign(api.rooms, affiliations_api); - /* https://xmpp.org/extensions/xep-0045.html - * ---------------------------------------- - * 100 message Entering a groupchat Inform user that any occupant is allowed to see the user's full JID - * 101 message (out of band) Affiliation change Inform user that his or her affiliation changed while not in the groupchat - * 102 message Configuration change Inform occupants that groupchat now shows unavailable members - * 103 message Configuration change Inform occupants that groupchat now does not show unavailable members - * 104 message Configuration change Inform occupants that a non-privacy-related groupchat configuration change has occurred - * 110 presence Any groupchat presence Inform user that presence refers to one of its own groupchat occupants - * 170 message or initial presence Configuration change Inform occupants that groupchat logging is now enabled - * 171 message Configuration change Inform occupants that groupchat logging is now disabled - * 172 message Configuration change Inform occupants that the groupchat is now non-anonymous - * 173 message Configuration change Inform occupants that the groupchat is now semi-anonymous - * 174 message Configuration change Inform occupants that the groupchat is now fully-anonymous - * 201 presence Entering a groupchat Inform user that a new groupchat has been created - * 210 presence Entering a groupchat Inform user that the service has assigned or modified the occupant's roomnick - * 301 presence Removal from groupchat Inform user that he or she has been banned from the groupchat - * 303 presence Exiting a groupchat Inform all occupants of new groupchat nickname - * 307 presence Removal from groupchat Inform user that he or she has been kicked from the groupchat - * 321 presence Removal from groupchat Inform user that he or she is being removed from the groupchat because of an affiliation change - * 322 presence Removal from groupchat Inform user that he or she is being removed from the groupchat because the groupchat has been changed to members-only and the user is not a member - * 332 presence Removal from groupchat Inform user that he or she is being removed from the groupchat because of a system shutdown - */ - - shared_converse.muc = { - info_messages: { - 100: __('This groupchat is not anonymous'), - 102: __('This groupchat now shows unavailable members'), - 103: __('This groupchat does not show unavailable members'), - 104: __('The groupchat configuration has changed'), - 170: __('Groupchat logging is now enabled'), - 171: __('Groupchat logging is now disabled'), - 172: __('This groupchat is now no longer anonymous'), - 173: __('This groupchat is now semi-anonymous'), - 174: __('This groupchat is now fully-anonymous'), - 201: __('A new groupchat has been created') - }, - new_nickname_messages: { - // XXX: Note the triple underscore function and not double underscore. - 210: ___('Your nickname has been automatically set to %1$s'), - 303: ___('Your nickname has been changed to %1$s') - }, - disconnect_messages: { - 301: __('You have been banned from this groupchat'), - 333: __('You have exited this groupchat due to a technical problem'), - 307: __('You have been kicked from this groupchat'), - 321: __('You have been removed from this groupchat because of an affiliation change'), - 322: __("You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"), - 332: __('You have been removed from this groupchat because the service hosting it is being shut down') - } - }; - - shared_converse.router.route('converse/room?jid=:jid', routeToRoom); - - shared_converse.ChatRoom = shared_converse.ChatBox.extend(muc); - shared_converse.ChatRoomMessage = shared_converse.Message.extend(muc_message); - shared_converse.ChatRoomOccupants = occupants; - shared_converse.ChatRoomOccupant = occupant; - /** - * Collection which stores MUC messages - * @class - * @namespace _converse.ChatRoomMessages - * @memberOf _converse - */ - - shared_converse.ChatRoomMessages = Collection.extend({ - model: shared_converse.ChatRoomMessage, - comparator: 'time' - }); - Object.assign(shared_converse, { - getDefaultMUCNickname: getDefaultMUCNickname, - isInfoVisible: isInfoVisible, - onDirectMUCInvitation: onDirectMUCInvitation - }); - /************************ BEGIN Event Handlers ************************/ - - if (api.settings.get('allow_muc_invitations')) { - api.listen.on('connected', registerDirectInvitationHandler); - api.listen.on('reconnected', registerDirectInvitationHandler); - } - - api.listen.on('addClientFeatures', () => api.disco.own.features.add(`${muc_Strophe.NS.CONFINFO}+notify`)); - api.listen.on('addClientFeatures', onAddClientFeatures); - api.listen.on('beforeResourceBinding', onBeforeResourceBinding); - api.listen.on('beforeTearDown', onBeforeTearDown); - api.listen.on('chatBoxesFetched', autoJoinRooms); - api.listen.on('disconnected', disconnectChatRooms); - api.listen.on('statusInitialized', onStatusInitialized); - api.listen.on('windowStateChanged', onWindowStateChanged); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/bookmarks/model.js - - -const { - Strophe: bookmarks_model_Strophe -} = core_converse.env; -const Bookmark = Model.extend({ - idAttribute: 'jid', - - getDisplayName() { - return bookmarks_model_Strophe.xmlunescape(this.get('name')); - } - -}); -/* harmony default export */ const bookmarks_model = (Bookmark); -// EXTERNAL MODULE: ./node_modules/jed/jed.js -var jed = __webpack_require__(6336); -var jed_default = /*#__PURE__*/__webpack_require__.n(jed); -;// CONCATENATED MODULE: ./src/i18n/index.js -/** - * @module i18n - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description This is the internationalization module - */ - - - -const { - dayjs -} = core_converse.env; - -function detectLocale(library_check) { - /* Determine which locale is supported by the user's system as well - * as by the relevant library (e.g. converse.js or dayjs). - * @param { Function } library_check - Returns a boolean indicating whether - * the locale is supported. - */ - let locale; - - if (window.navigator.userLanguage) { - locale = isLocaleAvailable(window.navigator.userLanguage, library_check); - } - - if (window.navigator.languages && !locale) { - for (let i = 0; i < window.navigator.languages.length && !locale; i++) { - locale = isLocaleAvailable(window.navigator.languages[i], library_check); - } - } - - if (window.navigator.browserLanguage && !locale) { - locale = isLocaleAvailable(window.navigator.browserLanguage, library_check); - } - - if (window.navigator.language && !locale) { - locale = isLocaleAvailable(window.navigator.language, library_check); - } - - if (window.navigator.systemLanguage && !locale) { - locale = isLocaleAvailable(window.navigator.systemLanguage, library_check); - } - - return locale || 'en'; -} - -function isConverseLocale(locale, supported_locales) { - return typeof locale === 'string' && supported_locales.includes(locale); -} - -function getLocale(preferred_locale, isSupportedByLibrary) { - if (typeof preferred_locale === 'string') { - if (preferred_locale === 'en' || isSupportedByLibrary(preferred_locale)) { - return preferred_locale; - } - } - - return detectLocale(isSupportedByLibrary) || 'en'; -} -/* Check whether the locale or sub locale (e.g. en-US, en) is supported. - * @param { String } locale - The locale to check for - * @param { Function } available - Returns a boolean indicating whether the locale is supported - */ - - -function isLocaleAvailable(locale, available) { - if (available(locale)) { - return locale; - } else { - var sublocale = locale.split("-")[0]; - - if (sublocale !== locale && available(sublocale)) { - return sublocale; - } - } -} -/* Fetch the translations for the given local at the given URL. - * @private - * @method i18n#fetchTranslations - * @param { _converse } - */ - - -async function fetchTranslations(_converse) { - const { - api, - locale - } = _converse; - const dayjs_locale = locale.toLowerCase().replace('_', '-'); - - if (!isConverseLocale(locale, api.settings.get("locales")) || locale === 'en') { - return; - } - - const { - default: data - } = await __webpack_require__(7521)(`./${locale}/LC_MESSAGES/converse.po`); - await __webpack_require__(9434)(`./${dayjs_locale}.js`); - dayjs.locale(getLocale(dayjs_locale, l => dayjs.locale(l))); - jed_instance = new (jed_default())(data); -} - -let jed_instance; -/** - * @namespace i18n - */ - -Object.assign(i18n, { - getLocale(preferred_locale, available_locales) { - return getLocale(preferred_locale, preferred => isConverseLocale(preferred, available_locales)); - }, - - translate(str) { - if (!jed_instance) { - return jed_default().sprintf.apply((jed_default()), arguments); - } - - const t = jed_instance.translate(str); - - if (arguments.length > 1) { - return t.fetch.apply(t, [].slice.call(arguments, 1)); - } else { - return t.fetch(); - } - }, - - async initialize() { - if (shared_converse.isTestEnv()) { - shared_converse.locale = 'en'; - } else { - try { - const preferred_locale = api.settings.get('i18n'); - shared_converse.locale = i18n.getLocale(preferred_locale, api.settings.get("locales")); - await fetchTranslations(shared_converse); - } catch (e) { - headless_log.fatal(e.message); - shared_converse.locale = 'en'; - } - } - }, - - __(...args) { - return i18n.translate(...args); - } - -}); -const __ = i18n.__; -;// CONCATENATED MODULE: ./src/headless/plugins/bookmarks/collection.js - - - - - - - -const { - Strophe: collection_Strophe, - $iq: collection_$iq, - sizzle: collection_sizzle -} = core_converse.env; -const Bookmarks = { - model: bookmarks_model, - comparator: item => item.get('name').toLowerCase(), - - initialize() { - this.on('add', bm => this.openBookmarkedRoom(bm).then(bm => this.markRoomAsBookmarked(bm)).catch(e => headless_log.fatal(e))); - this.on('remove', this.markRoomAsUnbookmarked, this); - this.on('remove', this.sendBookmarkStanza, this); - const cache_key = `converse.room-bookmarks${shared_converse.bare_jid}`; - this.fetched_flag = cache_key + 'fetched'; - initStorage(this, cache_key); - }, - - async openBookmarkedRoom(bookmark) { - if (api.settings.get('muc_respect_autojoin') && bookmark.get('autojoin')) { - const groupchat = await api.rooms.create(bookmark.get('jid'), { - 'nick': bookmark.get('nick') - }); - groupchat.maybeShow(); - } - - return bookmark; - }, - - fetchBookmarks() { - const deferred = getOpenPromise(); - - if (window.sessionStorage.getItem(this.fetched_flag)) { - this.fetch({ - 'success': () => deferred.resolve(), - 'error': () => deferred.resolve() - }); - } else { - this.fetchBookmarksFromServer(deferred); - } - - return deferred; - }, - - createBookmark(options) { - this.create(options); - this.sendBookmarkStanza().catch(iq => this.onBookmarkError(iq, options)); - }, - - sendBookmarkStanza() { - const stanza = collection_$iq({ - 'type': 'set', - 'from': shared_converse.connection.jid - }).c('pubsub', { - 'xmlns': collection_Strophe.NS.PUBSUB - }).c('publish', { - 'node': collection_Strophe.NS.BOOKMARKS - }).c('item', { - 'id': 'current' - }).c('storage', { - 'xmlns': collection_Strophe.NS.BOOKMARKS - }); - this.forEach(model => { - stanza.c('conference', { - 'name': model.get('name'), - 'autojoin': model.get('autojoin'), - 'jid': model.get('jid') - }).c('nick').t(model.get('nick')).up().up(); - }); - stanza.up().up().up(); - stanza.c('publish-options').c('x', { - 'xmlns': collection_Strophe.NS.XFORM, - 'type': 'submit' - }).c('field', { - 'var': 'FORM_TYPE', - 'type': 'hidden' - }).c('value').t('http://jabber.org/protocol/pubsub#publish-options').up().up().c('field', { - 'var': 'pubsub#persist_items' - }).c('value').t('true').up().up().c('field', { - 'var': 'pubsub#access_model' - }).c('value').t('whitelist'); - return api.sendIQ(stanza); - }, - - onBookmarkError(iq, options) { - headless_log.error("Error while trying to add bookmark"); - headless_log.error(iq); - api.alert('error', __('Error'), [__("Sorry, something went wrong while trying to save your bookmark.")]); - this.findWhere({ - 'jid': options.jid - }).destroy(); - }, - - fetchBookmarksFromServer(deferred) { - const stanza = collection_$iq({ - 'from': shared_converse.connection.jid, - 'type': 'get' - }).c('pubsub', { - 'xmlns': collection_Strophe.NS.PUBSUB - }).c('items', { - 'node': collection_Strophe.NS.BOOKMARKS - }); - api.sendIQ(stanza).then(iq => this.onBookmarksReceived(deferred, iq)).catch(iq => this.onBookmarksReceivedError(deferred, iq)); - }, - - markRoomAsBookmarked(bookmark) { - const groupchat = shared_converse.chatboxes.get(bookmark.get('jid')); - - if (groupchat !== undefined) { - groupchat.save('bookmarked', true); - } - }, - - markRoomAsUnbookmarked(bookmark) { - const groupchat = shared_converse.chatboxes.get(bookmark.get('jid')); - - if (groupchat !== undefined) { - groupchat.save('bookmarked', false); - } - }, - - createBookmarksFromStanza(stanza) { - const xmlns = collection_Strophe.NS.BOOKMARKS; - const sel = `items[node="${xmlns}"] item storage[xmlns="${xmlns}"] conference`; - collection_sizzle(sel, stanza).forEach(el => { - var _el$querySelector; - - const jid = el.getAttribute('jid'); - const bookmark = this.get(jid); - const attrs = { - 'jid': jid, - 'name': el.getAttribute('name') || jid, - 'autojoin': el.getAttribute('autojoin') === 'true', - 'nick': ((_el$querySelector = el.querySelector('nick')) === null || _el$querySelector === void 0 ? void 0 : _el$querySelector.textContent) || '' - }; - bookmark ? bookmark.save(attrs) : this.create(attrs); - }); - }, - - onBookmarksReceived(deferred, iq) { - this.createBookmarksFromStanza(iq); - window.sessionStorage.setItem(this.fetched_flag, true); - - if (deferred !== undefined) { - return deferred.resolve(); - } - }, - - onBookmarksReceivedError(deferred, iq) { - if (iq === null) { - headless_log.error('Error: timeout while fetching bookmarks'); - api.alert('error', __('Timeout Error'), [__("The server did not return your bookmarks within the allowed time. " + "You can reload the page to request them again.")]); - } else if (deferred) { - if (iq.querySelector('error[type="cancel"] item-not-found')) { - // Not an exception, the user simply doesn't have any bookmarks. - window.sessionStorage.setItem(this.fetched_flag, true); - return deferred.resolve(); - } else { - headless_log.error('Error while fetching bookmarks'); - headless_log.error(iq); - return deferred.reject(new Error("Could not fetch bookmarks")); - } - } else { - headless_log.error('Error while fetching bookmarks'); - headless_log.error(iq); - } - }, - - getUnopenedBookmarks() { - return this.filter(b => !shared_converse.chatboxes.get(b.get('jid'))); - } - -}; -/* harmony default export */ const collection = (Bookmarks); -;// CONCATENATED MODULE: ./src/headless/plugins/bookmarks/utils.js - -const { - Strophe: bookmarks_utils_Strophe -} = core_converse.env; -async function checkBookmarksSupport() { - const identity = await api.disco.getIdentity('pubsub', 'pep', shared_converse.bare_jid); - - if (shared_converse.allow_public_bookmarks) { - return !!identity; - } else { - return api.disco.supports(bookmarks_utils_Strophe.NS.PUBSUB + '#publish-options', shared_converse.bare_jid); - } -} -async function initBookmarks() { - if (!api.settings.get('allow_bookmarks')) { - return; - } - - if (await checkBookmarksSupport()) { - shared_converse.bookmarks = new shared_converse.Bookmarks(); - await shared_converse.bookmarks.fetchBookmarks(); - /** - * Triggered once the _converse.Bookmarks collection - * has been created and cached bookmarks have been fetched. - * @event _converse#bookmarksInitialized - * @example _converse.api.listen.on('bookmarksInitialized', () => { ... }); - */ - - api.trigger('bookmarksInitialized'); - } -} -/** - * Check if the user has a bookmark with a saved nickanme - * for this groupchat and return it. - */ - -function getNicknameFromBookmark(jid) { - if (!shared_converse.bookmarks || !api.settings.get('allow_bookmarks')) { - return null; - } - - const bookmark = shared_converse.bookmarks.findWhere({ - 'jid': jid - }); - - if (bookmark) { - return bookmark.get('nick'); - } -} -;// CONCATENATED MODULE: ./src/headless/plugins/bookmarks/index.js -/** - * @description - * Converse.js plugin which adds views for bookmarks specified in XEP-0048. - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - -const { - Strophe: bookmarks_Strophe, - sizzle: bookmarks_sizzle -} = core_converse.env; -bookmarks_Strophe.addNamespace('BOOKMARKS', 'storage:bookmarks'); - -function handleBookmarksPush(message) { - if (bookmarks_sizzle(`event[xmlns="${bookmarks_Strophe.NS.PUBSUB}#event"] items[node="${bookmarks_Strophe.NS.BOOKMARKS}"]`, message).length) { - api.waitUntil('bookmarksInitialized').then(() => shared_converse.bookmarks.createBookmarksFromStanza(message)).catch(e => headless_log.fatal(e)); - } - - return true; -} - -core_converse.plugins.add('converse-bookmarks', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ["converse-chatboxes", "converse-muc"], - overrides: { - // Overrides mentioned here will be picked up by converse.js's - // plugin architecture they will replace existing methods on the - // relevant objects or classes. - // - // New functions which don't exist yet can also be added. - ChatRoom: { - getDisplayName() { - const { - _converse - } = this.__super__; - - if (this.get('bookmarked') && _converse.bookmarks) { - const bookmark = _converse.bookmarks.findWhere({ - 'jid': this.get('jid') - }); - - if (bookmark) { - return bookmark.get('name'); - } - } - - return this.__super__.getDisplayName.apply(this, arguments); - }, - - getAndPersistNickname(nick) { - nick = nick || getNicknameFromBookmark(this.get('jid')); - return this.__super__.getAndPersistNickname.call(this, nick); - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - api.settings.extend({ - allow_bookmarks: true, - allow_public_bookmarks: false, - muc_respect_autojoin: true - }); - api.promises.add('bookmarksInitialized'); - shared_converse.Bookmark = bookmarks_model; - shared_converse.Bookmarks = Collection.extend(collection); - shared_converse.BookmarksList = Model.extend({ - defaults: { - "toggle-state": shared_converse.OPENED - } - }); - api.listen.on('addClientFeatures', () => { - if (api.settings.get('allow_bookmarks')) { - api.disco.own.features.add(bookmarks_Strophe.NS.BOOKMARKS + '+notify'); - } - }); - api.listen.on('clearSession', () => { - if (shared_converse.bookmarks !== undefined) { - shared_converse.bookmarks.clearStore({ - 'silent': true - }); - - window.sessionStorage.removeItem(shared_converse.bookmarks.fetched_flag); - delete shared_converse.bookmarks; - } - }); - api.listen.on('connected', async () => { - // Add a handler for bookmarks pushed from other connected clients - const { - connection - } = shared_converse; - connection.addHandler(handleBookmarksPush, null, 'message', 'headline', null, shared_converse.bare_jid); - await Promise.all([api.waitUntil('chatBoxesFetched')]); - initBookmarks(); - }); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/bosh.js -/** - * @module converse-bosh - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Converse.js plugin which add support for XEP-0206: XMPP Over BOSH - */ - - - - -const { - Strophe: bosh_Strophe -} = core_converse.env; -const BOSH_SESSION_ID = 'converse.bosh-session'; -core_converse.plugins.add('converse-bosh', { - enabled() { - return !shared_converse.api.settings.get("blacklisted_plugins").includes('converse-bosh'); - }, - - initialize() { - api.settings.extend({ - bosh_service_url: undefined, - prebind_url: null - }); - - async function initBOSHSession() { - const id = BOSH_SESSION_ID; - - if (!shared_converse.bosh_session) { - shared_converse.bosh_session = new Model({ - id - }); - shared_converse.bosh_session.browserStorage = shared_converse.createStore(id, "session"); - await new Promise(resolve => shared_converse.bosh_session.fetch({ - 'success': resolve, - 'error': resolve - })); - } - - if (shared_converse.jid) { - if (shared_converse.bosh_session.get('jid') !== shared_converse.jid) { - const jid = await shared_converse.setUserJID(shared_converse.jid); - - shared_converse.bosh_session.clear({ - 'silent': true - }); - - shared_converse.bosh_session.save({ - jid - }); - } - } else { - // Keepalive - const jid = shared_converse.bosh_session.get('jid'); - - jid && (await shared_converse.setUserJID(jid)); - } - - return shared_converse.bosh_session; - } - - shared_converse.startNewPreboundBOSHSession = function () { - if (!api.settings.get('prebind_url')) { - throw new Error("startNewPreboundBOSHSession: If you use prebind then you MUST supply a prebind_url"); - } - - const xhr = new XMLHttpRequest(); - xhr.open('GET', api.settings.get('prebind_url'), true); - xhr.setRequestHeader('Accept', 'application/json, text/javascript'); - - xhr.onload = async function () { - if (xhr.status >= 200 && xhr.status < 400) { - const data = JSON.parse(xhr.responseText); - const jid = await shared_converse.setUserJID(data.jid); - - shared_converse.connection.attach(jid, data.sid, data.rid, shared_converse.connection.onConnectStatusChanged); - } else { - xhr.onerror(); - } - }; - - xhr.onerror = function () { - delete shared_converse.connection; - /** - * Triggered when fetching prebind tokens failed - * @event _converse#noResumeableBOSHSession - * @type { _converse } - * @example _converse.api.listen.on('noResumeableBOSHSession', _converse => { ... }); - */ - - api.trigger('noResumeableBOSHSession', shared_converse); - }; - - xhr.send(); - }; - - shared_converse.restoreBOSHSession = async function () { - const jid = (await initBOSHSession()).get('jid'); - - if (jid && shared_converse.connection._proto instanceof bosh_Strophe.Bosh) { - try { - shared_converse.connection.restore(jid, shared_converse.connection.onConnectStatusChanged); - - return true; - } catch (e) { - !shared_converse.isTestEnv() && headless_log.warn("Could not restore session for jid: " + jid + " Error message: " + e.message); - return false; - } - } - - return false; - }; - /************************ BEGIN Event Handlers ************************/ - - - api.listen.on('clearSession', () => { - if (shared_converse.bosh_session === undefined) { - // Remove manually, even if we don't have the corresponding - // model, to avoid trying to reconnect to a stale BOSH session - const id = BOSH_SESSION_ID; - sessionStorage.removeItem(id); - sessionStorage.removeItem(`${id}-${id}`); - } else { - shared_converse.bosh_session.destroy(); - - delete shared_converse.bosh_session; - } - }); - api.listen.on('setUserJID', () => { - if (shared_converse.bosh_session !== undefined) { - shared_converse.bosh_session.save({ - 'jid': shared_converse.jid - }); - } - }); - api.listen.on('addClientFeatures', () => api.disco.own.features.add(bosh_Strophe.NS.BOSH)); - /************************ END Event Handlers ************************/ - - /************************ BEGIN API ************************/ - - Object.assign(api, { - /** - * This namespace lets you access the BOSH tokens - * - * @namespace api.tokens - * @memberOf api - */ - tokens: { - /** - * @method api.tokens.get - * @param {string} [id] The type of token to return ('rid' or 'sid'). - * @returns 'string' A token, either the RID or SID token depending on what's asked for. - * @example _converse.api.tokens.get('rid'); - */ - get(id) { - if (shared_converse.connection === undefined) { - return null; - } - - if (id.toLowerCase() === 'rid') { - return shared_converse.connection.rid || shared_converse.connection._proto.rid; - } else if (id.toLowerCase() === 'sid') { - return shared_converse.connection.sid || shared_converse.connection._proto.sid; - } - } - - } - }); - /************************ end api ************************/ - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/caps/utils.js - - -const { - Strophe: caps_utils_Strophe, - $build: utils_$build -} = core_converse.env; - -function propertySort(array, property) { - return array.sort((a, b) => { - return a[property] > b[property] ? -1 : 1; - }); -} - -function generateVerificationString() { - const identities = shared_converse.api.disco.own.identities.get(); - - const features = shared_converse.api.disco.own.features.get(); - - if (identities.length > 1) { - propertySort(identities, "category"); - propertySort(identities, "type"); - propertySort(identities, "lang"); - } - - let S = identities.reduce((result, id) => `${result}${id.category}/${id.type}/${(id === null || id === void 0 ? void 0 : id.lang) ?? ''}/${id.name}<`, ""); - features.sort(); - S = features.reduce((result, feature) => `${result}${feature}<`, S); - return SHA1.b64_sha1(S); -} - -function createCapsNode() { - return utils_$build("c", { - 'xmlns': caps_utils_Strophe.NS.CAPS, - 'hash': "sha-1", - 'node': "https://conversejs.org", - 'ver': generateVerificationString() - }).nodeTree; -} -;// CONCATENATED MODULE: ./src/headless/plugins/caps/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - -const { - Strophe: caps_Strophe -} = core_converse.env; -caps_Strophe.addNamespace('CAPS', "http://jabber.org/protocol/caps"); -core_converse.plugins.add('converse-caps', { - dependencies: ['converse-status'], - - initialize() { - api.listen.on('constructedPresence', (_, p) => p.root().cnode(createCapsNode()).up() && p); - api.listen.on('constructedMUCPresence', (_, p) => p.root().cnode(createCapsNode()).up() && p); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/carbons.js -/** - * @module converse-carbons - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Implements support for XEP-0280 Message Carbons - */ - - - -/* Ask the XMPP server to enable Message Carbons - * See XEP-0280 https://xmpp.org/extensions/xep-0280.html#enabling - */ - -function enableCarbons(reconnecting) { - var _converse$session2; - - if (reconnecting) { - var _converse$session; - - (_converse$session = shared_converse.session) === null || _converse$session === void 0 ? void 0 : _converse$session.set({ - 'carbons_enabled': false - }); - } - - if (!api.settings.get("message_carbons") || (_converse$session2 = shared_converse.session) !== null && _converse$session2 !== void 0 && _converse$session2.get('carbons_enabled')) { - return; - } - - const carbons_iq = new Strophe.Builder('iq', { - 'from': shared_converse.connection.jid, - 'id': 'enablecarbons', - 'type': 'set' - }).c('enable', { - xmlns: Strophe.NS.CARBONS - }); - - shared_converse.connection.addHandler(iq => { - if (iq.querySelectorAll('error').length > 0) { - headless_log.warn('An error occurred while trying to enable message carbons.'); - } else { - shared_converse.session.set({ - 'carbons_enabled': true - }); - - headless_log.debug('Message carbons have been enabled.'); - } - - shared_converse.session.save(); // Gather multiple sets into one save - - }, null, "iq", null, "enablecarbons"); - - shared_converse.connection.send(carbons_iq); -} - -core_converse.plugins.add('converse-carbons', { - initialize() { - api.settings.extend({ - message_carbons: true - }); - api.listen.on('afterResourceBinding', enableCarbons); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/chatboxes/chatboxes.js - - - -const ChatBoxes = Collection.extend({ - comparator: 'time_opened', - - model(attrs, options) { - return new shared_converse.ChatBox(attrs, options); - }, - - onChatBoxesFetched(collection) { - collection.filter(c => !c.isValid()).forEach(c => c.destroy()); - /** - * Triggered once all chat boxes have been recreated from the browser cache - * @event _converse#chatBoxesFetched - * @type { object } - * @property { _converse.ChatBox | _converse.ChatRoom } chatbox - * @property { XMLElement } stanza - * @example _converse.api.listen.on('chatBoxesFetched', obj => { ... }); - * @example _converse.api.waitUntil('chatBoxesFetched').then(() => { ... }); - */ - - api.trigger('chatBoxesFetched'); - }, - - onConnected(reconnecting) { - if (reconnecting) { - return; - } - - initStorage(this, `converse.chatboxes-${shared_converse.bare_jid}`); - this.fetch({ - 'add': true, - 'success': c => this.onChatBoxesFetched(c) - }); - } - -}); -/* harmony default export */ const chatboxes = (ChatBoxes); -;// CONCATENATED MODULE: ./src/headless/plugins/chatboxes/utils.js - - -const { - Strophe: chatboxes_utils_Strophe -} = core_converse.env; -async function createChatBox(jid, attrs, Model) { - jid = chatboxes_utils_Strophe.getBareJidFromJid(jid.toLowerCase()); - Object.assign(attrs, { - 'jid': jid, - 'id': jid - }); - let chatbox; - - try { - chatbox = new Model(attrs, { - 'collection': shared_converse.chatboxes - }); - } catch (e) { - headless_log.error(e); - return null; - } - - await chatbox.initialized; - - if (!chatbox.isValid()) { - chatbox.destroy(); - return null; - } - - shared_converse.chatboxes.add(chatbox); - - return chatbox; -} -;// CONCATENATED MODULE: ./src/headless/plugins/chatboxes/api.js - - -/** - * The "chatboxes" namespace. - * - * @namespace api.chatboxes - * @memberOf api - */ - -/* harmony default export */ const chatboxes_api = ({ - /** - * @method api.chats.create - * @param { String|String[] } jids - A JID or array of JIDs - * @param { Object } [attrs] An object containing configuration attributes - * @param { Model } model - The type of chatbox that should be created - */ - async create(jids = [], attrs = {}, model) { - await api.waitUntil('chatBoxesFetched'); - - if (typeof jids === 'string') { - return createChatBox(jids, attrs, model); - } else { - return Promise.all(jids.map(jid => createChatBox(jid, attrs, model))); - } - }, - - /** - * @method api.chats.get - * @param { String|String[] } jids - A JID or array of JIDs - */ - async get(jids) { - await api.waitUntil('chatBoxesFetched'); - - if (jids === undefined) { - return shared_converse.chatboxes.models; - } else if (typeof jids === 'string') { - return shared_converse.chatboxes.get(jids.toLowerCase()); - } else { - jids = jids.map(j => j.toLowerCase()); - return shared_converse.chatboxes.models.filter(m => jids.includes(m.get('jid'))); - } - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/chatboxes/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - -const { - Strophe: chatboxes_Strophe -} = core_converse.env; -core_converse.plugins.add('converse-chatboxes', { - dependencies: ["converse-emoji", "converse-roster", "converse-vcard"], - - initialize() { - api.promises.add(['chatBoxesFetched', 'chatBoxesInitialized', 'privateChatsAutoJoined']); - Object.assign(api, { - 'chatboxes': chatboxes_api - }); - shared_converse.ChatBoxes = chatboxes; - api.listen.on('addClientFeatures', () => { - api.disco.own.features.add(chatboxes_Strophe.NS.MESSAGE_CORRECT); - api.disco.own.features.add(chatboxes_Strophe.NS.HTTPUPLOAD); - api.disco.own.features.add(chatboxes_Strophe.NS.OUTOFBAND); - }); - api.listen.on('pluginsInitialized', () => { - shared_converse.chatboxes = new shared_converse.ChatBoxes(); - /** - * Triggered once the _converse.ChatBoxes collection has been initialized. - * @event _converse#chatBoxesInitialized - * @example _converse.api.listen.on('chatBoxesInitialized', () => { ... }); - * @example _converse.api.waitUntil('chatBoxesInitialized').then(() => { ... }); - */ - - api.trigger('chatBoxesInitialized'); - }); - api.listen.on('presencesInitialized', reconnecting => shared_converse.chatboxes.onConnected(reconnecting)); - api.listen.on('reconnected', () => shared_converse.chatboxes.forEach(m => m.onReconnection())); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/headlines.js -/** - * @module converse-headlines - * @copyright 2020, the Converse.js contributors - * @description XEP-0045 Multi-User Chat Views - */ - - - -core_converse.plugins.add('converse-headlines', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ["converse-chat"], - overrides: { - // Overrides mentioned here will be picked up by converse.js's - // plugin architecture they will replace existing methods on the - // relevant objects or classes. - // - // New functions which don't exist yet can also be added. - ChatBoxes: { - model(attrs, options) { - const { - _converse - } = this.__super__; - - if (attrs.type == _converse.HEADLINES_TYPE) { - return new _converse.HeadlinesBox(attrs, options); - } else { - return this.__super__.model.apply(this, arguments); - } - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - - /** - * Shows headline messages - * @class - * @namespace _converse.HeadlinesBox - * @memberOf _converse - */ - shared_converse.HeadlinesBox = shared_converse.ChatBox.extend({ - defaults() { - return { - 'bookmarked': false, - 'hidden': ['mobile', 'fullscreen'].includes(api.settings.get("view_mode")), - 'message_type': 'headline', - 'num_unread': 0, - 'time_opened': this.get('time_opened') || new Date().getTime(), - 'type': shared_converse.HEADLINES_TYPE - }; - }, - - async initialize() { - this.set({ - 'box_id': `box-${this.get('jid')}` - }); - this.initUI(); - this.initMessages(); - await this.fetchMessages(); - /** - * Triggered once a {@link _converse.HeadlinesBox} has been created and initialized. - * @event _converse#headlinesBoxInitialized - * @type { _converse.HeadlinesBox } - * @example _converse.api.listen.on('headlinesBoxInitialized', model => { ... }); - */ - - api.trigger('headlinesBoxInitialized', this); - } - - }); - - async function onHeadlineMessage(stanza) { - // Handler method for all incoming messages of type "headline". - if (isHeadline(stanza) || isServerMessage(stanza)) { - const from_jid = stanza.getAttribute('from'); - await api.waitUntil('rosterInitialized'); - - if (from_jid.includes('@') && !shared_converse.roster.get(from_jid) && !api.settings.get("allow_non_roster_messaging")) { - return; - } - - if (stanza.querySelector('body') === null) { - // Avoid creating a chat box if we have nothing to show inside it. - return; - } - - const chatbox = shared_converse.chatboxes.create({ - 'id': from_jid, - 'jid': from_jid, - 'type': shared_converse.HEADLINES_TYPE, - 'from': from_jid - }); - - const attrs = await parseMessage(stanza, shared_converse); - await chatbox.createMessage(attrs); - api.trigger('message', { - chatbox, - stanza, - attrs - }); - } - } - /************************ BEGIN Event Handlers ************************/ - - - function registerHeadlineHandler() { - shared_converse.connection.addHandler(message => onHeadlineMessage(message) || true, null, 'message'); - } - - api.listen.on('connected', registerHeadlineHandler); - api.listen.on('reconnected', registerHeadlineHandler); - /************************ END Event Handlers ************************/ - - /************************ BEGIN API ************************/ - - Object.assign(api, { - /** - * The "headlines" namespace, which is used for headline-channels - * which are read-only channels containing messages of type - * "headline". - * - * @namespace api.headlines - * @memberOf api - */ - headlines: { - /** - * Retrieves a headline-channel or all headline-channels. - * - * @method api.headlines.get - * @param {String|String[]} jids - e.g. 'buddy@example.com' or ['buddy1@example.com', 'buddy2@example.com'] - * @param {Object} [attrs] - Attributes to be set on the _converse.ChatBox model. - * @param {Boolean} [create=false] - Whether the chat should be created if it's not found. - * @returns { Promise<_converse.HeadlinesBox> } - */ - async get(jids, attrs = {}, create = false) { - async function _get(jid) { - let model = await api.chatboxes.get(jid); - - if (!model && create) { - model = await api.chatboxes.create(jid, attrs, shared_converse.HeadlinesBox); - } else { - model = model && model.get('type') === shared_converse.HEADLINES_TYPE ? model : null; - - if (model && Object.keys(attrs).length) { - model.save(attrs); - } - } - - return model; - } - - if (jids === undefined) { - const chats = await api.chatboxes.get(); - return chats.filter(c => c.get('type') === shared_converse.HEADLINES_TYPE); - } else if (typeof jids === 'string') { - return _get(jids); - } - - return Promise.all(jids.map(jid => _get(jid))); - } - - } - }); - /************************ END API ************************/ - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/mam/placeholder.js - - -const placeholder_u = core_converse.env.utils; -class MAMPlaceholderMessage extends Model { - defaults() { - // eslint-disable-line class-methods-use-this - return { - 'msgid': placeholder_u.getUniqueId(), - 'is_ephemeral': false - }; - } - -} -;// CONCATENATED MODULE: ./src/headless/shared/rsm.js -/** - * @module converse-rsm - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description XEP-0059 Result Set Management - * Some code taken from the Strophe RSM plugin, licensed under the MIT License - * Copyright 2006-2017 Strophe (https://github.com/strophe/strophejs) - */ - - -const { - Strophe: rsm_Strophe, - $build: rsm_$build -} = core_converse.env; -rsm_Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm'); -/** - * @typedef { Object } RSMQueryParameters - * [XEP-0059 RSM](https://xmpp.org/extensions/xep-0059.html) Attributes that can be used to filter query results - * @property { String } [after] - The XEP-0359 stanza ID of a message after which messages should be returned. Implies forward paging. - * @property { String } [before] - The XEP-0359 stanza ID of a message before which messages should be returned. Implies backward paging. - * @property { Integer } [index=0] - The index of the results page to return. - * @property { Integer } [max] - The maximum number of items to return. - */ - -const RSM_QUERY_PARAMETERS = ['after', 'before', 'index', 'max']; - -const rsm_toNumber = v => Number(v); - -const rsm_toString = v => v.toString(); - -const RSM_TYPES = { - 'after': rsm_toString, - 'before': rsm_toString, - 'count': rsm_toNumber, - 'first': rsm_toString, - 'index': rsm_toNumber, - 'last': rsm_toString, - 'max': rsm_toNumber -}; - -const isUndefined = x => typeof x === 'undefined'; // This array contains both query attributes and response attributes - - -const RSM_ATTRIBUTES = Object.keys(RSM_TYPES); -/** - * Instances of this class are used to page through query results according to XEP-0059 Result Set Management - * @class RSM - */ - -class RSM { - static getQueryParameters(options = {}) { - return lodash_es_pick(options, RSM_QUERY_PARAMETERS); - } - - static parseXMLResult(set) { - const result = {}; - - for (var i = 0; i < RSM_ATTRIBUTES.length; i++) { - const attr = RSM_ATTRIBUTES[i]; - const elem = set.getElementsByTagName(attr)[0]; - - if (!isUndefined(elem) && elem !== null) { - result[attr] = RSM_TYPES[attr](rsm_Strophe.getText(elem)); - - if (attr == 'first') { - result.index = RSM_TYPES['index'](elem.getAttribute('index')); - } - } - } - - return result; - } - /** - * Create a new RSM instance - * @param { Object } options - Configuration options - * @constructor - */ - - - constructor(options = {}) { - this.query = RSM.getQueryParameters(options); - this.result = options.xml ? RSM.parseXMLResult(options.xml) : {}; - } - /** - * Returns a `` XML element that confirms to XEP-0059 Result Set Management. - * The element is constructed based on the { @link module:converse-rsm~RSMQueryParameters } - * that are set on this RSM instance. - * @returns { XMLElement } - */ - - - toXML() { - const xml = rsm_$build('set', { - xmlns: rsm_Strophe.NS.RSM - }); - - const reducer = (xml, a) => !isUndefined(this.query[a]) ? xml.c(a).t((this.query[a] || '').toString()).up() : xml; - - return RSM_QUERY_PARAMETERS.reduce(reducer, xml).tree(); - } - - next(max, before) { - const options = Object.assign({}, this.query, { - after: this.result.last, - before, - max - }); - return new RSM(options); - } - - previous(max, after) { - const options = Object.assign({}, this.query, { - after, - before: this.result.first, - max - }); - return new RSM(options); - } - -} -shared_converse.RSM_ATTRIBUTES = RSM_ATTRIBUTES; -shared_converse.RSM = RSM; -;// CONCATENATED MODULE: ./src/headless/plugins/mam/api.js - - - - -const { - Strophe: mam_api_Strophe, - $iq: mam_api_$iq, - dayjs: api_dayjs -} = core_converse.env; -const { - NS: api_NS -} = mam_api_Strophe; -const api_u = core_converse.env.utils; -/* harmony default export */ const mam_api = ({ - /** - * The [XEP-0313](https://xmpp.org/extensions/xep-0313.html) Message Archive Management API - * - * Enables you to query an XMPP server for archived messages. - * - * See also the [message-archiving](/docs/html/configuration.html#message-archiving) - * option in the configuration settings section, which you'll - * usually want to use in conjunction with this API. - * - * @namespace _converse.api.archive - * @memberOf _converse.api - */ - archive: { - /** - * @typedef { module:converse-rsm~RSMQueryParameters } MAMFilterParameters - * Filter parameters which can be used to filter a MAM XEP-0313 archive - * @property { String } [end] - A date string in ISO-8601 format, before which messages should be returned. Implies backward paging. - * @property { String } [start] - A date string in ISO-8601 format, after which messages should be returned. Implies forward paging. - * @property { String } [with] - A JID against which to match messages, according to either their `to` or `from` attributes. - * An item in a MUC archive matches if the publisher of the item matches the JID. - * If `with` is omitted, all messages that match the rest of the query will be returned, regardless of to/from - * addresses of each message. - */ - - /** - * The options that can be passed in to the { @link _converse.api.archive.query } method - * @typedef { module:converse-mam~MAMFilterParameters } ArchiveQueryOptions - * @property { Boolean } [groupchat=false] - Whether the MAM archive is for a groupchat. - */ - - /** - * Query for archived messages. - * - * The options parameter can also be an instance of - * RSM to enable easy querying between results pages. - * - * @method _converse.api.archive.query - * @param { module:converse-mam~ArchiveQueryOptions } options - An object containing query parameters - * @throws {Error} An error is thrown if the XMPP server responds with an error. - * @returns { Promise } A promise which resolves - * to a { @link module:converse-mam~MAMQueryResult } object. - * - * @example - * // Requesting all archived messages - * // ================================ - * // - * // The simplest query that can be made is to simply not pass in any parameters. - * // Such a query will return all archived messages for the current user. - * - * let result; - * try { - * result = await api.archive.query(); - * } catch (e) { - * // The query was not successful, perhaps inform the user? - * // The IQ stanza returned by the XMPP server is passed in, so that you - * // may inspect it and determine what the problem was. - * } - * // Do something with the messages, like showing them in your webpage. - * result.messages.forEach(m => this.showMessage(m)); - * - * @example - * // Requesting all archived messages for a particular contact or room - * // ================================================================= - * // - * // To query for messages sent between the current user and another user or room, - * // the query options need to contain the the JID (Jabber ID) of the user or - * // room under the `with` key. - * - * // For a particular user - * let result; - * try { - * result = await api.archive.query({'with': 'john@doe.net'}); - * } catch (e) { - * // The query was not successful - * } - * - * // For a particular room - * let result; - * try { - * result = await api.archive.query({'with': 'discuss@conference.doglovers.net', 'groupchat': true}); - * } catch (e) { - * // The query was not successful - * } - * - * @example - * // Requesting all archived messages before or after a certain date - * // =============================================================== - * // - * // The `start` and `end` parameters are used to query for messages - * // within a certain timeframe. The passed in date values may either be ISO8601 - * // formatted date strings, or JavaScript Date objects. - * - * const options = { - * 'with': 'john@doe.net', - * 'start': '2010-06-07T00:00:00Z', - * 'end': '2010-07-07T13:23:54Z' - * }; - * let result; - * try { - * result = await api.archive.query(options); - * } catch (e) { - * // The query was not successful - * } - * - * @example - * // Limiting the amount of messages returned - * // ======================================== - * // - * // The amount of returned messages may be limited with the `max` parameter. - * // By default, the messages are returned from oldest to newest. - * - * // Return maximum 10 archived messages - * let result; - * try { - * result = await api.archive.query({'with': 'john@doe.net', 'max':10}); - * } catch (e) { - * // The query was not successful - * } - * - * @example - * // Paging forwards through a set of archived messages - * // ================================================== - * // - * // When limiting the amount of messages returned per query, you might want to - * // repeatedly make a further query to fetch the next batch of messages. - * // - * // To simplify this usecase for you, the callback method receives not only an array - * // with the returned archived messages, but also a special RSM (*Result Set Management*) - * // object which contains the query parameters you passed in, as well - * // as two utility methods `next`, and `previous`. - * // - * // When you call one of these utility methods on the returned RSM object, and then - * // pass the result into a new query, you'll receive the next or previous batch of - * // archived messages. Please note, when calling these methods, pass in an integer - * // to limit your results. - * - * const options = {'with': 'john@doe.net', 'max':10}; - * let result; - * try { - * result = await api.archive.query(options); - * } catch (e) { - * // The query was not successful - * } - * // Do something with the messages, like showing them in your webpage. - * result.messages.forEach(m => this.showMessage(m)); - * - * while (!result.complete) { - * try { - * result = await api.archive.query(Object.assign(options, rsm.next(10).query)); - * } catch (e) { - * // The query was not successful - * } - * // Do something with the messages, like showing them in your webpage. - * result.messages.forEach(m => this.showMessage(m)); - * } - * - * @example - * // Paging backwards through a set of archived messages - * // =================================================== - * // - * // To page backwards through the archive, you need to know the UID of the message - * // which you'd like to page backwards from and then pass that as value for the - * // `before` parameter. If you simply want to page backwards from the most recent - * // message, pass in the `before` parameter with an empty string value `''`. - * - * let result; - * const options = {'before': '', 'max':5}; - * try { - * result = await api.archive.query(options); - * } catch (e) { - * // The query was not successful - * } - * // Do something with the messages, like showing them in your webpage. - * result.messages.forEach(m => this.showMessage(m)); - * - * // Now we query again, to get the previous batch. - * try { - * result = await api.archive.query(Object.assign(options, rsm.previous(5).query)); - * } catch (e) { - * // The query was not successful - * } - * // Do something with the messages, like showing them in your webpage. - * result.messages.forEach(m => this.showMessage(m)); - * - */ - async query(options) { - if (!api.connection.connected()) { - throw new Error('Can\'t call `api.archive.query` before having established an XMPP session'); - } - - const attrs = { - 'type': 'set' - }; - - if (options && options.groupchat) { - if (!options['with']) { - throw new Error('You need to specify a "with" value containing ' + 'the chat room JID, when querying groupchat messages.'); - } - - attrs.to = options['with']; - } - - const jid = attrs.to || shared_converse.bare_jid; - const supported = await api.disco.supports(api_NS.MAM, jid); - - if (!supported) { - headless_log.warn(`Did not fetch MAM archive for ${jid} because it doesn't support ${api_NS.MAM}`); - return { - 'messages': [] - }; - } - - const queryid = api_u.getUniqueId(); - const stanza = mam_api_$iq(attrs).c('query', { - 'xmlns': api_NS.MAM, - 'queryid': queryid - }); - - if (options) { - stanza.c('x', { - 'xmlns': api_NS.XFORM, - 'type': 'submit' - }).c('field', { - 'var': 'FORM_TYPE', - 'type': 'hidden' - }).c('value').t(api_NS.MAM).up().up(); - - if (options['with'] && !options.groupchat) { - stanza.c('field', { - 'var': 'with' - }).c('value').t(options['with']).up().up(); - } - - ['start', 'end'].forEach(t => { - if (options[t]) { - const date = api_dayjs(options[t]); - - if (date.isValid()) { - stanza.c('field', { - 'var': t - }).c('value').t(date.toISOString()).up().up(); - } else { - throw new TypeError(`archive.query: invalid date provided for: ${t}`); - } - } - }); - stanza.up(); - const rsm = new RSM(options); - - if (Object.keys(rsm.query).length) { - stanza.cnode(rsm.toXML()); - } - } - - const messages = []; - - const message_handler = shared_converse.connection.addHandler(stanza => { - const result = sizzle_default()(`message > result[xmlns="${api_NS.MAM}"]`, stanza).pop(); - - if (result === undefined || result.getAttribute('queryid') !== queryid) { - return true; - } - - const from = stanza.getAttribute('from') || shared_converse.bare_jid; - - if (options.groupchat) { - if (from !== options['with']) { - headless_log.warn(`Ignoring alleged groupchat MAM message from ${stanza.getAttribute('from')}`); - return true; - } - } else if (from !== shared_converse.bare_jid) { - headless_log.warn(`Ignoring alleged MAM message from ${stanza.getAttribute('from')}`); - return true; - } - - messages.push(stanza); - return true; - }, api_NS.MAM); - - let error; - const iq_result = await api.sendIQ(stanza, api.settings.get('message_archiving_timeout'), false); - - if (iq_result === null) { - const { - __ - } = shared_converse; - - const err_msg = __("Timeout while trying to fetch archived messages."); - - headless_log.error(err_msg); - error = new shared_converse.TimeoutError(err_msg); - return { - messages, - error - }; - } else if (api_u.isErrorStanza(iq_result)) { - const { - __ - } = shared_converse; - - const err_msg = __('An error occurred while querying for archived messages.'); - - headless_log.error(err_msg); - headless_log.error(iq_result); - error = new Error(err_msg); - return { - messages, - error - }; - } - - shared_converse.connection.deleteHandler(message_handler); - - let rsm; - const fin = iq_result && sizzle_default()(`fin[xmlns="${api_NS.MAM}"]`, iq_result).pop(); - const complete = (fin === null || fin === void 0 ? void 0 : fin.getAttribute('complete')) === 'true'; - const set = sizzle_default()(`set[xmlns="${api_NS.RSM}"]`, fin).pop(); - - if (set) { - rsm = new RSM({ ...options, - 'xml': set - }); - } - /** - * @typedef { Object } MAMQueryResult - * @property { Array } messages - * @property { RSM } [rsm] - An instance of { @link RSM }. - * You can call `next()` or `previous()` on this instance, - * to get the RSM query parameters for the next or previous - * page in the result set. - * @property { Boolean } complete - * @property { Error } [error] - */ - - - return { - messages, - rsm, - complete - }; - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/mam/utils.js - - - - - - -const { - Strophe: mam_utils_Strophe, - $iq: mam_utils_$iq -} = core_converse.env; -const { - NS: utils_NS -} = mam_utils_Strophe; -const mam_utils_u = core_converse.env.utils; -function onMAMError(iq) { - if (iq !== null && iq !== void 0 && iq.querySelectorAll('feature-not-implemented').length) { - headless_log.warn(`Message Archive Management (XEP-0313) not supported by ${iq.getAttribute('from')}`); - } else { - headless_log.error(`Error while trying to set archiving preferences for ${iq.getAttribute('from')}.`); - headless_log.error(iq); - } -} -/** - * Handle returned IQ stanza containing Message Archive - * Management (XEP-0313) preferences. - * - * XXX: For now we only handle the global default preference. - * The XEP also provides for per-JID preferences, which is - * currently not supported in converse.js. - * - * Per JID preferences will be set in chat boxes, so it'll - * probbaly be handled elsewhere in any case. - */ - -function onMAMPreferences(iq, feature) { - const preference = sizzle_default()(`prefs[xmlns="${utils_NS.MAM}"]`, iq).pop(); - const default_pref = preference.getAttribute('default'); - - if (default_pref !== api.settings.get('message_archiving')) { - const stanza = mam_utils_$iq({ - 'type': 'set' - }).c('prefs', { - 'xmlns': utils_NS.MAM, - 'default': api.settings.get('message_archiving') - }); - Array.from(preference.children).forEach(child => stanza.cnode(child).up()); // XXX: Strictly speaking, the server should respond with the updated prefs - // (see example 18: https://xmpp.org/extensions/xep-0313.html#config) - // but Prosody doesn't do this, so we don't rely on it. - - api.sendIQ(stanza).then(() => feature.save({ - 'preferences': { - 'default': api.settings.get('message_archiving') - } - })).catch(shared_converse.onMAMError); - } else { - feature.save({ - 'preferences': { - 'default': api.settings.get('message_archiving') - } - }); - } -} -function getMAMPrefsFromFeature(feature) { - const prefs = feature.get('preferences') || {}; - - if (feature.get('var') !== utils_NS.MAM || api.settings.get('message_archiving') === undefined) { - return; - } - - if (prefs['default'] !== api.settings.get('message_archiving')) { - api.sendIQ(mam_utils_$iq({ - 'type': 'get' - }).c('prefs', { - 'xmlns': utils_NS.MAM - })).then(iq => shared_converse.onMAMPreferences(iq, feature)).catch(shared_converse.onMAMError); - } -} -function preMUCJoinMAMFetch(muc) { - if (!api.settings.get('muc_show_logs_before_join') || !muc.features.get('mam_enabled') || muc.get('prejoin_mam_fetched')) { - return; - } - - fetchNewestMessages(muc); - muc.save({ - 'prejoin_mam_fetched': true - }); -} -async function handleMAMResult(model, result, query, options, should_page) { - await api.emojis.initialize(); - - const is_muc = model.get('type') === shared_converse.CHATROOMS_TYPE; - - result.messages = result.messages.map(s => is_muc ? parseMUCMessage(s, model, shared_converse) : parseMessage(s, shared_converse)); - /** - * Synchronous event which allows listeners to first do some - * work based on the MAM result before calling the handlers here. - * @event _converse#MAMResult - */ - - const data = { - query, - 'chatbox': model, - 'messages': result.messages - }; - await api.trigger('MAMResult', data, { - 'synchronous': true - }); - result.messages.forEach(m => model.queueMessage(m)); - - if (result.error) { - const event_id = result.error.retry_event_id = mam_utils_u.getUniqueId(); - api.listen.once(event_id, () => fetchArchivedMessages(model, options, should_page)); - model.createMessageFromError(result.error); - } -} -/** - * @typedef { Object } MAMOptions - * A map of MAM related options that may be passed to fetchArchivedMessages - * @param { integer } [options.max] - The maximum number of items to return. - * Defaults to "archived_messages_page_size" - * @param { string } [options.after] - The XEP-0359 stanza ID of a message - * after which messages should be returned. Implies forward paging. - * @param { string } [options.before] - The XEP-0359 stanza ID of a message - * before which messages should be returned. Implies backward paging. - * @param { string } [options.end] - A date string in ISO-8601 format, - * before which messages should be returned. Implies backward paging. - * @param { string } [options.start] - A date string in ISO-8601 format, - * after which messages should be returned. Implies forward paging. - * @param { string } [options.with] - The JID of the entity with - * which messages were exchanged. - * @param { boolean } [options.groupchat] - True if archive in groupchat. - */ - -/** - * Fetch XEP-0313 archived messages based on the passed in criteria. - * @param { _converse.ChatBox | _converse.ChatRoom } model - * @param { MAMOptions } [options] - * @param { ('forwards'|'backwards'|null)} [should_page=null] - Determines whether - * this function should recursively page through the entire result set if a limited - * number of results were returned. - */ - -async function fetchArchivedMessages(model, options = {}, should_page = null) { - if (model.disable_mam) { - return; - } - - const is_muc = model.get('type') === shared_converse.CHATROOMS_TYPE; - - const mam_jid = is_muc ? model.get('jid') : shared_converse.bare_jid; - - if (!(await api.disco.supports(utils_NS.MAM, mam_jid))) { - return; - } - - const max = api.settings.get('archived_messages_page_size'); - const query = Object.assign({ - 'groupchat': is_muc, - 'max': max, - 'with': model.get('jid') - }, options); - const result = await api.archive.query(query); - await handleMAMResult(model, result, query, options, should_page); - - if (result.rsm && !result.complete) { - if (should_page) { - if (should_page === 'forwards') { - options = result.rsm.next(max, options.before).query; - } else if (should_page === 'backwards') { - options = result.rsm.previous(max, options.after).query; - } - - return fetchArchivedMessages(model, options, should_page); - } else { - createPlaceholder(model, options, result); - } - } -} -/** - * Create a placeholder message which is used to indicate gaps in the history. - * @param { _converse.ChatBox | _converse.ChatRoom } model - * @param { MAMOptions } options - * @param { object } result - The RSM result object - */ - -async function createPlaceholder(model, options, result) { - if (options.before == '' && (model.messages.length === 0 || !options.start)) { - // Fetching the latest MAM messages with an empty local cache - return; - } - - if (options.before && !options.start) { - // Infinite scrolling upward - return; - } - - if (options.before == null) { - // eslint-disable-line no-eq-null - // Adding placeholders when paging forwards is not supported yet, - // since currently with standard Converse, we only page forwards - // when fetching the entire history (i.e. no gaps should arise). - return; - } - - const msgs = await Promise.all(result.messages); - const { - rsm - } = result; - const key = `stanza_id ${model.get('jid')}`; - const adjacent_message = msgs.find(m => m[key] === rsm.result.first); - const msg_data = { - 'template_hook': 'getMessageTemplate', - 'time': new Date(new Date(adjacent_message['time']) - 1).toISOString(), - 'before': rsm.result.first, - 'start': options.start - }; - model.messages.add(new MAMPlaceholderMessage(msg_data)); -} -/** - * Fetches messages that might have been archived *after* - * the last archived message in our local cache. - * @param { _converse.ChatBox | _converse.ChatRoom } - */ - - -function fetchNewestMessages(model) { - if (model.disable_mam) { - return; - } - - const most_recent_msg = model.most_recent_cached_message; // if clear_messages_on_reconnection is true, than any recent messages - // must have been received *after* connection and we instead must query - // for earlier messages - - if (most_recent_msg && !api.settings.get('clear_messages_on_reconnection')) { - const should_page = api.settings.get('mam_request_all_pages'); - - if (should_page) { - const stanza_id = most_recent_msg.get(`stanza_id ${model.get('jid')}`); - - if (stanza_id) { - fetchArchivedMessages(model, { - 'after': stanza_id - }, 'forwards'); - } else { - fetchArchivedMessages(model, { - 'start': most_recent_msg.get('time') - }, 'forwards'); - } - } else { - fetchArchivedMessages(model, { - 'before': '', - 'start': most_recent_msg.get('time') - }); - } - } else { - fetchArchivedMessages(model, { - 'before': '' - }); - } -} -;// CONCATENATED MODULE: ./src/headless/plugins/mam/index.js -/** - * @description XEP-0313 Message Archive Management - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - -const { - Strophe: mam_Strophe -} = core_converse.env; -const { - NS: mam_NS -} = mam_Strophe; -core_converse.plugins.add('converse-mam', { - dependencies: ['converse-disco', 'converse-muc'], - - initialize() { - api.settings.extend({ - archived_messages_page_size: '50', - mam_request_all_pages: true, - message_archiving: undefined, - // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs) - message_archiving_timeout: 20000 // Time (in milliseconds) to wait before aborting MAM request - - }); - Object.assign(api, mam_api); // This is mainly done to aid with tests - - Object.assign(shared_converse, { - onMAMError: onMAMError, - onMAMPreferences: onMAMPreferences, - handleMAMResult: handleMAMResult, - MAMPlaceholderMessage: MAMPlaceholderMessage - }); - /************************ Event Handlers ************************/ - - api.listen.on('addClientFeatures', () => api.disco.own.features.add(mam_NS.MAM)); - api.listen.on('serviceDiscovered', getMAMPrefsFromFeature); - api.listen.on('chatRoomViewInitialized', view => { - if (api.settings.get('muc_show_logs_before_join')) { - preMUCJoinMAMFetch(view.model); // If we want to show MAM logs before entering the MUC, we need - // to be informed once it's clear that this MUC supports MAM. - - view.model.features.on('change:mam_enabled', () => preMUCJoinMAMFetch(view.model)); - } - }); - api.listen.on('enteredNewRoom', muc => muc.features.get('mam_enabled') && fetchNewestMessages(muc)); - api.listen.on('chatReconnected', chat => { - // XXX: For MUCs, we listen to enteredNewRoom instead - if (chat.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - fetchNewestMessages(chat); - } - }); - api.listen.on('afterMessagesFetched', chat => { - // XXX: We don't want to query MAM every time this is triggered - // since it's not necessary when the chat is restored from cache. - // (given that BOSH or SMACKS will ensure that you get messages - // sent during the reload). - // With MUCs we can listen for `enteredNewRoom`. - if (chat.get('type') === shared_converse.PRIVATE_CHAT_TYPE && !shared_converse.connection.restored) { - fetchNewestMessages(chat); - } - }); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/ping/utils.js - -const { - Strophe: ping_utils_Strophe, - $iq: ping_utils_$iq -} = core_converse.env; -let lastStanzaDate; -function utils_onWindowStateChanged(data) { - if (data.state === 'visible' && api.connection.connected()) { - api.ping(null, 5000); - } -} -function setLastStanzaDate(date) { - lastStanzaDate = date; -} - -function pong(ping) { - lastStanzaDate = new Date(); - const from = ping.getAttribute('from'); - const id = ping.getAttribute('id'); - const iq = ping_utils_$iq({ - type: 'result', - to: from, - id: id - }); - - shared_converse.connection.sendIQ(iq); - - return true; -} - -function registerPongHandler() { - if (shared_converse.connection.disco !== undefined) { - api.disco.own.features.add(ping_utils_Strophe.NS.PING); - } - - return shared_converse.connection.addHandler(pong, ping_utils_Strophe.NS.PING, "iq", "get"); -} -function registerPingHandler() { - shared_converse.connection.addHandler(() => { - if (api.settings.get('ping_interval') > 0) { - // Handler on each stanza, saves the received date - // in order to ping only when needed. - lastStanzaDate = new Date(); - return true; - } - }); -} -function onConnected() { - // Wrapper so that we can spy on registerPingHandler in tests - registerPongHandler(); - registerPingHandler(); -} -function onEverySecond() { - if (shared_converse.isTestEnv() || !api.connection.connected()) { - return; - } - - const ping_interval = api.settings.get('ping_interval'); - - if (ping_interval > 0) { - const now = new Date(); - - if (!lastStanzaDate) { - lastStanzaDate = now; - } - - if ((now - lastStanzaDate) / 1000 > ping_interval) { - api.ping(); - } - } -} -;// CONCATENATED MODULE: ./src/headless/plugins/ping/api.js - - - -const { - Strophe: ping_api_Strophe, - $iq: ping_api_$iq, - u: ping_api_u -} = core_converse.env; -/* harmony default export */ const ping_api = ({ - /** - * Pings the service represented by the passed in JID by sending an IQ stanza. - * @private - * @method api.ping - * @param { String } [jid] - The JID of the service to ping - * @param { Integer } [timeout] - The amount of time in - * milliseconds to wait for a response. The default is 10000; - */ - async ping(jid, timeout) { - // XXX: We could first check here if the server advertised that it supports PING. - // However, some servers don't advertise while still responding to pings - // - // const feature = _converse.disco_entities[_converse.domain].features.findWhere({'var': Strophe.NS.PING}); - setLastStanzaDate(new Date()); - jid = jid || ping_api_Strophe.getDomainFromJid(shared_converse.bare_jid); - - if (shared_converse.connection) { - const iq = ping_api_$iq({ - 'type': 'get', - 'to': jid, - 'id': ping_api_u.getUniqueId('ping') - }).c('ping', { - 'xmlns': ping_api_Strophe.NS.PING - }); - const result = await api.sendIQ(iq, timeout || 10000, false); - - if (result === null) { - headless_log.warn(`Timeout while pinging ${jid}`); - - if (jid === ping_api_Strophe.getDomainFromJid(shared_converse.bare_jid)) { - api.connection.reconnect(); - } - } else if (ping_api_u.isErrorStanza(result)) { - headless_log.error(`Error while pinging ${jid}`); - headless_log.error(result); - } - - return true; - } - - return false; - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/ping/index.js -/** - * @description - * Converse.js plugin which add support for application-level pings - * as specified in XEP-0199 XMPP Ping. - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - -const { - Strophe: ping_Strophe -} = core_converse.env; -ping_Strophe.addNamespace('PING', "urn:xmpp:ping"); -core_converse.plugins.add('converse-ping', { - initialize() { - api.settings.extend({ - ping_interval: 60 //in seconds - - }); - Object.assign(api, ping_api); - setInterval(onEverySecond, 1000); - api.listen.on('connected', onConnected); - api.listen.on('reconnected', onConnected); - api.listen.on('windowStateChanged', utils_onWindowStateChanged); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/pubsub.js -/** - * @module converse-pubsub - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - -const { - Strophe: pubsub_Strophe, - $iq: pubsub_$iq -} = core_converse.env; -pubsub_Strophe.addNamespace('PUBSUB_ERROR', pubsub_Strophe.NS.PUBSUB + "#errors"); -core_converse.plugins.add('converse-pubsub', { - dependencies: ["converse-disco"], - - initialize() { - /************************ BEGIN API ************************/ - // We extend the default converse.js API to add methods specific to MUC groupchats. - Object.assign(shared_converse.api, { - /** - * The "pubsub" namespace groups methods relevant to PubSub - * - * @namespace _converse.api.pubsub - * @memberOf _converse.api - */ - 'pubsub': { - /** - * Publshes an item to a PubSub node - * - * @method _converse.api.pubsub.publish - * @param {string} jid The JID of the pubsub service where the node resides. - * @param {string} node The node being published to - * @param {Strophe.Builder} item The Strophe.Builder representation of the XML element being published - * @param {object} options An object representing the publisher options - * (see https://xmpp.org/extensions/xep-0060.html#publisher-publish-options) - * @param {boolean} strict_options Indicates whether the publisher - * options are a strict requirement or not. If they're NOT - * strict, then Converse will publish to the node even if - * the publish options precondication cannot be met. - */ - async 'publish'(jid, node, item, options, strict_options = true) { - const stanza = pubsub_$iq({ - 'from': shared_converse.bare_jid, - 'type': 'set', - 'to': jid - }).c('pubsub', { - 'xmlns': pubsub_Strophe.NS.PUBSUB - }).c('publish', { - 'node': node - }).cnode(item.tree()).up().up(); - - if (options) { - jid = jid || shared_converse.bare_jid; - - if (await api.disco.supports(pubsub_Strophe.NS.PUBSUB + '#publish-options', jid)) { - stanza.c('publish-options').c('x', { - 'xmlns': pubsub_Strophe.NS.XFORM, - 'type': 'submit' - }).c('field', { - 'var': 'FORM_TYPE', - 'type': 'hidden' - }).c('value').t(`${pubsub_Strophe.NS.PUBSUB}#publish-options`).up().up(); - Object.keys(options).forEach(k => stanza.c('field', { - 'var': k - }).c('value').t(options[k]).up().up()); - } else { - headless_log.warn(`_converse.api.publish: ${jid} does not support #publish-options, ` + `so we didn't set them even though they were provided.`); - } - } - - try { - await api.sendIQ(stanza); - } catch (iq) { - if (iq instanceof Element && strict_options && iq.querySelector(`precondition-not-met[xmlns="${pubsub_Strophe.NS.PUBSUB_ERROR}"]`)) { - // The publish-options precondition couldn't be - // met. We re-publish but without publish-options. - const el = stanza.nodeTree; - el.querySelector('publish-options').outerHTML = ''; - headless_log.warn(`PubSub: Republishing without publish options. ${el.outerHTML}`); - await api.sendIQ(el); - } else { - throw iq; - } - } - } - - } - }); - /************************ END API ************************/ - } - -}); -;// CONCATENATED MODULE: ./node_modules/lodash-es/isNumber.js - - -/** `Object#toString` result references. */ - -var isNumber_numberTag = '[object Number]'; -/** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are - * classified as numbers, use the `_.isFinite` method. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. - * @example - * - * _.isNumber(3); - * // => true - * - * _.isNumber(Number.MIN_VALUE); - * // => true - * - * _.isNumber(Infinity); - * // => true - * - * _.isNumber('3'); - * // => false - */ - -function isNumber(value) { - return typeof value == 'number' || lodash_es_isObjectLike(value) && _baseGetTag(value) == isNumber_numberTag; -} - -/* harmony default export */ const lodash_es_isNumber = (isNumber); -;// CONCATENATED MODULE: ./node_modules/lodash-es/isNaN.js - -/** - * Checks if `value` is `NaN`. - * - * **Note:** This method is based on - * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as - * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for - * `undefined` and other non-number values. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`. - * @example - * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false - */ - -function isNaN_isNaN(value) { - // An `NaN` primitive is the only value that is not equal to itself. - // Perform the `toStringTag` check first to avoid errors with some - // ActiveX objects in IE. - return lodash_es_isNumber(value) && value != +value; -} - -/* harmony default export */ const lodash_es_isNaN = (isNaN_isNaN); -;// CONCATENATED MODULE: ./src/headless/plugins/status/status.js - - - - -const { - Strophe: status_Strophe, - $pres: status_$pres -} = core_converse.env; -const XMPPStatus = Model.extend({ - defaults() { - return { - "status": api.settings.get("default_state") - }; - }, - - initialize() { - this.on('change', item => { - if (!lodash_es_isObject(item.changed)) { - return; - } - - if ('status' in item.changed || 'status_message' in item.changed) { - api.user.presence.send(this.get('status'), null, this.get('status_message')); - } - }); - }, - - getNickname() { - return shared_converse.nickname; - }, - - getFullname() { - // Gets overridden in converse-vcard - return ''; - }, - - async constructPresence(type, to = null, status_message) { - type = typeof type === 'string' ? type : this.get('status') || api.settings.get("default_state"); - status_message = typeof status_message === 'string' ? status_message : this.get('status_message'); - let presence; - const attrs = { - to - }; - - if (type === 'unavailable' || type === 'probe' || type === 'error' || type === 'unsubscribe' || type === 'unsubscribed' || type === 'subscribe' || type === 'subscribed') { - attrs['type'] = type; - presence = status_$pres(attrs); - } else if (type === 'offline') { - attrs['type'] = 'unavailable'; - presence = status_$pres(attrs); - } else if (type === 'online') { - presence = status_$pres(attrs); - } else { - presence = status_$pres(attrs).c('show').t(type).up(); - } - - if (status_message) { - presence.c('status').t(status_message).up(); - } - - const priority = api.settings.get("priority"); - presence.c('priority').t(lodash_es_isNaN(Number(priority)) ? 0 : priority).up(); - - if (shared_converse.idle) { - const idle_since = new Date(); - idle_since.setSeconds(idle_since.getSeconds() - shared_converse.idle_seconds); - presence.c('idle', { - xmlns: status_Strophe.NS.IDLE, - since: idle_since.toISOString() - }); - } - - presence = await api.hook('constructedPresence', null, presence); - return presence; - } - -}); -/* harmony default export */ const status_status = (XMPPStatus); -;// CONCATENATED MODULE: ./src/headless/plugins/status/api.js - -/* harmony default export */ const status_api = ({ - /** - * @namespace _converse.api.user.presence - * @memberOf _converse.api.user - */ - presence: { - /** - * Send out a presence stanza - * @method _converse.api.user.presence.send - * @param { String } type - * @param { String } to - * @param { String } [status] - An optional status message - * @param { Element[]|Strophe.Builder[]|Element|Strophe.Builder } [child_nodes] - * Nodes(s) to be added as child nodes of the `presence` XML element. - */ - async send(type, to, status, child_nodes) { - await api.waitUntil('statusInitialized'); - const presence = await shared_converse.xmppstatus.constructPresence(type, to, status); - - if (child_nodes) { - if (!Array.isArray(child_nodes)) { - child_nodes = [child_nodes]; - } - - child_nodes.map(c => (c === null || c === void 0 ? void 0 : c.tree()) ?? c).forEach(c => presence.cnode(c).up()); - } - - api.send(presence); - } - - }, - - /** - * Set and get the user's chat status, also called their *availability*. - * @namespace _converse.api.user.status - * @memberOf _converse.api.user - */ - status: { - /** - * Return the current user's availability status. - * @async - * @method _converse.api.user.status.get - * @example _converse.api.user.status.get(); - */ - async get() { - await api.waitUntil('statusInitialized'); - return shared_converse.xmppstatus.get('status'); - }, - - /** - * The user's status can be set to one of the following values: - * - * @async - * @method _converse.api.user.status.set - * @param {string} value The user's chat status (e.g. 'away', 'dnd', 'offline', 'online', 'unavailable' or 'xa') - * @param {string} [message] A custom status message - * - * @example _converse.api.user.status.set('dnd'); - * @example _converse.api.user.status.set('dnd', 'In a meeting'); - */ - async set(value, message) { - const data = { - 'status': value - }; - - if (!Object.keys(shared_converse.STATUS_WEIGHTS).includes(value)) { - throw new Error('Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'); - } - - if (typeof message === 'string') { - data.status_message = message; - } - - await api.waitUntil('statusInitialized'); - - shared_converse.xmppstatus.save(data); - }, - - /** - * Set and retrieve the user's custom status message. - * - * @namespace _converse.api.user.status.message - * @memberOf _converse.api.user.status - */ - message: { - /** - * @async - * @method _converse.api.user.status.message.get - * @returns {string} The status message - * @example const message = _converse.api.user.status.message.get() - */ - async get() { - await api.waitUntil('statusInitialized'); - return shared_converse.xmppstatus.get('status_message'); - }, - - /** - * @async - * @method _converse.api.user.status.message.set - * @param {string} status The status message - * @example _converse.api.user.status.message.set('In a meeting'); - */ - async set(status) { - await api.waitUntil('statusInitialized'); - - shared_converse.xmppstatus.save({ - status_message: status - }); - } - - } - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/status/utils.js - - -const { - Strophe: status_utils_Strophe, - $build: status_utils_$build -} = core_converse.env; - -function utils_onStatusInitialized(reconnecting) { - /** - * Triggered when the user's own chat status has been initialized. - * @event _converse#statusInitialized - * @example _converse.api.listen.on('statusInitialized', status => { ... }); - * @example _converse.api.waitUntil('statusInitialized').then(() => { ... }); - */ - api.trigger('statusInitialized', reconnecting); -} - -function initStatus(reconnecting) { - // If there's no xmppstatus obj, then we were never connected to - // begin with, so we set reconnecting to false. - reconnecting = shared_converse.xmppstatus === undefined ? false : reconnecting; - - if (reconnecting) { - utils_onStatusInitialized(reconnecting); - } else { - const id = `converse.xmppstatus-${shared_converse.bare_jid}`; - shared_converse.xmppstatus = new shared_converse.XMPPStatus({ - id - }); - initStorage(shared_converse.xmppstatus, id, 'session'); - - shared_converse.xmppstatus.fetch({ - 'success': () => utils_onStatusInitialized(reconnecting), - 'error': () => utils_onStatusInitialized(reconnecting), - 'silent': true - }); - } -} -function onUserActivity() { - var _converse$connection; - - /* Resets counters and flags relating to CSI and auto_away/auto_xa */ - if (shared_converse.idle_seconds > 0) { - shared_converse.idle_seconds = 0; - } - - if (!((_converse$connection = shared_converse.connection) !== null && _converse$connection !== void 0 && _converse$connection.authenticated)) { - // We can't send out any stanzas when there's no authenticated connection. - // This can happen when the connection reconnects. - return; - } - - if (shared_converse.inactive) { - shared_converse.sendCSI(shared_converse.ACTIVE); - } - - if (shared_converse.idle) { - shared_converse.idle = false; - api.user.presence.send(); - } - - if (shared_converse.auto_changed_status === true) { - shared_converse.auto_changed_status = false; // XXX: we should really remember the original state here, and - // then set it back to that... - - shared_converse.xmppstatus.set('status', api.settings.get("default_state")); - } -} -function utils_onEverySecond() { - var _converse$connection2; - - /* An interval handler running every second. - * Used for CSI and the auto_away and auto_xa features. - */ - if (!((_converse$connection2 = shared_converse.connection) !== null && _converse$connection2 !== void 0 && _converse$connection2.authenticated)) { - // We can't send out any stanzas when there's no authenticated connection. - // This can happen when the connection reconnects. - return; - } - - const stat = shared_converse.xmppstatus.get('status'); - - shared_converse.idle_seconds++; - - if (api.settings.get("csi_waiting_time") > 0 && shared_converse.idle_seconds > api.settings.get("csi_waiting_time") && !shared_converse.inactive) { - shared_converse.sendCSI(shared_converse.INACTIVE); - } - - if (api.settings.get("idle_presence_timeout") > 0 && shared_converse.idle_seconds > api.settings.get("idle_presence_timeout") && !shared_converse.idle) { - shared_converse.idle = true; - api.user.presence.send(); - } - - if (api.settings.get("auto_away") > 0 && shared_converse.idle_seconds > api.settings.get("auto_away") && stat !== 'away' && stat !== 'xa' && stat !== 'dnd') { - shared_converse.auto_changed_status = true; - - shared_converse.xmppstatus.set('status', 'away'); - } else if (api.settings.get("auto_xa") > 0 && shared_converse.idle_seconds > api.settings.get("auto_xa") && stat !== 'xa' && stat !== 'dnd') { - shared_converse.auto_changed_status = true; - - shared_converse.xmppstatus.set('status', 'xa'); - } -} -/** - * Send out a Client State Indication (XEP-0352) - * @function sendCSI - * @param { String } stat - The user's chat status - */ - -function sendCSI(stat) { - api.send(status_utils_$build(stat, { - xmlns: status_utils_Strophe.NS.CSI - })); - shared_converse.inactive = stat === shared_converse.INACTIVE ? true : false; -} -function registerIntervalHandler() { - /* Set an interval of one second and register a handler for it. - * Required for the auto_away, auto_xa and csi_waiting_time features. - */ - if (api.settings.get("auto_away") < 1 && api.settings.get("auto_xa") < 1 && api.settings.get("csi_waiting_time") < 1 && api.settings.get("idle_presence_timeout") < 1) { - // Waiting time of less then one second means features aren't used. - return; - } - - shared_converse.idle_seconds = 0; - shared_converse.auto_changed_status = false; // Was the user's status changed by Converse? - - const { - unloadevent - } = shared_converse; - window.addEventListener('click', shared_converse.onUserActivity); - window.addEventListener('focus', shared_converse.onUserActivity); - window.addEventListener('keypress', shared_converse.onUserActivity); - window.addEventListener('mousemove', shared_converse.onUserActivity); - window.addEventListener(unloadevent, shared_converse.onUserActivity, { - 'once': true, - 'passive': true - }); - window.addEventListener(unloadevent, () => { - var _converse$session; - - return (_converse$session = shared_converse.session) === null || _converse$session === void 0 ? void 0 : _converse$session.save('active', false); - }); - shared_converse.everySecondTrigger = window.setInterval(shared_converse.onEverySecond, 1000); -} -;// CONCATENATED MODULE: ./src/headless/plugins/status/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - -core_converse.plugins.add('converse-status', { - initialize() { - api.settings.extend({ - auto_away: 0, - // Seconds after which user status is set to 'away' - auto_xa: 0, - // Seconds after which user status is set to 'xa' - csi_waiting_time: 0, - // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out. - default_state: 'online', - priority: 0 - }); - api.promises.add(['statusInitialized']); - shared_converse.XMPPStatus = status_status; - shared_converse.onUserActivity = onUserActivity; - shared_converse.onEverySecond = utils_onEverySecond; - shared_converse.sendCSI = sendCSI; - shared_converse.registerIntervalHandler = registerIntervalHandler; - Object.assign(shared_converse.api.user, status_api); - api.listen.on('presencesInitialized', reconnecting => { - if (!reconnecting) { - shared_converse.registerIntervalHandler(); - } - }); - api.listen.on('clearSession', () => { - if (shared_converse.shouldClearCache() && shared_converse.xmppstatus) { - shared_converse.xmppstatus.destroy(); - - delete shared_converse.xmppstatus; - api.promises.add(['statusInitialized']); - } - }); - api.listen.on('connected', () => initStatus(false)); - api.listen.on('reconnected', () => initStatus(true)); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/roster/utils.js - - - - -const { - $pres: utils_$pres -} = core_converse.env; - -async function initRoster() { - // Initialize the Bakcbone collections that represent the contats - // roster and the roster groups. - await api.waitUntil('VCardsInitialized'); - shared_converse.roster = new shared_converse.RosterContacts(); - let id = `converse.contacts-${shared_converse.bare_jid}`; - initStorage(shared_converse.roster, id); - shared_converse.roster.data = new Model(); - id = `converse-roster-model-${shared_converse.bare_jid}`; - shared_converse.roster.data.id = id; - initStorage(shared_converse.roster.data, id); - - shared_converse.roster.data.fetch(); - /** - * Triggered once the `_converse.RosterContacts` - * been created, but not yet populated with data. - * This event is useful when you want to create views for these collections. - * @event _converse#chatBoxMaximized - * @example _converse.api.listen.on('rosterInitialized', () => { ... }); - * @example _converse.api.waitUntil('rosterInitialized').then(() => { ... }); - */ - - - api.trigger('rosterInitialized'); -} -/** - * Fetch all the roster groups, and then the roster contacts. - * Emit an event after fetching is done in each case. - * @private - * @param { Bool } ignore_cache - If set to to true, the local cache - * will be ignored it's guaranteed that the XMPP server - * will be queried for the roster. - */ - - -async function populateRoster(ignore_cache = false) { - if (ignore_cache) { - shared_converse.send_initial_presence = true; - } - - try { - await shared_converse.roster.fetchRosterContacts(); - api.trigger('rosterContactsFetched'); - } catch (reason) { - headless_log.error(reason); - } finally { - shared_converse.send_initial_presence && api.user.presence.send(); - } -} - -function updateUnreadCounter(chatbox) { - var _converse$roster; - - const contact = (_converse$roster = shared_converse.roster) === null || _converse$roster === void 0 ? void 0 : _converse$roster.findWhere({ - 'jid': chatbox.get('jid') - }); - contact === null || contact === void 0 ? void 0 : contact.save({ - 'num_unread': chatbox.get('num_unread') - }); -} - -function registerPresenceHandler() { - unregisterPresenceHandler(); - shared_converse.presence_ref = shared_converse.connection.addHandler(presence => { - shared_converse.roster.presenceHandler(presence); - - return true; - }, null, 'presence', null); -} - -function unregisterPresenceHandler() { - if (shared_converse.presence_ref !== undefined) { - shared_converse.connection.deleteHandler(shared_converse.presence_ref); - - delete shared_converse.presence_ref; - } -} - -async function clearPresences() { - var _converse$presences; - - await ((_converse$presences = shared_converse.presences) === null || _converse$presences === void 0 ? void 0 : _converse$presences.clearStore()); -} -/** - * Roster specific event handler for the clearSession event - */ - - -async function utils_onClearSession() { - await clearPresences(); - - if (shared_converse.shouldClearCache()) { - if (shared_converse.rostergroups) { - await shared_converse.rostergroups.clearStore(); - delete shared_converse.rostergroups; - } - - if (shared_converse.roster) { - var _converse$roster$data; - - (_converse$roster$data = shared_converse.roster.data) === null || _converse$roster$data === void 0 ? void 0 : _converse$roster$data.destroy(); - await shared_converse.roster.clearStore(); - delete shared_converse.roster; - } - } -} -/** - * Roster specific event handler for the presencesInitialized event - * @param { Boolean } reconnecting - */ - -async function onPresencesInitialized(reconnecting) { - if (reconnecting) { - /** - * Similar to `rosterInitialized`, but instead pertaining to reconnection. - * This event indicates that the roster and its groups are now again - * available after Converse.js has reconnected. - * @event _converse#rosterReadyAfterReconnection - * @example _converse.api.listen.on('rosterReadyAfterReconnection', () => { ... }); - */ - api.trigger('rosterReadyAfterReconnection'); - } else { - await initRoster(); - } - - shared_converse.roster.onConnected(); - - registerPresenceHandler(); - populateRoster(!shared_converse.connection.restored); -} -/** - * Roster specific event handler for the statusInitialized event - * @param { Boolean } reconnecting - */ - -async function roster_utils_onStatusInitialized(reconnecting) { - if (reconnecting) { - // When reconnecting and not resuming a previous session, - // we clear all cached presence data, since it might be stale - // and we'll receive new presence updates - !shared_converse.connection.hasResumed() && (await clearPresences()); - } else { - shared_converse.presences = new shared_converse.Presences(); - const id = `converse.presences-${shared_converse.bare_jid}`; - initStorage(shared_converse.presences, id, 'session'); // We might be continuing an existing session, so we fetch - // cached presence data. - - shared_converse.presences.fetch(); - } - /** - * Triggered once the _converse.Presences collection has been - * initialized and its cached data fetched. - * Returns a boolean indicating whether this event has fired due to - * Converse having reconnected. - * @event _converse#presencesInitialized - * @type { bool } - * @example _converse.api.listen.on('presencesInitialized', reconnecting => { ... }); - */ - - - api.trigger('presencesInitialized', reconnecting); -} -/** - * Roster specific event handler for the chatBoxesInitialized event - */ - -function onChatBoxesInitialized() { - shared_converse.chatboxes.on('change:num_unread', updateUnreadCounter); - - shared_converse.chatboxes.on('add', chatbox => { - if (chatbox.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - chatbox.setRosterContact(chatbox.get('jid')); - } - }); -} -/** - * Roster specific handler for the rosterContactsFetched promise - */ - -function onRosterContactsFetched() { - shared_converse.roster.on('add', contact => { - // When a new contact is added, check if we already have a - // chatbox open for it, and if so attach it to the chatbox. - const chatbox = shared_converse.chatboxes.findWhere({ - 'jid': contact.get('jid') - }); - - chatbox === null || chatbox === void 0 ? void 0 : chatbox.setRosterContact(contact.get('jid')); - }); -} -/** - * Reject or cancel another user's subscription to our presence updates. - * @function rejectPresenceSubscription - * @param { String } jid - The Jabber ID of the user whose subscription is being canceled - * @param { String } message - An optional message to the user - */ - -function rejectPresenceSubscription(jid, message) { - const pres = utils_$pres({ - to: jid, - type: "unsubscribed" - }); - - if (message && message !== "") { - pres.c("status").t(message); - } - - api.send(pres); -} -function contactsComparator(contact1, contact2) { - const status1 = contact1.presence.get('show') || 'offline'; - const status2 = contact2.presence.get('show') || 'offline'; - - if (shared_converse.STATUS_WEIGHTS[status1] === shared_converse.STATUS_WEIGHTS[status2]) { - const name1 = contact1.getDisplayName().toLowerCase(); - const name2 = contact2.getDisplayName().toLowerCase(); - return name1 < name2 ? -1 : name1 > name2 ? 1 : 0; - } else { - return shared_converse.STATUS_WEIGHTS[status1] < shared_converse.STATUS_WEIGHTS[status2] ? -1 : 1; - } -} -function groupsComparator(a, b) { - const HEADER_WEIGHTS = {}; - HEADER_WEIGHTS[shared_converse.HEADER_UNREAD] = 0; - HEADER_WEIGHTS[shared_converse.HEADER_REQUESTING_CONTACTS] = 1; - HEADER_WEIGHTS[shared_converse.HEADER_CURRENT_CONTACTS] = 2; - HEADER_WEIGHTS[shared_converse.HEADER_UNGROUPED] = 3; - HEADER_WEIGHTS[shared_converse.HEADER_PENDING_CONTACTS] = 4; - const WEIGHTS = HEADER_WEIGHTS; - const special_groups = Object.keys(HEADER_WEIGHTS); - const a_is_special = special_groups.includes(a); - const b_is_special = special_groups.includes(b); - - if (!a_is_special && !b_is_special) { - return a.toLowerCase() < b.toLowerCase() ? -1 : a.toLowerCase() > b.toLowerCase() ? 1 : 0; - } else if (a_is_special && b_is_special) { - return WEIGHTS[a] < WEIGHTS[b] ? -1 : WEIGHTS[a] > WEIGHTS[b] ? 1 : 0; - } else if (!a_is_special && b_is_special) { - const a_header = shared_converse.HEADER_CURRENT_CONTACTS; - return WEIGHTS[a_header] < WEIGHTS[b] ? -1 : WEIGHTS[a_header] > WEIGHTS[b] ? 1 : 0; - } else if (a_is_special && !b_is_special) { - const b_header = shared_converse.HEADER_CURRENT_CONTACTS; - return WEIGHTS[a] < WEIGHTS[b_header] ? -1 : WEIGHTS[a] > WEIGHTS[b_header] ? 1 : 0; - } -} -;// CONCATENATED MODULE: ./src/headless/plugins/roster/contact.js - - - - -const { - Strophe: contact_Strophe, - $iq: contact_$iq, - $pres: contact_$pres -} = core_converse.env; -/** - * @class - * @namespace RosterContact - */ - -const RosterContact = Model.extend({ - defaults: { - 'chat_state': undefined, - 'groups': [], - 'image': shared_converse.DEFAULT_IMAGE, - 'image_type': shared_converse.DEFAULT_IMAGE_TYPE, - 'num_unread': 0, - 'status': undefined - }, - - async initialize(attributes) { - this.initialized = getOpenPromise(); - this.setPresence(); - const { - jid - } = attributes; - const bare_jid = contact_Strophe.getBareJidFromJid(jid).toLowerCase(); - attributes.jid = bare_jid; - this.set(Object.assign({ - 'id': bare_jid, - 'jid': bare_jid, - 'user_id': contact_Strophe.getNodeFromJid(jid) - }, attributes)); - /** - * When a contact's presence status has changed. - * The presence status is either `online`, `offline`, `dnd`, `away` or `xa`. - * @event _converse#contactPresenceChanged - * @type { _converse.RosterContact } - * @example _converse.api.listen.on('contactPresenceChanged', contact => { ... }); - */ - - this.listenTo(this.presence, 'change:show', () => api.trigger('contactPresenceChanged', this)); - this.listenTo(this.presence, 'change:show', () => this.trigger('presenceChanged')); - /** - * Synchronous event which provides a hook for further initializing a RosterContact - * @event _converse#rosterContactInitialized - * @param { _converse.RosterContact } contact - */ - - await api.trigger('rosterContactInitialized', this, { - 'Synchronous': true - }); - this.initialized.resolve(); - }, - - setPresence() { - const jid = this.get('jid'); - this.presence = shared_converse.presences.findWhere({ - 'jid': jid - }) || shared_converse.presences.create({ - 'jid': jid - }); - }, - - openChat() { - const attrs = this.attributes; - api.chats.open(attrs.jid, attrs, true); - }, - - /** - * Return a string of tab-separated values that are to be used when - * matching against filter text. - * - * The goal is to be able to filter against the VCard fullname, - * roster nickname and JID. - * @returns { String } Lower-cased, tab-separated values - */ - getFilterCriteria() { - const nick = this.get('nickname'); - const jid = this.get('jid'); - let criteria = this.getDisplayName(); - criteria = !criteria.includes(jid) ? criteria.concat(` ${jid}`) : criteria; - criteria = !criteria.includes(nick) ? criteria.concat(` ${nick}`) : criteria; - return criteria.toLowerCase(); - }, - - getDisplayName() { - // Gets overridden in converse-vcard where the fullname is may be returned - if (this.get('nickname')) { - return this.get('nickname'); - } else { - return this.get('jid'); - } - }, - - getFullname() { - // Gets overridden in converse-vcard where the fullname may be returned - return this.get('jid'); - }, - - /** - * Send a presence subscription request to this roster contact - * @private - * @method _converse.RosterContacts#subscribe - * @param { String } message - An optional message to explain the - * reason for the subscription request. - */ - subscribe(message) { - const pres = contact_$pres({ - to: this.get('jid'), - type: "subscribe" - }); - - if (message && message !== "") { - pres.c("status").t(message).up(); - } - - const nick = shared_converse.xmppstatus.getNickname() || shared_converse.xmppstatus.getFullname(); - - if (nick) { - pres.c('nick', { - 'xmlns': contact_Strophe.NS.NICK - }).t(nick).up(); - } - - api.send(pres); - this.save('ask', "subscribe"); // ask === 'subscribe' Means we have asked to subscribe to them. - - return this; - }, - - /** - * Upon receiving the presence stanza of type "subscribed", - * the user SHOULD acknowledge receipt of that subscription - * state notification by sending a presence stanza of type - * "subscribe" to the contact - * @private - * @method _converse.RosterContacts#ackSubscribe - */ - ackSubscribe() { - api.send(contact_$pres({ - 'type': 'subscribe', - 'to': this.get('jid') - })); - }, - - /** - * Upon receiving the presence stanza of type "unsubscribed", - * the user SHOULD acknowledge receipt of that subscription state - * notification by sending a presence stanza of type "unsubscribe" - * this step lets the user's server know that it MUST no longer - * send notification of the subscription state change to the user. - * @private - * @method _converse.RosterContacts#ackUnsubscribe - * @param { String } jid - The Jabber ID of the user who is unsubscribing - */ - ackUnsubscribe() { - api.send(contact_$pres({ - 'type': 'unsubscribe', - 'to': this.get('jid') - })); - this.removeFromRoster(); - this.destroy(); - }, - - /** - * Unauthorize this contact's presence subscription - * @private - * @method _converse.RosterContacts#unauthorize - * @param { String } message - Optional message to send to the person being unauthorized - */ - unauthorize(message) { - rejectPresenceSubscription(this.get('jid'), message); - return this; - }, - - /** - * Authorize presence subscription - * @private - * @method _converse.RosterContacts#authorize - * @param { String } message - Optional message to send to the person being authorized - */ - authorize(message) { - const pres = contact_$pres({ - 'to': this.get('jid'), - 'type': "subscribed" - }); - - if (message && message !== "") { - pres.c("status").t(message); - } - - api.send(pres); - return this; - }, - - /** - * Instruct the XMPP server to remove this contact from our roster - * @private - * @method _converse.RosterContacts# - * @returns { Promise } - */ - removeFromRoster() { - const iq = contact_$iq({ - type: 'set' - }).c('query', { - xmlns: contact_Strophe.NS.ROSTER - }).c('item', { - jid: this.get('jid'), - subscription: "remove" - }); - return api.sendIQ(iq); - } - -}); -/* harmony default export */ const contact = (RosterContact); -;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseSum.js -/** - * The base implementation of `_.sum` and `_.sumBy` without support for - * iteratee shorthands. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {number} Returns the sum. - */ -function baseSum(array, iteratee) { - var result, - index = -1, - length = array.length; - - while (++index < length) { - var current = iteratee(array[index]); - - if (current !== undefined) { - result = result === undefined ? current : result + current; - } - } - - return result; -} - -/* harmony default export */ const _baseSum = (baseSum); -;// CONCATENATED MODULE: ./node_modules/lodash-es/sum.js - - -/** - * Computes the sum of the values in `array`. - * - * @static - * @memberOf _ - * @since 3.4.0 - * @category Math - * @param {Array} array The array to iterate over. - * @returns {number} Returns the sum. - * @example - * - * _.sum([4, 2, 8, 6]); - * // => 20 - */ - -function sum(array) { - return array && array.length ? _baseSum(array, lodash_es_identity) : 0; -} - -/* harmony default export */ const lodash_es_sum = (sum); -;// CONCATENATED MODULE: ./src/headless/plugins/roster/contacts.js - - - - - - - - - -const { - Strophe: contacts_Strophe, - $iq: contacts_$iq, - sizzle: contacts_sizzle, - u: contacts_u -} = core_converse.env; -const RosterContacts = Collection.extend({ - model: contact, - - initialize() { - const id = `roster.state-${shared_converse.bare_jid}-${this.get('jid')}`; - this.state = new Model({ - id, - 'collapsed_groups': [] - }); - initStorage(this.state, id); - this.state.fetch(); - }, - - onConnected() { - // Called as soon as the connection has been established - // (either after initial login, or after reconnection). - // Use the opportunity to register stanza handlers. - this.registerRosterHandler(); - this.registerRosterXHandler(); - }, - - registerRosterHandler() { - // Register a handler for roster IQ "set" stanzas, which update - // roster contacts. - shared_converse.connection.addHandler(iq => { - shared_converse.roster.onRosterPush(iq); - - return true; - }, contacts_Strophe.NS.ROSTER, 'iq', "set"); - }, - - registerRosterXHandler() { - // Register a handler for RosterX message stanzas, which are - // used to suggest roster contacts to a user. - let t = 0; - - shared_converse.connection.addHandler(function (msg) { - window.setTimeout(function () { - shared_converse.connection.flush(); - - shared_converse.roster.subscribeToSuggestedItems.bind(shared_converse.roster)(msg); - }, t); - t += msg.querySelectorAll('item').length * 250; - return true; - }, contacts_Strophe.NS.ROSTERX, 'message', null); - }, - - /** - * Fetches the roster contacts, first by trying the browser cache, - * and if that's empty, then by querying the XMPP server. - * @private - * @returns {promise} Promise which resolves once the contacts have been fetched. - */ - async fetchRosterContacts() { - const result = await new Promise((resolve, reject) => { - this.fetch({ - 'add': true, - 'silent': true, - 'success': resolve, - 'error': (c, e) => reject(e) - }); - }); - - if (contacts_u.isErrorObject(result)) { - headless_log.error(result); // Force a full roster refresh - - shared_converse.session.save('roster_cached', false); - - this.data.save('version', undefined); - } - - if (shared_converse.session.get('roster_cached')) { - /** - * The contacts roster has been retrieved from the local cache (`sessionStorage`). - * @event _converse#cachedRoster - * @type { _converse.RosterContacts } - * @example _converse.api.listen.on('cachedRoster', (items) => { ... }); - * @example _converse.api.waitUntil('cachedRoster').then(items => { ... }); - */ - api.trigger('cachedRoster', result); - } else { - shared_converse.send_initial_presence = true; - return shared_converse.roster.fetchFromServer(); - } - }, - - subscribeToSuggestedItems(msg) { - Array.from(msg.querySelectorAll('item')).forEach(item => { - if (item.getAttribute('action') === 'add') { - shared_converse.roster.addAndSubscribe(item.getAttribute('jid'), shared_converse.xmppstatus.getNickname() || shared_converse.xmppstatus.getFullname()); - } - }); - return true; - }, - - isSelf(jid) { - return contacts_u.isSameBareJID(jid, shared_converse.connection.jid); - }, - - /** - * Add a roster contact and then once we have confirmation from - * the XMPP server we subscribe to that contact's presence updates. - * @private - * @method _converse.RosterContacts#addAndSubscribe - * @param { String } jid - The Jabber ID of the user being added and subscribed to. - * @param { String } name - The name of that user - * @param { Array.String } groups - Any roster groups the user might belong to - * @param { String } message - An optional message to explain the reason for the subscription request. - * @param { Object } attributes - Any additional attributes to be stored on the user's model. - */ - async addAndSubscribe(jid, name, groups, message, attributes) { - const contact = await this.addContactToRoster(jid, name, groups, attributes); - - if (contact instanceof shared_converse.RosterContact) { - contact.subscribe(message); - } - }, - - /** - * Send an IQ stanza to the XMPP server to add a new roster contact. - * @private - * @method _converse.RosterContacts#sendContactAddIQ - * @param { String } jid - The Jabber ID of the user being added - * @param { String } name - The name of that user - * @param { Array.String } groups - Any roster groups the user might belong to - * @param { Function } callback - A function to call once the IQ is returned - * @param { Function } errback - A function to call if an error occurred - */ - sendContactAddIQ(jid, name, groups) { - name = name ? name : null; - const iq = contacts_$iq({ - 'type': 'set' - }).c('query', { - 'xmlns': contacts_Strophe.NS.ROSTER - }).c('item', { - jid, - name - }); - groups.forEach(g => iq.c('group').t(g).up()); - return api.sendIQ(iq); - }, - - /** - * Adds a RosterContact instance to _converse.roster and - * registers the contact on the XMPP server. - * Returns a promise which is resolved once the XMPP server has responded. - * @private - * @method _converse.RosterContacts#addContactToRoster - * @param { String } jid - The Jabber ID of the user being added and subscribed to. - * @param { String } name - The name of that user - * @param { Array.String } groups - Any roster groups the user might belong to - * @param { Object } attributes - Any additional attributes to be stored on the user's model. - */ - async addContactToRoster(jid, name, groups, attributes) { - await api.waitUntil('rosterContactsFetched'); - groups = groups || []; - - try { - await this.sendContactAddIQ(jid, name, groups); - } catch (e) { - headless_log.error(e); - alert(__('Sorry, there was an error while trying to add %1$s as a contact.', name || jid)); - return e; - } - - return this.create(Object.assign({ - 'ask': undefined, - 'nickname': name, - groups, - jid, - 'requesting': false, - 'subscription': 'none' - }, attributes), { - 'sort': false - }); - }, - - async subscribeBack(bare_jid, presence) { - const contact = this.get(bare_jid); - - if (contact instanceof shared_converse.RosterContact) { - contact.authorize().subscribe(); - } else { - var _sizzle$pop; - - // Can happen when a subscription is retried or roster was deleted - const nickname = ((_sizzle$pop = contacts_sizzle(`nick[xmlns="${contacts_Strophe.NS.NICK}"]`, presence).pop()) === null || _sizzle$pop === void 0 ? void 0 : _sizzle$pop.textContent) || null; - const contact = await this.addContactToRoster(bare_jid, nickname, [], { - 'subscription': 'from' - }); - - if (contact instanceof shared_converse.RosterContact) { - contact.authorize().subscribe(); - } - } - }, - - getNumOnlineContacts() { - const ignored = ['offline', 'unavailable']; - return lodash_es_sum(this.models.filter(m => !ignored.includes(m.presence.get('show')))); - }, - - /** - * Handle roster updates from the XMPP server. - * See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push - * @private - * @method _converse.RosterContacts#onRosterPush - * @param { XMLElement } IQ - The IQ stanza received from the XMPP server. - */ - onRosterPush(iq) { - const id = iq.getAttribute('id'); - const from = iq.getAttribute('from'); - - if (from && from !== shared_converse.bare_jid) { - // https://tools.ietf.org/html/rfc6121#page-15 - // - // A receiving client MUST ignore the stanza unless it has no 'from' - // attribute (i.e., implicitly from the bare JID of the user's - // account) or it has a 'from' attribute whose value matches the - // user's bare JID . - headless_log.warn(`Ignoring roster illegitimate roster push message from ${iq.getAttribute('from')}`); - return; - } - - api.send(contacts_$iq({ - type: 'result', - id, - from: shared_converse.connection.jid - })); - const query = contacts_sizzle(`query[xmlns="${contacts_Strophe.NS.ROSTER}"]`, iq).pop(); - this.data.save('version', query.getAttribute('ver')); - const items = contacts_sizzle(`item`, query); - - if (items.length > 1) { - headless_log.error(iq); - throw new Error('Roster push query may not contain more than one "item" element.'); - } - - if (items.length === 0) { - headless_log.warn(iq); - headless_log.warn('Received a roster push stanza without an "item" element.'); - return; - } - - this.updateContact(items.pop()); - /** - * When the roster receives a push event from server (i.e. new entry in your contacts roster). - * @event _converse#rosterPush - * @type { XMLElement } - * @example _converse.api.listen.on('rosterPush', iq => { ... }); - */ - - api.trigger('rosterPush', iq); - return; - }, - - rosterVersioningSupported() { - return api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver') && this.data.get('version'); - }, - - /** - * Fetch the roster from the XMPP server - * @private - * @emits _converse#roster - * @returns {promise} - */ - async fetchFromServer() { - const stanza = contacts_$iq({ - 'type': 'get', - 'id': contacts_u.getUniqueId('roster') - }).c('query', { - xmlns: contacts_Strophe.NS.ROSTER - }); - - if (this.rosterVersioningSupported()) { - stanza.attrs({ - 'ver': this.data.get('version') - }); - } - - const iq = await api.sendIQ(stanza, null, false); - - if (iq.getAttribute('type') !== 'error') { - const query = contacts_sizzle(`query[xmlns="${contacts_Strophe.NS.ROSTER}"]`, iq).pop(); - - if (query) { - const items = contacts_sizzle(`item`, query); - items.forEach(item => this.updateContact(item)); - this.data.save('version', query.getAttribute('ver')); - } - } else if (!contacts_u.isServiceUnavailableError(iq)) { - // Some unknown error happened, so we will try to fetch again if the page reloads. - headless_log.error(iq); - headless_log.error("Error while trying to fetch roster from the server"); - return; - } - - shared_converse.session.save('roster_cached', true); - /** - * When the roster has been received from the XMPP server. - * See also the `cachedRoster` event further up, which gets called instead of - * `roster` if its already in `sessionStorage`. - * @event _converse#roster - * @type { XMLElement } - * @example _converse.api.listen.on('roster', iq => { ... }); - * @example _converse.api.waitUntil('roster').then(iq => { ... }); - */ - - - api.trigger('roster', iq); - }, - - /* Update or create RosterContact models based on the given `item` XML - * node received in the resulting IQ stanza from the server. - * @private - * @param { XMLElement } item - */ - updateContact(item) { - const jid = item.getAttribute('jid'); - const contact = this.get(jid); - const subscription = item.getAttribute("subscription"); - const ask = item.getAttribute("ask"); - const groups = [...new Set(contacts_sizzle('group', item).map(e => e.textContent))]; - - if (!contact) { - if (subscription === "none" && ask === null || subscription === "remove") { - return; // We're lazy when adding contacts. - } - - this.create({ - 'ask': ask, - 'nickname': item.getAttribute("name"), - 'groups': groups, - 'jid': jid, - 'subscription': subscription - }, { - sort: false - }); - } else { - if (subscription === "remove") { - return contact.destroy(); - } // We only find out about requesting contacts via the - // presence handler, so if we receive a contact - // here, we know they aren't requesting anymore. - // see docs/DEVELOPER.rst - - - contact.save({ - 'subscription': subscription, - 'ask': ask, - 'nickname': item.getAttribute("name"), - 'requesting': null, - 'groups': groups - }); - } - }, - - createRequestingContact(presence) { - var _sizzle$pop2; - - const bare_jid = contacts_Strophe.getBareJidFromJid(presence.getAttribute('from')); - const nickname = ((_sizzle$pop2 = contacts_sizzle(`nick[xmlns="${contacts_Strophe.NS.NICK}"]`, presence).pop()) === null || _sizzle$pop2 === void 0 ? void 0 : _sizzle$pop2.textContent) || null; - const user_data = { - 'jid': bare_jid, - 'subscription': 'none', - 'ask': null, - 'requesting': true, - 'nickname': nickname - }; - /** - * Triggered when someone has requested to subscribe to your presence (i.e. to be your contact). - * @event _converse#contactRequest - * @type { _converse.RosterContact } - * @example _converse.api.listen.on('contactRequest', contact => { ... }); - */ - - api.trigger('contactRequest', this.create(user_data)); - }, - - handleIncomingSubscription(presence) { - const jid = presence.getAttribute('from'), - bare_jid = contacts_Strophe.getBareJidFromJid(jid), - contact = this.get(bare_jid); - - if (!api.settings.get('allow_contact_requests')) { - rejectPresenceSubscription(jid, __("This client does not allow presence subscriptions")); - } - - if (api.settings.get('auto_subscribe')) { - if (!contact || contact.get('subscription') !== 'to') { - this.subscribeBack(bare_jid, presence); - } else { - contact.authorize(); - } - } else { - if (contact) { - if (contact.get('subscription') !== 'none') { - contact.authorize(); - } else if (contact.get('ask') === "subscribe") { - contact.authorize(); - } - } else { - this.createRequestingContact(presence); - } - } - }, - - handleOwnPresence(presence) { - const jid = presence.getAttribute('from'), - resource = contacts_Strophe.getResourceFromJid(jid), - presence_type = presence.getAttribute('type'); - - if (shared_converse.connection.jid !== jid && presence_type !== 'unavailable' && (api.settings.get('synchronize_availability') === true || api.settings.get('synchronize_availability') === resource)) { - var _presence$querySelect, _presence$querySelect2; - - // Another resource has changed its status and - // synchronize_availability option set to update, - // we'll update ours as well. - const show = ((_presence$querySelect = presence.querySelector('show')) === null || _presence$querySelect === void 0 ? void 0 : _presence$querySelect.textContent) || 'online'; - - shared_converse.xmppstatus.save({ - 'status': show - }, { - 'silent': true - }); - - const status_message = (_presence$querySelect2 = presence.querySelector('status')) === null || _presence$querySelect2 === void 0 ? void 0 : _presence$querySelect2.textContent; - - if (status_message) { - shared_converse.xmppstatus.save({ - 'status_message': status_message - }); - } - } - - if (shared_converse.jid === jid && presence_type === 'unavailable') { - // XXX: We've received an "unavailable" presence from our - // own resource. Apparently this happens due to a - // Prosody bug, whereby we send an IQ stanza to remove - // a roster contact, and Prosody then sends - // "unavailable" globally, instead of directed to the - // particular user that's removed. - // - // Here is the bug report: https://prosody.im/issues/1121 - // - // I'm not sure whether this might legitimately happen - // in other cases. - // - // As a workaround for now we simply send our presence again, - // otherwise we're treated as offline. - api.user.presence.send(); - } - }, - - presenceHandler(presence) { - var _presence$querySelect3; - - const presence_type = presence.getAttribute('type'); - - if (presence_type === 'error') { - return true; - } - - const jid = presence.getAttribute('from'), - bare_jid = contacts_Strophe.getBareJidFromJid(jid); - - if (this.isSelf(bare_jid)) { - return this.handleOwnPresence(presence); - } else if (contacts_sizzle(`query[xmlns="${contacts_Strophe.NS.MUC}"]`, presence).length) { - return; // Ignore MUC - } - - const status_message = (_presence$querySelect3 = presence.querySelector('status')) === null || _presence$querySelect3 === void 0 ? void 0 : _presence$querySelect3.textContent; - const contact = this.get(bare_jid); - - if (contact && status_message !== contact.get('status')) { - contact.save({ - 'status': status_message - }); - } - - if (presence_type === 'subscribed' && contact) { - contact.ackSubscribe(); - } else if (presence_type === 'unsubscribed' && contact) { - contact.ackUnsubscribe(); - } else if (presence_type === 'unsubscribe') { - return; - } else if (presence_type === 'subscribe') { - this.handleIncomingSubscription(presence); - } else if (presence_type === 'unavailable' && contact) { - const resource = contacts_Strophe.getResourceFromJid(jid); - contact.presence.removeResource(resource); - } else if (contact) { - // presence_type is undefined - contact.presence.addResource(presence); - } - } - -}); -/* harmony default export */ const contacts = (RosterContacts); -;// CONCATENATED MODULE: ./src/headless/plugins/roster/api.js - -const { - Strophe: roster_api_Strophe -} = core_converse.env; -/* harmony default export */ const roster_api = ({ - /** - * @namespace _converse.api.contacts - * @memberOf _converse.api - */ - contacts: { - /** - * This method is used to retrieve roster contacts. - * - * @method _converse.api.contacts.get - * @params {(string[]|string)} jid|jids The JID or JIDs of - * the contacts to be returned. - * @returns {promise} Promise which resolves with the - * _converse.RosterContact (or an array of them) representing the contact. - * - * @example - * // Fetch a single contact - * _converse.api.listen.on('rosterContactsFetched', function () { - * const contact = await _converse.api.contacts.get('buddy@example.com') - * // ... - * }); - * - * @example - * // To get multiple contacts, pass in an array of JIDs: - * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = await _converse.api.contacts.get( - * ['buddy1@example.com', 'buddy2@example.com'] - * ) - * // ... - * }); - * - * @example - * // To return all contacts, simply call ``get`` without any parameters: - * _converse.api.listen.on('rosterContactsFetched', function () { - * const contacts = await _converse.api.contacts.get(); - * // ... - * }); - */ - async get(jids) { - await api.waitUntil('rosterContactsFetched'); - - const _getter = jid => shared_converse.roster.get(roster_api_Strophe.getBareJidFromJid(jid)); - - if (jids === undefined) { - jids = shared_converse.roster.pluck('jid'); - } else if (typeof jids === 'string') { - return _getter(jids); - } - - return jids.map(_getter); - }, - - /** - * Add a contact. - * - * @method _converse.api.contacts.add - * @param {string} jid The JID of the contact to be added - * @param {string} [name] A custom name to show the user by in the roster - * @example - * _converse.api.contacts.add('buddy@example.com') - * @example - * _converse.api.contacts.add('buddy@example.com', 'Buddy') - */ - async add(jid, name) { - await api.waitUntil('rosterContactsFetched'); - - if (typeof jid !== 'string' || !jid.includes('@')) { - throw new TypeError('contacts.add: invalid jid'); - } - - return shared_converse.roster.addAndSubscribe(jid, name); - } - - } -}); -;// CONCATENATED MODULE: ./src/headless/plugins/roster/presence.js - - - - - -const { - Strophe: presence_Strophe, - dayjs: presence_dayjs, - sizzle: presence_sizzle -} = core_converse.env; -const Resource = Model.extend({ - 'idAttribute': 'name' -}); -const Resources = Collection.extend({ - 'model': Resource -}); -const Presence = Model.extend({ - defaults: { - 'show': 'offline' - }, - - initialize() { - this.resources = new Resources(); - const id = `converse.identities-${this.get('jid')}`; - initStorage(this.resources, id, 'session'); - this.listenTo(this.resources, 'update', this.onResourcesChanged); - this.listenTo(this.resources, 'change', this.onResourcesChanged); - }, - - onResourcesChanged() { - var _hpr$attributes; - - const hpr = this.getHighestPriorityResource(); - const show = (hpr === null || hpr === void 0 ? void 0 : (_hpr$attributes = hpr.attributes) === null || _hpr$attributes === void 0 ? void 0 : _hpr$attributes.show) || 'offline'; - - if (this.get('show') !== show) { - this.save({ - 'show': show - }); - } - }, - - /** - * Return the resource with the highest priority. - * If multiple resources have the same priority, take the latest one. - * @private - */ - getHighestPriorityResource() { - return this.resources.sortBy(r => `${r.get('priority')}-${r.get('timestamp')}`).reverse()[0]; - }, - - /** - * Adds a new resource and it's associated attributes as taken - * from the passed in presence stanza. - * Also updates the presence if the resource has higher priority (and is newer). - * @private - * @param { XMLElement } presence: The presence stanza - */ - addResource(presence) { - var _presence$querySelect, _presence$querySelect2; - - const jid = presence.getAttribute('from'), - name = presence_Strophe.getResourceFromJid(jid), - delay = presence_sizzle(`delay[xmlns="${presence_Strophe.NS.DELAY}"]`, presence).pop(), - priority = ((_presence$querySelect = presence.querySelector('priority')) === null || _presence$querySelect === void 0 ? void 0 : _presence$querySelect.textContent) ?? 0, - resource = this.resources.get(name), - settings = { - 'name': name, - 'priority': lodash_es_isNaN(parseInt(priority, 10)) ? 0 : parseInt(priority, 10), - 'show': ((_presence$querySelect2 = presence.querySelector('show')) === null || _presence$querySelect2 === void 0 ? void 0 : _presence$querySelect2.textContent) ?? 'online', - 'timestamp': delay ? presence_dayjs(delay.getAttribute('stamp')).toISOString() : new Date().toISOString() - }; - - if (resource) { - resource.save(settings); - } else { - this.resources.create(settings); - } - }, - - /** - * Remove the passed in resource from the resources map. - * Also redetermines the presence given that there's one less - * resource. - * @private - * @param { string } name: The resource name - */ - removeResource(name) { - const resource = this.resources.get(name); - - if (resource) { - resource.destroy(); - } - } - -}); -const Presences = Collection.extend({ - 'model': Presence -}); -;// CONCATENATED MODULE: ./src/headless/plugins/roster/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - -core_converse.plugins.add('converse-roster', { - dependencies: ['converse-status'], - - initialize() { - api.settings.extend({ - 'allow_contact_requests': true, - 'auto_subscribe': false, - 'synchronize_availability': true - }); - api.promises.add(['cachedRoster', 'roster', 'rosterContactsFetched', 'rosterInitialized']); // API methods only available to plugins - - Object.assign(shared_converse.api, roster_api); - shared_converse.HEADER_CURRENT_CONTACTS = __('My contacts'); - shared_converse.HEADER_PENDING_CONTACTS = __('Pending contacts'); - shared_converse.HEADER_REQUESTING_CONTACTS = __('Contact requests'); - shared_converse.HEADER_UNGROUPED = __('Ungrouped'); - shared_converse.HEADER_UNREAD = __('New messages'); - shared_converse.Presence = Presence; - shared_converse.Presences = Presences; - shared_converse.RosterContact = contact; - shared_converse.RosterContacts = contacts; - api.listen.on('beforeTearDown', () => unregisterPresenceHandler()); - api.listen.on('chatBoxesInitialized', onChatBoxesInitialized); - api.listen.on('clearSession', utils_onClearSession); - api.listen.on('presencesInitialized', onPresencesInitialized); - api.listen.on('statusInitialized', roster_utils_onStatusInitialized); - api.listen.on('streamResumptionFailed', () => shared_converse.session.set('roster_cached', false)); - api.waitUntil('rosterContactsFetched').then(onRosterContactsFetched); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/smacks/utils.js - - - -const { - Strophe: smacks_utils_Strophe -} = core_converse.env; -const smacks_utils_u = core_converse.env.utils; - -function isStreamManagementSupported() { - if (api.connection.isType('bosh') && !shared_converse.isTestEnv()) { - return false; - } - - return api.disco.stream.getFeature('sm', smacks_utils_Strophe.NS.SM); -} - -function handleAck(el) { - if (!shared_converse.session.get('smacks_enabled')) { - return true; - } - - const handled = parseInt(el.getAttribute('h'), 10); - - const last_known_handled = shared_converse.session.get('num_stanzas_handled_by_server'); - - const delta = handled - last_known_handled; - - if (delta < 0) { - const err_msg = `New reported stanza count lower than previous. ` + `New: ${handled} - Previous: ${last_known_handled}`; - headless_log.error(err_msg); - } - - const unacked_stanzas = shared_converse.session.get('unacked_stanzas'); - - if (delta > unacked_stanzas.length) { - const err_msg = `Higher reported acknowledge count than unacknowledged stanzas. ` + `Reported Acknowledged Count: ${delta} -` + `Unacknowledged Stanza Count: ${unacked_stanzas.length} -` + `New: ${handled} - Previous: ${last_known_handled}`; - headless_log.error(err_msg); - } - - shared_converse.session.save({ - 'num_stanzas_handled_by_server': handled, - 'num_stanzas_since_last_ack': 0, - 'unacked_stanzas': unacked_stanzas.slice(delta) - }); - - return true; -} - -function sendAck() { - if (shared_converse.session.get('smacks_enabled')) { - const h = shared_converse.session.get('num_stanzas_handled'); - - const stanza = smacks_utils_u.toStanza(``); - api.send(stanza); - } - - return true; -} - -function stanzaHandler(el) { - if (shared_converse.session.get('smacks_enabled')) { - if (smacks_utils_u.isTagEqual(el, 'iq') || smacks_utils_u.isTagEqual(el, 'presence') || smacks_utils_u.isTagEqual(el, 'message')) { - const h = shared_converse.session.get('num_stanzas_handled'); - - shared_converse.session.save('num_stanzas_handled', h + 1); - } - } - - return true; -} - -function initSessionData() { - shared_converse.session.save({ - 'smacks_enabled': shared_converse.session.get('smacks_enabled') || false, - 'num_stanzas_handled': shared_converse.session.get('num_stanzas_handled') || 0, - 'num_stanzas_handled_by_server': shared_converse.session.get('num_stanzas_handled_by_server') || 0, - 'num_stanzas_since_last_ack': shared_converse.session.get('num_stanzas_since_last_ack') || 0, - 'unacked_stanzas': shared_converse.session.get('unacked_stanzas') || [] - }); -} - -function resetSessionData() { - var _converse$session; - - (_converse$session = shared_converse.session) === null || _converse$session === void 0 ? void 0 : _converse$session.save({ - 'smacks_enabled': false, - 'num_stanzas_handled': 0, - 'num_stanzas_handled_by_server': 0, - 'num_stanzas_since_last_ack': 0, - 'unacked_stanzas': [] - }); -} - -function saveSessionData(el) { - const data = { - 'smacks_enabled': true - }; - - if (['1', 'true'].includes(el.getAttribute('resume'))) { - data['smacks_stream_id'] = el.getAttribute('id'); - } - - shared_converse.session.save(data); - - return true; -} - -function onFailedStanza(el) { - if (el.querySelector('item-not-found')) { - // Stream resumption must happen before resource binding but - // enabling a new stream must happen after resource binding. - // Since resumption failed, we simply continue. - // - // After resource binding, sendEnableStanza will be called - // based on the afterResourceBinding event. - headless_log.warn('Could not resume previous SMACKS session, session id not found. ' + 'A new session will be established.'); - } else { - headless_log.error('Failed to enable stream management'); - headless_log.error(el.outerHTML); - } - - resetSessionData(); - /** - * Triggered when the XEP-0198 stream could not be resumed. - * @event _converse#streamResumptionFailed - */ - - api.trigger('streamResumptionFailed'); - return true; -} - -function resendUnackedStanzas() { - const stanzas = shared_converse.session.get('unacked_stanzas'); // We clear the unacked_stanzas array because it'll get populated - // again in `onStanzaSent` - - - shared_converse.session.save('unacked_stanzas', []); // XXX: Currently we're resending *all* unacked stanzas, including - // IQ[type="get"] stanzas that longer have handlers (because the - // page reloaded or we reconnected, causing removal of handlers). - // - // *Side-note:* Is it necessary to clear handlers upon reconnection? - // - // I've considered not resending those stanzas, but then keeping - // track of what's been sent and ack'd and their order gets - // prohibitively complex. - // - // It's unclear how much of a problem this poses. - // - // Two possible solutions are running @converse/headless as a - // service worker or handling IQ[type="result"] stanzas - // differently, more like push stanzas, so that they don't need - // explicit handlers. - - - stanzas.forEach(s => api.send(s)); -} - -function onResumedStanza(el) { - saveSessionData(el); - handleAck(el); - resendUnackedStanzas(); - shared_converse.connection.do_bind = false; // No need to bind our resource anymore - - shared_converse.connection.authenticated = true; - shared_converse.connection.restored = true; - - shared_converse.connection._changeConnectStatus(smacks_utils_Strophe.Status.CONNECTED, null); -} - -async function sendResumeStanza() { - const promise = getOpenPromise(); - - shared_converse.connection._addSysHandler(el => promise.resolve(onResumedStanza(el)), smacks_utils_Strophe.NS.SM, 'resumed'); - - shared_converse.connection._addSysHandler(el => promise.resolve(onFailedStanza(el)), smacks_utils_Strophe.NS.SM, 'failed'); - - const previous_id = shared_converse.session.get('smacks_stream_id'); - - const h = shared_converse.session.get('num_stanzas_handled'); - - const stanza = smacks_utils_u.toStanza(``); - api.send(stanza); - - shared_converse.connection.flush(); - - await promise; -} - -async function sendEnableStanza() { - if (!api.settings.get('enable_smacks') || shared_converse.session.get('smacks_enabled')) { - return; - } - - if (await isStreamManagementSupported()) { - const promise = getOpenPromise(); - - shared_converse.connection._addSysHandler(el => promise.resolve(saveSessionData(el)), smacks_utils_Strophe.NS.SM, 'enabled'); - - shared_converse.connection._addSysHandler(el => promise.resolve(onFailedStanza(el)), smacks_utils_Strophe.NS.SM, 'failed'); - - const resume = api.connection.isType('websocket') || shared_converse.isTestEnv(); - - const stanza = smacks_utils_u.toStanza(``); - api.send(stanza); - - shared_converse.connection.flush(); - - await promise; - } -} -const smacks_handlers = []; -async function enableStreamManagement() { - var _converse$session2; - - if (!api.settings.get('enable_smacks')) { - return; - } - - if (!(await isStreamManagementSupported())) { - return; - } - - const conn = shared_converse.connection; - - while (smacks_handlers.length) { - conn.deleteHandler(smacks_handlers.pop()); - } - - smacks_handlers.push(conn.addHandler(stanzaHandler)); - smacks_handlers.push(conn.addHandler(sendAck, smacks_utils_Strophe.NS.SM, 'r')); - smacks_handlers.push(conn.addHandler(handleAck, smacks_utils_Strophe.NS.SM, 'a')); - - if ((_converse$session2 = shared_converse.session) !== null && _converse$session2 !== void 0 && _converse$session2.get('smacks_stream_id')) { - await sendResumeStanza(); - } else { - resetSessionData(); - } -} -function onStanzaSent(stanza) { - if (!shared_converse.session) { - headless_log.warn('No _converse.session!'); - return; - } - - if (!shared_converse.session.get('smacks_enabled')) { - return; - } - - if (smacks_utils_u.isTagEqual(stanza, 'iq') || smacks_utils_u.isTagEqual(stanza, 'presence') || smacks_utils_u.isTagEqual(stanza, 'message')) { - const stanza_string = smacks_utils_Strophe.serialize(stanza); - - shared_converse.session.save('unacked_stanzas', (shared_converse.session.get('unacked_stanzas') || []).concat([stanza_string])); - - const max_unacked = api.settings.get('smacks_max_unacked_stanzas'); - - if (max_unacked > 0) { - const num = shared_converse.session.get('num_stanzas_since_last_ack') + 1; - - if (num % max_unacked === 0) { - // Request confirmation of sent stanzas - api.send(smacks_utils_u.toStanza(``)); - } - - shared_converse.session.save({ - 'num_stanzas_since_last_ack': num - }); - } - } -} -;// CONCATENATED MODULE: ./src/headless/plugins/smacks/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Converse.js plugin which adds support for XEP-0198: Stream Management - */ - - -const { - Strophe: smacks_Strophe -} = core_converse.env; -smacks_Strophe.addNamespace('SM', 'urn:xmpp:sm:3'); -core_converse.plugins.add('converse-smacks', { - initialize() { - // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - api.settings.extend({ - 'enable_smacks': true, - 'smacks_max_unacked_stanzas': 5 - }); - api.listen.on('afterResourceBinding', sendEnableStanza); - api.listen.on('beforeResourceBinding', enableStreamManagement); - api.listen.on('send', onStanzaSent); - api.listen.on('userSessionInitialized', initSessionData); - } - -}); -;// CONCATENATED MODULE: ./src/headless/plugins/vcard.js -/** - * @module converse-vcard - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - -const { - Strophe: vcard_Strophe, - $iq: vcard_$iq, - dayjs: vcard_dayjs -} = core_converse.env; -const vcard_u = core_converse.env.utils; -core_converse.plugins.add('converse-vcard', { - dependencies: ["converse-status", "converse-roster"], - overrides: { - XMPPStatus: { - getNickname() { - const { - _converse - } = this.__super__; - - const nick = this.__super__.getNickname.apply(this); - - if (!nick && _converse.xmppstatus.vcard) { - return _converse.xmppstatus.vcard.get('nickname'); - } else { - return nick; - } - }, - - getFullname() { - const { - _converse - } = this.__super__; - - const fullname = this.__super__.getFullname.apply(this); - - if (!fullname && _converse.xmppstatus.vcard) { - return _converse.xmppstatus.vcard.get('fullname'); - } else { - return fullname; - } - } - - }, - RosterContact: { - getDisplayName() { - if (!this.get('nickname') && this.vcard) { - return this.vcard.getDisplayName(); - } else { - return this.__super__.getDisplayName.apply(this); - } - }, - - getFullname() { - if (this.vcard) { - return this.vcard.get('fullname'); - } else { - return this.__super__.getFullname.apply(this); - } - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - api.promises.add('VCardsInitialized'); - /** - * Represents a VCard - * @class - * @namespace _converse.VCard - * @memberOf _converse - */ - - shared_converse.VCard = Model.extend({ - defaults: { - 'image': shared_converse.DEFAULT_IMAGE, - 'image_type': shared_converse.DEFAULT_IMAGE_TYPE - }, - - set(key, val, options) { - // Override Model.prototype.set to make sure that the - // default `image` and `image_type` values are maintained. - let attrs; - - if (typeof key === 'object') { - attrs = key; - options = val; - } else { - (attrs = {})[key] = val; - } - - if ('image' in attrs && !attrs['image']) { - attrs['image'] = shared_converse.DEFAULT_IMAGE; - attrs['image_type'] = shared_converse.DEFAULT_IMAGE_TYPE; - return Model.prototype.set.call(this, attrs, options); - } else { - return Model.prototype.set.apply(this, arguments); - } - }, - - getDisplayName() { - return this.get('nickname') || this.get('fullname') || this.get('jid'); - } - - }); - shared_converse.VCards = Collection.extend({ - model: shared_converse.VCard, - - initialize() { - this.on('add', vcard => vcard.get('jid') && api.vcard.update(vcard)); - } - - }); - - async function onVCardData(jid, iq) { - const vcard = iq.querySelector('vCard'); - let result = {}; - - if (vcard !== null) { - var _vcard$querySelector, _vcard$querySelector2, _vcard$querySelector3, _vcard$querySelector4, _vcard$querySelector5, _vcard$querySelector6, _vcard$querySelector7; - - result = { - 'stanza': iq, - 'fullname': (_vcard$querySelector = vcard.querySelector('FN')) === null || _vcard$querySelector === void 0 ? void 0 : _vcard$querySelector.textContent, - 'nickname': (_vcard$querySelector2 = vcard.querySelector('NICKNAME')) === null || _vcard$querySelector2 === void 0 ? void 0 : _vcard$querySelector2.textContent, - 'image': (_vcard$querySelector3 = vcard.querySelector('PHOTO BINVAL')) === null || _vcard$querySelector3 === void 0 ? void 0 : _vcard$querySelector3.textContent, - 'image_type': (_vcard$querySelector4 = vcard.querySelector('PHOTO TYPE')) === null || _vcard$querySelector4 === void 0 ? void 0 : _vcard$querySelector4.textContent, - 'url': (_vcard$querySelector5 = vcard.querySelector('URL')) === null || _vcard$querySelector5 === void 0 ? void 0 : _vcard$querySelector5.textContent, - 'role': (_vcard$querySelector6 = vcard.querySelector('ROLE')) === null || _vcard$querySelector6 === void 0 ? void 0 : _vcard$querySelector6.textContent, - 'email': (_vcard$querySelector7 = vcard.querySelector('EMAIL USERID')) === null || _vcard$querySelector7 === void 0 ? void 0 : _vcard$querySelector7.textContent, - 'vcard_updated': new Date().toISOString(), - 'vcard_error': undefined - }; - } - - if (result.image) { - const buffer = vcard_u.base64ToArrayBuffer(result['image']); - const ab = await crypto.subtle.digest('SHA-1', buffer); - result['image_hash'] = vcard_u.arrayBufferToHex(ab); - } - - return result; - } - - function createStanza(type, jid, vcard_el) { - const iq = vcard_$iq(jid ? { - 'type': type, - 'to': jid - } : { - 'type': type - }); - - if (!vcard_el) { - iq.c("vCard", { - 'xmlns': vcard_Strophe.NS.VCARD - }); - } else { - iq.cnode(vcard_el); - } - - return iq; - } - - async function getVCard(_converse, jid) { - const to = vcard_Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid; - let iq; - - try { - iq = await api.sendIQ(createStanza("get", to)); - } catch (iq) { - return { - 'stanza': iq, - 'jid': jid, - 'vcard_error': new Date().toISOString() - }; - } - - return onVCardData(jid, iq); - } - - async function setVCardOnModel(model) { - let jid; - - if (model instanceof shared_converse.Message) { - if (model.get('type') === 'error') { - return; - } - - jid = model.get('from'); - } else { - jid = model.get('jid'); - } - - await api.waitUntil('VCardsInitialized'); - model.vcard = shared_converse.vcards.findWhere({ - 'jid': jid - }); - - if (!model.vcard) { - model.vcard = shared_converse.vcards.create({ - 'jid': jid - }); - } - - model.vcard.on('change', () => model.trigger('vcard:change')); - } - - function getVCardForChatroomOccupant(message) { - var _message$collection; - - const chatbox = message === null || message === void 0 ? void 0 : (_message$collection = message.collection) === null || _message$collection === void 0 ? void 0 : _message$collection.chatbox; - const nick = vcard_Strophe.getResourceFromJid(message.get('from')); - - if (chatbox && chatbox.get('nick') === nick) { - return shared_converse.xmppstatus.vcard; - } else { - const jid = message.occupant && message.occupant.get('jid') || message.get('from'); - - if (jid) { - return shared_converse.vcards.findWhere({ - jid - }) || shared_converse.vcards.create({ - jid - }); - } else { - headless_log.error(`Could not assign VCard for message because no JID found! msgid: ${message.get('msgid')}`); - return; - } - } - } - - async function setVCardOnMUCMessage(message) { - await api.waitUntil('VCardsInitialized'); - - if (['error', 'info'].includes(message.get('type'))) { - return; - } else { - message.vcard = getVCardForChatroomOccupant(message); - } - } - - shared_converse.initVCardCollection = async function () { - shared_converse.vcards = new shared_converse.VCards(); - const id = `${shared_converse.bare_jid}-converse.vcards`; - initStorage(shared_converse.vcards, id); - await new Promise(resolve => { - shared_converse.vcards.fetch({ - 'success': resolve, - 'error': resolve - }, { - 'silent': true - }); - }); - const vcards = shared_converse.vcards; - - if (shared_converse.session) { - const jid = shared_converse.session.get('bare_jid'); - - shared_converse.xmppstatus.vcard = vcards.findWhere({ - 'jid': jid - }) || vcards.create({ - 'jid': jid - }); - } - /** - * Triggered as soon as the `_converse.vcards` collection has been initialized and populated from cache. - * @event _converse#VCardsInitialized - */ - - - api.trigger('VCardsInitialized'); - }; - - function clearVCardsSession() { - if (shared_converse.shouldClearCache()) { - api.promises.add('VCardsInitialized'); - - if (shared_converse.vcards) { - shared_converse.vcards.clearStore(); - - delete shared_converse.vcards; - } - } - } - /************************ BEGIN Event Handlers ************************/ - - - api.listen.on('chatBoxInitialized', m => setVCardOnModel(m)); - api.listen.on('chatRoomInitialized', m => setVCardOnModel(m)); - api.listen.on('chatRoomMessageInitialized', m => setVCardOnMUCMessage(m)); - api.listen.on('addClientFeatures', () => api.disco.own.features.add(vcard_Strophe.NS.VCARD)); - api.listen.on('clearSession', () => clearVCardsSession()); - api.listen.on('messageInitialized', m => setVCardOnModel(m)); - api.listen.on('rosterContactInitialized', m => setVCardOnModel(m)); - api.listen.on('statusInitialized', shared_converse.initVCardCollection); - /************************ BEGIN API ************************/ - - Object.assign(shared_converse.api, { - /** - * The XEP-0054 VCard API - * - * This API lets you access and update user VCards - * - * @namespace _converse.api.vcard - * @memberOf _converse.api - */ - 'vcard': { - /** - * Enables setting new values for a VCard. - * - * Sends out an IQ stanza to set the user's VCard and if - * successful, it updates the {@link _converse.VCard} - * for the passed in JID. - * - * @method _converse.api.vcard.set - * @param {string} jid The JID for which the VCard should be set - * @param {object} data A map of VCard keys and values - * @example - * _converse.api.vcard.set({ - * 'jid': _converse.bare_jid, - * 'fn': 'John Doe', - * 'nickname': 'jdoe' - * }).then(() => { - * // Succes - * }).catch(() => { - * // Failure - * }). - */ - async set(jid, data) { - if (!jid) { - throw Error("No jid provided for the VCard data"); - } - - const div = document.createElement('div'); - const vcard_el = vcard_u.toStanza(` - - ${data.fn} - ${data.nickname} - ${data.url} - ${data.role} - ${data.email} - - ${data.image_type} - ${data.image} - - `, div); - let result; - - try { - result = await api.sendIQ(createStanza("set", jid, vcard_el)); - } catch (e) { - throw e; - } - - await api.vcard.update(jid, true); - return result; - }, - - /** - * @method _converse.api.vcard.get - * @param {Model|string} model Either a `Model` instance, or a string JID. - * If a `Model` instance is passed in, then it must have either a `jid` - * attribute or a `muc_jid` attribute. - * @param {boolean} [force] A boolean indicating whether the vcard should be - * fetched even if it's been fetched before. - * @returns {promise} A Promise which resolves with the VCard data for a particular JID or for - * a `Model` instance which represents an entity with a JID (such as a roster contact, - * chat or chatroom occupant). - * - * @example - * _converse.api.waitUntil('rosterContactsFetched').then(() => { - * _converse.api.vcard.get('someone@example.org').then( - * (vcard) => { - * // Do something with the vcard... - * } - * ); - * }); - */ - get(model, force) { - if (typeof model === 'string') { - return getVCard(shared_converse, model); - } else if (force || !model.get('vcard_updated') || !vcard_dayjs(model.get('vcard_error')).isSame(new Date(), "day")) { - const jid = model.get('jid'); - - if (!jid) { - headless_log.error("No JID to get vcard for"); - } - - return getVCard(shared_converse, jid); - } else { - return Promise.resolve({}); - } - }, - - /** - * Fetches the VCard associated with a particular `Model` instance - * (by using its `jid` or `muc_jid` attribute) and then updates the model with the - * returned VCard data. - * - * @method _converse.api.vcard.update - * @param {Model} model A `Model` instance - * @param {boolean} [force] A boolean indicating whether the vcard should be - * fetched again even if it's been fetched before. - * @returns {promise} A promise which resolves once the update has completed. - * @example - * _converse.api.waitUntil('rosterContactsFetched').then(async () => { - * const chatbox = await _converse.chatboxes.getChatBox('someone@example.org'); - * _converse.api.vcard.update(chatbox); - * }); - */ - async update(model, force) { - const data = await this.get(model, force); - model = typeof model === 'string' ? shared_converse.vcards.findWhere({ - 'jid': model - }) : model; - - if (!model) { - headless_log.error(`Could not find a VCard model for ${model}`); - return; - } - - delete data['stanza']; - model.save(data); - } - - } - }); - } - -}); -;// CONCATENATED MODULE: ./src/headless/headless.js -/* START: Removable components - * -------------------- - * Any of the following components may be removed if they're not needed. - */ - // XEP-0050 Ad Hoc Commands - - // XEP-0199 XMPP Ping - - // XEP-0206 BOSH - - // XEP-0115 Entity Capabilities - - // XEP-0280 Message Carbons - - // RFC-6121 Instant messaging - - - // XEP-0030 Service discovery - - // Support for headline messages - - // XEP-0313 Message Archive Management - - // XEP-0045 Multi-user chat - - // XEP-0199 XMPP Ping - - // XEP-0060 Pubsub - - // RFC-6121 Contacts Roster - - // XEP-0198 Stream Management - - - // XEP-0054 VCard-temp - -/* END: Removable components */ - - -/* harmony default export */ const headless = ((/* unused pure expression or super */ null && (converse))); -;// CONCATENATED MODULE: ./src/shared/registry.js - -const registry = {}; - -function registry_define(name, constructor) { - this.registry[name] = constructor; -} - -function register() { - Object.keys(registry).forEach(name => { - if (!customElements.get(name)) { - customElements.define(name, registry[name]); - } - }); -} - -api.elements = { - registry, - define: registry_define, - register -}; -;// CONCATENATED MODULE: ./src/shared/components/element.js - - -class CustomElement extends lit_element_h { - constructor() { - super(); - Object.assign(this, Events); - } - - createRenderRoot() { - // Render without the shadow DOM - return this; - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.stopListening(); - } - -} -;// CONCATENATED MODULE: ./src/shared/constants.js -// These are all the view-layer plugins. -// -// For the full Converse build, this list serves -// as a whitelist (see src/converse.js) in addition to the -// CORE_PLUGINS list in src/headless/consts.js. -const VIEW_PLUGINS = ['converse-bookmark-views', 'converse-chatboxviews', 'converse-chatview', 'converse-controlbox', 'converse-dragresize', 'converse-fullscreen', 'converse-headlines-view', 'converse-mam-views', 'converse-minimize', 'converse-modal', 'converse-muc-views', 'converse-notification', 'converse-omemo', 'converse-profile', 'converse-push', 'converse-register', 'converse-roomslist', 'converse-rootview', 'converse-rosterview', 'converse-singleton']; -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js -var injectStylesIntoStyleTag = __webpack_require__(3379); -var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleDomAPI.js -var styleDomAPI = __webpack_require__(7795); -var styleDomAPI_default = /*#__PURE__*/__webpack_require__.n(styleDomAPI); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertBySelector.js -var insertBySelector = __webpack_require__(569); -var insertBySelector_default = /*#__PURE__*/__webpack_require__.n(insertBySelector); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js -var setAttributesWithoutAttributes = __webpack_require__(3565); -var setAttributesWithoutAttributes_default = /*#__PURE__*/__webpack_require__.n(setAttributesWithoutAttributes); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/insertStyleElement.js -var insertStyleElement = __webpack_require__(9216); -var insertStyleElement_default = /*#__PURE__*/__webpack_require__.n(insertStyleElement); -// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/styleTagTransform.js -var styleTagTransform = __webpack_require__(4589); -var styleTagTransform_default = /*#__PURE__*/__webpack_require__.n(styleTagTransform); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/styles/index.scss -var styles = __webpack_require__(4452); -;// CONCATENATED MODULE: ./src/shared/styles/index.scss - - - - - - - - - - - -var options = {}; - -options.styleTagTransform = (styleTagTransform_default()); -options.setAttributes = (setAttributesWithoutAttributes_default()); - - options.insert = insertBySelector_default().bind(null, "head"); - -options.domAPI = (styleDomAPI_default()); -options.insertStyleElement = (insertStyleElement_default()); - -var update = injectStylesIntoStyleTag_default()(styles/* default */.Z, options); - - - - - /* harmony default export */ const shared_styles = (styles/* default */.Z && styles/* default.locals */.Z.locals ? styles/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/templates/form.js - - -/* harmony default export */ const templates_form = (o => { - const i18n_heading = __('Bookmark this groupchat'); - - const i18n_autojoin = __('Would you like this groupchat to be automatically joined upon startup?'); - - const i18n_cancel = __('Cancel'); - - const i18n_name = __('The name for this bookmark:'); - - const i18n_nick = __('What should your nickname for this groupchat be?'); - - const i18n_submit = __('Save'); - - return T` -
- ${i18n_heading} -
- - -
-
- - -
-
- - -
-
- - -
-
- `; -}); -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/form.js - - - - -class MUCBookmarkForm extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - } - - render() { - return templates_form(Object.assign(this.model.toJSON(), { - 'onCancel': ev => this.closeBookmarkForm(ev), - 'onSubmit': ev => this.onBookmarkFormSubmitted(ev) - })); - } - - onBookmarkFormSubmitted(ev) { - var _ev$target$querySelec, _ev$target$querySelec2, _ev$target$querySelec3; - - ev.preventDefault(); - - shared_converse.bookmarks.createBookmark({ - 'jid': this.model.get('jid'), - 'autojoin': ((_ev$target$querySelec = ev.target.querySelector('input[name="autojoin"]')) === null || _ev$target$querySelec === void 0 ? void 0 : _ev$target$querySelec.checked) || false, - 'name': (_ev$target$querySelec2 = ev.target.querySelector('input[name=name]')) === null || _ev$target$querySelec2 === void 0 ? void 0 : _ev$target$querySelec2.value, - 'nick': (_ev$target$querySelec3 = ev.target.querySelector('input[name=nick]')) === null || _ev$target$querySelec3 === void 0 ? void 0 : _ev$target$querySelec3.value - }); - - this.closeBookmarkForm(ev); - } - - closeBookmarkForm(ev) { - ev.preventDefault(); - this.model.session.save('view', null); - } - -} - -api.elements.define('converse-muc-bookmark-form', MUCBookmarkForm); -/* harmony default export */ const bookmark_views_form = (MUCBookmarkForm); -;// CONCATENATED MODULE: ./node_modules/lodash-es/invokeMap.js - - - - - -/** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `path` is a function, it's invoked - * for, and `this` bound to, each element in `collection`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {...*} [args] The arguments to invoke each method with. - * @returns {Array} Returns the array of results. - * @example - * - * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invokeMap([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] - */ - -var invokeMap = _baseRest(function (collection, path, args) { - var index = -1, - isFunc = typeof path == 'function', - result = lodash_es_isArrayLike(collection) ? Array(collection.length) : []; - _baseEach(collection, function (value) { - result[++index] = isFunc ? _apply(path, value, args) : _baseInvoke(value, path, args); - }); - return result; -}); -/* harmony default export */ const lodash_es_invokeMap = (invokeMap); -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/utils.js - - - - - -function getHeadingButtons(view, buttons) { - if (shared_converse.allow_bookmarks && view.model.get('type') === shared_converse.CHATROOMS_TYPE) { - const bookmarked = view.model.get('bookmarked'); - const data = { - 'i18n_title': bookmarked ? __('Unbookmark this groupchat') : __('Bookmark this groupchat'), - 'i18n_text': bookmarked ? __('Unbookmark') : __('Bookmark'), - 'handler': ev => view.toggleBookmark(ev), - 'a_class': 'toggle-bookmark', - 'icon_class': 'fa-bookmark', - 'name': 'bookmark' - }; - const names = buttons.map(t => t.name); - const idx = names.indexOf('details'); - const data_promise = checkBookmarksSupport().then(s => s ? data : ''); - return idx > -1 ? [...buttons.slice(0, idx), data_promise, ...buttons.slice(idx)] : [data_promise, ...buttons]; - } - - return buttons; -} -function removeBookmarkViaEvent(ev) { - ev.preventDefault(); - const name = ev.target.getAttribute('data-bookmark-name'); - const jid = ev.target.getAttribute('data-room-jid'); - - if (confirm(__('Are you sure you want to remove the bookmark "%1$s"?', name))) { - lodash_es_invokeMap(shared_converse.bookmarks.where({ - jid - }), Model.prototype.destroy); - } -} -async function addBookmarkViaEvent(ev) { - ev.preventDefault(); - const jid = ev.target.getAttribute('data-room-jid'); - const room = await api.rooms.open(jid, { - 'bring_to_foreground': true - }); - room.session.save('view', core_converse.MUC.VIEWS.BOOKMARK); -} -function openRoomViaEvent(ev) { - ev.preventDefault(); - const { - Strophe - } = core_converse.env; - const name = ev.target.textContent; - const jid = ev.target.getAttribute('data-room-jid'); - const data = { - 'name': name || Strophe.unescapeNode(Strophe.getNodeFromJid(jid)) || jid - }; - api.rooms.open(jid, data, true); -} -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/templates/item.js - - - - -/* harmony default export */ const item = (o => { - const jid = o.bm.get('jid'); - const is_hidden = !!(api.settings.get('hide_open_bookmarks') && shared_converse.chatboxes.get(jid)); - - const info_remove_bookmark = __('Unbookmark this groupchat'); - - const open_title = __('Click to open this groupchat'); - - return T` -
- `; -}); -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/templates/list.js - - - - -/* harmony default export */ const list = (o => { - const is_collapsed = shared_converse.bookmarks.getUnopenedBookmarks().length ? true : false; - - const desc_bookmarks = __('Click to toggle the bookmarks list'); - - const label_bookmarks = __('Bookmarks'); - - return T` -
- - - - ${label_bookmarks} -
- ${shared_converse.bookmarks.map(bm => item(Object.assign({ - bm - }, o)))} -
-
- `; -}); -;// CONCATENATED MODULE: ./node_modules/@converse/skeletor/src/element.js -function element_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - - - - -const paddedLt = /^\s* this.render(), - 'error': (model, err) => { - headless_log.error(err); - this.render(); - } - }); - } - - render() { - V(list({ - 'toggleBookmarksList': ev => this.toggleBookmarksList(ev), - 'toggle_state': this.model.get('toggle-state') - }), this); - } - - toggleBookmarksList(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - const icon_el = ev.target.matches('.fa') ? ev.target : ev.target.querySelector('.fa'); - - if (bookmarks_list_u.hasClass('fa-caret-down', icon_el)) { - bookmarks_list_u.slideIn(this.querySelector('.bookmarks')); - this.model.save({ - 'toggle-state': shared_converse.CLOSED - }); - icon_el.classList.remove('fa-caret-down'); - icon_el.classList.add('fa-caret-right'); - } else { - icon_el.classList.remove('fa-caret-right'); - icon_el.classList.add('fa-caret-down'); - bookmarks_list_u.slideOut(this.querySelector('.bookmarks')); - this.model.save({ - 'toggle-state': shared_converse.OPENED - }); - } - } - -} -api.elements.define('converse-bookmarks', BookmarksView); -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/mixins.js - -const { - u: mixins_u -} = core_converse.env; -const bookmarkableChatRoomView = { - /** - * Set whether the groupchat is bookmarked or not. - * @private - */ - setBookmarkState() { - if (shared_converse.bookmarks !== undefined) { - const models = shared_converse.bookmarks.where({ - 'jid': this.model.get('jid') - }); - - if (!models.length) { - this.model.save('bookmarked', false); - } else { - this.model.save('bookmarked', true); - } - } - }, - - renderBookmarkForm() { - if (!this.bookmark_form) { - this.bookmark_form = new shared_converse.MUCBookmarkForm({ - 'model': this.model, - 'chatroomview': this - }); - const container_el = this.querySelector('.chatroom-body'); - container_el.insertAdjacentElement('beforeend', this.bookmark_form.el); - } - - mixins_u.showElement(this.bookmark_form.el); - }, - - toggleBookmark(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - - const models = shared_converse.bookmarks.where({ - 'jid': this.model.get('jid') - }); - - if (!models.length) { - this.model.session.set('view', core_converse.MUC.VIEWS.BOOKMARK); - } else { - models.forEach(model => model.destroy()); - } - } - -}; -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/bookmark-views/styles/bookmarks.scss -var bookmarks = __webpack_require__(3742); -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/styles/bookmarks.scss - - - - - - - - - - - -var bookmarks_options = {}; - -bookmarks_options.styleTagTransform = (styleTagTransform_default()); -bookmarks_options.setAttributes = (setAttributesWithoutAttributes_default()); - - bookmarks_options.insert = insertBySelector_default().bind(null, "head"); - -bookmarks_options.domAPI = (styleDomAPI_default()); -bookmarks_options.insertStyleElement = (insertStyleElement_default()); - -var bookmarks_update = injectStylesIntoStyleTag_default()(bookmarks/* default */.Z, bookmarks_options); - - - - - /* harmony default export */ const styles_bookmarks = (bookmarks/* default */.Z && bookmarks/* default.locals */.Z.locals ? bookmarks/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/bookmark-views/index.js -/** - * @description Converse.js plugin which adds views for XEP-0048 bookmarks - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - -core_converse.plugins.add('converse-bookmark-views', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - */ - dependencies: ['converse-chatboxes', 'converse-muc', 'converse-muc-views'], - - initialize() { - // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - api.settings.extend({ - hide_open_bookmarks: true - }); - shared_converse.removeBookmarkViaEvent = removeBookmarkViaEvent; - shared_converse.addBookmarkViaEvent = addBookmarkViaEvent; - Object.assign(shared_converse.ChatRoomView.prototype, bookmarkableChatRoomView); - shared_converse.MUCBookmarkForm = bookmark_views_form; - shared_converse.BookmarksView = BookmarksView; - api.listen.on('getHeadingButtons', getHeadingButtons); - api.listen.on('chatRoomViewInitialized', view => view.setBookmarkState()); - } - -}); -;// CONCATENATED MODULE: ./src/templates/background_logo.js - - -/* harmony default export */ const background_logo = (() => T` -
-
-
- - Logo Converse - - - - - - - - - - - - - - - - - - - - - - - - - - - - converse.js - - -
- ${api.settings.get('view_mode') === 'overlayed' ? T`
` : ''} -
`); -;// CONCATENATED MODULE: ./node_modules/lit-html/directive.js -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const directive_t = { - ATTRIBUTE: 1, - CHILD: 2, - PROPERTY: 3, - BOOLEAN_ATTRIBUTE: 4, - EVENT: 5, - ELEMENT: 6 -}, - directive_i = t => (...i) => ({ - _$litDirective$: t, - values: i -}); - -class directive_s { - constructor(t) {} - - T(t, i, s) { - this.Σdt = t, this.M = i, this.Σct = s; - } - - S(t, i) { - return this.update(t, i); - } - - update(t, i) { - return this.render(...i); - } - -} - - -;// CONCATENATED MODULE: ./node_modules/lit-html/directive-helpers.js - -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -const { - et: directive_helpers_t -} = Z, - directive_helpers_i = o => null === o || "object" != typeof o && "function" != typeof o, - directive_helpers_n = { - HTML: 1, - SVG: 2 -}, - directive_helpers_v = (o, t) => { - var i, n; - return void 0 === t ? void 0 !== (null === (i = o) || void 0 === i ? void 0 : i._$litType$) : (null === (n = o) || void 0 === n ? void 0 : n._$litType$) === t; -}, - directive_helpers_l = o => { - var t; - return void 0 !== (null === (t = o) || void 0 === t ? void 0 : t._$litDirective$); -}, - directive_helpers_r = o => { - var t; - return null === (t = o) || void 0 === t ? void 0 : t._$litDirective$; -}, - directive_helpers_d = o => void 0 === o.strings, - directive_helpers_e = () => document.createComment(""), - directive_helpers_u = (o, i, n) => { - var v; - const l = o.A.parentNode, - r = void 0 === i ? o.B : i.A; - - if (void 0 === n) { - const i = l.insertBefore(directive_helpers_e(), r), - v = l.insertBefore(directive_helpers_e(), r); - n = new directive_helpers_t(i, v, o, o.options); - } else { - const t = n.B.nextSibling, - i = n.M !== o; - - if (i && (null === (v = n.Q) || void 0 === v || v.call(n, o), n.M = o), t !== r || i) { - let o = n.A; - - for (; o !== t;) { - const t = o.nextSibling; - l.insertBefore(o, r), o = t; - } - } - } - - return n; -}, - directive_helpers_c = (o, t, i = o) => (o.I(t, i), o), - directive_helpers_s = {}, - directive_helpers_f = (o, t = directive_helpers_s) => o.H = t, - directive_helpers_a = o => o.H, - directive_helpers_m = o => { - var t; - null === (t = o.P) || void 0 === t || t.call(o, !1, !0); - let i = o.A; - const n = o.B.nextSibling; - - for (; i !== n;) { - const o = i.nextSibling; - i.remove(), i = o; - } -}, - directive_helpers_p = o => { - o.R(); -}; - - -;// CONCATENATED MODULE: ./node_modules/lit-html/directives/repeat.js - - - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -const repeat_u = (e, s, t) => { - const r = new Map(); - - for (let l = s; l <= t; l++) r.set(e[l], l); - - return r; -}, - repeat_c = directive_i(class extends directive_s { - constructor(e) { - if (super(e), e.type !== directive_t.CHILD) throw Error("repeat() can only be used in text expressions"); - } - - Mt(e, s, t) { - let r; - void 0 === t ? t = s : void 0 !== s && (r = s); - const l = [], - o = []; - let i = 0; - - for (const s of e) l[i] = r ? r(s, i) : i, o[i] = t(s, i), i++; - - return { - values: o, - keys: l - }; - } - - render(e, s, t) { - return this.Mt(e, s, t).values; - } - - update(s, [t, r, c]) { - var d; - const p = directive_helpers_a(s), - { - values: v, - keys: a - } = this.Mt(t, r, c); - if (!p) return this.Pt = a, v; - const h = null !== (d = this.Pt) && void 0 !== d ? d : this.Pt = [], - m = []; - let x, - y, - j = 0, - k = p.length - 1, - w = 0, - b = v.length - 1; - - for (; j <= k && w <= b;) if (null === p[j]) j++;else if (null === p[k]) k--;else if (h[j] === a[w]) m[w] = directive_helpers_c(p[j], v[w]), j++, w++;else if (h[k] === a[b]) m[b] = directive_helpers_c(p[k], v[b]), k--, b--;else if (h[j] === a[b]) m[b] = directive_helpers_c(p[j], v[b]), directive_helpers_u(s, m[b + 1], p[j]), j++, b--;else if (h[k] === a[w]) m[w] = directive_helpers_c(p[k], v[w]), directive_helpers_u(s, p[j], p[k]), k--, w++;else if (void 0 === x && (x = repeat_u(a, w, b), y = repeat_u(h, j, k)), x.has(h[j])) { - if (x.has(h[k])) { - const e = y.get(a[w]), - t = void 0 !== e ? p[e] : null; - - if (null === t) { - const e = directive_helpers_u(s, p[j]); - directive_helpers_c(e, v[w]), m[w] = e; - } else m[w] = directive_helpers_c(t, v[w]), directive_helpers_u(s, p[j], t), p[e] = null; - - w++; - } else directive_helpers_m(p[k]), k--; - } else directive_helpers_m(p[j]), j++; - - for (; w <= b;) { - const e = directive_helpers_u(s, m[b + 1]); - directive_helpers_c(e, v[w]), m[w++] = e; - } - - for (; j <= k;) { - const e = p[j++]; - null !== e && directive_helpers_m(e); - } - - return this.Pt = a, directive_helpers_f(s, m), lit_html_w; - } - -}); - - -;// CONCATENATED MODULE: ./node_modules/lit/directives/repeat.js - -;// CONCATENATED MODULE: ./src/plugins/chatboxviews/templates/chats.js - - - - -function shouldShowChat(c) { - const { - CONTROLBOX_TYPE - } = shared_converse; - const is_minimized = api.settings.get('view_mode') === 'overlayed' && c.get('minimized'); - return c.get('type') === CONTROLBOX_TYPE || !(c.get('hidden') || is_minimized); -} - -/* harmony default export */ const chats = (() => { - const { - chatboxes, - CONTROLBOX_TYPE, - CHATROOMS_TYPE, - HEADLINES_TYPE - } = shared_converse; - const view_mode = api.settings.get('view_mode'); - const connection = shared_converse === null || shared_converse === void 0 ? void 0 : shared_converse.connection; - const logged_out = !(connection !== null && connection !== void 0 && connection.connected) || !(connection !== null && connection !== void 0 && connection.authenticated) || (connection === null || connection === void 0 ? void 0 : connection.disconnecting); - return T` - ${!logged_out && view_mode === 'overlayed' ? T`` : ''} - ${repeat_c(chatboxes.filter(shouldShowChat), m => m.get('jid'), m => { - if (m.get('type') === CONTROLBOX_TYPE) { - return T` - ${view_mode === 'overlayed' ? T`` : ''} - - `; - } else if (m.get('type') === CHATROOMS_TYPE) { - return T` - - `; - } else if (m.get('type') === HEADLINES_TYPE) { - return T` - - `; - } else { - return T` - - `; - } - })} - `; -}); -;// CONCATENATED MODULE: ./src/plugins/chatboxviews/view.js - - - - - - -class ConverseChats extends ElementView { - initialize() { - this.model = shared_converse.chatboxes; - this.listenTo(this.model, 'add', this.render); - this.listenTo(this.model, 'change:closed', this.render); - this.listenTo(this.model, 'change:hidden', this.render); - this.listenTo(this.model, 'change:jid', this.render); - this.listenTo(this.model, 'change:minimized', this.render); - this.listenTo(this.model, 'destroy', this.render); // Use listenTo instead of api.listen.to so that event handlers - // automatically get deregistered when the component is dismounted - - this.listenTo(shared_converse, 'connected', this.render); - this.listenTo(shared_converse, 'reconnected', this.render); - this.listenTo(shared_converse, 'disconnected', this.render); - const bg = document.getElementById('conversejs-bg'); - - if (bg && !bg.innerHTML.trim()) { - V(background_logo(), bg); - } - - const body = document.querySelector('body'); - body.classList.add(`converse-${api.settings.get('view_mode')}`); - this.render(); - /** - * Triggered once the _converse.ChatBoxViews view-colleciton has been initialized - * @event _converse#chatBoxViewsInitialized - * @example _converse.api.listen.on('chatBoxViewsInitialized', () => { ... }); - */ - - api.trigger('chatBoxViewsInitialized'); - } - - render() { - V(chats(), this); - } - -} - -api.elements.define('converse-chats', ConverseChats); -;// CONCATENATED MODULE: ./src/plugins/chatboxviews/container.js -class ChatBoxViews { - constructor() { - this.views = {}; - } - - add(key, val) { - this.views[key] = val; - } - - get(key) { - return this.views[key]; - } - - getAll() { - return Object.values(this.views); - } - - keys() { - return Object.keys(this.views); - } - - remove(key) { - delete this.views[key]; - } - - map(f) { - return Object.values(this.views).map(f); - } - - forEach(f) { - return Object.values(this.views).forEach(f); - } - - filter(f) { - return Object.values(this.views).filter(f); - } - - closeAllChatBoxes() { - return Promise.all(Object.values(this.views).map(v => v.close({ - 'name': 'closeAllChatBoxes' - }))); - } - -} - -/* harmony default export */ const container = (ChatBoxViews); -;// CONCATENATED MODULE: ./src/shared/templates/avatar.js - - -const getImgHref = (image, image_type) => { - return image.startsWith('data:') ? image : `data:${image_type};base64,${image}`; -}; - -/* harmony default export */ const avatar = (o => { - if (o.image) { - return T` - - - `; - } else { - return ''; - } -}); -;// CONCATENATED MODULE: ./node_modules/@converse/skeletor/src/view.js -// Backbone.js 1.4.0 -// (c) 2010-2019 Jeremy Ashkenas and DocumentCloud -// Backbone may be freely distributed under the MIT license. -// View -// ---- -// Views are almost more convention than they are actual code. A View -// is simply a JavaScript object that represents a logical chunk of UI in the -// DOM. This might be a single item, an entire list, a sidebar or panel, or -// even the surrounding frame which wraps your whole app. Defining a chunk of -// UI as a **View** allows you to define your DOM events declaratively, without -// having to worry about render order ... and makes it easy for the view to -// react to specific changes in the state of your models. - - - - - - - - - -const view_paddedLt = /^\s* { - shared_converse.chatboxes.on('destroy', m => shared_converse.chatboxviews.remove(m.get('jid'))); - }); - api.listen.on('cleanup', () => delete shared_converse.chatboxviews); - api.listen.on('clearSession', () => shared_converse.chatboxviews.closeAllChatBoxes()); - api.listen.on('chatBoxViewsInitialized', calculateViewportHeightUnit); - window.addEventListener('resize', calculateViewportHeightUnit); - /************************ END Event Handlers ************************/ - - Object.assign(core_converse, { - /** - * Public API method which will ensure that the #conversejs element - * is inserted into a container element. - * - * This method is useful when the #conversejs element has been - * detached from the DOM somehow. - * @async - * @memberOf converse - * @method insertInto - * @example - * converse.insertInto(document.querySelector('#converse-container')); - */ - insertInto(container) { - var _converse$chatboxview; - - const el = (_converse$chatboxview = shared_converse.chatboxviews) === null || _converse$chatboxview === void 0 ? void 0 : _converse$chatboxview.el; - - if (el && !container.contains(el)) { - container.insertAdjacentElement('afterBegin', el); - } else if (!el) { - throw new Error('Cannot insert non-existing #conversejs element into the DOM'); - } - } - - }); - } - -}); -// EXTERNAL MODULE: ./node_modules/bootstrap.native/dist/bootstrap-native.js -var bootstrap_native = __webpack_require__(6434); -var bootstrap_native_default = /*#__PURE__*/__webpack_require__.n(bootstrap_native); -;// CONCATENATED MODULE: ./src/templates/alert.js - -/* harmony default export */ const templates_alert = (o => T``); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/modals/styles/_modal.scss -var _modal = __webpack_require__(6300); -;// CONCATENATED MODULE: ./src/modals/styles/_modal.scss - - - - - - - - - - - -var _modal_options = {}; - -_modal_options.styleTagTransform = (styleTagTransform_default()); -_modal_options.setAttributes = (setAttributesWithoutAttributes_default()); - - _modal_options.insert = insertBySelector_default().bind(null, "head"); - -_modal_options.domAPI = (styleDomAPI_default()); -_modal_options.insertStyleElement = (insertStyleElement_default()); - -var _modal_update = injectStylesIntoStyleTag_default()(_modal/* default */.Z, _modal_options); - - - - - /* harmony default export */ const styles_modal = (_modal/* default */.Z && _modal/* default.locals */.Z.locals ? _modal/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/modals/base.js - - - - - - - -const { - sizzle: base_sizzle -} = core_converse.env; -const base_u = core_converse.env.utils; -const BaseModal = View.extend({ - className: "modal", - persistent: false, - // Whether this modal should persist in the DOM once it's been closed - events: { - 'click .nav-item .nav-link': 'switchTab' - }, - - initialize(options) { - if (!this.id) { - throw new Error("Each modal class must have a unique id attribute"); - } // Allow properties to be set via passed in options - - - Object.assign(this, options); - this.render(); - this.el.setAttribute('tabindex', '-1'); - this.el.setAttribute('role', 'dialog'); - this.el.setAttribute('aria-hidden', 'true'); - const label_id = this.el.querySelector('.modal-title').getAttribute('id'); - label_id && this.el.setAttribute('aria-labelledby', label_id); - this.insertIntoDOM(); - const Modal = (bootstrap_native_default()).Modal; - this.modal = new Modal(this.el, { - backdrop: true, - keyboard: true - }); - this.el.addEventListener('hide.bs.modal', () => this.onHide(), false); - }, - - onHide() { - base_u.removeClass('selected', this.trigger_el); - !this.persistent && api.modal.remove(this); - }, - - insertIntoDOM() { - const container_el = document.querySelector("#converse-modals"); - container_el.insertAdjacentElement('beforeEnd', this.el); - }, - - switchTab(ev) { - ev.stopPropagation(); - ev.preventDefault(); - base_sizzle('.nav-link.active', this.el).forEach(el => { - base_u.removeClass('active', this.el.querySelector(el.getAttribute('href'))); - base_u.removeClass('active', el); - }); - base_u.addClass('active', ev.target); - base_u.addClass('active', this.el.querySelector(ev.target.getAttribute('href'))); - }, - - alert(message, type = 'primary') { - const body = this.el.querySelector('.modal-alert'); - - if (body === null) { - headless_log.error("Could not find a .modal-alert element in the modal to show an alert message in!"); - return; - } // FIXME: Instead of adding the alert imperatively, we should - // find a way to let the modal rerender with an alert message - - - V(templates_alert({ - 'type': `alert-${type}`, - 'message': message - }), body); - const el = body.firstElementChild; - setTimeout(() => { - base_u.addClass('fade-out', el); - setTimeout(() => base_u.removeElement(el), 600); - }, 5000); - }, - - show(ev) { - if (ev) { - ev.preventDefault(); - this.trigger_el = ev.target; - !base_u.hasClass('chat-image', this.trigger_el) && base_u.addClass('selected', this.trigger_el); - } - - this.modal.show(); - } - -}); -/* harmony default export */ const base = (BaseModal); -;// CONCATENATED MODULE: ./src/modals/templates/buttons.js - - -const modal_close_button = T``; -const modal_header_close_button = T``; -;// CONCATENATED MODULE: ./src/modals/templates/alert.js - - -/* harmony default export */ const modals_templates_alert = (o => T` - -`); -;// CONCATENATED MODULE: ./src/modals/alert.js - - - -const Alert = base.extend({ - id: 'alert-modal', - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change', this.render); - }, - - toHTML() { - return modals_templates_alert(Object.assign({ - __: __ - }, this.model.toJSON())); - } - -}); -/* harmony default export */ const modals_alert = (Alert); -;// CONCATENATED MODULE: ./src/modals/templates/prompt.js - - - -const tpl_field = f => T` -
- -
-`; - -/* harmony default export */ const templates_prompt = (o => T` - -`); -;// CONCATENATED MODULE: ./src/modals/confirm.js - - - -const Confirm = base.extend({ - id: 'confirm-modal', - events: { - 'submit .confirm': 'onConfimation' - }, - - initialize() { - this.confirmation = getOpenPromise(); - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change', this.render); - this.el.addEventListener('closed.bs.modal', () => this.confirmation.reject(), false); - }, - - toHTML() { - return templates_prompt(this.model.toJSON()); - }, - - afterRender() { - if (!this.close_handler_registered) { - this.el.addEventListener('closed.bs.modal', () => { - if (!this.confirmation.isResolved) { - this.confirmation.reject(); - } - }, false); - this.close_handler_registered = true; - } - }, - - onConfimation(ev) { - ev.preventDefault(); - const form_data = new FormData(ev.target); - const fields = (this.model.get('fields') || []).map(field => { - const value = form_data.get(field.name).trim(); - field.value = value; - - if (field.challenge) { - field.challenge_failed = value !== field.challenge; - } - - return field; - }); - - if (fields.filter(c => c.challenge_failed).length) { - this.model.set('fields', fields); // Setting an array doesn't trigger a change event - - this.model.trigger('change'); - return; - } - - this.confirmation.resolve(fields); - this.modal.hide(); - } - -}); -/* harmony default export */ const modals_confirm = (Confirm); -;// CONCATENATED MODULE: ./src/plugins/modal.js -/** - * @module converse-modal - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - -core_converse.env.BootstrapModal = base; // expose to plugins - -let modals = []; -const modal_api = { - /** - * API namespace for methods relating to modals - * @namespace _converse.api.modal - * @memberOf _converse.api - */ - modal: { - /** - * Shows a modal of type `ModalClass` to the user. - * Will create a new instance of that class if an existing one isn't - * found. - * @param { Class } ModalClass - * @param { Object } [properties] - Optional properties that will be - * set on a newly created modal instance (if no pre-existing modal was - * found). - * @param { Event } [event] - The DOM event that causes the modal to be shown. - */ - show(ModalClass, properties, ev) { - const modal = this.get(ModalClass.id) || this.create(ModalClass, properties); - modal.show(ev); - return modal; - }, - - /** - * Return a modal with the passed-in identifier, if it exists. - * @param { String } id - */ - get(id) { - return modals.filter(m => m.id == id).pop(); - }, - - /** - * Create a modal of the passed-in type. - * @param { Class } ModalClass - * @param { Object } [properties] - Optional properties that will be - * set on the modal instance. - */ - create(ModalClass, properties) { - const modal = new ModalClass(properties); - modals.push(modal); - return modal; - }, - - /** - * Remove a particular modal - * @param { View } modal - */ - remove(modal) { - modals = modals.filter(m => m !== modal); - modal.remove(); - }, - - /** - * Remove all modals - */ - removeAll() { - modals.forEach(m => m.remove()); - modals = []; - } - - }, - - /** - * Show a confirm modal to the user. - * @method _converse.api.confirm - * @param { String } title - The header text for the confirmation dialog - * @param { (String[]|String) } messages - The text to show to the user - * @param { Array } fields - An object representing a fields presented to the user. - * @property { String } Field.label - The form label for the input field. - * @property { String } Field.name - The name for the input field. - * @property { String } [Field.challenge] - A challenge value that must be provided by the user. - * @property { String } [Field.placeholder] - The placeholder for the input field. - * @property { Boolean} [Field.required] - Whether the field is required or not - * @returns { Promise } A promise which resolves with an array of - * filled in fields or `false` if the confirm dialog was closed or canceled. - */ - async confirm(title, messages = [], fields = []) { - if (typeof messages === 'string') { - messages = [messages]; - } - - const model = new Model({ - title, - messages, - fields, - 'type': 'confirm' - }); - const confirm = new modals_confirm({ - model - }); - confirm.show(); - let result; - - try { - result = await confirm.confirmation; - } catch (e) { - result = false; - } - - confirm.remove(); - return result; - }, - - /** - * Show a prompt modal to the user. - * @method _converse.api.prompt - * @param { String } title - The header text for the prompt - * @param { (String[]|String) } messages - The prompt text to show to the user - * @param { String } placeholder - The placeholder text for the prompt input - * @returns { Promise } A promise which resolves with the text provided by the - * user or `false` if the user canceled the prompt. - */ - async prompt(title, messages = [], placeholder = '') { - if (typeof messages === 'string') { - messages = [messages]; - } - - const model = new Model({ - title, - messages, - 'fields': [{ - 'name': 'reason', - 'placeholder': placeholder - }], - 'type': 'prompt' - }); - const prompt = new modals_confirm({ - model - }); - prompt.show(); - let result; - - try { - var _await$prompt$confirm; - - result = (_await$prompt$confirm = (await prompt.confirmation).pop()) === null || _await$prompt$confirm === void 0 ? void 0 : _await$prompt$confirm.value; - } catch (e) { - result = false; - } - - prompt.remove(); - return result; - }, - - /** - * Show an alert modal to the user. - * @method _converse.api.alert - * @param { ('info'|'warn'|'error') } type - The type of alert. - * @param { String } title - The header text for the alert. - * @param { (String[]|String) } messages - The alert text to show to the user. - */ - alert(type, title, messages) { - if (typeof messages === 'string') { - messages = [messages]; - } - - let level; - - if (type === 'error') { - level = 'alert-danger'; - } else if (type === 'info') { - level = 'alert-info'; - } else if (type === 'warn') { - level = 'alert-warning'; - } - - const model = new Model({ - 'title': title, - 'messages': messages, - 'level': level, - 'type': 'alert' - }); - api.modal.show(modals_alert, { - model - }); - } - -}; -core_converse.plugins.add('converse-modal', { - initialize() { - api.listen.on('disconnect', () => { - const container = document.querySelector("#converse-modals"); - - if (container) { - container.innerHTML = ''; - } - }); - api.listen.on('clearSession', () => api.modal.removeAll()); - Object.assign(shared_converse.api, modal_api); - } - -}); -;// CONCATENATED MODULE: ./node_modules/lit-html/async-directive.js - - - - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -const async_directive_r = (i, t) => { - var s, e; - const o = i.N; - if (void 0 === o) return !1; - - for (const i of o) null === (e = (s = i).O) || void 0 === e || e.call(s, t, !1), async_directive_r(i, t); - - return !0; -}, - async_directive_o = i => { - let t, s; - - do { - if (void 0 === (t = i.M)) break; - s = t.N, s.delete(i), i = t; - } while (0 === (null == s ? void 0 : s.size)); -}, - async_directive_h = i => { - for (let t; t = i.M; i = t) { - let s = t.N; - if (void 0 === s) t.N = s = new Set();else if (s.has(i)) break; - s.add(i), async_directive_d(t); - } -}; - -function async_directive_n(i) { - void 0 !== this.N ? (async_directive_o(this), this.M = i, async_directive_h(this)) : this.M = i; -} - -function async_directive_l(i, t = !1, s = 0) { - const e = this.H, - h = this.N; - if (void 0 !== h && 0 !== h.size) if (t) { - if (Array.isArray(e)) for (let i = s; i < e.length; i++) async_directive_r(e[i], !1), async_directive_o(e[i]);else null != e && (async_directive_r(e, !1), async_directive_o(e)); - } else async_directive_r(this, i); -} - -const async_directive_d = i => { - var t, e, r, o; - i.type == directive_t.CHILD && (null !== (t = (r = i).P) && void 0 !== t || (r.P = async_directive_l), null !== (e = (o = i).Q) && void 0 !== e || (o.Q = async_directive_n)); -}; - -class async_directive_c extends directive_s { - constructor() { - super(...arguments), this.isConnected = !0, this.ut = lit_html_w, this.N = void 0; - } - - T(i, t, s) { - super.T(i, t, s), async_directive_h(this); - } - - O(i, t = !0) { - this.at(i), t && (async_directive_r(this, i), async_directive_o(this)); - } - - at(t) { - var s, e; - t !== this.isConnected && (t ? (this.isConnected = !0, this.ut !== lit_html_w && (this.setValue(this.ut), this.ut = lit_html_w), null === (s = this.reconnected) || void 0 === s || s.call(this)) : (this.isConnected = !1, null === (e = this.disconnected) || void 0 === e || e.call(this))); - } - - S(i, t) { - if (!this.isConnected) throw Error(`AsyncDirective ${this.constructor.name} was rendered while its tree was disconnected.`); - return super.S(i, t); - } - - setValue(i) { - if (this.isConnected) { - if (directive_helpers_d(this.Σdt)) this.Σdt.I(i, this);else { - const t = [...this.Σdt.H]; - t[this.Σct] = i, this.Σdt.I(t, this, 0); - } - } else this.ut = i; - } - - disconnected() {} - - reconnected() {} - -} - - -;// CONCATENATED MODULE: ./node_modules/lit-html/directives/until.js - - - - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -const until_e = t => !directive_helpers_i(t) && "function" == typeof t.then, - until_o = directive_i(class extends async_directive_c { - constructor() { - super(...arguments), this.Ct = 2147483647, this.Rt = []; - } - - render(...r) { - var s; - return null !== (s = r.find(t => !until_e(t))) && void 0 !== s ? s : lit_html_w; - } - - update(r, s) { - const i = this.Rt; - let o = i.length; - this.Rt = s; - - for (let t = 0; t < s.length && !(t > this.Ct); t++) { - const r = s[t]; - if (!until_e(r)) return this.Ct = t, r; - t < o && r === i[t] || (this.Ct = 2147483647, o = 0, Promise.resolve(r).then(t => { - const s = this.Rt.indexOf(r); - s > -1 && s < this.Ct && (this.Ct = s, this.setValue(t)); - })); - } - - return lit_html_w; - } - -}); - - -;// CONCATENATED MODULE: ./node_modules/lit/directives/until.js - -;// CONCATENATED MODULE: ./src/shared/chat/message-actions.js - - - - - - -const { - Strophe: message_actions_Strophe, - u: message_actions_u -} = core_converse.env; - -class MessageActions extends CustomElement { - static get properties() { - return { - correcting: { - type: Boolean - }, - editable: { - type: Boolean - }, - hide_url_previews: { - type: Boolean - }, - is_retracted: { - type: Boolean - }, - message_type: { - type: String - }, - model: { - type: Object - }, - unfurls: { - type: Number - } - }; - } - - render() { - return T`${until_o(this.renderActions(), '')}`; - } - - async renderActions() { - // We want to let the message actions menu drop upwards if we're at the - // bottom of the message history, and down otherwise. This is to avoid - // the menu disappearing behind the bottom panel (toolbar, textarea etc). - // That's difficult to know from state, so we're making an approximation here. - const should_drop_up = this.model.collection.length > 2 && this.model === this.model.collection.last(); - const buttons = await this.getActionButtons(); - const items = buttons.map(b => MessageActions.getActionsDropdownItem(b)); - - if (items.length) { - return T``; - } else { - return ''; - } - } - - static getActionsDropdownItem(o) { - return T` - - `; - } - - onMessageEditButtonClicked(ev) { - var _u$ancestor, _u$ancestor$querySele; - - ev.preventDefault(); - const currently_correcting = this.model.collection.findWhere('correcting'); // TODO: Use state intead of DOM querying - // Then this code can also be put on the model - - const unsent_text = (_u$ancestor = message_actions_u.ancestor(this, '.chatbox')) === null || _u$ancestor === void 0 ? void 0 : (_u$ancestor$querySele = _u$ancestor.querySelector('.chat-textarea')) === null || _u$ancestor$querySele === void 0 ? void 0 : _u$ancestor$querySele.value; - - if (unsent_text && (!currently_correcting || currently_correcting.get('message') !== unsent_text)) { - if (!confirm(__('You have an unsent message which will be lost if you continue. Are you sure?'))) { - return; - } - } - - if (currently_correcting !== this.model) { - currently_correcting === null || currently_correcting === void 0 ? void 0 : currently_correcting.save('correcting', false); - this.model.save('correcting', true); - } else { - this.model.save('correcting', false); - } - } - - async onDirectMessageRetractButtonClicked() { - if (this.model.get('sender') !== 'me') { - return headless_log.error("onMessageRetractButtonClicked called for someone else's message!"); - } - - const retraction_warning = __('Be aware that other XMPP/Jabber clients (and servers) may ' + 'not yet support retractions and that this message may not ' + 'be removed everywhere.'); - - const messages = [__('Are you sure you want to retract this message?')]; - - if (api.settings.get('show_retraction_warning')) { - messages[1] = retraction_warning; - } - - const result = await api.confirm(__('Confirm'), messages); - - if (result) { - const chatbox = this.model.collection.chatbox; - chatbox.retractOwnMessage(this.model); - } - } - /** - * Retract someone else's message in this groupchat. - * @private - * @param { _converse.Message } message - The message which we're retracting. - * @param { string } [reason] - The reason for retracting the message. - */ - - - async retractOtherMessage(reason) { - const chatbox = this.model.collection.chatbox; - const result = await chatbox.retractOtherMessage(this.model, reason); - - if (result === null) { - const err_msg = __(`A timeout occurred while trying to retract the message`); - - api.alert('error', __('Error'), err_msg); - headless_log(err_msg, message_actions_Strophe.LogLevel.WARN); - } else if (message_actions_u.isErrorStanza(result)) { - const err_msg = __(`Sorry, you're not allowed to retract this message.`); - - api.alert('error', __('Error'), err_msg); - headless_log(err_msg, message_actions_Strophe.LogLevel.WARN); - headless_log(result, message_actions_Strophe.LogLevel.WARN); - } - } - - async onMUCMessageRetractButtonClicked() { - const retraction_warning = __('Be aware that other XMPP/Jabber clients (and servers) may ' + 'not yet support retractions and that this message may not ' + 'be removed everywhere.'); - - if (this.model.mayBeRetracted()) { - const messages = [__('Are you sure you want to retract this message?')]; - - if (api.settings.get('show_retraction_warning')) { - messages[1] = retraction_warning; - } - - if (await api.confirm(__('Confirm'), messages)) { - const chatbox = this.model.collection.chatbox; - chatbox.retractOwnMessage(this.model); - } - } else if (await this.model.mayBeModerated()) { - if (this.model.get('sender') === 'me') { - let messages = [__('Are you sure you want to retract this message?')]; - - if (api.settings.get('show_retraction_warning')) { - messages = [messages[0], retraction_warning, messages[1]]; - } - - !!(await api.confirm(__('Confirm'), messages)) && this.retractOtherMessage(); - } else { - let messages = [__('You are about to retract this message.'), __('You may optionally include a message, explaining the reason for the retraction.')]; - - if (api.settings.get('show_retraction_warning')) { - messages = [messages[0], retraction_warning, messages[1]]; - } - - const reason = await api.prompt(__('Message Retraction'), messages, __('Optional reason')); - reason !== false && this.retractOtherMessage(reason); - } - } else { - const err_msg = __(`Sorry, you're not allowed to retract this message`); - - api.alert('error', __('Error'), err_msg); - } - } - - onMessageRetractButtonClicked(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - const chatbox = this.model.collection.chatbox; - - if (chatbox.get('type') === shared_converse.CHATROOMS_TYPE) { - this.onMUCMessageRetractButtonClicked(); - } else { - this.onDirectMessageRetractButtonClicked(); - } - } - - onHidePreviewsButtonClicked(ev) { - var _ev$preventDefault2; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - - if (this.hide_url_previews) { - this.model.save({ - 'hide_url_previews': false, - 'url_preview_transition': 'fade-in' - }); - } else { - const ogp_metadata = this.model.get('ogp_metadata') || []; - const unfurls_to_show = api.settings.get('muc_show_ogp_unfurls') && ogp_metadata.length; - - if (unfurls_to_show) { - this.model.set('url_preview_transition', 'fade-out'); - } else { - this.model.save({ - 'hide_url_previews': true, - 'url_preview_transition': 'fade-in' - }); - } - } - } - - async getActionButtons() { - var _this$model$get; - - const buttons = []; - - if (this.editable) { - buttons.push({ - 'i18n_text': this.correcting ? __('Cancel Editing') : __('Edit'), - 'handler': ev => this.onMessageEditButtonClicked(ev), - 'button_class': 'chat-msg__action-edit', - 'icon_class': 'fa fa-pencil-alt', - 'name': 'edit' - }); - } - - const may_be_moderated = this.model.get('type') === 'groupchat' && (await this.model.mayBeModerated()); - const retractable = !this.is_retracted && (this.model.mayBeRetracted() || may_be_moderated); - - if (retractable) { - buttons.push({ - 'i18n_text': __('Retract'), - 'handler': ev => this.onMessageRetractButtonClicked(ev), - 'button_class': 'chat-msg__action-retract', - 'icon_class': 'fas fa-trash-alt', - 'name': 'retract' - }); - } - - if (!this.model.collection) { - // While we were awaiting, this model got removed from the - // collection (happens during tests) - return []; - } - - const ogp_metadata = this.model.get('ogp_metadata') || []; - const unfurls_to_show = api.settings.get('muc_show_ogp_unfurls') && ogp_metadata.length; - const media_to_show = (_this$model$get = this.model.get('media_urls')) === null || _this$model$get === void 0 ? void 0 : _this$model$get.length; - - if (unfurls_to_show || media_to_show) { - let title; - const hidden_preview = this.hide_url_previews; - - if (ogp_metadata.length > 1) { - title = hidden_preview ? __('Show URL previews') : __('Hide URL previews'); - } else if (ogp_metadata.length === 1) { - title = hidden_preview ? __('Show URL preview') : __('Hide URL preview'); - } else { - title = hidden_preview ? __('Show media') : __('Hide media'); - } - - buttons.push({ - 'i18n_text': title, - 'handler': ev => this.onHidePreviewsButtonClicked(ev), - 'button_class': 'chat-msg__action-hide-previews', - 'icon_class': this.hide_url_previews ? 'fas fa-eye' : 'fas fa-eye-slash', - 'name': 'hide' - }); - } - /** - * *Hook* which allows plugins to add more message action buttons - * @event _converse#getMessageActionButtons - * @example - * api.listen.on('getMessageActionButtons', (el, buttons) => { - * buttons.push({ - * 'i18n_text': 'Foo', - * 'handler': ev => alert('Foo!'), - * 'button_class': 'chat-msg__action-foo', - * 'icon_class': 'fa fa-check', - * 'name': 'foo' - * }); - * return buttons; - * }); - */ - - - return api.hook('getMessageActionButtons', this, buttons); - } - -} - -api.elements.define('converse-message-actions', MessageActions); -;// CONCATENATED MODULE: ./src/modals/templates/image.js - - - -/* harmony default export */ const templates_image = (o => { - return T` - `; -}); -;// CONCATENATED MODULE: ./src/modals/image.js - - -/* harmony default export */ const modals_image = (base.extend({ - id: 'image-modal', - - toHTML() { - return templates_image({ - 'src': this.src, - 'onload': ev => ev.target.parentElement.style.height = `${ev.target.height}px` - }); - } - -})); -;// CONCATENATED MODULE: ./node_modules/lit/directive.js - -;// CONCATENATED MODULE: ./src/templates/audio.js - -/* harmony default export */ const audio = ((url, hide_url) => T`${hide_url ? '' : T`${url}`}`); -;// CONCATENATED MODULE: ./src/shared/gif/stream.js -class Stream { - constructor(data) { - if (data.toString().indexOf('ArrayBuffer') > 0) { - data = new Uint8Array(data); - } - - this.data = data; - this.len = this.data.length; - this.pos = 0; - } - - readByte() { - if (this.pos >= this.data.length) { - throw new Error('Attempted to read past end of stream.'); - } - - if (this.data instanceof Uint8Array) return this.data[this.pos++];else return this.data.charCodeAt(this.pos++) & 0xFF; - } - - readBytes(n) { - const bytes = []; - - for (let i = 0; i < n; i++) { - bytes.push(this.readByte()); - } - - return bytes; - } - - read(n) { - let s = ''; - - for (let i = 0; i < n; i++) { - s += String.fromCharCode(this.readByte()); - } - - return s; - } - - readUnsigned() { - // Little-endian. - const a = this.readBytes(2); - return (a[1] << 8) + a[0]; - } - -} -;// CONCATENATED MODULE: ./src/shared/gif/utils.js -/** - * @copyright Shachaf Ben-Kiki and the Converse.js contributors - * @description - * Started as a fork of Shachaf Ben-Kiki's jsgif library - * https://github.com/shachaf/jsgif - * @license MIT License - */ -function bitsToNum(ba) { - return ba.reduce(function (s, n) { - return s * 2 + n; - }, 0); -} - -function byteToBitArr(bite) { - const a = []; - - for (let i = 7; i >= 0; i--) { - a.push(!!(bite & 1 << i)); - } - - return a; -} - -function lzwDecode(minCodeSize, data) { - // TODO: Now that the GIF parser is a bit different, maybe this should get an array of bytes instead of a String? - let pos = 0; // Maybe this streaming thing should be merged with the Stream? - - function readCode(size) { - let code = 0; - - for (let i = 0; i < size; i++) { - if (data.charCodeAt(pos >> 3) & 1 << (pos & 7)) { - code |= 1 << i; - } - - pos++; - } - - return code; - } - - const output = []; - const clearCode = 1 << minCodeSize; - const eoiCode = clearCode + 1; - let codeSize = minCodeSize + 1; - let dict = []; - - const clear = function () { - dict = []; - codeSize = minCodeSize + 1; - - for (let i = 0; i < clearCode; i++) { - dict[i] = [i]; - } - - dict[clearCode] = []; - dict[eoiCode] = null; - }; - - let code; - let last; - - while (true) { - // eslint-disable-line no-constant-condition - last = code; - code = readCode(codeSize); - - if (code === clearCode) { - clear(); - continue; - } - - if (code === eoiCode) break; - - if (code < dict.length) { - if (last !== clearCode) { - dict.push(dict[last].concat(dict[code][0])); - } - } else { - if (code !== dict.length) throw new Error('Invalid LZW code.'); - dict.push(dict[last].concat(dict[last][0])); - } - - output.push.apply(output, dict[code]); - - if (dict.length === 1 << codeSize && codeSize < 12) { - // If we're at the last code and codeSize is 12, the next code will be a clearCode, and it'll be 12 bits long. - codeSize++; - } - } // I don't know if this is technically an error, but some GIFs do it. - //if (Math.ceil(pos / 8) !== data.length) throw new Error('Extraneous LZW bytes.'); - - - return output; -} - -function readSubBlocks(st) { - let size, data; - data = ''; - - do { - size = st.readByte(); - data += st.read(size); - } while (size !== 0); - - return data; -} -/** - * Parses GIF image color table information - * @param { Stream } st - * @param { Number } entries - */ - - -function parseCT(st, entries) { - // Each entry is 3 bytes, for RGB. - const ct = []; - - for (let i = 0; i < entries; i++) { - ct.push(st.readBytes(3)); - } - - return ct; -} -/** - * Parses GIF image information - * @param { Stream } st - * @param { ByteStream } img - * @param { Function } [callback] - */ - - -function parseImg(st, img, callback) { - function deinterlace(pixels, width) { - // Of course this defeats the purpose of interlacing. And it's *probably* - // the least efficient way it's ever been implemented. But nevertheless... - const newPixels = new Array(pixels.length); - const rows = pixels.length / width; - - function cpRow(toRow, fromRow) { - const fromPixels = pixels.slice(fromRow * width, (fromRow + 1) * width); - newPixels.splice.apply(newPixels, [toRow * width, width].concat(fromPixels)); - } // See appendix E. - - - const offsets = [0, 4, 2, 1]; - const steps = [8, 8, 4, 2]; - let fromRow = 0; - - for (let pass = 0; pass < 4; pass++) { - for (let toRow = offsets[pass]; toRow < rows; toRow += steps[pass]) { - cpRow(toRow, fromRow); - fromRow++; - } - } - - return newPixels; - } - - img.leftPos = st.readUnsigned(); - img.topPos = st.readUnsigned(); - img.width = st.readUnsigned(); - img.height = st.readUnsigned(); - const bits = byteToBitArr(st.readByte()); - img.lctFlag = bits.shift(); - img.interlaced = bits.shift(); - img.sorted = bits.shift(); - img.reserved = bits.splice(0, 2); - img.lctSize = bitsToNum(bits.splice(0, 3)); - - if (img.lctFlag) { - img.lct = parseCT(st, 1 << img.lctSize + 1); - } - - img.lzwMinCodeSize = st.readByte(); - const lzwData = readSubBlocks(st); - img.pixels = lzwDecode(img.lzwMinCodeSize, lzwData); - - if (img.interlaced) { - // Move - img.pixels = deinterlace(img.pixels, img.width); - } - - callback === null || callback === void 0 ? void 0 : callback(img); -} -/** - * Parses GIF header information - * @param { Stream } st - * @param { Function } [callback] - */ - - -function parseHeader(st, callback) { - const hdr = {}; - hdr.sig = st.read(3); - hdr.ver = st.read(3); - - if (hdr.sig !== 'GIF') { - throw new Error('Not a GIF file.'); - } - - hdr.width = st.readUnsigned(); - hdr.height = st.readUnsigned(); - const bits = byteToBitArr(st.readByte()); - hdr.gctFlag = bits.shift(); - hdr.colorRes = bitsToNum(bits.splice(0, 3)); - hdr.sorted = bits.shift(); - hdr.gctSize = bitsToNum(bits.splice(0, 3)); - hdr.bgColor = st.readByte(); - hdr.pixelAspectRatio = st.readByte(); // if not 0, aspectRatio = (pixelAspectRatio + 15) / 64 - - if (hdr.gctFlag) { - hdr.gct = parseCT(st, 1 << hdr.gctSize + 1); - } - - callback === null || callback === void 0 ? void 0 : callback(hdr); -} - -function parseExt(st, block, handler) { - function parseGCExt(block) { - st.readByte(); // blocksize, always 4 - - const bits = byteToBitArr(st.readByte()); - block.reserved = bits.splice(0, 3); // Reserved; should be 000. - - block.disposalMethod = bitsToNum(bits.splice(0, 3)); - block.userInput = bits.shift(); - block.transparencyGiven = bits.shift(); - block.delayTime = st.readUnsigned(); - block.transparencyIndex = st.readByte(); - block.terminator = st.readByte(); - handler === null || handler === void 0 ? void 0 : handler.gce(block); - } - - function parseComExt(block) { - block.comment = readSubBlocks(st); - handler.com && handler.com(block); - } - - function parsePTExt(block) { - // No one *ever* uses this. If you use it, deal with parsing it yourself. - st.readByte(); // blocksize, always 12 - - block.ptHeader = st.readBytes(12); - block.ptData = readSubBlocks(st); - handler.pte && handler.pte(block); - } - - function parseAppExt(block) { - function parseNetscapeExt(block) { - st.readByte(); // blocksize, always 3 - - block.unknown = st.readByte(); // ??? Always 1? What is this? - - block.iterations = st.readUnsigned(); - block.terminator = st.readByte(); - handler.app && handler.app.NETSCAPE && handler.app.NETSCAPE(block); - } - - function parseUnknownAppExt(block) { - block.appData = readSubBlocks(st); // FIXME: This won't work if a handler wants to match on any identifier. - - handler.app && handler.app[block.identifier] && handler.app[block.identifier](block); - } - - st.readByte(); // blocksize, always 11 - - block.identifier = st.read(8); - block.authCode = st.read(3); - - switch (block.identifier) { - case 'NETSCAPE': - parseNetscapeExt(block); - break; - - default: - parseUnknownAppExt(block); - break; - } - } - - function parseUnknownExt(block) { - block.data = readSubBlocks(st); - handler.unknown && handler.unknown(block); - } - - block.label = st.readByte(); - - switch (block.label) { - case 0xF9: - block.extType = 'gce'; - parseGCExt(block); - break; - - case 0xFE: - block.extType = 'com'; - parseComExt(block); - break; - - case 0x01: - block.extType = 'pte'; - parsePTExt(block); - break; - - case 0xFF: - block.extType = 'app'; - parseAppExt(block); - break; - - default: - block.extType = 'unknown'; - parseUnknownExt(block); - break; - } -} -/** - * @param { Stream } st - * @param { GIFParserHandlers } handler - */ - - -function parseBlock(st, handler) { - const block = {}; - block.sentinel = st.readByte(); - - switch (String.fromCharCode(block.sentinel)) { - // For ease of matching - case '!': - block.type = 'ext'; - parseExt(st, block, handler); - break; - - case ',': - block.type = 'img'; - parseImg(st, block, handler === null || handler === void 0 ? void 0 : handler.img); - break; - - case ';': - block.type = 'eof'; - handler === null || handler === void 0 ? void 0 : handler.eof(block); - break; - - default: - throw new Error('Unknown block: 0x' + block.sentinel.toString(16)); - // TODO: Pad this with a 0. - } - - if (block.type !== 'eof') setTimeout(() => parseBlock(st, handler), 0); -} -/** - * Takes a Stream and parses it for GIF data, calling the relevant handler - * methods on the passed in `handler` object. - * @param { Stream } st - * @param { GIFParserHandlers } handler - */ - - -function parseGIF(st, handler = {}) { - parseHeader(st, handler === null || handler === void 0 ? void 0 : handler.hdr); - setTimeout(() => parseBlock(st, handler), 0); -} -;// CONCATENATED MODULE: ./src/shared/gif/index.js -/** - * @copyright Shachaf Ben-Kiki, JC Brand - * @description - * Started as a fork of Shachaf Ben-Kiki's jsgif library - * https://github.com/shachaf/jsgif - * @license MIT License - */ - - - -const DELAY_FACTOR = 10; -class ConverseGif { - /** - * Creates a new ConverseGif instance - * @param { HTMLElement } el - * @param { Object } [options] - * @param { Number } [options.width] - The width, in pixels, of the canvas - * @param { Number } [options.height] - The height, in pixels, of the canvas - * @param { Boolean } [options.loop=true] - Setting this to `true` will enable looping of the gif - * @param { Boolean } [options.autoplay=true] - Same as the rel:autoplay attribute above, this arg overrides the img tag info. - * @param { Number } [options.max_width] - Scale images over max_width down to max_width. Helpful with mobile. - * @param { Function } [options.onIterationEnd] - Add a callback for when the gif reaches the end of a single loop (one iteration). The first argument passed will be the gif HTMLElement. - * @param { Boolean } [options.show_progress_bar=true] - * @param { String } [options.progress_bg_color='rgba(0,0,0,0.4)'] - * @param { String } [options.progress_color='rgba(255,0,22,.8)'] - * @param { Number } [options.progress_bar_height=5] - */ - constructor(el, opts) { - this.options = Object.assign({ - width: null, - height: null, - autoplay: true, - loop: true, - show_progress_bar: true, - progress_bg_color: 'rgba(0,0,0,0.4)', - progress_color: 'rgba(255,0,22,.8)', - progress_bar_height: 5 - }, opts); - this.el = el; - this.gif_el = el.querySelector('img'); - this.canvas = el.querySelector('canvas'); - this.ctx = this.canvas.getContext('2d'); // It's good practice to pre-render to an offscreen canvas - - this.offscreenCanvas = document.createElement('canvas'); - this.ctx_scaled = false; - this.disposal_method = null; - this.disposal_restore_from_idx = null; - this.frame = null; - this.frame_offsets = []; // elements have .x and .y properties - - this.frames = []; - this.last_disposal_method = null; - this.last_img = null; - this.load_error = null; - this.playing = this.options.autoplay; - this.transparency = null; - this.frame_idx = 0; - this.iteration_count = 0; - this.start = null; - this.initialize(); - } - - async initialize() { - if (this.options.width && this.options.height) { - this.setSizes(this.options.width, this.options.height); - } - - const data = await this.fetchGIF(this.gif_el.src); - requestAnimationFrame(() => this.startParsing(data)); - } - - initPlayer() { - if (this.load_error) return; - - if (!(this.options.width && this.options.height)) { - this.ctx.scale(this.getCanvasScale(), this.getCanvasScale()); - } // Show the first frame - - - this.frame_idx = 0; - this.putFrame(this.frame_idx); - - if (this.options.autoplay) { - var _this$frames$this$fra; - - const delay = (((_this$frames$this$fra = this.frames[this.frame_idx]) === null || _this$frames$this$fra === void 0 ? void 0 : _this$frames$this$fra.delay) ?? 0) * DELAY_FACTOR; - setTimeout(() => this.play(), delay); - } - } - /** - * Gets the index of the frame "up next" - * @returns {number} - */ - - - getNextFrameNo() { - return (this.frame_idx + 1 + this.frames.length) % this.frames.length; - } - /** - * Called once we've looped through all frames in the GIF - * @returns { Boolean } - Returns `true` if the GIF is now paused (i.e. further iterations are not desired) - */ - - - onIterationEnd() { - var _this$options$onItera, _this$options; - - this.iteration_count++; - (_this$options$onItera = (_this$options = this.options).onIterationEnd) === null || _this$options$onItera === void 0 ? void 0 : _this$options$onItera.call(_this$options, this); - - if (!this.options.loop) { - this.pause(); - return true; - } - - return false; - } - /** - * Inner callback for the `requestAnimationFrame` function. - * - * This method gets wrapped by an arrow function so that the `previous_timestamp` and - * `frame_delay` parameters can also be passed in. The `timestamp` - * parameter comes from `requestAnimationFrame`. - * - * The purpose of this method is to call `putFrame` with the right delay - * in order to render the GIF animation. - * - * Note, this method will cause the *next* upcoming frame to be rendered, - * not the current one. - * - * This means `this.frame_idx` will be incremented before calling `this.putFrame`, so - * `putFrame(0)` needs to be called *before* this method, otherwise the - * animation will incorrectly start from frame #1 (this is done in `initPlayer`). - * - * @param { DOMHighRestTimestamp } timestamp - The timestamp as returned by `requestAnimationFrame` - * @param { DOMHighRestTimestamp } previous_timestamp - The timestamp from the previous iteration of this method. - * We need this in order to calculate whether we have waited long enough to - * show the next frame. - * @param { Number } frame_delay - The delay (in 1/100th of a second) - * before the currently being shown frame should be replaced by a new one. - */ - - - onAnimationFrame(timestamp, previous_timestamp, frame_delay) { - var _this$frames$this$fra2; - - if (!this.playing) { - return; - } - - if (timestamp - previous_timestamp < frame_delay) { - this.hovering ? this.drawPauseIcon() : this.putFrame(this.frame_idx); // We need to wait longer - - requestAnimationFrame(ts => this.onAnimationFrame(ts, previous_timestamp, frame_delay)); - return; - } - - const next_frame = this.getNextFrameNo(); - - if (next_frame === 0 && this.onIterationEnd()) { - return; - } - - this.frame_idx = next_frame; - this.putFrame(this.frame_idx); - const delay = (((_this$frames$this$fra2 = this.frames[this.frame_idx]) === null || _this$frames$this$fra2 === void 0 ? void 0 : _this$frames$this$fra2.delay) || 8) * DELAY_FACTOR; - requestAnimationFrame(ts => this.onAnimationFrame(ts, timestamp, delay)); - } - - setSizes(w, h) { - this.canvas.width = w * this.getCanvasScale(); - this.canvas.height = h * this.getCanvasScale(); - this.offscreenCanvas.width = w; - this.offscreenCanvas.height = h; - this.offscreenCanvas.style.width = w + 'px'; - this.offscreenCanvas.style.height = h + 'px'; - this.offscreenCanvas.getContext('2d').setTransform(1, 0, 0, 1, 0, 0); - } - - setFrameOffset(frame, offset) { - if (!this.frame_offsets[frame]) { - this.frame_offsets[frame] = offset; - return; - } - - if (typeof offset.x !== 'undefined') { - this.frame_offsets[frame].x = offset.x; - } - - if (typeof offset.y !== 'undefined') { - this.frame_offsets[frame].y = offset.y; - } - } - - doShowProgress(pos, length, draw) { - if (draw && this.options.show_progress_bar) { - let height = this.options.progress_bar_height; - const top = (this.canvas.height - height) / (this.ctx_scaled ? this.getCanvasScale() : 1); - const mid = pos / length * this.canvas.width / (this.ctx_scaled ? this.getCanvasScale() : 1); - const width = this.canvas.width / (this.ctx_scaled ? this.getCanvasScale() : 1); - height /= this.ctx_scaled ? this.getCanvasScale() : 1; - this.ctx.fillStyle = this.options.progress_bg_color; - this.ctx.fillRect(mid, top, width - mid, height); - this.ctx.fillStyle = this.options.progress_color; - this.ctx.fillRect(0, top, mid, height); - } - } - /** - * Starts parsing the GIF stream data by calling `parseGIF` and passing in - * a map of handler functions. - * @param { String } data - The GIF file data, as returned by the server - */ - - - startParsing(data) { - const stream = new Stream(data); - /** - * @typedef { Object } GIFParserHandlers - * A map of callback functions passed `parseGIF`. These functions are - * called as various parts of the GIF file format are parsed. - * @property { Function } hdr - Callback to handle the GIF header data - * @property { Function } gce - Callback to handle the GIF Graphic Control Extension data - * @property { Function } com - Callback to handle the comment extension block - * @property { Function } img - Callback to handle image data - * @property { Function } eof - Callback once the end of file has been reached - */ - - const handler = { - 'hdr': this.withProgress(stream, header => this.handleHeader(header)), - 'gce': this.withProgress(stream, gce => this.handleGCE(gce)), - 'com': this.withProgress(stream), - 'img': this.withProgress(stream, img => this.doImg(img), true), - 'eof': () => this.handleEOF(stream) - }; - - try { - parseGIF(stream, handler); - } catch (err) { - this.showError('parse'); - } - } - - drawError() { - this.ctx.fillStyle = 'black'; - this.ctx.fillRect(0, 0, this.options.width ? this.options.width : this.hdr.width, this.options.height ? this.options.height : this.hdr.height); - this.ctx.strokeStyle = 'red'; - this.ctx.lineWidth = 3; - this.ctx.moveTo(0, 0); - this.ctx.lineTo(this.options.width ? this.options.width : this.hdr.width, this.options.height ? this.options.height : this.hdr.height); - this.ctx.moveTo(0, this.options.height ? this.options.height : this.hdr.height); - this.ctx.lineTo(this.options.width ? this.options.width : this.hdr.width, 0); - this.ctx.stroke(); - } - - showError(errtype) { - this.load_error = errtype; - this.hdr = { - width: this.gif_el.width, - height: this.gif_el.height - }; // Fake header. - - this.frames = []; - this.drawError(); - this.el.requestUpdate(); - } - - handleHeader(header) { - this.hdr = header; - this.setSizes(this.options.width ?? this.hdr.width, this.options.height ?? this.hdr.height); - } - /** - * Handler for GIF Graphic Control Extension (GCE) data - */ - - - handleGCE(gce) { - this.pushFrame(gce.delayTime); - this.clear(); - this.transparency = gce.transparencyGiven ? gce.transparencyIndex : null; - this.disposal_method = gce.disposalMethod; - } - /** - * Handler for when the end of the GIF's file has been reached - */ - - - handleEOF(stream) { - this.doDecodeProgress(stream, false); - - if (!(this.options.width && this.options.height)) { - this.canvas.width = this.hdr.width * this.getCanvasScale(); - this.canvas.height = this.hdr.height * this.getCanvasScale(); - } - - this.initPlayer(); - !this.options.autoplay && this.drawPlayIcon(); - } - - pushFrame(delay) { - if (!this.frame) return; - this.frames.push({ - data: this.frame.getImageData(0, 0, this.hdr.width, this.hdr.height), - delay - }); - this.frame_offsets.push({ - x: 0, - y: 0 - }); - } - - doImg(img) { - this.frame = this.frame || this.offscreenCanvas.getContext('2d'); - const currIdx = this.frames.length; //ct = color table, gct = global color table - - const ct = img.lctFlag ? img.lct : this.hdr.gct; // TODO: What if neither exists? - - /* - * Disposal method indicates the way in which the graphic is to - * be treated after being displayed. - * - * Values : 0 - No disposal specified. The decoder is - * not required to take any action. - * 1 - Do not dispose. The graphic is to be left - * in place. - * 2 - Restore to background color. The area used by the - * graphic must be restored to the background color. - * 3 - Restore to previous. The decoder is required to - * restore the area overwritten by the graphic with - * what was there prior to rendering the graphic. - * - * Importantly, "previous" means the frame state - * after the last disposal of method 0, 1, or 2. - */ - - if (currIdx > 0) { - if (this.last_disposal_method === 3) { - // Restore to previous - // If we disposed every frame including first frame up to this point, then we have - // no composited frame to restore to. In this case, restore to background instead. - if (this.disposal_restore_from_idx !== null) { - this.frame.putImageData(this.frames[this.disposal_restore_from_idx].data, 0, 0); - } else { - this.frame.clearRect(this.last_img.leftPos, this.last_img.topPos, this.last_img.width, this.last_img.height); - } - } else { - this.disposal_restore_from_idx = currIdx - 1; - } - - if (this.last_disposal_method === 2) { - // Restore to background color - // Browser implementations historically restore to transparent; we do the same. - // http://www.wizards-toolkit.org/discourse-server/viewtopic.php?f=1&t=21172#p86079 - this.frame.clearRect(this.last_img.leftPos, this.last_img.topPos, this.last_img.width, this.last_img.height); - } - } // else, Undefined/Do not dispose. - // frame contains final pixel data from the last frame; do nothing - //Get existing pixels for img region after applying disposal method - - - const imgData = this.frame.getImageData(img.leftPos, img.topPos, img.width, img.height); //apply color table colors - - img.pixels.forEach((pixel, i) => { - // imgData.data === [R,G,B,A,R,G,B,A,...] - if (pixel !== this.transparency) { - imgData.data[i * 4 + 0] = ct[pixel][0]; - imgData.data[i * 4 + 1] = ct[pixel][1]; - imgData.data[i * 4 + 2] = ct[pixel][2]; - imgData.data[i * 4 + 3] = 255; // Opaque. - } - }); - this.frame.putImageData(imgData, img.leftPos, img.topPos); - - if (!this.ctx_scaled) { - this.ctx.scale(this.getCanvasScale(), this.getCanvasScale()); - this.ctx_scaled = true; - } - - if (!this.last_img) { - // This is the first receivd image, so we draw it - this.ctx.drawImage(this.offscreenCanvas, 0, 0); - } - - this.last_img = img; - } - /** - * Draws a gif frame at a specific index inside the canvas. - * @param { Number } i - The frame index - */ - - - putFrame(i, show_pause_on_hover = true) { - i = parseInt(i, 10); - - if (i > this.frames.length - 1) { - i = 0; - } - - if (i < 0) { - i = 0; - } - - const offset = this.frame_offsets[i]; - this.offscreenCanvas.getContext('2d').putImageData(this.frames[i].data, offset.x, offset.y); - this.ctx.globalCompositeOperation = 'copy'; - this.ctx.drawImage(this.offscreenCanvas, 0, 0); - - if (show_pause_on_hover && this.hovering) { - this.drawPauseIcon(); - } - } - - clear() { - this.transparency = null; - this.last_disposal_method = this.disposal_method; - this.disposal_method = null; - this.frame = null; - } - /** - * Start playing the gif - */ - - - play() { - this.playing = true; - requestAnimationFrame(ts => this.onAnimationFrame(ts, 0, 0)); - } - /** - * Pause the gif - */ - - - pause() { - this.playing = false; - requestAnimationFrame(() => this.drawPlayIcon()); - } - - drawPauseIcon() { - if (!this.playing) { - return; - } // Clear the potential play button by re-rendering the current frame - - - this.putFrame(this.frame_idx, false); - this.ctx.globalCompositeOperation = 'source-over'; // Draw dark overlay - - this.ctx.fillStyle = 'rgb(0, 0, 0, 0.25)'; - this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); - const icon_size = this.canvas.height * 0.1; // Draw bars - - this.ctx.lineWidth = this.canvas.height * 0.04; - this.ctx.beginPath(); - this.ctx.moveTo(this.canvas.width / 2 - icon_size / 2, this.canvas.height / 2 - icon_size); - this.ctx.lineTo(this.canvas.width / 2 - icon_size / 2, this.canvas.height / 2 + icon_size); - this.ctx.fillStyle = 'rgb(200, 200, 200, 0.75)'; - this.ctx.stroke(); - this.ctx.beginPath(); - this.ctx.moveTo(this.canvas.width / 2 + icon_size / 2, this.canvas.height / 2 - icon_size); - this.ctx.lineTo(this.canvas.width / 2 + icon_size / 2, this.canvas.height / 2 + icon_size); - this.ctx.fillStyle = 'rgb(200, 200, 200, 0.75)'; - this.ctx.stroke(); // Draw circle - - this.ctx.lineWidth = this.canvas.height * 0.02; - this.ctx.strokeStyle = 'rgb(200, 200, 200, 0.75)'; - this.ctx.beginPath(); - this.ctx.arc(this.canvas.width / 2, this.canvas.height / 2, icon_size * 1.5, 0, 2 * Math.PI); - this.ctx.stroke(); - } - - drawPlayIcon() { - if (this.playing) { - return; - } // Clear the potential pause button by re-rendering the current frame - - - this.putFrame(this.frame_idx, false); - this.ctx.globalCompositeOperation = 'source-over'; // Draw dark overlay - - this.ctx.fillStyle = 'rgb(0, 0, 0, 0.25)'; - this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Draw triangle - - const triangle_size = this.canvas.height * 0.1; - const region = new Path2D(); - region.moveTo(this.canvas.width / 2 + triangle_size, this.canvas.height / 2); // start at the pointy end - - region.lineTo(this.canvas.width / 2 - triangle_size / 2, this.canvas.height / 2 + triangle_size); - region.lineTo(this.canvas.width / 2 - triangle_size / 2, this.canvas.height / 2 - triangle_size); - region.closePath(); - this.ctx.fillStyle = 'rgb(200, 200, 200, 0.75)'; - this.ctx.fill(region); // Draw circle - - const circle_size = triangle_size * 1.5; - this.ctx.lineWidth = this.canvas.height * 0.02; - this.ctx.strokeStyle = 'rgb(200, 200, 200, 0.75)'; - this.ctx.beginPath(); - this.ctx.arc(this.canvas.width / 2, this.canvas.height / 2, circle_size, 0, 2 * Math.PI); - this.ctx.stroke(); - } - - doDecodeProgress(stream, draw) { - this.doShowProgress(stream.pos, stream.data.length, draw); - } - /** - * @param{boolean=} draw Whether to draw progress bar or not; - * this is not idempotent because of translucency. - * Note that this means that the text will be unsynchronized - * with the progress bar on non-frames; - * but those are typically so small (GCE etc.) that it doesn't really matter - */ - - - withProgress(stream, fn, draw) { - return block => { - fn === null || fn === void 0 ? void 0 : fn(block); - this.doDecodeProgress(stream, draw); - }; - } - - getCanvasScale() { - let scale; - - if (this.options.max_width && this.hdr && this.hdr.width > this.options.max_width) { - scale = this.options.max_width / this.hdr.width; - } else { - scale = 1; - } - - return scale; - } - /** - * Makes an HTTP request to fetch a GIF - * @param { String } url - * @returns { Promise } Returns a promise which resolves with the response data. - */ - - - fetchGIF(url) { - const promise = getOpenPromise(); - const h = new XMLHttpRequest(); - h.open('GET', url, true); - h === null || h === void 0 ? void 0 : h.overrideMimeType('text/plain; charset=x-user-defined'); - - h.onload = () => { - if (h.status != 200) { - this.showError('xhr - response'); - return promise.reject(); - } - - promise.resolve(h.response); - }; - - h.onprogress = e => e.lengthComputable && this.doShowProgress(e.loaded, e.total, true); - - h.onerror = () => this.showError('xhr'); - - h.send(); - return promise; - } - -} -;// CONCATENATED MODULE: ./src/templates/file.js - - -/* harmony default export */ const file = ((url, name) => { - const i18n_download = __('Download file "%1$s"', name); - - return T`${i18n_download}`; -}); -;// CONCATENATED MODULE: ./src/templates/form_captcha.js - -/* harmony default export */ const form_captcha = (o => T` -
- ${o.label ? T`` : ''} - - -
-`); -;// CONCATENATED MODULE: ./src/templates/form_checkbox.js - -/* harmony default export */ const form_checkbox = (o => T` -
- - -
`); -;// CONCATENATED MODULE: ./src/templates/form_help.js - -/* harmony default export */ const form_help = (o => T`

${o.text}

`); -;// CONCATENATED MODULE: ./src/templates/form_input.js - -/* harmony default export */ const form_input = (o => T` -
- ${o.type !== 'hidden' ? T`` : ''} - - - ${o.type === 'password' && o.fixed_username ? T` - - ` : ''} - - -
`); -;// CONCATENATED MODULE: ./src/templates/form_select.js - - -const tpl_option = o => T``; - -/* harmony default export */ const form_select = (o => { - var _o$options; - - return T` -
- - -
`; -}); -;// CONCATENATED MODULE: ./src/templates/form_textarea.js - -/* harmony default export */ const form_textarea = (o => T` - - -`); -;// CONCATENATED MODULE: ./src/templates/form_url.js - -/* harmony default export */ const form_url = (o => T` - `); -;// CONCATENATED MODULE: ./src/templates/form_username.js - -/* harmony default export */ const form_username = (o => T` -
- ${o.label ? T`` : ''} -
-
- -
${o.domain}
-
-
-
`); -;// CONCATENATED MODULE: ./src/templates/hyperlink.js - - - -function onClickXMPPURI(ev) { - ev.preventDefault(); - api.rooms.open(ev.target.href); -} - -/* harmony default export */ const hyperlink = ((uri, url_text) => { - let normalized_url = uri.normalize()._string; - - const pretty_url = uri._parts.urn ? normalized_url : uri.readable(); - const visible_url = url_text || pretty_url; - - if (!uri._parts.protocol && !normalized_url.startsWith('http://') && !normalized_url.startsWith('https://')) { - normalized_url = 'http://' + normalized_url; - } - - if (uri._parts.protocol === 'xmpp' && uri._parts.query === 'join') { - return T` - ${visible_url}`; - } - - return T`${visible_url}`; -}); -;// CONCATENATED MODULE: ./src/templates/video.js - -/* harmony default export */ const video = ((url, hide_url) => T`${hide_url ? '' : T`${url}`}`); -;// CONCATENATED MODULE: ./src/utils/html.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description This is the DOM/HTML utilities module. - */ - - - - - - - - - - - - - - - - - - -const { - sizzle: html_sizzle -} = core_converse.env; -const APPROVED_URL_PROTOCOLS = ['http', 'https', 'xmpp', 'mailto']; - -function getAutoCompleteProperty(name, options) { - return { - 'muc#roomconfig_lang': 'language', - 'muc#roomconfig_roomsecret': options !== null && options !== void 0 && options.new_password ? 'new-password' : 'current-password' - }[name]; -} - -const XFORM_TYPE_MAP = { - 'text-private': 'password', - 'text-single': 'text', - 'fixed': 'label', - 'boolean': 'checkbox', - 'hidden': 'hidden', - 'jid-multi': 'textarea', - 'list-single': 'dropdown', - 'list-multi': 'dropdown' -}; - -function slideOutWrapup(el) { - /* Wrapup function for slideOut. */ - el.removeAttribute('data-slider-marker'); - el.classList.remove('collapsed'); - el.style.overflow = ''; - el.style.height = ''; -} - -function getFileName(uri) { - try { - return decodeURI(uri.filename()); - } catch (error) { - headless_log.debug(error); - return uri.filename(); - } -} -/** - * Returns the markup for a URL that points to a downloadable asset - * (such as a video, image or audio file). - * @method u#getOOBURLMarkup - * @param { String } url - * @returns { String } - */ - - -function getOOBURLMarkup(url) { - const uri = getURI(url); - - if (uri === null) { - return url; - } - - if (isVideoURL(uri)) { - return video(url); - } else if (isAudioURL(uri)) { - return audio(url); - } else if (isImageURL(uri)) { - return file(uri.toString(), getFileName(uri)); - } else { - return file(uri.toString(), getFileName(uri)); - } -} -/** - * Return the height of the passed in DOM element, - * based on the heights of its children. - * @method u#calculateElementHeight - * @param {HTMLElement} el - * @returns {integer} - */ - -utils_core.calculateElementHeight = function (el) { - return Array.from(el.children).reduce((result, child) => result + child.offsetHeight, 0); -}; - -utils_core.getNextElement = function (el, selector = '*') { - let next_el = el.nextElementSibling; - - while (next_el !== null && !html_sizzle.matchesSelector(next_el, selector)) { - next_el = next_el.nextElementSibling; - } - - return next_el; -}; - -utils_core.getPreviousElement = function (el, selector = '*') { - let prev_el = el.previousElementSibling; - - while (prev_el !== null && !html_sizzle.matchesSelector(prev_el, selector)) { - prev_el = prev_el.previousElementSibling; - } - - return prev_el; -}; - -utils_core.getFirstChildElement = function (el, selector = '*') { - let first_el = el.firstElementChild; - - while (first_el !== null && !html_sizzle.matchesSelector(first_el, selector)) { - first_el = first_el.nextElementSibling; - } - - return first_el; -}; - -utils_core.getLastChildElement = function (el, selector = '*') { - let last_el = el.lastElementChild; - - while (last_el !== null && !html_sizzle.matchesSelector(last_el, selector)) { - last_el = last_el.previousElementSibling; - } - - return last_el; -}; - -utils_core.hasClass = function (className, el) { - return el instanceof Element && el.classList.contains(className); -}; - -utils_core.toggleClass = function (className, el) { - utils_core.hasClass(className, el) ? utils_core.removeClass(className, el) : utils_core.addClass(className, el); -}; -/** - * Add a class to an element. - * @method u#addClass - * @param {string} className - * @param {Element} el - */ - - -utils_core.addClass = function (className, el) { - el instanceof Element && el.classList.add(className); - return el; -}; -/** - * Remove a class from an element. - * @method u#removeClass - * @param {string} className - * @param {Element} el - */ - - -utils_core.removeClass = function (className, el) { - el instanceof Element && el.classList.remove(className); - return el; -}; - -utils_core.removeElement = function (el) { - el instanceof Element && el.parentNode && el.parentNode.removeChild(el); - return el; -}; - -utils_core.getElementFromTemplateResult = function (tr) { - const div = document.createElement('div'); - V(tr, div); - return div.firstElementChild; -}; - -utils_core.showElement = el => { - utils_core.removeClass('collapsed', el); - utils_core.removeClass('hidden', el); -}; - -utils_core.hideElement = function (el) { - el instanceof Element && el.classList.add('hidden'); - return el; -}; - -utils_core.ancestor = function (el, selector) { - let parent = el; - - while (parent !== null && !html_sizzle.matchesSelector(parent, selector)) { - parent = parent.parentElement; - } - - return parent; -}; -/** - * Return the element's siblings until one matches the selector. - * @private - * @method u#nextUntil - * @param { HTMLElement } el - * @param { String } selector - */ - - -utils_core.nextUntil = function (el, selector) { - const matches = []; - let sibling_el = el.nextElementSibling; - - while (sibling_el !== null && !sibling_el.matches(selector)) { - matches.push(sibling_el); - sibling_el = sibling_el.nextElementSibling; - } - - return matches; -}; -/** - * Helper method that replace HTML-escaped symbols with equivalent characters - * (e.g. transform occurrences of '&' to '&') - * @private - * @method u#unescapeHTML - * @param { String } string - a String containing the HTML-escaped symbols. - */ - - -utils_core.unescapeHTML = function (string) { - var div = document.createElement('div'); - div.innerHTML = string; - return div.innerText; -}; - -utils_core.escapeHTML = function (string) { - return string.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); -}; - -function isProtocolApproved(protocol, safeProtocolsList = APPROVED_URL_PROTOCOLS) { - return !!safeProtocolsList.includes(protocol); -} // Will return false if URL is malformed or contains disallowed characters - - -function isUrlValid(urlString) { - try { - const url = new URL(urlString); - return !!url; - } catch (error) { - return false; - } -} - -function getHyperlinkTemplate(url) { - const http_url = RegExp('^w{3}.', 'ig').test(url) ? `http://${url}` : url; - const uri = getURI(url); - - if (uri !== null && isUrlValid(http_url) && (isProtocolApproved(uri._parts.protocol) || !uri._parts.protocol)) { - return hyperlink(uri, url); - } - - return url; -} - -utils_core.slideInAllElements = function (elements, duration = 300) { - return Promise.all(Array.from(elements).map(e => utils_core.slideIn(e, duration))); -}; - -utils_core.slideToggleElement = function (el, duration) { - if (utils_core.hasClass('collapsed', el) || utils_core.hasClass('hidden', el)) { - return utils_core.slideOut(el, duration); - } else { - return utils_core.slideIn(el, duration); - } -}; -/** - * Shows/expands an element by sliding it out of itself - * @private - * @method u#slideOut - * @param { HTMLElement } el - The HTML string - * @param { Number } duration - The duration amount in milliseconds - */ - - -utils_core.slideOut = function (el, duration = 200) { - return new Promise((resolve, reject) => { - if (!el) { - const err = 'An element needs to be passed in to slideOut'; - headless_log.warn(err); - reject(new Error(err)); - return; - } - - const marker = el.getAttribute('data-slider-marker'); - - if (marker) { - el.removeAttribute('data-slider-marker'); - window.cancelAnimationFrame(marker); - } - - const end_height = utils_core.calculateElementHeight(el); - - if (window.converse_disable_effects) { - // Effects are disabled (for tests) - el.style.height = end_height + 'px'; - slideOutWrapup(el); - resolve(); - return; - } - - if (!utils_core.hasClass('collapsed', el) && !utils_core.hasClass('hidden', el)) { - resolve(); - return; - } - - const steps = duration / 17; // We assume 17ms per animation which is ~60FPS - - let height = 0; - - function draw() { - height += end_height / steps; - - if (height < end_height) { - el.style.height = height + 'px'; - el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw)); - } else { - // We recalculate the height to work around an apparent - // browser bug where browsers don't know the correct - // offsetHeight beforehand. - el.removeAttribute('data-slider-marker'); - el.style.height = utils_core.calculateElementHeight(el) + 'px'; - el.style.overflow = ''; - el.style.height = ''; - resolve(); - } - } - - el.style.height = '0'; - el.style.overflow = 'hidden'; - el.classList.remove('hidden'); - el.classList.remove('collapsed'); - el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw)); - }); -}; - -utils_core.slideIn = function (el, duration = 200) { - /* Hides/collapses an element by sliding it into itself. */ - return new Promise((resolve, reject) => { - if (!el) { - const err = 'An element needs to be passed in to slideIn'; - headless_log.warn(err); - return reject(new Error(err)); - } else if (utils_core.hasClass('collapsed', el)) { - return resolve(el); - } else if (window.converse_disable_effects) { - // Effects are disabled (for tests) - el.classList.add('collapsed'); - el.style.height = ''; - return resolve(el); - } - - const marker = el.getAttribute('data-slider-marker'); - - if (marker) { - el.removeAttribute('data-slider-marker'); - window.cancelAnimationFrame(marker); - } - - const original_height = el.offsetHeight, - steps = duration / 17; // We assume 17ms per animation which is ~60FPS - - let height = original_height; - el.style.overflow = 'hidden'; - - function draw() { - height -= original_height / steps; - - if (height > 0) { - el.style.height = height + 'px'; - el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw)); - } else { - el.removeAttribute('data-slider-marker'); - el.classList.add('collapsed'); - el.style.height = ''; - resolve(el); - } - } - - el.setAttribute('data-slider-marker', window.requestAnimationFrame(draw)); - }); -}; - -function afterAnimationEnds(el, callback) { - el.classList.remove('visible'); - - if (lodash_es_isFunction(callback)) { - callback(); - } -} - -utils_core.isInDOM = function (el) { - return document.querySelector('body').contains(el); -}; - -utils_core.isVisible = function (el) { - if (el === null) { - return false; - } - - if (utils_core.hasClass('hidden', el)) { - return false; - } // XXX: Taken from jQuery's "visible" implementation - - - return el.offsetWidth > 0 || el.offsetHeight > 0 || el.getClientRects().length > 0; -}; - -utils_core.fadeIn = function (el, callback) { - if (!el) { - headless_log.warn('An element needs to be passed in to fadeIn'); - } - - if (window.converse_disable_effects) { - el.classList.remove('hidden'); - return afterAnimationEnds(el, callback); - } - - if (utils_core.hasClass('hidden', el)) { - el.classList.add('visible'); - el.classList.remove('hidden'); - el.addEventListener('webkitAnimationEnd', () => afterAnimationEnds(el, callback)); - el.addEventListener('animationend', () => afterAnimationEnds(el, callback)); - el.addEventListener('oanimationend', () => afterAnimationEnds(el, callback)); - } else { - afterAnimationEnds(el, callback); - } -}; -/** - * Takes an XML field in XMPP XForm (XEP-004: Data Forms) format returns a - * [TemplateResult](https://lit.polymer-project.org/api/classes/_lit_html_.templateresult.html). - * @method u#xForm2TemplateResult - * @param { XMLElement } field - the field to convert - * @param { XMLElement } stanza - the containing stanza - * @param { Object } options - * @returns { TemplateResult } - */ - - -utils_core.xForm2TemplateResult = function (field, stanza, options) { - if (field.getAttribute('type') === 'list-single' || field.getAttribute('type') === 'list-multi') { - const values = utils_core.queryChildren(field, 'value').map(el => el === null || el === void 0 ? void 0 : el.textContent); - const options = utils_core.queryChildren(field, 'option').map(option => { - var _option$querySelector; - - const value = (_option$querySelector = option.querySelector('value')) === null || _option$querySelector === void 0 ? void 0 : _option$querySelector.textContent; - return { - 'value': value, - 'label': option.getAttribute('label'), - 'selected': values.includes(value), - 'required': !!field.querySelector('required') - }; - }); - return form_select({ - options, - 'id': utils_core.getUniqueId(), - 'label': field.getAttribute('label'), - 'multiple': field.getAttribute('type') === 'list-multi', - 'name': field.getAttribute('var'), - 'required': !!field.querySelector('required') - }); - } else if (field.getAttribute('type') === 'fixed') { - var _field$querySelector; - - const text = (_field$querySelector = field.querySelector('value')) === null || _field$querySelector === void 0 ? void 0 : _field$querySelector.textContent; - return form_help({ - text - }); - } else if (field.getAttribute('type') === 'jid-multi') { - var _field$querySelector2; - - return form_textarea({ - 'name': field.getAttribute('var'), - 'label': field.getAttribute('label') || '', - 'value': (_field$querySelector2 = field.querySelector('value')) === null || _field$querySelector2 === void 0 ? void 0 : _field$querySelector2.textContent, - 'required': !!field.querySelector('required') - }); - } else if (field.getAttribute('type') === 'boolean') { - var _field$querySelector3; - - const value = (_field$querySelector3 = field.querySelector('value')) === null || _field$querySelector3 === void 0 ? void 0 : _field$querySelector3.textContent; - return form_checkbox({ - 'id': utils_core.getUniqueId(), - 'name': field.getAttribute('var'), - 'label': field.getAttribute('label') || '', - 'checked': (value === '1' || value === 'true') && 'checked="1"' || '', - 'required': !!field.querySelector('required') - }); - } else if (field.getAttribute('var') === 'url') { - var _field$querySelector4; - - return form_url({ - 'label': field.getAttribute('label') || '', - 'value': (_field$querySelector4 = field.querySelector('value')) === null || _field$querySelector4 === void 0 ? void 0 : _field$querySelector4.textContent - }); - } else if (field.getAttribute('var') === 'username') { - var _field$querySelector5; - - return form_username({ - 'domain': ' @' + options.domain, - 'name': field.getAttribute('var'), - 'type': XFORM_TYPE_MAP[field.getAttribute('type')], - 'label': field.getAttribute('label') || '', - 'value': (_field$querySelector5 = field.querySelector('value')) === null || _field$querySelector5 === void 0 ? void 0 : _field$querySelector5.textContent, - 'required': !!field.querySelector('required') - }); - } else if (field.getAttribute('var') === 'ocr') { - // Captcha - const uri = field.querySelector('uri'); - const el = html_sizzle('data[cid="' + uri.textContent.replace(/^cid:/, '') + '"]', stanza)[0]; - return form_captcha({ - 'label': field.getAttribute('label'), - 'name': field.getAttribute('var'), - 'data': el === null || el === void 0 ? void 0 : el.textContent, - 'type': uri.getAttribute('type'), - 'required': !!field.querySelector('required') - }); - } else { - var _field$querySelector6; - - const name = field.getAttribute('var'); - return form_input({ - 'id': utils_core.getUniqueId(), - 'label': field.getAttribute('label') || '', - 'name': name, - 'fixed_username': options === null || options === void 0 ? void 0 : options.fixed_username, - 'autocomplete': getAutoCompleteProperty(name, options), - 'placeholder': null, - 'required': !!field.querySelector('required'), - 'type': XFORM_TYPE_MAP[field.getAttribute('type')], - 'value': (_field$querySelector6 = field.querySelector('value')) === null || _field$querySelector6 === void 0 ? void 0 : _field$querySelector6.textContent - }); - } -}; - -Object.assign(utils_core, { - getOOBURLMarkup -}); -/* harmony default export */ const html = (utils_core); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/components/styles/gif.scss -var gif = __webpack_require__(3735); -;// CONCATENATED MODULE: ./src/shared/components/styles/gif.scss - - - - - - - - - - - -var gif_options = {}; - -gif_options.styleTagTransform = (styleTagTransform_default()); -gif_options.setAttributes = (setAttributesWithoutAttributes_default()); - - gif_options.insert = insertBySelector_default().bind(null, "head"); - -gif_options.domAPI = (styleDomAPI_default()); -gif_options.insertStyleElement = (insertStyleElement_default()); - -var gif_update = injectStylesIntoStyleTag_default()(gif/* default */.Z, gif_options); - - - - - /* harmony default export */ const styles_gif = (gif/* default */.Z && gif/* default.locals */.Z.locals ? gif/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/components/gif.js - - - - - - -class ConverseGIF extends CustomElement { - static get properties() { - /** - * @typedef { Object } ConverseGIFComponentProperties - * @property { Boolean } autoplay - * @property { Boolean } noloop - * @property { String } progress_color - * @property { String } nick - * @property { ('url'|'empty'|'error') } fallback - * @property { String } src - */ - return { - 'autoplay': { - type: Boolean - }, - 'noloop': { - type: Boolean - }, - 'progress_color': { - type: String - }, - 'fallback': { - type: String - }, - 'src': { - type: String - } - }; - } - - constructor() { - super(); - this.autoplay = false; - this.noloop = false; - this.fallback = 'url'; - } - - initialize() { - const options = { - 'autoplay': this.autoplay, - 'loop': !this.noloop - }; - - if (this.progress_color) { - options['progress_color'] = this.progress_color; - } - - this.supergif = new ConverseGif(this, options); - } - - updated(changed) { - if (!this.supergif || changed.has('src')) { - this.initialize(); - return; - } - - if (changed.has('autoplay')) { - this.supergif.options.autoplay = this.autoplay; - } - - if (changed.has('noloop')) { - this.supergif.options.loop = !this.noloop; - } - - if (changed.has('progress_color')) { - this.supergif.options.progress_color = this.progress_color; - } - } - - render() { - var _this$supergif; - - return (_this$supergif = this.supergif) !== null && _this$supergif !== void 0 && _this$supergif.load_error && ['url', 'empty'].includes(this.fallback) ? this.renderErrorFallback() : T` this.setHover()} - @mouseleave=${() => this.unsetHover()} - @click=${ev => this.onControlsClicked(ev)}>`; - } - - renderErrorFallback() { - if (this.fallback === 'url') { - return getHyperlinkTemplate(this.src); - } else if (this.fallback === 'empty') { - return ''; - } - } - - setHover() { - if (this.supergif) { - this.supergif.hovering = true; - this.hover_timeout && clearTimeout(this.hover_timeout); - this.hover_timeout = setTimeout(() => this.unsetHover(), 2000); - } - } - - unsetHover() { - if (this.supergif) this.supergif.hovering = false; - } - - onControlsClicked(ev) { - ev.preventDefault(); - - if (this.supergif.playing) { - this.supergif.pause(); - } else { - // When the user manually clicks play, we turn on looping - this.supergif.options.loop = true; - this.supergif.play(); - } - } - -} -api.elements.define('converse-gif', ConverseGIF); -;// CONCATENATED MODULE: ./src/templates/gif.js - - -/* harmony default export */ const templates_gif = ((url, hide_url) => T`${hide_url ? '' : T`${url}`}`); -;// CONCATENATED MODULE: ./node_modules/lit/async-directive.js - -;// CONCATENATED MODULE: ./src/shared/directives/image.js - - - - - - -const { - URI: image_URI -} = core_converse.env; - -class ImageDirective extends async_directive_c { - render(src, href, onLoad, onClick) { - return href ? T`${this.renderImage(src, href, onLoad, onClick)}` : this.renderImage(src, href, onLoad, onClick); - } - - renderImage(src, href, onLoad, onClick) { - return T` this.onError(src, href, onLoad, onClick)} - @load=${onLoad}/>`; - } - - onError(src, href, onLoad, onClick) { - if (isURLWithImageExtension(src)) { - href && this.setValue(getHyperlinkTemplate(href)); - } else { - // Before giving up and falling back to just rendering a hyperlink, - // we attach `.png` and try one more time. - // This works with some Imgur URLs - const uri = new image_URI(src); - const filename = uri.filename(); - uri.filename(`${filename}.png`); - this.setValue(renderImage(uri.toString(), href, onLoad, onClick)); - } - } - -} -/** - * lit directive which attempts to render an element from a URL. - * It will fall back to rendering an element if it can't. - * - * @param { String } src - The value that will be assigned to the `src` attribute of the `` element. - * @param { String } href - The value that will be assigned to the `href` attribute of the `` element. - * @param { Function } onLoad - A callback function to be called once the image has loaded. - * @param { Function } onClick - A callback function to be called once the image has been clicked. - */ - - -const renderImage = directive_i(ImageDirective); -;// CONCATENATED MODULE: ./src/templates/image.js - - -/* harmony default export */ const src_templates_image = (o => T`${renderImage(o.url, o.href, o.onLoad, o.onClick)}`); -;// CONCATENATED MODULE: ./src/shared/directives/styling.js - - - - - -async function transform(t) { - await t.addTemplates(); - return t.payload; -} - -class StylingDirective extends directive_s { - render(txt, offset, mentions, options) { - // eslint-disable-line class-methods-use-this - const t = new RichText(txt, offset, mentions, Object.assign(options, { - 'show_images': false, - 'embed_videos': false, - 'embed_audio': false - })); - return T`${until_o(transform(t), T`${t}`)}`; - } - -} - -const renderStylingDirectiveBody = directive_i(StylingDirective); -;// CONCATENATED MODULE: ./src/shared/styling.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - * @description Utility functions to help with parsing XEP-393 message styling hints - * @todo Other parsing helpers can be made more abstract and placed here. - */ - - -const bracketing_directives = ['*', '_', '~', '`']; -const styling_directives = [...bracketing_directives, '```', '>']; -const styling_map = { - '*': { - 'name': 'strong', - 'type': 'span' - }, - '_': { - 'name': 'emphasis', - 'type': 'span' - }, - '~': { - 'name': 'strike', - 'type': 'span' - }, - '`': { - 'name': 'preformatted', - 'type': 'span' - }, - '```': { - 'name': 'preformatted_block', - 'type': 'block' - }, - '>': { - 'name': 'quote', - 'type': 'block' - } -}; -const dont_escape = ['_', '>', '`', '~']; -const styling_templates = { - // m is the chatbox model - // i is the offset of this directive relative to the start of the original message - 'emphasis': (txt, i, mentions, options) => T`_${renderStylingDirectiveBody(txt, i, mentions, options)}_`, - 'preformatted': txt => T`\`${txt}\``, - 'preformatted_block': txt => T`
\`\`\`
${txt}
\`\`\`
`, - 'quote': (txt, i, mentions, options) => T`
${renderStylingDirectiveBody(txt, i, mentions, options)}
`, - 'strike': (txt, i, mentions, options) => T`~${renderStylingDirectiveBody(txt, i, mentions, options)}~`, - 'strong': (txt, i, mentions, options) => T`*${renderStylingDirectiveBody(txt, i, mentions, options)}*` -}; -/** - * Checks whether a given character "d" at index "i" of "text" is a valid opening or closing directive. - * @param { String } d - The potential directive - * @param { String } text - The text in which the directive appears - * @param { Number } i - The directive index - * @param { Boolean } opening - Check for a valid opening or closing directive - */ - -function isValidDirective(d, text, i, opening) { - // Ignore directives that are parts of words - // More info on the Regexes used here: https://javascript.info/regexp-unicode#unicode-properties-p - if (opening) { - const regex = RegExp(dont_escape.includes(d) ? `^(\\p{L}|\\p{N})${d}` : `^(\\p{L}|\\p{N})\\${d}`, 'u'); - - if (i > 1 && regex.test(text.slice(i - 1))) { - return false; - } - - const is_quote = isQuoteDirective(d); - - if (is_quote && i > 0 && text[i - 1] !== '\n') { - // Quote directives must be on newlines - return false; - } else if (bracketing_directives.includes(d) && text[i + 1] === d) { - // Don't consider empty bracketing directives as valid (e.g. **, `` etc.) - return false; - } - } else { - const regex = RegExp(dont_escape.includes(d) ? `^${d}(\\p{L}|\\p{N})` : `^\\${d}(\\p{L}|\\p{N})`, 'u'); - - if (i < text.length - 1 && regex.test(text.slice(i))) { - return false; - } - - if (bracketing_directives.includes(d) && text[i - 1] === d) { - // Don't consider empty directives as valid (e.g. **, `` etc.) - return false; - } - } - - return true; -} -/** - * Given a specific index "i" of "text", return the directive it matches or - * null otherwise. - * @param { String } text - The text in which the directive appears - * @param { Number } i - The directive index - * @param { Boolean } opening - Whether we're looking for an opening or closing directive - */ - - -function getDirective(text, i, opening = true) { - let d; - - if (/(^```\s*\n|^```\s*$)/.test(text.slice(i)) && (i === 0 || text[i - 1] === '\n' || text[i - 1] === '>')) { - d = text.slice(i, i + 3); - } else if (styling_directives.includes(text.slice(i, i + 1))) { - d = text.slice(i, i + 1); - if (!isValidDirective(d, text, i, opening)) return null; - } else { - return null; - } - - return d; -} -/** - * Given a directive "d", which occurs in "text" at index "i", check that it - * has a valid closing directive and return the length from start to end of the - * directive. - * @param { String } d -The directive - * @param { Number } i - The directive index - * @param { String } text -The text in which the directive appears - */ - - -function getDirectiveLength(d, text, i) { - if (!d) { - return 0; - } - - const begin = i; - i += d.length; - - if (isQuoteDirective(d)) { - i += text.slice(i).split(/\n[^>]/).shift().length; - return i - begin; - } else if (styling_map[d].type === 'span') { - const line = text.slice(i).split('\n').shift(); - let j = 0; - let idx = line.indexOf(d); - - while (idx !== -1) { - if (getDirective(text, i + idx, false) === d) { - return idx + 2 * d.length; - } - - idx = line.indexOf(d, j++); - } - - return 0; - } else { - // block directives - const substring = text.slice(i + 1); - let j = 0; - let idx = substring.indexOf(d); - - while (idx !== -1) { - if (getDirective(text, i + 1 + idx, false) === d) { - return idx + 1 + 2 * d.length; - } - - idx = substring.indexOf(d, j++); - } - - return 0; - } -} - -function getDirectiveAndLength(text, i) { - const d = getDirective(text, i); - const length = d ? getDirectiveLength(d, text, i) : 0; - return length > 0 ? { - d, - length - } : {}; -} -const isQuoteDirective = d => ['>', '>'].includes(d); -function getDirectiveTemplate(d, text, offset, mentions, options) { - const template = styling_templates[styling_map[d].name]; - - if (isQuoteDirective(d)) { - const newtext = text.replace(/\n>/g, '\n') // Don't show the directive itself - .replace(/\n$/, ''); // Trim line-break at the end - - return template(newtext, offset, mentions, options); - } else { - return template(text, offset, mentions, options); - } -} -function containsDirectives(text) { - for (let i = 0; i < styling_directives.length; i++) { - if (text.includes(styling_directives[i])) { - return true; - } - } -} -;// CONCATENATED MODULE: ./src/shared/rich-text.js - - - - - - - - - - - - -const { - URI: rich_text_URI -} = core_converse.env; - -const rich_text_isString = s => typeof s === 'string'; // We don't render more than two line-breaks, replace extra line-breaks with -// the zero-width whitespace character - - -const collapseLineBreaks = text => text.replace(/\n\n+/g, m => `\n${'\u200B'.repeat(m.length - 2)}\n`); - -const tpl_mention_with_nick = o => T`${o.mention}`; - -const tpl_mention = o => T`${o.mention}`; -/** - * @class RichText - * A String subclass that is used to render rich text (i.e. text that contains - * hyperlinks, images, mentions, styling etc.). - * - * The "rich" parts of the text is represented by lit TemplateResult - * objects which are added via the {@link RichText.addTemplateResult} - * method and saved as metadata. - * - * By default Converse adds TemplateResults to support emojis, hyperlinks, - * images, map URIs and mentions. - * - * 3rd party plugins can listen for the `beforeMessageBodyTransformed` - * and/or `afterMessageBodyTransformed` events and then call - * `addTemplateResult` on the RichText instance in order to add their own - * rich features. - */ - - -class RichText extends String { - /** - * Create a new {@link RichText} instance. - * @param { String } text - The text to be annotated - * @param { Integer } offset - The offset of this particular piece of text - * from the start of the original message text. This is necessary because - * RichText instances can be nested when templates call directives - * which create new RichText instances (as happens with XEP-393 styling directives). - * @param { Array } mentions - An array of mention references - * @param { Object } options - * @param { String } options.nick - The current user's nickname (only relevant if the message is in a XEP-0045 MUC) - * @param { Boolean } options.render_styling - Whether XEP-0393 message styling should be applied to the message - * @param { Boolean } options.show_images - Whether image URLs should be rendered as tags. - * @param { Boolean } options.embed_videos - Whether video URLs should be rendered as
- `; -}); -;// CONCATENATED MODULE: ./src/modals/user-details.js - - - - - -const user_details_u = core_converse.env.utils; - -function removeContact(contact) { - contact.removeFromRoster(() => contact.destroy(), e => { - e && headless_log.error(e); - api.alert('error', __('Error'), [__('Sorry, there was an error while trying to remove %1$s as a contact.', contact.getDisplayName())]); - }); -} - -const UserDetailsModal = base.extend({ - id: 'user-details-modal', - persistent: true, - events: { - 'click button.refresh-contact': 'refreshContact', - 'click .fingerprint-trust .btn input': 'toggleDeviceTrust' - }, - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.model.rosterContactAdded.then(() => this.registerContactEventHandlers()); - this.listenTo(this.model, 'change', this.render); - this.registerContactEventHandlers(); - /** - * Triggered once the UserDetailsModal has been initialized - * @event _converse#userDetailsModalInitialized - * @type { _converse.ChatBox } - * @example _converse.api.listen.on('userDetailsModalInitialized', chatbox => { ... }); - */ - - api.trigger('userDetailsModalInitialized', this.model); - }, - - toHTML() { - var _this$model; - - const vcard = (_this$model = this.model) === null || _this$model === void 0 ? void 0 : _this$model.vcard; - const vcard_json = vcard ? vcard.toJSON() : {}; - return user_details(Object.assign(this.model.toJSON(), vcard_json, { - '_converse': shared_converse, - 'allow_contact_removal': api.settings.get('allow_contact_removal'), - 'display_name': this.model.getDisplayName(), - 'is_roster_contact': this.model.contact !== undefined, - 'removeContact': ev => this.removeContact(ev), - 'view': this, - 'utils': user_details_u - })); - }, - - registerContactEventHandlers() { - if (this.model.contact !== undefined) { - this.listenTo(this.model.contact, 'change', this.render); - this.listenTo(this.model.contact.vcard, 'change', this.render); - this.model.contact.on('destroy', () => { - delete this.model.contact; - this.render(); - }); - } - }, - - async refreshContact(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); - } - - const refresh_icon = this.el.querySelector('.fa-refresh'); - user_details_u.addClass('fa-spin', refresh_icon); - - try { - await api.vcard.update(this.model.contact.vcard, true); - } catch (e) { - headless_log.fatal(e); - this.alert(__('Sorry, something went wrong while trying to refresh'), 'danger'); - } - - user_details_u.removeClass('fa-spin', refresh_icon); - }, - - removeContact(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - - if (!api.settings.get('allow_contact_removal')) { - return; - } - - const result = confirm(__("Are you sure you want to remove this contact?")); - - if (result === true) { - // XXX: The `dismissHandler` in bootstrap.native tries to - // reference the remove button after it's been cleared from - // the DOM, so we delay removing the contact to give it time. - setTimeout(() => removeContact(this.model.contact), 1); - this.modal.hide(); - } - } - -}); -shared_converse.UserDetailsModal = UserDetailsModal; -/* harmony default export */ const modals_user_details = (UserDetailsModal); -;// CONCATENATED MODULE: ./src/shared/chat/templates/info-message.js - - - -const { - dayjs: info_message_dayjs -} = core_converse.env; -/* harmony default export */ const info_message = (el => { - const isodate = info_message_dayjs(el.model.get('time')).toISOString(); - - const i18n_retry = __('Retry'); - - return T` -
- -
- - -
- ${el.model.get('reason') ? T`${el.model.get('reason')}` : ``} - ${el.model.get('error_text') ? T`${el.model.get('error_text')}` : ``} - ${el.model.get('retry_event_id') ? T`${i18n_retry}` : ''} -
`; -}); -;// CONCATENATED MODULE: ./src/shared/chat/templates/unfurl.js - - - -function isValidURL(url) { - // We don't consider relative URLs as valid - return !!getURI(url).host(); -} - -function isValidImage(image) { - return image && isImageDomainAllowed(image) && isValidURL(image); -} - -function shouldHideMediaURL(o) { - return isGIFURL(o.url) || isVideoURL(o.url) || isAudioURL(o.url); -} - -const tpl_url_wrapper = (o, wrapped_template) => o.url && isValidURL(o.url) && !isGIFURL(o.url) ? T`${wrapped_template(o)}` : wrapped_template(o); - -const tpl_image = o => T``; - -/* harmony default export */ const unfurl = (o => { - const valid_image = isValidImage(o.image); - const has_body_info = o.title || o.description || o.url; - - if (valid_image || has_body_info) { - return T`
- ${valid_image ? tpl_url_wrapper(o, tpl_image) : ''} - ${has_body_info ? T` -
- ${o.title ? tpl_url_wrapper(o, o => T`
${o.title}
`) : ''} - ${o.description ? T`

` : ''} - ${o.url ? T`

${getURI(o.url).domain()}

` : ''} -
` : ''} -
`; - } else { - return ''; - } -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/chat/styles/unfurl.scss -var styles_unfurl = __webpack_require__(7415); -;// CONCATENATED MODULE: ./src/shared/chat/styles/unfurl.scss - - - - - - - - - - - -var unfurl_options = {}; - -unfurl_options.styleTagTransform = (styleTagTransform_default()); -unfurl_options.setAttributes = (setAttributesWithoutAttributes_default()); - - unfurl_options.insert = insertBySelector_default().bind(null, "head"); - -unfurl_options.domAPI = (styleDomAPI_default()); -unfurl_options.insertStyleElement = (insertStyleElement_default()); - -var unfurl_update = injectStylesIntoStyleTag_default()(styles_unfurl/* default */.Z, unfurl_options); - - - - - /* harmony default export */ const chat_styles_unfurl = (styles_unfurl/* default */.Z && styles_unfurl/* default.locals */.Z.locals ? styles_unfurl/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/chat/unfurl.js - - - - -class MessageUnfurl extends CustomElement { - static get properties() { - return { - description: { - type: String - }, - image: { - type: String - }, - jid: { - type: String - }, - title: { - type: String - }, - url: { - type: String - } - }; - } - - render() { - return unfurl(Object.assign({ - 'onload': () => this.onImageLoad() - }, { - description: this.description || '', - image: this.image || '', - title: this.title || '', - url: this.url || '' - })); - } - - onImageLoad() { - this.dispatchEvent(new CustomEvent('imageLoaded', { - detail: this, - 'bubbles': true - })); - } - -} -api.elements.define('converse-message-unfurl', MessageUnfurl); -;// CONCATENATED MODULE: ./src/shared/chat/templates/message.js - - - - -/* harmony default export */ const templates_message = ((el, o) => { - var _el$model$get, _el$model$get2; - - const i18n_new_messages = __('New messages'); - - return T` - ${o.is_first_unread ? T`

${i18n_new_messages}
` : ''} -
- - - - - ${o.should_show_avatar ? renderAvatar(el.getAvatarData()) : ''} -
- - ${!o.is_me_message ? T` - - ${o.username} - ${o.hats.map(h => T`${h.title}`)} - - ${o.is_encrypted ? T`` : ''} - ` : ''} -
-
- ${o.is_me_message ? T` -   - ${o.is_me_message ? '**' : ''}${o.username} ` : ''} - ${o.is_retracted ? el.renderRetraction() : el.renderMessageText()} -
- -
- - ${!el.model.get('hide_url_previews') ? (_el$model$get2 = el.model.get('ogp_metadata')) === null || _el$model$get2 === void 0 ? void 0 : _el$model$get2.map(m => { - var _el$chatbox; - - return T``; - }) : ''} -
-
`; -}); -;// CONCATENATED MODULE: ./src/shared/chat/templates/message-text.js - - - - - -const tpl_edited_icon = el => { - const i18n_edited = __('This message has been edited'); - - return T``; -}; - -/* harmony default export */ const message_text = (el => { - const i18n_show = __('Show more'); - - const is_groupchat_message = el.model.get('type') === 'groupchat'; - - const i18n_show_less = __('Show less'); - - const tpl_spoiler_hint = T` - - `; - const spoiler_classes = el.model.get('is_spoiler') ? `spoiler ${el.model.get('is_spoiler_visible') ? '' : 'hidden'}` : ''; - const text = el.model.getMessageText(); - const show_oob = el.model.get('oob_url') && text !== el.model.get('oob_url'); - return T` - ${el.model.get('is_spoiler') ? tpl_spoiler_hint : ''} - ${el.model.get('subject') ? T`
${el.model.get('subject')}
` : ''} - - - ${el.model.get('received') && !el.model.isMeCommand() && !is_groupchat_message ? T`` : ''} - ${el.model.get('edited') ? tpl_edited_icon(el) : ''} - - ${show_oob ? T`
${getOOBURLMarkup(el.model.get('oob_url'))}
` : ''} -
${el.model.get('error_text') || el.model.get('error')}
- `; -}); -;// CONCATENATED MODULE: ./src/templates/spinner.js - -/* harmony default export */ const spinner = ((o = {}) => { - var _o$classes; - - if ((_o$classes = o.classes) !== null && _o$classes !== void 0 && _o$classes.includes('hor_centered')) { - return T`
`; - } else { - return T``; - } -}); -// EXTERNAL MODULE: ./node_modules/lodash/debounce.js -var lodash_debounce = __webpack_require__(4971); -var debounce_default = /*#__PURE__*/__webpack_require__.n(lodash_debounce); -;// CONCATENATED MODULE: ./src/shared/chat/templates/new-day.js - -/* harmony default export */ const new_day = (o => T` -
-
- -
-`); -;// CONCATENATED MODULE: ./src/shared/chat/utils.js - - - -const { - dayjs: utils_dayjs -} = core_converse.env; -function onScrolledDown(model) { - if (!model.isHidden()) { - if (api.settings.get('allow_url_history_change')) { - // Clear location hash if set to one of the messages in our history - const hash = window.location.hash; - hash && model.messages.get(hash.slice(1)) && shared_converse.router.history.navigate(); - } - } -} -/** - * Called when the chat content is scrolled up or down. - * We want to record when the user has scrolled away from - * the bottom, so that we don't automatically scroll away - * from what the user is reading when new messages are received. - * - * Don't call this method directly, instead, call `markScrolled`, - * which debounces this method. - */ - -function _markScrolled(ev) { - const el = ev.target; - - if (el.nodeName.toLowerCase() !== 'converse-chat-content') { - return; - } - - let scrolled = true; - const is_at_bottom = Math.floor(el.scrollTop) === 0; - const is_at_top = Math.ceil(el.clientHeight - el.scrollTop) >= el.scrollHeight - Math.ceil(el.scrollHeight / 20); - - if (is_at_bottom) { - scrolled = false; - onScrolledDown(el.model); - } else if (is_at_top) { - /** - * Triggered once the chat's message area has been scrolled to the top - * @event _converse#chatBoxScrolledUp - * @property { _converse.ChatBoxView | _converse.ChatRoomView } view - * @example _converse.api.listen.on('chatBoxScrolledUp', obj => { ... }); - */ - api.trigger('chatBoxScrolledUp', el); - } - - if (el.model.get('scolled') !== scrolled) { - el.model.ui.set({ - scrolled - }); - } -} - -const markScrolled = debounce_default()(ev => _markScrolled(ev), 50); -/** - * Given a message object, returns a TemplateResult indicating a new day if - * the passed in message is more than a day later than its predecessor. - * @param { _converse.Message } - */ - -function getDayIndicator(message) { - var _message$collection; - - const messages = (_message$collection = message.collection) === null || _message$collection === void 0 ? void 0 : _message$collection.models; - - if (!messages) { - return; - } - - const idx = messages.indexOf(message); - const prev_message = messages[idx - 1]; - - if (!prev_message || utils_dayjs(message.get('time')).isAfter(utils_dayjs(prev_message.get('time')), 'day')) { - const day_date = utils_dayjs(message.get('time')).startOf('day'); - return new_day({ - 'type': 'date', - 'time': day_date.toISOString(), - 'datestring': day_date.format("dddd MMM Do YYYY") - }); - } -} -function getHats(message) { - if (message.get('type') === 'groupchat') { - var _message$occupant; - - const allowed_hats = api.settings.get('muc_hats').filter(hat => hat).map(hat => hat.toLowerCase()); - let vcard_roles = []; - - if (allowed_hats.includes('vcard_roles')) { - vcard_roles = message.vcard ? message.vcard.get('role') : null; - vcard_roles = vcard_roles ? vcard_roles.split(',').filter(hat => hat).map(hat => ({ - title: hat - })) : []; - } - - const muc_role = message.occupant ? [message.occupant.get('role')] : []; - const muc_affiliation = message.occupant ? [message.occupant.get('affiliation')] : []; - const affiliation_role_hats = [...muc_role, ...muc_affiliation].filter(hat => hat).filter(hat => allowed_hats.includes(hat.toLowerCase())).map(hat => ({ - title: hat - })); - const hats = allowed_hats.includes('xep317') ? ((_message$occupant = message.occupant) === null || _message$occupant === void 0 ? void 0 : _message$occupant.get('hats')) || [] : []; - return [...hats, ...vcard_roles, ...affiliation_role_hats]; - } - - return []; -} -;// CONCATENATED MODULE: ./src/shared/chat/message.js - - - - - - - - - - - - - - - - - - - -const { - Strophe: chat_message_Strophe, - dayjs: message_dayjs -} = core_converse.env; -class Message extends CustomElement { - static get properties() { - return { - jid: { - type: String - }, - mid: { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - async initialize() { - await this.setModels(); - - if (!this.model) { - // Happen during tests due to a race condition - headless_log.error('Could not find module for converse-chat-message'); - return; - } - - this.listenTo(this.chatbox, 'change:first_unread_id', this.requestUpdate); - this.listenTo(this.model, 'change', this.requestUpdate); - this.model.vcard && this.listenTo(this.model.vcard, 'change', this.requestUpdate); - - if (this.model.get('type') === 'groupchat') { - if (this.model.occupant) { - this.listenTo(this.model.occupant, 'change', this.requestUpdate); - } else { - this.listenTo(this.model, 'occupantAdded', () => { - this.listenTo(this.model.occupant, 'change', this.requestUpdate); - }); - } - } - } - - async setModels() { - this.chatbox = await api.chatboxes.get(this.jid); - await this.chatbox.initialized; - await this.chatbox.messages.fetched; - this.model = this.chatbox.messages.get(this.mid); - this.model && this.requestUpdate(); - } - - render() { - if (!this.model) { - return ''; - } else if (this.show_spinner) { - return spinner(); - } else if (this.model.get('file') && this.model.get('upload') !== shared_converse.SUCCESS) { - return this.renderFileProgress(); - } else if (['error', 'info'].includes(this.model.get('type'))) { - return this.renderInfoMessage(); - } else { - return this.renderChatMessage(); - } - } - - getProps() { - return Object.assign(this.model.toJSON(), this.getDerivedMessageProps()); - } - - renderInfoMessage() { - return info_message(this); - } - - renderFileProgress() { - if (!this.model.file) { - // Can happen when file upload failed and page was reloaded - return ''; - } - - const i18n_uploading = __('Uploading file:'); - - const filename = this.model.file.name; - const size = filesize_min_default()(this.model.file.size); - return T` -
- ${renderAvatar(this.getAvatarData())} -
- ${i18n_uploading} ${filename}, ${size} - -
-
`; - } - - renderChatMessage() { - return templates_message(this, this.getProps()); - } - - shouldShowAvatar() { - return api.settings.get('show_message_avatar') && !this.model.isMeCommand() && this.type !== 'headline'; - } - - getAvatarData() { - var _this$model$vcard, _this$model$vcard2; - - const image_type = ((_this$model$vcard = this.model.vcard) === null || _this$model$vcard === void 0 ? void 0 : _this$model$vcard.get('image_type')) || shared_converse.DEFAULT_IMAGE_TYPE; - const image_data = ((_this$model$vcard2 = this.model.vcard) === null || _this$model$vcard2 === void 0 ? void 0 : _this$model$vcard2.get('image')) || shared_converse.DEFAULT_IMAGE; - const image = "data:" + image_type + ";base64," + image_data; - return { - 'classes': 'chat-msg__avatar', - 'height': 36, - 'width': 36, - image - }; - } - - onUnfurlAnimationEnd() { - if (this.model.get('url_preview_transition') === 'fade-out') { - this.model.save({ - 'hide_url_previews': !this.model.get('hide_url_previews'), - 'url_preview_transition': 'fade-in' - }); - } - } - - async onRetryClicked() { - this.show_spinner = true; - this.requestUpdate(); - await api.trigger(this.model.get('retry_event_id'), { - 'synchronous': true - }); - this.model.destroy(); - this.parentElement.removeChild(this); - } - - isRetracted() { - return this.model.get('retracted') || this.model.get('moderated') === 'retracted'; - } - - hasMentions() { - const is_groupchat = this.model.get('type') === 'groupchat'; - return is_groupchat && this.model.get('sender') === 'them' && this.chatbox.isUserMentioned(this.model); - } - - getOccupantAffiliation() { - var _this$model$occupant; - - return (_this$model$occupant = this.model.occupant) === null || _this$model$occupant === void 0 ? void 0 : _this$model$occupant.get('affiliation'); - } - - getOccupantRole() { - var _this$model$occupant2; - - return (_this$model$occupant2 = this.model.occupant) === null || _this$model$occupant2 === void 0 ? void 0 : _this$model$occupant2.get('role'); - } - - getExtraMessageClasses() { - const extra_classes = [this.model.isFollowup() ? 'chat-msg--followup' : null, this.model.get('is_delayed') ? 'delayed' : null, this.model.isMeCommand() ? 'chat-msg--action' : null, this.isRetracted() ? 'chat-msg--retracted' : null, this.model.get('type'), this.shouldShowAvatar() ? 'chat-msg--with-avatar' : null].map(c => c); - - if (this.model.get('type') === 'groupchat') { - extra_classes.push(this.getOccupantRole() ?? ''); - extra_classes.push(this.getOccupantAffiliation() ?? ''); - - if (this.model.get('sender') === 'them' && this.hasMentions()) { - extra_classes.push('mentioned'); - } - } - - this.model.get('correcting') && extra_classes.push('correcting'); - return extra_classes.filter(c => c).join(" "); - } - - getDerivedMessageProps() { - const format = api.settings.get('time_format'); - return { - 'pretty_time': message_dayjs(this.model.get('edited') || this.model.get('time')).format(format), - 'has_mentions': this.hasMentions(), - 'hats': getHats(this.model), - 'is_first_unread': this.chatbox.get('first_unread_id') === this.model.get('id'), - 'is_me_message': this.model.isMeCommand(), - 'is_retracted': this.isRetracted(), - 'username': this.model.getDisplayName(), - 'should_show_avatar': this.shouldShowAvatar() - }; - } - - getRetractionText() { - if (this.model.get('type') === 'groupchat' && this.model.get('moderated_by')) { - const retracted_by_mod = this.model.get('moderated_by'); - const chatbox = this.model.collection.chatbox; - - if (!this.model.mod) { - this.model.mod = chatbox.occupants.findOccupant({ - 'jid': retracted_by_mod - }) || chatbox.occupants.findOccupant({ - 'nick': chat_message_Strophe.getResourceFromJid(retracted_by_mod) - }); - } - - const modname = this.model.mod ? this.model.mod.getDisplayName() : 'A moderator'; - return __('%1$s has removed this message', modname); - } else { - return __('%1$s has removed this message', this.model.getDisplayName()); - } - } - - renderRetraction() { - const retraction_text = this.isRetracted() ? this.getRetractionText() : null; - return T` -
${retraction_text}
- ${this.model.get('moderation_reason') ? T`${this.model.get('moderation_reason')}` : ''} - `; - } - - renderMessageText() { - return message_text(this); - } - - showUserModal(ev) { - if (this.model.get('sender') === 'me') { - api.modal.show(shared_converse.ProfileModal, { - model: this.model - }, ev); - } else if (this.model.get('type') === 'groupchat') { - ev.preventDefault(); - api.modal.show(modals_occupant, { - 'model': this.model.occupant - }, ev); - } else { - ev.preventDefault(); - const chatbox = this.model.collection.chatbox; - api.modal.show(modals_user_details, { - model: chatbox - }, ev); - } - } - - showMessageVersionsModal(ev) { - ev.preventDefault(); - api.modal.show(modals_message_versions, { - 'model': this.model - }, ev); - } - - toggleSpoilerMessage(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - this.model.save({ - 'is_spoiler_visible': !this.model.get('is_spoiler_visible') - }); - } - -} -api.elements.define('converse-chat-message', Message); -;// CONCATENATED MODULE: ./src/shared/chat/message-history.js - - - - - - - -class MessageHistory extends CustomElement { - static get properties() { - return { - model: { - type: Object - }, - messages: { - type: Array - } - }; - } - - render() { - const msgs = this.messages; - - if (msgs.length) { - return repeat_c(msgs, m => m.get('id'), m => T`${this.renderMessage(m)}`); - } else { - return ''; - } - } - - renderMessage(model) { - if (model.get('dangling_retraction') || model.get('is_only_key')) { - return ''; - } - - const template_hook = model.get('template_hook'); - - if (typeof template_hook === 'string') { - const template_promise = api.hook(template_hook, model, ''); - return until_o(template_promise, ''); - } else { - const template = T``; - const day = getDayIndicator(model); - return day ? [day, template] : template; - } - } - -} -api.elements.define('converse-message-history', MessageHistory); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/chat/styles/chat-content.scss -var chat_content = __webpack_require__(8269); -;// CONCATENATED MODULE: ./src/shared/chat/styles/chat-content.scss - - - - - - - - - - - -var chat_content_options = {}; - -chat_content_options.styleTagTransform = (styleTagTransform_default()); -chat_content_options.setAttributes = (setAttributesWithoutAttributes_default()); - - chat_content_options.insert = insertBySelector_default().bind(null, "head"); - -chat_content_options.domAPI = (styleDomAPI_default()); -chat_content_options.insertStyleElement = (insertStyleElement_default()); - -var chat_content_update = injectStylesIntoStyleTag_default()(chat_content/* default */.Z, chat_content_options); - - - - - /* harmony default export */ const styles_chat_content = (chat_content/* default */.Z && chat_content/* default.locals */.Z.locals ? chat_content/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/chat/chat-content.js - - - - - - -class ChatContent extends CustomElement { - static get properties() { - return { - jid: { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.removeEventListener('scroll', markScrolled); - } - - async initialize() { - await this.setModels(); - this.listenTo(this.model, 'change:hidden_occupants', this.requestUpdate); - this.listenTo(this.model.messages, 'add', this.requestUpdate); - this.listenTo(this.model.messages, 'change', this.requestUpdate); - this.listenTo(this.model.messages, 'remove', this.requestUpdate); - this.listenTo(this.model.messages, 'rendered', this.requestUpdate); - this.listenTo(this.model.messages, 'reset', this.requestUpdate); - this.listenTo(this.model.notifications, 'change', this.requestUpdate); - this.listenTo(this.model.ui, 'change', this.requestUpdate); - this.listenTo(this.model.ui, 'change:scrolled', this.scrollDown); - - if (this.model.occupants) { - this.listenTo(this.model.occupants, 'change', this.requestUpdate); - } - - this.addEventListener('scroll', markScrolled); - } - - async setModels() { - this.model = await api.chatboxes.get(this.jid); - await this.model.initialized; - this.requestUpdate(); - } - - render() { - var _this$model$ui; - - if (!this.model) { - return ''; - } // This element has "flex-direction: reverse", so elements here are - // shown in reverse order. - - - return T` -
${this.model.getNotificationsText()}
- - - ${(_this$model$ui = this.model.ui) !== null && _this$model$ui !== void 0 && _this$model$ui.get('chat-content-spinner-top') ? T`` : ''} - `; - } - - scrollDown() { - if (this.model.ui.get('scrolled')) { - return; - } - - if (this.scrollTo) { - const behavior = this.scrollTop ? 'smooth' : 'auto'; - this.scrollTo({ - 'top': 0, - behavior - }); - } else { - this.scrollTop = 0; - } - /** - * Triggered once the converse-chat-content element has been scrolled down to the bottom. - * @event _converse#chatBoxScrolledDown - * @type {object} - * @property { _converse.ChatBox | _converse.ChatRoom } chatbox - The chat model - * @example _converse.api.listen.on('chatBoxScrolledDown', obj => { ... }); - */ - - - api.trigger('chatBoxScrolledDown', { - 'chatbox': this.model - }); - } - -} -api.elements.define('converse-chat-content', ChatContent); -;// CONCATENATED MODULE: ./node_modules/lit-html/directives/unsafe-html.js - - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ - -class unsafe_html_n extends directive_s { - constructor(i) { - if (super(i), this.vt = A, i.type !== directive_t.CHILD) throw Error(this.constructor.directiveName + "() can only be used in child bindings"); - } - - render(r) { - if (r === A) return this.Vt = void 0, this.vt = r; - if (r === lit_html_w) return r; - if ("string" != typeof r) throw Error(this.constructor.directiveName + "() called with a non-string value"); - if (r === this.vt) return this.Vt; - this.vt = r; - const s = [r]; - return s.raw = s, this.Vt = { - _$litType$: this.constructor.resultType, - strings: s, - values: [] - }; - } - -} - -unsafe_html_n.directiveName = "unsafeHTML", unsafe_html_n.resultType = 1; -const unsafe_html_o = directive_i(unsafe_html_n); - -;// CONCATENATED MODULE: ./node_modules/lit/directives/unsafe-html.js - -;// CONCATENATED MODULE: ./src/shared/chat/help-messages.js - - - - - - -class ChatHelp extends CustomElement { - static get properties() { - return { - chat_type: { - type: String - }, - messages: { - type: Array - }, - model: { - type: Object - }, - type: { - type: String - } - }; - } - - render() { - const isodate = new Date().toISOString(); - return [T``, ...this.messages.map(m => this.renderHelpMessage({ - isodate, - 'markup': purify_default().sanitize(m, { - 'ALLOWED_TAGS': ['strong'] - }) - }))]; - } - - close() { - this.model.set({ - 'show_help_messages': false - }); - } - - renderHelpMessage(o) { - return T`
${unsafe_html_o(o.markup)}
`; - } - -} -api.elements.define('converse-chat-help', ChatHelp); -;// CONCATENATED MODULE: ./src/shared/chat/templates/emoji-picker.js - - - -const emoji_picker_u = core_converse.env.utils; - -const emoji_category = o => { - return T` -
  • - - ${o.emoji} -
  • - `; -}; - -const emoji_picker_header = o => { - const cats = api.settings.get('emoji_categories'); - - const transform = c => cats[c] ? emoji_category(Object.assign({ - 'category': c, - 'emoji': o.sn2Emoji(cats[c]) - }, o)) : ''; - - return T`
      ${Object.keys(cats).map(transform)}
    `; -}; - -const emoji_item = o => { - return T` -
  • - ${emoji_picker_u.shortnamesToEmojis(o.emoji.sn)} -
  • - `; -}; - -const tpl_search_results = o => { - const i18n_search_results = __('Search results'); - - return T` - - ${i18n_search_results} -
      - ${o.search_results.map(emoji => emoji_item(Object.assign({ - emoji - }, o)))} -
    -
    - `; -}; - -const emojis_for_category = o => { - return T` - ${__(api.settings.get('emoji_category_labels')[o.category])} -
      - ${Object.values(core_converse.emojis.json[o.category]).map(emoji => emoji_item(Object.assign({ - emoji - }, o)))} -
    `; -}; - -const tpl_all_emojis = o => { - const cats = api.settings.get('emoji_categories'); - return T` - - ${Object.keys(cats).map(c => cats[c] ? emojis_for_category(Object.assign({ - 'category': c - }, o)) : '')} - `; -}; - -const skintone_emoji = o => { - return T` -
  • - ${emoji_picker_u.shortnamesToEmojis(':' + o.skintone + ':')} -
  • `; -}; - -const tpl_emoji_picker = o => { - const i18n_search = __('Search'); - - const skintones = ['tone1', 'tone2', 'tone3', 'tone4', 'tone5']; - return T` -
    - - ${o.query ? '' : emoji_picker_header(o)} -
    - ${o.render_emojis ? T`` : ''} - -
    - -
      ${skintones.map(skintone => skintone_emoji(Object.assign({ - skintone - }, o)))}
    -
    `; -}; -;// CONCATENATED MODULE: ./src/shared/chat/emoji-picker-content.js - - - - -const { - sizzle: emoji_picker_content_sizzle -} = core_converse.env; -class EmojiPickerContent extends CustomElement { - static get properties() { - return { - 'chatview': { - type: Object - }, - 'search_results': { - type: Array - }, - 'current_skintone': { - type: String - }, - 'model': { - type: Object - }, - 'query': { - type: String - } - }; - } - - render() { - const props = { - 'current_skintone': this.current_skintone, - 'insertEmoji': ev => this.insertEmoji(ev), - 'query': this.query, - 'search_results': this.search_results, - 'shouldBeHidden': shortname => this.shouldBeHidden(shortname) - }; - return T` -
    - ${tpl_search_results(props)} - ${tpl_all_emojis(props)} -
    - `; - } - - firstUpdated() { - this.initIntersectionObserver(); - } - - initIntersectionObserver() { - if (!window.IntersectionObserver) { - return; - } - - if (this.observer) { - this.observer.disconnect(); - } else { - const options = { - root: this.querySelector('.emoji-picker__lists'), - threshold: [0.1] - }; - - const handler = ev => this.setCategoryOnVisibilityChange(ev); - - this.observer = new IntersectionObserver(handler, options); - } - - emoji_picker_content_sizzle('.emoji-picker', this).forEach(a => this.observer.observe(a)); - } - - setCategoryOnVisibilityChange(entries) { - const selected = this.parentElement.navigator.selected; - const intersection_with_selected = entries.filter(i => i.target.contains(selected)).pop(); - let current; // Choose the intersection that contains the currently selected - // element, or otherwise the one with the largest ratio. - - if (intersection_with_selected) { - current = intersection_with_selected; - } else { - current = entries.reduce((p, c) => c.intersectionRatio >= ((p === null || p === void 0 ? void 0 : p.intersectionRatio) || 0) ? c : p, null); - } - - if (current && current.isIntersecting) { - const category = current.target.getAttribute('data-category'); - - if (category !== this.model.get('current_category')) { - this.parentElement.preserve_scroll = true; - this.model.save({ - 'current_category': category - }); - } - } - } - - insertEmoji(ev) { - ev.preventDefault(); - ev.stopPropagation(); - const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target; - this.parentElement.insertIntoTextArea(target.getAttribute('data-emoji')); - } - - shouldBeHidden(shortname) { - // Helper method for the template which decides whether an - // emoji should be hidden, based on which skin tone is - // currently being applied. - if (shortname.includes('_tone')) { - if (!this.current_skintone || !shortname.includes(this.current_skintone)) { - return true; - } - } else { - if (this.current_skintone && core_converse.emojis.toned.includes(shortname)) { - return true; - } - } - - if (this.query && !shared_converse.FILTER_CONTAINS(shortname, this.query)) { - return true; - } - - return false; - } - -} -api.elements.define('converse-emoji-picker-content', EmojiPickerContent); -;// CONCATENATED MODULE: ./src/shared/chat/emoji-dropdown.js - - - - - - -const emoji_dropdown_u = core_converse.env.utils; -class EmojiDropdown extends Dropdown { - static get properties() { - return { - chatview: { - type: Object - } - }; - } - - constructor() { - super(); // This is an optimization, we lazily render the emoji picker, otherwise tests slow to a crawl. - - this.render_emojis = false; - } - - initModel() { - if (!this.init_promise) { - this.init_promise = (async () => { - await api.emojis.initialize(); - const id = `converse.emoji-${shared_converse.bare_jid}-${this.chatview.model.get('jid')}`; - this.model = new shared_converse.EmojiPicker({ - 'id': id - }); - initStorage(this.model, id); - await new Promise(resolve => this.model.fetch({ - 'success': resolve, - 'error': resolve - })); // We never want still be in the autocompleting state upon page load - - this.model.set({ - 'autocompleting': null, - 'ac_position': null - }); - })(); - } - - return this.init_promise; - } - - render() { - return T` -
    - - -
    `; - } - - connectedCallback() { - super.connectedCallback(); - this.render_emojis = false; - } - - toggleMenu(ev) { - ev.stopPropagation(); - ev.preventDefault(); - - if (emoji_dropdown_u.hasClass('show', this.menu)) { - if (emoji_dropdown_u.ancestor(ev.target, '.toggle-emojis')) { - this.hideMenu(); - } - } else { - this.showMenu(); - } - } - - async showMenu() { - await this.initModel(); - - if (!this.render_emojis) { - // Trigger an update so that emojis are rendered - this.render_emojis = true; - await this.requestUpdate(); - } - - super.showMenu(); - setTimeout(() => { - var _this$querySelector; - - return (_this$querySelector = this.querySelector('.emoji-search')) === null || _this$querySelector === void 0 ? void 0 : _this$querySelector.focus(); - }); - } - -} -api.elements.define('converse-emoji-dropdown', EmojiDropdown); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/chat/styles/emoji.scss -var emoji = __webpack_require__(9796); -;// CONCATENATED MODULE: ./src/shared/chat/styles/emoji.scss - - - - - - - - - - - -var emoji_options = {}; - -emoji_options.styleTagTransform = (styleTagTransform_default()); -emoji_options.setAttributes = (setAttributesWithoutAttributes_default()); - - emoji_options.insert = insertBySelector_default().bind(null, "head"); - -emoji_options.domAPI = (styleDomAPI_default()); -emoji_options.insertStyleElement = (insertStyleElement_default()); - -var emoji_update = injectStylesIntoStyleTag_default()(emoji/* default */.Z, emoji_options); - - - - - /* harmony default export */ const styles_emoji = (emoji/* default */.Z && emoji/* default.locals */.Z.locals ? emoji/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/chat/emoji-picker.js - - - - - - - - -const chat_emoji_picker_u = core_converse.env.utils; -class EmojiPicker extends CustomElement { - static get properties() { - return { - 'chatview': { - type: Object - }, - 'current_category': { - type: String, - 'reflect': true - }, - 'current_skintone': { - type: String, - 'reflect': true - }, - 'model': { - type: Object - }, - 'query': { - type: String, - 'reflet': true - }, - // This is an optimization, we lazily render the emoji picker, otherwise tests slow to a crawl. - 'render_emojis': { - type: Boolean - } - }; - } - - firstUpdated() { - super.firstUpdated(); - this.listenTo(this.model, 'change', o => this.onModelChanged(o.changed)); - this.initArrowNavigation(); - } - - constructor() { - super(); - this._search_results = []; - this.debouncedFilter = lodash_es_debounce(input => this.model.set({ - 'query': input.value - }), 250); - } - - get search_results() { - return this._search_results; - } - - set search_results(value) { - this._search_results = value; - this.requestUpdate(); - } - - render() { - return tpl_emoji_picker({ - 'chatview': this.chatview, - 'current_category': this.current_category, - 'current_skintone': this.current_skintone, - 'model': this.model, - 'onCategoryPicked': ev => this.chooseCategory(ev), - 'onSearchInputBlurred': ev => this.chatview.emitFocused(ev), - 'onSearchInputFocus': ev => this.onSearchInputFocus(ev), - 'onSearchInputKeyDown': ev => this.onKeyDown(ev), - 'onSkintonePicked': ev => this.chooseSkinTone(ev), - 'query': this.query, - 'search_results': this.search_results, - 'render_emojis': this.render_emojis, - 'sn2Emoji': shortname => chat_emoji_picker_u.shortnamesToEmojis(this.getTonedShortname(shortname)) - }); - } - - updated(changed) { - changed.has('query') && this.updateSearchResults(changed); - changed.has('current_category') && this.setScrollPosition(); - } - - onModelChanged(changed) { - if ('current_category' in changed) this.current_category = changed.current_category; - if ('current_skintone' in changed) this.current_skintone = changed.current_skintone; - if ('query' in changed) this.query = changed.query; - } - - setScrollPosition() { - if (this.preserve_scroll) { - this.preserve_scroll = false; - return; - } - - const el = this.querySelector('.emoji-lists__container--browse'); - const heading = this.querySelector(`#emoji-picker-${this.current_category}`); - - if (heading) { - // +4 due to 2px padding on list elements - el.scrollTop = heading.offsetTop - heading.offsetHeight * 3 + 4; - } - } - - updateSearchResults(changed) { - const old_query = changed.get('query'); - const contains = shared_converse.FILTER_CONTAINS; - - if (this.query) { - if (this.query === old_query) { - return this.search_results; - } else if (old_query && this.query.includes(old_query)) { - this.search_results = this.search_results.filter(e => contains(e.sn, this.query)); - } else { - this.search_results = core_converse.emojis.list.filter(e => contains(e.sn, this.query)); - } - } else if (this.search_results.length) { - // Avoid re-rendering by only setting to new empty array if it wasn't empty before - this.search_results = []; - } - - this.requestUpdate(); - } - - registerEvents() { - this.onGlobalKeyDown = ev => this._onGlobalKeyDown(ev); - - const body = document.querySelector('body'); - body.addEventListener('keydown', this.onGlobalKeyDown); - } - - connectedCallback() { - super.connectedCallback(); - this.registerEvents(); - } - - disconnectedCallback() { - const body = document.querySelector('body'); - body.removeEventListener('keydown', this.onGlobalKeyDown); - super.disconnectedCallback(); - } - - _onGlobalKeyDown(ev) { - if (!this.navigator) { - return; - } - - if (ev.keyCode === core_converse.keycodes.ENTER && this.navigator.selected && chat_emoji_picker_u.isVisible(this)) { - this.onEnterPressed(ev); - } else if (ev.keyCode === core_converse.keycodes.DOWN_ARROW && !this.navigator.enabled && chat_emoji_picker_u.isVisible(this)) { - this.enableArrowNavigation(ev); - } - } - - setCategoryForElement(el) { - const old_category = this.current_category; - const category = (el === null || el === void 0 ? void 0 : el.getAttribute('data-category')) || old_category; - - if (old_category !== category) { - this.model.save({ - 'current_category': category - }); - } - } - - insertIntoTextArea(value) { - const autocompleting = this.model.get('autocompleting'); - const ac_position = this.model.get('ac_position'); - this.chatview.getMessageForm().insertIntoTextArea(value, autocompleting, false, ac_position); - this.model.set({ - 'autocompleting': null, - 'query': '', - 'ac_position': null - }); - } - - chooseSkinTone(ev) { - ev.preventDefault(); - ev.stopPropagation(); - const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target; - const skintone = target.getAttribute("data-skintone").trim(); - - if (this.current_skintone === skintone) { - this.model.save({ - 'current_skintone': '' - }); - } else { - this.model.save({ - 'current_skintone': skintone - }); - } - } - - chooseCategory(ev) { - ev.preventDefault && ev.preventDefault(); - ev.stopPropagation && ev.stopPropagation(); - const el = ev.target.matches('li') ? ev.target : chat_emoji_picker_u.ancestor(ev.target, 'li'); - this.setCategoryForElement(el); - this.navigator.select(el); - !this.navigator.enabled && this.navigator.enable(); - } - - onKeyDown(ev) { - if (ev.keyCode === core_converse.keycodes.TAB) { - if (ev.target.value) { - ev.preventDefault(); - const match = core_converse.emojis.shortnames.find(sn => shared_converse.FILTER_CONTAINS(sn, ev.target.value)); - match && this.model.set({ - 'query': match - }); - } else if (!this.navigator.enabled) { - this.enableArrowNavigation(ev); - } - } else if (ev.keyCode === core_converse.keycodes.DOWN_ARROW && !this.navigator.enabled) { - this.enableArrowNavigation(ev); - } else if (ev.keyCode === core_converse.keycodes.ENTER) { - this.onEnterPressed(ev); - } else if (ev.keyCode === core_converse.keycodes.ESCAPE) { - chat_emoji_picker_u.ancestor(this, 'converse-emoji-dropdown').hideMenu(); - this.chatview.el.querySelector('.chat-textarea').focus(); - ev.stopPropagation(); - ev.preventDefault(); - } else if (ev.keyCode !== core_converse.keycodes.ENTER && ev.keyCode !== core_converse.keycodes.DOWN_ARROW) { - this.debouncedFilter(ev.target); - } - } - - onEnterPressed(ev) { - if (ev.emoji_keypress_handled) { - // Prevent the emoji from being inserted a 2nd time due to this - // method being called by two event handlers: onKeyDown and _onGlobalKeyDown - return; - } - - ev.preventDefault(); - ev.stopPropagation(); - ev.emoji_keypress_handled = true; - - if (core_converse.emojis.shortnames.includes(ev.target.value)) { - this.insertIntoTextArea(ev.target.value); - } else if (this.search_results.length === 1) { - this.insertIntoTextArea(this.search_results[0].sn); - } else if (this.navigator.selected && this.navigator.selected.matches('.insert-emoji')) { - this.insertIntoTextArea(this.navigator.selected.getAttribute('data-emoji')); - } else if (this.navigator.selected && this.navigator.selected.matches('.emoji-category')) { - this.chooseCategory({ - 'target': this.navigator.selected - }); - } - } - - onSearchInputFocus(ev) { - this.chatview.emitBlurred(ev); - this.disableArrowNavigation(); - } - - getTonedShortname(shortname) { - if (core_converse.emojis.toned.includes(shortname) && this.current_skintone) { - return `${shortname.slice(0, shortname.length - 1)}_${this.current_skintone}:`; - } - - return shortname; - } - - initArrowNavigation() { - if (!this.navigator) { - const default_selector = 'li:not(.hidden):not(.emoji-skintone), .emoji-search'; - const options = { - 'jump_to_picked': '.emoji-category', - 'jump_to_picked_selector': '.emoji-category.picked', - 'jump_to_picked_direction': dom_navigator.DIRECTION.down, - 'picked_selector': '.picked', - 'scroll_container': this.querySelector('.emoji-picker__lists'), - 'getSelector': direction => { - if (direction === dom_navigator.DIRECTION.down) { - const c = this.navigator.selected && this.navigator.selected.getAttribute('data-category'); - return c ? `ul[data-category="${c}"] li:not(.hidden):not(.emoji-skintone), .emoji-search` : default_selector; - } else { - return default_selector; - } - }, - 'onSelected': el => { - el.matches('.insert-emoji') && this.setCategoryForElement(el.parentElement); - el.matches('.insert-emoji, .emoji-category') && el.firstElementChild.focus(); - el.matches('.emoji-search') && el.focus(); - } - }; - this.navigator = new dom_navigator(this, options); - } - } - - disableArrowNavigation() { - this.navigator.disable(); - } - - enableArrowNavigation(ev) { - var _ev$preventDefault, _ev$stopPropagation; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation = ev.stopPropagation) === null || _ev$stopPropagation === void 0 ? void 0 : _ev$stopPropagation.call(ev); - this.disableArrowNavigation(); - this.navigator.enable(); - this.navigator.handleKeydown(ev); - } - -} -api.elements.define('converse-emoji-picker', EmojiPicker); -;// CONCATENATED MODULE: ./src/shared/chat/templates/message-limit.js - - -/* harmony default export */ const message_limit = (counter => { - const i18n_chars_remaining = __('Message characters remaining'); - - return T`${counter}`; -}); -;// CONCATENATED MODULE: ./src/shared/chat/message-limit.js - - - -class MessageLimitIndicator extends CustomElement { - static get properties() { - return { - model: { - type: Object - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.listenTo(this.model, 'change:draft', this.requestUpdate); - } - - render() { - const limit = api.settings.get('message_limit'); - if (!limit) return ''; - const chars = this.model.get('draft') || ''; - return message_limit(limit - chars.length); - } - -} -api.elements.define('converse-message-limit-indicator', MessageLimitIndicator); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/chat/styles/toolbar.scss -var toolbar = __webpack_require__(7991); -;// CONCATENATED MODULE: ./src/shared/chat/styles/toolbar.scss - - - - - - - - - - - -var toolbar_options = {}; - -toolbar_options.styleTagTransform = (styleTagTransform_default()); -toolbar_options.setAttributes = (setAttributesWithoutAttributes_default()); - - toolbar_options.insert = insertBySelector_default().bind(null, "head"); - -toolbar_options.domAPI = (styleDomAPI_default()); -toolbar_options.insertStyleElement = (insertStyleElement_default()); - -var toolbar_update = injectStylesIntoStyleTag_default()(toolbar/* default */.Z, toolbar_options); - - - - - /* harmony default export */ const styles_toolbar = (toolbar/* default */.Z && toolbar/* default.locals */.Z.locals ? toolbar/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/chat/toolbar.js - - - - - - - - -const toolbar_Strophe = core_converse.env.Strophe; -class ChatToolbar extends CustomElement { - static get properties() { - return { - composing_spoiler: { - type: Boolean - }, - hidden_occupants: { - type: Boolean - }, - is_groupchat: { - type: Boolean - }, - message_limit: { - type: Number - }, - model: { - type: Object - }, - show_call_button: { - type: Boolean - }, - show_emoji_button: { - type: Boolean - }, - show_send_button: { - type: Boolean - }, - show_spoiler_button: { - type: Boolean - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.listenTo(this.model, 'change:composing_spoiler', this.requestUpdate); - } - - render() { - const i18n_send_message = __('Send the message'); - - return T` - ${until_o(this.getButtons(), '')} - ${this.show_send_button ? T`` : ''} - `; - } - - firstUpdated() { - /** - * Triggered once the _converse.ChatBoxView's toolbar has been rendered - * @event _converse#renderToolbar - * @type { _converse.ChatBoxView } - * @example _converse.api.listen.on('renderToolbar', this => { ... }); - */ - api.trigger('renderToolbar', this); - } - - getButtons() { - var _api$settings$get; - - const buttons = []; - - if (this.show_emoji_button) { - const chatview = shared_converse.chatboxviews.get(this.model.get('jid')); - - buttons.push(T``); - } - - if (this.show_call_button) { - const i18n_start_call = __('Start a call'); - - buttons.push(T` - `); - } - - const message_limit = api.settings.get('message_limit'); - - if (message_limit) { - buttons.push(T` - - `); - } - - if (this.show_spoiler_button) { - buttons.push(this.getSpoilerButton()); - } - - const http_upload_promise = api.disco.supports(toolbar_Strophe.NS.HTTPUPLOAD, shared_converse.domain); - buttons.push(T`${until_o(http_upload_promise.then(is_supported => this.getHTTPUploadButton(is_supported)), '')}`); - - if (this.is_groupchat && (_api$settings$get = api.settings.get('visible_toolbar_buttons')) !== null && _api$settings$get !== void 0 && _api$settings$get.toggle_occupants) { - const i18n_hide_occupants = __('Hide participants'); - - const i18n_show_occupants = __('Show participants'); - - buttons.push(T` - `); - } - /** - * *Hook* which allows plugins to add more buttons to a chat's toolbar - * @event _converse#getToolbarButtons - * @example - * api.listen.on('getToolbarButtons', (toolbar_el, buttons) { - * buttons.push(html` - * ` - * ); - * return buttons; - * } - */ - - - return shared_converse.api.hook('getToolbarButtons', this, buttons); - } - - getHTTPUploadButton(is_supported) { - if (is_supported) { - const i18n_choose_file = __('Choose a file to send'); - - return T` - - `; - } else { - return ''; - } - } - - getSpoilerButton() { - var _model$presence; - - const model = this.model; - - if (!this.is_groupchat && !((_model$presence = model.presence) !== null && _model$presence !== void 0 && _model$presence.resources.length)) { - return; - } - - let i18n_toggle_spoiler; - - if (this.composing_spoiler) { - i18n_toggle_spoiler = __("Click to write as a normal (non-spoiler) message"); - } else { - i18n_toggle_spoiler = __("Click to write your message as a spoiler"); - } - - const markup = T` - `; - - if (this.is_groupchat) { - return markup; - } else { - const contact_jid = model.get('jid'); - const spoilers_promise = Promise.all(model.presence.resources.map(r => api.disco.supports(toolbar_Strophe.NS.SPOILER, `${contact_jid}/${r.get('name')}`))).then(results => results.reduce((acc, val) => acc && val, true)); - return T`${until_o(spoilers_promise.then(() => markup), '')}`; - } - } - - toggleFileUpload(ev) { - var _ev$preventDefault, _ev$stopPropagation; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation = ev.stopPropagation) === null || _ev$stopPropagation === void 0 ? void 0 : _ev$stopPropagation.call(ev); - this.querySelector('.fileupload').click(); - } - - onFileSelection(evt) { - this.model.sendFiles(evt.target.files); - } - - toggleComposeSpoilerMessage(ev) { - var _ev$preventDefault2, _ev$stopPropagation2; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation2 = ev.stopPropagation) === null || _ev$stopPropagation2 === void 0 ? void 0 : _ev$stopPropagation2.call(ev); - this.model.set('composing_spoiler', !this.model.get('composing_spoiler')); - } - - toggleOccupants(ev) { - var _ev$preventDefault3, _ev$stopPropagation3; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault3 = ev.preventDefault) === null || _ev$preventDefault3 === void 0 ? void 0 : _ev$preventDefault3.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation3 = ev.stopPropagation) === null || _ev$stopPropagation3 === void 0 ? void 0 : _ev$stopPropagation3.call(ev); - this.model.save({ - 'hidden_occupants': !this.model.get('hidden_occupants') - }); - } - - toggleCall(ev) { - var _ev$preventDefault4, _ev$stopPropagation4; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault4 = ev.preventDefault) === null || _ev$preventDefault4 === void 0 ? void 0 : _ev$preventDefault4.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation4 = ev.stopPropagation) === null || _ev$stopPropagation4 === void 0 ? void 0 : _ev$stopPropagation4.call(ev); - /** - * When a call button (i.e. with class .toggle-call) on a chatbox has been clicked. - * @event _converse#callButtonClicked - * @type { object } - * @property { Strophe.Connection } _converse.connection - The XMPP Connection object - * @property { _converse.ChatBox | _converse.ChatRoom } _converse.connection - The XMPP Connection object - * @example _converse.api.listen.on('callButtonClicked', (connection, model) => { ... }); - */ - - api.trigger('callButtonClicked', { - connection: shared_converse.connection, - model: this.model - }); - } - -} -window.customElements.define('converse-chat-toolbar', ChatToolbar); -;// CONCATENATED MODULE: ./src/plugins/chatview/utils.js - - - -function clearHistory(jid) { - if (shared_converse.router.history.getFragment() === `converse/chat?jid=${jid}`) { - shared_converse.router.navigate(''); - } -} -async function getHeadingDropdownItem(promise_or_data) { - const data = await promise_or_data; - return T` - - - ${data.i18n_text} - - `; -} -async function getHeadingStandaloneButton(promise_or_data) { - const data = await promise_or_data; - return T` - - `; -} -async function clearMessages(chat) { - const result = confirm(__('Are you sure you want to clear the messages from this conversation?')); - - if (result === true) { - await chat.clearMessages(); - } -} -function parseMessageForCommands(chat, text) { - const match = text.replace(/^\s*/, '').match(/^\/(.*)\s*$/); - - if (match) { - if (match[1] === 'clear') { - clearMessages(chat); - return true; - } else if (match[1] === 'close') { - var _converse$chatboxview; - - (_converse$chatboxview = shared_converse.chatboxviews.get(chat.get('jid'))) === null || _converse$chatboxview === void 0 ? void 0 : _converse$chatboxview.close(); - return true; - } else if (match[1] === 'help') { - chat.set({ - 'show_help_messages': false - }, { - 'silent': true - }); - chat.set({ - 'show_help_messages': true - }); - return true; - } - } -} -function resetElementHeight(ev) { - if (ev.target.value) { - const height = ev.target.scrollHeight + 'px'; - - if (ev.target.style.height != height) { - ev.target.style.height = 'auto'; - ev.target.style.height = height; - } - } else { - ev.target.style = ''; - } -} -;// CONCATENATED MODULE: ./src/plugins/chatview/templates/chat-head.js - - - - - - - -async function getStandaloneButtons(promise) { - const heading_btns = await promise; - const standalone_btns = heading_btns.filter(b => b.standalone); - return standalone_btns.map(b => getHeadingStandaloneButton(b)); -} - -async function getDropdownButtons(promise) { - const heading_btns = await promise; - const dropdown_btns = heading_btns.filter(b => !b.standalone); - return dropdown_btns.map(b => getHeadingDropdownItem(b)); -} - -/* harmony default export */ const chat_head = (o => { - var _o$model; - - const vcard = (_o$model = o.model) === null || _o$model === void 0 ? void 0 : _o$model.vcard; - const vcard_json = vcard ? vcard.toJSON() : {}; - - const i18n_profile = __("The User's Profile Image"); - - const avatar_data = Object.assign({ - 'alt_text': i18n_profile, - 'extra_classes': '', - 'height': 40, - 'width': 40 - }, vcard_json); - const avatar = T`${renderAvatar(avatar_data)}`; - const display_name = o.model.getDisplayName(); - - const tpl_dropdown_btns = () => getDropdownButtons(o.heading_buttons_promise).then(btns => btns.length ? T`` : ''); - - const tpl_standalone_btns = () => getStandaloneButtons(o.heading_buttons_promise).then(btns => btns.reverse().map(b => until_o(b, ''))); - - return T` -
    -
    - ${!shared_converse.api.settings.get("singleton") ? T`` : ''} - ${o.type !== shared_converse.HEADLINES_TYPE ? T`${avatar}` : ''} -
    - ${o.type !== shared_converse.HEADLINES_TYPE ? T`${display_name}` : display_name} -
    -
    -
    - ${until_o(tpl_dropdown_btns(), '')} - ${until_o(tpl_standalone_btns(), '')} -
    -
    - ${o.status ? T`

    ${o.status}

    ` : ''} - `; -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/chatview/styles/chat-head.scss -var styles_chat_head = __webpack_require__(6220); -;// CONCATENATED MODULE: ./src/plugins/chatview/styles/chat-head.scss - - - - - - - - - - - -var chat_head_options = {}; - -chat_head_options.styleTagTransform = (styleTagTransform_default()); -chat_head_options.setAttributes = (setAttributesWithoutAttributes_default()); - - chat_head_options.insert = insertBySelector_default().bind(null, "head"); - -chat_head_options.domAPI = (styleDomAPI_default()); -chat_head_options.insertStyleElement = (insertStyleElement_default()); - -var chat_head_update = injectStylesIntoStyleTag_default()(styles_chat_head/* default */.Z, chat_head_options); - - - - - /* harmony default export */ const chatview_styles_chat_head = (styles_chat_head/* default */.Z && styles_chat_head/* default.locals */.Z.locals ? styles_chat_head/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/chatview/heading.js - - - - - - -class ChatHeading extends CustomElement { - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - initialize() { - var _this$model$rosterCon; - - this.model = shared_converse.chatboxes.get(this.getAttribute('jid')); - this.listenTo(this.model, 'change:status', this.requestUpdate); - this.listenTo(this.model, 'vcard:change', this.requestUpdate); - - if (this.model.contact) { - this.listenTo(this.model.contact, 'destroy', this.requestUpdate); - } - - (_this$model$rosterCon = this.model.rosterContactAdded) === null || _this$model$rosterCon === void 0 ? void 0 : _this$model$rosterCon.then(() => { - this.listenTo(this.model.contact, 'change:nickname', this.requestUpdate); - this.requestUpdate(); - }); - } - - render() { - return chat_head(Object.assign(this.model.toJSON(), { - 'heading_buttons_promise': this.getHeadingButtons(), - 'model': this.model, - 'showUserDetailsModal': ev => this.showUserDetailsModal(ev) - })); - } - - showUserDetailsModal(ev) { - ev.preventDefault(); - api.modal.show(modals_user_details, { - model: this.model - }, ev); - } - - close(ev) { - ev.preventDefault(); - this.model.close(); - } - /** - * Returns a list of objects which represent buttons for the chat's header. - * @async - * @emits _converse#getHeadingButtons - */ - - - getHeadingButtons() { - const buttons = [{ - 'a_class': 'show-user-details-modal', - 'handler': ev => this.showUserDetailsModal(ev), - 'i18n_text': __('Details'), - 'i18n_title': __('See more information about this person'), - 'icon_class': 'fa-id-card', - 'name': 'details', - 'standalone': api.settings.get('view_mode') === 'overlayed' - }]; - - if (!api.settings.get('singleton')) { - buttons.push({ - 'a_class': 'close-chatbox-button', - 'handler': ev => this.close(ev), - 'i18n_text': __('Close'), - 'i18n_title': __('Close and end this conversation'), - 'icon_class': 'fa-times', - 'name': 'close', - 'standalone': api.settings.get('view_mode') === 'overlayed' - }); - } - /** - * *Hook* which allows plugins to add more buttons to a chat's heading. - * @event _converse#getHeadingButtons - * @example - * api.listen.on('getHeadingButtons', (view, buttons) => { - * buttons.push({ - * 'i18n_title': __('Foo'), - * 'i18n_text': __('Foo Bar'), - * 'handler': ev => alert('Foo!'), - * 'a_class': 'toggle-foo', - * 'icon_class': 'fa-foo', - * 'name': 'foo' - * }); - * return buttons; - * }); - */ - - - const chatview = shared_converse.chatboxviews.get(this.getAttribute('jid')); - - if (chatview) { - return shared_converse.api.hook('getHeadingButtons', chatview, buttons); - } else { - return buttons; // Happens during tests - } - } - -} -api.elements.define('converse-chat-heading', ChatHeading); -;// CONCATENATED MODULE: ./src/plugins/chatview/templates/message-form.js - - - - -/* harmony default export */ const message_form = (o => { - const label_message = o.composing_spoiler ? __('Hidden message') : __('Message'); - - const label_spoiler_hint = __('Optional hint'); - - const show_send_button = api.settings.get('show_send_button'); - return T` -
    - - -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/chatview/message-form.js - - - - - -const { - u: message_form_u -} = core_converse.env; -class MessageForm extends ElementView { - async connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.getAttribute('jid')); - await this.model.initialized; - this.listenTo(this.model.messages, 'change:correcting', this.onMessageCorrecting); - this.render(); - } - - toHTML() { - var _this$querySelector, _this$querySelector2; - - return message_form(Object.assign(this.model.toJSON(), { - 'onDrop': ev => this.onDrop(ev), - 'hint_value': (_this$querySelector = this.querySelector('.spoiler-hint')) === null || _this$querySelector === void 0 ? void 0 : _this$querySelector.value, - 'message_value': (_this$querySelector2 = this.querySelector('.chat-textarea')) === null || _this$querySelector2 === void 0 ? void 0 : _this$querySelector2.value, - 'onChange': ev => this.model.set({ - 'draft': ev.target.value - }), - 'onKeyDown': ev => this.onKeyDown(ev), - 'onKeyUp': ev => this.onKeyUp(ev), - 'onPaste': ev => this.onPaste(ev), - 'viewUnreadMessages': ev => this.viewUnreadMessages(ev) - })); - } - /** - * Insert a particular string value into the textarea of this chat box. - * @param {string} value - The value to be inserted. - * @param {(boolean|string)} [replace] - Whether an existing value - * should be replaced. If set to `true`, the entire textarea will - * be replaced with the new value. If set to a string, then only - * that string will be replaced *if* a position is also specified. - * @param {integer} [position] - The end index of the string to be - * replaced with the new value. - */ - - - insertIntoTextArea(value, replace = false, correcting = false, position) { - const textarea = this.querySelector('.chat-textarea'); - - if (correcting) { - message_form_u.addClass('correcting', textarea); - } else { - message_form_u.removeClass('correcting', textarea); - } - - if (replace) { - if (position && typeof replace == 'string') { - textarea.value = textarea.value.replace(new RegExp(replace, 'g'), (match, offset) => offset == position - replace.length ? value + ' ' : match); - } else { - textarea.value = value; - } - } else { - let existing = textarea.value; - - if (existing && existing[existing.length - 1] !== ' ') { - existing = existing + ' '; - } - - textarea.value = existing + value + ' '; - } - - const ev = document.createEvent('HTMLEvents'); - ev.initEvent('change', false, true); - textarea.dispatchEvent(ev); - message_form_u.placeCaretAtEnd(textarea); - } - - onMessageCorrecting(message) { - if (message.get('correcting')) { - this.insertIntoTextArea(message_form_u.prefixMentions(message), true, true); - } else { - const currently_correcting = this.model.messages.findWhere('correcting'); - - if (currently_correcting && currently_correcting !== message) { - this.insertIntoTextArea(message_form_u.prefixMentions(message), true, true); - } else { - this.insertIntoTextArea('', true, false); - } - } - } - - onEscapePressed(ev) { - ev.preventDefault(); - const idx = this.model.messages.findLastIndex('correcting'); - const message = idx >= 0 ? this.model.messages.at(idx) : null; - - if (message) { - message.save('correcting', false); - } - - this.insertIntoTextArea('', true, false); - } - - onPaste(ev) { - ev.stopPropagation(); - - if (ev.clipboardData.files.length !== 0) { - ev.preventDefault(); // Workaround for quirk in at least Firefox 60.7 ESR: - // It seems that pasted files disappear from the event payload after - // the event has finished, which apparently happens during async - // processing in sendFiles(). So we copy the array here. - - this.model.sendFiles(Array.from(ev.clipboardData.files)); - return; - } - - this.model.set({ - 'draft': ev.clipboardData.getData('text/plain') - }); - } - - onKeyUp(ev) { - this.model.set({ - 'draft': ev.target.value - }); - } - - onKeyDown(ev) { - if (ev.ctrlKey) { - // When ctrl is pressed, no chars are entered into the textarea. - return; - } - - if (!ev.shiftKey && !ev.altKey && !ev.metaKey) { - if (ev.keyCode === core_converse.keycodes.TAB) { - const value = message_form_u.getCurrentWord(ev.target, null, /(:.*?:)/g); - - if (value.startsWith(':')) { - ev.preventDefault(); - ev.stopPropagation(); - this.model.trigger('emoji-picker-autocomplete', ev.target, value); - } - } else if (ev.keyCode === core_converse.keycodes.FORWARD_SLASH) { - // Forward slash is used to run commands. Nothing to do here. - return; - } else if (ev.keyCode === core_converse.keycodes.ESCAPE) { - return this.onEscapePressed(ev, this); - } else if (ev.keyCode === core_converse.keycodes.ENTER) { - return this.onFormSubmitted(ev); - } else if (ev.keyCode === core_converse.keycodes.UP_ARROW && !ev.target.selectionEnd) { - const textarea = this.querySelector('.chat-textarea'); - - if (!textarea.value || message_form_u.hasClass('correcting', textarea)) { - return this.model.editEarlierMessage(); - } - } else if (ev.keyCode === core_converse.keycodes.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length && message_form_u.hasClass('correcting', this.querySelector('.chat-textarea'))) { - return this.model.editLaterMessage(); - } - } - - if ([core_converse.keycodes.SHIFT, core_converse.keycodes.META, core_converse.keycodes.META_RIGHT, core_converse.keycodes.ESCAPE, core_converse.keycodes.ALT].includes(ev.keyCode)) { - return; - } - - if (this.model.get('chat_state') !== shared_converse.COMPOSING) { - // Set chat state to composing if keyCode is not a forward-slash - // (which would imply an internal command and not a message). - this.model.setChatState(shared_converse.COMPOSING); - } - } - - parseMessageForCommands(text) { - // Wrap util so that we can override in the MUC message-form component - return parseMessageForCommands(this.model, text); - } - - async onFormSubmitted(ev) { - var _ev$preventDefault, _this$querySelector3; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - const textarea = this.querySelector('.chat-textarea'); - const message_text = textarea.value.trim(); - - if (api.settings.get('message_limit') && message_text.length > api.settings.get('message_limit') || !message_text.replace(/\s/g, '').length) { - return; - } - - if (!shared_converse.connection.authenticated) { - const err_msg = __('Sorry, the connection has been lost, and your message could not be sent'); - - api.alert('error', __('Error'), err_msg); - api.connection.reconnect(); - return; - } - - let spoiler_hint, - hint_el = {}; - - if (this.model.get('composing_spoiler')) { - hint_el = this.querySelector('form.sendXMPPMessage input.spoiler-hint'); - spoiler_hint = hint_el.value; - } - - message_form_u.addClass('disabled', textarea); - textarea.setAttribute('disabled', 'disabled'); - (_this$querySelector3 = this.querySelector('converse-emoji-dropdown')) === null || _this$querySelector3 === void 0 ? void 0 : _this$querySelector3.hideMenu(); - const is_command = this.parseMessageForCommands(message_text); - const message = is_command ? null : await this.model.sendMessage({ - 'body': message_text, - spoiler_hint - }); - - if (is_command || message) { - hint_el.value = ''; - textarea.value = ''; - message_form_u.removeClass('correcting', textarea); - textarea.style.height = 'auto'; - this.model.set({ - 'draft': '' - }); - } - - if (api.settings.get('view_mode') === 'overlayed') { - // XXX: Chrome flexbug workaround. The .chat-content area - // doesn't resize when the textarea is resized to its original size. - const chatview = shared_converse.chatboxviews.get(this.getAttribute('jid')); - - const msgs_container = chatview.querySelector('.chat-content__messages'); - msgs_container.parentElement.style.display = 'none'; - } - - textarea.removeAttribute('disabled'); - message_form_u.removeClass('disabled', textarea); - - if (api.settings.get('view_mode') === 'overlayed') { - // XXX: Chrome flexbug workaround. - const chatview = shared_converse.chatboxviews.get(this.getAttribute('jid')); - - const msgs_container = chatview.querySelector('.chat-content__messages'); - msgs_container.parentElement.style.display = ''; - } // Suppress events, otherwise superfluous CSN gets set - // immediately after the message, causing rate-limiting issues. - - - this.model.setChatState(shared_converse.ACTIVE, { - 'silent': true - }); - textarea.focus(); - } - -} -api.elements.define('converse-message-form', MessageForm); -;// CONCATENATED MODULE: ./src/plugins/chatview/templates/bottom-panel.js - - - -/* harmony default export */ const bottom_panel = (o => { - const unread_msgs = __('You have unread messages'); - - const message_limit = api.settings.get('message_limit'); - const show_call_button = api.settings.get('visible_toolbar_buttons').call; - const show_emoji_button = api.settings.get('visible_toolbar_buttons').emoji; - const show_send_button = api.settings.get('show_send_button'); - const show_spoiler_button = api.settings.get('visible_toolbar_buttons').spoiler; - const show_toolbar = api.settings.get('show_toolbar'); - return T` - ${o.model.ui.get('scrolled') && o.model.get('num_unread') ? T`
    o.viewUnreadMessages(ev)}>▼ ${unread_msgs} ▼
    ` : ''} - ${api.settings.get('show_toolbar') ? T` - ` : ''} - - `; -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/chatview/styles/chat-bottom-panel.scss -var chat_bottom_panel = __webpack_require__(4639); -;// CONCATENATED MODULE: ./src/plugins/chatview/styles/chat-bottom-panel.scss - - - - - - - - - - - -var chat_bottom_panel_options = {}; - -chat_bottom_panel_options.styleTagTransform = (styleTagTransform_default()); -chat_bottom_panel_options.setAttributes = (setAttributesWithoutAttributes_default()); - - chat_bottom_panel_options.insert = insertBySelector_default().bind(null, "head"); - -chat_bottom_panel_options.domAPI = (styleDomAPI_default()); -chat_bottom_panel_options.insertStyleElement = (insertStyleElement_default()); - -var chat_bottom_panel_update = injectStylesIntoStyleTag_default()(chat_bottom_panel/* default */.Z, chat_bottom_panel_options); - - - - - /* harmony default export */ const styles_chat_bottom_panel = (chat_bottom_panel/* default */.Z && chat_bottom_panel/* default.locals */.Z.locals ? chat_bottom_panel/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/chatview/bottom-panel.js -function bottom_panel_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - - - -class ChatBottomPanel extends ElementView { - constructor() { - super(); - - bottom_panel_defineProperty(this, "events", { - 'click .send-button': 'sendButtonClicked', - 'click .toggle-clear': 'clearMessages' - }); - - this.debouncedRender = lodash_es_debounce(this.render, 100); - } - - async connectedCallback() { - super.connectedCallback(); - await this.initialize(); - this.render(); // don't call in initialize, since the MUCBottomPanel subclasses it - // and we want to render after it has finished as wel. - } - - async initialize() { - this.model = await api.chatboxes.get(this.getAttribute('jid')); - await this.model.initialized; - this.listenTo(this.model, 'change:num_unread', this.debouncedRender); - this.listenTo(this.model, 'emoji-picker-autocomplete', this.autocompleteInPicker); - this.addEventListener('focusin', ev => this.emitFocused(ev)); - this.addEventListener('focusout', ev => this.emitBlurred(ev)); - } - - render() { - V(bottom_panel({ - 'model': this.model, - 'viewUnreadMessages': ev => this.viewUnreadMessages(ev) - }), this); - } - - sendButtonClicked(ev) { - var _this$querySelector; - - (_this$querySelector = this.querySelector('converse-message-form')) === null || _this$querySelector === void 0 ? void 0 : _this$querySelector.onFormSubmitted(ev); - } - - viewUnreadMessages(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - this.model.ui.set({ - 'scrolled': false - }); - } - - emitFocused(ev) { - var _converse$chatboxview; - - (_converse$chatboxview = shared_converse.chatboxviews.get(this.getAttribute('jid'))) === null || _converse$chatboxview === void 0 ? void 0 : _converse$chatboxview.emitFocused(ev); - } - - emitBlurred(ev) { - var _converse$chatboxview2; - - (_converse$chatboxview2 = shared_converse.chatboxviews.get(this.getAttribute('jid'))) === null || _converse$chatboxview2 === void 0 ? void 0 : _converse$chatboxview2.emitBlurred(ev); - } - - onDrop(evt) { - if (evt.dataTransfer.files.length == 0) { - // There are no files to be dropped, so this isn’t a file - // transfer operation. - return; - } - - evt.preventDefault(); - this.model.sendFiles(evt.dataTransfer.files); - } - - onDragOver(ev) { - // eslint-disable-line class-methods-use-this - ev.preventDefault(); - } - - clearMessages(ev) { - var _ev$preventDefault2; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - clearMessages(this.model); - } - - async autocompleteInPicker(input, value) { - await api.emojis.initialize(); - const emoji_picker = this.querySelector('converse-emoji-picker'); - - if (emoji_picker) { - emoji_picker.model.set({ - 'ac_position': input.selectionStart, - 'autocompleting': value, - 'query': value - }); - const emoji_dropdown = this.querySelector('converse-emoji-dropdown'); - emoji_dropdown === null || emoji_dropdown === void 0 ? void 0 : emoji_dropdown.showMenu(); - } - } - -} -api.elements.define('converse-chat-bottom-panel', ChatBottomPanel); -;// CONCATENATED MODULE: ./src/shared/chat/baseview.js - - - -class BaseChatView extends CustomElement { - static get properties() { - return { - jid: { - type: String - } - }; - } - - disconnectedCallback() { - super.disconnectedCallback(); - - shared_converse.chatboxviews.remove(this.jid, this); - } - - updated() { - if (this.model && this.jid !== this.model.get('jid')) { - this.stopListening(); - - shared_converse.chatboxviews.remove(this.model.get('jid'), this); - - delete this.model; - this.requestUpdate(); - this.initialize(); - } - } - - close(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - return this.model.close(ev); - } - - maybeFocus() { - api.settings.get('auto_focus') && this.focus(); - } - - focus() { - const textarea_el = this.getElementsByClassName('chat-textarea')[0]; - - if (textarea_el && document.activeElement !== textarea_el) { - textarea_el.focus(); - } - - return this; - } - - emitBlurred(ev) { - if (this.contains(document.activeElement) || this.contains(ev.relatedTarget)) { - // Something else in this chatbox is still focused - return; - } - /** - * Triggered when the focus has been removed from a particular chat. - * @event _converse#chatBoxBlurred - * @type { _converse.ChatBoxView | _converse.ChatRoomView } - * @example _converse.api.listen.on('chatBoxBlurred', (view, event) => { ... }); - */ - - - api.trigger('chatBoxBlurred', this, ev); - } - - emitFocused(ev) { - if (this.contains(ev.relatedTarget)) { - // Something else in this chatbox was already focused - return; - } - /** - * Triggered when the focus has been moved to a particular chat. - * @event _converse#chatBoxFocused - * @type { _converse.ChatBoxView | _converse.ChatRoomView } - * @example _converse.api.listen.on('chatBoxFocused', (view, event) => { ... }); - */ - - - api.trigger('chatBoxFocused', this, ev); - } - - getBottomPanel() { - if (this.model.get('type') === shared_converse.CHATROOMS_TYPE) { - return this.querySelector('converse-muc-bottom-panel'); - } else { - return this.querySelector('converse-chat-bottom-panel'); - } - } - - getMessageForm() { - if (this.model.get('type') === shared_converse.CHATROOMS_TYPE) { - return this.querySelector('converse-muc-message-form'); - } else { - return this.querySelector('converse-message-form'); - } - } - /** - * Scrolls the chat down. - * - * This method will always scroll the chat down, regardless of - * whether the user scrolled up manually or not. - * @param { Event } [ev] - An optional event that is the cause for needing to scroll down. - */ - - - scrollDown(ev) { - var _ev$preventDefault2, _ev$stopPropagation; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation = ev.stopPropagation) === null || _ev$stopPropagation === void 0 ? void 0 : _ev$stopPropagation.call(ev); - - if (this.model.ui.get('scrolled')) { - this.model.ui.set({ - 'scrolled': false - }); - } - - onScrolledDown(this.model); - } - - onWindowStateChanged(data) { - if (data.state === 'visible') { - if (!this.model.isHidden()) { - this.model.clearUnreadMsgCounter(); - } - } else if (data.state === 'hidden') { - this.model.setChatState(shared_converse.INACTIVE, { - 'silent': true - }); - this.model.sendChatState(); - } - } - -} -;// CONCATENATED MODULE: ./src/plugins/chatview/templates/chat.js - - -/* harmony default export */ const chat = (o => T` -
    - - ${o.model ? T` - -
    -
    - - - ${o.show_help_messages ? T`
    -
    ` : ''} -
    - -
    - ` : ''} -
    -`); -;// CONCATENATED MODULE: ./src/plugins/chatview/chat.js -function chat_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - -/** - * The view of an open/ongoing chat conversation. - * @class - * @namespace _converse.ChatBoxView - * @memberOf _converse - */ - -class ChatView extends BaseChatView { - constructor(...args) { - super(...args); - - chat_defineProperty(this, "length", 200); - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - async initialize() { - shared_converse.chatboxviews.add(this.jid, this); - - this.model = shared_converse.chatboxes.get(this.jid); - this.listenTo(shared_converse, 'windowStateChanged', this.onWindowStateChanged); - this.listenTo(this.model, 'change:hidden', () => !this.model.get('hidden') && this.afterShown()); - this.listenTo(this.model, 'change:show_help_messages', this.requestUpdate); - await this.model.messages.fetched; - !this.model.get('hidden') && this.afterShown(); - /** - * Triggered once the {@link _converse.ChatBoxView} has been initialized - * @event _converse#chatBoxViewInitialized - * @type { _converse.HeadlinesBoxView } - * @example _converse.api.listen.on('chatBoxViewInitialized', view => { ... }); - */ - - api.trigger('chatBoxViewInitialized', this); - } - - render() { - return chat(Object.assign({ - 'model': this.model, - 'help_messages': this.getHelpMessages(), - 'show_help_messages': this.model.get('show_help_messages') - }, this.model.toJSON())); - } - - getHelpMessages() { - // eslint-disable-line class-methods-use-this - return [`/clear: ${__('Remove messages')}`, `/close: ${__('Close this chat')}`, `/me: ${__('Write in the third person')}`, `/help: ${__('Show this menu')}`]; - } - - showControlBox() { - var _converse$chatboxview; - - // eslint-disable-line class-methods-use-this - // Used in mobile view, to navigate back to the controlbox - (_converse$chatboxview = shared_converse.chatboxviews.get('controlbox')) === null || _converse$chatboxview === void 0 ? void 0 : _converse$chatboxview.show(); - } - - afterShown() { - this.model.setChatState(shared_converse.ACTIVE); - this.model.clearUnreadMsgCounter(); - this.maybeFocus(); - } - -} -api.elements.define('converse-chat', ChatView); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/chatview/styles/index.scss -var chatview_styles = __webpack_require__(9107); -;// CONCATENATED MODULE: ./src/plugins/chatview/styles/index.scss - - - - - - - - - - - -var styles_options = {}; - -styles_options.styleTagTransform = (styleTagTransform_default()); -styles_options.setAttributes = (setAttributesWithoutAttributes_default()); - - styles_options.insert = insertBySelector_default().bind(null, "head"); - -styles_options.domAPI = (styleDomAPI_default()); -styles_options.insertStyleElement = (insertStyleElement_default()); - -var styles_update = injectStylesIntoStyleTag_default()(chatview_styles/* default */.Z, styles_options); - - - - - /* harmony default export */ const plugins_chatview_styles = (chatview_styles/* default */.Z && chatview_styles/* default.locals */.Z.locals ? chatview_styles/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/chatview/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - - -const { - Strophe: chatview_Strophe -} = core_converse.env; -core_converse.plugins.add('converse-chatview', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ['converse-chatboxviews', 'converse-chat', 'converse-disco', 'converse-modal'], - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - api.settings.extend({ - 'auto_focus': true, - 'debounced_content_rendering': true, - 'embed_videos': true, - 'embed_audio': true, - 'filter_url_query_params': null, - 'image_urls_regex': null, - 'message_limit': 0, - 'muc_hats': ['xep317'], - 'show_images_inline': true, - 'show_message_avatar': true, - 'show_retraction_warning': true, - 'show_send_button': true, - 'show_toolbar': true, - 'time_format': 'HH:mm', - 'use_system_emojis': true, - 'visible_toolbar_buttons': { - 'call': false, - 'clear': true, - 'emoji': true, - 'spoiler': true - } - }); - shared_converse.ChatBoxView = ChatView; - api.listen.on('connected', () => api.disco.own.features.add(chatview_Strophe.NS.SPOILER)); - api.listen.on('chatBoxClosed', model => clearHistory(model.get('jid'))); - } - -}); -;// CONCATENATED MODULE: ./src/shared/components/brand-byline.js - - - -class ConverseBrandByline extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - const is_fullscreen = api.settings.get('view_mode') === 'fullscreen'; - return T` - ${is_fullscreen ? T` -

    ${shared_converse.VERSION_NAME}

    -

    - Open Source XMPP chat client - brought to you by Opkode -

    -

    - Translate - it into your own language -

    - ` : ''} - `; - } - -} -api.elements.define('converse-brand-byline', ConverseBrandByline); -;// CONCATENATED MODULE: ./src/shared/components/brand-logo.js - - - -class ConverseBrandLogo extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - const is_fullscreen = api.settings.get('view_mode') === 'fullscreen'; - return T` - - - - - converse.js - ${is_fullscreen ? T` - - ` : ''} - - - - `; - } - -} -api.elements.define('converse-brand-logo', ConverseBrandLogo); -;// CONCATENATED MODULE: ./src/shared/components/brand-heading.js - - - - - -class ConverseBrandHeading extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - return T` - - - `; - } - -} -api.elements.define('converse-brand-heading', ConverseBrandHeading); -;// CONCATENATED MODULE: ./src/plugins/controlbox/templates/loginpanel.js - - - - - - -const trust_checkbox = checked => { - const i18n_hint_trusted = __('To improve performance, we cache your data in this browser. ' + 'Uncheck this box if this is a public computer or if you want your data to be deleted when you log out. ' + 'It\'s important that you explicitly log out, otherwise not all cached data might be deleted. ' + 'Please note, when using an untrusted device, OMEMO encryption is NOT available.'); - - const i18n_trusted = __('This is a trusted device'); - - return T` - - `; -}; - -const password_input = () => { - const i18n_password = __('Password'); - - return T` -
    - - -
    - `; -}; - -const register_link = () => { - const i18n_create_account = __("Create an account"); - - const i18n_hint_no_account = __("Don't have a chat account?"); - - return T` -
    -

    ${i18n_hint_no_account}

    -

    -
    - `; -}; - -const show_register_link = () => { - return shared_converse.allow_registration && !api.settings.get("auto_login") && shared_converse.pluggable.plugins["converse-register"].enabled(shared_converse); -}; - -const auth_fields = o => { - const i18n_login = __('Log in'); - - const i18n_xmpp_address = __("XMPP Address"); - - return T` -
    - - -
    - ${o.authentication !== o.EXTERNAL ? password_input() : ''} - ${o.show_trust_checkbox ? trust_checkbox(o.show_trust_checkbox === 'off' ? false : true) : ''} -
    - -
    - ${show_register_link() ? register_link(o) : ''} - `; -}; - -const form_fields = o => { - const i18n_disconnected = __('Disconnected'); - - const i18n_anon_login = __('Click here to log in anonymously'); - - return T` - ${o.authentication == o.LOGIN || o.authentication == o.EXTERNAL ? auth_fields(o) : ''} - ${o.authentication == o.ANONYMOUS ? T`` : ''} - ${o.authentication == o.PREBIND ? T`

    ${i18n_disconnected}

    ` : ''} - `; -}; - -/* harmony default export */ const loginpanel = (o => T` - -
    -
    - - -
    - ${shared_converse.CONNECTION_STATUS[o.connection_status] === 'CONNECTING' ? spinner({ - 'classes': 'hor_centered' -}) : form_fields(o)} -
    -`); -;// CONCATENATED MODULE: ./src/plugins/controlbox/loginpanel.js -function loginpanel_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - - -const loginpanel_u = core_converse.env.utils; -const { - Strophe: loginpanel_Strophe -} = core_converse.env; -const REPORTABLE_STATUSES = [0, // ERROR' -1, // CONNECTING -2, // CONNFAIL -3, // AUTHENTICATING -4, // AUTHFAIL -7, // DISCONNECTING -10 // RECONNECTING -]; -const PRETTY_CONNECTION_STATUS = { - 0: 'Error', - 1: 'Connecting', - 2: 'Connection failure', - 3: 'Authenticating', - 4: 'Authentication failure', - 5: 'Connected', - 6: 'Disconnected', - 7: 'Disconnecting', - 8: 'Attached', - 9: 'Redirect', - 10: 'Reconnecting' -}; -const CONNECTION_STATUS_CSS_CLASS = { - 'Error': 'error', - 'Connecting': 'info', - 'Connection failure': 'error', - 'Authenticating': 'info', - 'Authentication failure': 'error', - 'Connected': 'info', - 'Disconnected': 'error', - 'Disconnecting': 'warn', - 'Attached': 'info', - 'Redirect': 'info', - 'Reconnecting': 'warn' -}; -const LoginPanelModel = Model.extend({ - defaults: { - // Passed-by-reference. Fine in this case because there's only one such model. - 'errors': [] - } -}); - -class LoginPanel extends ElementView { - constructor(...args) { - super(...args); - - loginpanel_defineProperty(this, "id", "converse-login-panel"); - - loginpanel_defineProperty(this, "className", 'controlbox-pane fade-in row no-gutters'); - - loginpanel_defineProperty(this, "events", { - 'submit form#converse-login': 'authenticate', - 'change input': 'validate' - }); - } - - initialize() { - this.model = new LoginPanelModel(); - this.listenTo(this.model, 'change', this.render); - this.listenTo(shared_converse.connfeedback, 'change', this.render); - this.render(); - this.initPopovers(); - } - - render() { - const connection_status = shared_converse.connfeedback.get('connection_status'); - - let feedback_class, pretty_status; - - if (REPORTABLE_STATUSES.includes(connection_status)) { - pretty_status = PRETTY_CONNECTION_STATUS[connection_status]; - feedback_class = CONNECTION_STATUS_CSS_CLASS[pretty_status]; - } - - V(loginpanel(Object.assign(this.model.toJSON(), { - '_converse': shared_converse, - 'ANONYMOUS': shared_converse.ANONYMOUS, - 'EXTERNAL': shared_converse.EXTERNAL, - 'LOGIN': shared_converse.LOGIN, - 'PREBIND': shared_converse.PREBIND, - 'auto_login': api.settings.get('auto_login'), - 'authentication': api.settings.get("authentication"), - 'connection_status': connection_status, - 'conn_feedback_class': feedback_class, - 'conn_feedback_subject': pretty_status, - 'conn_feedback_message': shared_converse.connfeedback.get('message'), - 'placeholder_username': (api.settings.get('locked_domain') || api.settings.get('default_domain')) && __('Username') || __('user@domain'), - 'show_trust_checkbox': api.settings.get('allow_user_trust_override') - })), this); - } - - initPopovers() { - Array.from(this.querySelectorAll('[data-title]')).forEach(el => { - new (bootstrap_native_default()).Popover(el, { - 'trigger': api.settings.get("view_mode") === 'mobile' && 'click' || 'hover', - 'dismissible': api.settings.get("view_mode") === 'mobile' && true || false, - 'container': this.parentElement.parentElement.parentElement - }); - }); - } - - validate() { - const form = this.querySelector('form'); - const jid_element = form.querySelector('input[name=jid]'); - - if (jid_element.value && !api.settings.get('locked_domain') && !api.settings.get('default_domain') && !loginpanel_u.isValidJID(jid_element.value)) { - jid_element.setCustomValidity(__('Please enter a valid XMPP address')); - return false; - } - - jid_element.setCustomValidity(''); - return true; - } - /** - * Authenticate the user based on a form submission event. - * @param { Event } ev - */ - - - authenticate(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - - if (api.settings.get("authentication") === shared_converse.ANONYMOUS) { - return this.connect(shared_converse.jid, null); - } - - if (!this.validate()) { - return; - } - - const form_data = new FormData(ev.target); - - shared_converse.config.save({ - 'trusted': form_data.get('trusted') && true || false - }); - - let jid = form_data.get('jid'); - - if (api.settings.get('locked_domain')) { - const last_part = '@' + api.settings.get('locked_domain'); - - if (jid.endsWith(last_part)) { - jid = jid.substr(0, jid.length - last_part.length); - } - - jid = loginpanel_Strophe.escapeNode(jid) + last_part; - } else if (api.settings.get('default_domain') && !jid.includes('@')) { - jid = jid + '@' + api.settings.get('default_domain'); - } - - this.connect(jid, form_data.get('password')); - } - - connect(jid, password) { - // eslint-disable-line class-methods-use-this - if (["converse/login", "converse/register"].includes(shared_converse.router.history.getFragment())) { - shared_converse.router.navigate('', { - 'replace': true - }); - } - - shared_converse.connection && shared_converse.connection.reset(); - api.user.login(jid, password); - } - -} - -api.elements.define('converse-login-panel', LoginPanel); -;// CONCATENATED MODULE: ./src/plugins/controlbox/utils.js - -const controlbox_utils_u = core_converse.env.utils; -function addControlBox() { - var _converse$chatboxview; - - const m = shared_converse.chatboxes.add(new shared_converse.ControlBox({ - 'id': 'controlbox' - })); - - (_converse$chatboxview = shared_converse.chatboxviews.get('controlbox')) === null || _converse$chatboxview === void 0 ? void 0 : _converse$chatboxview.setModel(); - return m; -} -function showControlBox(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - const controlbox = shared_converse.chatboxes.get('controlbox') || addControlBox(); - controlbox_utils_u.safeSave(controlbox, { - 'closed': false - }); -} -function navigateToControlBox(jid) { - showControlBox(); - - const model = shared_converse.chatboxes.get(jid); - - controlbox_utils_u.safeSave(model, { - 'hidden': true - }); -} -function disconnect() { - /* Upon disconnection, set connected to `false`, so that if - * we reconnect, "onConnected" will be called, - * to fetch the roster again and to send out a presence stanza. - */ - const view = shared_converse.chatboxviews.get('controlbox'); - - view.model.set({ - 'connected': false - }); - return view; -} -function controlbox_utils_clearSession() { - const chatboxviews = shared_converse === null || shared_converse === void 0 ? void 0 : shared_converse.chatboxviews; - const view = chatboxviews && chatboxviews.get('controlbox'); - - if (view) { - controlbox_utils_u.safeSave(view.model, { - 'connected': false - }); - - if (view !== null && view !== void 0 && view.controlbox_pane) { - view.controlbox_pane.remove(); - delete view.controlbox_pane; - } - } -} -function onChatBoxesFetched() { - const controlbox = shared_converse.chatboxes.get('controlbox') || addControlBox(); - controlbox.save({ - 'connected': true - }); -} -;// CONCATENATED MODULE: ./src/plugins/controlbox/templates/navback.js - - -/* harmony default export */ const navback = (jid => { - return T` navigateToControlBox(jid)}>`; -}); -;// CONCATENATED MODULE: ./src/plugins/controlbox/navback.js - - - - -class ControlBoxNavback extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - render() { - return navback(this.jid); - } - -} - -api.elements.define('converse-controlbox-navback', ControlBoxNavback); -/* harmony default export */ const controlbox_navback = ((/* unused pure expression or super */ null && (ControlBoxNavback))); -;// CONCATENATED MODULE: ./src/plugins/controlbox/model.js - - -const { - dayjs: model_dayjs -} = core_converse.env; -/** - * The ControlBox is the section of the chat that contains the open groupchats, - * bookmarks and roster. - * - * In `overlayed` `view_mode` it's a box like the chat boxes, in `fullscreen` - * `view_mode` it's a left-aligned sidebar. - * @mixin - */ - -const ControlBox = Model.extend({ - defaults() { - return { - 'bookmarked': false, - 'box_id': 'controlbox', - 'chat_state': undefined, - 'closed': !api.settings.get('show_controlbox_by_default'), - 'num_unread': 0, - 'time_opened': model_dayjs(0).valueOf(), - 'type': shared_converse.CONTROLBOX_TYPE, - 'url': '' - }; - }, - - validate(attrs) { - if (attrs.type === shared_converse.CONTROLBOX_TYPE) { - if (api.settings.get('view_mode') === 'embedded' && api.settings.get('singleton')) { - return 'Controlbox not relevant in embedded view mode'; - } - - return; - } - - return shared_converse.ChatBox.prototype.validate.call(this, attrs); - }, - - maybeShow(force) { - if (!force && this.get('id') === 'controlbox') { - // Must return the chatbox - return this; - } - - return shared_converse.ChatBox.prototype.maybeShow.call(this, force); - }, - - onReconnection() { - this.save('connected', true); - } - -}); -/* harmony default export */ const controlbox_model = (ControlBox); -;// CONCATENATED MODULE: ./src/plugins/controlbox/templates/toggle.js - - - -/* harmony default export */ const toggle = (o => { - const i18n_toggle = api.connection.connected() ? __('Chat Contacts') : __('Toggle chat'); - return T`${i18n_toggle}`; -}); -;// CONCATENATED MODULE: ./src/plugins/controlbox/toggle.js - - - - - -class ControlBoxToggle extends CustomElement { - async connectedCallback() { - super.connectedCallback(); - await api.waitUntil('initialized'); - this.model = shared_converse.chatboxes.get('controlbox'); - this.listenTo(this.model, 'change:closed', () => this.requestUpdate()); - this.requestUpdate(); - } - - render() { - var _this$model; - - return toggle({ - 'onClick': showControlBox, - 'hide': !((_this$model = this.model) !== null && _this$model !== void 0 && _this$model.get('closed')) - }); - } - -} - -api.elements.define('converse-controlbox-toggle', ControlBoxToggle); -/* harmony default export */ const controlbox_toggle = (ControlBoxToggle); -;// CONCATENATED MODULE: ./src/plugins/controlbox/templates/controlbox.js - - -/* harmony default export */ const controlbox = (o => T` -
    - -
    - ${o.sticky_controlbox ? '' : T` - - `} -
    -
    -
    - ${o.connected ? T` - - -
    - - -
    - ${api.settings.get("authentication") === shared_converse.ANONYMOUS ? '' : T`
    `}` : o['active-form'] === 'register' ? T`` : T``} -
    -
    -
    -`); -;// CONCATENATED MODULE: ./src/plugins/controlbox/controlbox.js - - - - -const controlbox_u = core_converse.env.utils; -/** - * The ControlBox is the section of the chat that contains the open groupchats, - * bookmarks and roster. - * - * In `overlayed` `view_mode` it's a box like the chat boxes, in `fullscreen` - * `view_mode` it's a left-aligned sidebar. - */ - -class controlbox_ControlBox extends ElementView { - initialize() { - this.setModel(); - this.render(); - - shared_converse.chatboxviews.add('controlbox', this); - /** - * Triggered when the _converse.ControlBoxView has been initialized and therefore - * exists. The controlbox contains the login and register forms when the user is - * logged out and a list of the user's contacts and group chats when logged in. - * @event _converse#controlBoxInitialized - * @type { _converse.ControlBoxView } - * @example _converse.api.listen.on('controlBoxInitialized', view => { ... }); - */ - - - api.trigger('controlBoxInitialized', this); - } - - setModel() { - this.model = shared_converse.chatboxes.get('controlbox'); - this.initEventHandlers(); - } - - initEventHandlers() { - // Keep event handler registration in a separate method so that it can - // be called when a new controlbox is created and assigned to this - // element. - this.listenTo(this.model, 'change:active-form', this.render); - this.listenTo(this.model, 'change:connected', this.render); - this.listenTo(this.model, 'change:closed', () => !this.model.get('closed') && this.afterShown()); - } - - render() { - V(controlbox({ - 'sticky_controlbox': api.settings.get('sticky_controlbox'), - ...this.model.toJSON(), - 'close': ev => this.close(ev) - }), this); - } - - afterRender() { - if (this.model.get('connected') && this.model.get('closed') === undefined) { - this.model.set('closed', !api.settings.get('show_controlbox_by_default')); - } - } - - close(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - - if ((ev === null || ev === void 0 ? void 0 : ev.name) === 'closeAllChatBoxes' && (shared_converse.disconnection_cause !== shared_converse.LOGOUT || api.settings.get('show_controlbox_by_default'))) { - return; - } - - if (api.settings.get('sticky_controlbox')) { - return; - } - - controlbox_u.safeSave(this.model, { - 'closed': true - }); - api.trigger('controlBoxClosed', this); - return this; - } - - afterShown() { - /** - * Triggered once the controlbox has been opened - * @event _converse#controlBoxOpened - * @type {_converse.ControlBox} - */ - api.trigger('controlBoxOpened', this); - return this; - } - - showHelpMessages() { - // eslint-disable-line class-methods-use-this - return; - } - -} - -api.elements.define('converse-controlbox', controlbox_ControlBox); -/* harmony default export */ const controlbox_controlbox = (controlbox_ControlBox); -;// CONCATENATED MODULE: ./src/plugins/controlbox/api.js - -const { - u: controlbox_api_u -} = core_converse.env; -/* harmony default export */ const controlbox_api = ({ - /** - * The "controlbox" namespace groups methods pertaining to the - * controlbox view - * - * @namespace _converse.api.controlbox - * @memberOf _converse.api - */ - controlbox: { - /** - * Opens the controlbox - * @method _converse.api.controlbox.open - * @returns { Promise<_converse.ControlBox> } - */ - async open() { - await api.waitUntil('chatBoxesFetched'); - const model = (await api.chatboxes.get('controlbox')) || api.chatboxes.create('controlbox', {}, shared_converse.Controlbox); - controlbox_api_u.safeSave(model, { - 'closed': false - }); - return model; - }, - - /** - * Returns the controlbox view. - * @method _converse.api.controlbox.get - * @returns { View } View representing the controlbox - * @example const view = _converse.api.controlbox.get(); - */ - get() { - return shared_converse.chatboxviews.get('controlbox'); - } - - } -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/controlbox/styles/_controlbox.scss -var _controlbox = __webpack_require__(7581); -;// CONCATENATED MODULE: ./src/plugins/controlbox/styles/_controlbox.scss - - - - - - - - - - - -var _controlbox_options = {}; - -_controlbox_options.styleTagTransform = (styleTagTransform_default()); -_controlbox_options.setAttributes = (setAttributesWithoutAttributes_default()); - - _controlbox_options.insert = insertBySelector_default().bind(null, "head"); - -_controlbox_options.domAPI = (styleDomAPI_default()); -_controlbox_options.insertStyleElement = (insertStyleElement_default()); - -var _controlbox_update = injectStylesIntoStyleTag_default()(_controlbox/* default */.Z, _controlbox_options); - - - - - /* harmony default export */ const styles_controlbox = (_controlbox/* default */.Z && _controlbox/* default.locals */.Z.locals ? _controlbox/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/controlbox/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - - - - - -core_converse.plugins.add('converse-controlbox', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ['converse-modal', 'converse-chatboxes', 'converse-chat', 'converse-rosterview', 'converse-chatview'], - - enabled(_converse) { - return !_converse.api.settings.get('singleton'); - }, - - overrides: { - // Overrides mentioned here will be picked up by converse.js's - // plugin architecture they will replace existing methods on the - // relevant objects or classes. - // - // New functions which don't exist yet can also be added. - ChatBoxes: { - model(attrs, options) { - if (attrs && attrs.id == 'controlbox') { - return new controlbox_model(attrs, options); - } else { - return this.__super__.model.apply(this, arguments); - } - } - - } - }, - - initialize() { - api.settings.extend({ - allow_logout: true, - allow_user_trust_override: true, - default_domain: undefined, - locked_domain: undefined, - show_controlbox_by_default: false, - sticky_controlbox: false - }); - api.promises.add('controlBoxInitialized'); - Object.assign(api, controlbox_api); - shared_converse.ControlBoxView = controlbox_controlbox; - shared_converse.ControlBox = controlbox_model; - shared_converse.ControlBoxToggle = controlbox_toggle; - /******************** Event Handlers ********************/ - - api.listen.on('chatBoxesFetched', onChatBoxesFetched); - api.listen.on('cleanup', () => delete shared_converse.controlboxtoggle); - api.listen.on('clearSession', controlbox_utils_clearSession); - api.listen.on('will-reconnect', disconnect); - api.waitUntil('chatBoxViewsInitialized').then(addControlBox).catch(e => headless_log.fatal(e)); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/dragresize/utils.js - -const { - u: dragresize_utils_u -} = core_converse.env; -function onStartVerticalResize(ev, trigger = true) { - if (!api.settings.get('allow_dragresize')) { - return true; - } - - ev.preventDefault(); // Record element attributes for mouseMove(). - - const flyout = dragresize_utils_u.ancestor(ev.target, '.box-flyout'); - const style = window.getComputedStyle(flyout); - const chatbox_el = flyout.parentElement; - chatbox_el.height = parseInt(style.height.replace(/px$/, ''), 10); - shared_converse.resizing = { - 'chatbox': chatbox_el, - 'direction': 'top' - }; - chatbox_el.prev_pageY = ev.pageY; - - if (trigger) { - /** - * Triggered once the user starts to vertically resize a {@link _converse.ChatBoxView} - * @event _converse#startVerticalResize - * @example _converse.api.listen.on('startVerticalResize', (view) => { ... }); - */ - api.trigger('startVerticalResize', chatbox_el); - } -} -function onStartHorizontalResize(ev, trigger = true) { - if (!api.settings.get('allow_dragresize')) { - return true; - } - - ev.preventDefault(); - const flyout = dragresize_utils_u.ancestor(ev.target, '.box-flyout'); - const style = window.getComputedStyle(flyout); - const chatbox_el = flyout.parentElement; - chatbox_el.width = parseInt(style.width.replace(/px$/, ''), 10); - shared_converse.resizing = { - 'chatbox': chatbox_el, - 'direction': 'left' - }; - chatbox_el.prev_pageX = ev.pageX; - - if (trigger) { - /** - * Triggered once the user starts to horizontally resize a {@link _converse.ChatBoxView} - * @event _converse#startHorizontalResize - * @example _converse.api.listen.on('startHorizontalResize', (view) => { ... }); - */ - api.trigger('startHorizontalResize', chatbox_el); - } -} -function onStartDiagonalResize(ev) { - onStartHorizontalResize(ev, false); - onStartVerticalResize(ev, false); - shared_converse.resizing.direction = 'topleft'; - /** - * Triggered once the user starts to diagonally resize a {@link _converse.ChatBoxView} - * @event _converse#startDiagonalResize - * @example _converse.api.listen.on('startDiagonalResize', (view) => { ... }); - */ - - api.trigger('startDiagonalResize', this); -} -/** - * Applies some resistance to `value` around the `default_value`. - * If value is close enough to `default_value`, then it is returned, otherwise - * `value` is returned. - * @param { Integer } value - * @param { Integer } default_value - * @returns { Integer } - */ - -function applyDragResistance(value, default_value) { - if (value === undefined) { - return undefined; - } else if (default_value === undefined) { - return value; - } - - const resistance = 10; - - if (value !== default_value && Math.abs(value - default_value) < resistance) { - return default_value; - } - - return value; -} -function onMouseMove(ev) { - if (!shared_converse.resizing || !api.settings.get('allow_dragresize')) { - return true; - } - - ev.preventDefault(); - - shared_converse.resizing.chatbox.resizeChatBox(ev); -} -function onMouseUp(ev) { - if (!shared_converse.resizing || !api.settings.get('allow_dragresize')) { - return true; - } - - ev.preventDefault(); - const height = applyDragResistance(shared_converse.resizing.chatbox.height, shared_converse.resizing.chatbox.model.get('default_height')); - const width = applyDragResistance(shared_converse.resizing.chatbox.width, shared_converse.resizing.chatbox.model.get('default_width')); - - if (api.connection.connected()) { - shared_converse.resizing.chatbox.model.save({ - 'height': height - }); - - shared_converse.resizing.chatbox.model.save({ - 'width': width - }); - } else { - shared_converse.resizing.chatbox.model.set({ - 'height': height - }); - - shared_converse.resizing.chatbox.model.set({ - 'width': width - }); - } - - shared_converse.resizing = null; -} -;// CONCATENATED MODULE: ./src/plugins/dragresize/templates/dragresize.js - - -/* harmony default export */ const dragresize = (() => T` -
    -
    -
    -`); -;// CONCATENATED MODULE: ./src/plugins/dragresize/components/dragresize.js - - - -class ConverseDragResize extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - return dragresize(); - } - -} - -customElements.define('converse-dragresize', ConverseDragResize); -;// CONCATENATED MODULE: ./src/plugins/dragresize/mixin.js - - - -const DragResizableMixin = { - initDragResize() { - var _converse$connection; - - const view = this; - const debouncedSetDimensions = lodash_es_debounce(() => view.setDimensions()); - window.addEventListener('resize', view.debouncedSetDimensions); - this.listenTo(this.model, 'destroy', () => window.removeEventListener('resize', debouncedSetDimensions)); // Determine and store the default box size. - // We need this information for the drag-resizing feature. - - const flyout = this.querySelector('.box-flyout'); - const style = window.getComputedStyle(flyout); - - if (this.model.get('height') === undefined) { - const height = parseInt(style.height.replace(/px$/, ''), 10); - const width = parseInt(style.width.replace(/px$/, ''), 10); - this.model.set('height', height); - this.model.set('default_height', height); - this.model.set('width', width); - this.model.set('default_width', width); - } - - const min_width = style['min-width']; - const min_height = style['min-height']; - this.model.set('min_width', min_width.endsWith('px') ? Number(min_width.replace(/px$/, '')) : 0); - this.model.set('min_height', min_height.endsWith('px') ? Number(min_height.replace(/px$/, '')) : 0); // Initialize last known mouse position - - this.prev_pageY = 0; - this.prev_pageX = 0; - - if ((_converse$connection = shared_converse.connection) !== null && _converse$connection !== void 0 && _converse$connection.connected) { - this.height = this.model.get('height'); - this.width = this.model.get('width'); - } - - return this; - }, - - resizeChatBox(ev) { - let diff; - - if (shared_converse.resizing.direction.indexOf('top') === 0) { - diff = ev.pageY - this.prev_pageY; - - if (diff) { - this.height = this.height - diff > (this.model.get('min_height') || 0) ? this.height - diff : this.model.get('min_height'); - this.prev_pageY = ev.pageY; - this.setChatBoxHeight(this.height); - } - } - - if (shared_converse.resizing.direction.includes('left')) { - diff = this.prev_pageX - ev.pageX; - - if (diff) { - this.width = this.width + diff > (this.model.get('min_width') || 0) ? this.width + diff : this.model.get('min_width'); - this.prev_pageX = ev.pageX; - this.setChatBoxWidth(this.width); - } - } - }, - - setDimensions() { - // Make sure the chat box has the right height and width. - this.adjustToViewport(); - this.setChatBoxHeight(this.model.get('height')); - this.setChatBoxWidth(this.model.get('width')); - }, - - setChatBoxHeight(height) { - if (height) { - height = applyDragResistance(height, this.model.get('default_height')) + 'px'; - } else { - height = ''; - } - - const flyout_el = this.querySelector('.box-flyout'); - - if (flyout_el !== null) { - flyout_el.style.height = height; - } - }, - - setChatBoxWidth(width) { - if (width) { - width = applyDragResistance(width, this.model.get('default_width')) + 'px'; - } else { - width = ''; - } - - this.style.width = width; - const flyout_el = this.querySelector('.box-flyout'); - - if (flyout_el !== null) { - flyout_el.style.width = width; - } - }, - - adjustToViewport() { - /* Event handler called when viewport gets resized. We remove - * custom width/height from chat boxes. - */ - const viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); - const viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); - - if (viewport_width <= 480) { - this.model.set('height', undefined); - this.model.set('width', undefined); - } else if (viewport_width <= this.model.get('width')) { - this.model.set('width', undefined); - } else if (viewport_height <= this.model.get('height')) { - this.model.set('height', undefined); - } - } - -}; -/* harmony default export */ const mixin = (DragResizableMixin); -;// CONCATENATED MODULE: ./src/plugins/dragresize/index.js -/** - * @module converse-dragresize - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - -core_converse.plugins.add('converse-dragresize', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ['converse-chatview', 'converse-headlines-view', 'converse-muc-views'], - - enabled(_converse) { - return _converse.api.settings.get('view_mode') == 'overlayed'; - }, - - overrides: { - // Overrides mentioned here will be picked up by converse.js's - // plugin architecture they will replace existing methods on the - // relevant objects or classes. - ChatBox: { - initialize() { - const result = this.__super__.initialize.apply(this, arguments); - - const height = this.get('height'); - const width = this.get('width'); - const save = this.get('id') === 'controlbox' ? a => this.set(a) : a => this.save(a); - save({ - 'height': applyDragResistance(height, this.get('default_height')), - 'width': applyDragResistance(width, this.get('default_width')) - }); - return result; - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - api.settings.extend({ - 'allow_dragresize': true - }); - Object.assign(shared_converse.ChatBoxView.prototype, mixin); - Object.assign(shared_converse.ChatRoomView.prototype, mixin); - Object.assign(shared_converse.ControlBoxView.prototype, mixin); - /************************ BEGIN Event Handlers ************************/ - - function registerGlobalEventHandlers() { - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - } - - function unregisterGlobalEventHandlers() { - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - } - - api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers); - api.listen.on('unregisteredGlobalEventHandlers', unregisterGlobalEventHandlers); - api.listen.on('beforeShowingChatView', view => view.initDragResize().setDimensions()); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/singleton.js -/** - * @module converse-singleton - * @copyright JC Brand - * @license Mozilla Public License (MPLv2) - * @description A plugin which restricts Converse to only one chat. - */ - -core_converse.plugins.add('converse-singleton', { - enabled(_converse) { - return _converse.api.settings.get("singleton"); - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - api.settings.extend({ - 'allow_logout': false, - // No point in logging out when we have auto_login as true. - 'allow_muc_invitations': false, - // Doesn't make sense to allow because only - // roster contacts can be invited - 'hide_muc_server': true - }); - - if (!Array.isArray(api.settings.get('auto_join_rooms')) && !Array.isArray(api.settings.get('auto_join_private_chats'))) { - throw new Error("converse-singleton: auto_join_rooms must be an Array"); - } - - if (api.settings.get('auto_join_rooms').length > 1 || api.settings.get('auto_join_private_chats').length > 1) { - throw new Error("It doesn't make sense to have singleton set to true and " + "auto_join_rooms or auto_join_private_chats set to more then one, " + "since only one chat room may be open at any time."); - } - } - -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/fullscreen/styles/fullscreen.scss -var fullscreen = __webpack_require__(6752); -;// CONCATENATED MODULE: ./src/plugins/fullscreen/styles/fullscreen.scss - - - - - - - - - - - -var fullscreen_options = {}; - -fullscreen_options.styleTagTransform = (styleTagTransform_default()); -fullscreen_options.setAttributes = (setAttributesWithoutAttributes_default()); - - fullscreen_options.insert = insertBySelector_default().bind(null, "head"); - -fullscreen_options.domAPI = (styleDomAPI_default()); -fullscreen_options.insertStyleElement = (insertStyleElement_default()); - -var fullscreen_update = injectStylesIntoStyleTag_default()(fullscreen/* default */.Z, fullscreen_options); - - - - - /* harmony default export */ const styles_fullscreen = (fullscreen/* default */.Z && fullscreen/* default.locals */.Z.locals ? fullscreen/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/fullscreen/index.js -/** - * @module converse-fullscreen - * @license Mozilla Public License (MPLv2) - * @copyright 2020, the Converse.js contributors - */ - - - - - - -core_converse.plugins.add('converse-fullscreen', { - enabled(_converse) { - return _converse.isUniView(); - }, - - initialize() { - api.settings.extend({ - chatview_avatar_height: 50, - chatview_avatar_width: 50, - hide_open_bookmarks: true, - show_controlbox_by_default: true, - sticky_controlbox: true - }); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/templates/chat-head.js - - - -/* harmony default export */ const templates_chat_head = (o => { - const tpl_standalone_btns = o => o.standalone_btns.reverse().map(b => until_o(b, '')); - - return T` -
    -
    - ${!shared_converse.api.settings.get("singleton") ? T`` : ''} -
    ${o.display_name}
    -
    -
    - ${o.dropdown_btns.length ? T`` : ''} - ${o.standalone_btns.length ? tpl_standalone_btns(o) : ''} -
    -
    - ${o.status ? T`

    ${o.status}

    ` : ''} - `; -}); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/heading.js - - - - - - -class HeadlinesHeading extends ElementView { - async connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.getAttribute('jid')); - await this.model.initialized; - this.render(); - } - - async render() { - const tpl = await this.generateHeadingTemplate(); - V(tpl, this); - } - - async generateHeadingTemplate() { - const heading_btns = await this.getHeadingButtons(); - const standalone_btns = heading_btns.filter(b => b.standalone); - const dropdown_btns = heading_btns.filter(b => !b.standalone); - return templates_chat_head(Object.assign(this.model.toJSON(), { - 'display_name': this.model.getDisplayName(), - 'dropdown_btns': dropdown_btns.map(b => getHeadingDropdownItem(b)), - 'standalone_btns': standalone_btns.map(b => getHeadingStandaloneButton(b)) - })); - } - /** - * Returns a list of objects which represent buttons for the headlines header. - * @async - * @emits _converse#getHeadingButtons - * @method HeadlinesHeading#getHeadingButtons - */ - - - getHeadingButtons() { - const buttons = []; - - if (!api.settings.get('singleton')) { - buttons.push({ - 'a_class': 'close-chatbox-button', - 'handler': ev => this.close(ev), - 'i18n_text': __('Close'), - 'i18n_title': __('Close these announcements'), - 'icon_class': 'fa-times', - 'name': 'close', - 'standalone': api.settings.get('view_mode') === 'overlayed' - }); - } - - return shared_converse.api.hook('getHeadingButtons', this, buttons); - } - - close(ev) { - ev.preventDefault(); - this.model.close(); - } - -} -api.elements.define('converse-headlines-heading', HeadlinesHeading); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/templates/headlines.js - - -/* harmony default export */ const headlines = (model => T` -
    - - ${model ? T` - - -
    -
    - -
    -
    ` : ''} -
    -`); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/view.js - - - - -class HeadlinesView extends BaseChatView { - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - async initialize() { - shared_converse.chatboxviews.add(this.jid, this); - - this.model = shared_converse.chatboxes.get(this.jid); - this.model.disable_mam = true; // Don't do MAM queries for this box - - this.listenTo(shared_converse, 'windowStateChanged', this.onWindowStateChanged); - this.listenTo(this.model, 'change:hidden', () => this.afterShown()); - this.listenTo(this.model, 'destroy', this.remove); - this.listenTo(this.model.messages, 'add', this.requestUpdate); - this.listenTo(this.model.messages, 'remove', this.requestUpdate); - this.listenTo(this.model.messages, 'reset', this.requestUpdate); - await this.model.messages.fetched; - this.model.maybeShow(); - /** - * Triggered once the {@link _converse.HeadlinesBoxView} has been initialized - * @event _converse#headlinesBoxViewInitialized - * @type { _converse.HeadlinesBoxView } - * @example _converse.api.listen.on('headlinesBoxViewInitialized', view => { ... }); - */ - - api.trigger('headlinesBoxViewInitialized', this); - } - - render() { - return headlines(this.model); - } - - async close(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - - if (shared_converse.router.history.getFragment() === 'converse/chat?jid=' + this.model.get('jid')) { - shared_converse.router.navigate(''); - } - - await this.model.close(ev); - return this; - } - - getNotifications() { - // eslint-disable-line class-methods-use-this - // Override method in ChatBox. We don't show notifications for - // headlines boxes. - return []; - } - - afterShown() { - this.model.clearUnreadMsgCounter(); - } - -} - -api.elements.define('converse-headlines', HeadlinesView); -;// CONCATENATED MODULE: ./src/templates/headline_list.js - - -const tpl_headline_box = o => T` - -`; - -/* harmony default export */ const headline_list = (o => T` -
    -
    - ${o.headlineboxes.map(headlinebox => tpl_headline_box(Object.assign({ - headlinebox -}, o)))} -
    -
    -`); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/templates/panel.js - - -/* harmony default export */ const panel = (o => T` -
    -
    - ${o.heading_headline} -
    -
    - ${headline_list(o)} -`); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/panel.js -function panel_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - -/** - * View which renders headlines section of the control box. - * @class - * @namespace _converse.HeadlinesPanel - * @memberOf _converse - */ - -class HeadlinesPanel extends ElementView { - constructor(...args) { - super(...args); - - panel_defineProperty(this, "events", { - 'click .open-headline': 'openHeadline' - }); - } - - initialize() { - this.model = shared_converse.chatboxes; - this.listenTo(this.model, 'add', this.renderIfHeadline); - this.listenTo(this.model, 'remove', this.renderIfHeadline); - this.listenTo(this.model, 'destroy', this.renderIfHeadline); - this.render(); - } - - toHTML() { - return panel({ - 'heading_headline': __('Announcements'), - 'headlineboxes': this.model.filter(m => m.get('type') === shared_converse.HEADLINES_TYPE), - 'open_title': __('Click to open this server message') - }); - } - - renderIfHeadline(model) { - return model && model.get('type') === shared_converse.HEADLINES_TYPE && this.render(); - } - - openHeadline(ev) { - // eslint-disable-line class-methods-use-this - ev.preventDefault(); - const jid = ev.target.getAttribute('data-headline-jid'); - - const chat = shared_converse.chatboxes.get(jid); - - chat.maybeShow(true); - } - -} -api.elements.define('converse-headlines-panel', HeadlinesPanel); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/headlines-view/styles/headlines.scss -var styles_headlines = __webpack_require__(115); -;// CONCATENATED MODULE: ./src/plugins/headlines-view/styles/headlines.scss - - - - - - - - - - - -var headlines_options = {}; - -headlines_options.styleTagTransform = (styleTagTransform_default()); -headlines_options.setAttributes = (setAttributesWithoutAttributes_default()); - - headlines_options.insert = insertBySelector_default().bind(null, "head"); - -headlines_options.domAPI = (styleDomAPI_default()); -headlines_options.insertStyleElement = (insertStyleElement_default()); - -var headlines_update = injectStylesIntoStyleTag_default()(styles_headlines/* default */.Z, headlines_options); - - - - - /* harmony default export */ const headlines_view_styles_headlines = (styles_headlines/* default */.Z && styles_headlines/* default.locals */.Z.locals ? styles_headlines/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/headlines-view/index.js -/** - * @module converse-headlines-view - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - -core_converse.plugins.add('converse-headlines-view', { - /* Plugin dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. - * - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. By default it's - * false, which means these plugins are only loaded opportunistically. - * - * NB: These plugins need to have already been loaded by the bundler - */ - dependencies: ['converse-headlines', 'converse-chatview'], - - initialize() { - shared_converse.HeadlinesPanel = HeadlinesPanel; - } - -}); -;// CONCATENATED MODULE: ./src/plugins/mam-views/templates/placeholder.js - - - -/* harmony default export */ const placeholder = (el => { - return el.model.get('fetching') ? spinner({ - 'classes': 'hor_centered' - }) : T` -
    -
    `; -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/mam-views/styles/placeholder.scss -var styles_placeholder = __webpack_require__(8859); -;// CONCATENATED MODULE: ./src/plugins/mam-views/styles/placeholder.scss - - - - - - - - - - - -var placeholder_options = {}; - -placeholder_options.styleTagTransform = (styleTagTransform_default()); -placeholder_options.setAttributes = (setAttributesWithoutAttributes_default()); - - placeholder_options.insert = insertBySelector_default().bind(null, "head"); - -placeholder_options.domAPI = (styleDomAPI_default()); -placeholder_options.insertStyleElement = (insertStyleElement_default()); - -var placeholder_update = injectStylesIntoStyleTag_default()(styles_placeholder/* default */.Z, placeholder_options); - - - - - /* harmony default export */ const mam_views_styles_placeholder = (styles_placeholder/* default */.Z && styles_placeholder/* default.locals */.Z.locals ? styles_placeholder/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/mam-views/placeholder.js - - - - - - -class Placeholder extends CustomElement { - static get properties() { - return { - 'model': { - type: Object - } - }; - } - - render() { - return placeholder(this); - } - - async fetchMissingMessages(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - this.model.set('fetching', true); - const options = { - 'before': this.model.get('before'), - 'start': this.model.get('start') - }; - await fetchArchivedMessages(this.model.collection.chatbox, options); - this.model.destroy(); - } - -} - -api.elements.define('converse-mam-placeholder', Placeholder); -;// CONCATENATED MODULE: ./src/plugins/mam-views/utils.js - - - - - -function getPlaceholderTemplate(message, tpl) { - if (message instanceof MAMPlaceholderMessage) { - return T``; - } else { - return tpl; - } -} -async function fetchMessagesOnScrollUp(view) { - if (view.model.ui.get('chat-content-spinner-top')) { - return; - } - - if (view.model.messages.length) { - const is_groupchat = view.model.get('type') === shared_converse.CHATROOMS_TYPE; - - const oldest_message = view.model.getOldestMessage(); - - if (oldest_message) { - const by_jid = is_groupchat ? view.model.get('jid') : shared_converse.bare_jid; - const stanza_id = oldest_message && oldest_message.get(`stanza_id ${by_jid}`); - view.model.ui.set('chat-content-spinner-top', true); - - try { - if (stanza_id) { - await fetchArchivedMessages(view.model, { - 'before': stanza_id - }); - } else { - await fetchArchivedMessages(view.model, { - 'end': oldest_message.get('time') - }); - } - } catch (e) { - headless_log.error(e); - view.model.ui.set('chat-content-spinner-top', false); - return; - } - - if (api.settings.get('allow_url_history_change')) { - shared_converse.router.history.navigate(`#${oldest_message.get('msgid')}`); - } - - setTimeout(() => view.model.ui.set('chat-content-spinner-top', false), 250); - } - } -} -;// CONCATENATED MODULE: ./src/plugins/mam-views/index.js -/** - * @description UI code XEP-0313 Message Archive Management - * @copyright 2021, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - -core_converse.plugins.add('converse-mam-views', { - dependencies: ['converse-mam', 'converse-chatview', 'converse-muc-views'], - - initialize() { - api.listen.on('chatBoxScrolledUp', fetchMessagesOnScrollUp); - api.listen.on('getMessageTemplate', getPlaceholderTemplate); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/minimize/templates/trimmed_chat.js - - -/* harmony default export */ const trimmed_chat = (o => { - const i18n_tooltip = __('Click to restore this chat'); - - const close_color = o.type === 'chatroom' ? "var(--chatroom-head-color)" : "var(--chat-head-text-color)"; - return T` - `; -}); -;// CONCATENATED MODULE: ./src/plugins/minimize/utils.js - - -const minimize_utils_u = core_converse.env.utils; - -function getChatBoxWidth(view) { - if (view.model.get('id') === 'controlbox') { - // We return the width of the controlbox or its toggle, - // depending on which is visible. - if (minimize_utils_u.isVisible(view)) { - return minimize_utils_u.getOuterWidth(view, true); - } else { - return minimize_utils_u.getOuterWidth(shared_converse.controlboxtoggle.el, true); - } - } else if (!view.model.get('minimized') && minimize_utils_u.isVisible(view)) { - return minimize_utils_u.getOuterWidth(view, true); - } - - return 0; -} - -function getShownChats() { - return shared_converse.chatboxviews.filter(el => // The controlbox can take a while to close, - // so we need to check its state. That's why we checked the 'closed' state. - !el.model.get('minimized') && !el.model.get('closed') && minimize_utils_u.isVisible(el)); -} - -function getMinimizedWidth() { - var _converse$minimized_c; - - const minimized_el = (_converse$minimized_c = shared_converse.minimized_chats) === null || _converse$minimized_c === void 0 ? void 0 : _converse$minimized_c.el; - return shared_converse.chatboxes.pluck('minimized').includes(true) ? minimize_utils_u.getOuterWidth(minimized_el, true) : 0; -} - -function getBoxesWidth(newchat) { - const new_id = newchat ? newchat.model.get('id') : null; - const newchat_width = newchat ? minimize_utils_u.getOuterWidth(newchat.el, true) : 0; - return Object.values(shared_converse.chatboxviews.xget(new_id)).reduce((memo, view) => memo + getChatBoxWidth(view), newchat_width); -} -/** - * This method is called when a newly created chat box will be shown. - * It checks whether there is enough space on the page to show - * another chat box. Otherwise it minimizes the oldest chat box - * to create space. - * @private - * @method _converse.ChatBoxViews#trimChats - * @param { _converse.ChatBoxView|_converse.ChatRoomView|_converse.ControlBoxView|_converse.HeadlinesBoxView } [newchat] - */ - - -async function trimChats(newchat) { - var _converse$minimized_c2; - - if (shared_converse.isTestEnv() || api.settings.get('no_trimming') || !api.connection.connected() || api.settings.get("view_mode") !== 'overlayed') { - return; - } - - const shown_chats = getShownChats(); - - if (shown_chats.length <= 1) { - return; - } - - const body_width = minimize_utils_u.getOuterWidth(document.querySelector('body'), true); - - if (getChatBoxWidth(shown_chats[0]) === body_width) { - // If the chats shown are the same width as the body, - // then we're in responsive mode and the chats are - // fullscreen. In this case we don't trim. - return; - } - - await api.waitUntil('minimizedChatsInitialized'); - const minimized_el = (_converse$minimized_c2 = shared_converse.minimized_chats) === null || _converse$minimized_c2 === void 0 ? void 0 : _converse$minimized_c2.el; - - if (minimized_el) { - while (getMinimizedWidth() + getBoxesWidth(newchat) > body_width) { - const new_id = newchat ? newchat.model.get('id') : null; - const oldest_chat = getOldestMaximizedChat([new_id]); - - if (oldest_chat) { - // We hide the chat immediately, because waiting - // for the event to fire (and letting the - // ChatBoxView hide it then) causes race - // conditions. - const view = shared_converse.chatboxviews.get(oldest_chat.get('id')); - - if (view) { - view.hide(); - } - - minimize(oldest_chat); - } else { - break; - } - } - } -} - -function getOldestMaximizedChat(exclude_ids) { - // Get oldest view (if its id is not excluded) - exclude_ids.push('controlbox'); - let i = 0; - - let model = shared_converse.chatboxes.sort().at(i); - - while (exclude_ids.includes(model.get('id')) || model.get('minimized') === true) { - i++; - model = shared_converse.chatboxes.at(i); - - if (!model) { - return null; - } - } - - return model; -} - -function addMinimizeButtonToChat(view, buttons) { - const data = { - 'a_class': 'toggle-chatbox-button', - 'handler': ev => minimize(ev, view.model), - 'i18n_text': __('Minimize'), - 'i18n_title': __('Minimize this chat'), - 'icon_class': "fa-minus", - 'name': 'minimize', - 'standalone': shared_converse.api.settings.get("view_mode") === 'overlayed' - }; - const names = buttons.map(t => t.name); - const idx = names.indexOf('close'); - return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons]; -} -function addMinimizeButtonToMUC(view, buttons) { - const data = { - 'a_class': 'toggle-chatbox-button', - 'handler': ev => minimize(ev, view.model), - 'i18n_text': __('Minimize'), - 'i18n_title': __('Minimize this groupchat'), - 'icon_class': "fa-minus", - 'name': 'minimize', - 'standalone': shared_converse.api.settings.get("view_mode") === 'overlayed' - }; - const names = buttons.map(t => t.name); - const idx = names.indexOf('signout'); - return idx > -1 ? [...buttons.slice(0, idx), data, ...buttons.slice(idx)] : [data, ...buttons]; -} -function maximize(ev, chatbox) { - if (ev !== null && ev !== void 0 && ev.preventDefault) { - ev.preventDefault(); - } else { - chatbox = ev; - } - - minimize_utils_u.safeSave(chatbox, { - 'hidden': false, - 'minimized': false, - 'time_opened': new Date().getTime() - }); -} -function minimize(ev, model) { - if (ev !== null && ev !== void 0 && ev.preventDefault) { - ev.preventDefault(); - } else { - model = ev; - } - - model.setChatState(shared_converse.INACTIVE); - minimize_utils_u.safeSave(model, { - 'hidden': true, - 'minimized': true, - 'time_minimized': new Date().toISOString() - }); -} -/** - * Handler which gets called when a {@link _converse#ChatBox} has it's - * `minimized` property set to false. - * - * Will trigger {@link _converse#chatBoxMaximized} - * @returns {_converse.ChatBoxView|_converse.ChatRoomView} - */ - -function onMaximized(model) { - if (!model.isScrolledUp()) { - model.clearUnreadMsgCounter(); - } - - model.setChatState(shared_converse.ACTIVE); - /** - * Triggered when a previously minimized chat gets maximized - * @event _converse#chatBoxMaximized - * @type { _converse.ChatBoxView } - * @example _converse.api.listen.on('chatBoxMaximized', view => { ... }); - */ - - api.trigger('chatBoxMaximized', model); -} -/** - * Handler which gets called when a {@link _converse#ChatBox} has it's - * `minimized` property set to true. - * - * Will trigger {@link _converse#chatBoxMinimized} - * @returns {_converse.ChatBoxView|_converse.ChatRoomView} - */ - - -function onMinimized(model) { - /** - * Triggered when a previously maximized chat gets Minimized - * @event _converse#chatBoxMinimized - * @type { _converse.ChatBoxView } - * @example _converse.api.listen.on('chatBoxMinimized', view => { ... }); - */ - api.trigger('chatBoxMinimized', model); -} - -function onMinimizedChanged(model) { - if (model.get('minimized')) { - onMinimized(model); - } else { - onMaximized(model); - } -} -;// CONCATENATED MODULE: ./src/plugins/minimize/components/minimized-chat.js - - - - -class MinimizedChat extends CustomElement { - static get properties() { - return { - model: { - type: Object - }, - title: { - type: String - }, - type: { - type: String - }, - num_unread: { - type: Number - } - }; - } - - render() { - const data = { - 'close': ev => this.close(ev), - 'num_unread': this.num_unread, - 'restore': ev => this.restore(ev), - 'title': this.title, - 'type': this.type - }; - return trimmed_chat(data); - } - - close(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - this.model.close(); - } - - restore(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - maximize(this.model); - } - -} -api.elements.define('converse-minimized-chat', MinimizedChat); -;// CONCATENATED MODULE: ./src/plugins/minimize/toggle.js - -const MinimizedChatsToggle = Model.extend({ - defaults: { - 'collapsed': false - } -}); -/* harmony default export */ const minimize_toggle = (MinimizedChatsToggle); -;// CONCATENATED MODULE: ./src/plugins/minimize/templates/chats-panel.js - - -/* harmony default export */ const chats_panel = (o => T``); -;// CONCATENATED MODULE: ./src/plugins/minimize/view.js - - - - - -class MinimizedChats extends CustomElement { - constructor() { - super(); - this.initialize(); - } - - async initialize() { - this.model = shared_converse.chatboxes; - await this.initToggle(); - this.listenTo(this.minchats, 'change:collapsed', this.requestUpdate); - this.listenTo(this.model, 'add', this.requestUpdate); - this.listenTo(this.model, 'change:fullname', this.requestUpdate); - this.listenTo(this.model, 'change:jid', this.requestUpdate); - this.listenTo(this.model, 'change:minimized', this.requestUpdate); - this.listenTo(this.model, 'change:name', this.requestUpdate); - this.listenTo(this.model, 'change:num_unread', this.requestUpdate); - this.listenTo(this.model, 'remove', this.requestUpdate); - this.listenTo(shared_converse, 'connected', this.requestUpdate); - this.listenTo(shared_converse, 'reconnected', this.requestUpdate); - this.listenTo(shared_converse, 'disconnected', this.requestUpdate); - } - - render() { - const chats = this.model.where({ - 'minimized': true - }); - const num_unread = chats.reduce((acc, chat) => acc + chat.get('num_unread'), 0); - const num_minimized = chats.reduce((acc, chat) => acc + (chat.get('minimized') ? 1 : 0), 0); - const collapsed = this.minchats.get('collapsed'); - const data = { - chats, - num_unread, - num_minimized, - collapsed - }; - - data.toggle = ev => this.toggle(ev); - - return chats_panel(data); - } - - async initToggle() { - const id = `converse.minchatstoggle-${shared_converse.bare_jid}`; - this.minchats = new minimize_toggle({ - id - }); - initStorage(this.minchats, id, 'session'); - await new Promise(resolve => this.minchats.fetch({ - 'success': resolve, - 'error': resolve - })); - } - - toggle(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - this.minchats.save({ - 'collapsed': !this.minchats.get('collapsed') - }); - } - -} -api.elements.define('converse-minimized-chats', MinimizedChats); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/minimize/styles/minimize.scss -var styles_minimize = __webpack_require__(7926); -;// CONCATENATED MODULE: ./src/plugins/minimize/styles/minimize.scss - - - - - - - - - - - -var minimize_options = {}; - -minimize_options.styleTagTransform = (styleTagTransform_default()); -minimize_options.setAttributes = (setAttributesWithoutAttributes_default()); - - minimize_options.insert = insertBySelector_default().bind(null, "head"); - -minimize_options.domAPI = (styleDomAPI_default()); -minimize_options.insertStyleElement = (insertStyleElement_default()); - -var minimize_update = injectStylesIntoStyleTag_default()(styles_minimize/* default */.Z, minimize_options); - - - - - /* harmony default export */ const minimize_styles_minimize = (styles_minimize/* default */.Z && styles_minimize/* default.locals */.Z.locals ? styles_minimize/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/minimize/index.js -/** - * @module converse-minimize - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - -const { - dayjs: minimize_dayjs -} = core_converse.env; -core_converse.plugins.add('converse-minimize', { - /* Optional dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. They are called "optional" because they might not be - * available, in which case any overrides applicable to them will be - * ignored. - * - * It's possible however to make optional dependencies non-optional. - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. - * - * NB: These plugins need to have already been loaded via require.js. - */ - dependencies: ["converse-chatview", "converse-controlbox", "converse-muc-views", "converse-headlines-view", "converse-dragresize"], - - enabled(_converse) { - return _converse.api.settings.get("view_mode") === 'overlayed'; - }, - - overrides: { - // Overrides mentioned here will be picked up by converse.js's - // plugin architecture they will replace existing methods on the - // relevant objects or classes. - // - // New functions which don't exist yet can also be added. - ChatBox: { - initialize() { - this.__super__.initialize.apply(this, arguments); - - this.on('change:hidden', m => !m.get('hidden') && maximize(this), this); - - if (this.get('id') === 'controlbox') { - return; - } - - this.save({ - 'minimized': this.get('minimized') || false, - 'time_minimized': this.get('time_minimized') || minimize_dayjs() - }); - }, - - maybeShow(force) { - if (!force && this.get('minimized')) { - // Must return the chatbox - return this; - } - - return this.__super__.maybeShow.apply(this, arguments); - }, - - isHidden() { - return this.__super__.isHidden.call(this) || this.get('minimized'); - } - - }, - ChatBoxView: { - isNewMessageHidden() { - return this.model.get('minimized') || this.__super__.isNewMessageHidden.apply(this, arguments); - }, - - setChatBoxHeight(height) { - if (!this.model.get('minimized')) { - return this.__super__.setChatBoxHeight.call(this, height); - } - }, - - setChatBoxWidth(width) { - if (!this.model.get('minimized')) { - return this.__super__.setChatBoxWidth.call(this, width); - } - } - - } - }, - - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by Converse.js's plugin machinery. - */ - api.settings.extend({ - 'no_trimming': false - }); - api.promises.add('minimizedChatsInitialized'); - shared_converse.MinimizedChatsToggle = minimize_toggle; - shared_converse.MinimizedChats = MinimizedChats; - shared_converse.minimize = {}; - shared_converse.minimize.trimChats = trimChats; - shared_converse.minimize.minimize = minimize; - shared_converse.minimize.maximize = maximize; - - function onChatInitialized(model) { - model.on('change:minimized', () => onMinimizedChanged(model)); - } - /************************ BEGIN Event Handlers ************************/ - - - api.listen.on('chatBoxViewInitialized', view => shared_converse.minimize.trimChats(view)); - api.listen.on('chatRoomViewInitialized', view => shared_converse.minimize.trimChats(view)); - api.listen.on('chatBoxMaximized', view => shared_converse.minimize.trimChats(view)); - api.listen.on('controlBoxOpened', view => shared_converse.minimize.trimChats(view)); - api.listen.on('chatBoxInitialized', onChatInitialized); - api.listen.on('chatRoomInitialized', onChatInitialized); - api.listen.on('getHeadingButtons', (view, buttons) => { - if (view.model.get('type') === shared_converse.CHATROOMS_TYPE) { - return addMinimizeButtonToMUC(view, buttons); - } else { - return addMinimizeButtonToChat(view, buttons); - } - }); - const debouncedTrimChats = lodash_es_debounce(() => shared_converse.minimize.trimChats(), 250); - api.listen.on('registeredGlobalEventHandlers', () => window.addEventListener("resize", debouncedTrimChats)); - api.listen.on('unregisteredGlobalEventHandlers', () => window.removeEventListener("resize", debouncedTrimChats)); - } - -}); -;// CONCATENATED MODULE: ./src/shared/autocomplete/utils.js - -const autocomplete_utils_u = core_converse.env.utils; -const utils_helpers = { - getElement(expr, el) { - return typeof expr === 'string' ? (el || document).querySelector(expr) : expr || null; - }, - - bind(element, o) { - if (element) { - for (var event in o) { - if (!Object.prototype.hasOwnProperty.call(o, event)) { - continue; - } - - const callback = o[event]; - event.split(/\s+/).forEach(event => element.addEventListener(event, callback)); - } - } - }, - - unbind(element, o) { - if (element) { - for (var event in o) { - if (!Object.prototype.hasOwnProperty.call(o, event)) { - continue; - } - - const callback = o[event]; - event.split(/\s+/).forEach(event => element.removeEventListener(event, callback)); - } - } - }, - - regExpEscape(s) { - return s.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); - }, - - isMention(word, ac_triggers) { - return ac_triggers.includes(word[0]) || autocomplete_utils_u.isMentionBoundary(word[0]) && ac_triggers.includes(word[1]); - } - -}; -const FILTER_CONTAINS = function (text, input) { - return RegExp(utils_helpers.regExpEscape(input.trim()), 'i').test(text); -}; -const FILTER_STARTSWITH = function (text, input) { - return RegExp('^' + utils_helpers.regExpEscape(input.trim()), 'i').test(text); -}; - -const SORT_BY_LENGTH = function (a, b) { - if (a.length !== b.length) { - return a.length - b.length; - } - - return a < b ? -1 : 1; -}; - -const SORT_BY_QUERY_POSITION = function (a, b) { - const query = a.query.toLowerCase(); - const x = a.label.toLowerCase().indexOf(query); - const y = b.label.toLowerCase().indexOf(query); - - if (x === y) { - return SORT_BY_LENGTH(a, b); - } - - return (x === -1 ? Infinity : x) < (y === -1 ? Infinity : y) ? -1 : 1; -}; -const ITEM = (text, input) => { - input = input.trim(); - const element = document.createElement('li'); - element.setAttribute('aria-selected', 'false'); - const regex = new RegExp('(' + input + ')', 'ig'); - const parts = input ? text.split(regex) : [text]; - parts.forEach(txt => { - if (input && txt.match(regex)) { - const match = document.createElement('mark'); - match.textContent = txt; - element.appendChild(match); - } else { - element.appendChild(document.createTextNode(txt)); - } - }); - return element; -}; -;// CONCATENATED MODULE: ./src/shared/autocomplete/suggestion.js -/** - * An autocomplete suggestion - */ -class Suggestion extends String { - /** - * @param { Any } data - The auto-complete data. Ideally an object e.g. { label, value }, - * which specifies the value and human-presentable label of the suggestion. - * @param { string } query - The query string being auto-completed - */ - constructor(data, query) { - super(); - const o = Array.isArray(data) ? { - label: data[0], - value: data[1] - } : typeof data === 'object' && 'label' in data && 'value' in data ? data : { - label: data, - value: data - }; - this.label = o.label || o.value; - this.value = o.value; - this.query = query; - } - - get lenth() { - return this.label.length; - } - - toString() { - return '' + this.label; - } - - valueOf() { - return this.toString(); - } - -} - -/* harmony default export */ const suggestion = (Suggestion); -;// CONCATENATED MODULE: ./src/shared/autocomplete/autocomplete.js -/** - * @copyright Lea Verou and the Converse.js contributors - * @description - * Started as a fork of Lea Verou's "Awesomplete" - * https://leaverou.github.io/awesomplete/ - * @license Mozilla Public License (MPLv2) - */ - - - - -const autocomplete_u = core_converse.env.utils; -class AutoComplete { - constructor(el, config = {}) { - this.suggestions = []; - this.is_opened = false; - - if (autocomplete_u.hasClass('suggestion-box', el)) { - this.container = el; - } else { - this.container = el.querySelector('.suggestion-box'); - } - - this.input = this.container.querySelector('.suggestion-box__input'); - this.input.setAttribute("aria-autocomplete", "list"); - this.ul = this.container.querySelector('.suggestion-box__results'); - this.status = this.container.querySelector('.suggestion-box__additions'); - Object.assign(this, { - 'match_current_word': false, - // Match only the current word, otherwise all input is matched - 'ac_triggers': [], - // Array of keys (`ev.key`) values that will trigger auto-complete - 'include_triggers': [], - // Array of trigger keys which should be included in the returned value - 'min_chars': 2, - 'max_items': 10, - 'auto_evaluate': true, - // Should evaluation happen automatically without any particular key as trigger? - 'auto_first': false, - // Should the first element be automatically selected? - 'data': a => a, - 'filter': FILTER_CONTAINS, - 'sort': config.sort === false ? false : SORT_BY_QUERY_POSITION, - 'item': ITEM - }, config); - this.index = -1; - this.bindEvents(); - - if (this.input.hasAttribute("list")) { - this.list = "#" + this.input.getAttribute("list"); - this.input.removeAttribute("list"); - } else { - this.list = this.input.getAttribute("data-list") || config.list || []; - } - } - - bindEvents() { - // Bind events - const input = { - "blur": () => this.close({ - 'reason': 'blur' - }) - }; - - if (this.auto_evaluate) { - input["input"] = () => this.evaluate(); - } - - this._events = { - 'input': input, - 'form': { - "submit": () => this.close({ - 'reason': 'submit' - }) - }, - 'ul': { - "mousedown": ev => this.onMouseDown(ev), - "mouseover": ev => this.onMouseOver(ev) - } - }; - utils_helpers.bind(this.input, this._events.input); - utils_helpers.bind(this.input.form, this._events.form); - utils_helpers.bind(this.ul, this._events.ul); - } - - set list(list) { - if (Array.isArray(list) || typeof list === "function") { - this._list = list; - } else if (typeof list === "string" && list.includes(",")) { - this._list = list.split(/\s*,\s*/); - } else { - var _helpers$getElement; - - // Element or CSS selector - const children = ((_helpers$getElement = utils_helpers.getElement(list)) === null || _helpers$getElement === void 0 ? void 0 : _helpers$getElement.children) || []; - this._list = Array.from(children).filter(el => !el.disabled).map(el => { - const text = el.textContent.trim(); - const value = el.value || text; - const label = el.label || text; - return value !== "" ? { - label, - value - } : null; - }).filter(i => i); - } - - if (document.activeElement === this.input) { - this.evaluate(); - } - } - - get list() { - return this._list; - } - - get selected() { - return this.index > -1; - } - - get opened() { - return this.is_opened; - } - - close(o) { - if (!this.opened) { - return; - } - - this.ul.setAttribute("hidden", ""); - this.is_opened = false; - this.index = -1; - this.trigger("suggestion-box-close", o || {}); - } - - insertValue(suggestion) { - if (this.match_current_word) { - autocomplete_u.replaceCurrentWord(this.input, suggestion.value); - } else { - this.input.value = suggestion.value; - } - } - - open() { - this.ul.removeAttribute("hidden"); - this.is_opened = true; - - if (this.auto_first && this.index === -1) { - this.goto(0); - } - - this.trigger("suggestion-box-open"); - } - - destroy() { - //remove events from the input and its form - utils_helpers.unbind(this.input, this._events.input); - utils_helpers.unbind(this.input.form, this._events.form); - this.input.removeAttribute("aria-autocomplete"); - } - - next() { - const count = this.ul.children.length; - this.goto(this.index < count - 1 ? this.index + 1 : count ? 0 : -1); - } - - previous() { - const count = this.ul.children.length, - pos = this.index - 1; - this.goto(this.selected && pos !== -1 ? pos : count - 1); - } - - goto(i) { - // Should not be used directly, highlights specific item without any checks! - const list = this.ul.children; - - if (this.selected) { - list[this.index].setAttribute("aria-selected", "false"); - } - - this.index = i; - - if (i > -1 && list.length > 0) { - list[i].setAttribute("aria-selected", "true"); - list[i].focus(); - this.status.textContent = list[i].textContent; // scroll to highlighted element in case parent's height is fixed - - this.ul.scrollTop = list[i].offsetTop - this.ul.clientHeight + list[i].clientHeight; - this.trigger("suggestion-box-highlight", { - 'text': this.suggestions[this.index] - }); - } - } - - select(selected) { - if (selected) { - this.index = autocomplete_u.siblingIndex(selected); - } else { - selected = this.ul.children[this.index]; - } - - if (selected) { - const suggestion = this.suggestions[this.index]; - this.insertValue(suggestion); - this.close({ - 'reason': 'select' - }); - this.auto_completing = false; - this.trigger("suggestion-box-selectcomplete", { - 'text': suggestion - }); - } - } - - onMouseOver(ev) { - const li = autocomplete_u.ancestor(ev.target, 'li'); - - if (li) { - this.goto(Array.prototype.slice.call(this.ul.children).indexOf(li)); - } - } - - onMouseDown(ev) { - if (ev.button !== 0) { - return; // Only select on left click - } - - const li = autocomplete_u.ancestor(ev.target, 'li'); - - if (li) { - ev.preventDefault(); - this.select(li, ev.target); - } - } - - onKeyDown(ev) { - if (this.opened) { - if ([core_converse.keycodes.ENTER, core_converse.keycodes.TAB].includes(ev.keyCode) && this.selected) { - ev.preventDefault(); - ev.stopPropagation(); - this.select(); - return true; - } else if (ev.keyCode === core_converse.keycodes.ESCAPE) { - this.close({ - 'reason': 'esc' - }); - return true; - } else if ([core_converse.keycodes.UP_ARROW, core_converse.keycodes.DOWN_ARROW].includes(ev.keyCode)) { - ev.preventDefault(); - ev.stopPropagation(); - this[ev.keyCode === core_converse.keycodes.UP_ARROW ? "previous" : "next"](); - return true; - } - } - - if ([core_converse.keycodes.SHIFT, core_converse.keycodes.META, core_converse.keycodes.META_RIGHT, core_converse.keycodes.ESCAPE, core_converse.keycodes.ALT].includes(ev.keyCode)) { - return; - } - - if (this.ac_triggers.includes(ev.key)) { - if (ev.key === "Tab") { - ev.preventDefault(); - } - - this.auto_completing = true; - } else if (ev.key === "Backspace") { - const word = autocomplete_u.getCurrentWord(ev.target, ev.target.selectionEnd - 1); - - if (utils_helpers.isMention(word, this.ac_triggers)) { - this.auto_completing = true; - } - } - } - - async evaluate(ev) { - const selecting = this.selected && ev && (ev.keyCode === core_converse.keycodes.UP_ARROW || ev.keyCode === core_converse.keycodes.DOWN_ARROW); - - if (!this.auto_evaluate && !this.auto_completing || selecting) { - return; - } - - const list = typeof this._list === "function" ? await this._list() : this._list; - - if (list.length === 0) { - return; - } - - let value = this.match_current_word ? autocomplete_u.getCurrentWord(this.input) : this.input.value; - const contains_trigger = utils_helpers.isMention(value, this.ac_triggers); - - if (contains_trigger) { - this.auto_completing = true; - - if (!this.include_triggers.includes(ev.key)) { - value = autocomplete_u.isMentionBoundary(value[0]) ? value.slice('2') : value.slice('1'); - } - } - - if ((contains_trigger || value.length) && value.length >= this.min_chars) { - this.index = -1; // Populate list with options that match - - this.ul.innerHTML = ""; - this.suggestions = list.map(item => new suggestion(this.data(item, value), value)).filter(item => this.filter(item, value)); - - if (this.sort !== false) { - this.suggestions = this.suggestions.sort(this.sort); - } - - this.suggestions = this.suggestions.slice(0, this.max_items); - this.suggestions.forEach(text => this.ul.appendChild(this.item(text, value))); - - if (this.ul.children.length === 0) { - this.close({ - 'reason': 'nomatches' - }); - } else { - this.open(); - } - } else { - this.close({ - 'reason': 'nomatches' - }); - - if (!contains_trigger) { - this.auto_completing = false; - } - } - } - -} // Make it an event emitter - -Object.assign(AutoComplete.prototype, Events); -/* harmony default export */ const autocomplete = (AutoComplete); -;// CONCATENATED MODULE: ./src/shared/autocomplete/component.js - - - - - -class AutoCompleteComponent extends CustomElement { - static get properties() { - return { - 'getAutoCompleteList': { - type: Function - }, - 'auto_evaluate': { - type: Boolean - }, - 'auto_first': { - type: Boolean - }, - // Should the first element be automatically selected? - 'filter': { - type: String - }, - 'include_triggers': { - type: String - }, - 'min_chars': { - type: Number - }, - 'name': { - type: String - }, - 'placeholder': { - type: String - }, - 'triggers': { - type: String - } - }; - } - - constructor() { - super(); - this.auto_evaluate = true; // Should evaluation happen automatically without any particular key as trigger? - - this.auto_first = false; // Should the first element be automatically selected? - - this.filter = 'contains'; - this.include_triggers = ''; // Space separated chars which should be included in the returned value - - this.match_current_word = false; // Match only the current word, otherwise all input is matched - - this.max_items = 10; - this.min_chars = 1; - this.triggers = ''; // String of space separated chars - } - - render() { - return T` -
    - - - -
    - `; - } - - firstUpdated() { - this.auto_complete = new autocomplete(this.firstElementChild, { - 'ac_triggers': this.triggers.split(' '), - 'auto_evaluate': this.auto_evaluate, - 'auto_first': this.auto_first, - 'filter': this.filter == 'contains' ? FILTER_CONTAINS : FILTER_STARTSWITH, - 'include_triggers': [], - 'list': () => this.getAutoCompleteList(), - 'match_current_word': true, - 'max_items': this.max_items, - 'min_chars': this.min_chars - }); - this.auto_complete.on('suggestion-box-selectcomplete', () => this.auto_completing = false); - } - - onKeyDown(ev) { - this.auto_complete.onKeyDown(ev); - } - - onKeyUp(ev) { - this.auto_complete.evaluate(ev); - } - -} -api.elements.define('converse-autocomplete', AutoCompleteComponent); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/shared/autocomplete/styles/_autocomplete.scss -var _autocomplete = __webpack_require__(8481); -;// CONCATENATED MODULE: ./src/shared/autocomplete/styles/_autocomplete.scss - - - - - - - - - - - -var _autocomplete_options = {}; - -_autocomplete_options.styleTagTransform = (styleTagTransform_default()); -_autocomplete_options.setAttributes = (setAttributesWithoutAttributes_default()); - - _autocomplete_options.insert = insertBySelector_default().bind(null, "head"); - -_autocomplete_options.domAPI = (styleDomAPI_default()); -_autocomplete_options.insertStyleElement = (insertStyleElement_default()); - -var _autocomplete_update = injectStylesIntoStyleTag_default()(_autocomplete/* default */.Z, _autocomplete_options); - - - - - /* harmony default export */ const styles_autocomplete = (_autocomplete/* default */.Z && _autocomplete/* default.locals */.Z.locals ? _autocomplete/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/shared/autocomplete/index.js - - - - - -shared_converse.FILTER_CONTAINS = FILTER_CONTAINS; -shared_converse.FILTER_STARTSWITH = FILTER_STARTSWITH; -shared_converse.AutoComplete = autocomplete; -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/ad-hoc-command-form.js - - -/* harmony default export */ const ad_hoc_command_form = ((o, command) => { - const i18n_hide = __('Hide'); - - const i18n_run = __('Execute'); - - return T` -
    - ${command.alert ? T`` : ''} -
    - - - -

    ${command.instructions}

    - ${command.fields} -
    -
    - - -
    -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/ad-hoc-command.js - - -/* harmony default export */ const ad_hoc_command = ((o, command) => T` -
  • - - ${command.node === o.showform ? ad_hoc_command_form(o, command) : ''} -
  • -`); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/moderator-tools.js - - - - -function getRoleHelpText(role) { - if (role === 'moderator') { - return __("Moderators are privileged users who can change the roles of other users (except those with admin or owner affiliations."); - } else if (role === 'participant') { - return __("The default role, implies that you can read and write messages."); - } else if (role == 'visitor') { - return __("Visitors aren't allowed to write messages in a moderated multi-user chat."); - } -} - -function getAffiliationHelpText(aff) { - if (aff === 'owner') { - return __("Owner is the highest affiliation. Owners can modify roles and affiliations of all other users."); - } else if (aff === 'admin') { - return __("Admin is the 2nd highest affiliation. Admins can modify roles and affiliations of all other users except owners."); - } else if (aff === 'outcast') { - return __("To ban a user, you give them the affiliation of \"outcast\"."); - } -} - -const role_option = o => T` - -`; - -const affiliation_option = o => T` - -`; - -const tpl_set_role_form = o => { - const i18n_change_role = __('Change role'); - - const i18n_new_role = __('New Role'); - - const i18n_reason = __('Reason'); - - return T` - - `; -}; - -const role_form_toggle = o => T` - - - `; - -const role_list_item = o => T` -
  • -
      -
    • -
      JID: ${o.item.jid}
      -
    • -
    • -
      Nickname: ${o.item.nick}
      -
    • -
    • -
      Role: ${o.item.role} ${o.assignable_roles.length ? role_form_toggle(o) : ''}
      - ${o.assignable_roles.length ? tpl_set_role_form(o) : ''} -
    • -
    -
  • -`; - -const tpl_set_affiliation_form = o => { - const i18n_change_affiliation = __('Change affiliation'); - - const i18n_new_affiliation = __('New affiliation'); - - const i18n_reason = __('Reason'); - - return T` - - `; -}; - -const affiliation_form_toggle = o => T` - - - `; - -const affiliation_list_item = o => T` -
  • -
      -
    • -
      JID: ${o.item.jid}
      -
    • -
    • -
      Nickname: ${o.item.nick}
      -
    • -
    • -
      Affiliation: ${o.item.affiliation} ${o.assignable_affiliations.length ? affiliation_form_toggle(o) : ''}
      - ${o.assignable_affiliations.length ? tpl_set_affiliation_form(o) : ''} -
    • -
    -
  • -`; - -const tpl_navigation = () => T` - -`; - -/* harmony default export */ const moderator_tools = (o => { - const i18n_affiliation = __('Affiliation'); - - const i18n_no_users_with_aff = __('No users with that affiliation found.'); - - const i18n_no_users_with_role = __('No users with that role found.'); - - const i18n_filter = __('Type here to filter the search results'); - - const i18n_role = __('Role'); - - const i18n_show_users = __('Show users'); - - const i18n_helptext_role = __("Roles are assigned to users to grant or deny them certain abilities in a multi-user chat. " + "They're assigned either explicitly or implicitly as part of an affiliation. " + "A role that's not due to an affiliation, is only valid for the duration of the user's session."); - - const i18n_helptext_affiliation = __("An affiliation is a long-lived entitlement which typically implies a certain role and which " + "grants privileges and responsibilities. For example admins and owners automatically have the " + "moderator role."); - - const show_both_tabs = o.queryable_roles.length && o.queryable_affiliations.length; - return T` - ${o.alert_message ? T`` : ''} - ${show_both_tabs ? tpl_navigation() : ''} - -
    - - ${o.queryable_affiliations.length ? T` -
    -
    -

    ${i18n_helptext_affiliation}

    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    - ${Array.isArray(o.users_with_affiliation) && o.users_with_affiliation.length > 5 ? T`` : ''} -
    -
    - - ${getAffiliationHelpText(o.affiliation) ? T`

    ${getAffiliationHelpText(o.affiliation)}

    ` : ''} -
    -
    -
    -
      - ${o.loading_users_with_affiliation ? T`
    • ${spinner()}
    • ` : ''} - ${Array.isArray(o.users_with_affiliation) && o.users_with_affiliation.length === 0 ? T`
    • ${i18n_no_users_with_aff}
    • ` : ''} - - ${o.users_with_affiliation instanceof Error ? T`
    • ${o.users_with_affiliation.message}
    • ` : (o.users_with_affiliation || []).map(item => (item.nick || item.jid).match(new RegExp(o.affiliations_filter, 'i')) ? affiliation_list_item(Object.assign({ - item - }, o)) : '')} -
    -
    -
    ` : ''} - - ${o.queryable_roles.length ? T` -
    -
    -

    ${i18n_helptext_role}

    -
    - -
    -
    - -
    -
    - -
    -
    -
    -
    - ${Array.isArray(o.users_with_role) && o.users_with_role.length > 5 ? T`` : ''} -
    -
    - - ${getRoleHelpText(o.role) ? T`

    ${getRoleHelpText(o.role)}

    ` : ''} -
    -
    -
    -
      - ${o.loading_users_with_role ? T`
    • ${spinner()}
    • ` : ''} - ${o.users_with_role && o.users_with_role.length === 0 ? T`
    • ${i18n_no_users_with_role}
    • ` : ''} - ${(o.users_with_role || []).map(item => item.nick.match(o.roles_filter) ? role_list_item(Object.assign({ - item - }, o)) : '')} -
    -
    -
    ` : ''} -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/modtools.js - - - - - - - - - -const { - Strophe: modtools_Strophe, - sizzle: modtools_sizzle, - u: modtools_u -} = core_converse.env; -class ModeratorTools extends CustomElement { - static get properties() { - return { - affiliation: { - type: String - }, - affiliations_filter: { - type: String, - attribute: false - }, - alert_message: { - type: String, - attribute: false - }, - alert_type: { - type: String, - attribute: false - }, - jid: { - type: String - }, - muc: { - type: Object, - attribute: false - }, - role: { - type: String - }, - roles_filter: { - type: String, - attribute: false - }, - users_with_affiliation: { - type: Array, - attribute: false - }, - users_with_role: { - type: Array, - attribute: false - } - }; - } - - constructor() { - super(); - this.affiliation = ''; - this.affiliations_filter = ''; - this.role = ''; - this.roles_filter = ''; - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - updated(changed) { - changed.has('role') && this.onSearchRoleChange(); - changed.has('affiliation') && this.onSearchAffiliationChange(); - changed.has('jid') && changed.get('jid') && this.initialize(); - } - - async initialize() { - this.initialized = getOpenPromise(); - const muc = await api.rooms.get(this.jid); - await muc.initialized; - this.muc = muc; - this.initialized.resolve(); - } - - render() { - var _this$muc; - - if ((_this$muc = this.muc) !== null && _this$muc !== void 0 && _this$muc.occupants) { - const occupant = this.muc.occupants.findWhere({ - 'jid': shared_converse.bare_jid - }); - return moderator_tools({ - 'affiliations_filter': this.affiliations_filter, - 'alert_message': this.alert_message, - 'alert_type': this.alert_type, - 'assignAffiliation': ev => this.assignAffiliation(ev), - 'assignRole': ev => this.assignRole(ev), - 'assignable_affiliations': getAssignableAffiliations(occupant), - 'assignable_roles': getAssignableRoles(occupant), - 'filterAffiliationResults': ev => this.filterAffiliationResults(ev), - 'filterRoleResults': ev => this.filterRoleResults(ev), - 'loading_users_with_affiliation': this.loading_users_with_affiliation, - 'queryAffiliation': ev => this.queryAffiliation(ev), - 'queryRole': ev => this.queryRole(ev), - 'queryable_affiliations': AFFILIATIONS.filter(a => !api.settings.get('modtools_disable_query').includes(a)), - 'queryable_roles': ROLES.filter(a => !api.settings.get('modtools_disable_query').includes(a)), - 'roles_filter': this.roles_filter, - 'switchTab': ev => this.switchTab(ev), - 'toggleForm': ev => this.toggleForm(ev), - 'users_with_affiliation': this.users_with_affiliation, - 'users_with_role': this.users_with_role - }); - } else { - return ''; - } - } - - async onSearchAffiliationChange() { - if (!this.affiliation) { - return; - } - - await this.initialized; - this.clearAlert(); - this.loading_users_with_affiliation = true; - this.users_with_affiliation = null; - - if (this.shouldFetchAffiliationsList()) { - const result = await getAffiliationList(this.affiliation, this.jid); - - if (result instanceof Error) { - this.alert(result.message, 'danger'); - this.users_with_affiliation = []; - } else { - this.users_with_affiliation = result; - } - } else { - this.users_with_affiliation = this.muc.getOccupantsWithAffiliation(this.affiliation); - } - - this.loading_users_with_affiliation = false; - } - - async onSearchRoleChange() { - if (!this.role) { - return; - } - - await this.initialized; - this.clearAlert(); - this.users_with_role = this.muc.getOccupantsWithRole(this.role); - } - - shouldFetchAffiliationsList() { - const affiliation = this.affiliation; - - if (affiliation === 'none') { - return false; - } - - const chatroom = this.muc; - const auto_fetched_affs = chatroom.occupants.getAutoFetchedAffiliationLists(); - - if (auto_fetched_affs.includes(affiliation)) { - return false; - } else { - return true; - } - } - - toggleForm(ev) { - // eslint-disable-line class-methods-use-this - ev.stopPropagation(); - ev.preventDefault(); - const toggle = modtools_u.ancestor(ev.target, '.toggle-form'); - const form_class = toggle.getAttribute('data-form'); - const form = modtools_u.ancestor(toggle, '.list-group-item').querySelector(`.${form_class}`); - - if (modtools_u.hasClass('hidden', form)) { - modtools_u.removeClass('hidden', form); - } else { - modtools_u.addClass('hidden', form); - } - } - - filterRoleResults(ev) { - this.roles_filter = ev.target.value; - this.render(); - } - - filterAffiliationResults(ev) { - this.affiliations_filter = ev.target.value; - } - - queryRole(ev) { - ev.stopPropagation(); - ev.preventDefault(); - const data = new FormData(ev.target); - const role = data.get('role'); - this.role = null; - this.role = role; - } - - queryAffiliation(ev) { - ev.stopPropagation(); - ev.preventDefault(); - const data = new FormData(ev.target); - const affiliation = data.get('affiliation'); - this.affiliation = null; - this.affiliation = affiliation; - } - - alert(message, type) { - this.alert_message = message; - this.alert_type = type; - } - - clearAlert() { - this.alert_message = undefined; - this.alert_type = undefined; - } - - async assignAffiliation(ev) { - ev.stopPropagation(); - ev.preventDefault(); - this.clearAlert(); - const data = new FormData(ev.target); - const affiliation = data.get('affiliation'); - const attrs = { - 'jid': data.get('jid'), - 'reason': data.get('reason') - }; - const current_affiliation = this.affiliation; - const muc_jid = this.muc.get('jid'); - - try { - await setAffiliation(affiliation, muc_jid, [attrs]); - } catch (e) { - if (e === null) { - this.alert(__('Timeout error while trying to set the affiliation'), 'danger'); - } else if (modtools_sizzle(`not-allowed[xmlns="${modtools_Strophe.NS.STANZAS}"]`, e).length) { - this.alert(__("Sorry, you're not allowed to make that change"), 'danger'); - } else { - this.alert(__('Sorry, something went wrong while trying to set the affiliation'), 'danger'); - } - - headless_log.error(e); - return; - } - - await this.muc.occupants.fetchMembers(); - this.affiliation = null; - this.affiliation = current_affiliation; - this.alert(__('Affiliation changed'), 'primary'); - } - - assignRole(ev) { - ev.stopPropagation(); - ev.preventDefault(); - this.clearAlert(); - const data = new FormData(ev.target); - const occupant = this.muc.getOccupant(data.get('jid') || data.get('nick')); - const role = data.get('role'); - const reason = data.get('reason'); - const current_role = this.role; - this.muc.setRole(occupant, role, reason, () => { - this.alert(__('Role changed'), 'primary'); - this.role = null; - this.role = current_role; - }, e => { - if (modtools_sizzle(`not-allowed[xmlns="${modtools_Strophe.NS.STANZAS}"]`, e).length) { - this.alert(__("You're not allowed to make that change"), 'danger'); - } else { - this.alert(__('Sorry, something went wrong while trying to set the role'), 'danger'); - - if (modtools_u.isErrorObject(e)) { - headless_log.error(e); - } - } - }); - } - -} -api.elements.define('converse-modtools', ModeratorTools); -;// CONCATENATED MODULE: ./src/plugins/muc-views/modals/templates/moderator-tools.js - - - -/* harmony default export */ const templates_moderator_tools = (o => { - const i18n_moderator_tools = __('Moderator Tools'); - - return T` - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/modals/moderator-tools.js - - - -const ModeratorToolsModal = base.extend({ - id: "converse-modtools-modal", - persistent: true, - - initialize(attrs) { - this.jid = attrs.jid; - this.affiliation = attrs.affiliation; - base.prototype.initialize.apply(this, arguments); - }, - - toHTML() { - return templates_moderator_tools(this); - } - -}); -/* harmony default export */ const modals_moderator_tools = (ModeratorToolsModal); -;// CONCATENATED MODULE: ./src/plugins/muc-views/utils.js - - - - - - - - -const { - Strophe: muc_views_utils_Strophe, - $pres: muc_views_utils_$pres, - $iq: muc_views_utils_$iq, - sizzle: muc_views_utils_sizzle, - u: muc_views_utils_u -} = core_converse.env; -const COMMAND_TO_AFFILIATION = { - 'admin': 'admin', - 'ban': 'outcast', - 'member': 'member', - 'owner': 'owner', - 'revoke': 'none' -}; -const COMMAND_TO_ROLE = { - 'deop': 'participant', - 'kick': 'none', - 'mute': 'visitor', - 'op': 'moderator', - 'voice': 'participant' -}; -function utils_clearHistory(jid) { - if (shared_converse.router.history.getFragment() === `converse/room?jid=${jid}`) { - shared_converse.router.navigate(''); - } -} -async function destroyMUC(model) { - const messages = [__('Are you sure you want to destroy this groupchat?')]; - let fields = [{ - 'name': 'challenge', - 'label': __('Please enter the XMPP address of this groupchat to confirm'), - 'challenge': model.get('jid'), - 'placeholder': __('name@example.org'), - 'required': true - }, { - 'name': 'reason', - 'label': __('Optional reason for destroying this groupchat'), - 'placeholder': __('Reason') - }, { - 'name': 'newjid', - 'label': __('Optional XMPP address for a new groupchat that replaces this one'), - 'placeholder': __('replacement@example.org') - }]; - - try { - var _fields$filter$pop, _fields$filter$pop2; - - fields = await api.confirm(__('Confirm'), messages, fields); - const reason = (_fields$filter$pop = fields.filter(f => f.name === 'reason').pop()) === null || _fields$filter$pop === void 0 ? void 0 : _fields$filter$pop.value; - const newjid = (_fields$filter$pop2 = fields.filter(f => f.name === 'newjid').pop()) === null || _fields$filter$pop2 === void 0 ? void 0 : _fields$filter$pop2.value; - return model.sendDestroyIQ(reason, newjid).then(() => model.close()); - } catch (e) { - headless_log.error(e); - } -} - -function setMUCDomain(domain, controlboxview) { - controlboxview.querySelector('converse-rooms-list').model.save('muc_domain', muc_views_utils_Strophe.getDomainFromJid(domain)); -} - -function setMUCDomainFromDisco(controlboxview) { - /* Check whether service discovery for the user's domain - * returned MUC information and use that to automatically - * set the MUC domain in the "Add groupchat" modal. - */ - function featureAdded(feature) { - if (!feature) { - return; - } - - if (feature.get('var') === muc_views_utils_Strophe.NS.MUC) { - feature.entity.getIdentity('conference', 'text').then(identity => { - if (identity) { - setMUCDomain(feature.get('from'), controlboxview); - } - }); - } - } - - api.waitUntil('discoInitialized').then(() => { - api.listen.on('serviceDiscovered', featureAdded); // Features could have been added before the controlbox was - // initialized. We're only interested in MUC - - shared_converse.disco_entities.each(entity => featureAdded(entity.features.findWhere({ - 'var': muc_views_utils_Strophe.NS.MUC - }))); - }).catch(e => headless_log.error(e)); -} - -function fetchAndSetMUCDomain(controlboxview) { - if (controlboxview.model.get('connected')) { - if (!controlboxview.querySelector('converse-rooms-list').model.get('muc_domain')) { - if (api.settings.get('muc_domain') === undefined) { - setMUCDomainFromDisco(controlboxview); - } else { - setMUCDomain(api.settings.get('muc_domain'), controlboxview); - } - } - } -} -function getNicknameRequiredTemplate(model) { - const jid = model.get('jid'); - - if (api.settings.get('muc_show_logs_before_join')) { - return T``; - } else { - return T``; - } -} -function getChatRoomBodyTemplate(o) { - const view = o.model.session.get('view'); - const jid = o.model.get('jid'); - const RS = core_converse.ROOMSTATUS; - const conn_status = o.model.session.get('connection_status'); - - if (view === core_converse.MUC.VIEWS.CONFIG) { - return T``; - } else if (view === core_converse.MUC.VIEWS.BOOKMARK) { - return T``; - } else { - return T` - ${conn_status == RS.PASSWORD_REQUIRED ? T`` : ''} - ${conn_status == RS.ENTERED ? T`` : ''} - ${conn_status == RS.CONNECTING ? spinner() : ''} - ${conn_status == RS.NICKNAME_REQUIRED ? getNicknameRequiredTemplate(o.model) : ''} - ${conn_status == RS.DISCONNECTED ? T`` : ''} - ${conn_status == RS.BANNED ? T`` : ''} - ${conn_status == RS.DESTROYED ? T`` : ''} - `; - } -} -function getAutoCompleteListItem(text, input) { - input = input.trim(); - const element = document.createElement('li'); - element.setAttribute('aria-selected', 'false'); - - if (api.settings.get('muc_mention_autocomplete_show_avatar')) { - const img = document.createElement('img'); - let dataUri = 'data:' + shared_converse.DEFAULT_IMAGE_TYPE + ';base64,' + shared_converse.DEFAULT_IMAGE; - - if (shared_converse.vcards) { - const vcard = shared_converse.vcards.findWhere({ - 'nickname': text - }); - - if (vcard) dataUri = 'data:' + vcard.get('image_type') + ';base64,' + vcard.get('image'); - } - - img.setAttribute('src', dataUri); - img.setAttribute('width', '22'); - img.setAttribute('class', 'avatar avatar-autocomplete'); - element.appendChild(img); - } - - const regex = new RegExp('(' + input + ')', 'ig'); - const parts = input ? text.split(regex) : [text]; - parts.forEach(txt => { - if (input && txt.match(regex)) { - const match = document.createElement('mark'); - match.textContent = txt; - element.appendChild(match); - } else { - element.appendChild(document.createTextNode(txt)); - } - }); - return element; -} -async function getAutoCompleteList() { - const models = [...(await api.rooms.get()), ...(await api.contacts.get())]; - const jids = [...new Set(models.map(o => muc_views_utils_Strophe.getDomainFromJid(o.get('jid'))))]; - return jids; -} -async function fetchCommandForm(command) { - const node = command.node; - const jid = command.jid; - const stanza = muc_views_utils_$iq({ - 'type': 'set', - 'to': jid - }).c('command', { - 'xmlns': muc_views_utils_Strophe.NS.ADHOC, - 'node': node, - 'action': 'execute' - }); - - try { - var _sizzle$pop; - - const iq = await api.sendIQ(stanza); - const cmd_el = muc_views_utils_sizzle(`command[xmlns="${muc_views_utils_Strophe.NS.ADHOC}"]`, iq).pop(); - command.sessionid = cmd_el.getAttribute('sessionid'); - command.instructions = (_sizzle$pop = muc_views_utils_sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()) === null || _sizzle$pop === void 0 ? void 0 : _sizzle$pop.textContent; - command.fields = muc_views_utils_sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el).map(f => muc_views_utils_u.xForm2TemplateResult(f, cmd_el)); - } catch (e) { - if (e === null) { - headless_log.error(`Error: timeout while trying to execute command for ${jid}`); - } else { - headless_log.error(`Error while trying to execute command for ${jid}`); - headless_log.error(e); - } - - command.fields = []; - } -} - -function setRole(muc, command, args, required_affiliations = [], required_roles = []) { - const role = COMMAND_TO_ROLE[command]; - - if (!role) { - throw Error(`ChatRoomView#setRole called with invalid command: ${command}`); - } - - if (!muc.verifyAffiliations(required_affiliations) || !muc.verifyRoles(required_roles)) { - return false; - } - - if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) { - return false; - } - - const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args); - - if (!nick_or_jid) { - return false; - } - - const reason = args.split(nick_or_jid, 2)[1].trim(); // We're guaranteed to have an occupant due to getNickOrJIDFromCommandArgs - - const occupant = muc.getOccupant(nick_or_jid); - muc.setRole(occupant, role, reason, undefined, e => muc.onCommandError(e)); - return true; -} - -function verifyAndSetAffiliation(muc, command, args, required_affiliations) { - const affiliation = COMMAND_TO_AFFILIATION[command]; - - if (!affiliation) { - throw Error(`verifyAffiliations called with invalid command: ${command}`); - } - - if (!muc.verifyAffiliations(required_affiliations)) { - return false; - } - - if (!muc.validateRoleOrAffiliationChangeArgs(command, args)) { - return false; - } - - const nick_or_jid = muc.getNickOrJIDFromCommandArgs(args); - - if (!nick_or_jid) { - return false; - } - - let jid; - const reason = args.split(nick_or_jid, 2)[1].trim(); - const occupant = muc.getOccupant(nick_or_jid); - - if (occupant) { - jid = occupant.get('jid'); - } else { - if (muc_views_utils_u.isValidJID(nick_or_jid)) { - jid = nick_or_jid; - } else { - const message = __("Couldn't find a participant with that nickname. " + 'They might have left the groupchat.'); - - muc.createMessage({ - message, - 'type': 'error' - }); - return; - } - } - - const attrs = { - jid, - reason - }; - - if (occupant && api.settings.get('auto_register_muc_nickname')) { - attrs['nick'] = occupant.get('nick'); - } - - setAffiliation(affiliation, muc.get('jid'), [attrs]).then(() => muc.occupants.fetchMembers()).catch(err => muc.onCommandError(err)); -} - -function showModeratorToolsModal(muc, affiliation) { - if (!muc.verifyRoles(['moderator'])) { - return; - } - - let modal = api.modal.get(modals_moderator_tools.id); - - if (modal) { - modal.affiliation = affiliation; - modal.render(); - } else { - modal = api.modal.create(modals_moderator_tools, { - affiliation, - 'jid': muc.get('jid') - }); - } - - modal.show(); -} -function parseMessageForMUCCommands(muc, text) { - if (api.settings.get('muc_disable_slash_commands') && !Array.isArray(api.settings.get('muc_disable_slash_commands'))) { - return parseMessageForCommands(muc, text); - } - - text = text.replace(/^\s*/, ''); - const command = (text.match(/^\/([a-zA-Z]*) ?/) || ['']).pop().toLowerCase(); - - if (!command) { - return false; - } - - const args = text.slice(('/' + command).length + 1).trim(); - - if (!muc.getAllowedCommands().includes(command)) { - return false; - } - - switch (command) { - case 'admin': - { - verifyAndSetAffiliation(muc, command, args, ['owner']); - break; - } - - case 'ban': - { - verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']); - break; - } - - case 'modtools': - { - showModeratorToolsModal(muc, args); - break; - } - - case 'deop': - { - // FIXME: /deop only applies to setting a moderators - // role to "participant" (which only admin/owner can - // do). Moderators can however set non-moderator's role - // to participant (e.g. visitor => participant). - // Currently we don't distinguish between these two - // cases. - setRole(muc, command, args, ['admin', 'owner']); - break; - } - - case 'destroy': - { - if (!muc.verifyAffiliations(['owner'])) { - break; - } - - destroyMUC(muc).catch(e => muc.onCommandError(e)); - break; - } - - case 'help': - { - muc.set({ - 'show_help_messages': false - }, { - 'silent': true - }); - muc.set({ - 'show_help_messages': true - }); - break; - } - - case 'kick': - { - setRole(muc, command, args, [], ['moderator']); - break; - } - - case 'mute': - { - setRole(muc, command, args, [], ['moderator']); - break; - } - - case 'member': - { - verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']); - break; - } - - case 'nick': - { - if (!muc.verifyRoles(['visitor', 'participant', 'moderator'])) { - break; - } else if (args.length === 0) { - // e.g. Your nickname is "coolguy69" - const message = __('Your nickname is "%1$s"', muc.get('nick')); - - muc.createMessage({ - message, - 'type': 'error' - }); - } else { - const jid = muc_views_utils_Strophe.getBareJidFromJid(muc.get('jid')); - api.send(muc_views_utils_$pres({ - from: shared_converse.connection.jid, - to: `${jid}/${args}`, - id: muc_views_utils_u.getUniqueId() - }).tree()); - } - - break; - } - - case 'owner': - verifyAndSetAffiliation(muc, command, args, ['owner']); - break; - - case 'op': - { - setRole(muc, command, args, ['admin', 'owner']); - break; - } - - case 'register': - { - if (args.length > 1) { - muc.createMessage({ - 'message': __('Error: invalid number of arguments'), - 'type': 'error' - }); - } else { - muc.registerNickname().then(err_msg => { - err_msg && muc.createMessage({ - 'message': err_msg, - 'type': 'error' - }); - }); - } - - break; - } - - case 'revoke': - { - verifyAndSetAffiliation(muc, command, args, ['admin', 'owner']); - break; - } - - case 'topic': - case 'subject': - muc.setSubject(args); - break; - - case 'voice': - { - setRole(muc, command, args, [], ['moderator']); - break; - } - - default: - return parseMessageForCommands(muc, text); - } - - return true; -} -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/ad-hoc.js - - - - -/* harmony default export */ const ad_hoc = (o => { - const i18n_choose_service = __('On which entity do you want to run commands?'); - - const i18n_choose_service_instructions = __('Certain XMPP services and entities allow privileged users to execute ad-hoc commands on them.'); - - const i18n_commands_found = __('Commands found'); - - const i18n_fetch_commands = __('List available commands'); - - const i18n_jid_placeholder = __('XMPP Address'); - - const i18n_no_commands_found = __('No commands found'); - - return T` - ${o.alert ? T`` : ''} -
    -
    - -
    -
    - -
    - ${o.view === 'list-commands' ? T` -
    -
      -
    • ${o.commands.length ? i18n_commands_found : i18n_no_commands_found}:
    • - ${o.commands.map(cmd => ad_hoc_command(o, cmd))} -
    -
    ` : ''} - -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/adhoc-commands.js - - - - - - - -const { - Strophe: adhoc_commands_Strophe, - $iq: adhoc_commands_$iq, - sizzle: adhoc_commands_sizzle, - u: adhoc_commands_u -} = core_converse.env; -class AdHocCommands extends CustomElement { - static get properties() { - return { - 'alert': { - type: String - }, - 'alert_type': { - type: String - }, - 'nonce': { - type: String - }, - // Used to force re-rendering - 'showform': { - type: String - }, - 'view': { - type: String - } - }; - } - - constructor() { - super(); - this.view = 'choose-service'; - this.showform = ''; - this.commands = []; - } - - render() { - return ad_hoc({ - 'alert': this.alert, - 'alert_type': this.alert_type, - 'commands': this.commands, - 'fetchCommands': ev => this.fetchCommands(ev), - 'hideCommandForm': ev => this.hideCommandForm(ev), - 'runCommand': ev => this.runCommand(ev), - 'showform': this.showform, - 'toggleCommandForm': ev => this.toggleCommandForm(ev), - 'view': this.view - }); - } - - async fetchCommands(ev) { - ev.preventDefault(); - delete this.alert_type; - delete this.alert; - const form_data = new FormData(ev.target); - const jid = form_data.get('jid').trim(); - let supported; - - try { - supported = await api.disco.supports(adhoc_commands_Strophe.NS.ADHOC, jid); - } catch (e) { - headless_log.error(e); - } - - if (supported) { - try { - this.commands = await api.adhoc.getCommands(jid); - this.view = 'list-commands'; - } catch (e) { - headless_log.error(e); - this.alert_type = 'danger'; - this.alert = __('Sorry, an error occurred while looking for commands on that entity.'); - this.commands = []; - headless_log.error(e); - return; - } - } else { - this.alert_type = 'danger'; - this.alert = __("The specified entity doesn't support ad-hoc commands"); - } - } - - async toggleCommandForm(ev) { - ev.preventDefault(); - const node = ev.target.getAttribute('data-command-node'); - const cmd = this.commands.filter(c => c.node === node)[0]; - this.showform !== node && (await fetchCommandForm(cmd)); - this.showform = node; - } - - hideCommandForm(ev) { - ev.preventDefault(); - this.showform = ''; - } - - async runCommand(ev) { - ev.preventDefault(); - const form_data = new FormData(ev.target); - const jid = form_data.get('command_jid').trim(); - const node = form_data.get('command_node').trim(); - const cmd = this.commands.filter(c => c.node === node)[0]; - cmd.alert = null; - this.nonce = adhoc_commands_u.getUniqueId(); - const inputs = adhoc_commands_sizzle(':input:not([type=button]):not([type=submit])', ev.target); - const config_array = inputs.filter(i => !['command_jid', 'command_node'].includes(i.getAttribute('name'))).map(adhoc_commands_u.webForm2xForm).filter(n => n); - const iq = adhoc_commands_$iq({ - to: jid, - type: "set" - }).c("command", { - 'sessionid': cmd.sessionid, - 'node': cmd.node, - 'xmlns': adhoc_commands_Strophe.NS.ADHOC - }).c("x", { - xmlns: adhoc_commands_Strophe.NS.XFORM, - type: "submit" - }); - config_array.forEach(node => iq.cnode(node).up()); - let result; - - try { - result = await api.sendIQ(iq); - } catch (e) { - cmd.alert_type = 'danger'; - cmd.alert = __('Sorry, an error occurred while trying to execute the command. See the developer console for details'); - headless_log.error('Error while trying to execute an ad-hoc command'); - headless_log.error(e); - } - - if (result) { - var _result$querySelector; - - cmd.alert = (_result$querySelector = result.querySelector('note')) === null || _result$querySelector === void 0 ? void 0 : _result$querySelector.textContent; - } else { - cmd.alert = 'Done'; - } - - cmd.alert_type = 'primary'; - this.nonce = adhoc_commands_u.getUniqueId(); - } - -} -api.elements.define('converse-adhoc-commands', AdHocCommands); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/message-form.js - - - - -/* harmony default export */ const templates_message_form = (o => { - const label_message = o.composing_spoiler ? __('Hidden message') : __('Message'); - - const label_spoiler_hint = __('Optional hint'); - - const show_send_button = api.settings.get('show_send_button'); - return T` - -
    - -
    - - - -
    -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/message-form.js - - - - -class MUCMessageForm extends MessageForm { - toHTML() { - var _this$querySelector, _this$querySelector2; - - return templates_message_form(Object.assign(this.model.toJSON(), { - 'hint_value': (_this$querySelector = this.querySelector('.spoiler-hint')) === null || _this$querySelector === void 0 ? void 0 : _this$querySelector.value, - 'message_value': (_this$querySelector2 = this.querySelector('.chat-textarea')) === null || _this$querySelector2 === void 0 ? void 0 : _this$querySelector2.value, - 'onChange': ev => this.model.set({ - 'draft': ev.target.value - }), - 'onDrop': ev => this.onDrop(ev), - 'onKeyDown': ev => this.onKeyDown(ev), - 'onKeyUp': ev => this.onKeyUp(ev), - 'onPaste': ev => this.onPaste(ev), - 'scrolled': this.model.ui.get('scrolled'), - 'viewUnreadMessages': ev => this.viewUnreadMessages(ev) - })); - } - - afterRender() { - const entered = this.model.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED; - const can_edit = entered && !(this.model.features.get('moderated') && this.model.getOwnRole() === 'visitor'); - - if (entered && can_edit) { - this.initMentionAutoComplete(); - } - } - - initMentionAutoComplete() { - this.mention_auto_complete = new shared_converse.AutoComplete(this, { - 'auto_first': true, - 'auto_evaluate': false, - 'min_chars': api.settings.get('muc_mention_autocomplete_min_chars'), - 'match_current_word': true, - 'list': () => this.getAutoCompleteList(), - 'filter': api.settings.get('muc_mention_autocomplete_filter') == 'contains' ? shared_converse.FILTER_CONTAINS : shared_converse.FILTER_STARTSWITH, - 'ac_triggers': ['Tab', '@'], - 'include_triggers': [], - 'item': getAutoCompleteListItem - }); - this.mention_auto_complete.on('suggestion-box-selectcomplete', () => this.auto_completing = false); - } - - parseMessageForCommands(text) { - return parseMessageForMUCCommands(this.model, text); - } - - getAutoCompleteList() { - return this.model.getAllKnownNicknames().map(nick => ({ - 'label': nick, - 'value': `@${nick}` - })); - } - - onKeyDown(ev) { - if (this.mention_auto_complete.onKeyDown(ev)) { - return; - } - - super.onKeyDown(ev); - } - - onKeyUp(ev) { - this.mention_auto_complete.evaluate(ev); - super.onKeyUp(ev); - } - -} -api.elements.define('converse-muc-message-form', MUCMessageForm); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-nickname-form.js - - - - -function submitNickname(ev, model) { - ev.preventDefault(); - const nick = ev.target.nick.value.trim(); - nick && model.join(nick); -} - -/* harmony default export */ const muc_nickname_form = (model => { - const i18n_nickname = __('Nickname'); - - const i18n_join = __('Enter groupchat'); - - const i18n_heading = api.settings.get('muc_show_logs_before_join') ? __('Choose a nickname to enter') : __('Please choose your nickname'); - const validation_message = model.get('nickname_validation_message'); - return T` -
    submitNickname(ev, model)}> -
    -
    - -

    ${validation_message}

    - -
    -
    - -
    -
    -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-bottom-panel.js - - - - - - - -const tpl_can_edit = o => { - const unread_msgs = __('You have unread messages'); - - const message_limit = api.settings.get('message_limit'); - const show_call_button = api.settings.get('visible_toolbar_buttons').call; - const show_emoji_button = api.settings.get('visible_toolbar_buttons').emoji; - const show_send_button = api.settings.get('show_send_button'); - const show_spoiler_button = api.settings.get('visible_toolbar_buttons').spoiler; - const show_toolbar = api.settings.get('show_toolbar'); - return T` - ${o.model.ui.get('scrolled') && o.model.get('num_unread') ? T`
    o.viewUnreadMessages(ev)}>▼ ${unread_msgs} ▼
    ` : ''} - ${show_toolbar ? T` - ` : ''} - `; -}; - -/* harmony default export */ const muc_bottom_panel = (o => { - const unread_msgs = __('You have unread messages'); - - const conn_status = o.model.session.get('connection_status'); - - const i18n_not_allowed = __("You're not allowed to send messages in this room"); - - if (conn_status === core_converse.ROOMSTATUS.ENTERED) { - return T` - ${o.model.ui.get('scrolled') && o.model.get('num_unread_general') ? T`
    o.viewUnreadMessages(ev)}>▼ ${unread_msgs} ▼
    ` : ''} - ${o.can_edit ? tpl_can_edit(o) : T`${i18n_not_allowed}`}`; - } else if (conn_status == core_converse.ROOMSTATUS.NICKNAME_REQUIRED) { - if (api.settings.get('muc_show_logs_before_join')) { - return T`${muc_nickname_form(o.model)}`; - } - } else { - return ''; - } -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/muc-views/styles/muc-bottom-panel.scss -var styles_muc_bottom_panel = __webpack_require__(2490); -;// CONCATENATED MODULE: ./src/plugins/muc-views/styles/muc-bottom-panel.scss - - - - - - - - - - - -var muc_bottom_panel_options = {}; - -muc_bottom_panel_options.styleTagTransform = (styleTagTransform_default()); -muc_bottom_panel_options.setAttributes = (setAttributesWithoutAttributes_default()); - - muc_bottom_panel_options.insert = insertBySelector_default().bind(null, "head"); - -muc_bottom_panel_options.domAPI = (styleDomAPI_default()); -muc_bottom_panel_options.insertStyleElement = (insertStyleElement_default()); - -var muc_bottom_panel_update = injectStylesIntoStyleTag_default()(styles_muc_bottom_panel/* default */.Z, muc_bottom_panel_options); - - - - - /* harmony default export */ const muc_views_styles_muc_bottom_panel = (styles_muc_bottom_panel/* default */.Z && styles_muc_bottom_panel/* default.locals */.Z.locals ? styles_muc_bottom_panel/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/muc-views/bottom-panel.js -function muc_views_bottom_panel_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - -class MUCBottomPanel extends ChatBottomPanel { - constructor(...args) { - super(...args); - - muc_views_bottom_panel_defineProperty(this, "events", { - 'click .hide-occupants': 'hideOccupants', - 'click .send-button': 'sendButtonClicked' - }); - } - - async initialize() { - await super.initialize(); - this.listenTo(this.model, 'change:hidden_occupants', this.debouncedRender); - this.listenTo(this.model, 'change:num_unread_general', this.debouncedRender); - this.listenTo(this.model.features, 'change:moderated', this.debouncedRender); - this.listenTo(this.model.occupants, 'add', this.renderIfOwnOccupant); - this.listenTo(this.model.occupants, 'change:role', this.renderIfOwnOccupant); - this.listenTo(this.model.session, 'change:connection_status', this.debouncedRender); - } - - render() { - const entered = this.model.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED; - const can_edit = entered && !(this.model.features.get('moderated') && this.model.getOwnRole() === 'visitor'); - V(muc_bottom_panel({ - can_edit, - entered, - 'model': this.model, - 'is_groupchat': true, - 'viewUnreadMessages': ev => this.viewUnreadMessages(ev) - }), this); - } - - renderIfOwnOccupant(o) { - o.get('jid') === shared_converse.bare_jid && this.debouncedRender(); - } - - sendButtonClicked(ev) { - var _this$querySelector; - - (_this$querySelector = this.querySelector('converse-message-form')) === null || _this$querySelector === void 0 ? void 0 : _this$querySelector.onFormSubmitted(ev); - } - - hideOccupants(ev) { - var _ev$preventDefault, _ev$stopPropagation; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation = ev.stopPropagation) === null || _ev$stopPropagation === void 0 ? void 0 : _ev$stopPropagation.call(ev); - this.model.save({ - 'hidden_occupants': true - }); - } - -} -api.elements.define('converse-muc-bottom-panel', MUCBottomPanel); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/occupant.js - - - -const occupant_title = o => { - const i18n_moderator_hint = __('This user is a moderator.'); - - const i18n_participant_hint = __('This user can send messages in this groupchat.'); - - const i18n_visitor_hint = __('This user can NOT send messages in this groupchat.'); - - const spaced_jid = `${o.jid} ` || ''; - - if (o.role === "moderator") { - return `${spaced_jid}${i18n_moderator_hint} ${o.hint_occupant}`; - } else if (o.role === "participant") { - return `${spaced_jid}${i18n_participant_hint} ${o.hint_occupant}`; - } else if (o.role === "visitor") { - return `${spaced_jid}${i18n_visitor_hint} ${o.hint_occupant}`; - } else if (!["visitor", "participant", "moderator"].includes(o.role)) { - return `${spaced_jid}${o.hint_occupant}`; - } -}; - -/* harmony default export */ const muc_views_templates_occupant = (o => { - const i18n_owner = __('Owner'); - - const i18n_admin = __('Admin'); - - const i18n_member = __('Member'); - - const i18n_moderator = __('Moderator'); - - const i18n_visitor = __('Visitor'); - - return T` -
  • -
    -
    -
    -
    -
    - ${o.nick || o.jid} - - ${o.affiliation === "owner" ? T`${i18n_owner}` : ''} - ${o.affiliation === "admin" ? T`${i18n_admin}` : ''} - ${o.affiliation === "member" ? T`${i18n_member}` : ''} - ${o.role === "moderator" ? T`${i18n_moderator}` : ''} - ${o.role === "visitor" ? T`${i18n_visitor}` : ''} - -
    -
    -
  • - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-sidebar.js - - - -const PRETTY_CHAT_STATUS = { - 'offline': 'Offline', - 'unavailable': 'Unavailable', - 'xa': 'Extended Away', - 'away': 'Away', - 'dnd': 'Do not disturb', - 'chat': 'Chattty', - 'online': 'Online' -}; -/* harmony default export */ const muc_sidebar = (o => { - const i18n_occupant_hint = occupant => __('Click to mention %1$s in your message.', occupant.get('nick')); - - const i18n_participants = __('Participants'); - - const occupant_tpls = o.occupants.map(occupant => { - return muc_views_templates_occupant(Object.assign({ - 'jid': '', - 'hint_show': PRETTY_CHAT_STATUS[occupant.get('show')], - 'hint_occupant': i18n_occupant_hint(occupant), - 'onOccupantClicked': o.onOccupantClicked - }, occupant.toJSON())); - }); - return T` -
    - -
    - ${i18n_participants} -
    -
    -
    -
      ${occupant_tpls}
    - `; -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/muc-views/styles/muc-occupants.scss -var muc_occupants = __webpack_require__(1107); -;// CONCATENATED MODULE: ./src/plugins/muc-views/styles/muc-occupants.scss - - - - - - - - - - - -var muc_occupants_options = {}; - -muc_occupants_options.styleTagTransform = (styleTagTransform_default()); -muc_occupants_options.setAttributes = (setAttributesWithoutAttributes_default()); - - muc_occupants_options.insert = insertBySelector_default().bind(null, "head"); - -muc_occupants_options.domAPI = (styleDomAPI_default()); -muc_occupants_options.insertStyleElement = (insertStyleElement_default()); - -var muc_occupants_update = injectStylesIntoStyleTag_default()(muc_occupants/* default */.Z, muc_occupants_options); - - - - - /* harmony default export */ const styles_muc_occupants = (muc_occupants/* default */.Z && muc_occupants/* default.locals */.Z.locals ? muc_occupants/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/muc-views/sidebar.js - - - - - -const { - u: sidebar_u -} = core_converse.env; -class MUCSidebar extends CustomElement { - static get properties() { - return { - jid: { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - this.listenTo(this.model.occupants, 'add', this.requestUpdate); - this.listenTo(this.model.occupants, 'remove', this.requestUpdate); - this.listenTo(this.model.occupants, 'change', this.requestUpdate); - this.model.initialized.then(() => this.requestUpdate()); - } - - render() { - const tpl = muc_sidebar(Object.assign(this.model.toJSON(), { - 'occupants': [...this.model.occupants.models], - 'closeSidebar': ev => this.closeSidebar(ev), - 'onOccupantClicked': ev => this.onOccupantClicked(ev) - })); - return tpl; - } - - closeSidebar(ev) { - var _ev$preventDefault, _ev$stopPropagation; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - ev === null || ev === void 0 ? void 0 : (_ev$stopPropagation = ev.stopPropagation) === null || _ev$stopPropagation === void 0 ? void 0 : _ev$stopPropagation.call(ev); - sidebar_u.safeSave(this.model, { - 'hidden_occupants': true - }); - } - - onOccupantClicked(ev) { - var _ev$preventDefault2; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - - const chatview = shared_converse.chatboxviews.get(this.getAttribute('jid')); - - chatview === null || chatview === void 0 ? void 0 : chatview.getBottomPanel().insertIntoTextArea(`@${ev.target.textContent}`); - } - -} -api.elements.define('converse-muc-sidebar', MUCSidebar); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-chatarea.js - - - - - - -/* harmony default export */ const muc_chatarea = (o => { - var _o$model; - - return T` -
    -
    - - - ${(_o$model = o.model) !== null && _o$model !== void 0 && _o$model.get('show_help_messages') ? T`
    -
    ` : ''} -
    - -
    - - ${o.model ? T` - ` : ''} -`; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/chatarea.js - - - - -const { - u: chatarea_u -} = core_converse.env; -class MUCChatArea extends CustomElement { - static get properties() { - return { - jid: { - type: String - }, - show_help_messages: { - type: Boolean - }, - type: { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - async initialize() { - this.model = await api.rooms.get(this.jid); - this.listenTo(this.model, 'change:show_help_messages', () => this.requestUpdate()); - this.listenTo(this.model, 'change:hidden_occupants', () => this.requestUpdate()); - this.listenTo(this.model.session, 'change:connection_status', () => this.requestUpdate()); // Bind so that we can pass it to addEventListener and removeEventListener - - this.onMouseMove = this._onMouseMove.bind(this); - this.onMouseUp = this._onMouseUp.bind(this); - this.requestUpdate(); // Make sure we render again after the model has been attached - } - - render() { - return muc_chatarea({ - 'getHelpMessages': () => this.getHelpMessages(), - 'jid': this.jid, - 'model': this.model, - 'onMousedown': ev => this.onMousedown(ev), - 'show_send_button': shared_converse.show_send_button, - 'shouldShowSidebar': () => this.shouldShowSidebar(), - 'type': this.type - }); - } - - shouldShowSidebar() { - return !this.model.get('hidden_occupants') && this.model.session.get('connection_status') === core_converse.ROOMSTATUS.ENTERED; - } - - getHelpMessages() { - const setting = api.settings.get('muc_disable_slash_commands'); - const disabled_commands = Array.isArray(setting) ? setting : []; - return [`/admin: ${__("Change user's affiliation to admin")}`, `/ban: ${__('Ban user by changing their affiliation to outcast')}`, `/clear: ${__('Clear the chat area')}`, `/close: ${__('Close this groupchat')}`, `/deop: ${__('Change user role to participant')}`, `/destroy: ${__('Remove this groupchat')}`, `/help: ${__('Show this menu')}`, `/kick: ${__('Kick user from groupchat')}`, `/me: ${__('Write in 3rd person')}`, `/member: ${__('Grant membership to a user')}`, `/modtools: ${__('Opens up the moderator tools GUI')}`, `/mute: ${__("Remove user's ability to post messages")}`, `/nick: ${__('Change your nickname')}`, `/op: ${__('Grant moderator role to user')}`, `/owner: ${__('Grant ownership of this groupchat')}`, `/register: ${__('Register your nickname')}`, `/revoke: ${__("Revoke the user's current affiliation")}`, `/subject: ${__('Set groupchat subject')}`, `/topic: ${__('Set groupchat subject (alias for /subject)')}`, `/voice: ${__('Allow muted user to post messages')}`].filter(line => disabled_commands.every(c => !line.startsWith(c + '<', 9))).filter(line => this.model.getAllowedCommands().some(c => line.startsWith(c + '<', 9))); - } - - onMousedown(ev) { - if (chatarea_u.hasClass('dragresize-occupants-left', ev.target)) { - this.onStartResizeOccupants(ev); - } - } - - onStartResizeOccupants(ev) { - this.resizing = true; - this.addEventListener('mousemove', this.onMouseMove); - this.addEventListener('mouseup', this.onMouseUp); - const sidebar_el = this.querySelector('converse-muc-sidebar'); - const style = window.getComputedStyle(sidebar_el); - this.width = parseInt(style.width.replace(/px$/, ''), 10); - this.prev_pageX = ev.pageX; - } - - _onMouseMove(ev) { - if (this.resizing) { - ev.preventDefault(); - const delta = this.prev_pageX - ev.pageX; - this.resizeSidebarView(delta, ev.pageX); - this.prev_pageX = ev.pageX; - } - } - - _onMouseUp(ev) { - if (this.resizing) { - ev.preventDefault(); - this.resizing = false; - this.removeEventListener('mousemove', this.onMouseMove); - this.removeEventListener('mouseup', this.onMouseUp); - const sidebar_el = this.querySelector('converse-muc-sidebar'); - const element_position = sidebar_el.getBoundingClientRect(); - const occupants_width = this.calculateSidebarWidth(element_position, 0); - chatarea_u.safeSave(this.model, { - occupants_width - }); - } - } - - calculateSidebarWidth(element_position, delta) { - let occupants_width = element_position.width + delta; - const room_width = this.clientWidth; // keeping display in boundaries - - if (occupants_width < room_width * 0.2) { - // set pixel to 20% width - occupants_width = room_width * 0.2; - this.is_minimum = true; - } else if (occupants_width > room_width * 0.75) { - // set pixel to 75% width - occupants_width = room_width * 0.75; - this.is_maximum = true; - } else if (room_width - occupants_width < 250) { - // resize occupants if chat-area becomes smaller than 250px (min-width property set in css) - occupants_width = room_width - 250; - this.is_maximum = true; - } else { - this.is_maximum = false; - this.is_minimum = false; - } - - return occupants_width; - } - - resizeSidebarView(delta, current_mouse_position) { - const sidebar_el = this.querySelector('converse-muc-sidebar'); - const element_position = sidebar_el.getBoundingClientRect(); - - if (this.is_minimum) { - this.is_minimum = element_position.left < current_mouse_position; - } else if (this.is_maximum) { - this.is_maximum = element_position.left > current_mouse_position; - } else { - const occupants_width = this.calculateSidebarWidth(element_position, delta); - sidebar_el.style.flex = '0 0 ' + occupants_width + 'px'; - } - } - -} -api.elements.define('converse-muc-chatarea', MUCChatArea); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-config-form.js - - - - -const { - sizzle: muc_config_form_sizzle -} = core_converse.env; -const muc_config_form_u = core_converse.env.utils; -/* harmony default export */ const muc_config_form = (o => { - const whitelist = api.settings.get('roomconfig_whitelist'); - const config_stanza = o.model.session.get('config_stanza'); - let fields = []; - let instructions = ''; - let title; - - if (config_stanza) { - var _stanza$querySelector, _stanza$querySelector2; - - const stanza = muc_config_form_u.toStanza(config_stanza); - fields = muc_config_form_sizzle('field', stanza); - - if (whitelist.length) { - fields = fields.filter(f => whitelist.includes(f.getAttribute('var'))); - } - - const password_protected = o.model.features.get('passwordprotected'); - const options = { - 'new_password': !password_protected, - 'fixed_username': o.model.get('jid') - }; - fields = fields.map(f => muc_config_form_u.xForm2TemplateResult(f, stanza, options)); - instructions = (_stanza$querySelector = stanza.querySelector('instructions')) === null || _stanza$querySelector === void 0 ? void 0 : _stanza$querySelector.textContent; - title = (_stanza$querySelector2 = stanza.querySelector('title')) === null || _stanza$querySelector2 === void 0 ? void 0 : _stanza$querySelector2.textContent; - } else { - title = __('Loading configuration form'); - } - - const i18n_save = __('Save'); - - const i18n_cancel = __('Cancel'); - - return T` -
    - -
    - ${title} - ${title !== instructions ? T`

    ${instructions}

    ` : ''} - ${fields.length ? fields : spinner({ - 'classes': 'hor_centered' - })} -
    - ${fields.length ? T` -
    - - -
    ` : ''} -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/config-form.js - - - - - -const { - sizzle: config_form_sizzle -} = core_converse.env; -const config_form_u = core_converse.env.utils; - -class MUCConfigForm extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - this.listenTo(this.model.features, 'change:passwordprotected', this.requestUpdate); - this.listenTo(this.model.session, 'change:config_stanza', this.requestUpdate); - this.getConfig(); - } - - render() { - return muc_config_form({ - 'model': this.model, - 'closeConfigForm': ev => this.closeForm(ev), - 'submitConfigForm': ev => this.submitConfigForm(ev) - }); - } - - async getConfig() { - const iq = await this.model.fetchRoomConfiguration(); - this.model.session.set('config_stanza', iq.outerHTML); - } - - async submitConfigForm(ev) { - ev.preventDefault(); - const inputs = config_form_sizzle(':input:not([type=button]):not([type=submit])', ev.target); - const config_array = inputs.map(config_form_u.webForm2xForm).filter(f => f); - - try { - await this.model.sendConfiguration(config_array); - } catch (e) { - headless_log.error(e); - - const message = __("Sorry, an error occurred while trying to submit the config form.") + " " + __("Check your browser's developer console for details."); - - api.alert('error', __('Error'), message); - } - - await this.model.refreshDiscoInfo(); - this.closeForm(); - } - - closeForm(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - this.model.session.set('view', null); - } - -} - -api.elements.define('converse-muc-config-form', MUCConfigForm); -/* harmony default export */ const config_form = ((/* unused pure expression or super */ null && (MUCConfigForm))); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-destroyed.js - - - -const tpl_moved = o => { - const i18n_moved = __('The conversation has moved to a new address. Click the link below to enter.'); - - return T` -

    ${i18n_moved}

    - `; -}; - -/* harmony default export */ const muc_destroyed = (o => { - const i18n_non_existent = __('This groupchat no longer exists'); - - const i18n_reason = __('The following reason was given: "%1$s"', o.reason || ''); - - return T` -
    -

    ${i18n_non_existent}

    -
    - ${o.reason ? T`

    ${i18n_reason}

    ` : ''} - ${o.moved_jid ? tpl_moved(o) : ''} - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/destroyed.js - - - - -class MUCDestroyed extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - } - - render() { - const reason = this.model.get('destroyed_reason'); - const moved_jid = this.model.get('moved_jid'); - return muc_destroyed({ - moved_jid, - reason, - 'onSwitch': ev => this.onSwitch(ev) - }); - } - - async onSwitch(ev) { - ev.preventDefault(); - const moved_jid = this.model.get('moved_jid'); - const room = await api.rooms.get(moved_jid, {}, true); - room.maybeShow(true); - this.model.destroy(); - } - -} - -api.elements.define('converse-muc-destroyed', MUCDestroyed); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-disconnect.js - -/* harmony default export */ const muc_disconnect = (messages => { - return T` -
    -

    ${messages[0]}

    - ${messages.slice(1).map(m => T`

    ${m}

    `)} -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/disconnected.js - - - - - -class MUCDisconnected extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - } - - render() { - const message = this.model.session.get('disconnection_message'); - - if (!message) { - return; - } - - const messages = [message]; - const actor = this.model.session.get('disconnection_actor'); - - if (actor) { - messages.push(__('This action was done by %1$s.', actor)); - } - - const reason = this.model.session.get('disconnection_reason'); - - if (reason) { - messages.push(__('The reason given is: "%1$s".', reason)); - } - - return muc_disconnect(messages); - } - -} - -api.elements.define('converse-muc-disconnected', MUCDisconnected); -;// CONCATENATED MODULE: ./src/modals/templates/muc-invite.js - - - -/* harmony default export */ const muc_invite = (o => { - const i18n_invite = __('Invite'); - - const i18n_invite_heading = __('Invite someone to this groupchat'); - - const i18n_jid_placeholder = __('user@example.org'); - - const i18n_error_message = __('Please enter a valid XMPP address'); - - const i18n_invite_label = __('XMPP Address'); - - const i18n_reason = __('Optional reason for the invitation'); - - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/modals/muc-invite.js - - - - -const muc_invite_u = core_converse.env.utils; -/* harmony default export */ const modals_muc_invite = (base.extend({ - id: "muc-invite-modal", - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change', this.render); - this.initInviteWidget(); - }, - - toHTML() { - return muc_invite(Object.assign(this.model.toJSON(), { - 'submitInviteForm': ev => this.submitInviteForm(ev) - })); - }, - - initInviteWidget() { - if (this.invite_auto_complete) { - this.invite_auto_complete.destroy(); - } - - const list = shared_converse.roster.map(i => ({ - 'label': i.getDisplayName(), - 'value': i.get('jid') - })); - - const el = this.el.querySelector('.suggestion-box').parentElement; - this.invite_auto_complete = new shared_converse.AutoComplete(el, { - 'min_chars': 1, - 'list': list - }); - }, - - submitInviteForm(ev) { - ev.preventDefault(); // TODO: Add support for sending an invite to multiple JIDs - - const data = new FormData(ev.target); - const jid = data.get('invitee_jids'); - const reason = data.get('reason'); - - if (muc_invite_u.isValidJID(jid)) { - // TODO: Create and use API here - this.chatroomview.model.directInvite(jid, reason); - this.modal.hide(); - } else { - this.model.set({ - 'invalid_invite_jid': true - }); - } - } - -})); -;// CONCATENATED MODULE: ./src/modals/templates/muc-details.js - - - - - -const subject = o => { - const i18n_topic = __('Topic'); - - const i18n_topic_author = __('Topic author'); - - return T` -

    ${i18n_topic}: ${o.subject.text}

    -

    ${i18n_topic_author}: ${o.subject && o.subject.author}

    - `; -}; - -/* harmony default export */ const muc_details = (o => { - const i18n_address = __('Groupchat XMPP address'); - - const i18n_archiving = __('Message archiving'); - - const i18n_archiving_help = __('Messages are archived on the server'); - - const i18n_desc = __('Description'); - - const i18n_features = __('Features'); - - const i18n_hidden = __('Hidden'); - - const i18n_hidden_help = __('This groupchat is not publicly searchable'); - - const i18n_members_help = __('This groupchat is restricted to members only'); - - const i18n_members_only = __('Members only'); - - const i18n_moderated = __('Moderated'); - - const i18n_moderated_help = __('Participants entering this groupchat need to request permission to write'); - - const i18n_name = __('Name'); - - const i18n_no_pass_help = __('This groupchat does not require a password upon entry'); - - const i18n_no_password_required = __('No password required'); - - const i18n_not_anonymous = __('Not anonymous'); - - const i18n_not_anonymous_help = __('All other groupchat participants can see your XMPP address'); - - const i18n_not_moderated = __('Not moderated'); - - const i18n_not_moderated_help = __('Participants entering this groupchat can write right away'); - - const i18n_online_users = __('Online users'); - - const i18n_open = __('Open'); - - const i18n_open_help = __('Anyone can join this groupchat'); - - const i18n_password_help = __('This groupchat requires a password before entry'); - - const i18n_password_protected = __('Password protected'); - - const i18n_persistent = __('Persistent'); - - const i18n_persistent_help = __('This groupchat persists even if it\'s unoccupied'); - - const i18n_public = __('Public'); - - const i18n_semi_anon = __('Semi-anonymous'); - - const i18n_semi_anon_help = __('Only moderators can see your XMPP address'); - - const i18n_temporary = __('Temporary'); - - const i18n_temporary_help = __('This groupchat will disappear once the last person leaves'); - - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/modals/muc-details.js - - - -/* harmony default export */ const modals_muc_details = (base.extend({ - id: "muc-details-modal", - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change', this.render); - this.listenTo(this.model.features, 'change', this.render); - this.listenTo(this.model.occupants, 'add', this.render); - this.listenTo(this.model.occupants, 'change', this.render); - }, - - toHTML() { - return muc_details(Object.assign(this.model.toJSON(), { - 'config': this.model.config.toJSON(), - 'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()), - 'features': this.model.features.toJSON(), - 'num_occupants': this.model.occupants.length - })); - } - -})); -;// CONCATENATED MODULE: ./src/shared/components/rich-text.js - - - -/** - * The RichText custom element allows you to parse transform text into rich DOM elements. - * @example - */ - -class rich_text_RichText extends CustomElement { - static get properties() { - /** - * @typedef { Object } RichTextComponentProperties - * @property { Boolean } embed_audio - * Whether URLs that point to audio files should render as audio players. - * @property { Boolean } embed_videos - * Whether URLs that point to video files should render as video players. - * @property { Array } mentions - An array of objects representing chat mentions - * @property { String } nick - The current user's nickname, relevant for mentions - * @property { Number } offset - The text offset, in case this is a nested RichText element. - * @property { Function } onImgClick - * @property { Function } onImgLoad - * @property { Boolean } render_styling - * Whether XEP-0393 message styling hints should be rendered - * @property { Boolean } show_images - * Whether URLs that point to image files should render as images - * @property { Boolean } hide_media_urls - * If media URLs are rendered as media, then this option determines - * whether the original URL is also still shown or not. - * Only relevant in conjunction with `show_images`, `embed_audio` and `embed_videos`. - * @property { Boolean } show_me_message - * Whether text that starts with /me should be rendered in the 3rd person. - * @property { String } text - The text that will get transformed. - */ - return { - embed_audio: { - type: Boolean - }, - embed_videos: { - type: Boolean - }, - mentions: { - type: Array - }, - nick: { - type: String - }, - offset: { - type: Number - }, - onImgClick: { - type: Function - }, - onImgLoad: { - type: Function - }, - render_styling: { - type: Boolean - }, - show_images: { - type: Boolean - }, - hide_media_urls: { - type: Boolean - }, - show_me_message: { - type: Boolean - }, - text: { - type: String - } - }; - } - - constructor() { - super(); - this.embed_audio = false; - this.embed_videos = false; - this.hide_media_urls = false; - this.mentions = []; - this.offset = 0; - this.render_styling = false; - this.show_image_urls = true; - this.show_images = false; - this.show_me_message = false; - } - - render() { - const options = { - embed_audio: this.embed_audio, - embed_videos: this.embed_videos, - nick: this.nick, - onImgClick: this.onImgClick, - onImgLoad: this.onImgLoad, - render_styling: this.render_styling, - show_images: this.show_images, - show_me_message: this.show_me_message, - hide_media_urls: this.hide_media_urls - }; - return rich_text(this.text, this.offset, this.mentions, options); - } - -} -api.elements.define('converse-rich-text', rich_text_RichText); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-head.js - - - - - - - -const tpl_standalone_btns = o => o.standalone_btns.reverse().map(b => until_o(b, '')); - -/* harmony default export */ const muc_head = (o => { - const i18n_hide_topic = __('Hide the groupchat topic'); - - const i18n_bookmarked = __('This groupchat is bookmarked'); - - const subject = o.subject ? o.subject.text : ''; - const show_subject = subject && !o.subject_hidden; - return T` -
    - ${!shared_converse.api.settings.get("singleton") ? T`` : ''} -
    ${o.title} - ${o.bookmarked ? T`` : ''} -
    -
    - ${o.standalone_btns.length ? tpl_standalone_btns(o) : ''} - ${o.dropdown_btns.length ? T`` : ''} -
    -
    - ${show_subject ? T`

    - -

    ` : ''} - `; -}); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/muc-views/styles/muc-head.scss -var styles_muc_head = __webpack_require__(679); -;// CONCATENATED MODULE: ./src/plugins/muc-views/styles/muc-head.scss - - - - - - - - - - - -var muc_head_options = {}; - -muc_head_options.styleTagTransform = (styleTagTransform_default()); -muc_head_options.setAttributes = (setAttributesWithoutAttributes_default()); - - muc_head_options.insert = insertBySelector_default().bind(null, "head"); - -muc_head_options.domAPI = (styleDomAPI_default()); -muc_head_options.insertStyleElement = (insertStyleElement_default()); - -var muc_head_update = injectStylesIntoStyleTag_default()(styles_muc_head/* default */.Z, muc_head_options); - - - - - /* harmony default export */ const muc_views_styles_muc_head = (styles_muc_head/* default */.Z && styles_muc_head/* default.locals */.Z.locals ? styles_muc_head/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/muc-views/heading.js - - - - - - - - - - - - -class MUCHeading extends ElementView { - async connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.getAttribute('jid')); - this.debouncedRender = lodash_es_debounce(this.render, 100); - this.listenTo(this.model, 'change', this.debouncedRender); - const user_settings = await shared_converse.api.user.settings.getModel(); - this.listenTo(user_settings, 'change:mucs_with_hidden_subject', this.debouncedRender); - await this.model.initialized; - this.listenTo(this.model.features, 'change:open', this.debouncedRender); - this.model.occupants.forEach(o => this.onOccupantAdded(o)); - this.listenTo(this.model.occupants, 'add', this.onOccupantAdded); - this.listenTo(this.model.occupants, 'change:affiliation', this.onOccupantAffiliationChanged); - this.render(); - } - - async render() { - const tpl = await this.generateHeadingTemplate(); - V(tpl, this); - } - - onOccupantAdded(occupant) { - if (occupant.get('jid') === shared_converse.bare_jid) { - this.debouncedRender(); - } - } - - onOccupantAffiliationChanged(occupant) { - if (occupant.get('jid') === shared_converse.bare_jid) { - this.debouncedRender(); - } - } - - showRoomDetailsModal(ev) { - ev.preventDefault(); - api.modal.show(modals_muc_details, { - 'model': this.model - }, ev); - } - - showInviteModal(ev) { - ev.preventDefault(); - api.modal.show(modals_muc_invite, { - 'model': new Model(), - 'chatroomview': this - }, ev); - } - - toggleTopic(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - this.model.toggleSubjectHiddenState(); - } - - getAndRenderConfigurationForm() { - this.model.session.set('view', core_converse.MUC.VIEWS.CONFIG); - } - - close(ev) { - ev.preventDefault(); - this.model.close(); - } - - destroy(ev) { - ev.preventDefault(); - destroyMUC(this.model); - } - /** - * Returns a list of objects which represent buttons for the groupchat header. - * @emits _converse#getHeadingButtons - */ - - - getHeadingButtons(subject_hidden) { - const buttons = []; - buttons.push({ - 'i18n_text': __('Details'), - 'i18n_title': __('Show more information about this groupchat'), - 'handler': ev => this.showRoomDetailsModal(ev), - 'a_class': 'show-muc-details-modal', - 'icon_class': 'fa-info-circle', - 'name': 'details' - }); - - if (this.model.getOwnAffiliation() === 'owner') { - buttons.push({ - 'i18n_text': __('Configure'), - 'i18n_title': __('Configure this groupchat'), - 'handler': () => this.getAndRenderConfigurationForm(), - 'a_class': 'configure-chatroom-button', - 'icon_class': 'fa-wrench', - 'name': 'configure' - }); - } - - if (this.model.invitesAllowed()) { - buttons.push({ - 'i18n_text': __('Invite'), - 'i18n_title': __('Invite someone to join this groupchat'), - 'handler': ev => this.showInviteModal(ev), - 'a_class': 'open-invite-modal', - 'icon_class': 'fa-user-plus', - 'name': 'invite' - }); - } - - const subject = this.model.get('subject'); - - if (subject && subject.text) { - buttons.push({ - 'i18n_text': subject_hidden ? __('Show topic') : __('Hide topic'), - 'i18n_title': subject_hidden ? __('Show the topic message in the heading') : __('Hide the topic in the heading'), - 'handler': ev => this.toggleTopic(ev), - 'a_class': 'hide-topic', - 'icon_class': 'fa-minus-square', - 'name': 'toggle-topic' - }); - } - - const conn_status = this.model.session.get('connection_status'); - - if (conn_status === core_converse.ROOMSTATUS.ENTERED) { - const allowed_commands = this.model.getAllowedCommands(); - - if (allowed_commands.includes('modtools')) { - buttons.push({ - 'i18n_text': __('Moderate'), - 'i18n_title': __('Moderate this groupchat'), - 'handler': () => showModeratorToolsModal(this.model), - 'a_class': 'moderate-chatroom-button', - 'icon_class': 'fa-user-cog', - 'name': 'moderate' - }); - } - - if (allowed_commands.includes('destroy')) { - buttons.push({ - 'i18n_text': __('Destroy'), - 'i18n_title': __('Remove this groupchat'), - 'handler': ev => this.destroy(ev), - 'a_class': 'destroy-chatroom-button', - 'icon_class': 'fa-trash', - 'name': 'destroy' - }); - } - } - - if (!api.settings.get('singleton')) { - buttons.push({ - 'i18n_text': __('Leave'), - 'i18n_title': __('Leave and close this groupchat'), - 'handler': async ev => { - ev.stopPropagation(); - const messages = [__('Are you sure you want to leave this groupchat?')]; - const result = await api.confirm(__('Confirm'), messages); - result && this.close(ev); - }, - 'a_class': 'close-chatbox-button', - 'standalone': api.settings.get('view_mode') === 'overlayed', - 'icon_class': 'fa-sign-out-alt', - 'name': 'signout' - }); - } - - const chatview = shared_converse.chatboxviews.get(this.getAttribute('jid')); - - if (chatview) { - return shared_converse.api.hook('getHeadingButtons', chatview, buttons); - } else { - return buttons; // Happens during tests - } - } - /** - * Returns the groupchat heading TemplateResult to be rendered. - */ - - - async generateHeadingTemplate() { - const subject_hidden = await this.model.isSubjectHidden(); - const heading_btns = await this.getHeadingButtons(subject_hidden); - const standalone_btns = heading_btns.filter(b => b.standalone); - const dropdown_btns = heading_btns.filter(b => !b.standalone); - return muc_head(Object.assign(this.model.toJSON(), { - _converse: shared_converse, - subject_hidden, - 'dropdown_btns': dropdown_btns.map(b => getHeadingDropdownItem(b)), - 'standalone_btns': standalone_btns.map(b => getHeadingStandaloneButton(b)), - 'title': this.model.getDisplayName() - })); - } - -} -api.elements.define('converse-muc-heading', MUCHeading); -;// CONCATENATED MODULE: ./src/plugins/muc-views/nickname-form.js - - - - -class MUCNicknameForm extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - } - - render() { - return muc_nickname_form(this.model); - } - -} - -api.elements.define('converse-muc-nickname-form', MUCNicknameForm); -/* harmony default export */ const nickname_form = ((/* unused pure expression or super */ null && (MUCNicknameForm))); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-password-form.js - - -/* harmony default export */ const muc_password_form = (o => { - const i18n_heading = __('This groupchat requires a password'); - - const i18n_password = __('Password: '); - - const i18n_submit = __('Submit'); - - return T` -
    -
    - -

    ${o.validation_message}

    - - -
    -
    - -
    -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/password-form.js - - - - -class MUCPasswordForm extends CustomElement { - static get properties() { - return { - 'jid': { - type: String - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.model = shared_converse.chatboxes.get(this.jid); - this.listenTo(this.model, 'change:password_validation_message', this.render); - this.render(); - } - - render() { - return muc_password_form({ - 'jid': this.model.get('jid'), - 'submitPassword': ev => this.submitPassword(ev), - 'validation_message': this.model.get('password_validation_message') - }); - } - - submitPassword(ev) { - ev.preventDefault(); - const password = this.querySelector('input[type=password]').value; - this.model.join(this.model.get('nick'), password); - this.model.set('password_validation_message', null); - } - -} - -api.elements.define('converse-muc-password-form', MUCPasswordForm); -/* harmony default export */ const password_form = ((/* unused pure expression or super */ null && (MUCPasswordForm))); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc.js - - - - - - - - - -/* harmony default export */ const templates_muc = (o => { - return T` -
    - - ${o.model ? T` - - -
    ${getChatRoomBodyTemplate(o)}
    - ` : ''} -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/muc.js -function muc_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - -class MUCView extends BaseChatView { - constructor(...args) { - super(...args); - - muc_defineProperty(this, "length", 300); - - muc_defineProperty(this, "is_chatroom", true); - } - - connectedCallback() { - super.connectedCallback(); - this.initialize(); - } - - async initialize() { - this.model = await api.rooms.get(this.jid); - - shared_converse.chatboxviews.add(this.jid, this); - - this.setAttribute('id', this.model.get('box_id')); - this.listenTo(shared_converse, 'windowStateChanged', this.onWindowStateChanged); - this.listenTo(this.model, 'change:composing_spoiler', this.requestUpdateMessageForm); - this.listenTo(this.model.session, 'change:connection_status', this.onConnectionStatusChanged); - this.listenTo(this.model.session, 'change:view', this.requestUpdate); - this.onConnectionStatusChanged(); - this.model.maybeShow(); - /** - * Triggered once a { @link _converse.ChatRoomView } has been opened - * @event _converse#chatRoomViewInitialized - * @type { _converse.ChatRoomView } - * @example _converse.api.listen.on('chatRoomViewInitialized', view => { ... }); - */ - - api.trigger('chatRoomViewInitialized', this); - } - - render() { - return templates_muc({ - 'model': this.model - }); - } - - onConnectionStatusChanged() { - const conn_status = this.model.session.get('connection_status'); - - if (conn_status === core_converse.ROOMSTATUS.CONNECTING) { - this.model.session.save({ - 'disconnection_actor': undefined, - 'disconnection_message': undefined, - 'disconnection_reason': undefined - }); - this.model.save({ - 'moved_jid': undefined, - 'password_validation_message': undefined, - 'reason': undefined - }); - } - - this.requestUpdate(); - } - -} -api.elements.define('converse-muc', MUCView); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/muc-views/styles/index.scss -var muc_views_styles = __webpack_require__(1557); -;// CONCATENATED MODULE: ./src/plugins/muc-views/styles/index.scss - - - - - - - - - - - -var muc_views_styles_options = {}; - -muc_views_styles_options.styleTagTransform = (styleTagTransform_default()); -muc_views_styles_options.setAttributes = (setAttributesWithoutAttributes_default()); - - muc_views_styles_options.insert = insertBySelector_default().bind(null, "head"); - -muc_views_styles_options.domAPI = (styleDomAPI_default()); -muc_views_styles_options.insertStyleElement = (insertStyleElement_default()); - -var muc_views_styles_update = injectStylesIntoStyleTag_default()(muc_views_styles/* default */.Z, muc_views_styles_options); - - - - - /* harmony default export */ const plugins_muc_views_styles = (muc_views_styles/* default */.Z && muc_views_styles/* default.locals */.Z.locals ? muc_views_styles/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/muc-views/index.js -/** - * @copyright 2020, the Converse.js contributors - * @description XEP-0045 Multi-User Chat Views - * @license Mozilla Public License (MPLv2) - */ - - - - - - - -core_converse.MUC.VIEWS = { - CONFIG: 'config-form', - BOOKMARK: 'bookmark-form' -}; -core_converse.plugins.add('converse-muc-views', { - /* Dependencies are other plugins which might be - * overridden or relied upon, and therefore need to be loaded before - * this plugin. They are "optional" because they might not be - * available, in which case any overrides applicable to them will be - * ignored. - * - * NB: These plugins need to have already been loaded via require.js. - * - * It's possible to make these dependencies "non-optional". - * If the setting "strict_plugin_dependencies" is set to true, - * an error will be raised if the plugin is not found. - */ - dependencies: ['converse-modal', 'converse-controlbox', 'converse-chatview'], - - initialize() { - const { - _converse - } = this; // Configuration values for this plugin - // ==================================== - // Refer to docs/source/configuration.rst for explanations of these - // configuration settings. - - api.settings.extend({ - 'auto_list_rooms': false, - 'cache_muc_messages': true, - 'locked_muc_nickname': false, - 'modtools_disable_query': [], - 'muc_disable_slash_commands': false, - 'muc_mention_autocomplete_filter': 'contains', - 'muc_mention_autocomplete_min_chars': 0, - 'muc_mention_autocomplete_show_avatar': true, - 'muc_roomid_policy': null, - 'muc_roomid_policy_hint': null, - 'roomconfig_whitelist': [], - 'show_retraction_warning': true, - 'visible_toolbar_buttons': { - 'toggle_occupants': true - } - }); - _converse.ChatRoomView = MUCView; - api.listen.on('clearsession', () => { - const view = _converse.chatboxviews.get('controlbox'); - - if (view && view.roomspanel) { - view.roomspanel.model.destroy(); - view.roomspanel.remove(); - delete view.roomspanel; - } - }); - api.listen.on('controlBoxInitialized', view => { - if (!api.settings.get('allow_muc')) { - return; - } - - fetchAndSetMUCDomain(view); - view.model.on('change:connected', () => fetchAndSetMUCDomain(view)); - }); - api.listen.on('chatBoxClosed', model => { - if (model.get('type') === _converse.CHATROOMS_TYPE) { - utils_clearHistory(model.get('jid')); - } - }); - } - -}); -// EXTERNAL MODULE: ./node_modules/favico.js-slevomat/favico.js -var favico = __webpack_require__(2329); -var favico_default = /*#__PURE__*/__webpack_require__.n(favico); -;// CONCATENATED MODULE: ./src/plugins/notifications/utils.js - - - - - -const { - Strophe: notifications_utils_Strophe -} = core_converse.env; -const supports_html5_notification = ('Notification' in window); -core_converse.env.Favico = (favico_default()); -let favicon; -function isMessageToHiddenChat(attrs) { - var _converse$chatboxes$g; - - return shared_converse.isTestEnv() || (((_converse$chatboxes$g = shared_converse.chatboxes.get(attrs.from)) === null || _converse$chatboxes$g === void 0 ? void 0 : _converse$chatboxes$g.isHidden()) ?? false); -} -function areDesktopNotificationsEnabled() { - return shared_converse.isTestEnv() || supports_html5_notification && api.settings.get('show_desktop_notifications') && Notification.permission === 'granted'; -} -function clearFavicon() { - var _navigator$clearAppBa, _navigator; - - favicon = null; - (_navigator$clearAppBa = (_navigator = navigator).clearAppBadge) === null || _navigator$clearAppBa === void 0 ? void 0 : _navigator$clearAppBa.call(_navigator).catch(e => headless_log.error("Could not clear unread count in app badge " + e)); -} -function updateUnreadFavicon() { - if (api.settings.get('show_tab_notifications')) { - var _navigator$setAppBadg, _navigator2; - - favicon = favicon ?? new core_converse.env.Favico({ - type: 'circle', - animation: 'pop' - }); - const chats = shared_converse.chatboxes.models; - const num_unread = chats.reduce((acc, chat) => acc + (chat.get('num_unread') || 0), 0); - favicon.badge(num_unread); - (_navigator$setAppBadg = (_navigator2 = navigator).setAppBadge) === null || _navigator$setAppBadg === void 0 ? void 0 : _navigator$setAppBadg.call(_navigator2, num_unread).catch(e => headless_log.error("Could set unread count in app badge - " + e)); - } -} - -function isReferenced(references, muc_jid, nick) { - const check = r => [shared_converse.bare_jid, `${muc_jid}/${nick}`].includes(r.uri.replace(/^xmpp:/, '')); - - return references.reduce((acc, r) => acc || check(r), false); -} -/** - * Is this a group message for which we should notify the user? - * @private - * @param { MUCMessageAttributes } attrs - */ - - -async function shouldNotifyOfGroupMessage(attrs) { - if (!(attrs !== null && attrs !== void 0 && attrs.body) && !(attrs !== null && attrs !== void 0 && attrs.message)) { - // attrs.message is used by 'info' messages - return false; - } - - const jid = attrs.from; - const muc_jid = attrs.from_muc; - const notify_all = api.settings.get('notify_all_room_messages'); - - const room = shared_converse.chatboxes.get(muc_jid); - - const resource = notifications_utils_Strophe.getResourceFromJid(jid); - const sender = resource && notifications_utils_Strophe.unescapeNode(resource) || ''; - let is_mentioned = false; - const nick = room.get('nick'); - - if (api.settings.get('notify_nicknames_without_references')) { - is_mentioned = new RegExp(`\\b${nick}\\b`).test(attrs.body); - } - - const is_not_mine = sender !== nick; - const should_notify_user = notify_all === true || Array.isArray(notify_all) && notify_all.includes(muc_jid) || isReferenced(attrs.references, muc_jid, nick) || is_mentioned; - - if (is_not_mine && !!should_notify_user) { - /** - * *Hook* which allows plugins to run further logic to determine - * whether a notification should be sent out for this message. - * @event _converse#shouldNotifyOfGroupMessage - * @example - * api.listen.on('shouldNotifyOfGroupMessage', (should_notify) => { - * return should_notify && flurb === floob; - * }); - */ - const should_notify = await api.hook('shouldNotifyOfGroupMessage', attrs, true); - return should_notify; - } - - return false; -} - -async function shouldNotifyOfInfoMessage(attrs) { - if (!attrs.from_muc) { - return false; - } - - const room = await api.rooms.get(attrs.from_muc); - - if (!room) { - return false; - } - - const nick = room.get('nick'); - const muc_jid = attrs.from_muc; - const notify_all = api.settings.get('notify_all_room_messages'); - return notify_all === true || Array.isArray(notify_all) && notify_all.includes(muc_jid) || isReferenced(attrs.references, muc_jid, nick); -} -/** - * @private - * @async - * @method shouldNotifyOfMessage - * @param { MessageData|MUCMessageData } data - */ - - -function shouldNotifyOfMessage(data) { - const { - attrs - } = data; - - if (!attrs || attrs.is_forwarded) { - return false; - } - - if (attrs['type'] === 'groupchat') { - return shouldNotifyOfGroupMessage(attrs); - } else if (attrs['type'] === 'info') { - return shouldNotifyOfInfoMessage(attrs); - } else if (attrs.is_headline) { - // We want to show notifications for headline messages. - return isMessageToHiddenChat(attrs); - } - - const is_me = notifications_utils_Strophe.getBareJidFromJid(attrs.from) === shared_converse.bare_jid; - - return !isEmptyMessage(attrs) && !is_me && (api.settings.get('show_desktop_notifications') === 'all' || isMessageToHiddenChat(attrs)); -} - -function showFeedbackNotification(data) { - if (data.klass === 'error' || data.klass === 'warn') { - const n = new Notification(data.subject, { - body: data.message, - lang: shared_converse.locale, - icon: shared_converse.notification_icon - }); - setTimeout(n.close.bind(n), 5000); - } -} -/** - * Creates an HTML5 Notification to inform of a change in a - * contact's chat state. - */ - -function showChatStateNotification(contact) { - if (shared_converse.chatstate_notification_blacklist.includes(contact.jid)) { - // Don't notify if the user is being ignored. - return; - } - - const chat_state = contact.presence.get('show'); - let message = null; - - if (chat_state === 'offline') { - message = __('has gone offline'); - } else if (chat_state === 'away') { - message = __('has gone away'); - } else if (chat_state === 'dnd') { - message = __('is busy'); - } else if (chat_state === 'online') { - message = __('has come online'); - } - - if (message === null) { - return; - } - - const n = new Notification(contact.getDisplayName(), { - body: message, - lang: shared_converse.locale, - icon: shared_converse.notification_icon - }); - setTimeout(() => n.close(), 5000); -} -/** - * Shows an HTML5 Notification with the passed in message - * @private - * @param { MessageData|MUCMessageData } data - */ - - -function showMessageNotification(data) { - const { - attrs - } = data; - - if (attrs.is_error) { - return; - } - - if (!areDesktopNotificationsEnabled()) { - return; - } - - let title, roster_item; - const full_from_jid = attrs.from; - const from_jid = notifications_utils_Strophe.getBareJidFromJid(full_from_jid); - - if (attrs.type == 'info') { - title = attrs.message; - } else if (attrs.type === 'headline') { - if (!from_jid.includes('@') || api.settings.get('allow_non_roster_messaging')) { - title = __('Notification from %1$s', from_jid); - } else { - return; - } - } else if (!from_jid.includes('@')) { - // workaround for Prosody which doesn't give type "headline" - title = __('Notification from %1$s', from_jid); - } else if (attrs.type === 'groupchat') { - title = __('%1$s says', notifications_utils_Strophe.getResourceFromJid(full_from_jid)); - } else { - if (shared_converse.roster === undefined) { - headless_log.error('Could not send notification, because roster is undefined'); - return; - } - - roster_item = shared_converse.roster.get(from_jid); - - if (roster_item !== undefined) { - title = __('%1$s says', roster_item.getDisplayName()); - } else { - if (api.settings.get('allow_non_roster_messaging')) { - title = __('%1$s says', from_jid); - } else { - return; - } - } - } - - let body; - - if (attrs.type == 'info') { - body = attrs.reason; - } else { - body = attrs.is_encrypted ? attrs.plaintext : attrs.body; - - if (!body) { - return; - } - } - - const n = new Notification(title, { - 'body': body, - 'lang': shared_converse.locale, - 'icon': api.settings.get('notification_icon'), - 'requireInteraction': !shared_converse.notification_delay - }); - - if (api.settings.get('notification_delay')) { - setTimeout(() => n.close(), api.settings.get('notification_delay')); - } - - n.onclick = function (event) { - event.preventDefault(); - window.focus(); - - const chat = shared_converse.chatboxes.get(from_jid); - - chat.maybeShow(true); - }; -} - -function playSoundNotification() { - if (api.settings.get('play_sounds') && window.Audio !== undefined) { - const audioOgg = new Audio(api.settings.get('sounds_path') + 'msg_received.ogg'); - const canPlayOgg = audioOgg.canPlayType('audio/ogg'); - - if (canPlayOgg === 'probably') { - return audioOgg.play(); - } - - const audioMp3 = new Audio(api.settings.get('sounds_path') + 'msg_received.mp3'); - const canPlayMp3 = audioMp3.canPlayType('audio/mp3'); - - if (canPlayMp3 === 'probably') { - audioMp3.play(); - } else if (canPlayOgg === 'maybe') { - audioOgg.play(); - } else if (canPlayMp3 === 'maybe') { - audioMp3.play(); - } - } -} -/** - * Event handler for the on('message') event. Will call methods - * to play sounds and show HTML5 notifications. - */ - - -async function handleMessageNotification(data) { - if (!(await shouldNotifyOfMessage(data))) { - return false; - } - /** - * Triggered when a notification (sound or HTML5 notification) for a new - * message has will be made. - * @event _converse#messageNotification - * @type { MessageData|MUCMessageData} - * @example _converse.api.listen.on('messageNotification', data => { ... }); - */ - - - api.trigger('messageNotification', data); - playSoundNotification(); - showMessageNotification(data); -} -function handleFeedback(data) { - if (areDesktopNotificationsEnabled(true)) { - showFeedbackNotification(data); - } -} -/** - * Event handler for on('contactPresenceChanged'). - * Will show an HTML5 notification to indicate that the chat status has changed. - */ - -function handleChatStateNotification(contact) { - if (areDesktopNotificationsEnabled() && api.settings.get('show_chat_state_notifications')) { - showChatStateNotification(contact); - } -} - -function showContactRequestNotification(contact) { - const n = new Notification(contact.getDisplayName(), { - body: __('wants to be your contact'), - lang: shared_converse.locale, - icon: shared_converse.notification_icon - }); - setTimeout(() => n.close(), 5000); -} - -function handleContactRequestNotification(contact) { - if (areDesktopNotificationsEnabled(true)) { - showContactRequestNotification(contact); - } -} -function requestPermission() { - if (supports_html5_notification && !['denied', 'granted'].includes(Notification.permission)) { - // Ask user to enable HTML5 notifications - Notification.requestPermission(); - } -} -;// CONCATENATED MODULE: ./src/plugins/notifications/index.js -/** - * @module converse-notification - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - -core_converse.plugins.add('converse-notification', { - dependencies: ['converse-chatboxes'], - - initialize() { - api.settings.extend({ - // ^ a list of JIDs to ignore concerning chat state notifications - chatstate_notification_blacklist: [], - notification_delay: 5000, - notification_icon: 'logo/conversejs-filled.svg', - notify_all_room_messages: false, - notify_nicknames_without_references: false, - play_sounds: true, - show_chat_state_notifications: false, - show_desktop_notifications: true, - show_tab_notifications: true, - sounds_path: api.settings.get('assets_path') + '/sounds/' - }); - /************************ Event Handlers ************************/ - - api.listen.on('clearSession', clearFavicon); // Needed for tests - - api.waitUntil('chatBoxesInitialized').then(() => shared_converse.chatboxes.on('change:num_unread', updateUnreadFavicon)); - api.listen.on('pluginsInitialized', function () { - // We only register event handlers after all plugins are - // registered, because other plugins might override some of our - // handlers. - api.listen.on('contactRequest', handleContactRequestNotification); - api.listen.on('contactPresenceChanged', handleChatStateNotification); - api.listen.on('message', handleMessageNotification); - api.listen.on('feedback', handleFeedback); - api.listen.on('connected', requestPermission); - }); - } - -}); -;// CONCATENATED MODULE: ./src/modals/templates/user-settings.js - - - - - - - -const user_settings_tpl_navigation = o => { - const i18n_about = __('About'); - - const i18n_commands = __('Commands'); - - return T` - - `; -}; - -/* harmony default export */ const templates_user_settings = (o => { - const i18n_modal_title = __('Settings'); - - const first_subtitle = __('%1$s Open Source %2$s XMPP chat client brought to you by %3$s Opkode %2$s', '', '', ''); - - const second_subtitle = __('%1$s Translate %2$s it into your own language', '', ''); - - const show_client_info = api.settings.get('show_client_info'); - const allow_adhoc_commands = api.settings.get('allow_adhoc_commands'); - const show_both_tabs = show_client_info && allow_adhoc_commands; - return T` - -`; -}); -;// CONCATENATED MODULE: ./src/modals/user-settings.js - - - -let user_settings_converse; - -/* harmony default export */ const modals_user_settings = (base.extend({ - id: "converse-client-info-modal", - - initialize(settings) { - user_settings_converse = settings._converse; - base.prototype.initialize.apply(this, arguments); - }, - - toHTML() { - return templates_user_settings(Object.assign(this.model.toJSON(), this.model.vcard.toJSON(), { - 'version_name': user_settings_converse.VERSION_NAME - })); - } - -})); -;// CONCATENATED MODULE: ./src/plugins/profile/templates/profile.js - - - -/* harmony default export */ const profile = (o => { - const i18n_logout = __('Log out'); - - const i18n_change_status = __('Click to change your chat status'); - - const i18n_details = __('Show details about this chat client'); - - const show_settings_button = api.settings.get('show_client_info') || api.settings.get('allow_adhoc_commands'); - return T` -
    -
    - - - - ${o.fullname} - ${show_settings_button ? T`` : ''} - ${api.settings.get('allow_logout') ? T`` : ''} -
    - -
    -`; -}); -;// CONCATENATED MODULE: ./src/plugins/profile/statusview.js - - - - - - - -function getPrettyStatus(stat) { - if (stat === 'chat') { - return __('online'); - } else if (stat === 'dnd') { - return __('busy'); - } else if (stat === 'xa') { - return __('away for long'); - } else if (stat === 'away') { - return __('away'); - } else if (stat === 'offline') { - return __('offline'); - } else { - return __(stat) || __('online'); - } -} - -class ProfileView extends ElementViewWithAvatar { - async initialize() { - this.model = shared_converse.xmppstatus; - this.listenTo(this.model, "change", this.render); - await api.waitUntil('VCardsInitialized'); - this.listenTo(this.model.vcard, "change", this.render); - this.render(); - } - - render() { - const chat_status = this.model.get('status') || 'offline'; - V(profile(Object.assign(this.model.toJSON(), this.model.vcard.toJSON(), { - chat_status, - 'fullname': this.model.vcard.get('fullname') || shared_converse.bare_jid, - "showUserSettingsModal": ev => this.showUserSettingsModal(ev), - 'status_message': this.model.get('status_message') || __("I am %1$s", getPrettyStatus(chat_status)), - 'logout': this.logout, - 'showStatusChangeModal': () => this.showStatusChangeModal(), - 'showProfileModal': () => this.showProfileModal() - })), this); - this.renderAvatar(); - } - - showProfileModal(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - api.modal.show(shared_converse.ProfileModal, { - model: this.model - }, ev); - } - - showStatusChangeModal(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - api.modal.show(shared_converse.ChatStatusModal, { - model: this.model - }, ev); - } - - showUserSettingsModal(ev) { - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - api.modal.show(modals_user_settings, { - model: this.model, - _converse: shared_converse - }, ev); - } - - logout(ev) { - // eslint-disable-line class-methods-use-this - ev === null || ev === void 0 ? void 0 : ev.preventDefault(); - const result = confirm(__("Are you sure you want to log out?")); - - if (result === true) { - api.user.logout(); - } - } - -} - -api.elements.define('converse-user-profile', ProfileView); -;// CONCATENATED MODULE: ./src/modals/templates/chat-status.js - - -/* harmony default export */ const chat_status = (o => T` - -`); -;// CONCATENATED MODULE: ./src/modals/chat-status.js - - - - -const chat_status_u = core_converse.env.utils; -const ChatStatusModal = base.extend({ - id: "modal-status-change", - events: { - "submit form#set-xmpp-status": "onFormSubmitted", - "click .clear-input": "clearStatusMessage" - }, - - toHTML() { - return chat_status(Object.assign(this.model.toJSON(), this.model.vcard.toJSON(), { - 'label_away': __('Away'), - 'label_busy': __('Busy'), - 'label_cancel': __('Cancel'), - 'label_close': __('Close'), - 'label_custom_status': __('Custom status'), - 'label_offline': __('Offline'), - 'label_online': __('Online'), - 'label_save': __('Save'), - 'label_xa': __('Away for long'), - 'modal_title': __('Change chat status'), - 'placeholder_status_message': __('Personal status message') - })); - }, - - afterRender() { - this.el.addEventListener('shown.bs.modal', () => { - this.el.querySelector('input[name="status_message"]').focus(); - }, false); - }, - - clearStatusMessage(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); - chat_status_u.hideElement(this.el.querySelector('.clear-input')); - } - - const roster_filter = this.el.querySelector('input[name="status_message"]'); - roster_filter.value = ''; - }, - - onFormSubmitted(ev) { - ev.preventDefault(); - const data = new FormData(ev.target); - this.model.save({ - 'status_message': data.get('status_message'), - 'status': data.get('chat_status') - }); - this.modal.hide(); - } - -}); -shared_converse.ChatStatusModal = ChatStatusModal; -/* harmony default export */ const modals_chat_status = ((/* unused pure expression or super */ null && (ChatStatusModal))); -;// CONCATENATED MODULE: ./src/shared/components/image-picker.js - - - - - - -const i18n_profile_picture = __('Your profile picture'); - -class ImagePicker extends CustomElement { - static get properties() { - return { - 'height': { - type: Number - }, - 'image': { - type: String - }, - 'width': { - type: Number - } - }; - } - - render() { - const avatar_data = { - 'height': this.height, - 'image': this.image, - 'width': this.width - }; - return T` - - ${renderAvatar(avatar_data)} - - - `; - } - - openFileSelection(ev) { - ev.preventDefault(); - this.querySelector('input[type="file"]').click(); - } - - updateFilePreview(ev) { - const file = ev.target.files[0]; - const reader = new FileReader(); - - reader.onloadend = () => this.image = reader.result; - - reader.readAsDataURL(file); - } - -} -api.elements.define('converse-image-picker', ImagePicker); -;// CONCATENATED MODULE: ./src/modals/templates/profile.js - - - - - - -const profile_u = core_converse.env.utils; - -const fingerprint = o => T` - ${profile_u.formatFingerprint(o.view.current_device.get('bundle').fingerprint)}`; - -const device_with_fingerprint = o => { - const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following fingerprint'); - - return T` -
  • - -
  • - `; -}; - -const device_without_fingerprint = o => { - const i18n_device_without_fingerprint = __('Device without a fingerprint'); - - const i18n_fingerprint_checkbox_label = __('Checkbox for selecting the following device'); - - return T` -
  • - -
  • - `; -}; - -const device_item = o => T` - ${o.device.get('bundle') && o.device.get('bundle').fingerprint ? device_with_fingerprint(o) : device_without_fingerprint(o)} -`; - -const device_list = o => { - var _o$view$other_devices; - - const i18n_other_devices = __('Other OMEMO-enabled devices'); - - const i18n_other_devices_label = __('Checkbox to select fingerprints of all other OMEMO devices'); - - const i18n_remove_devices = __('Remove checked devices and close'); - - const i18n_select_all = __('Select all'); - - return T` -
      -
    • - -
    • - ${(_o$view$other_devices = o.view.other_devices) === null || _o$view$other_devices === void 0 ? void 0 : _o$view$other_devices.map(device => device_item(Object.assign({ - device - }, o)))} -
    -
    - `; -}; // TODO: this needs to go as a component into the OMEMO plugin folder - - -const omemo_page = o => { - var _o$view$other_devices2; - - const i18n_fingerprint = __("This device's OMEMO fingerprint"); - - const i18n_generate = __('Generate new keys and fingerprint'); - - return T` -
    -
    -
      -
    • ${i18n_fingerprint}
    • -
    • - ${o.view.current_device && o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint ? fingerprint(o) : spinner()} -
    • -
    -
    - -
    - ${(_o$view$other_devices2 = o.view.other_devices) !== null && _o$view$other_devices2 !== void 0 && _o$view$other_devices2.length ? device_list(o) : ''} -
    -
    `; -}; - -/* harmony default export */ const templates_profile = (o => { - const heading_profile = __('Your Profile'); - - const i18n_email = __('Email'); - - const i18n_fullname = __('Full Name'); - - const i18n_jid = __('XMPP Address'); - - const i18n_nickname = __('Nickname'); - - const i18n_role = __('Role'); - - const i18n_save = __('Save and close'); - - const i18n_role_help = __('Use commas to separate multiple roles. Your roles are shown next to your name on your chat messages.'); - - const i18n_url = __('URL'); - - const i18n_omemo = __('OMEMO'); - - const i18n_profile = __('Profile'); - - const navigation = o.view.current_device ? T`` : ''; - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/modals/profile.js - - - - - - -const { - sizzle: profile_sizzle -} = converse.env; -const ProfileModal = base.extend({ - id: "user-profile-modal", - events: { - 'submit .profile-form': 'onFormSubmitted' - }, - - initialize() { - this.listenTo(this.model, 'change', this.render); - base.prototype.initialize.apply(this, arguments); - /** - * Triggered when the _converse.ProfileModal has been created and initialized. - * @event _converse#profileModalInitialized - * @type { _converse.XMPPStatus } - * @example _converse.api.listen.on('profileModalInitialized', status => { ... }); - */ - - api.trigger('profileModalInitialized', this.model); - }, - - toHTML() { - return templates_profile(Object.assign(this.model.toJSON(), this.model.vcard.toJSON(), this.getAvatarData(), { - 'view': this - })); - }, - - getAvatarData() { - const image_type = this.model.vcard.get('image_type'); - const image_data = this.model.vcard.get('image'); - const image = "data:" + image_type + ";base64," + image_data; - return { - 'height': 128, - 'width': 128, - image - }; - }, - - afterRender() { - this.tabs = profile_sizzle('.nav-item .nav-link', this.el).map(e => new (bootstrap_native_default()).Tab(e)); - }, - - async setVCard(data) { - try { - await api.vcard.set(shared_converse.bare_jid, data); - } catch (err) { - headless_log.fatal(err); - this.alert([__("Sorry, an error happened while trying to save your profile data."), __("You can check your browser's developer console for any error output.")].join(" ")); - return; - } - - this.modal.hide(); - }, - - onFormSubmitted(ev) { - ev.preventDefault(); - const reader = new FileReader(); - const form_data = new FormData(ev.target); - const image_file = form_data.get('image'); - const data = { - 'fn': form_data.get('fn'), - 'nickname': form_data.get('nickname'), - 'role': form_data.get('role'), - 'email': form_data.get('email'), - 'url': form_data.get('url') - }; - - if (!image_file.size) { - Object.assign(data, { - 'image': this.model.vcard.get('image'), - 'image_type': this.model.vcard.get('image_type') - }); - this.setVCard(data); - } else { - reader.onloadend = () => { - Object.assign(data, { - 'image': btoa(reader.result), - 'image_type': image_file.type - }); - this.setVCard(data); - }; - - reader.readAsBinaryString(image_file); - } - } - -}); -shared_converse.ProfileModal = ProfileModal; -/* harmony default export */ const modals_profile = ((/* unused pure expression or super */ null && (ProfileModal))); -;// CONCATENATED MODULE: ./src/plugins/profile/index.js -/** - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - -core_converse.plugins.add('converse-profile', { - dependencies: ["converse-status", "converse-modal", "converse-vcard", "converse-chatboxviews"], - - initialize() { - api.settings.extend({ - 'allow_adhoc_commands': true, - 'show_client_info': true - }); - } - -}); -;// CONCATENATED MODULE: ./src/utils/file.js -const MIMETYPES_MAP = { - 'aac': 'audio/aac', - 'abw': 'application/x-abiword', - 'arc': 'application/x-freearc', - 'avi': 'video/x-msvideo', - 'azw': 'application/vnd.amazon.ebook', - 'bin': 'application/octet-stream', - 'bmp': 'image/bmp', - 'bz': 'application/x-bzip', - 'bz2': 'application/x-bzip2', - 'cda': 'application/x-cdf', - 'csh': 'application/x-csh', - 'css': 'text/css', - 'csv': 'text/csv', - 'doc': 'application/msword', - 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'eot': 'application/vnd.ms-fontobject', - 'epub': 'application/epub+zip', - 'gif': 'image/gif', - 'gz': 'application/gzip', - 'htm': 'text/html', - 'html': 'text/html', - 'ico': 'image/vnd.microsoft.icon', - 'ics': 'text/calendar', - 'jar': 'application/java-archive', - 'jpeg': 'image/jpeg', - 'jpg': 'image/jpeg', - 'js': 'text/javascript', - 'json': 'application/json', - 'jsonld': 'application/ld+json', - 'm4a': 'audio/mp4', - 'mid': 'audio/midi', - 'midi': 'audio/midi', - 'mjs': 'text/javascript', - 'mp3': 'audio/mpeg', - 'mp4': 'video/mp4', - 'mpeg': 'video/mpeg', - 'mpkg': 'application/vnd.apple.installer+xml', - 'odp': 'application/vnd.oasis.opendocument.presentation', - 'ods': 'application/vnd.oasis.opendocument.spreadsheet', - 'odt': 'application/vnd.oasis.opendocument.text', - 'oga': 'audio/ogg', - 'ogv': 'video/ogg', - 'ogx': 'application/ogg', - 'opus': 'audio/opus', - 'otf': 'font/otf', - 'png': 'image/png', - 'pdf': 'application/pdf', - 'php': 'application/x-httpd-php', - 'ppt': 'application/vnd.ms-powerpoint', - 'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'rar': 'application/vnd.rar', - 'rtf': 'application/rtf', - 'sh': 'application/x-sh', - 'svg': 'image/svg+xml', - 'swf': 'application/x-shockwave-flash', - 'tar': 'application/x-tar', - 'tif': 'image/tiff', - 'tiff': 'image/tiff', - 'ts': 'video/mp2t', - 'ttf': 'font/ttf', - 'txt': 'text/plain', - 'vsd': 'application/vnd.visio', - 'wav': 'audio/wav', - 'weba': 'audio/webm', - 'webm': 'video/webm', - 'webp': 'image/webp', - 'woff': 'font/woff', - 'woff2': 'font/woff2', - 'xhtml': 'application/xhtml+xml', - 'xls': 'application/vnd.ms-excel', - 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xml': 'text/xml', - 'xul': 'application/vnd.mozilla.xul+xml', - 'zip': 'application/zip', - '3gp': 'video/3gpp', - '3g2': 'video/3gpp2', - '7z': 'application/x-7z-compressed' -}; -;// CONCATENATED MODULE: ./src/plugins/omemo/consts.js -const UNDECIDED = 0; -const TRUSTED = 1; -const UNTRUSTED = -1; -const TAG_LENGTH = 128; -const KEY_ALGO = { - 'name': 'AES-GCM', - 'length': 128 -}; -;// CONCATENATED MODULE: ./node_modules/lodash-es/concat.js - - - - -/** - * Creates a new array concatenating `array` with any additional arrays - * and/or values. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Array - * @param {Array} array The array to concatenate. - * @param {...*} [values] The values to concatenate. - * @returns {Array} Returns the new concatenated array. - * @example - * - * var array = [1]; - * var other = _.concat(array, 2, [3], [[4]]); - * - * console.log(other); - * // => [1, 2, 3, [4]] - * - * console.log(array); - * // => [1] - */ - -function concat() { - var length = arguments.length; - - if (!length) { - return []; - } - - var args = Array(length - 1), - array = arguments[0], - index = length; - - while (index--) { - args[index - 1] = arguments[index]; - } - - return _arrayPush(lodash_es_isArray(array) ? _copyArray(array) : [array], _baseFlatten(args, 1)); -} - -/* harmony default export */ const lodash_es_concat = (concat); -;// CONCATENATED MODULE: ./src/headless/utils/arraybuffer.js - -const { - u: arraybuffer_u -} = core_converse.env; -function appendArrayBuffer(buffer1, buffer2) { - const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); - tmp.set(new Uint8Array(buffer1), 0); - tmp.set(new Uint8Array(buffer2), buffer1.byteLength); - return tmp.buffer; -} -function arrayBufferToHex(ab) { - // https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex#40031979 - return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join(''); -} -function arrayBufferToString(ab) { - return new TextDecoder("utf-8").decode(ab); -} -function stringToArrayBuffer(string) { - const bytes = new TextEncoder("utf-8").encode(string); - return bytes.buffer; -} -function arrayBufferToBase64(ab) { - return btoa(new Uint8Array(ab).reduce((data, byte) => data + String.fromCharCode(byte), '')); -} -function base64ToArrayBuffer(b64) { - const binary_string = window.atob(b64), - len = binary_string.length, - bytes = new Uint8Array(len); - - for (let i = 0; i < len; i++) { - bytes[i] = binary_string.charCodeAt(i); - } - - return bytes.buffer; -} -function hexToArrayBuffer(hex) { - const typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))); - return typedArray.buffer; -} -Object.assign(arraybuffer_u, { - arrayBufferToHex, - arrayBufferToString, - stringToArrayBuffer, - arrayBufferToBase64, - base64ToArrayBuffer -}); -;// CONCATENATED MODULE: ./src/plugins/omemo/utils.js -/* global libsignal */ - - - - - - - - - - - - - - - - -const { - $msg: utils_$msg, - Strophe: omemo_utils_Strophe, - URI: utils_URI, - sizzle: omemo_utils_sizzle, - u: omemo_utils_u -} = core_converse.env; - -async function encryptMessage(plaintext) { - // The client MUST use fresh, randomly generated key/IV pairs - // with AES-128 in Galois/Counter Mode (GCM). - // For GCM a 12 byte IV is strongly suggested as other IV lengths - // will require additional calculations. In principle any IV size - // can be used as long as the IV doesn't ever repeat. NIST however - // suggests that only an IV size of 12 bytes needs to be supported - // by implementations. - // - // https://crypto.stackexchange.com/questions/26783/ciphertext-and-tag-size-and-iv-transmission-with-aes-in-gcm-mode - const iv = crypto.getRandomValues(new window.Uint8Array(12)); - const key = await crypto.subtle.generateKey(KEY_ALGO, true, ['encrypt', 'decrypt']); - const algo = { - 'name': 'AES-GCM', - 'iv': iv, - 'tagLength': TAG_LENGTH - }; - const encrypted = await crypto.subtle.encrypt(algo, key, stringToArrayBuffer(plaintext)); - const length = encrypted.byteLength - (128 + 7 >> 3); - const ciphertext = encrypted.slice(0, length); - const tag = encrypted.slice(length); - const exported_key = await crypto.subtle.exportKey('raw', key); - return { - 'key': exported_key, - 'tag': tag, - 'key_and_tag': appendArrayBuffer(exported_key, tag), - 'payload': arrayBufferToBase64(ciphertext), - 'iv': arrayBufferToBase64(iv) - }; -} - -async function decryptMessage(obj) { - const key_obj = await crypto.subtle.importKey('raw', obj.key, KEY_ALGO, true, ['encrypt', 'decrypt']); - const cipher = appendArrayBuffer(base64ToArrayBuffer(obj.payload), obj.tag); - const algo = { - 'name': 'AES-GCM', - 'iv': base64ToArrayBuffer(obj.iv), - 'tagLength': TAG_LENGTH - }; - return arrayBufferToString(await crypto.subtle.decrypt(algo, key_obj, cipher)); -} - -const omemo = { - decryptMessage, - encryptMessage -}; -async function encryptFile(file) { - const iv = crypto.getRandomValues(new Uint8Array(12)); - const key = await crypto.subtle.generateKey({ - name: 'AES-GCM', - length: 256 - }, true, ['encrypt', 'decrypt']); - const encrypted = await crypto.subtle.encrypt({ - name: 'AES-GCM', - iv - }, key, await file.arrayBuffer()); - const exported_key = await window.crypto.subtle.exportKey('raw', key); - const encrypted_file = new File([encrypted], file.name, { - type: file.type, - lastModified: file.lastModified - }); - encrypted_file.xep454_ivkey = arrayBufferToHex(iv) + arrayBufferToHex(exported_key); - return encrypted_file; -} -function setEncryptedFileURL(message, attrs) { - const url = attrs.oob_url.replace(/^https?:/, 'aesgcm:') + '#' + message.file.xep454_ivkey; - return Object.assign(attrs, { - 'oob_url': null, - // Since only the body gets encrypted, we don't set the oob_url - 'message': url, - 'body': url - }); -} - -async function decryptFile(iv, key, cipher) { - const key_obj = await crypto.subtle.importKey('raw', hexToArrayBuffer(key), 'AES-GCM', false, ['decrypt']); - const algo = { - 'name': 'AES-GCM', - 'iv': hexToArrayBuffer(iv) - }; - return crypto.subtle.decrypt(algo, key_obj, cipher); -} - -async function downloadFile(url) { - let response; - - try { - response = await fetch(url); - } catch (e) { - headless_log.error(`${e.name}: Failed to download encrypted media: ${url}`); - headless_log.error(e); - return null; - } - - if (response.status >= 200 && response.status < 400) { - return response.arrayBuffer(); - } -} - -async function getAndDecryptFile(uri) { - var _uri$filename; - - const hash = uri.hash().slice(1); - const protocol = window.location.hostname === 'localhost' ? 'http' : 'https'; - const http_url = uri.toString().replace(/^aesgcm/, protocol); - const cipher = await downloadFile(http_url); - - if (cipher === null) { - headless_log.error(`Could not decrypt file ${uri.toString()} since it could not be downloaded`); - return null; - } - - const iv = hash.slice(0, 24); - const key = hash.slice(24); - let content; - - try { - content = await decryptFile(iv, key, cipher); - } catch (e) { - headless_log.error(`Could not decrypt file ${uri.toString()}`); - headless_log.error(e); - return null; - } - - const [filename, extension] = (_uri$filename = uri.filename()) === null || _uri$filename === void 0 ? void 0 : _uri$filename.split('.'); - const mimetype = MIMETYPES_MAP[extension]; - - try { - const file = new File([content], filename, { - 'type': mimetype - }); - return URL.createObjectURL(file); - } catch (e) { - headless_log.error(`Could not decrypt file ${uri.toString()}`); - headless_log.error(e); - return null; - } -} - -function getTemplateForObjectURL(uri, obj_url, richtext) { - const file_url = uri.toString(); - - if (obj_url === null) { - return file_url; - } - - if (isImageURL(file_url)) { - return src_templates_image({ - 'url': obj_url, - 'onClick': richtext.onImgClick, - 'onLoad': richtext.onImgLoad - }); - } else if (isAudioURL(file_url)) { - return audio(obj_url); - } else if (isVideoURL(file_url)) { - return video(obj_url); - } else { - return file(obj_url, uri.filename()); - } -} - -function addEncryptedFiles(text, offset, richtext) { - const objs = []; - - try { - const parse_options = { - 'start': /\b(aesgcm:\/\/)/gi - }; - utils_URI.withinString(text, (url, start, end) => { - objs.push({ - url, - start, - end - }); - return url; - }, parse_options); - } catch (error) { - headless_log.debug(error); - return; - } - - objs.forEach(o => { - const uri = getURI(text.slice(o.start, o.end)); - const promise = getAndDecryptFile(uri).then(obj_url => getTemplateForObjectURL(uri, obj_url, richtext)); - const template = T`${until_o(promise, '')}`; - richtext.addTemplateResult(o.start + offset, o.end + offset, template); - }); -} - -function handleEncryptedFiles(richtext) { - if (!shared_converse.config.get('trusted')) { - return; - } - - richtext.addAnnotations((text, offset) => addEncryptedFiles(text, offset, richtext)); -} -function parseEncryptedMessage(stanza, attrs) { - if (attrs.is_encrypted && attrs.encrypted.key) { - // https://xmpp.org/extensions/xep-0384.html#usecases-receiving - if (attrs.encrypted.prekey === true) { - return decryptPrekeyWhisperMessage(attrs); - } else { - return decryptWhisperMessage(attrs); - } - } else { - return attrs; - } -} -function utils_onChatBoxesInitialized() { - shared_converse.chatboxes.on('add', chatbox => { - checkOMEMOSupported(chatbox); - - if (chatbox.get('type') === shared_converse.CHATROOMS_TYPE) { - chatbox.occupants.on('add', o => onOccupantAdded(chatbox, o)); - chatbox.features.on('change', () => checkOMEMOSupported(chatbox)); - } - }); -} -function onChatInitialized(el) { - el.listenTo(el.model.messages, 'add', message => { - if (message.get('is_encrypted') && !message.get('is_error')) { - el.model.save('omemo_supported', true); - } - }); - el.listenTo(el.model, 'change:omemo_supported', () => { - if (!el.model.get('omemo_supported') && el.model.get('omemo_active')) { - el.model.set('omemo_active', false); - } else { - var _el$querySelector; - - // Manually trigger an update, setting omemo_active to - // false above will automatically trigger one. - (_el$querySelector = el.querySelector('converse-chat-toolbar')) === null || _el$querySelector === void 0 ? void 0 : _el$querySelector.requestUpdate(); - } - }); - el.listenTo(el.model, 'change:omemo_active', () => { - el.querySelector('converse-chat-toolbar').requestUpdate(); - }); -} -function getSessionCipher(jid, id) { - const address = new libsignal.SignalProtocolAddress(jid, id); - return new window.libsignal.SessionCipher(shared_converse.omemo_store, address); -} - -async function handleDecryptedWhisperMessage(attrs, key_and_tag) { - const encrypted = attrs.encrypted; - - const devicelist = shared_converse.devicelists.getDeviceList(attrs.from); - - await devicelist._devices_promise; - let device = devicelist.get(encrypted.device_id); - - if (!device) { - device = await devicelist.devices.create({ - 'id': encrypted.device_id, - 'jid': attrs.from - }, { - 'promise': true - }); - } - - if (encrypted.payload) { - const key = key_and_tag.slice(0, 16); - const tag = key_and_tag.slice(16); - const result = await omemo.decryptMessage(Object.assign(encrypted, { - 'key': key, - 'tag': tag - })); - device.save('active', true); - return result; - } -} - -function getDecryptionErrorAttributes(e) { - if (api.settings.get('loglevel') === 'debug') { - return { - 'error_text': __('Sorry, could not decrypt a received OMEMO message due to an error.') + ` ${e.name} ${e.message}`, - 'error_type': 'Decryption', - 'is_ephemeral': true, - 'is_error': true, - 'type': 'error' - }; - } else { - return {}; - } -} - -async function decryptPrekeyWhisperMessage(attrs) { - const session_cipher = getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10)); - const key = base64ToArrayBuffer(attrs.encrypted.key); - let key_and_tag; - - try { - key_and_tag = await session_cipher.decryptPreKeyWhisperMessage(key, 'binary'); - } catch (e) { - // TODO from the XEP: - // There are various reasons why decryption of an - // OMEMOKeyExchange or an OMEMOAuthenticatedMessage - // could fail. One reason is if the message was - // received twice and already decrypted once, in this - // case the client MUST ignore the decryption failure - // and not show any warnings/errors. In all other cases - // of decryption failure, clients SHOULD respond by - // forcibly doing a new key exchange and sending a new - // OMEMOKeyExchange with a potentially empty SCE - // payload. By building a new session with the original - // sender this way, the invalid session of the original - // sender will get overwritten with this newly created, - // valid session. - headless_log.error(`${e.name} ${e.message}`); - return Object.assign(attrs, getDecryptionErrorAttributes(e)); - } // TODO from the XEP: - // When a client receives the first message for a given - // ratchet key with a counter of 53 or higher, it MUST send - // a heartbeat message. Heartbeat messages are normal OMEMO - // encrypted messages where the SCE payload does not include - // any elements. These heartbeat messages cause the ratchet - // to forward, thus consequent messages will have the - // counter restarted from 0. - - - try { - const plaintext = await handleDecryptedWhisperMessage(attrs, key_and_tag); - await shared_converse.omemo_store.generateMissingPreKeys(); - await shared_converse.omemo_store.publishBundle(); - - if (plaintext) { - return Object.assign(attrs, { - 'plaintext': plaintext - }); - } else { - return Object.assign(attrs, { - 'is_only_key': true - }); - } - } catch (e) { - headless_log.error(`${e.name} ${e.message}`); - return Object.assign(attrs, getDecryptionErrorAttributes(e)); - } -} - -async function decryptWhisperMessage(attrs) { - const from_jid = attrs.from_muc ? attrs.from_real_jid : attrs.from; - - if (!from_jid) { - Object.assign(attrs, { - 'error_text': __("Sorry, could not decrypt a received OMEMO message because we don't have the XMPP address for that user."), - 'error_type': 'Decryption', - 'is_ephemeral': false, - 'is_error': true, - 'type': 'error' - }); - } - - const session_cipher = getSessionCipher(from_jid, parseInt(attrs.encrypted.device_id, 10)); - const key = base64ToArrayBuffer(attrs.encrypted.key); - - try { - const key_and_tag = await session_cipher.decryptWhisperMessage(key, 'binary'); - const plaintext = await handleDecryptedWhisperMessage(attrs, key_and_tag); - return Object.assign(attrs, { - 'plaintext': plaintext - }); - } catch (e) { - headless_log.error(`${e.name} ${e.message}`); - return Object.assign(attrs, getDecryptionErrorAttributes(e)); - } -} - -function addKeysToMessageStanza(stanza, dicts, iv) { - for (const i in dicts) { - if (Object.prototype.hasOwnProperty.call(dicts, i)) { - const payload = dicts[i].payload; - const device = dicts[i].device; - const prekey = 3 == parseInt(payload.type, 10); - stanza.c('key', { - 'rid': device.get('id') - }).t(btoa(payload.body)); - - if (prekey) { - stanza.attrs({ - 'prekey': prekey - }); - } - - stanza.up(); - - if (i == dicts.length - 1) { - stanza.c('iv').t(iv).up().up(); - } - } - } - - return Promise.resolve(stanza); -} -/** - * Given an XML element representing a user's OMEMO bundle, parse it - * and return a map. - */ - -function parseBundle(bundle_el) { - const signed_prekey_public_el = bundle_el.querySelector('signedPreKeyPublic'); - const signed_prekey_signature_el = bundle_el.querySelector('signedPreKeySignature'); - const prekeys = omemo_utils_sizzle(`prekeys > preKeyPublic`, bundle_el).map(el => ({ - 'id': parseInt(el.getAttribute('preKeyId'), 10), - 'key': el.textContent - })); - return { - 'identity_key': bundle_el.querySelector('identityKey').textContent.trim(), - 'signed_prekey': { - 'id': parseInt(signed_prekey_public_el.getAttribute('signedPreKeyId'), 10), - 'public_key': signed_prekey_public_el.textContent, - 'signature': signed_prekey_signature_el.textContent - }, - 'prekeys': prekeys - }; -} -async function generateFingerprint(device) { - var _device$get; - - if ((_device$get = device.get('bundle')) !== null && _device$get !== void 0 && _device$get.fingerprint) { - return; - } - - const bundle = await device.getBundle(); - bundle['fingerprint'] = arrayBufferToHex(base64ToArrayBuffer(bundle['identity_key'])); - device.save('bundle', bundle); - device.trigger('change:bundle'); // Doesn't get triggered automatically due to pass-by-reference -} -async function getDevicesForContact(jid) { - await api.waitUntil('OMEMOInitialized'); - - const devicelist = shared_converse.devicelists.get(jid) || shared_converse.devicelists.create({ - 'jid': jid - }); - - await devicelist.fetchDevices(); - return devicelist.devices; -} -function generateDeviceID() { - /* Generates a device ID, making sure that it's unique */ - const existing_ids = shared_converse.devicelists.get(shared_converse.bare_jid).devices.pluck('id'); - - let device_id = libsignal.KeyHelper.generateRegistrationId(); // Before publishing a freshly generated device id for the first time, - // a device MUST check whether that device id already exists, and if so, generate a new one. - - let i = 0; - - while (existing_ids.includes(device_id)) { - device_id = libsignal.KeyHelper.generateRegistrationId(); - i++; - - if (i === 10) { - throw new Error('Unable to generate a unique device ID'); - } - } - - return device_id.toString(); -} - -async function buildSession(device) { - // TODO: check device-get('jid') versus the 'from' attribute which is used - // to build a session when receiving an encrypted message in a MUC. - // https://github.com/conversejs/converse.js/issues/1481#issuecomment-509183431 - const address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id')); - const sessionBuilder = new libsignal.SessionBuilder(shared_converse.omemo_store, address); - const prekey = device.getRandomPreKey(); - const bundle = await device.getBundle(); - return sessionBuilder.processPreKey({ - 'registrationId': parseInt(device.get('id'), 10), - 'identityKey': base64ToArrayBuffer(bundle.identity_key), - 'signedPreKey': { - 'keyId': bundle.signed_prekey.id, - // - 'publicKey': base64ToArrayBuffer(bundle.signed_prekey.public_key), - 'signature': base64ToArrayBuffer(bundle.signed_prekey.signature) - }, - 'preKey': { - 'keyId': prekey.id, - // - 'publicKey': base64ToArrayBuffer(prekey.key) - } - }); -} - -async function getSession(device) { - if (!device.get('bundle')) { - headless_log.error(`Could not build an OMEMO session for device ${device.get('id')} because we don't have its bundle`); - return null; - } - - const address = new libsignal.SignalProtocolAddress(device.get('jid'), device.get('id')); - const session = await shared_converse.omemo_store.loadSession(address.toString()); - - if (session) { - return session; - } else { - try { - const session = await buildSession(device); - return session; - } catch (e) { - headless_log.error(`Could not build an OMEMO session for device ${device.get('id')}`); - headless_log.error(e); - return null; - } - } -} - -function updateBundleFromStanza(stanza) { - const items_el = omemo_utils_sizzle(`items`, stanza).pop(); - - if (!items_el || !items_el.getAttribute('node').startsWith(omemo_utils_Strophe.NS.OMEMO_BUNDLES)) { - return; - } - - const device_id = items_el.getAttribute('node').split(':')[1]; - const jid = stanza.getAttribute('from'); - const bundle_el = omemo_utils_sizzle(`item > bundle`, items_el).pop(); - - const devicelist = shared_converse.devicelists.getDeviceList(jid); - - const device = devicelist.devices.get(device_id) || devicelist.devices.create({ - 'id': device_id, - 'jid': jid - }); - device.save({ - 'bundle': parseBundle(bundle_el) - }); -} - -function updateDevicesFromStanza(stanza) { - const items_el = omemo_utils_sizzle(`items[node="${omemo_utils_Strophe.NS.OMEMO_DEVICELIST}"]`, stanza).pop(); - - if (!items_el) { - return; - } - - const device_selector = `item list[xmlns="${omemo_utils_Strophe.NS.OMEMO}"] device`; - const device_ids = omemo_utils_sizzle(device_selector, items_el).map(d => d.getAttribute('id')); - const jid = stanza.getAttribute('from'); - - const devicelist = shared_converse.devicelists.getDeviceList(jid); - - const devices = devicelist.devices; - const removed_ids = lodash_es_difference(devices.pluck('id'), device_ids); - removed_ids.forEach(id => { - if (jid === shared_converse.bare_jid && id === shared_converse.omemo_store.get('device_id')) { - return; // We don't set the current device as inactive - } - - devices.get(id).save('active', false); - }); - device_ids.forEach(device_id => { - const device = devices.get(device_id); - - if (device) { - device.save('active', true); - } else { - devices.create({ - 'id': device_id, - 'jid': jid - }); - } - }); - - if (omemo_utils_u.isSameBareJID(jid, shared_converse.bare_jid)) { - // Make sure our own device is on the list - // (i.e. if it was removed, add it again). - devicelist.publishCurrentDevice(device_ids); - } -} - -function registerPEPPushHandler() { - // Add a handler for devices pushed from other connected clients - shared_converse.connection.addHandler(message => { - try { - if (omemo_utils_sizzle(`event[xmlns="${omemo_utils_Strophe.NS.PUBSUB}#event"]`, message).length) { - updateDevicesFromStanza(message); - updateBundleFromStanza(message); - } - } catch (e) { - headless_log.error(e.message); - } - - return true; - }, null, 'message', 'headline'); -} -function restoreOMEMOSession() { - if (shared_converse.omemo_store === undefined) { - const id = `converse.omemosession-${shared_converse.bare_jid}`; - shared_converse.omemo_store = new shared_converse.OMEMOStore({ - id - }); - initStorage(shared_converse.omemo_store, id); - } - - return shared_converse.omemo_store.fetchSession(); -} - -function fetchDeviceLists() { - return new Promise((success, error) => shared_converse.devicelists.fetch({ - success, - 'error': (m, e) => error(e) - })); -} - -async function fetchOwnDevices() { - await fetchDeviceLists(); - - let own_devicelist = shared_converse.devicelists.get(shared_converse.bare_jid); - - if (own_devicelist) { - own_devicelist.fetchDevices(); - } else { - own_devicelist = await shared_converse.devicelists.create({ - 'jid': shared_converse.bare_jid - }, { - 'promise': true - }); - } - - return own_devicelist._devices_promise; -} - -async function initOMEMO() { - if (!shared_converse.config.get('trusted') || api.settings.get('clear_cache_on_logout')) { - headless_log.warn('Not initializing OMEMO, since this browser is not trusted or clear_cache_on_logout is set to true'); - return; - } - - shared_converse.devicelists = new shared_converse.DeviceLists(); - const id = `converse.devicelists-${shared_converse.bare_jid}`; - initStorage(shared_converse.devicelists, id); - - try { - await fetchOwnDevices(); - await restoreOMEMOSession(); - await shared_converse.omemo_store.publishBundle(); - } catch (e) { - headless_log.error('Could not initialize OMEMO support'); - headless_log.error(e); - return; - } - /** - * Triggered once OMEMO support has been initialized - * @event _converse#OMEMOInitialized - * @example _converse.api.listen.on('OMEMOInitialized', () => { ... }); */ - - - api.trigger('OMEMOInitialized'); -} - -async function onOccupantAdded(chatroom, occupant) { - if (occupant.isSelf() || !chatroom.features.get('nonanonymous') || !chatroom.features.get('membersonly')) { - return; - } - - if (chatroom.get('omemo_active')) { - const supported = await shared_converse.contactHasOMEMOSupport(occupant.get('jid')); - - if (!supported) { - chatroom.createMessage({ - 'message': __("%1$s doesn't appear to have a client that supports OMEMO. " + 'Encrypted chat will no longer be possible in this grouchat.', occupant.get('nick')), - 'type': 'error' - }); - chatroom.save({ - 'omemo_active': false, - 'omemo_supported': false - }); - } - } -} - -async function checkOMEMOSupported(chatbox) { - let supported; - - if (chatbox.get('type') === shared_converse.CHATROOMS_TYPE) { - await api.waitUntil('OMEMOInitialized'); - supported = chatbox.features.get('nonanonymous') && chatbox.features.get('membersonly'); - } else if (chatbox.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - supported = await shared_converse.contactHasOMEMOSupport(chatbox.get('jid')); - } - - chatbox.set('omemo_supported', supported); - - if (supported && api.settings.get('omemo_default')) { - chatbox.set('omemo_active', true); - } -} - -function toggleOMEMO(ev) { - ev.stopPropagation(); - ev.preventDefault(); - const toolbar_el = omemo_utils_u.ancestor(ev.target, 'converse-chat-toolbar'); - - if (!toolbar_el.model.get('omemo_supported')) { - let messages; - - if (toolbar_el.model.get('type') === shared_converse.CHATROOMS_TYPE) { - messages = [__('Cannot use end-to-end encryption in this groupchat, ' + 'either the groupchat has some anonymity or not all participants support OMEMO.')]; - } else { - messages = [__("Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.", toolbar_el.model.contact.getDisplayName())]; - } - - return api.alert('error', __('Error'), messages); - } - - toolbar_el.model.save({ - 'omemo_active': !toolbar_el.model.get('omemo_active') - }); -} - -function getOMEMOToolbarButton(toolbar_el, buttons) { - const model = toolbar_el.model; - - const is_muc = model.get('type') === shared_converse.CHATROOMS_TYPE; - - let title; - - if (model.get('omemo_supported')) { - const i18n_plaintext = __('Messages are being sent in plaintext'); - - const i18n_encrypted = __('Messages are sent encrypted'); - - title = model.get('omemo_active') ? i18n_encrypted : i18n_plaintext; - } else if (is_muc) { - title = __('This groupchat needs to be members-only and non-anonymous in ' + 'order to support OMEMO encrypted messages'); - } else { - title = __('OMEMO encryption is not supported'); - } - - let color; - - if (model.get('omemo_supported')) { - color = model.get('omemo_active') ? `var(--info-color)` : `var(--error-color)`; - } else { - color = `var(--muc-toolbar-btn-disabled-color)`; - } - - buttons.push(T` - - `); - return buttons; -} -async function getBundlesAndBuildSessions(chatbox) { - const no_devices_err = __('Sorry, no devices found to which we can send an OMEMO encrypted message.'); - - let devices; - - if (chatbox.get('type') === shared_converse.CHATROOMS_TYPE) { - const collections = await Promise.all(chatbox.occupants.map(o => getDevicesForContact(o.get('jid')))); - devices = collections.reduce((a, b) => lodash_es_concat(a, b.models), []); - } else if (chatbox.get('type') === shared_converse.PRIVATE_CHAT_TYPE) { - const their_devices = await getDevicesForContact(chatbox.get('jid')); - - if (their_devices.length === 0) { - const err = new Error(no_devices_err); - err.user_facing = true; - throw err; - } - - const own_devices = shared_converse.devicelists.get(shared_converse.bare_jid).devices; - - devices = [...own_devices.models, ...their_devices.models]; - } // Filter out our own device - - - const id = shared_converse.omemo_store.get('device_id'); - - devices = devices.filter(d => d.get('id') !== id); // Fetch bundles if necessary - - await Promise.all(devices.map(d => d.getBundle())); - const sessions = devices.filter(d => d).map(d => getSession(d)); - await Promise.all(sessions); - - if (sessions.includes(null)) { - // We couldn't build a session for certain devices. - devices = devices.filter(d => sessions[devices.indexOf(d)]); - - if (devices.length === 0) { - const err = new Error(no_devices_err); - err.user_facing = true; - throw err; - } - } - - return devices; -} -function createOMEMOMessageStanza(chatbox, message, devices) { - const body = __('This is an OMEMO encrypted message which your client doesn’t seem to support. ' + 'Find more information on https://conversations.im/omemo'); - - if (!message.get('message')) { - throw new Error('No message body to encrypt!'); - } - - const stanza = utils_$msg({ - 'from': shared_converse.connection.jid, - 'to': chatbox.get('jid'), - 'type': chatbox.get('message_type'), - 'id': message.get('msgid') - }).c('body').t(body).up(); - - if (message.get('type') === 'chat') { - stanza.c('request', { - 'xmlns': omemo_utils_Strophe.NS.RECEIPTS - }).up(); - } // An encrypted header is added to the message for - // each device that is supposed to receive it. - // These headers simply contain the key that the - // payload message is encrypted with, - // and they are separately encrypted using the - // session corresponding to the counterpart device. - - - stanza.c('encrypted', { - 'xmlns': omemo_utils_Strophe.NS.OMEMO - }).c('header', { - 'sid': shared_converse.omemo_store.get('device_id') - }); - return omemo.encryptMessage(message.get('message')).then(obj => { - // The 16 bytes key and the GCM authentication tag (The tag - // SHOULD have at least 128 bit) are concatenated and for each - // intended recipient device, i.e. both own devices as well as - // devices associated with the contact, the result of this - // concatenation is encrypted using the corresponding - // long-standing SignalProtocol session. - const promises = devices.filter(device => device.get('trusted') != UNTRUSTED && device.get('active')).map(device => chatbox.encryptKey(obj.key_and_tag, device)); - return Promise.all(promises).then(dicts => addKeysToMessageStanza(stanza, dicts, obj.iv)).then(stanza => { - stanza.c('payload').t(obj.payload).up().up(); - stanza.c('store', { - 'xmlns': omemo_utils_Strophe.NS.HINTS - }).up(); - stanza.c('encryption', { - 'xmlns': omemo_utils_Strophe.NS.EME, - namespace: omemo_utils_Strophe.NS.OMEMO - }); - return stanza; - }); - }); -} -;// CONCATENATED MODULE: ./src/plugins/omemo/overrides/chatbox.js - - -const chatbox_ChatBox = { - async sendMessage(attrs) { - var _attrs; - - if (this.get('omemo_active') && (_attrs = attrs) !== null && _attrs !== void 0 && _attrs.body) { - var _attrs2; - - const plaintext = (_attrs2 = attrs) === null || _attrs2 === void 0 ? void 0 : _attrs2.body; - attrs = this.getOutgoingMessageAttributes(attrs); - attrs['is_encrypted'] = true; - attrs['plaintext'] = plaintext; - let message, stanza; - - try { - const devices = await getBundlesAndBuildSessions(this); - message = await this.createMessage(attrs); - stanza = await createOMEMOMessageStanza(this, message, devices); - } catch (e) { - this.handleMessageSendError(e); - return null; - } - - shared_converse.api.send(stanza); - - return message; - } else { - return this.__super__.sendMessage.apply(this, arguments); - } - } - -}; -/* harmony default export */ const chatbox = (chatbox_ChatBox); -;// CONCATENATED MODULE: ./src/plugins/omemo/mixins/converse.js - -const ConverseMixins = { - generateFingerprints: async function (jid) { - const devices = await getDevicesForContact(jid); - return Promise.all(devices.map(d => generateFingerprint(d))); - }, - getDeviceForContact: function (jid, device_id) { - return getDevicesForContact(jid).then(devices => devices.get(device_id)); - }, - contactHasOMEMOSupport: async function (jid) { - /* Checks whether the contact advertises any OMEMO-compatible devices. */ - const devices = await getDevicesForContact(jid); - return devices.length > 0; - } -}; -/* harmony default export */ const mixins_converse = (ConverseMixins); -;// CONCATENATED MODULE: ./src/plugins/omemo/errors.js -class IQError extends Error { - constructor(message, iq) { - super(message, iq); - this.name = 'IQError'; - this.iq = iq; - } - -} -;// CONCATENATED MODULE: ./src/plugins/omemo/device.js - - - - - - -const { - Strophe: device_Strophe, - sizzle: device_sizzle, - u: device_u, - $iq: device_$iq -} = core_converse.env; -/** - * @class - * @namespace _converse.Device - * @memberOf _converse - */ - -const Device = Model.extend({ - defaults: { - 'trusted': UNDECIDED, - 'active': true - }, - - getRandomPreKey() { - // XXX: assumes that the bundle has already been fetched - const bundle = this.get('bundle'); - return bundle.prekeys[device_u.getRandomInt(bundle.prekeys.length)]; - }, - - async fetchBundleFromServer() { - const stanza = device_$iq({ - 'type': 'get', - 'from': shared_converse.bare_jid, - 'to': this.get('jid') - }).c('pubsub', { - 'xmlns': device_Strophe.NS.PUBSUB - }).c('items', { - 'node': `${device_Strophe.NS.OMEMO_BUNDLES}:${this.get('id')}` - }); - let iq; - - try { - iq = await api.sendIQ(stanza); - } catch (iq) { - headless_log.error(`Could not fetch bundle for device ${this.get('id')} from ${this.get('jid')}`); - headless_log.error(iq); - return null; - } - - if (iq.querySelector('error')) { - throw new IQError('Could not fetch bundle', iq); - } - - const publish_el = device_sizzle(`items[node="${device_Strophe.NS.OMEMO_BUNDLES}:${this.get('id')}"]`, iq).pop(); - const bundle_el = device_sizzle(`bundle[xmlns="${device_Strophe.NS.OMEMO}"]`, publish_el).pop(); - const bundle = parseBundle(bundle_el); - this.save('bundle', bundle); - return bundle; - }, - - /** - * Fetch and save the bundle information associated with - * this device, if the information is not cached already. - * @method _converse.Device#getBundle - */ - getBundle() { - if (this.get('bundle')) { - return Promise.resolve(this.get('bundle'), this); - } else { - return this.fetchBundleFromServer(); - } - } - -}); -/* harmony default export */ const device = (Device); -;// CONCATENATED MODULE: ./src/plugins/omemo/devicelist.js - - - - - -const { - Strophe: devicelist_Strophe, - $build: devicelist_$build, - $iq: devicelist_$iq, - sizzle: devicelist_sizzle -} = core_converse.env; -/** - * @class - * @namespace _converse.DeviceList - * @memberOf _converse - */ - -const DeviceList = Model.extend({ - idAttribute: 'jid', - - initialize() { - this.initDevices(); - }, - - initDevices() { - this.devices = new shared_converse.Devices(); - const id = `converse.devicelist-${shared_converse.bare_jid}-${this.get('jid')}`; - initStorage(this.devices, id); - this.fetchDevices(); - }, - - async onDevicesFound(collection) { - if (collection.length === 0) { - let ids; - - try { - ids = await this.fetchDevicesFromServer(); - } catch (e) { - if (e === null) { - headless_log.error(`Timeout error while fetching devices for ${this.get('jid')}`); - } else { - headless_log.error(`Could not fetch devices for ${this.get('jid')}`); - headless_log.error(e); - } - - this.destroy(); - } - - if (this.get('jid') === shared_converse.bare_jid) { - await this.publishCurrentDevice(ids); - } - } - }, - - fetchDevices() { - if (this._devices_promise === undefined) { - this._devices_promise = new Promise(resolve => { - this.devices.fetch({ - 'success': c => resolve(this.onDevicesFound(c)), - 'error': (m, e) => { - headless_log.error(e); - resolve(); - } - }); - }); - } - - return this._devices_promise; - }, - - async getOwnDeviceId() { - let device_id = shared_converse.omemo_store.get('device_id'); - - if (!this.devices.findWhere({ - 'id': device_id - })) { - // Generate a new bundle if we cannot find our device - await shared_converse.omemo_store.generateBundle(); - device_id = shared_converse.omemo_store.get('device_id'); - } - - return device_id; - }, - - async publishCurrentDevice(device_ids) { - if (this.get('jid') !== shared_converse.bare_jid) { - return; // We only publish for ourselves. - } - - await restoreOMEMOSession(); - - if (!shared_converse.omemo_store) { - // Happens during tests. The connection gets torn down - // before publishCurrentDevice has time to finish. - headless_log.warn('publishCurrentDevice: omemo_store is not defined, likely a timing issue'); - return; - } - - if (!device_ids.includes(await this.getOwnDeviceId())) { - return this.publishDevices(); - } - }, - - async fetchDevicesFromServer() { - const stanza = devicelist_$iq({ - 'type': 'get', - 'from': shared_converse.bare_jid, - 'to': this.get('jid') - }).c('pubsub', { - 'xmlns': devicelist_Strophe.NS.PUBSUB - }).c('items', { - 'node': devicelist_Strophe.NS.OMEMO_DEVICELIST - }); - let iq; - - try { - iq = await api.sendIQ(stanza); - } catch (e) { - headless_log.error(e); - return []; - } - - const selector = `list[xmlns="${devicelist_Strophe.NS.OMEMO}"] device`; - const device_ids = devicelist_sizzle(selector, iq).map(d => d.getAttribute('id')); - await Promise.all(device_ids.map(id => this.devices.create({ - id, - 'jid': this.get('jid') - }, { - 'promise': true - }))); - return device_ids; - }, - - /** - * Send an IQ stanza to the current user's "devices" PEP node to - * ensure that all devices are published for potential chat partners to see. - * See: https://xmpp.org/extensions/xep-0384.html#usecases-announcing - */ - publishDevices() { - const item = devicelist_$build('item', { - 'id': 'current' - }).c('list', { - 'xmlns': devicelist_Strophe.NS.OMEMO - }); - this.devices.filter(d => d.get('active')).forEach(d => item.c('device', { - 'id': d.get('id') - }).up()); - const options = { - 'pubsub#access_model': 'open' - }; - return api.pubsub.publish(null, devicelist_Strophe.NS.OMEMO_DEVICELIST, item, options, false); - }, - - removeOwnDevices(device_ids) { - if (this.get('jid') !== shared_converse.bare_jid) { - throw new Error("Cannot remove devices from someone else's device list"); - } - - device_ids.forEach(device_id => this.devices.get(device_id).destroy()); - return this.publishDevices(); - } - -}); -/* harmony default export */ const devicelist = (DeviceList); -;// CONCATENATED MODULE: ./src/plugins/omemo/devicelists.js - - -/** - * @class - * @namespace _converse.DeviceLists - * @memberOf _converse - */ - -const DeviceLists = Collection.extend({ - model: devicelist, - - /** - * Returns the {@link _converse.DeviceList} for a particular JID. - * The device list will be created if it doesn't exist already. - * @private - * @method _converse.DeviceLists#getDeviceList - * @param { String } jid - The Jabber ID for which the device list will be returned. - */ - getDeviceList(jid) { - return this.get(jid) || this.create({ - 'jid': jid - }); - } - -}); -/* harmony default export */ const devicelists = (DeviceLists); -;// CONCATENATED MODULE: ./src/plugins/omemo/devices.js - - -/* harmony default export */ const devices = (Collection.extend({ - model: device -})); -;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseRange.js -/* Built-in method references for those with the same name as other `lodash` methods. */ -var nativeCeil = Math.ceil, - _baseRange_nativeMax = Math.max; -/** - * The base implementation of `_.range` and `_.rangeRight` which doesn't - * coerce arguments. - * - * @private - * @param {number} start The start of the range. - * @param {number} end The end of the range. - * @param {number} step The value to increment or decrement by. - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Array} Returns the range of numbers. - */ - -function baseRange(start, end, step, fromRight) { - var index = -1, - length = _baseRange_nativeMax(nativeCeil((end - start) / (step || 1)), 0), - result = Array(length); - - while (length--) { - result[fromRight ? length : ++index] = start; - start += step; - } - - return result; -} - -/* harmony default export */ const _baseRange = (baseRange); -;// CONCATENATED MODULE: ./node_modules/lodash-es/_createRange.js - - - -/** - * Creates a `_.range` or `_.rangeRight` function. - * - * @private - * @param {boolean} [fromRight] Specify iterating from right to left. - * @returns {Function} Returns the new range function. - */ - -function createRange(fromRight) { - return function (start, end, step) { - if (step && typeof step != 'number' && _isIterateeCall(start, end, step)) { - end = step = undefined; - } // Ensure the sign of `-0` is preserved. - - - start = lodash_es_toFinite(start); - - if (end === undefined) { - end = start; - start = 0; - } else { - end = lodash_es_toFinite(end); - } - - step = step === undefined ? start < end ? 1 : -1 : lodash_es_toFinite(step); - return _baseRange(start, end, step, fromRight); - }; -} - -/* harmony default export */ const _createRange = (createRange); -;// CONCATENATED MODULE: ./node_modules/lodash-es/range.js - -/** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to, but not including, `end`. A step of `-1` is used if a negative - * `start` is specified without an `end` or `step`. If `end` is not specified, - * it's set to `start` with `start` then set to `0`. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the range of numbers. - * @see _.inRange, _.rangeRight - * @example - * - * _.range(4); - * // => [0, 1, 2, 3] - * - * _.range(-4); - * // => [0, -1, -2, -3] - * - * _.range(1, 5); - * // => [1, 2, 3, 4] - * - * _.range(0, 20, 5); - * // => [0, 5, 10, 15] - * - * _.range(0, -4, -1); - * // => [0, -1, -2, -3] - * - * _.range(1, 4, 0); - * // => [1, 1, 1] - * - * _.range(0); - * // => [] - */ - -var range = _createRange(); -/* harmony default export */ const lodash_es_range = (range); -;// CONCATENATED MODULE: ./src/plugins/omemo/store.js -/* global libsignal */ - - - - - - - - -const { - Strophe: store_Strophe, - $build: store_$build, - u: store_u -} = core_converse.env; -const OMEMOStore = Model.extend({ - Direction: { - SENDING: 1, - RECEIVING: 2 - }, - - getIdentityKeyPair() { - const keypair = this.get('identity_keypair'); - return Promise.resolve({ - 'privKey': store_u.base64ToArrayBuffer(keypair.privKey), - 'pubKey': store_u.base64ToArrayBuffer(keypair.pubKey) - }); - }, - - getLocalRegistrationId() { - return Promise.resolve(parseInt(this.get('device_id'), 10)); - }, - - isTrustedIdentity(identifier, identity_key, direction) { - // eslint-disable-line no-unused-vars - if (identifier === null || identifier === undefined) { - throw new Error("Can't check identity key for invalid key"); - } - - if (!(identity_key instanceof ArrayBuffer)) { - throw new Error('Expected identity_key to be an ArrayBuffer'); - } - - const trusted = this.get('identity_key' + identifier); - - if (trusted === undefined) { - return Promise.resolve(true); - } - - return Promise.resolve(store_u.arrayBufferToBase64(identity_key) === trusted); - }, - - loadIdentityKey(identifier) { - if (identifier === null || identifier === undefined) { - throw new Error("Can't load identity_key for invalid identifier"); - } - - return Promise.resolve(store_u.base64ToArrayBuffer(this.get('identity_key' + identifier))); - }, - - saveIdentity(identifier, identity_key) { - if (identifier === null || identifier === undefined) { - throw new Error("Can't save identity_key for invalid identifier"); - } - - const address = new libsignal.SignalProtocolAddress.fromString(identifier); - const existing = this.get('identity_key' + address.getName()); - const b64_idkey = store_u.arrayBufferToBase64(identity_key); - this.save('identity_key' + address.getName(), b64_idkey); - - if (existing && b64_idkey !== existing) { - return Promise.resolve(true); - } else { - return Promise.resolve(false); - } - }, - - getPreKeys() { - return this.get('prekeys') || {}; - }, - - loadPreKey(key_id) { - const res = this.getPreKeys()[key_id]; - - if (res) { - return Promise.resolve({ - 'privKey': store_u.base64ToArrayBuffer(res.privKey), - 'pubKey': store_u.base64ToArrayBuffer(res.pubKey) - }); - } - - return Promise.resolve(); - }, - - storePreKey(key_id, key_pair) { - const prekey = {}; - prekey[key_id] = { - 'pubKey': store_u.arrayBufferToBase64(key_pair.pubKey), - 'privKey': store_u.arrayBufferToBase64(key_pair.privKey) - }; - this.save('prekeys', Object.assign(this.getPreKeys(), prekey)); - return Promise.resolve(); - }, - - removePreKey(key_id) { - this.save('prekeys', lodash_es_omit(this.getPreKeys(), key_id)); - return Promise.resolve(); - }, - - loadSignedPreKey(keyId) { - // eslint-disable-line no-unused-vars - const res = this.get('signed_prekey'); - - if (res) { - return Promise.resolve({ - 'privKey': store_u.base64ToArrayBuffer(res.privKey), - 'pubKey': store_u.base64ToArrayBuffer(res.pubKey) - }); - } - - return Promise.resolve(); - }, - - storeSignedPreKey(spk) { - if (typeof spk !== 'object') { - // XXX: We've changed the signature of this method from the - // example given in InMemorySignalProtocolStore. - // Should be fine because the libsignal code doesn't - // actually call this method. - throw new Error('storeSignedPreKey: expected an object'); - } - - this.save('signed_prekey', { - 'id': spk.keyId, - 'privKey': store_u.arrayBufferToBase64(spk.keyPair.privKey), - 'pubKey': store_u.arrayBufferToBase64(spk.keyPair.pubKey), - // XXX: The InMemorySignalProtocolStore does not pass - // in or store the signature, but we need it when we - // publish out bundle and this method isn't called from - // within libsignal code, so we modify it to also store - // the signature. - 'signature': store_u.arrayBufferToBase64(spk.signature) - }); - return Promise.resolve(); - }, - - removeSignedPreKey(key_id) { - if (this.get('signed_prekey')['id'] === key_id) { - this.unset('signed_prekey'); - this.save(); - } - - return Promise.resolve(); - }, - - loadSession(identifier) { - return Promise.resolve(this.get('session' + identifier)); - }, - - storeSession(identifier, record) { - return Promise.resolve(this.save('session' + identifier, record)); - }, - - removeSession(identifier) { - return Promise.resolve(this.unset('session' + identifier)); - }, - - removeAllSessions(identifier) { - const keys = Object.keys(this.attributes).filter(key => key.startsWith('session' + identifier) ? key : false); - const attrs = {}; - keys.forEach(key => { - attrs[key] = undefined; - }); - this.save(attrs); - return Promise.resolve(); - }, - - publishBundle() { - const signed_prekey = this.get('signed_prekey'); - const node = `${store_Strophe.NS.OMEMO_BUNDLES}:${this.get('device_id')}`; - const item = store_$build('item').c('bundle', { - 'xmlns': store_Strophe.NS.OMEMO - }).c('signedPreKeyPublic', { - 'signedPreKeyId': signed_prekey.id - }).t(signed_prekey.pubKey).up().c('signedPreKeySignature').t(signed_prekey.signature).up().c('identityKey').t(this.get('identity_keypair').pubKey).up().c('prekeys'); - Object.values(this.get('prekeys')).forEach((prekey, id) => item.c('preKeyPublic', { - 'preKeyId': id - }).t(prekey.pubKey).up()); - const options = { - 'pubsub#access_model': 'open' - }; - return api.pubsub.publish(null, node, item, options, false); - }, - - async generateMissingPreKeys() { - const missing_keys = lodash_es_difference(lodash_es_invokeMap(lodash_es_range(0, shared_converse.NUM_PREKEYS), Number.prototype.toString), Object.keys(this.getPreKeys())); - - if (missing_keys.length < 1) { - headless_log.warn('No missing prekeys to generate for our own device'); - return Promise.resolve(); - } - - const keys = await Promise.all(missing_keys.map(id => libsignal.KeyHelper.generatePreKey(parseInt(id, 10)))); - keys.forEach(k => this.storePreKey(k.keyId, k.keyPair)); - const marshalled_keys = Object.keys(this.getPreKeys()).map(k => ({ - 'id': k.keyId, - 'key': store_u.arrayBufferToBase64(k.pubKey) - })); - - const devicelist = shared_converse.devicelists.get(shared_converse.bare_jid); - - const device = devicelist.devices.get(this.get('device_id')); - const bundle = await device.getBundle(); - device.save('bundle', Object.assign(bundle, { - 'prekeys': marshalled_keys - })); - }, - - /** - * Generate a the data used by the X3DH key agreement protocol - * that can be used to build a session with a device. - */ - async generateBundle() { - // The first thing that needs to happen if a client wants to - // start using OMEMO is they need to generate an IdentityKey - // and a Device ID. The IdentityKey is a Curve25519 [6] - // public/private Key pair. The Device ID is a randomly - // generated integer between 1 and 2^31 - 1. - const identity_keypair = await libsignal.KeyHelper.generateIdentityKeyPair(); - const bundle = {}; - const identity_key = store_u.arrayBufferToBase64(identity_keypair.pubKey); - const device_id = generateDeviceID(); - bundle['identity_key'] = identity_key; - bundle['device_id'] = device_id; - this.save({ - 'device_id': device_id, - 'identity_keypair': { - 'privKey': store_u.arrayBufferToBase64(identity_keypair.privKey), - 'pubKey': identity_key - }, - 'identity_key': identity_key - }); - const signed_prekey = await libsignal.KeyHelper.generateSignedPreKey(identity_keypair, 0); - - shared_converse.omemo_store.storeSignedPreKey(signed_prekey); - - bundle['signed_prekey'] = { - 'id': signed_prekey.keyId, - 'public_key': store_u.arrayBufferToBase64(signed_prekey.keyPair.pubKey), - 'signature': store_u.arrayBufferToBase64(signed_prekey.signature) - }; - const keys = await Promise.all(lodash_es_range(0, shared_converse.NUM_PREKEYS).map(id => libsignal.KeyHelper.generatePreKey(id))); - keys.forEach(k => shared_converse.omemo_store.storePreKey(k.keyId, k.keyPair)); - - const devicelist = shared_converse.devicelists.get(shared_converse.bare_jid); - - const device = await devicelist.devices.create({ - 'id': bundle.device_id, - 'jid': shared_converse.bare_jid - }, { - 'promise': true - }); - const marshalled_keys = keys.map(k => ({ - 'id': k.keyId, - 'key': store_u.arrayBufferToBase64(k.keyPair.pubKey) - })); - bundle['prekeys'] = marshalled_keys; - device.save('bundle', bundle); - }, - - fetchSession() { - if (this._setup_promise === undefined) { - this._setup_promise = new Promise((resolve, reject) => { - this.fetch({ - 'success': () => { - if (!shared_converse.omemo_store.get('device_id')) { - this.generateBundle().then(resolve).catch(reject); - } else { - resolve(); - } - }, - 'error': (model, resp) => { - headless_log.warn("Could not fetch OMEMO session from cache, we'll generate a new one."); - headless_log.warn(resp); - this.generateBundle().then(resolve).catch(reject); - } - }); - }); - } - - return this._setup_promise; - } - -}); -/* harmony default export */ const store = (OMEMOStore); -;// CONCATENATED MODULE: ./src/plugins/omemo/overrides/profile-modal.js - - - - -const { - Strophe: profile_modal_Strophe, - sizzle: profile_modal_sizzle, - u: profile_modal_u -} = core_converse.env; -const profile_modal_ProfileModal = { - events: { - 'change input.select-all': 'selectAll', - 'click .generate-bundle': 'generateOMEMODeviceBundle', - 'submit .fingerprint-removal': 'removeSelectedFingerprints' - }, - - initialize() { - this.debouncedRender = lodash_es_debounce(this.render, 50); - this.devicelist = shared_converse.devicelists.get(shared_converse.bare_jid); - this.listenTo(this.devicelist.devices, 'change:bundle', this.debouncedRender); - this.listenTo(this.devicelist.devices, 'reset', this.debouncedRender); - this.listenTo(this.devicelist.devices, 'reset', this.debouncedRender); - this.listenTo(this.devicelist.devices, 'remove', this.debouncedRender); - this.listenTo(this.devicelist.devices, 'add', this.debouncedRender); - return this.__super__.initialize.apply(this, arguments); - }, - - beforeRender() { - var _converse$omemo_store, _this$__super__$befor; - - const device_id = (_converse$omemo_store = shared_converse.omemo_store) === null || _converse$omemo_store === void 0 ? void 0 : _converse$omemo_store.get('device_id'); - - if (device_id) { - this.current_device = this.devicelist.devices.get(device_id); - this.other_devices = this.devicelist.devices.filter(d => d.get('id') !== device_id); - } - - return (_this$__super__$befor = this.__super__.beforeRender) === null || _this$__super__$befor === void 0 ? void 0 : _this$__super__$befor.apply(this, arguments); - }, - - selectAll(ev) { - let sibling = profile_modal_u.ancestor(ev.target, 'li'); - - while (sibling) { - sibling.querySelector('input[type="checkbox"]').checked = ev.target.checked; - sibling = sibling.nextElementSibling; - } - }, - - removeSelectedFingerprints(ev) { - ev.preventDefault(); - ev.stopPropagation(); - ev.target.querySelector('.select-all').checked = false; - const device_ids = profile_modal_sizzle('.fingerprint-removal-item input[type="checkbox"]:checked', ev.target).map(c => c.value); - this.devicelist.removeOwnDevices(device_ids).then(this.modal.hide).catch(err => { - headless_log.error(err); - - shared_converse.api.alert(profile_modal_Strophe.LogLevel.ERROR, __('Error'), [__('Sorry, an error occurred while trying to remove the devices.')]); - }); - }, - - generateOMEMODeviceBundle(ev) { - ev.preventDefault(); - - if (confirm(__('Are you sure you want to generate new OMEMO keys? ' + 'This will remove your old keys and all previously encrypted messages will no longer be decryptable on this device.'))) { - api.omemo.bundle.generate(); - } - } - -}; -/* harmony default export */ const profile_modal = (profile_modal_ProfileModal); -;// CONCATENATED MODULE: ./src/plugins/omemo/overrides/user-details-modal.js - -const user_details_modal_UserDetailsModal = { - events: { - 'click .fingerprint-trust .btn input': 'toggleDeviceTrust' - }, - - initialize() { - const jid = this.model.get('jid'); - this.devicelist = shared_converse.devicelists.getDeviceList(jid); - this.listenTo(this.devicelist.devices, 'change:bundle', this.render); - this.listenTo(this.devicelist.devices, 'change:trusted', this.render); - this.listenTo(this.devicelist.devices, 'remove', this.render); - this.listenTo(this.devicelist.devices, 'add', this.render); - this.listenTo(this.devicelist.devices, 'reset', this.render); - return this.__super__.initialize.apply(this, arguments); - }, - - toggleDeviceTrust(ev) { - const radio = ev.target; - const device = this.devicelist.devices.get(radio.getAttribute('name')); - device.save('trusted', parseInt(radio.value, 10)); - } - -}; -/* harmony default export */ const user_details_modal = (user_details_modal_UserDetailsModal); -;// CONCATENATED MODULE: ./src/plugins/omemo/api.js - - -/* harmony default export */ const omemo_api = ({ - /** - * The "omemo" namespace groups methods relevant to OMEMO - * encryption. - * - * @namespace _converse.api.omemo - * @memberOf _converse.api - */ - 'omemo': { - /** - * The "bundle" namespace groups methods relevant to the user's - * OMEMO bundle. - * - * @namespace _converse.api.omemo.bundle - * @memberOf _converse.api.omemo - */ - 'bundle': { - /** - * Lets you generate a new OMEMO device bundle - * - * @method _converse.api.omemo.bundle.generate - * @returns {promise} Promise which resolves once we have a result from the server. - */ - 'generate': async () => { - // Remove current device - const devicelist = shared_converse.devicelists.get(shared_converse.bare_jid); - - const device_id = shared_converse.omemo_store.get('device_id'); - - if (device_id) { - const device = devicelist.devices.get(device_id); - - shared_converse.omemo_store.unset(device_id); - - if (device) { - await new Promise(done => device.destroy({ - 'success': done, - 'error': done - })); - } - - devicelist.devices.trigger('remove'); - } // Generate new device bundle and publish - // https://xmpp.org/extensions/attic/xep-0384-0.3.0.html#usecases-announcing - - - await shared_converse.omemo_store.generateBundle(); - await devicelist.publishDevices(); - const device = devicelist.devices.get(shared_converse.omemo_store.get('device_id')); - const fp = generateFingerprint(device); - await shared_converse.omemo_store.publishBundle(); - return fp; - } - } - } -}); -;// CONCATENATED MODULE: ./src/plugins/omemo/mixins/chatbox.js - - - - -const { - Strophe: chatbox_Strophe, - sizzle: chatbox_sizzle -} = core_converse.env; -/** - * Mixin object that contains OMEMO-related methods for - * {@link _converse.ChatBox} or {@link _converse.ChatRoom} objects. - * - * @typedef {Object} OMEMOEnabledChatBox - */ - -const OMEMOEnabledChatBox = { - encryptKey(plaintext, device) { - return getSessionCipher(device.get('jid'), device.get('id')).encrypt(plaintext).then(payload => ({ - 'payload': payload, - 'device': device - })); - }, - - handleMessageSendError(e) { - if (e.name === 'IQError') { - this.save('omemo_supported', false); - const err_msgs = []; - - if (chatbox_sizzle(`presence-subscription-required[xmlns="${chatbox_Strophe.NS.PUBSUB_ERROR}"]`, e.iq).length) { - err_msgs.push(__("Sorry, we're unable to send an encrypted message because %1$s " + 'requires you to be subscribed to their presence in order to see their OMEMO information', e.iq.getAttribute('from'))); - } else if (chatbox_sizzle(`remote-server-not-found[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]`, e.iq).length) { - err_msgs.push(__("Sorry, we're unable to send an encrypted message because the remote server for %1$s could not be found", e.iq.getAttribute('from'))); - } else { - err_msgs.push(__('Unable to send an encrypted message due to an unexpected error.')); - err_msgs.push(e.iq.outerHTML); - } - - api.alert('error', __('Error'), err_msgs); - headless_log.error(e); - } else if (e.user_facing) { - api.alert('error', __('Error'), [e.message]); - headless_log.error(e); - } else { - throw e; - } - } - -}; -;// CONCATENATED MODULE: ./src/plugins/omemo/index.js -/** - * @module converse-omemo - * @copyright The Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - - - - - - - - - -const { - Strophe: omemo_Strophe -} = core_converse.env; -core_converse.env.omemo = omemo; -omemo_Strophe.addNamespace('OMEMO_DEVICELIST', omemo_Strophe.NS.OMEMO + '.devicelist'); -omemo_Strophe.addNamespace('OMEMO_VERIFICATION', omemo_Strophe.NS.OMEMO + '.verification'); -omemo_Strophe.addNamespace('OMEMO_WHITELISTED', omemo_Strophe.NS.OMEMO + '.whitelisted'); -omemo_Strophe.addNamespace('OMEMO_BUNDLES', omemo_Strophe.NS.OMEMO + '.bundles'); -core_converse.plugins.add('converse-omemo', { - enabled(_converse) { - return window.libsignal && _converse.config.get('trusted') && !api.settings.get('clear_cache_on_logout') && !_converse.api.settings.get('blacklisted_plugins').includes('converse-omemo'); - }, - - dependencies: ['converse-chatview', 'converse-pubsub', 'converse-profile'], - overrides: { - ProfileModal: profile_modal, - UserDetailsModal: user_details_modal, - ChatBox: chatbox - }, - - initialize() { - api.settings.extend({ - 'omemo_default': false - }); - api.promises.add(['OMEMOInitialized']); - shared_converse.NUM_PREKEYS = 100; // Set here so that tests can override - - Object.assign(shared_converse.ChatBox.prototype, OMEMOEnabledChatBox); - Object.assign(shared_converse, mixins_converse); - Object.assign(shared_converse.api, omemo_api); - shared_converse.OMEMOStore = store; - shared_converse.Device = device; - shared_converse.Devices = devices; - shared_converse.DeviceList = devicelist; - shared_converse.DeviceLists = devicelists; - /******************** Event Handlers ********************/ - - api.waitUntil('chatBoxesInitialized').then(utils_onChatBoxesInitialized); - api.listen.on('afterFileUploaded', (msg, attrs) => msg.file.xep454_ivkey ? setEncryptedFileURL(msg, attrs) : attrs); - api.listen.on('beforeFileUpload', (chat, file) => chat.get('omemo_active') ? encryptFile(file) : file); - api.listen.on('parseMessage', parseEncryptedMessage); - api.listen.on('parseMUCMessage', parseEncryptedMessage); - api.listen.on('chatBoxViewInitialized', onChatInitialized); - api.listen.on('chatRoomViewInitialized', onChatInitialized); - api.listen.on('connected', registerPEPPushHandler); - api.listen.on('getToolbarButtons', getOMEMOToolbarButton); - api.listen.on('statusInitialized', initOMEMO); - api.listen.on('addClientFeatures', () => api.disco.own.features.add(`${omemo_Strophe.NS.OMEMO_DEVICELIST}+notify`)); - api.listen.on('afterMessageBodyTransformed', handleEncryptedFiles); - api.listen.on('userDetailsModalInitialized', contact => { - const jid = contact.get('jid'); - - shared_converse.generateFingerprints(jid).catch(e => headless_log.error(e)); - }); - api.listen.on('profileModalInitialized', () => { - shared_converse.generateFingerprints(shared_converse.bare_jid).catch(e => headless_log.error(e)); - }); - api.listen.on('afterTearDown', () => delete shared_converse.omemo_store); - api.listen.on('clearSession', () => { - if (shared_converse.shouldClearCache() && shared_converse.devicelists) { - shared_converse.devicelists.clearStore(); - - delete shared_converse.devicelists; - } - }); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/push/utils.js - - -const { - Strophe: push_utils_Strophe, - $iq: push_utils_$iq -} = core_converse.env; - -async function disablePushAppServer(domain, push_app_server) { - if (!push_app_server.jid) { - return; - } - - if (!(await api.disco.supports(push_utils_Strophe.NS.PUSH, domain || shared_converse.bare_jid))) { - headless_log.warn(`Not disabling push app server "${push_app_server.jid}", no disco support from your server.`); - return; - } - - const stanza = push_utils_$iq({ - 'type': 'set' - }); - - if (domain !== shared_converse.bare_jid) { - stanza.attrs({ - 'to': domain - }); - } - - stanza.c('disable', { - 'xmlns': push_utils_Strophe.NS.PUSH, - 'jid': push_app_server.jid - }); - - if (push_app_server.node) { - stanza.attrs({ - 'node': push_app_server.node - }); - } - - api.sendIQ(stanza).catch(e => { - headless_log.error(`Could not disable push app server for ${push_app_server.jid}`); - headless_log.error(e); - }); -} - -async function enablePushAppServer(domain, push_app_server) { - if (!push_app_server.jid || !push_app_server.node) { - return; - } - - const identity = await api.disco.getIdentity('pubsub', 'push', push_app_server.jid); - - if (!identity) { - return headless_log.warn(`Not enabling push the service "${push_app_server.jid}", it doesn't have the right disco identtiy.`); - } - - const result = await Promise.all([api.disco.supports(push_utils_Strophe.NS.PUSH, push_app_server.jid), api.disco.supports(push_utils_Strophe.NS.PUSH, domain)]); - - if (!result[0] && !result[1]) { - headless_log.warn(`Not enabling push app server "${push_app_server.jid}", no disco support from your server.`); - return; - } - - const stanza = push_utils_$iq({ - 'type': 'set' - }); - - if (domain !== shared_converse.bare_jid) { - stanza.attrs({ - 'to': domain - }); - } - - stanza.c('enable', { - 'xmlns': push_utils_Strophe.NS.PUSH, - 'jid': push_app_server.jid, - 'node': push_app_server.node - }); - - if (push_app_server.secret) { - stanza.c('x', { - 'xmlns': push_utils_Strophe.NS.XFORM, - 'type': 'submit' - }).c('field', { - 'var': 'FORM_TYPE' - }).c('value').t(`${push_utils_Strophe.NS.PUBSUB}#publish-options`).up().up().c('field', { - 'var': 'secret' - }).c('value').t(push_app_server.secret); - } - - return api.sendIQ(stanza); -} - -async function enablePush(domain) { - domain = domain || shared_converse.bare_jid; - const push_enabled = shared_converse.session.get('push_enabled') || []; - - if (push_enabled.includes(domain)) { - return; - } - - const enabled_services = api.settings.get('push_app_servers').filter(s => !s.disable); - const disabled_services = api.settings.get('push_app_servers').filter(s => s.disable); - const enabled = enabled_services.map(s => enablePushAppServer(domain, s)); - const disabled = disabled_services.map(s => disablePushAppServer(domain, s)); - - try { - await Promise.all(enabled.concat(disabled)); - } catch (e) { - headless_log.error('Could not enable or disable push App Server'); - if (e) headless_log.error(e); - } finally { - push_enabled.push(domain); - } - - shared_converse.session.save('push_enabled', push_enabled); -} -function onChatBoxAdded(model) { - if (model.get('type') == shared_converse.CHATROOMS_TYPE) { - enablePush(push_utils_Strophe.getDomainFromJid(model.get('jid'))); - } -} -;// CONCATENATED MODULE: ./src/plugins/push/index.js -/** - * @description - * Converse.js plugin which add support for registering - * an "App Server" as defined in XEP-0357 - * @copyright 2021, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - -const { - Strophe: push_Strophe -} = core_converse.env; -push_Strophe.addNamespace('PUSH', 'urn:xmpp:push:0'); -core_converse.plugins.add('converse-push', { - initialize() { - /* The initialize function gets called as soon as the plugin is - * loaded by converse.js's plugin machinery. - */ - api.settings.extend({ - 'push_app_servers': [], - 'enable_muc_push': false - }); - api.listen.on('statusInitialized', () => enablePush()); - - if (api.settings.get('enable_muc_push')) { - api.listen.on('chatBoxesInitialized', () => shared_converse.chatboxes.on('add', onChatBoxAdded)); - } - } - -}); -;// CONCATENATED MODULE: ./src/plugins/register/templates/registration_form.js - - - -/* harmony default export */ const registration_form = (o => { - const i18n_choose_provider = __('Choose a different provider'); - - const i18n_has_account = __('Already have a chat account?'); - - const i18n_legend = __('Account Registration:'); - - const i18n_login = __('Log in here'); - - const i18n_register = __('Register'); - - const registration_domain = api.settings.get('registration_domain'); - return T` -
    - ${i18n_legend} ${o.domain} -

    ${o.title}

    -

    ${o.instructions}

    - - ${o.form_fields} - -
    - ${o.fields ? T` - - ` : ''} - ${registration_domain ? '' : T` - - `} -
    -

    ${i18n_has_account}

    -

    -
    -
    -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/register/templates/register_panel.js - - - - - - -const tpl_form_request = () => { - const default_domain = api.settings.get('registration_domain'); - - const i18n_fetch_form = __("Hold tight, we're fetching the registration form…"); - - const i18n_cancel = __('Cancel'); - - return T` -
    - ${spinner({ - 'classes': 'hor_centered' - })} -

    ${i18n_fetch_form}

    - ${default_domain ? '' : T` - - `} -
    - `; -}; - -const tpl_domain_input = () => { - const domain_placeholder = api.settings.get('domain_placeholder'); - - const i18n_providers = __('Tip: A list of public XMPP providers is available'); - - const i18n_providers_link = __('here'); - - const href_providers = api.settings.get('providers_link'); - return T` - -

    - ${i18n_providers} - ${i18n_providers_link}. -

    - `; -}; - -const tpl_fetch_form_buttons = () => { - const i18n_register = __('Fetch registration form'); - - const i18n_existing_account = __('Already have a chat account?'); - - const i18n_login = __('Log in here'); - - return T` -
    - -
    -
    -

    ${i18n_existing_account}

    -

    -
    - `; -}; - -const tpl_choose_provider = () => { - const default_domain = api.settings.get('registration_domain'); - - const i18n_create_account = __('Create your account'); - - const i18n_choose_provider = __('Please enter the XMPP provider to register with:'); - - return T` -
    - ${i18n_create_account} -
    - - - ${default_domain ? default_domain : tpl_domain_input()} -
    - ${default_domain ? '' : tpl_fetch_form_buttons()} -
    - `; -}; - -const CHOOSE_PROVIDER = 0; -const FETCHING_FORM = 1; -const REGISTRATION_FORM = 2; -/* harmony default export */ const register_panel = (o => { - return T` - - ${o.model.get('registration_status') === CHOOSE_PROVIDER ? tpl_choose_provider() : ''} - ${o.model.get('registration_status') === FETCHING_FORM ? tpl_form_request(o) : ''} - ${o.model.get('registration_status') === REGISTRATION_FORM ? registration_form(o) : ''} - `; -}); -;// CONCATENATED MODULE: ./src/plugins/register/panel.js -function register_panel_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - - - - - - - - - - - - - // Strophe methods for building stanzas - -const { - Strophe: panel_Strophe, - sizzle: panel_sizzle, - $iq: panel_$iq -} = core_converse.env; -const panel_u = core_converse.env.utils; -const panel_CHOOSE_PROVIDER = 0; -const panel_FETCHING_FORM = 1; -const panel_REGISTRATION_FORM = 2; -/** - * @class - * @namespace _converse.RegisterPanel - * @memberOf _converse - */ - -class RegisterPanel extends ElementView { - constructor(...args) { - super(...args); - - register_panel_defineProperty(this, "id", "converse-register-panel"); - - register_panel_defineProperty(this, "className", 'controlbox-pane fade-in'); - - register_panel_defineProperty(this, "events", { - 'submit form#converse-register': 'onFormSubmission', - 'click .button-cancel': 'renderProviderChoiceForm' - }); - } - - initialize() { - this.reset(); - - const controlbox = shared_converse.chatboxes.get('controlbox'); - - this.model = controlbox; - this.listenTo(shared_converse, 'connectionInitialized', this.registerHooks); - this.listenTo(this.model, 'change:registration_status', this.render); - const domain = api.settings.get('registration_domain'); - - if (domain) { - this.fetchRegistrationForm(domain); - } else { - this.model.set('registration_status', panel_CHOOSE_PROVIDER); - } - } - - render() { - V(register_panel({ - 'domain': this.domain, - 'fields': this.fields, - 'form_fields': this.form_fields, - 'instructions': this.instructions, - 'model': this.model, - 'title': this.title - }), this); - } - /** - * Hook into Strophe's _connect_cb, so that we can send an IQ - * requesting the registration fields. - */ - - - registerHooks() { - const conn = shared_converse.connection; - - const connect_cb = conn._connect_cb.bind(conn); - - conn._connect_cb = (req, callback, raw) => { - if (!this._registering) { - connect_cb(req, callback, raw); - } else { - if (this.getRegistrationFields(req, callback)) { - this._registering = false; - } - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.render(); - } - /** - * Send an IQ stanza to the XMPP server asking for the registration fields. - * @private - * @method _converse.RegisterPanel#getRegistrationFields - * @param { Strophe.Request } req - The current request - * @param { Function } callback - The callback function - */ - - - getRegistrationFields(req, _callback) { - const conn = shared_converse.connection; - conn.connected = true; - - const body = conn._proto._reqToData(req); - - if (!body) { - return; - } - - if (conn._proto._connect_cb(body) === panel_Strophe.Status.CONNFAIL) { - this.showValidationError(__("Sorry, we're unable to connect to your chosen provider.")); - return false; - } - - const register = body.getElementsByTagName("register"); - const mechanisms = body.getElementsByTagName("mechanism"); - - if (register.length === 0 && mechanisms.length === 0) { - conn._proto._no_auth_received(_callback); - - return false; - } - - if (register.length === 0) { - conn._changeConnectStatus(panel_Strophe.Status.REGIFAIL); - - this.showValidationError(__("Sorry, the given provider does not support in " + "band account registration. Please try with a " + "different provider.")); - return true; - } // Send an IQ stanza to get all required data fields - - - conn._addSysHandler(this.onRegistrationFields.bind(this), null, "iq", null, null); - - const stanza = panel_$iq({ - type: "get" - }).c("query", { - xmlns: panel_Strophe.NS.REGISTER - }).tree(); - stanza.setAttribute("id", conn.getUniqueId("sendIQ")); - conn.send(stanza); - conn.connected = false; - return true; - } - /** - * Handler for {@link _converse.RegisterPanel#getRegistrationFields} - * @private - * @method _converse.RegisterPanel#onRegistrationFields - * @param { XMLElement } stanza - The query stanza. - */ - - - onRegistrationFields(stanza) { - if (stanza.getAttribute("type") === "error") { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.REGIFAIL, __('Something went wrong while establishing a connection with "%1$s". ' + 'Are you sure it exists?', this.domain)); - - return false; - } - - if (stanza.getElementsByTagName("query").length !== 1) { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.REGIFAIL, "unknown"); - - return false; - } - - this.setFields(stanza); - - if (this.model.get('registration_status') === panel_FETCHING_FORM) { - this.renderRegistrationForm(stanza); - } - - return false; - } - - reset(settings) { - const defaults = { - fields: {}, - urls: [], - title: "", - instructions: "", - registered: false, - _registering: false, - domain: null, - form_type: null - }; - Object.assign(this, defaults); - - if (settings) { - Object.assign(this, lodash_es_pick(settings, Object.keys(defaults))); - } - } - /** - * Event handler when the #converse-register form is submitted. - * Depending on the available input fields, we delegate to other methods. - * @private - * @param { Event } ev - */ - - - onFormSubmission(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); - } - - if (ev.target.querySelector('input[name=domain]') === null) { - this.submitRegistrationForm(ev.target); - } else { - this.onProviderChosen(ev.target); - } - } - /** - * Callback method that gets called when the user has chosen an XMPP provider - * @private - * @method _converse.RegisterPanel#onProviderChosen - * @param { HTMLElement } form - The form that was submitted - */ - - - onProviderChosen(form) { - const domain_input = form.querySelector('input[name=domain]'), - domain = domain_input === null || domain_input === void 0 ? void 0 : domain_input.value; - - if (!domain) { - // TODO: add validation message - domain_input.classList.add('error'); - return; - } - - form.querySelector('input[type=submit]').classList.add('hidden'); - this.fetchRegistrationForm(domain.trim()); - } - /** - * Fetch a registration form from the requested domain - * @private - * @method _converse.RegisterPanel#fetchRegistrationForm - * @param { String } domain_name - XMPP server domain - */ - - - async fetchRegistrationForm(domain_name) { - var _converse$connection; - - this.model.set('registration_status', panel_FETCHING_FORM); - this.reset({ - 'domain': panel_Strophe.getDomainFromJid(domain_name), - '_registering': true - }); - await shared_converse.initConnection(this.domain); // When testing, the test tears down before the async function - // above finishes. So we use optional chaining here - - (_converse$connection = shared_converse.connection) === null || _converse$connection === void 0 ? void 0 : _converse$connection.connect(this.domain, "", status => this.onConnectStatusChanged(status)); - return false; - } - - giveFeedback(message, klass) { - let feedback = this.querySelector('.reg-feedback'); - - if (feedback !== null) { - feedback.parentNode.removeChild(feedback); - } - - const form = this.querySelector('form'); - form.insertAdjacentHTML('afterbegin', ''); - feedback = form.querySelector('.reg-feedback'); - feedback.textContent = message; - - if (klass) { - feedback.classList.add(klass); - } - } - - showSpinner() { - const form = this.querySelector('form'); - V(spinner(), form); - return this; - } - /** - * Callback function called by Strophe whenever the connection status changes. - * Passed to Strophe specifically during a registration attempt. - * @private - * @method _converse.RegisterPanel#onConnectStatusChanged - * @param { integer } status_code - The Strophe.Status status code - */ - - - onConnectStatusChanged(status_code) { - headless_log.debug('converse-register: onConnectStatusChanged'); - - if ([panel_Strophe.Status.DISCONNECTED, panel_Strophe.Status.CONNFAIL, panel_Strophe.Status.REGIFAIL, panel_Strophe.Status.NOTACCEPTABLE, panel_Strophe.Status.CONFLICT].includes(status_code)) { - headless_log.error(`Problem during registration: Strophe.Status is ${shared_converse.CONNECTION_STATUS[status_code]}`); - this.abortRegistration(); - } else if (status_code === panel_Strophe.Status.REGISTERED) { - headless_log.debug("Registered successfully."); - - shared_converse.connection.reset(); - - this.showSpinner(); - - if (["converse/login", "converse/register"].includes(shared_converse.router.history.getFragment())) { - shared_converse.router.navigate('', { - 'replace': true - }); - } - - if (this.fields.password && this.fields.username) { - // automatically log the user in - shared_converse.connection.connect(this.fields.username.toLowerCase() + '@' + this.domain.toLowerCase(), this.fields.password, shared_converse.onConnectStatusChanged); - - this.giveFeedback(__('Now logging you in'), 'info'); - } else { - shared_converse.giveFeedback(__('Registered successfully')); - } - - this.reset(); - } - } - - getLegacyFormFields() { - const input_fields = Object.keys(this.fields).map(key => { - if (key === "username") { - return form_username({ - 'domain': ` @${this.domain}`, - 'name': key, - 'type': "text", - 'label': key, - 'value': '', - 'required': true - }); - } else { - return form_input({ - 'label': key, - 'name': key, - 'placeholder': key, - 'required': true, - 'type': key === 'password' || key === 'email' ? key : "text", - 'value': '' - }); - } - }); - const urls = this.urls.map(u => form_url({ - 'label': '', - 'value': u - })); - return [...input_fields, ...urls]; - } - - getFormFields(stanza) { - if (this.form_type === 'xform') { - return Array.from(stanza.querySelectorAll('field')).map(field => utils_form.xForm2TemplateResult(field, stanza, { - 'domain': this.domain - })); - } else { - return this.getLegacyFormFields(); - } - } - /** - * Renders the registration form based on the XForm fields - * received from the XMPP server. - * @private - * @method _converse.RegisterPanel#renderRegistrationForm - * @param { XMLElement } stanza - The IQ stanza received from the XMPP server. - */ - - - renderRegistrationForm(stanza) { - this.form_fields = this.getFormFields(stanza); - this.model.set('registration_status', panel_REGISTRATION_FORM); - } - - showValidationError(message) { - const form = this.querySelector('form'); - let flash = form.querySelector('.form-errors'); - - if (flash === null) { - flash = ''; - const instructions = form.querySelector('p.instructions'); - - if (instructions === null) { - form.insertAdjacentHTML('afterbegin', flash); - } else { - instructions.insertAdjacentHTML('afterend', flash); - } - - flash = form.querySelector('.form-errors'); - } else { - flash.innerHTML = ''; - } - - flash.insertAdjacentHTML('beforeend', '

    ' + message + '

    '); - flash.classList.remove('hidden'); - } - /** - * Report back to the user any error messages received from the - * XMPP server after attempted registration. - * @private - * @method _converse.RegisterPanel#reportErrors - * @param { XMLElement } stanza - The IQ stanza received from the XMPP server - */ - - - reportErrors(stanza) { - const errors = stanza.querySelectorAll('error'); - errors.forEach(e => this.showValidationError(e.textContent)); - - if (!errors.length) { - const message = __('The provider rejected your registration attempt. ' + 'Please check the values you entered for correctness.'); - - this.showValidationError(message); - } - } - - renderProviderChoiceForm(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); - } - - shared_converse.connection._proto._abortAllRequests(); - - shared_converse.connection.reset(); - - this.render(); - } - - abortRegistration() { - shared_converse.connection._proto._abortAllRequests(); - - shared_converse.connection.reset(); - - if ([panel_FETCHING_FORM, panel_REGISTRATION_FORM].includes(this.model.get('registration_status'))) { - if (api.settings.get('registration_domain')) { - this.fetchRegistrationForm(api.settings.get('registration_domain')); - } - } else { - this.render(); - } - } - /** - * Handler, when the user submits the registration form. - * Provides form error feedback or starts the registration process. - * @private - * @method _converse.RegisterPanel#submitRegistrationForm - * @param { HTMLElement } form - The HTML form that was submitted - */ - - - submitRegistrationForm(form) { - const has_empty_inputs = Array.from(this.querySelectorAll('input.required')).reduce((result, input) => { - if (input.value === '') { - input.classList.add('error'); - return result + 1; - } - - return result; - }, 0); - - if (has_empty_inputs) { - return; - } - - const inputs = panel_sizzle(':input:not([type=button]):not([type=submit])', form); - const iq = panel_$iq({ - 'type': 'set', - 'id': panel_u.getUniqueId() - }).c("query", { - xmlns: panel_Strophe.NS.REGISTER - }); - - if (this.form_type === 'xform') { - iq.c("x", { - xmlns: panel_Strophe.NS.XFORM, - type: 'submit' - }); - const xml_nodes = inputs.map(i => utils_form.webForm2xForm(i)).filter(n => n); - xml_nodes.forEach(n => iq.cnode(n).up()); - } else { - inputs.forEach(input => iq.c(input.getAttribute('name'), {}, input.value)); - } - - shared_converse.connection._addSysHandler(this._onRegisterIQ.bind(this), null, "iq", null, null); - - shared_converse.connection.send(iq); - - this.setFields(iq.tree()); - } - /* Stores the values that will be sent to the XMPP server during attempted registration. - * @private - * @method _converse.RegisterPanel#setFields - * @param { XMLElement } stanza - the IQ stanza that will be sent to the XMPP server. - */ - - - setFields(stanza) { - const query = stanza.querySelector('query'); - const xform = panel_sizzle(`x[xmlns="${panel_Strophe.NS.XFORM}"]`, query); - - if (xform.length > 0) { - this._setFieldsFromXForm(xform.pop()); - } else { - this._setFieldsFromLegacy(query); - } - } - - _setFieldsFromLegacy(query) { - [].forEach.call(query.children, field => { - if (field.tagName.toLowerCase() === 'instructions') { - this.instructions = panel_Strophe.getText(field); - return; - } else if (field.tagName.toLowerCase() === 'x') { - if (field.getAttribute('xmlns') === 'jabber:x:oob') { - this.urls.concat(panel_sizzle('url', field).map(u => u.textContent)); - } - - return; - } - - this.fields[field.tagName.toLowerCase()] = panel_Strophe.getText(field); - }); - this.form_type = 'legacy'; - } - - _setFieldsFromXForm(xform) { - var _xform$querySelector, _xform$querySelector2; - - this.title = (_xform$querySelector = xform.querySelector('title')) === null || _xform$querySelector === void 0 ? void 0 : _xform$querySelector.textContent; - this.instructions = (_xform$querySelector2 = xform.querySelector('instructions')) === null || _xform$querySelector2 === void 0 ? void 0 : _xform$querySelector2.textContent; - xform.querySelectorAll('field').forEach(field => { - const _var = field.getAttribute('var'); - - if (_var) { - var _field$querySelector; - - this.fields[_var.toLowerCase()] = ((_field$querySelector = field.querySelector('value')) === null || _field$querySelector === void 0 ? void 0 : _field$querySelector.textContent) ?? ''; - } else { - // TODO: other option seems to be type="fixed" - headless_log.warn("Found field we couldn't parse"); - } - }); - this.form_type = 'xform'; - } - /** - * Callback method that gets called when a return IQ stanza - * is received from the XMPP server, after attempting to - * register a new user. - * @private - * @method _converse.RegisterPanel#reportErrors - * @param { XMLElement } stanza - The IQ stanza. - */ - - - _onRegisterIQ(stanza) { - if (stanza.getAttribute("type") === "error") { - headless_log.error("Registration failed."); - this.reportErrors(stanza); - let error = stanza.getElementsByTagName("error"); - - if (error.length !== 1) { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.REGIFAIL, "unknown"); - - return false; - } - - error = error[0].firstElementChild.tagName.toLowerCase(); - - if (error === 'conflict') { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.CONFLICT, error); - } else if (error === 'not-acceptable') { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.NOTACCEPTABLE, error); - } else { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.REGIFAIL, error); - } - } else { - shared_converse.connection._changeConnectStatus(panel_Strophe.Status.REGISTERED, null); - } - - return false; - } - -} - -api.elements.define('converse-register-panel', RegisterPanel); -;// CONCATENATED MODULE: ./src/plugins/register/index.js -/** - * @module converse-register - * @description - * This is a Converse.js plugin which add support for in-band registration - * as specified in XEP-0077. - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - // Strophe methods for building stanzas - -const { - Strophe: register_Strophe -} = core_converse.env; // Add Strophe Namespaces - -register_Strophe.addNamespace('REGISTER', 'jabber:iq:register'); // Add Strophe Statuses - -const register_i = Object.keys(register_Strophe.Status).reduce((max, k) => Math.max(max, register_Strophe.Status[k]), 0); -register_Strophe.Status.REGIFAIL = register_i + 1; -register_Strophe.Status.REGISTERED = register_i + 2; -register_Strophe.Status.CONFLICT = register_i + 3; -register_Strophe.Status.NOTACCEPTABLE = register_i + 5; -core_converse.plugins.add('converse-register', { - dependencies: ['converse-controlbox'], - - enabled() { - return true; - }, - - initialize() { - shared_converse.CONNECTION_STATUS[register_Strophe.Status.REGIFAIL] = 'REGIFAIL'; - shared_converse.CONNECTION_STATUS[register_Strophe.Status.REGISTERED] = 'REGISTERED'; - shared_converse.CONNECTION_STATUS[register_Strophe.Status.CONFLICT] = 'CONFLICT'; - shared_converse.CONNECTION_STATUS[register_Strophe.Status.NOTACCEPTABLE] = 'NOTACCEPTABLE'; - api.settings.extend({ - 'allow_registration': true, - 'domain_placeholder': __(' e.g. conversejs.org'), - // Placeholder text shown in the domain input on the registration form - 'providers_link': 'https://compliance.conversations.im/', - // Link to XMPP providers shown on registration page - 'registration_domain': '' - }); - - async function setActiveForm(value) { - await api.waitUntil('controlBoxInitialized'); - - const controlbox = shared_converse.chatboxes.get('controlbox'); - - controlbox.set({ - 'active-form': value - }); - } - - shared_converse.router.route('converse/login', () => setActiveForm('login')); - - shared_converse.router.route('converse/register', () => setActiveForm('register')); - - api.listen.on('controlBoxInitialized', view => { - view.model.on('change:active-form', view.showLoginOrRegisterForm, view); - }); - } - -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/add-muc.js - - - - - - -const nickname_input = o => { - const i18n_nickname = __('Nickname'); - - const i18n_required_field = __('This field is required'); - - return T` -
    - - -
    - `; -}; - -/* harmony default export */ const add_muc = (o => { - const i18n_join = __('Join'); - - const i18n_enter = __('Enter a new Groupchat'); - - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/modals/add-muc.js - - - - -const add_muc_u = core_converse.env.utils; -const { - Strophe: add_muc_Strophe -} = core_converse.env; -/* harmony default export */ const modals_add_muc = (base.extend({ - persistent: true, - id: 'add-chatroom-modal', - events: { - 'submit form.add-chatroom': 'openChatRoom', - 'keyup .roomjid-input': 'checkRoomidPolicy', - 'change .roomjid-input': 'checkRoomidPolicy' - }, - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change:muc_domain', this.render); - this.muc_roomid_policy_error_msg = null; - }, - - toHTML() { - let placeholder = ''; - - if (!api.settings.get('locked_muc_domain')) { - const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain'); - placeholder = muc_domain ? `name@${muc_domain}` : __('name@conference.example.org'); - } - - return add_muc(Object.assign(this.model.toJSON(), { - '_converse': shared_converse, - 'label_room_address': api.settings.get('muc_domain') ? __('Groupchat name') : __('Groupchat address'), - 'chatroom_placeholder': placeholder, - 'muc_roomid_policy_error_msg': this.muc_roomid_policy_error_msg, - 'muc_roomid_policy_hint': api.settings.get('muc_roomid_policy_hint') - })); - }, - - afterRender() { - this.el.addEventListener('shown.bs.modal', () => { - this.el.querySelector('input[name="chatroom"]').focus(); - }, false); - }, - - parseRoomDataFromEvent(form) { - const data = new FormData(form); - const jid = data.get('chatroom'); - let nick; - - if (api.settings.get('locked_muc_nickname')) { - nick = shared_converse.getDefaultMUCNickname(); - - if (!nick) { - throw new Error("Using locked_muc_nickname but no nickname found!"); - } - } else { - nick = data.get('nickname').trim(); - } - - return { - 'jid': jid, - 'nick': nick - }; - }, - - openChatRoom(ev) { - ev.preventDefault(); - const data = this.parseRoomDataFromEvent(ev.target); - - if (data.nick === "") { - // Make sure defaults apply if no nick is provided. - data.nick = undefined; - } - - let jid; - - if (api.settings.get('locked_muc_domain') || api.settings.get('muc_domain') && !add_muc_u.isValidJID(data.jid)) { - jid = `${add_muc_Strophe.escapeNode(data.jid)}@${api.settings.get('muc_domain')}`; - } else { - jid = data.jid; - this.model.setDomain(jid); - } - - api.rooms.open(jid, Object.assign(data, { - jid - }), true); - this.modal.hide(); - ev.target.reset(); - }, - - checkRoomidPolicy() { - if (api.settings.get('muc_roomid_policy') && api.settings.get('muc_domain')) { - let jid = this.el.querySelector('.roomjid-input').value; - - if (core_converse.locked_muc_domain || !add_muc_u.isValidJID(jid)) { - jid = `${add_muc_Strophe.escapeNode(jid)}@${api.settings.get('muc_domain')}`; - } - - const roomid = add_muc_Strophe.getNodeFromJid(jid); - const roomdomain = add_muc_Strophe.getDomainFromJid(jid); - - if (api.settings.get('muc_domain') !== roomdomain || api.settings.get('muc_roomid_policy').test(roomid)) { - this.muc_roomid_policy_error_msg = null; - } else { - this.muc_roomid_policy_error_msg = __('Groupchat id is invalid.'); - } - - this.render(); - } - } - -})); -;// CONCATENATED MODULE: ./node_modules/lodash-es/head.js -/** - * Gets the first element of `array`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @alias first - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @example - * - * _.head([1, 2, 3]); - * // => 1 - * - * _.head([]); - * // => undefined - */ -function head(array) { - return array && array.length ? array[0] : undefined; -} - -/* harmony default export */ const lodash_es_head = (head); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-list.js - - - - - - -const muc_list_form = o => { - const i18n_query = __('Show groupchats'); - - const i18n_server_address = __('Server address'); - - return T` -
    -
    - - -
    - -
    - `; -}; - -const tpl_item = (o, item) => { - const i18n_info_title = __('Show more information on this groupchat'); - - const i18n_open_title = __('Click to open this groupchat'); - - return T` -
  • - -
  • - `; -}; - -/* harmony default export */ const muc_list = (o => { - const i18n_list_chatrooms = __('Query for Groupchats'); - - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/templates/muc-description.js - - -/* harmony default export */ const muc_description = (o => { - const i18n_desc = __('Description:'); - - const i18n_jid = __('Groupchat XMPP Address:'); - - const i18n_occ = __('Participants:'); - - const i18n_features = __('Features:'); - - const i18n_requires_auth = __('Requires authentication'); - - const i18n_hidden = __('Hidden'); - - const i18n_requires_invite = __('Requires an invitation'); - - const i18n_moderated = __('Moderated'); - - const i18n_non_anon = __('Non-anonymous'); - - const i18n_open_room = __('Open'); - - const i18n_permanent_room = __('Permanent'); - - const i18n_public = __('Public'); - - const i18n_semi_anon = __('Semi-anonymous'); - - const i18n_temp_room = __('Temporary'); - - const i18n_unmoderated = __('Unmoderated'); - - return T` -
    -

    ${i18n_jid} ${o.jid}

    -

    ${i18n_desc} ${o.desc}

    -

    ${i18n_occ} ${o.occ}

    -

    ${i18n_features} -

      - ${o.passwordprotected ? T`
    • ${i18n_requires_auth}
    • ` : ''} - ${o.hidden ? T`
    • ${i18n_hidden}
    • ` : ''} - ${o.membersonly ? T`
    • ${i18n_requires_invite}
    • ` : ''} - ${o.moderated ? T`
    • ${i18n_moderated}
    • ` : ''} - ${o.nonanonymous ? T`
    • ${i18n_non_anon}
    • ` : ''} - ${o.open ? T`
    • ${i18n_open_room}
    • ` : ''} - ${o.persistent ? T`
    • ${i18n_permanent_room}
    • ` : ''} - ${o.publicroom ? T`
    • ${i18n_public}
    • ` : ''} - ${o.semianonymous ? T`
    • ${i18n_semi_anon}
    • ` : ''} - ${o.temporary ? T`
    • ${i18n_temp_room}
    • ` : ''} - ${o.unmoderated ? T`
    • ${i18n_unmoderated}
    • ` : ''} -
    -

    -
    -`; -}); -;// CONCATENATED MODULE: ./src/plugins/muc-views/modals/muc-list.js - - - - - - - - - -const { - Strophe: muc_list_Strophe, - $iq: muc_list_$iq, - sizzle: muc_list_sizzle -} = core_converse.env; -const muc_list_u = core_converse.env.utils; -/* Insert groupchat info (based on returned #disco IQ stanza) - * @function insertRoomInfo - * @param { HTMLElement } el - The HTML DOM element that contains the info. - * @param { XMLElement } stanza - The IQ stanza containing the groupchat info. - */ - -function insertRoomInfo(el, stanza) { - var _head, _head2; - - // All MUC features found here: https://xmpp.org/registrar/disco-features.html - el.querySelector('span.spinner').remove(); - el.querySelector('a.room-info').classList.add('selected'); - el.insertAdjacentHTML('beforeEnd', muc_list_u.getElementFromTemplateResult(muc_description({ - 'jid': stanza.getAttribute('from'), - 'desc': (_head = lodash_es_head(muc_list_sizzle('field[var="muc#roominfo_description"] value', stanza))) === null || _head === void 0 ? void 0 : _head.textContent, - 'occ': (_head2 = lodash_es_head(muc_list_sizzle('field[var="muc#roominfo_occupants"] value', stanza))) === null || _head2 === void 0 ? void 0 : _head2.textContent, - 'hidden': muc_list_sizzle('feature[var="muc_hidden"]', stanza).length, - 'membersonly': muc_list_sizzle('feature[var="muc_membersonly"]', stanza).length, - 'moderated': muc_list_sizzle('feature[var="muc_moderated"]', stanza).length, - 'nonanonymous': muc_list_sizzle('feature[var="muc_nonanonymous"]', stanza).length, - 'open': muc_list_sizzle('feature[var="muc_open"]', stanza).length, - 'passwordprotected': muc_list_sizzle('feature[var="muc_passwordprotected"]', stanza).length, - 'persistent': muc_list_sizzle('feature[var="muc_persistent"]', stanza).length, - 'publicroom': muc_list_sizzle('feature[var="muc_publicroom"]', stanza).length, - 'semianonymous': muc_list_sizzle('feature[var="muc_semianonymous"]', stanza).length, - 'temporary': muc_list_sizzle('feature[var="muc_temporary"]', stanza).length, - 'unmoderated': muc_list_sizzle('feature[var="muc_unmoderated"]', stanza).length - }))); -} -/** - * Show/hide extra information about a groupchat in a listing. - * @function toggleRoomInfo - * @param { Event } - */ - - -function toggleRoomInfo(ev) { - const parent_el = muc_list_u.ancestor(ev.target, '.room-item'); - const div_el = parent_el.querySelector('div.room-info'); - - if (div_el) { - muc_list_u.slideIn(div_el).then(muc_list_u.removeElement); - parent_el.querySelector('a.room-info').classList.remove('selected'); - } else { - parent_el.insertAdjacentElement('beforeend', muc_list_u.getElementFromTemplateResult(spinner())); - api.disco.info(ev.target.getAttribute('data-room-jid'), null).then(stanza => insertRoomInfo(parent_el, stanza)).catch(e => headless_log.error(e)); - } -} - -/* harmony default export */ const modals_muc_list = (base.extend({ - id: "muc-list-modal", - persistent: true, - - initialize() { - this.items = []; - this.loading_items = false; - base.prototype.initialize.apply(this, arguments); - - if (api.settings.get('muc_domain') && !this.model.get('muc_domain')) { - this.model.save('muc_domain', api.settings.get('muc_domain')); - } - - this.listenTo(this.model, 'change:muc_domain', this.onDomainChange); - this.el.addEventListener('shown.bs.modal', () => api.settings.get('locked_muc_domain') ? this.updateRoomsList() : this.el.querySelector('input[name="server"]').focus()); - }, - - toHTML() { - const muc_domain = this.model.get('muc_domain') || api.settings.get('muc_domain'); - return muc_list(Object.assign(this.model.toJSON(), { - 'show_form': !api.settings.get('locked_muc_domain'), - 'server_placeholder': muc_domain ? muc_domain : __('conference.example.org'), - 'items': this.items, - 'loading_items': this.loading_items, - 'openRoom': ev => this.openRoom(ev), - 'setDomainFromEvent': ev => this.setDomainFromEvent(ev), - 'submitForm': ev => this.showRooms(ev), - 'toggleRoomInfo': ev => this.toggleRoomInfo(ev) - })); - }, - - openRoom(ev) { - ev.preventDefault(); - const jid = ev.target.getAttribute('data-room-jid'); - const name = ev.target.getAttribute('data-room-name'); - this.modal.hide(); - api.rooms.open(jid, { - 'name': name - }, true); - }, - - toggleRoomInfo(ev) { - ev.preventDefault(); - toggleRoomInfo(ev); - }, - - onDomainChange() { - api.settings.get('auto_list_rooms') && this.updateRoomsList(); - }, - - /** - * Handle the IQ stanza returned from the server, containing - * all its public groupchats. - * @private - * @method _converse.ChatRoomView#onRoomsFound - * @param { HTMLElement } iq - */ - onRoomsFound(iq) { - this.loading_items = false; - const rooms = iq ? muc_list_sizzle('query item', iq) : []; - - if (rooms.length) { - this.model.set({ - 'feedback_text': __('Groupchats found') - }, { - 'silent': true - }); - this.items = rooms.map(getAttributes); - } else { - this.items = []; - this.model.set({ - 'feedback_text': __('No groupchats found') - }, { - 'silent': true - }); - } - - this.render(); - return true; - }, - - /** - * Send an IQ stanza to the server asking for all groupchats - * @private - * @method _converse.ChatRoomView#updateRoomsList - */ - updateRoomsList() { - const iq = muc_list_$iq({ - 'to': this.model.get('muc_domain'), - 'from': shared_converse.connection.jid, - 'type': "get" - }).c("query", { - xmlns: muc_list_Strophe.NS.DISCO_ITEMS - }); - api.sendIQ(iq).then(iq => this.onRoomsFound(iq)).catch(() => this.onRoomsFound()); - }, - - showRooms(ev) { - ev.preventDefault(); - this.loading_items = true; - this.render(); - const data = new FormData(ev.target); - this.model.setDomain(data.get('server')); - this.updateRoomsList(); - }, - - setDomainFromEvent(ev) { - this.model.setDomain(ev.target.value); - }, - - setNick(ev) { - this.model.save({ - nick: ev.target.value - }); - } - -})); -;// CONCATENATED MODULE: ./src/plugins/roomslist/templates/roomslist.js - - - - - - -const bookmark = o => { - const i18n_add_bookmark = __('Bookmark this groupchat'); - - const i18n_remove_bookmark = __('Unbookmark this groupchat'); - - if (o.bookmarked) { - return T` - `; - } else { - return T` - `; - } -}; - -const unread_indicator = o => T`${o.room.get('num_unread')}`; - -const activity_indicator = () => T``; - -const room_item = o => { - const i18n_leave_room = __('Leave this groupchat'); - - const has_unread_msgs = o.room.get('num_unread_general') || o.room.get('has_activity'); - return T` -
    - - ${o.room.get('num_unread') ? unread_indicator(o) : o.room.get('has_activity') ? activity_indicator(o) : ''} - - ${o.room.getDisplayName()} - - ${o.allow_bookmarks ? bookmark(o) : ''} - - - - -
    `; -}; - -/* harmony default export */ const roomslist = (o => { - const i18n_desc_rooms = __('Click to toggle the list of open groupchats'); - - const i18n_heading_chatrooms = __('Groupchats'); - - const i18n_title_list_rooms = __('Query for groupchats'); - - const i18n_title_new_room = __('Add a new groupchat'); - - return T` - - -
    - - ${__('Open Groupchats')} -
    - ${o.rooms.map(room => room_item(Object.assign({ - room - }, o)))} -
    -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/roomslist/model.js - - -const { - Strophe: roomslist_model_Strophe -} = core_converse.env; -const RoomsListModel = Model.extend({ - defaults: function () { - return { - 'muc_domain': api.settings.get('muc_domain'), - 'nick': shared_converse.getDefaultMUCNickname(), - 'toggle-state': shared_converse.OPENED - }; - }, - - setDomain(jid) { - if (!api.settings.get('locked_muc_domain')) { - this.save('muc_domain', roomslist_model_Strophe.getDomainFromJid(jid)); - } - } - -}); -/* harmony default export */ const roomslist_model = (RoomsListModel); -;// CONCATENATED MODULE: ./src/plugins/roomslist/view.js - - - - - - - - -const { - Strophe: view_Strophe, - u: view_u -} = core_converse.env; -class RoomsList extends ElementView { - initialize() { - const id = `converse.roomspanel${shared_converse.bare_jid}`; - this.model = new roomslist_model({ - id - }); - initStorage(this.model, id); - this.model.fetch(); - this.listenTo(shared_converse.chatboxes, 'add', this.renderIfChatRoom); - this.listenTo(shared_converse.chatboxes, 'remove', this.renderIfChatRoom); - this.listenTo(shared_converse.chatboxes, 'destroy', this.renderIfChatRoom); - this.listenTo(shared_converse.chatboxes, 'change', this.renderIfRelevantChange); - this.render(); - } - - renderIfChatRoom(model) { - view_u.isChatRoom(model) && this.render(); - } - - renderIfRelevantChange(model) { - const attrs = ['bookmarked', 'hidden', 'name', 'num_unread', 'num_unread_general', 'has_activity']; - const changed = model.changed || {}; - - if (view_u.isChatRoom(model) && Object.keys(changed).filter(m => attrs.includes(m)).length) { - this.render(); - } - } - - render() { - V(roomslist({ - 'addBookmark': ev => this.addBookmark(ev), - 'allow_bookmarks': shared_converse.allow_bookmarks && shared_converse.bookmarks, - 'closeRoom': ev => this.closeRoom(ev), - 'collapsed': this.model.get('toggle-state') !== shared_converse.OPENED, - 'currently_open': room => shared_converse.isUniView() && !room.get('hidden'), - 'model': this.model, - 'openRoom': ev => this.openRoom(ev), - 'removeBookmark': ev => this.removeBookmark(ev), - 'rooms': shared_converse.chatboxes.filter(m => m.get('type') === shared_converse.CHATROOMS_TYPE), - 'showRoomDetailsModal': ev => this.showRoomDetailsModal(ev), - 'toggleRoomsList': ev => this.toggleRoomsList(ev), - 'toggle_state': this.model.get('toggle-state') - }), this); - } - - showRoomDetailsModal(ev) { - // eslint-disable-line class-methods-use-this - const jid = ev.target.getAttribute('data-room-jid'); - - const room = shared_converse.chatboxes.get(jid); - - ev.preventDefault(); - api.modal.show(modals_muc_details, { - 'model': room - }, ev); - } - - async openRoom(ev) { - // eslint-disable-line class-methods-use-this - ev.preventDefault(); - const name = ev.target.textContent; - const jid = ev.target.getAttribute('data-room-jid'); - const data = { - 'name': name || view_Strophe.unescapeNode(view_Strophe.getNodeFromJid(jid)) || jid - }; - await api.rooms.open(jid, data, true); - } - - async closeRoom(ev) { - // eslint-disable-line class-methods-use-this - ev.preventDefault(); - const name = ev.target.getAttribute('data-room-name'); - - if (confirm(__("Are you sure you want to leave the groupchat %1$s?", name))) { - const jid = ev.target.getAttribute('data-room-jid'); - const room = await api.rooms.get(jid); - room.close(); - } - } - - removeBookmark(ev) { - // eslint-disable-line class-methods-use-this - shared_converse.removeBookmarkViaEvent(ev); - } - - addBookmark(ev) { - // eslint-disable-line class-methods-use-this - shared_converse.addBookmarkViaEvent(ev); - } - - toggleRoomsList(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - const icon_el = ev.target.matches('.fa') ? ev.target : ev.target.querySelector('.fa'); - - if (icon_el.classList.contains("fa-caret-down")) { - view_u.slideIn(this.querySelector('.open-rooms-list')).then(() => { - this.model.save({ - 'toggle-state': shared_converse.CLOSED - }); - icon_el.classList.remove("fa-caret-down"); - icon_el.classList.add("fa-caret-right"); - }); - } else { - view_u.slideOut(this.querySelector('.open-rooms-list')).then(() => { - this.model.save({ - 'toggle-state': shared_converse.OPENED - }); - icon_el.classList.remove("fa-caret-right"); - icon_el.classList.add("fa-caret-down"); - }); - } - } - -} -api.elements.define('converse-rooms-list', RoomsList); -;// CONCATENATED MODULE: ./src/plugins/roomslist/index.js -/** - * @description - * Converse.js plugin which shows a list of currently open - * rooms in the "Rooms Panel" of the ControlBox. - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - -core_converse.plugins.add('converse-roomslist', { - dependencies: ["converse-singleton", "converse-controlbox", "converse-muc", "converse-bookmarks"], - - initialize() { - // Event handlers - api.listen.on('connected', async () => { - if (shared_converse.allow_bookmarks) { - await api.waitUntil('bookmarksInitialized'); - } else { - await Promise.all([api.waitUntil('chatBoxesFetched')]); - } - }); - } - -}); -;// CONCATENATED MODULE: ./src/shared/templates/icons.js - -/* harmony default export */ const icons = (() => T` - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -`); -;// CONCATENATED MODULE: ./src/shared/components/font-awesome.js - - -class FontAwesome extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - return icons(); - } - -} -window.customElements.define('converse-fontawesome', FontAwesome); -;// CONCATENATED MODULE: ./src/plugins/rootview/templates/root.js - - - -/* harmony default export */ const templates_root = (() => { - let extra_classes = api.settings.get('singleton') ? 'converse-singleton' : ''; - extra_classes += `converse-${api.settings.get('view_mode')}`; - return T` - -
    - - `; -}); -;// CONCATENATED MODULE: ./src/plugins/rootview/root.js - - - -/** - * `converse-root` is an optional custom element which can be used to - * declaratively insert the Converse UI into the DOM. - * - * It can be inserted into the DOM before or after Converse has loaded or been - * initialized. - */ - -class ConverseRoot extends CustomElement { - render() { - // eslint-disable-line class-methods-use-this - return templates_root(); - } - - connectedCallback() { - super.connectedCallback(); - this.classList.add('conversejs'); - this.classList.add(`converse-${api.settings.get('view_mode')}`); - this.classList.add(`theme-${api.settings.get('theme')}`); - this.setAttribute('id', 'conversejs'); - } - -} - -customElements.define('converse-root', ConverseRoot); -;// CONCATENATED MODULE: ./src/plugins/rootview/utils.js - -function ensureElement() { - if (!api.settings.get('auto_insert')) { - return; - } - - const root = api.settings.get('root'); - - if (!root.querySelector('converse-root#conversejs')) { - const el = document.createElement('converse-root'); - const body = root.querySelector('body'); - - if (body) { - body.appendChild(el); - } else { - root.appendChild(el); // Perhaps inside a web component? - } - } -} -;// CONCATENATED MODULE: ./src/plugins/rootview/index.js - - - -core_converse.plugins.add('converse-rootview', { - initialize() { - api.settings.extend({ - 'auto_insert': true - }); - api.listen.on('chatBoxesInitialized', ensureElement); - } - -}); -;// CONCATENATED MODULE: ./src/modals/templates/add-contact.js - - - -/* harmony default export */ const add_contact = (o => { - const i18n_contact_placeholder = __('name@example.org'); - - const i18n_add = __('Add'); - - const i18n_error_message = __('Please enter a valid XMPP address'); - - const i18n_new_contact = __('Add a Contact'); - - const i18n_xmpp_address = __('XMPP Address'); - - const i18n_nickname = __('Nickname'); - - return T` - - `; -}); -;// CONCATENATED MODULE: ./src/modals/add-contact.js - - - - - - - -const { - Strophe: add_contact_Strophe -} = core_converse.env; -const add_contact_u = core_converse.env.utils; -const AddContactModal = base.extend({ - id: "add-contact-modal", - events: { - 'submit form': 'addContactFromForm' - }, - - initialize() { - base.prototype.initialize.apply(this, arguments); - this.listenTo(this.model, 'change', this.render); - }, - - toHTML() { - const label_nickname = api.settings.get('xhr_user_search_url') ? __('Contact name') : __('Optional nickname'); - return add_contact(Object.assign(this.model.toJSON(), { - _converse: shared_converse, - label_nickname - })); - }, - - afterRender() { - if (typeof api.settings.get('xhr_user_search_url') === 'string') { - this.initXHRAutoComplete(); - } else { - this.initJIDAutoComplete(); - } - - const jid_input = this.el.querySelector('input[name="jid"]'); - this.el.addEventListener('shown.bs.modal', () => jid_input.focus(), false); - }, - - initJIDAutoComplete() { - if (!api.settings.get('autocomplete_add_contact')) { - return; - } - - const el = this.el.querySelector('.suggestion-box__jid').parentElement; - this.jid_auto_complete = new shared_converse.AutoComplete(el, { - 'data': (text, input) => `${input.slice(0, input.indexOf("@"))}@${text}`, - 'filter': shared_converse.FILTER_STARTSWITH, - 'list': [...new Set(shared_converse.roster.map(item => add_contact_Strophe.getDomainFromJid(item.get('jid'))))] - }); - }, - - initXHRAutoComplete() { - if (!api.settings.get('autocomplete_add_contact')) { - return this.initXHRFetch(); - } - - const el = this.el.querySelector('.suggestion-box__name').parentElement; - this.name_auto_complete = new shared_converse.AutoComplete(el, { - 'auto_evaluate': false, - 'filter': shared_converse.FILTER_STARTSWITH, - 'list': [] - }); - const xhr = new window.XMLHttpRequest(); // `open` must be called after `onload` for mock/testing purposes. - - xhr.onload = () => { - if (xhr.responseText) { - const r = xhr.responseText; - this.name_auto_complete.list = JSON.parse(r).map(i => ({ - 'label': i.fullname || i.jid, - 'value': i.jid - })); - this.name_auto_complete.auto_completing = true; - this.name_auto_complete.evaluate(); - } - }; - - const input_el = this.el.querySelector('input[name="name"]'); - input_el.addEventListener('input', lodash_es_debounce(() => { - xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true); - xhr.send(); - }, 300)); - this.name_auto_complete.on('suggestion-box-selectcomplete', ev => { - this.el.querySelector('input[name="name"]').value = ev.text.label; - this.el.querySelector('input[name="jid"]').value = ev.text.value; - }); - }, - - initXHRFetch() { - this.xhr = new window.XMLHttpRequest(); - - this.xhr.onload = () => { - if (this.xhr.responseText) { - const r = this.xhr.responseText; - const list = JSON.parse(r).map(i => ({ - 'label': i.fullname || i.jid, - 'value': i.jid - })); - - if (list.length !== 1) { - const el = this.el.querySelector('.invalid-feedback'); - el.textContent = __('Sorry, could not find a contact with that name'); - add_contact_u.addClass('d-block', el); - return; - } - - const jid = list[0].value; - - if (this.validateSubmission(jid)) { - const form = this.el.querySelector('form'); - const name = list[0].label; - this.afterSubmission(form, jid, name); - } - } - }; - }, - - validateSubmission(jid) { - const el = this.el.querySelector('.invalid-feedback'); - - if (!jid || lodash_es_compact(jid.split('@')).length < 2) { - add_contact_u.addClass('is-invalid', this.el.querySelector('input[name="jid"]')); - add_contact_u.addClass('d-block', el); - return false; - } else if (shared_converse.roster.get(add_contact_Strophe.getBareJidFromJid(jid))) { - el.textContent = __('This contact has already been added'); - add_contact_u.addClass('d-block', el); - return false; - } - - add_contact_u.removeClass('d-block', el); - return true; - }, - - afterSubmission(form, jid, name) { - shared_converse.roster.addAndSubscribe(jid, name); - - this.model.clear(); - this.modal.hide(); - }, - - addContactFromForm(ev) { - ev.preventDefault(); - const data = new FormData(ev.target), - jid = (data.get('jid') || '').trim(); - - if (!jid && typeof api.settings.get('xhr_user_search_url') === 'string') { - const input_el = this.el.querySelector('input[name="name"]'); - this.xhr.open("GET", `${api.settings.get('xhr_user_search_url')}q=${encodeURIComponent(input_el.value)}`, true); - this.xhr.send(); - return; - } - - if (this.validateSubmission(jid)) { - this.afterSubmission(ev.target, jid, data.get('name')); - } - } - -}); -shared_converse.AddContactModal = AddContactModal; -/* harmony default export */ const modals_add_contact = ((/* unused pure expression or super */ null && (AddContactModal))); -;// CONCATENATED MODULE: ./src/plugins/rosterview/utils.js - -function highlightRosterItem(chatbox) { - var _converse$roster, _converse$roster$find; - - (_converse$roster = shared_converse.roster) === null || _converse$roster === void 0 ? void 0 : (_converse$roster$find = _converse$roster.findWhere({ - 'jid': chatbox.get('jid') - })) === null || _converse$roster$find === void 0 ? void 0 : _converse$roster$find.trigger('highlight'); -} -function toggleGroup(ev, name) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - - const collapsed = shared_converse.roster.state.get('collapsed_groups'); - - if (collapsed.includes(name)) { - shared_converse.roster.state.save('collapsed_groups', collapsed.filter(n => n !== name)); - } else { - shared_converse.roster.state.save('collapsed_groups', [...collapsed, name]); - } -} -function isContactFiltered(contact, groupname) { - const filter = shared_converse.roster_filter; - const type = filter.get('filter_type'); - const q = type === 'state' ? filter.get('chat_state').toLowerCase() : filter.get('filter_text').toLowerCase(); - if (!q) return false; - - if (type === 'state') { - const sticky_groups = [shared_converse.HEADER_REQUESTING_CONTACTS, shared_converse.HEADER_UNREAD]; - - if (sticky_groups.includes(groupname)) { - // When filtering by chat state, we still want to - // show sticky groups, even though they don't - // match the state in question. - return false; - } else if (q === 'unread_messages') { - return contact.get('num_unread') === 0; - } else if (q === 'online') { - return ["offline", "unavailable"].includes(contact.presence.get('show')); - } else { - return !contact.presence.get('show').includes(q); - } - } else if (type === 'contacts') { - return !contact.getFilterCriteria().includes(q); - } -} -function shouldShowContact(contact, groupname) { - const chat_status = contact.presence.get('show'); - - if (api.settings.get('hide_offline_users') && chat_status === 'offline') { - // If pending or requesting, show - if (contact.get('ask') === 'subscribe' || contact.get('subscription') === 'from' || contact.get('requesting') === true) { - return !isContactFiltered(contact, groupname); - } - - return false; - } - - return !isContactFiltered(contact, groupname); -} -function shouldShowGroup(group) { - const filter = shared_converse.roster_filter; - const type = filter.get('filter_type'); - - if (type === 'groups') { - var _filter$get; - - const q = (_filter$get = filter.get('filter_text')) === null || _filter$get === void 0 ? void 0 : _filter$get.toLowerCase(); - - if (!q) { - return true; - } - - if (!group.toLowerCase().includes(q)) { - return false; - } - } - - return true; -} -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/group.js - - - - - -const { - u: group_u -} = core_converse.env; - -function renderContact(contact) { - const jid = contact.get('jid'); - const extra_classes = []; - - if (shared_converse.isUniView()) { - const chatbox = shared_converse.chatboxes.get(jid); - - if (chatbox && !chatbox.get('hidden')) { - extra_classes.push('open'); - } - } - - const ask = contact.get('ask'); - const requesting = contact.get('requesting'); - const subscription = contact.get('subscription'); - - if (ask === 'subscribe' || subscription === 'from') { - /* ask === 'subscribe' - * Means we have asked to subscribe to them. - * - * subscription === 'from' - * They are subscribed to us, but not vice versa. - * We assume that there is a pending subscription - * from us to them (otherwise we're in a state not - * supported by converse.js). - * - * So in both cases the user is a "pending" contact. - */ - extra_classes.push('pending-xmpp-contact'); - } else if (requesting === true) { - extra_classes.push('requesting-xmpp-contact'); - } else if (subscription === 'both' || subscription === 'to' || group_u.isSameBareJID(jid, shared_converse.connection.jid)) { - extra_classes.push('current-xmpp-contact'); - extra_classes.push(subscription); - extra_classes.push(contact.presence.get('show')); - } - - return T` -
  • - -
  • `; -} - -/* harmony default export */ const group = (o => { - const i18n_title = __('Click to hide these contacts'); - - const collapsed = shared_converse.roster.state.get('collapsed_groups'); - - return T` -
    - toggleGroup(ev, o.name)}> - ${o.name} - -
      - ${o.contacts.map(renderContact)} -
    -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/roster.js - - - - - - - - -function populateContactsMap(contacts_map, contact) { - if (contact.get('ask') === 'subscribe') { - const name = shared_converse.HEADER_PENDING_CONTACTS; - contacts_map[name] ? contacts_map[name].push(contact) : contacts_map[name] = [contact]; - } else if (contact.get('requesting')) { - const name = shared_converse.HEADER_REQUESTING_CONTACTS; - contacts_map[name] ? contacts_map[name].push(contact) : contacts_map[name] = [contact]; - } else { - let contact_groups; - - if (api.settings.get('roster_groups')) { - contact_groups = contact.get('groups'); - contact_groups = contact_groups.length === 0 ? [shared_converse.HEADER_UNGROUPED] : contact_groups; - } else { - contact_groups = [shared_converse.HEADER_CURRENT_CONTACTS]; - } - - for (const name of contact_groups) { - contacts_map[name] ? contacts_map[name].push(contact) : contacts_map[name] = [contact]; - } - } - - if (contact.get('num_unread')) { - const name = shared_converse.HEADER_UNREAD; - contacts_map[name] ? contacts_map[name].push(contact) : contacts_map[name] = [contact]; - } - - return contacts_map; -} - -/* harmony default export */ const roster = (el => { - const i18n_heading_contacts = __('Contacts'); - - const i18n_title_add_contact = __('Add a contact'); - - const i18n_title_sync_contacts = __('Re-sync your contacts'); - - const roster = shared_converse.roster || []; - const contacts_map = roster.reduce((acc, contact) => populateContactsMap(acc, contact), {}); - const groupnames = Object.keys(contacts_map).filter(shouldShowGroup); - groupnames.sort(groupsComparator); - return T` - - -
    - ${repeat_c(groupnames, n => n, name => { - const contacts = contacts_map[name].filter(c => shouldShowContact(c, name)); - contacts.sort(contactsComparator); - - if (contacts.length) { - return group({ - 'contacts': contacts, - 'name': name - }); - } else { - return ''; - } - })} -
    - `; -}); -;// CONCATENATED MODULE: ./src/plugins/rosterview/rosterview.js - - - - -/** - * @class - * @namespace _converse.RosterView - * @memberOf _converse - */ - -class RosterView extends CustomElement { - constructor() { - super(); - this.initialize(); - } - - async initialize() { - await api.waitUntil('rosterInitialized'); - this.listenTo(shared_converse, 'rosterContactsFetched', this.requestUpdate); - this.listenTo(shared_converse.presences, 'change:show', this.requestUpdate); - this.listenTo(shared_converse.roster, 'add', this.requestUpdate); - this.listenTo(shared_converse.roster, 'destroy', this.requestUpdate); - this.listenTo(shared_converse.roster, 'remove', this.requestUpdate); - this.listenTo(shared_converse.roster, 'change', this.requestUpdate); - this.listenTo(shared_converse.roster.state, 'change', this.requestUpdate); - /** - * Triggered once the _converse.RosterView instance has been created and initialized. - * @event _converse#rosterViewInitialized - * @example _converse.api.listen.on('rosterViewInitialized', () => { ... }); - */ - - api.trigger('rosterViewInitialized'); - } - - firstUpdated() { - this.listenToRosterFilter(); - } - - render() { - return roster(this); - } - - listenToRosterFilter() { - this.filter_view = this.querySelector('converse-roster-filter'); - this.filter_view.addEventListener('update', () => this.requestUpdate()); - } - - showAddContactModal(ev) { - // eslint-disable-line class-methods-use-this - api.modal.show(shared_converse.AddContactModal, { - 'model': new Model() - }, ev); - } - - async syncContacts(ev) { - // eslint-disable-line class-methods-use-this - ev.preventDefault(); - this.syncing_contacts = true; - this.requestUpdate(); - - shared_converse.roster.data.save('version', null); - - await shared_converse.roster.fetchFromServer(); - api.user.presence.send(); - this.syncing_contacts = false; - this.requestUpdate(); - } - -} -api.elements.define('converse-roster', RosterView); -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/pending_contact.js - - - - -const tpl_pending_contact = o => T`${o.display_name}`; - -/* harmony default export */ const pending_contact = (o => { - const i18n_remove = __('Click to remove %1$s as a contact', o.display_name); - - return T` - ${api.settings.get('allow_chat_pending_contacts') ? T`${tpl_pending_contact(o)}` : tpl_pending_contact(o)} - `; -}); -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/requesting_contact.js - - - -const tpl_requesting_contact = o => T`${o.display_name}`; - -/* harmony default export */ const requesting_contact = (o => T` - ${api.settings.get('allow_chat_pending_contacts') ? T`${tpl_requesting_contact(o)}` : tpl_requesting_contact(o)} - - `); -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/roster_item.js - - - - -/* harmony default export */ const roster_item = (o => { - const i18n_chat = __('Click to chat with %1$s (XMPP address: %2$s)', o.display_name, o.jid); - - const i18n_remove = __('Click to remove %1$s as a contact', o.display_name); - - return T` - - ${renderAvatar(o.getAvatarData())} - - ${o.num_unread ? T`${o.num_unread}` : ''} - ${o.display_name} - - ${api.settings.get('allow_contact_removal') ? T`` : ''}`; -}); -;// CONCATENATED MODULE: ./src/plugins/rosterview/contactview.js - - - - - - - -const contactview_u = core_converse.env.utils; -class contactview_RosterContact extends CustomElement { - static get properties() { - return { - model: { - type: Object - } - }; - } - - connectedCallback() { - super.connectedCallback(); - this.listenTo(this.model, "change", this.requestUpdate); - this.listenTo(this.model, "highlight", this.requestUpdate); - this.listenTo(this.model, 'vcard:change', this.requestUpdate); - } - - render() { - const ask = this.model.get('ask'); - const requesting = this.model.get('requesting'); - const subscription = this.model.get('subscription'); - const jid = this.model.get('jid'); - - if (ask === 'subscribe' || subscription === 'from') { - /* ask === 'subscribe' - * Means we have asked to subscribe to them. - * - * subscription === 'from' - * They are subscribed to use, but not vice versa. - * We assume that there is a pending subscription - * from us to them (otherwise we're in a state not - * supported by converse.js). - * - * So in both cases the user is a "pending" contact. - */ - const display_name = this.model.getDisplayName(); - return pending_contact(Object.assign(this.model.toJSON(), { - display_name, - 'openChat': ev => this.openChat(ev), - 'removeContact': ev => this.removeContact(ev) - })); - } else if (requesting === true) { - const display_name = this.model.getDisplayName(); - return requesting_contact(Object.assign(this.model.toJSON(), { - display_name, - 'openChat': ev => this.openChat(ev), - 'acceptRequest': ev => this.acceptRequest(ev), - 'declineRequest': ev => this.declineRequest(ev), - 'desc_accept': __("Click to accept the contact request from %1$s", display_name), - 'desc_decline': __("Click to decline the contact request from %1$s", display_name), - 'allow_chat_pending_contacts': api.settings.get('allow_chat_pending_contacts') - })); - } else if (subscription === 'both' || subscription === 'to' || contactview_u.isSameBareJID(jid, shared_converse.connection.jid)) { - return this.renderRosterItem(this.model); - } - } - - renderRosterItem(item) { - const STATUSES = { - 'dnd': __('This contact is busy'), - 'online': __('This contact is online'), - 'offline': __('This contact is offline'), - 'unavailable': __('This contact is unavailable'), - 'xa': __('This contact is away for an extended period'), - 'away': __('This contact is away') - }; - const show = item.presence.get('show') || 'offline'; - let status_icon; - - if (show === 'online') { - status_icon = 'fa fa-circle chat-status chat-status--online'; - } else if (show === 'away') { - status_icon = 'fa fa-circle chat-status chat-status--away'; - } else if (show === 'xa') { - status_icon = 'far fa-circle chat-status chat-status-xa'; - } else if (show === 'dnd') { - status_icon = 'fa fa-minus-circle chat-status chat-status--busy'; - } else { - status_icon = 'fa fa-times-circle chat-status chat-status--offline'; - } - - const display_name = item.getDisplayName(); - return roster_item(Object.assign(item.toJSON(), { - show, - display_name, - status_icon, - 'openChat': ev => this.openChat(ev), - 'removeContact': ev => this.removeContact(ev), - 'getAvatarData': () => this.getAvatarData(), - 'desc_status': STATUSES[show], - 'num_unread': item.get('num_unread') || 0 - })); - } - - getAvatarData() { - var _this$model$vcard, _this$model$vcard2; - - const image_type = ((_this$model$vcard = this.model.vcard) === null || _this$model$vcard === void 0 ? void 0 : _this$model$vcard.get('image_type')) || shared_converse.DEFAULT_IMAGE_TYPE; - const image_data = ((_this$model$vcard2 = this.model.vcard) === null || _this$model$vcard2 === void 0 ? void 0 : _this$model$vcard2.get('image')) || shared_converse.DEFAULT_IMAGE; - const image = "data:" + image_type + ";base64," + image_data; - return { - 'classes': 'avatar', - 'height': 30, - 'width': 30, - image - }; - } - - openChat(ev) { - var _ev$preventDefault; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault = ev.preventDefault) === null || _ev$preventDefault === void 0 ? void 0 : _ev$preventDefault.call(ev); - this.model.openChat(); - } - - removeContact(ev) { - var _ev$preventDefault2; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault2 = ev.preventDefault) === null || _ev$preventDefault2 === void 0 ? void 0 : _ev$preventDefault2.call(ev); - - if (!api.settings.get('allow_contact_removal')) { - return; - } - - if (!confirm(__("Are you sure you want to remove this contact?"))) { - return; - } - - try { - this.model.removeFromRoster(); - - if (this.model.collection) { - // The model might have already been removed as - // result of a roster push. - this.model.destroy(); - } - } catch (e) { - headless_log.error(e); - api.alert('error', __('Error'), [__('Sorry, there was an error while trying to remove %1$s as a contact.', this.model.getDisplayName())]); - } - } - - async acceptRequest(ev) { - var _ev$preventDefault3; - - ev === null || ev === void 0 ? void 0 : (_ev$preventDefault3 = ev.preventDefault) === null || _ev$preventDefault3 === void 0 ? void 0 : _ev$preventDefault3.call(ev); - await shared_converse.roster.sendContactAddIQ(this.model.get('jid'), this.model.getFullname(), []); - this.model.authorize().subscribe(); - } - - declineRequest(ev) { - if (ev && ev.preventDefault) { - ev.preventDefault(); - } - - const result = confirm(__("Are you sure you want to decline this contact request?")); - - if (result === true) { - this.model.unauthorize().destroy(); - } - - return this; - } - -} -api.elements.define('converse-roster-contact', contactview_RosterContact); -;// CONCATENATED MODULE: ./src/plugins/rosterview/templates/roster_filter.js - - -/* harmony default export */ const roster_filter = (o => { - const i18n_placeholder = __('Filter'); - - const title_contact_filter = __('Filter by contact name'); - - const title_group_filter = __('Filter by group name'); - - const title_status_filter = __('Filter by status'); - - const label_any = __('Any'); - - const label_unread_messages = __('Unread'); - - const label_online = __('Online'); - - const label_chatty = __('Chatty'); - - const label_busy = __('Busy'); - - const label_away = __('Away'); - - const label_xa = __('Extended Away'); - - const label_offline = __('Offline'); - - return T` -
    -
    -
    - - - -
    -
    - - - -
    - -
    -
    `; -}); -;// CONCATENATED MODULE: ./src/plugins/rosterview/filterview.js - - - - - - -const RosterFilter = Model.extend({ - initialize() { - this.set({ - 'filter_text': '', - 'filter_type': 'contacts', - 'chat_state': 'online' - }); - } - -}); -class RosterFilterView extends CustomElement { - constructor() { - super(); - this.initialize(); - } - - initialize() { - const model = new shared_converse.RosterFilter(); - model.id = `_converse.rosterfilter-${shared_converse.bare_jid}`; - initStorage(model, model.id); - this.model = model; - shared_converse.roster_filter = model; - this.liveFilter = lodash_es_debounce(() => { - this.model.save({ - 'filter_text': this.querySelector('.roster-filter').value - }); - }, 250); - this.listenTo(shared_converse, 'rosterContactsFetched', this.requestUpdate); - this.listenTo(shared_converse.presences, 'change:show', this.requestUpdate); - this.listenTo(shared_converse.roster, "add", this.requestUpdate); - this.listenTo(shared_converse.roster, "destroy", this.requestUpdate); - this.listenTo(shared_converse.roster, "remove", this.requestUpdate); - this.listenTo(this.model, 'change', this.dispatchUpdateEvent); - this.listenTo(this.model, 'change', this.requestUpdate); - this.model.fetch(); - } - - render() { - return roster_filter(Object.assign(this.model.toJSON(), { - visible: this.shouldBeVisible(), - changeChatStateFilter: ev => this.changeChatStateFilter(ev), - changeTypeFilter: ev => this.changeTypeFilter(ev), - clearFilter: ev => this.clearFilter(ev), - liveFilter: ev => this.liveFilter(ev), - submitFilter: ev => this.submitFilter(ev) - })); - } - - dispatchUpdateEvent() { - this.dispatchEvent(new CustomEvent('update', { - 'detail': this.model.changed - })); - } - - changeChatStateFilter(ev) { - ev && ev.preventDefault(); - this.model.save({ - 'chat_state': this.querySelector('.state-type').value - }); - } - - changeTypeFilter(ev) { - ev && ev.preventDefault(); - const type = ev.target.dataset.type; - - if (type === 'state') { - this.model.save({ - 'filter_type': type, - 'chat_state': this.querySelector('.state-type').value - }); - } else { - this.model.save({ - 'filter_type': type, - 'filter_text': this.querySelector('.roster-filter').value - }); - } - } - - submitFilter(ev) { - ev && ev.preventDefault(); - this.liveFilter(); - } - /** - * Returns true if the filter is enabled (i.e. if the user - * has added values to the filter). - * @private - * @method _converse.RosterFilterView#isActive - */ - - - isActive() { - return this.model.get('filter_type') === 'state' || this.model.get('filter_text'); - } - - shouldBeVisible() { - var _converse$roster; - - return ((_converse$roster = shared_converse.roster) === null || _converse$roster === void 0 ? void 0 : _converse$roster.length) >= 5 || this.isActive(); - } - - clearFilter(ev) { - ev && ev.preventDefault(); - this.model.save({ - 'filter_text': '' - }); - } - -} -api.elements.define('converse-roster-filter', RosterFilterView); -// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[3].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[3].use[3]!./node_modules/mini-css-extract-plugin/dist/loader.js!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[6].use[1]!./node_modules/postcss-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[6].use[3]!./src/plugins/rosterview/styles/roster.scss -var styles_roster = __webpack_require__(74); -;// CONCATENATED MODULE: ./src/plugins/rosterview/styles/roster.scss - - - - - - - - - - - -var roster_options = {}; - -roster_options.styleTagTransform = (styleTagTransform_default()); -roster_options.setAttributes = (setAttributesWithoutAttributes_default()); - - roster_options.insert = insertBySelector_default().bind(null, "head"); - -roster_options.domAPI = (styleDomAPI_default()); -roster_options.insertStyleElement = (insertStyleElement_default()); - -var roster_update = injectStylesIntoStyleTag_default()(styles_roster/* default */.Z, roster_options); - - - - - /* harmony default export */ const rosterview_styles_roster = (styles_roster/* default */.Z && styles_roster/* default.locals */.Z.locals ? styles_roster/* default.locals */.Z.locals : undefined); - -;// CONCATENATED MODULE: ./src/plugins/rosterview/index.js -/** - * @copyright 2020, the Converse.js contributors - * @license Mozilla Public License (MPLv2) - */ - - - - - - - - - - -core_converse.plugins.add('converse-rosterview', { - dependencies: ["converse-roster", "converse-modal", "converse-chatboxviews"], - - initialize() { - api.settings.extend({ - 'autocomplete_add_contact': true, - 'allow_chat_pending_contacts': true, - 'allow_contact_removal': true, - 'hide_offline_users': false, - 'roster_groups': true, - 'xhr_user_search_url': null - }); - api.promises.add('rosterViewInitialized'); - shared_converse.RosterFilter = RosterFilter; - shared_converse.RosterFilterView = RosterFilterView; - shared_converse.RosterContactView = contactview_RosterContact; - /* -------- Event Handlers ----------- */ - - api.listen.on('chatBoxesInitialized', () => { - shared_converse.chatboxes.on('destroy', chatbox => highlightRosterItem(chatbox)); - - shared_converse.chatboxes.on('change:hidden', chatbox => highlightRosterItem(chatbox)); - }); - api.listen.on('afterTearDown', () => { - var _converse$rotergroups; - - return (_converse$rotergroups = shared_converse.rotergroups) === null || _converse$rotergroups === void 0 ? void 0 : _converse$rotergroups.off().reset(); - }); - } - -}); -;// CONCATENATED MODULE: ./src/converse.js -/** - * @description Converse.js (A browser based XMPP chat client) - * @copyright 2021, The Converse developers - * @license Mozilla Public License (MPLv2) - */ - - - - - - - -/* START: Removable plugins - * ------------------------ - * Any of the following plugin imports may be removed if the plugin is not needed - */ - - // Views for XEP-0048 Bookmarks - - // Renders standalone chat boxes for single user chat - - // The control box - - // Allows chat boxes to be resized by dragging them - - - - - // Allows chat boxes to be minimized - - // Views related to MUC - - - - - // XEP-0357 Push Notifications - - // XEP-0077 In-band registration - - // Show currently open chat rooms - - - - -/* END: Removable components */ - -shared_converse.CustomElement = CustomElement; -const initialize = core_converse.initialize; - -core_converse.initialize = function (settings, callback) { - if (Array.isArray(settings.whitelisted_plugins)) { - settings.whitelisted_plugins = settings.whitelisted_plugins.concat(VIEW_PLUGINS); - } else { - settings.whitelisted_plugins = VIEW_PLUGINS; - } - - return initialize(settings, callback); -}; - -/* harmony default export */ const src_converse = (core_converse); - -/***/ }), - -/***/ 6300: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 3742: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 7904: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 4639: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 6220: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 9107: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 7581: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 6752: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 115: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 8859: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 7926: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 1557: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 2490: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 679: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 1107: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 74: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 8481: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 8269: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 9796: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 5223: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 7991: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 7415: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 1065: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 3735: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 3543: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 4452: -/***/ ((module, __webpack_exports__, __webpack_require__) => { - -"use strict"; -/* harmony export */ __webpack_require__.d(__webpack_exports__, { -/* harmony export */ "Z": () => (__WEBPACK_DEFAULT_EXPORT__) -/* harmony export */ }); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7620); -/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1246); -/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__); -// Imports - - -var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default())); -// Module -___CSS_LOADER_EXPORT___.push([module.id, "", "",{"version":3,"sources":[],"names":[],"mappings":"","sourceRoot":""}]); -// Exports -/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___); - - -/***/ }), - -/***/ 9434: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var map = { - "./af.js": [ - 7905, - 9210 - ], - "./am.js": [ - 2067, - 5073 - ], - "./ar-dz.js": [ - 7171, - 9406 - ], - "./ar-kw.js": [ - 6712, - 9897 - ], - "./ar-ly.js": [ - 4198, - 3521 - ], - "./ar-ma.js": [ - 5390, - 5313 - ], - "./ar-sa.js": [ - 8916, - 485 - ], - "./ar-tn.js": [ - 5507, - 8040 - ], - "./ar.js": [ - 8817, - 6755 - ], - "./az.js": [ - 978, - 4963 - ], - "./be.js": [ - 8413, - 9478 - ], - "./bg.js": [ - 7575, - 578 - ], - "./bi.js": [ - 7186, - 2984 - ], - "./bm.js": [ - 7646, - 2263 - ], - "./bn.js": [ - 2760, - 280 - ], - "./bo.js": [ - 10, - 9950 - ], - "./br.js": [ - 5476, - 760 - ], - "./bs.js": [ - 8581, - 9833 - ], - "./ca.js": [ - 545, - 102 - ], - "./cs.js": [ - 7831, - 7400 - ], - "./cv.js": [ - 4598, - 4481 - ], - "./cy.js": [ - 5401, - 6740 - ], - "./da.js": [ - 3499, - 2548 - ], - "./de-at.js": [ - 4026, - 7175 - ], - "./de-ch.js": [ - 7423, - 1679 - ], - "./de.js": [ - 1857, - 52 - ], - "./dv.js": [ - 8027, - 5569 - ], - "./el.js": [ - 4542, - 1606 - ], - "./en-au.js": [ - 8781, - 5485 - ], - "./en-ca.js": [ - 7867, - 4035 - ], - "./en-gb.js": [ - 4147, - 6031 - ], - "./en-ie.js": [ - 5454, - 8129 - ], - "./en-il.js": [ - 7199, - 3463 - ], - "./en-in.js": [ - 8109, - 6898 - ], - "./en-nz.js": [ - 9102, - 8547 - ], - "./en-sg.js": [ - 8113, - 1735 - ], - "./en-tt.js": [ - 5859, - 6105 - ], - "./en.js": [ - 1606, - 535 - ], - "./eo.js": [ - 5545, - 5121 - ], - "./es-do.js": [ - 5839, - 8758 - ], - "./es-pr.js": [ - 3979, - 911 - ], - "./es-us.js": [ - 8624, - 3208 - ], - "./es.js": [ - 4153, - 3411 - ], - "./et.js": [ - 7754, - 4153 - ], - "./eu.js": [ - 2697, - 1396 - ], - "./fa.js": [ - 9068, - 5544 - ], - "./fi.js": [ - 6587, - 2130 - ], - "./fo.js": [ - 26, - 8745 - ], - "./fr-ca.js": [ - 3026, - 7363 - ], - "./fr-ch.js": [ - 2246, - 7952 - ], - "./fr.js": [ - 8278, - 1910 - ], - "./fy.js": [ - 4172, - 6376 - ], - "./ga.js": [ - 9691, - 688 - ], - "./gd.js": [ - 2713, - 5050 - ], - "./gl.js": [ - 6212, - 5818 - ], - "./gom-latn.js": [ - 5516, - 825 - ], - "./gu.js": [ - 945, - 3623 - ], - "./he.js": [ - 7346, - 9372 - ], - "./hi.js": [ - 802, - 8010 - ], - "./hr.js": [ - 1804, - 7419 - ], - "./ht.js": [ - 8259, - 5822 - ], - "./hu.js": [ - 3650, - 8214 - ], - "./hy-am.js": [ - 4800, - 5407 - ], - "./id.js": [ - 5275, - 9513 - ], - "./is.js": [ - 4039, - 1194 - ], - "./it-ch.js": [ - 8846, - 6010 - ], - "./it.js": [ - 6792, - 1880 - ], - "./ja.js": [ - 4679, - 1107 - ], - "./jv.js": [ - 4770, - 4305 - ], - "./ka.js": [ - 7564, - 5186 - ], - "./kk.js": [ - 7316, - 5206 - ], - "./km.js": [ - 1209, - 2475 - ], - "./kn.js": [ - 6713, - 7523 - ], - "./ko.js": [ - 9735, - 3446 - ], - "./ku.js": [ - 5121, - 7024 - ], - "./ky.js": [ - 5901, - 5055 - ], - "./lb.js": [ - 4565, - 5215 - ], - "./lo.js": [ - 8525, - 1204 - ], - "./lt.js": [ - 1305, - 7899 - ], - "./lv.js": [ - 9629, - 631 - ], - "./me.js": [ - 913, - 145 - ], - "./mi.js": [ - 3389, - 7454 - ], - "./mk.js": [ - 5524, - 4951 - ], - "./ml.js": [ - 5321, - 7679 - ], - "./mn.js": [ - 8082, - 8618 - ], - "./mr.js": [ - 3838, - 5600 - ], - "./ms-my.js": [ - 5435, - 882 - ], - "./ms.js": [ - 1525, - 9095 - ], - "./mt.js": [ - 2680, - 9665 - ], - "./my.js": [ - 1390, - 5166 - ], - "./nb.js": [ - 192, - 646 - ], - "./ne.js": [ - 9534, - 9030 - ], - "./nl-be.js": [ - 307, - 3155 - ], - "./nl.js": [ - 808, - 1520 - ], - "./nn.js": [ - 4644, - 7050 - ], - "./oc-lnc.js": [ - 9099, - 7203 - ], - "./pa-in.js": [ - 7117, - 5850 - ], - "./pl.js": [ - 5406, - 1211 - ], - "./pt-br.js": [ - 1291, - 5274 - ], - "./pt.js": [ - 402, - 265 - ], - "./ro.js": [ - 4491, - 8022 - ], - "./ru.js": [ - 1187, - 559 - ], - "./rw.js": [ - 8978, - 3221 - ], - "./sd.js": [ - 454, - 1298 - ], - "./se.js": [ - 1708, - 1942 - ], - "./si.js": [ - 5334, - 9333 - ], - "./sk.js": [ - 7506, - 6783 - ], - "./sl.js": [ - 7608, - 9625 - ], - "./sq.js": [ - 2402, - 8603 - ], - "./sr-cyrl.js": [ - 7786, - 3435 - ], - "./sr.js": [ - 8407, - 7390 - ], - "./ss.js": [ - 2068, - 9238 - ], - "./sv-fi.js": [ - 1057, - 9997 - ], - "./sv.js": [ - 4316, - 9652 - ], - "./sw.js": [ - 9716, - 9733 - ], - "./ta.js": [ - 1541, - 7645 - ], - "./te.js": [ - 5649, - 7714 - ], - "./tet.js": [ - 6539, - 555 - ], - "./tg.js": [ - 222, - 2446 - ], - "./th.js": [ - 4805, - 1729 - ], - "./tk.js": [ - 2679, - 5256 - ], - "./tl-ph.js": [ - 821, - 9443 - ], - "./tlh.js": [ - 9201, - 2814 - ], - "./tr.js": [ - 8273, - 8665 - ], - "./tzl.js": [ - 4837, - 2843 - ], - "./tzm-latn.js": [ - 7003, - 3933 - ], - "./tzm.js": [ - 2165, - 4342 - ], - "./ug-cn.js": [ - 810, - 6890 - ], - "./uk.js": [ - 6638, - 1619 - ], - "./ur.js": [ - 9542, - 9568 - ], - "./uz-latn.js": [ - 3089, - 1110 - ], - "./uz.js": [ - 3380, - 3153 - ], - "./vi.js": [ - 2718, - 8073 - ], - "./x-pseudo.js": [ - 6316, - 4423 - ], - "./yo.js": [ - 2863, - 8692 - ], - "./zh-cn.js": [ - 4385, - 9630 - ], - "./zh-hk.js": [ - 4338, - 3755 - ], - "./zh-tw.js": [ - 1208, - 6776 - ], - "./zh.js": [ - 8383, - 8458 - ] -}; -function webpackAsyncContext(req) { - if(!__webpack_require__.o(map, req)) { - return Promise.resolve().then(() => { - var e = new Error("Cannot find module '" + req + "'"); - e.code = 'MODULE_NOT_FOUND'; - throw e; - }); - } - - var ids = map[req], id = ids[0]; - return __webpack_require__.e(ids[1]).then(() => { - return __webpack_require__.t(id, 7); - }); -} -webpackAsyncContext.keys = () => (Object.keys(map)); -webpackAsyncContext.id = 9434; -module.exports = webpackAsyncContext; - -/***/ }), - -/***/ 3379: -/***/ ((module) => { - -"use strict"; - - -var stylesInDom = []; - -function getIndexByIdentifier(identifier) { - var result = -1; - - for (var i = 0; i < stylesInDom.length; i++) { - if (stylesInDom[i].identifier === identifier) { - result = i; - break; - } - } - - return result; -} - -function modulesToDom(list, options) { - var idCountMap = {}; - var identifiers = []; - - for (var i = 0; i < list.length; i++) { - var item = list[i]; - var id = options.base ? item[0] + options.base : item[0]; - var count = idCountMap[id] || 0; - var identifier = "".concat(id, " ").concat(count); - idCountMap[id] = count + 1; - var index = getIndexByIdentifier(identifier); - var obj = { - css: item[1], - media: item[2], - sourceMap: item[3] - }; - - if (index !== -1) { - stylesInDom[index].references++; - stylesInDom[index].updater(obj); - } else { - stylesInDom.push({ - identifier: identifier, - updater: addStyle(obj, options), - references: 1 - }); - } - - identifiers.push(identifier); - } - - return identifiers; -} - -function addStyle(obj, options) { - var api = options.domAPI(options); - api.update(obj); - return function updateStyle(newObj) { - if (newObj) { - if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) { - return; - } - - api.update(obj = newObj); - } else { - api.remove(); - } - }; -} - -module.exports = function (list, options) { - options = options || {}; - list = list || []; - var lastIdentifiers = modulesToDom(list, options); - return function update(newList) { - newList = newList || []; - - for (var i = 0; i < lastIdentifiers.length; i++) { - var identifier = lastIdentifiers[i]; - var index = getIndexByIdentifier(identifier); - stylesInDom[index].references--; - } - - var newLastIdentifiers = modulesToDom(newList, options); - - for (var _i = 0; _i < lastIdentifiers.length; _i++) { - var _identifier = lastIdentifiers[_i]; - - var _index = getIndexByIdentifier(_identifier); - - if (stylesInDom[_index].references === 0) { - stylesInDom[_index].updater(); - - stylesInDom.splice(_index, 1); - } - } - - lastIdentifiers = newLastIdentifiers; - }; -}; - -/***/ }), - -/***/ 569: -/***/ ((module) => { - -"use strict"; - - -var memo = {}; -/* istanbul ignore next */ - -function getTarget(target) { - if (typeof memo[target] === "undefined") { - var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself - - if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) { - try { - // This will throw an exception if access to iframe is blocked - // due to cross-origin restrictions - styleTarget = styleTarget.contentDocument.head; - } catch (e) { - // istanbul ignore next - styleTarget = null; - } - } - - memo[target] = styleTarget; - } - - return memo[target]; -} -/* istanbul ignore next */ - - -function insertBySelector(insert, style) { - var target = getTarget(insert); - - if (!target) { - throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."); - } - - target.appendChild(style); -} - -module.exports = insertBySelector; - -/***/ }), - -/***/ 9216: -/***/ ((module) => { - -"use strict"; - - -/* istanbul ignore next */ -function insertStyleElement(options) { - var style = document.createElement("style"); - options.setAttributes(style, options.attributes); - options.insert(style); - return style; -} - -module.exports = insertStyleElement; - -/***/ }), - -/***/ 3565: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -"use strict"; - - -/* istanbul ignore next */ -function setAttributesWithoutAttributes(style) { - var nonce = true ? __webpack_require__.nc : 0; - - if (nonce) { - style.setAttribute("nonce", nonce); - } -} - -module.exports = setAttributesWithoutAttributes; - -/***/ }), - -/***/ 7795: -/***/ ((module) => { - -"use strict"; - - -/* istanbul ignore next */ -function apply(style, options, obj) { - var css = obj.css; - var media = obj.media; - var sourceMap = obj.sourceMap; - - if (media) { - style.setAttribute("media", media); - } else { - style.removeAttribute("media"); - } - - if (sourceMap && typeof btoa !== "undefined") { - css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */"); - } // For old IE - - /* istanbul ignore if */ - - - options.styleTagTransform(css, style); -} - -function removeStyleElement(style) { - // istanbul ignore if - if (style.parentNode === null) { - return false; - } - - style.parentNode.removeChild(style); -} -/* istanbul ignore next */ - - -function domAPI(options) { - var style = options.insertStyleElement(options); - return { - update: function update(obj) { - apply(style, options, obj); - }, - remove: function remove() { - removeStyleElement(style); - } - }; -} - -module.exports = domAPI; - -/***/ }), - -/***/ 4589: -/***/ ((module) => { - -"use strict"; - - -/* istanbul ignore next */ -function styleTagTransform(css, style) { - if (style.styleSheet) { - style.styleSheet.cssText = css; - } else { - while (style.firstChild) { - style.removeChild(style.firstChild); - } - - style.appendChild(document.createTextNode(css)); - } -} - -module.exports = styleTagTransform; - -/***/ }), - -/***/ 7521: -/***/ ((module, __unused_webpack_exports, __webpack_require__) => { - -var map = { - "./af/LC_MESSAGES/converse.po": [ - 2866, - 5830 - ], - "./ar/LC_MESSAGES/converse.po": [ - 7693, - 4469 - ], - "./bg/LC_MESSAGES/converse.po": [ - 5638, - 2551 - ], - "./ca/LC_MESSAGES/converse.po": [ - 6124, - 1553 - ], - "./cs/LC_MESSAGES/converse.po": [ - 4283, - 5301 - ], - "./da/LC_MESSAGES/converse.po": [ - 8135, - 1163 - ], - "./de/LC_MESSAGES/converse.po": [ - 254, - 2895 - ], - "./el/LC_MESSAGES/converse.po": [ - 8778, - 5524 - ], - "./eo/LC_MESSAGES/converse.po": [ - 4111, - 2433 - ], - "./es/LC_MESSAGES/converse.po": [ - 3516, - 8269 - ], - "./eu/LC_MESSAGES/converse.po": [ - 6160, - 3103 - ], - "./fa/LC_MESSAGES/converse.po": [ - 7167, - 321 - ], - "./fi/LC_MESSAGES/converse.po": [ - 5819, - 7618 - ], - "./fr/LC_MESSAGES/converse.po": [ - 7500, - 5129 - ], - "./gl/LC_MESSAGES/converse.po": [ - 8429, - 777 - ], - "./he/LC_MESSAGES/converse.po": [ - 2233, - 4363 - ], - "./hi/LC_MESSAGES/converse.po": [ - 7785, - 4468 - ], - "./hu/LC_MESSAGES/converse.po": [ - 2325, - 6239 - ], - "./id/LC_MESSAGES/converse.po": [ - 4938, - 6678 - ], - "./it/LC_MESSAGES/converse.po": [ - 2755, - 3719 - ], - "./ja/LC_MESSAGES/converse.po": [ - 2693, - 6249 - ], - "./lt/LC_MESSAGES/converse.po": [ - 3547, - 513 - ], - "./mr/LC_MESSAGES/converse.po": [ - 2260, - 1784 - ], - "./nb/LC_MESSAGES/converse.po": [ - 9701, - 473 - ], - "./nl/LC_MESSAGES/converse.po": [ - 3175, - 2473 - ], - "./nl_BE/LC_MESSAGES/converse.po": [ - 658, - 8131 - ], - "./oc/LC_MESSAGES/converse.po": [ - 506, - 5500 - ], - "./pl/LC_MESSAGES/converse.po": [ - 275, - 3606 - ], - "./pt/LC_MESSAGES/converse.po": [ - 8300, - 6227 - ], - "./pt_BR/LC_MESSAGES/converse.po": [ - 7606, - 1455 - ], - "./ro/LC_MESSAGES/converse.po": [ - 6899, - 3539 - ], - "./ru/LC_MESSAGES/converse.po": [ - 499, - 7917 - ], - "./sv/LC_MESSAGES/converse.po": [ - 164, - 8859 - ], - "./th/LC_MESSAGES/converse.po": [ - 4307, - 457 - ], - "./tr/LC_MESSAGES/converse.po": [ - 5882, - 4195 - ], - "./uk/LC_MESSAGES/converse.po": [ - 6264, - 7979 - ], - "./vi/LC_MESSAGES/converse.po": [ - 1937, - 2110 - ], - "./zh_CN/LC_MESSAGES/converse.po": [ - 6592, - 3325 - ], - "./zh_TW/LC_MESSAGES/converse.po": [ - 1288, - 1458 - ] -}; -function webpackAsyncContext(req) { - if(!__webpack_require__.o(map, req)) { - return Promise.resolve().then(() => { - var e = new Error("Cannot find module '" + req + "'"); - e.code = 'MODULE_NOT_FOUND'; - throw e; - }); - } - - var ids = map[req], id = ids[0]; - return __webpack_require__.e(ids[1]).then(() => { - return __webpack_require__.t(id, 3); - }); -} -webpackAsyncContext.keys = () => (Object.keys(map)); -webpackAsyncContext.id = 7521; -module.exports = webpackAsyncContext; - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ id: moduleId, -/******/ loaded: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.loaded = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = __webpack_modules__; -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __webpack_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/create fake namespace object */ -/******/ (() => { -/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); -/******/ var leafPrototypes; -/******/ // create a fake namespace object -/******/ // mode & 1: value is a module id, require it -/******/ // mode & 2: merge all properties of value into the ns -/******/ // mode & 4: return value when already ns object -/******/ // mode & 16: return value when it's Promise-like -/******/ // mode & 8|1: behave like require -/******/ __webpack_require__.t = function(value, mode) { -/******/ if(mode & 1) value = this(value); -/******/ if(mode & 8) return value; -/******/ if(typeof value === 'object' && value) { -/******/ if((mode & 4) && value.__esModule) return value; -/******/ if((mode & 16) && typeof value.then === 'function') return value; -/******/ } -/******/ var ns = Object.create(null); -/******/ __webpack_require__.r(ns); -/******/ var def = {}; -/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; -/******/ for(var current = mode & 2 && value; typeof current == 'object' && !~leafPrototypes.indexOf(current); current = getProto(current)) { -/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); -/******/ } -/******/ def['default'] = () => (value); -/******/ __webpack_require__.d(ns, def); -/******/ return ns; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/ensure chunk */ -/******/ (() => { -/******/ __webpack_require__.f = {}; -/******/ // This file contains only the entry chunk. -/******/ // The chunk loading function for additional chunks -/******/ __webpack_require__.e = (chunkId) => { -/******/ return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => { -/******/ __webpack_require__.f[key](chunkId, promises); -/******/ return promises; -/******/ }, [])); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/get javascript chunk filename */ -/******/ (() => { -/******/ // This function allow to reference async chunks -/******/ __webpack_require__.u = (chunkId) => { -/******/ // return url for filenames based on template -/******/ return "" + {"52":"locales/dayjs/de-js","102":"locales/dayjs/ca-js","145":"locales/dayjs/me-js","265":"locales/dayjs/pt-js","280":"locales/dayjs/bn-js","321":"locales/fa-LC_MESSAGES-converse-po","457":"locales/th-LC_MESSAGES-converse-po","473":"locales/nb-LC_MESSAGES-converse-po","485":"locales/dayjs/ar-sa-js","513":"locales/lt-LC_MESSAGES-converse-po","535":"locales/dayjs/en-js","555":"locales/dayjs/tet-js","559":"locales/dayjs/ru-js","578":"locales/dayjs/bg-js","631":"locales/dayjs/lv-js","646":"locales/dayjs/nb-js","688":"locales/dayjs/ga-js","760":"locales/dayjs/br-js","777":"locales/gl-LC_MESSAGES-converse-po","825":"locales/dayjs/gom-latn-js","882":"locales/dayjs/ms-my-js","911":"locales/dayjs/es-pr-js","1107":"locales/dayjs/ja-js","1110":"locales/dayjs/uz-latn-js","1163":"locales/da-LC_MESSAGES-converse-po","1194":"locales/dayjs/is-js","1204":"locales/dayjs/lo-js","1211":"locales/dayjs/pl-js","1298":"locales/dayjs/sd-js","1396":"locales/dayjs/eu-js","1455":"locales/pt_BR-LC_MESSAGES-converse-po","1458":"locales/zh_TW-LC_MESSAGES-converse-po","1520":"locales/dayjs/nl-js","1553":"locales/ca-LC_MESSAGES-converse-po","1606":"locales/dayjs/el-js","1619":"locales/dayjs/uk-js","1679":"locales/dayjs/de-ch-js","1729":"locales/dayjs/th-js","1735":"locales/dayjs/en-sg-js","1784":"locales/mr-LC_MESSAGES-converse-po","1880":"locales/dayjs/it-js","1910":"locales/dayjs/fr-js","1942":"locales/dayjs/se-js","2110":"locales/vi-LC_MESSAGES-converse-po","2130":"locales/dayjs/fi-js","2263":"locales/dayjs/bm-js","2433":"locales/eo-LC_MESSAGES-converse-po","2446":"locales/dayjs/tg-js","2473":"locales/nl-LC_MESSAGES-converse-po","2475":"locales/dayjs/km-js","2548":"locales/dayjs/da-js","2551":"locales/bg-LC_MESSAGES-converse-po","2814":"locales/dayjs/tlh-js","2843":"locales/dayjs/tzl-js","2895":"locales/de-LC_MESSAGES-converse-po","2984":"locales/dayjs/bi-js","3103":"locales/eu-LC_MESSAGES-converse-po","3153":"locales/dayjs/uz-js","3155":"locales/dayjs/nl-be-js","3208":"locales/dayjs/es-us-js","3221":"locales/dayjs/rw-js","3325":"locales/zh_CN-LC_MESSAGES-converse-po","3411":"locales/dayjs/es-js","3435":"locales/dayjs/sr-cyrl-js","3446":"locales/dayjs/ko-js","3463":"locales/dayjs/en-il-js","3521":"locales/dayjs/ar-ly-js","3539":"locales/ro-LC_MESSAGES-converse-po","3606":"locales/pl-LC_MESSAGES-converse-po","3623":"locales/dayjs/gu-js","3719":"locales/it-LC_MESSAGES-converse-po","3755":"locales/dayjs/zh-hk-js","3933":"locales/dayjs/tzm-latn-js","4035":"locales/dayjs/en-ca-js","4153":"locales/dayjs/et-js","4195":"locales/tr-LC_MESSAGES-converse-po","4305":"locales/dayjs/jv-js","4342":"locales/dayjs/tzm-js","4363":"locales/he-LC_MESSAGES-converse-po","4423":"locales/dayjs/x-pseudo-js","4468":"locales/hi-LC_MESSAGES-converse-po","4469":"locales/ar-LC_MESSAGES-converse-po","4481":"locales/dayjs/cv-js","4610":"emojis","4951":"locales/dayjs/mk-js","4963":"locales/dayjs/az-js","5050":"locales/dayjs/gd-js","5055":"locales/dayjs/ky-js","5073":"locales/dayjs/am-js","5121":"locales/dayjs/eo-js","5129":"locales/fr-LC_MESSAGES-converse-po","5166":"locales/dayjs/my-js","5186":"locales/dayjs/ka-js","5206":"locales/dayjs/kk-js","5215":"locales/dayjs/lb-js","5256":"locales/dayjs/tk-js","5274":"locales/dayjs/pt-br-js","5301":"locales/cs-LC_MESSAGES-converse-po","5313":"locales/dayjs/ar-ma-js","5407":"locales/dayjs/hy-am-js","5485":"locales/dayjs/en-au-js","5500":"locales/oc-LC_MESSAGES-converse-po","5524":"locales/el-LC_MESSAGES-converse-po","5544":"locales/dayjs/fa-js","5569":"locales/dayjs/dv-js","5600":"locales/dayjs/mr-js","5818":"locales/dayjs/gl-js","5822":"locales/dayjs/ht-js","5830":"locales/af-LC_MESSAGES-converse-po","5850":"locales/dayjs/pa-in-js","6010":"locales/dayjs/it-ch-js","6031":"locales/dayjs/en-gb-js","6105":"locales/dayjs/en-tt-js","6227":"locales/pt-LC_MESSAGES-converse-po","6239":"locales/hu-LC_MESSAGES-converse-po","6249":"locales/ja-LC_MESSAGES-converse-po","6376":"locales/dayjs/fy-js","6678":"locales/id-LC_MESSAGES-converse-po","6740":"locales/dayjs/cy-js","6755":"locales/dayjs/ar-js","6776":"locales/dayjs/zh-tw-js","6783":"locales/dayjs/sk-js","6890":"locales/dayjs/ug-cn-js","6898":"locales/dayjs/en-in-js","7024":"locales/dayjs/ku-js","7050":"locales/dayjs/nn-js","7175":"locales/dayjs/de-at-js","7203":"locales/dayjs/oc-lnc-js","7363":"locales/dayjs/fr-ca-js","7390":"locales/dayjs/sr-js","7400":"locales/dayjs/cs-js","7419":"locales/dayjs/hr-js","7454":"locales/dayjs/mi-js","7523":"locales/dayjs/kn-js","7618":"locales/fi-LC_MESSAGES-converse-po","7645":"locales/dayjs/ta-js","7679":"locales/dayjs/ml-js","7714":"locales/dayjs/te-js","7899":"locales/dayjs/lt-js","7917":"locales/ru-LC_MESSAGES-converse-po","7952":"locales/dayjs/fr-ch-js","7979":"locales/uk-LC_MESSAGES-converse-po","8010":"locales/dayjs/hi-js","8022":"locales/dayjs/ro-js","8040":"locales/dayjs/ar-tn-js","8073":"locales/dayjs/vi-js","8129":"locales/dayjs/en-ie-js","8131":"locales/nl_BE-LC_MESSAGES-converse-po","8214":"locales/dayjs/hu-js","8269":"locales/es-LC_MESSAGES-converse-po","8458":"locales/dayjs/zh-js","8547":"locales/dayjs/en-nz-js","8603":"locales/dayjs/sq-js","8618":"locales/dayjs/mn-js","8665":"locales/dayjs/tr-js","8692":"locales/dayjs/yo-js","8745":"locales/dayjs/fo-js","8758":"locales/dayjs/es-do-js","8859":"locales/sv-LC_MESSAGES-converse-po","9030":"locales/dayjs/ne-js","9095":"locales/dayjs/ms-js","9210":"locales/dayjs/af-js","9238":"locales/dayjs/ss-js","9333":"locales/dayjs/si-js","9372":"locales/dayjs/he-js","9406":"locales/dayjs/ar-dz-js","9443":"locales/dayjs/tl-ph-js","9478":"locales/dayjs/be-js","9513":"locales/dayjs/id-js","9568":"locales/dayjs/ur-js","9625":"locales/dayjs/sl-js","9630":"locales/dayjs/zh-cn-js","9652":"locales/dayjs/sv-js","9665":"locales/dayjs/mt-js","9733":"locales/dayjs/sw-js","9833":"locales/dayjs/bs-js","9897":"locales/dayjs/ar-kw-js","9950":"locales/dayjs/bo-js","9997":"locales/dayjs/sv-fi-js"}[chunkId] + ".js"; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/get mini-css chunk filename */ -/******/ (() => { -/******/ // This function allow to reference all chunks -/******/ __webpack_require__.miniCssF = (chunkId) => { -/******/ // return url for filenames based on template -/******/ return undefined; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/get mini-css chunk filename */ -/******/ (() => { -/******/ // This function allow to reference all chunks -/******/ __webpack_require__.miniCssF = (chunkId) => { -/******/ // return url for filenames based on template -/******/ return undefined; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/global */ -/******/ (() => { -/******/ __webpack_require__.g = (function() { -/******/ if (typeof globalThis === 'object') return globalThis; -/******/ try { -/******/ return this || new Function('return this')(); -/******/ } catch (e) { -/******/ if (typeof window === 'object') return window; -/******/ } -/******/ })(); -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/load script */ -/******/ (() => { -/******/ var inProgress = {}; -/******/ var dataWebpackPrefix = "converse.js:"; -/******/ // loadScript function to load a script via script tag -/******/ __webpack_require__.l = (url, done, key, chunkId) => { -/******/ if(inProgress[url]) { inProgress[url].push(done); return; } -/******/ var script, needAttach; -/******/ if(key !== undefined) { -/******/ var scripts = document.getElementsByTagName("script"); -/******/ for(var i = 0; i < scripts.length; i++) { -/******/ var s = scripts[i]; -/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; } -/******/ } -/******/ } -/******/ if(!script) { -/******/ needAttach = true; -/******/ script = document.createElement('script'); -/******/ -/******/ script.charset = 'utf-8'; -/******/ script.timeout = 120; -/******/ if (__webpack_require__.nc) { -/******/ script.setAttribute("nonce", __webpack_require__.nc); -/******/ } -/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key); -/******/ script.src = url; -/******/ } -/******/ inProgress[url] = [done]; -/******/ var onScriptComplete = (prev, event) => { -/******/ // avoid mem leaks in IE. -/******/ script.onerror = script.onload = null; -/******/ clearTimeout(timeout); -/******/ var doneFns = inProgress[url]; -/******/ delete inProgress[url]; -/******/ script.parentNode && script.parentNode.removeChild(script); -/******/ doneFns && doneFns.forEach((fn) => (fn(event))); -/******/ if(prev) return prev(event); -/******/ } -/******/ ; -/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); -/******/ script.onerror = onScriptComplete.bind(null, script.onerror); -/******/ script.onload = onScriptComplete.bind(null, script.onload); -/******/ needAttach && document.head.appendChild(script); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/node module decorator */ -/******/ (() => { -/******/ __webpack_require__.nmd = (module) => { -/******/ module.paths = []; -/******/ if (!module.children) module.children = []; -/******/ return module; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/publicPath */ -/******/ (() => { -/******/ __webpack_require__.p = "/dist/"; -/******/ })(); -/******/ -/******/ /* webpack/runtime/jsonp chunk loading */ -/******/ (() => { -/******/ // no baseURI -/******/ -/******/ // object to store loaded and loading chunks -/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched -/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded -/******/ var installedChunks = { -/******/ 179: 0 -/******/ }; -/******/ -/******/ __webpack_require__.f.j = (chunkId, promises) => { -/******/ // JSONP chunk loading for javascript -/******/ var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; -/******/ if(installedChunkData !== 0) { // 0 means "already installed". -/******/ -/******/ // a Promise means "currently loading". -/******/ if(installedChunkData) { -/******/ promises.push(installedChunkData[2]); -/******/ } else { -/******/ if(true) { // all chunks have JS -/******/ // setup Promise in chunk cache -/******/ var promise = new Promise((resolve, reject) => (installedChunkData = installedChunks[chunkId] = [resolve, reject])); -/******/ promises.push(installedChunkData[2] = promise); -/******/ -/******/ // start chunk loading -/******/ var url = __webpack_require__.p + __webpack_require__.u(chunkId); -/******/ // create error before stack unwound to get useful stacktrace later -/******/ var error = new Error(); -/******/ var loadingEnded = (event) => { -/******/ if(__webpack_require__.o(installedChunks, chunkId)) { -/******/ installedChunkData = installedChunks[chunkId]; -/******/ if(installedChunkData !== 0) installedChunks[chunkId] = undefined; -/******/ if(installedChunkData) { -/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); -/******/ var realSrc = event && event.target && event.target.src; -/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; -/******/ error.name = 'ChunkLoadError'; -/******/ error.type = errorType; -/******/ error.request = realSrc; -/******/ installedChunkData[1](error); -/******/ } -/******/ } -/******/ }; -/******/ __webpack_require__.l(url, loadingEnded, "chunk-" + chunkId, chunkId); -/******/ } else installedChunks[chunkId] = 0; -/******/ } -/******/ } -/******/ }; -/******/ -/******/ // no prefetching -/******/ -/******/ // no preloaded -/******/ -/******/ // no HMR -/******/ -/******/ // no HMR manifest -/******/ -/******/ // no on chunks loaded -/******/ -/******/ // install a JSONP callback for chunk loading -/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { -/******/ var [chunkIds, moreModules, runtime] = data; -/******/ // add "moreModules" to the modules object, -/******/ // then flag all "chunkIds" as loaded and fire callback -/******/ var moduleId, chunkId, i = 0; -/******/ for(moduleId in moreModules) { -/******/ if(__webpack_require__.o(moreModules, moduleId)) { -/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; -/******/ } -/******/ } -/******/ if(runtime) var result = runtime(__webpack_require__); -/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); -/******/ for(;i < chunkIds.length; i++) { -/******/ chunkId = chunkIds[i]; -/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { -/******/ installedChunks[chunkId][0](); -/******/ } -/******/ installedChunks[chunkIds[i]] = 0; -/******/ } -/******/ -/******/ } -/******/ -/******/ var chunkLoadingGlobal = self["webpackChunkconverse_js"] = self["webpackChunkconverse_js"] || []; -/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); -/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be in strict mode. -(() => { -"use strict"; -// Converse.js -// https://conversejs.org -// -// Copyright (c) 2020, the Converse.js contributors -// Licensed under the Mozilla Public License (MPLv2) -// -// Webpack entry file -// -// The purpose of this file is to provide an initial temporary public API -// (window.converse) for **before** the rest of converse.js is loaded so -// that we can set the __webpack_public_path__ global variable. -// -// Once the rest converse.js has been loaded, window.converse will be replaced -// with the full-fledged public API. -const plugins = {}; -const converse = { - plugins: { - add(name, plugin) { - if (plugins[name] !== undefined) { - throw new TypeError(`Error: plugin with name "${name}" has already been ` + 'registered!'); - } - - plugins[name] = plugin; - } - - }, - - initialize(settings = {}) { - converse.load(settings).initialize(settings); - }, - - /** - * Public API method which explicitly loads Converse and allows you the - * possibility to pass in configuration settings which need to be defined - * before loading. Currently this is only the [assets_path](https://conversejs.org/docs/html/configuration.html#assets_path) - * setting. - * - * If not called explicitly, this method will be called implicitly once - * {@link converse.initialize} is called. - * - * In most cases, you probably don't need to explicitly call this method, - * however, until converse.js has been loaded you won't have access to the - * utility methods and globals exposed via {@link converse.env}. So if you - * need to access `converse.env` outside of any plugins and before - * `converse.initialize` has been called, then you need to call - * `converse.load` first. - * - * @memberOf converse - * @method load - * @param {object} settings A map of configuration-settings that are needed at load time. - * @example - * converse.load({assets_path: '/path/to/assets/'}); - */ - load(settings = {}) { - if (settings.assets_path) { - __webpack_require__.p = settings.assets_path; // eslint-disable-line no-undef - } - - __webpack_require__(6387); - - Object.keys(plugins).forEach(name => converse.plugins.add(name, plugins[name])); - return converse; - } - -}; -window.converse = converse; -/** - * Once Converse.js has loaded, it'll dispatch a custom event with the name `converse-loaded`. - * You can listen for this event in order to be informed as soon as converse.js has been - * loaded and parsed, which would mean it's safe to call `converse.initialize`. - * @event converse-loaded - * @example window.addEventListener('converse-loaded', () => converse.initialize()); - */ - -const ev = new CustomEvent('converse-loaded', { - 'detail': { - converse - } -}); -window.dispatchEvent(ev); -/* unused harmony default export */ var __WEBPACK_DEFAULT_EXPORT__ = ((/* unused pure expression or super */ null && (converse))); -})(); - -/******/ })() -; -//# sourceMappingURL=converse.js.map \ No newline at end of file