{"version":3,"file":"lib-jitsi-meet.min.js","mappings":"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","sources":["webpack://JitsiMeetJS/webpack/universalModuleDefinition","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/LogCollector.js","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/Logger.js","webpack://JitsiMeetJS/./node_modules/@jitsi/logger/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/grammar.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/parser.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/node_modules/sdp-transform/lib/writer.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-simulcast/lib/index.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-simulcast/lib/transform-utils.js","webpack://JitsiMeetJS/./node_modules/async/lib/async.js","webpack://JitsiMeetJS/./index.js","webpack://JitsiMeetJS/./modules/statistics/SpeakerStats.js","webpack://JitsiMeetJS/./modules/util/AuthUtil.js","webpack://JitsiMeetJS/./modules/util/EventEmitterForwarder.js","webpack://JitsiMeetJS/./modules/util/GlobalOnErrorHandler.js","webpack://JitsiMeetJS/./modules/util/RandomUtil.js","webpack://JitsiMeetJS/./modules/util/ScriptUtil.js","webpack://JitsiMeetJS/./modules/util/StringUtils.js","webpack://JitsiMeetJS/./modules/util/UsernameGenerator.js","webpack://JitsiMeetJS/./service/RTC/BridgeVideoType.js","webpack://JitsiMeetJS/./service/RTC/CameraFacingMode.js","webpack://JitsiMeetJS/./service/RTC/CodecMimeType.js","webpack://JitsiMeetJS/./service/RTC/MediaDirection.js","webpack://JitsiMeetJS/./service/RTC/RTCEvents.ts","webpack://JitsiMeetJS/./service/RTC/Resolutions.js","webpack://JitsiMeetJS/./service/RTC/VideoType.js","webpack://JitsiMeetJS/./service/authentication/AuthenticationEvents.js","webpack://JitsiMeetJS/./service/statistics/constants.js","webpack://JitsiMeetJS/./service/xmpp/XMPPEvents.ts","webpack://JitsiMeetJS/./node_modules/base64-js/index.js","webpack://JitsiMeetJS/./node_modules/bowser/es5.js","webpack://JitsiMeetJS/./node_modules/current-executing-script/dist/currentExecutingScript.js","webpack://JitsiMeetJS/./node_modules/events/events.js","webpack://JitsiMeetJS/./node_modules/js-md5/src/md5.js","webpack://JitsiMeetJS/./node_modules/lodash.clonedeep/index.js","webpack://JitsiMeetJS/./node_modules/lodash.debounce/index.js","webpack://JitsiMeetJS/./node_modules/lodash.isequal/index.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/grammar.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/index.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/parser.js","webpack://JitsiMeetJS/./node_modules/sdp-transform/lib/writer.js","webpack://JitsiMeetJS/./node_modules/sdp/sdp.js","webpack://JitsiMeetJS/./JitsiConferenceErrors.ts","webpack://JitsiMeetJS/./JitsiConferenceEvents.ts","webpack://JitsiMeetJS/./modules/statistics/constants.js","webpack://JitsiMeetJS/./JitsiTrackErrors.ts","webpack://JitsiMeetJS/./JitsiTrackError.js","webpack://JitsiMeetJS/./service/statistics/AnalyticsEvents.ts","webpack://JitsiMeetJS/./service/statistics/Events.ts","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/browser-detection/browsers.js","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/browser-detection/BrowserDetection.js","webpack://JitsiMeetJS/./node_modules/@jitsi/js-utils/jitsi-local-storage/index.js","webpack://JitsiMeetJS/./modules/browser/BrowserCapabilities.js","webpack://JitsiMeetJS/./modules/browser/index.js","webpack://JitsiMeetJS/./modules/statistics/AnalyticsAdapter.js","webpack://JitsiMeetJS/./modules/statistics/CallStats.js","webpack://JitsiMeetJS/./modules/statistics/LocalStatsCollector.js","webpack://JitsiMeetJS/./modules/util/MathUtil.js","webpack://JitsiMeetJS/./modules/statistics/PerformanceObserverStats.js","webpack://JitsiMeetJS/./service/RTC/MediaType.ts","webpack://JitsiMeetJS/./modules/statistics/RTPStatsCollector.js","webpack://JitsiMeetJS/./modules/statistics/statistics.js","webpack://JitsiMeetJS/./JitsiConferenceEventManager.js","webpack://JitsiMeetJS/./JitsiTrackEvents.js","webpack://JitsiMeetJS/./modules/connectivity/ParticipantConnectionStatus.js","webpack://JitsiMeetJS/./JitsiParticipant.js","webpack://JitsiMeetJS/./JitsiConnectionEvents.ts","webpack://JitsiMeetJS/./JitsiConnectionErrors.ts","webpack://JitsiMeetJS/./modules/util/Deferred.js","webpack://JitsiMeetJS/./modules/util/Listenable.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEContext.js","webpack://JitsiMeetJS/./modules/e2ee/KeyHandler.js","webpack://JitsiMeetJS/./modules/e2ee/ExternallyManagedKeyHandler.js","webpack://JitsiMeetJS/./node_modules/uuid/wrapper.mjs","webpack://JitsiMeetJS/./modules/e2ee/OlmAdapter.js","webpack://JitsiMeetJS/./modules/e2ee/ManagedKeyHandler.js","webpack://JitsiMeetJS/./modules/e2ee/crypto-utils.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEncryption.js","webpack://JitsiMeetJS/./modules/xmpp/Caps.js","webpack://JitsiMeetJS/./modules/connectivity/NetworkInfo.js","webpack://JitsiMeetJS/./modules/xmpp/ResumeTask.js","webpack://JitsiMeetJS/./modules/util/Retry.js","webpack://JitsiMeetJS/./modules/xmpp/StropheLastSuccess.js","webpack://JitsiMeetJS/./modules/xmpp/ConnectionPlugin.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.ping.js","webpack://JitsiMeetJS/./modules/xmpp/XmppConnection.js","webpack://JitsiMeetJS/./JitsiTranscriptionStatus.ts","webpack://JitsiMeetJS/./modules/xmpp/AVModeration.js","webpack://JitsiMeetJS/./modules/xmpp/BreakoutRooms.js","webpack://JitsiMeetJS/./modules/xmpp/Lobby.js","webpack://JitsiMeetJS/./modules/settings/Settings.js","webpack://JitsiMeetJS/./modules/xmpp/moderator.js","webpack://JitsiMeetJS/./modules/xmpp/ChatRoom.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.emuc.js","webpack://JitsiMeetJS/./modules/flags/FeatureFlags.js","webpack://JitsiMeetJS/./modules/xmpp/JingleHelperFunctions.js","webpack://JitsiMeetJS/./modules/RTC/ScreenObtainer.js","webpack://JitsiMeetJS/./modules/sdp/SDPUtil.js","webpack://JitsiMeetJS/./modules/sdp/SDP.js","webpack://JitsiMeetJS/./modules/sdp/SDPDiffer.js","webpack://JitsiMeetJS/./modules/util/AsyncQueue.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionState.ts","webpack://JitsiMeetJS/./modules/xmpp/JingleSession.js","webpack://JitsiMeetJS/./modules/xmpp/MediaSessionEvents.ts","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionPC.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.jingle.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.logger.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.rayo.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.util.js","webpack://JitsiMeetJS/./modules/xmpp/xmpp.js","webpack://JitsiMeetJS/./authenticateAndUpgradeRole.js","webpack://JitsiMeetJS/./modules/RTC/CodecSelection.js","webpack://JitsiMeetJS/./modules/RTC/BridgeChannel.js","webpack://JitsiMeetJS/./modules/RTC/RTCUtils.js","webpack://JitsiMeetJS/./modules/RTC/JitsiTrack.js","webpack://JitsiMeetJS/./modules/RTC/JitsiLocalTrack.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/lib/transform.js","webpack://JitsiMeetJS/./node_modules/@jitsi/sdp-interop/lib/interop.js","webpack://JitsiMeetJS/./service/RTC/SignalingEvents.ts","webpack://JitsiMeetJS/./service/RTC/SignalingLayer.js","webpack://JitsiMeetJS/./modules/sdp/SdpTransformUtil.js","webpack://JitsiMeetJS/./modules/sdp/LocalSdpMunger.js","webpack://JitsiMeetJS/./modules/sdp/RtxModifier.js","webpack://JitsiMeetJS/./modules/sdp/SdpConsistency.js","webpack://JitsiMeetJS/./modules/sdp/SdpSimulcast.ts","webpack://JitsiMeetJS/./modules/connectivity/TrackStreamingStatus.ts","webpack://JitsiMeetJS/./modules/RTC/JitsiRemoteTrack.js","webpack://JitsiMeetJS/./modules/RTC/TPCUtils.js","webpack://JitsiMeetJS/./modules/RTC/TraceablePeerConnection.js","webpack://JitsiMeetJS/./modules/RTC/RTC.js","webpack://JitsiMeetJS/./service/connectivity/ConnectionQualityEvents.ts","webpack://JitsiMeetJS/./modules/connectivity/ConnectionQuality.js","webpack://JitsiMeetJS/./modules/connectivity/IceFailedHandling.js","webpack://JitsiMeetJS/./modules/detection/DetectionEvents.ts","webpack://JitsiMeetJS/./modules/detection/NoAudioSignalDetection.js","webpack://JitsiMeetJS/./modules/detection/P2PDominantSpeakerDetection.js","webpack://JitsiMeetJS/./modules/webaudio/WebAudioUtils.js","webpack://JitsiMeetJS/./modules/detection/TrackVADEmitter.js","webpack://JitsiMeetJS/./modules/detection/VADAudioAnalyser.js","webpack://JitsiMeetJS/./modules/detection/VADNoiseDetection.js","webpack://JitsiMeetJS/./modules/detection/VADTalkMutedDetection.js","webpack://JitsiMeetJS/./service/e2eping/E2ePingEvents.ts","webpack://JitsiMeetJS/./modules/e2eping/e2eping.js","webpack://JitsiMeetJS/./modules/event/Jvb121EventGenerator.js","webpack://JitsiMeetJS/./modules/qualitycontrol/ReceiveVideoController.js","webpack://JitsiMeetJS/./modules/qualitycontrol/SendVideoController.js","webpack://JitsiMeetJS/./modules/recording/recordingXMLUtils.js","webpack://JitsiMeetJS/./modules/recording/JibriSession.js","webpack://JitsiMeetJS/./modules/recording/RecordingManager.js","webpack://JitsiMeetJS/./modules/statistics/AudioOutputProblemDetector.js","webpack://JitsiMeetJS/./modules/statistics/AvgRTPStatsReporter.js","webpack://JitsiMeetJS/./modules/statistics/SpeakerStatsCollector.js","webpack://JitsiMeetJS/./modules/transcription/recordingResult.js","webpack://JitsiMeetJS/./modules/transcription/trackRecorder.js","webpack://JitsiMeetJS/./modules/transcription/audioRecorder.js","webpack://JitsiMeetJS/./modules/transcription/word.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/SphinxTranscriptionService.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/AbstractTranscriptionService.js","webpack://JitsiMeetJS/./modules/transcription/transcriber.js","webpack://JitsiMeetJS/./modules/version/ComponentsVersions.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGWConstants.ts","webpack://JitsiMeetJS/./modules/videosipgw/JitsiVideoSIPGWSession.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGW.js","webpack://JitsiMeetJS/./modules/xmpp/SignalingLayerImpl.js","webpack://JitsiMeetJS/./JitsiConference.js","webpack://JitsiMeetJS/./JitsiConnection.js","webpack://JitsiMeetJS/./JitsiMediaDevicesEvents.ts","webpack://JitsiMeetJS/./JitsiMediaDevices.js","webpack://JitsiMeetJS/./modules/detection/ActiveDeviceDetector.js","webpack://JitsiMeetJS/./modules/proxyconnection/constants.ts","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionPC.js","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionService.js","webpack://JitsiMeetJS/./modules/statistics/PrecallTest.js","webpack://JitsiMeetJS/./modules/webaudio/AudioMixer.js","webpack://JitsiMeetJS/./JitsiMeetJS.js","webpack://JitsiMeetJS/./modules/recording/recordingConstants.js","webpack://JitsiMeetJS/./node_modules/strophe.js/dist/strophe.umd.js","webpack://JitsiMeetJS/./node_modules/strophejs-plugin-disco/lib/strophe.disco.js","webpack://JitsiMeetJS/./node_modules/strophejs-plugin-stream-management/lib/strophe.stream-management.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/bytesToUuid.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/index.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/md5-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/rng-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/sha1-browser.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v1.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v3.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v35.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v4.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/v5.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/utils.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/safari/safari_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/common_shim.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/adapter_core.js","webpack://JitsiMeetJS/./node_modules/webrtc-adapter/src/js/adapter_factory.js","webpack://JitsiMeetJS/webpack/bootstrap","webpack://JitsiMeetJS/webpack/runtime/amd options","webpack://JitsiMeetJS/webpack/runtime/compat get default export","webpack://JitsiMeetJS/webpack/runtime/define property getters","webpack://JitsiMeetJS/webpack/runtime/global","webpack://JitsiMeetJS/webpack/runtime/hasOwnProperty shorthand","webpack://JitsiMeetJS/webpack/runtime/make namespace object","webpack://JitsiMeetJS/webpack/runtime/node module decorator","webpack://JitsiMeetJS/webpack/startup"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"JitsiMeetJS\"] = factory();\n\telse\n\t\troot[\"JitsiMeetJS\"] = factory();\n})(self, function() {\nreturn ","/* Copyright @ 2016-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar Logger = require('./Logger.js');\n\n/**\n * Creates new <tt>LogCollector</tt>. Class implements <tt>LoggerTransport</tt>\n * and thus can be added as global transport in order to capture all the logs.\n *\n * It captures subsequent log lines created whenever <tt>Logger</tt> logs\n * a message and stores them in a queue in order to batch log entries. There are\n * time and size limit constraints which determine how often batch entries are\n * stored. Whenever one of these limits is exceeded the <tt>LogCollector</tt>\n * will use the <tt>logStorage</tt> object given as an argument to save\n * the batch log entry.\n *\n * @param {Object} logStorage an object which allows to store the logs collected\n * @param {function(string|object[])} logStorage.storeLogs a method called when\n * this <tt>LogCollector</tt> requests log entry storage. The method's argument\n * is an array which can contain <tt>string</tt>s and <tt>object</tt>s. If given\n * item is an object it means that it's an aggregated message. That is a message\n * which is the same as the previous one and it's representation has\n * the following format:\n * {\n *   {string} text: 'the text of some duplicated message'\n *   {number} count: 3 // how many times the message appeared in a row\n * }\n * If a message \"B\" after an aggregated message \"A\" is different, then it breaks\n * the sequence of \"A\". Which means that even if the next message \"C\" is\n * the same as \"A\" it will start a new aggregated message \"C\".\n * @param {function()} logStorage.isReady a method which should return\n * a <tt>boolean</tt> to tell the collector that it's ready to store. During the\n * time storage is not ready log batches will be cached and stored on the next\n * occasion (flush or interval timeout).\n *\n * @param {Object} options the <tt>LogCollector</tt> configuration options.\n * @param {number} options.maxEntryLength the size limit for a single log entry\n * to be stored. The <tt>LogCollector</tt> will push the entry as soon as it\n * reaches or exceeds this limit given that <tt>logStorage.isReady</tt>\n * returns <tt>true</tt>. Otherwise the log entry will be cached until the log\n * storage becomes ready. Note that the \"is ready\" condition is checked every\n * <tt>options.storeInterval</tt> milliseconds.\n * @param {number} options.storeInterval how often the logs should be stored in\n * case <tt>maxEntryLength</tt> was not exceeded.\n * @param {boolean} options.stringifyObjects indicates whether or not object\n * arguments should be \"stringified\" with <tt>JSON.stringify</tt> when a log\n * message is composed. Note that objects logged on the error log level are\n * always stringified.\n *\n * @constructor\n */\nfunction LogCollector(logStorage, options) {\n    this.logStorage = logStorage;\n    this.stringifyObjects = options && options.stringifyObjects ? options.stringifyObjects : false;\n    this.storeInterval = options && options.storeInterval ? options.storeInterval: 30000;\n    this.maxEntryLength = options && options.maxEntryLength ? options.maxEntryLength : 10000;\n    // Bind the log method for each level to the corresponding method name\n    // in order to implement \"global log transport\" object.\n    Object.keys(Logger.levels).forEach(\n    function (logLevel) {\n        var methodName = Logger.levels[logLevel];\n        this[methodName] = function () {\n            this._log.apply(this, arguments);\n        }.bind(this, logLevel);\n    }.bind(this));\n    /**\n     * The ID of store logs interval if one is currently scheduled or\n     * <tt>null</tt> otherwise.\n     * @type {number|null}\n     */\n    this.storeLogsIntervalID = null;\n    /**\n     * The log messages that are to be batched into log entry when\n     * {@link LogCollector._flush} method is called.\n     * @type {string[]}\n     */\n    this.queue = [];\n    /**\n     * The total length of all messages currently stored in the {@link queue}.\n     * @type {number}\n     */\n    this.totalLen = 0;\n    /**\n     * An array used to temporarily store log batches, before the storage gets\n     * ready.\n     * @type {string[]}\n     */\n    this.outputCache = [];\n}\n\n/**\n * Method called inside of {@link formatLogMessage} in order to covert an\n * <tt>Object</tt> argument to string. The conversion will happen when either\n * 'stringifyObjects' option is enabled or on the {@link Logger.levels.ERROR}\n * log level. The default implementation uses <tt>JSON.stringify</tt> and\n * returns \"[object with circular refs?]\" instead of an object if it fails.\n *\n * @param {object} someObject the <tt>object</tt> to be stringified.\n *\n * @return {string} the result of <tt>JSON.stringify</tt> or\n * \"[object with circular refs?]\" if any error occurs during \"stringification\".\n *\n * @protected\n */\nLogCollector.prototype.stringify = function (someObject) {\n    try {\n        return JSON.stringify(someObject);\n    } catch (error) {\n        return '[object with circular refs?]';\n    }\n};\n\n/**\n * Formats log entry for the given logging level and arguments passed to the\n * <tt>Logger</tt>'s log method. The first argument is log level and the next\n * arguments have to be captured using JS built-in 'arguments' variable.\n *\n * @param {Logger.levels} logLevel provides the logging level of the message to\n * be logged.\n * @param {Date} timestamp - The {@code Date} when a message has been logged.\n *\n * @return {string|null} a non-empty string representation of the log entry\n * crafted from the log arguments. If the return value is <tt>null</tt> then\n * the message wil be discarded by this <tt>LogCollector</tt>.\n *\n * @protected\n */\nLogCollector.prototype.formatLogMessage = function (\nlogLevel /* timestamp, arg2, arg3, arg4... */) {\n    var msg = '';\n    for (var i = 1, len = arguments.length; i < len; i++) {\n        var arg = arguments[i];\n        // objects logged on error level are always converted to JSON\n        if ((this.stringifyObjects || logLevel === Logger.levels.ERROR) &&\n            typeof arg === 'object') {\n            arg = this.stringify(arg);\n        }\n        msg += arg;\n        if (i !== len - 1) {\n            msg += ' ';\n        }\n    }\n    return msg.length ? msg : null;\n};\n\n/**\n * The log method bound to each of the logging levels in order to implement\n * \"global log transport\" object.\n *\n * @private\n */\nLogCollector.prototype._log = function() {\n\n    // var logLevel = arguments[0]; first argument is the log level\n    var timestamp = arguments[1];\n    var msg = this.formatLogMessage.apply(this, arguments);\n    if (msg) {\n        // The same as the previous message aggregation logic\n        var prevMessage = this.queue[this.queue.length - 1];\n        var prevMessageText = prevMessage && prevMessage.text;\n        if (prevMessageText === msg) {\n            prevMessage.count += 1;\n        } else {\n            this.queue.push({\n                text: msg,\n                timestamp: timestamp,\n                count: 1\n            });\n            this.totalLen += msg.length;\n        }\n    }\n\n    if (this.totalLen >= this.maxEntryLength) {\n        this._flush(true /* force */, true /* reschedule */);\n    }\n};\n\n/**\n * Starts periodical \"store logs\" task which will be triggered at the interval\n * specified in the constructor options.\n */\nLogCollector.prototype.start = function () {\n    this._reschedulePublishInterval();\n};\n\n/**\n * Reschedules the periodical \"store logs\" task which will store the next batch\n * log entry in the storage.\n * @private\n */\nLogCollector.prototype._reschedulePublishInterval = function () {\n    if (this.storeLogsIntervalID) {\n        window.clearTimeout(this.storeLogsIntervalID);\n        this.storeLogsIntervalID = null;\n    }\n    // It's actually a timeout, because it is rescheduled on every flush\n    this.storeLogsIntervalID = window.setTimeout(\n        this._flush.bind(\n            this, false /* do not force */, true /* reschedule */),\n        this.storeInterval);\n};\n\n/**\n * Call this method to flush the log entry buffer and store it in the log\n * storage immediately (given that the storage is ready).\n */\nLogCollector.prototype.flush = function() {\n    this._flush(\n        false /* do not force, as it will not be stored anyway */,\n        true /* reschedule next update */ );\n};\n\n/**\n * Stores the next batch log entry in the log storage.\n * @param {boolean} force enforce current logs batch to be stored or cached if\n * there is anything to be logged, but the storage is not ready yet. One of\n * legitimate reasons to force is when the logs length exceeds size limit which\n * could result in truncation.\n * @param {boolean} reschedule <tt>true</tt> if the next periodic task should be\n * scheduled after the log entry is stored. <tt>false</tt> will end the periodic\n * task cycle.\n * @private\n */\nLogCollector.prototype._flush = function(force, reschedule) {\n    // Publish only if there's anything to be logged\n    if (this.totalLen > 0 && (this.logStorage.isReady() || force)) {\n        //FIXME avoid truncating\n        // right now we don't care if the message size is \"slightly\" exceeded\n        if (this.logStorage.isReady()) {\n            // Sends all cached logs\n            if (this.outputCache.length) {\n                this.outputCache.forEach(\n                    function (cachedQueue) {\n                        this.logStorage.storeLogs(cachedQueue);\n                    }.bind(this)\n                );\n                // Clear the cache\n                this.outputCache = [];\n            }\n            // Send current batch\n            this.logStorage.storeLogs(this.queue);\n        } else {\n            this.outputCache.push(this.queue);\n        }\n\n        this.queue = [];\n        this.totalLen = 0;\n    }\n\n    if (reschedule) {\n        this._reschedulePublishInterval();\n    }\n};\n\n/**\n * Stops the periodical \"store logs\" task and immediately stores any pending\n * log entries as a batch.\n */\nLogCollector.prototype.stop = function() {\n    // Flush and stop publishing logs\n    this._flush(false /* do not force */, false /* do not reschedule */);\n};\n\nmodule.exports = LogCollector;\n","/* Copyright @ 2015-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n/*jslint latedef:false*/\n\n/**\n * Ordered log levels.\n */\nvar levels = {\n    \"trace\": 0,\n    \"debug\": 1,\n    \"info\": 2,\n    \"log\": 3,\n    \"warn\": 4,\n    \"error\": 5\n};\n\n/**\n * The default transport - console\n * @type LoggerTransport\n */\nLogger.consoleTransport = console;\n\n/**\n * The array which stores currently registered global transports.\n * @type {[LoggerTransport]}\n */\nvar globalTransports = [ Logger.consoleTransport ];\n\n/**\n * Adds given {@link LoggerTransport} instance to the list of global\n * transports which means that it'll be used by all {@link Logger}s\n * @param {LoggerTransport} transport\n */\nLogger.addGlobalTransport = function(transport) {\n    if (globalTransports.indexOf(transport) === -1) {\n        globalTransports.push(transport);\n    }\n};\n\n/**\n * Removes given {@link LoggerTransport} instance from the list of global\n * transports\n * @param {LoggerTransport} transport\n */\nLogger.removeGlobalTransport = function(transport) {\n    var transportIdx = globalTransports.indexOf(transport);\n    if (transportIdx !== -1) {\n        globalTransports.splice(transportIdx, 1);\n    }\n};\n\n/**\n * The global configuration options.\n */\nvar globalOptions = {};\n\n/**\n * Sets global options which will be used by all loggers. Changing these works\n * even after other loggers are created.\n */\nLogger.setGlobalOptions = function(options) {\n    globalOptions = options || {};\n};\n\n/**\n * Parses Error's object stack trace and extracts information about the last\n * caller before the log method was called.\n * @returns JS object with info about the caller - method name, file location,\n * line and column.\n */\nfunction getCallerInfo() {\n    var callerInfo = {\n        methodName: \"\",\n        fileLocation: \"\",\n        line: null,\n        column: null\n    };\n    //gets the part of the stack without the logger wrappers\n    var error = new Error();\n    var stack = error.stack? error.stack.split(\"\\n\") : [];\n    if(!stack || stack.length < 3) {\n        return callerInfo;\n    }\n    var m = null;\n    if(stack[3]) {\n        m = stack[3].match(/\\s*at\\s*(.+?)\\s*\\((\\S*)\\s*:(\\d*)\\s*:(\\d*)\\)/);\n    }\n    if(!m || m.length <= 4) {\n        //Firefox && Safari\n        if(stack[2].indexOf(\"log@\") === 0){\n            //Safari\n            callerInfo.methodName = stack[3].substr(0, stack[3].indexOf(\"@\"));\n        } else {\n            //Firefox\n            callerInfo.methodName = stack[2].substr(0, stack[2].indexOf(\"@\"));\n        }\n        return callerInfo;\n    }\n\n    callerInfo.methodName = m[1];\n    callerInfo.fileLocation = m[2];\n    callerInfo.line = m[3];\n    callerInfo.column = m[4];\n    return callerInfo;\n}\n\n/**\n * Logs messages using the transports and level from the logger.\n * @param logger a logger instance.\n * @param level the log level of the message. See the levels variable.\n * @param arguments array with arguments that will be logged.\n */\nfunction log() {\n    var logger = arguments[0], level = arguments[1],\n        args = Array.prototype.slice.call(arguments, 2);\n    if(levels[level] < logger.level) {\n        return;\n    }\n\n    var callerInfo\n        = !(logger.options.disableCallerInfo || globalOptions.disableCallerInfo) &&\n            getCallerInfo();\n    var transports = globalTransports.concat(logger.transports);\n    for(var i = 0; i < transports.length; i++) {\n        var t = transports[i];\n        var l = t[level];\n        if(l && typeof(l) === \"function\") {\n            var logPrefixes = [];\n\n            logPrefixes.push(new Date().toISOString());\n\n            if (logger.id) {\n                logPrefixes.push(\"[\" + logger.id + \"]\");\n            }\n\n            if (callerInfo && callerInfo.methodName.length > 1) {\n                logPrefixes.push(\"<\" + callerInfo.methodName + \">: \");\n            }\n\n            var fullLogParts = logPrefixes.concat(args);\n\n            l.bind(t).apply(t, fullLogParts);\n        }\n    }\n}\n\n/**\n *\n * Constructs new logger object.\n * @param level the logging level for the new logger\n * @param id optional identifier for the logger instance.\n * @param {LoggerTransport} transports optional list of handlers(objects) for\n * the logs. The handlers must support - log, warn, error, debug, info, trace.\n * @param options optional configuration file for how the logger should behave.\n * @param {boolean} options.disableCallerInfo Whether the call site of a logger\n * method invocation should be included in the log. Defaults to false, so the\n * call site will be included.\n */\nfunction Logger(level, id, transports, options) {\n    this.id = id;\n    this.options = options || {};\n    this.transports = transports;\n    if(!this.transports) {\n        this.transports = [];\n    }\n    this.level = levels[level];\n    var methods = Object.keys(levels);\n    for(var i = 0; i < methods.length; i++){\n        this[methods[i]] =\n            log.bind(null, this, methods[i]);\n    }\n}\n\n/**\n * Sets the log level for the logger.\n * @param level the new log level.\n */\nLogger.prototype.setLevel = function (level) {\n    this.level = levels[level];\n};\nmodule.exports = Logger;\n\n/**\n * Enum for the supported log levels.\n */\nLogger.levels = {\n    TRACE: \"trace\",\n    DEBUG: \"debug\",\n    INFO: \"info\",\n    LOG: \"log\",\n    WARN: \"warn\",\n    ERROR: \"error\"\n};\n","/* Copyright @ 2015-present 8x8, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nvar Logger = require(\"./Logger\");\nvar LogCollector = require(\"./LogCollector\");\n\n/**\n * Definition of the log method\n * @name log_method\n * @function\n * @param {...*} log_args the arguments to be logged\n */\n/**\n * The logger's transport type definition.\n *\n * @typedef {object} LoggerTransport\n *\n * @property {log_method} trace method called to log on {@link Logger.levels.TRACE} logging level\n * @property {log_method} debug method called to log on {@link Logger.levels.DEBUG} logging level\n * @property {log_method} info method called to log on {@link Logger.levels.INFO} logging level\n * @property {log_method} log method called to log on {@link Logger.levels.LOG} logging level\n * @property {log_method} warn method called to log on {@link Logger.levels.WARN} logging level\n * @property {log_method} error method called to log on {@link Logger.levels.ERROR} logging level\n */\n\n/**\n * Map with the created loggers with ID.\n */\nvar idLoggers = {};\n\n/**\n * Array with the loggers without id.\n */\nvar loggers = [];\n\n/**\n * Log level for the lbrary.\n */\nvar curLevel = Logger.levels.TRACE;\n\n\nmodule.exports = {\n    /**\n     * Adds given {@link LoggerTransport} instance to the list of global\n     * transports which means that it'll be used by all {@link Logger}s\n     * @param {LoggerTransport} transport\n     */\n    addGlobalTransport: function(transport) {\n        Logger.addGlobalTransport(transport);\n    },\n    /**\n     * Removes given {@link LoggerTransport} instance from the list of global\n     * transports\n     * @param {LoggerTransport} transport\n     */\n    removeGlobalTransport: function(transport) {\n        Logger.removeGlobalTransport(transport);\n    },\n    /**\n    * Sets global options which will be used by all loggers. Changing these\n    * works even after other loggers are created.\n    */\n    setGlobalOptions: function(options) {\n        Logger.setGlobalOptions(options);\n    },\n    /**\n     * Creates new logger.\n     * @arguments the same as Logger constructor\n     */\n    getLogger: function(id, transports, options) {\n        var logger = new Logger(curLevel, id, transports, options);\n        if(id) {\n            idLoggers[id] = idLoggers[id] || [];\n            idLoggers[id].push(logger);\n        } else {\n            loggers.push(logger);\n        }\n        return logger;\n    },\n    /**\n     * Changes the log level for the existing loggers by id.\n     * @param level the new log level.\n     * @param id if specified the level will be changed only for loggers with the\n     * same id. Otherwise the operation will affect all loggers that don't\n     * have id.\n     */\n    setLogLevelById: function(level, id) {\n        var l = id? (idLoggers[id] || []) : loggers;\n        for(var i = 0; i < l.length; i++) {\n            l[i].setLevel(level);\n        }\n    },\n    /**\n     * Changes the log level for all existing loggers.\n     * @param level the new log level.\n     */\n    setLogLevel: function (level) {\n        curLevel = level;\n        var i = 0;\n        for(; i < loggers.length; i++) {\n            loggers[i].setLevel(level);\n        }\n\n        for(var id in idLoggers) {\n            var l = idLoggers[id] || [];\n            for(i = 0; i < l.length; i++) {\n                l[i].setLevel(level);\n            }\n        }\n    },\n    /**\n     * The supported log levels.\n     */\n    levels: Logger.levels,\n    /**\n     * Exports the <tt>LogCollector</tt>.\n     */\n    LogCollector: LogCollector\n};\n","var grammar = module.exports = {\n  v: [{\n    name: 'version',\n    reg: /^(\\d*)$/\n  }],\n  o: [{\n    // o=- 20518 0 IN IP4 203.0.113.1\n    // NB: sessionId will be a String in most cases because it is huge\n    name: 'origin',\n    reg: /^(\\S*) (\\d*) (\\d*) (\\S*) IP(\\d) (\\S*)/,\n    names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],\n    format: '%s %s %d %s IP%d %s'\n  }],\n  // default parsing of these only (though some of these feel outdated)\n  s: [{ name: 'name' }],\n  i: [{ name: 'description' }],\n  u: [{ name: 'uri' }],\n  e: [{ name: 'email' }],\n  p: [{ name: 'phone' }],\n  z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly...\n  r: [{ name: 'repeats' }],   // TODO: this one can also be parsed properly\n  // k: [{}], // outdated thing ignored\n  t: [{\n    // t=0 0\n    name: 'timing',\n    reg: /^(\\d*) (\\d*)/,\n    names: ['start', 'stop'],\n    format: '%d %d'\n  }],\n  c: [{\n    // c=IN IP4 10.47.197.26\n    name: 'connection',\n    reg: /^IN IP(\\d) (\\S*)/,\n    names: ['version', 'ip'],\n    format: 'IN IP%d %s'\n  }],\n  b: [{\n    // b=AS:4000\n    push: 'bandwidth',\n    reg: /^(TIAS|AS|CT|RR|RS):(\\d*)/,\n    names: ['type', 'limit'],\n    format: '%s:%s'\n  }],\n  m: [{\n    // m=video 51744 RTP/AVP 126 97 98 34 31\n    // NB: special - pushes to session\n    // TODO: rtp/fmtp should be filtered by the payloads found here?\n    reg: /^(\\w*) (\\d*) ([\\w/]*)(?: (.*))?/,\n    names: ['type', 'port', 'protocol', 'payloads'],\n    format: '%s %d %s %s'\n  }],\n  a: [\n    {\n      // a=rtpmap:110 opus/48000/2\n      push: 'rtp',\n      reg: /^rtpmap:(\\d*) ([\\w\\-.]*)(?:\\s*\\/(\\d*)(?:\\s*\\/(\\S*))?)?/,\n      names: ['payload', 'codec', 'rate', 'encoding'],\n      format: function (o) {\n        return (o.encoding)\n          ? 'rtpmap:%d %s/%s/%s'\n          : o.rate\n            ? 'rtpmap:%d %s/%s'\n            : 'rtpmap:%d %s';\n      }\n    },\n    {\n      // a=fmtp:108 profile-level-id=24;object=23;bitrate=64000\n      // a=fmtp:111 minptime=10; useinbandfec=1\n      push: 'fmtp',\n      reg: /^fmtp:(\\d*) ([\\S| ]*)/,\n      names: ['payload', 'config'],\n      format: 'fmtp:%d %s'\n    },\n    {\n      // a=control:streamid=0\n      name: 'control',\n      reg: /^control:(.*)/,\n      format: 'control:%s'\n    },\n    {\n      // a=rtcp:65179 IN IP4 193.84.77.194\n      name: 'rtcp',\n      reg: /^rtcp:(\\d*)(?: (\\S*) IP(\\d) (\\S*))?/,\n      names: ['port', 'netType', 'ipVer', 'address'],\n      format: function (o) {\n        return (o.address != null)\n          ? 'rtcp:%d %s IP%d %s'\n          : 'rtcp:%d';\n      }\n    },\n    {\n      // a=rtcp-fb:98 trr-int 100\n      push: 'rtcpFbTrrInt',\n      reg: /^rtcp-fb:(\\*|\\d*) trr-int (\\d*)/,\n      names: ['payload', 'value'],\n      format: 'rtcp-fb:%s trr-int %d'\n    },\n    {\n      // a=rtcp-fb:98 nack rpsi\n      push: 'rtcpFb',\n      reg: /^rtcp-fb:(\\*|\\d*) ([\\w-_]*)(?: ([\\w-_]*))?/,\n      names: ['payload', 'type', 'subtype'],\n      format: function (o) {\n        return (o.subtype != null)\n          ? 'rtcp-fb:%s %s %s'\n          : 'rtcp-fb:%s %s';\n      }\n    },\n    {\n      // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n      // a=extmap:1/recvonly URI-gps-string\n      // a=extmap:3 urn:ietf:params:rtp-hdrext:encrypt urn:ietf:params:rtp-hdrext:smpte-tc 25@600/24\n      push: 'ext',\n      reg: /^extmap:(\\d+)(?:\\/(\\w+))?(?: (urn:ietf:params:rtp-hdrext:encrypt))? (\\S*)(?: (\\S*))?/,\n      names: ['value', 'direction', 'encrypt-uri', 'uri', 'config'],\n      format: function (o) {\n        return (\n          'extmap:%d' +\n          (o.direction ? '/%s' : '%v') +\n          (o['encrypt-uri'] ? ' %s' : '%v') +\n          ' %s' +\n          (o.config ? ' %s' : '')\n        );\n      }\n    },\n    {\n      // a=extmap-allow-mixed\n      name: 'extmapAllowMixed',\n      reg: /^(extmap-allow-mixed)/\n    },\n    {\n      // a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32\n      push: 'crypto',\n      reg: /^crypto:(\\d*) ([\\w_]*) (\\S*)(?: (\\S*))?/,\n      names: ['id', 'suite', 'config', 'sessionConfig'],\n      format: function (o) {\n        return (o.sessionConfig != null)\n          ? 'crypto:%d %s %s %s'\n          : 'crypto:%d %s %s';\n      }\n    },\n    {\n      // a=setup:actpass\n      name: 'setup',\n      reg: /^setup:(\\w*)/,\n      format: 'setup:%s'\n    },\n    {\n      // a=connection:new\n      name: 'connectionType',\n      reg: /^connection:(new|existing)/,\n      format: 'connection:%s'\n    },\n    {\n      // a=mid:1\n      name: 'mid',\n      reg: /^mid:([^\\s]*)/,\n      format: 'mid:%s'\n    },\n    {\n      // a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a\n      name: 'msid',\n      reg: /^msid:(.*)/,\n      format: 'msid:%s'\n    },\n    {\n      // a=ptime:20\n      name: 'ptime',\n      reg: /^ptime:(\\d*(?:\\.\\d*)*)/,\n      format: 'ptime:%d'\n    },\n    {\n      // a=maxptime:60\n      name: 'maxptime',\n      reg: /^maxptime:(\\d*(?:\\.\\d*)*)/,\n      format: 'maxptime:%d'\n    },\n    {\n      // a=sendrecv\n      name: 'direction',\n      reg: /^(sendrecv|recvonly|sendonly|inactive)/\n    },\n    {\n      // a=ice-lite\n      name: 'icelite',\n      reg: /^(ice-lite)/\n    },\n    {\n      // a=ice-ufrag:F7gI\n      name: 'iceUfrag',\n      reg: /^ice-ufrag:(\\S*)/,\n      format: 'ice-ufrag:%s'\n    },\n    {\n      // a=ice-pwd:x9cml/YzichV2+XlhiMu8g\n      name: 'icePwd',\n      reg: /^ice-pwd:(\\S*)/,\n      format: 'ice-pwd:%s'\n    },\n    {\n      // a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33\n      name: 'fingerprint',\n      reg: /^fingerprint:(\\S*) (\\S*)/,\n      names: ['type', 'hash'],\n      format: 'fingerprint:%s %s'\n    },\n    {\n      // a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n      // a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10\n      // a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10\n      // a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10\n      // a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10\n      push:'candidates',\n      reg: /^candidate:(\\S*) (\\d*) (\\S*) (\\d*) (\\S*) (\\d*) typ (\\S*)(?: raddr (\\S*) rport (\\d*))?(?: tcptype (\\S*))?(?: generation (\\d*))?(?: network-id (\\d*))?(?: network-cost (\\d*))?/,\n      names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],\n      format: function (o) {\n        var str = 'candidate:%s %d %s %d %s %d typ %s';\n\n        str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';\n\n        // NB: candidate has three optional chunks, so %void middles one if it's missing\n        str += (o.tcptype != null) ? ' tcptype %s' : '%v';\n\n        if (o.generation != null) {\n          str += ' generation %d';\n        }\n\n        str += (o['network-id'] != null) ? ' network-id %d' : '%v';\n        str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';\n        return str;\n      }\n    },\n    {\n      // a=end-of-candidates (keep after the candidates line for readability)\n      name: 'endOfCandidates',\n      reg: /^(end-of-candidates)/\n    },\n    {\n      // a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...\n      name: 'remoteCandidates',\n      reg: /^remote-candidates:(.*)/,\n      format: 'remote-candidates:%s'\n    },\n    {\n      // a=ice-options:google-ice\n      name: 'iceOptions',\n      reg: /^ice-options:(\\S*)/,\n      format: 'ice-options:%s'\n    },\n    {\n      // a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1\n      push: 'ssrcs',\n      reg: /^ssrc:(\\d*) ([\\w_-]*)(?::(.*))?/,\n      names: ['id', 'attribute', 'value'],\n      format: function (o) {\n        var str = 'ssrc:%d';\n        if (o.attribute != null) {\n          str += ' %s';\n          if (o.value != null) {\n            str += ':%s';\n          }\n        }\n        return str;\n      }\n    },\n    {\n      // a=ssrc-group:FEC 1 2\n      // a=ssrc-group:FEC-FR 3004364195 1080772241\n      push: 'ssrcGroups',\n      // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E\n      reg: /^ssrc-group:([\\x21\\x23\\x24\\x25\\x26\\x27\\x2A\\x2B\\x2D\\x2E\\w]*) (.*)/,\n      names: ['semantics', 'ssrcs'],\n      format: 'ssrc-group:%s %s'\n    },\n    {\n      // a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV\n      name: 'msidSemantic',\n      reg: /^msid-semantic:\\s?(\\w*) (\\S*)/,\n      names: ['semantic', 'token'],\n      format: 'msid-semantic: %s %s' // space after ':' is not accidental\n    },\n    {\n      // a=group:BUNDLE audio video\n      push: 'groups',\n      reg: /^group:(\\w*) (.*)/,\n      names: ['type', 'mids'],\n      format: 'group:%s %s'\n    },\n    {\n      // a=rtcp-mux\n      name: 'rtcpMux',\n      reg: /^(rtcp-mux)/\n    },\n    {\n      // a=rtcp-rsize\n      name: 'rtcpRsize',\n      reg: /^(rtcp-rsize)/\n    },\n    {\n      // a=sctpmap:5000 webrtc-datachannel 1024\n      name: 'sctpmap',\n      reg: /^sctpmap:([\\w_/]*) (\\S*)(?: (\\S*))?/,\n      names: ['sctpmapNumber', 'app', 'maxMessageSize'],\n      format: function (o) {\n        return (o.maxMessageSize != null)\n          ? 'sctpmap:%s %s %s'\n          : 'sctpmap:%s %s';\n      }\n    },\n    {\n      // a=x-google-flag:conference\n      name: 'xGoogleFlag',\n      reg: /^x-google-flag:([^\\s]*)/,\n      format: 'x-google-flag:%s'\n    },\n    {\n      // a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0\n      push: 'rids',\n      reg: /^rid:([\\d\\w]+) (\\w+)(?: ([\\S| ]*))?/,\n      names: ['id', 'direction', 'params'],\n      format: function (o) {\n        return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';\n      }\n    },\n    {\n      // a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]\n      // a=imageattr:* send [x=800,y=640] recv *\n      // a=imageattr:100 recv [x=320,y=240]\n      push: 'imageattrs',\n      reg: new RegExp(\n        // a=imageattr:97\n        '^imageattr:(\\\\d+|\\\\*)' +\n        // send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]\n        '[\\\\s\\\\t]+(send|recv)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*)' +\n        // recv [x=330,y=250]\n        '(?:[\\\\s\\\\t]+(recv|send)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*))?'\n      ),\n      names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],\n      format: function (o) {\n        return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');\n      }\n    },\n    {\n      // a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8\n      // a=simulcast:recv 1;4,5 send 6;7\n      name: 'simulcast',\n      reg: new RegExp(\n        // a=simulcast:\n        '^simulcast:' +\n        // send 1,2,3;~4,~5\n        '(send|recv) ([a-zA-Z0-9\\\\-_~;,]+)' +\n        // space + recv 6;~7,~8\n        '(?:\\\\s?(send|recv) ([a-zA-Z0-9\\\\-_~;,]+))?' +\n        // end\n        '$'\n      ),\n      names: ['dir1', 'list1', 'dir2', 'list2'],\n      format: function (o) {\n        return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');\n      }\n    },\n    {\n      // old simulcast draft 03 (implemented by Firefox)\n      //   https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03\n      // a=simulcast: recv pt=97;98 send pt=97\n      // a=simulcast: send rid=5;6;7 paused=6,7\n      name: 'simulcast_03',\n      reg: /^simulcast:[\\s\\t]+([\\S+\\s\\t]+)$/,\n      names: ['value'],\n      format: 'simulcast: %s'\n    },\n    {\n      // a=framerate:25\n      // a=framerate:29.97\n      name: 'framerate',\n      reg: /^framerate:(\\d+(?:$|\\.\\d+))/,\n      format: 'framerate:%s'\n    },\n    {\n      // RFC4570\n      // a=source-filter: incl IN IP4 239.5.2.31 10.1.15.5\n      name: 'sourceFilter',\n      reg: /^source-filter: *(excl|incl) (\\S*) (IP4|IP6|\\*) (\\S*) (.*)/,\n      names: ['filterMode', 'netType', 'addressTypes', 'destAddress', 'srcList'],\n      format: 'source-filter: %s %s %s %s %s'\n    },\n    {\n      // a=bundle-only\n      name: 'bundleOnly',\n      reg: /^(bundle-only)/\n    },\n    {\n      // a=label:1\n      name: 'label',\n      reg: /^label:(.+)/,\n      format: 'label:%s'\n    },\n    {\n      // RFC version 26 for SCTP over DTLS\n      // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-5\n      name: 'sctpPort',\n      reg: /^sctp-port:(\\d+)$/,\n      format: 'sctp-port:%s'\n    },\n    {\n      // RFC version 26 for SCTP over DTLS\n      // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-26#section-6\n      name: 'maxMessageSize',\n      reg: /^max-message-size:(\\d+)$/,\n      format: 'max-message-size:%s'\n    },\n    {\n      // RFC7273\n      // a=ts-refclk:ptp=IEEE1588-2008:39-A7-94-FF-FE-07-CB-D0:37\n      push:'tsRefClocks',\n      reg: /^ts-refclk:([^\\s=]*)(?:=(\\S*))?/,\n      names: ['clksrc', 'clksrcExt'],\n      format: function (o) {\n        return 'ts-refclk:%s' + (o.clksrcExt != null ? '=%s' : '');\n      }\n    },\n    {\n      // RFC7273\n      // a=mediaclk:direct=963214424\n      name:'mediaClk',\n      reg: /^mediaclk:(?:id=(\\S*))? *([^\\s=]*)(?:=(\\S*))?(?: *rate=(\\d+)\\/(\\d+))?/,\n      names: ['id', 'mediaClockName', 'mediaClockValue', 'rateNumerator', 'rateDenominator'],\n      format: function (o) {\n        var str = 'mediaclk:';\n        str += (o.id != null ? 'id=%s %s' : '%v%s');\n        str += (o.mediaClockValue != null ? '=%s' : '');\n        str += (o.rateNumerator != null ? ' rate=%s' : '');\n        str += (o.rateDenominator != null ? '/%s' : '');\n        return str;\n      }\n    },\n    {\n      // a=keywds:keywords\n      name: 'keywords',\n      reg: /^keywds:(.+)$/,\n      format: 'keywds:%s'\n    },\n    {\n      // a=content:main\n      name: 'content',\n      reg: /^content:(.+)/,\n      format: 'content:%s'\n    },\n    // BFCP https://tools.ietf.org/html/rfc4583\n    {\n      // a=floorctrl:c-s\n      name: 'bfcpFloorCtrl',\n      reg: /^floorctrl:(c-only|s-only|c-s)/,\n      format: 'floorctrl:%s'\n    },\n    {\n      // a=confid:1\n      name: 'bfcpConfId',\n      reg: /^confid:(\\d+)/,\n      format: 'confid:%s'\n    },\n    {\n      // a=userid:1\n      name: 'bfcpUserId',\n      reg: /^userid:(\\d+)/,\n      format: 'userid:%s'\n    },\n    {\n      // a=floorid:1\n      name: 'bfcpFloorId',\n      reg: /^floorid:(.+) (?:m-stream|mstrm):(.+)/,\n      names: ['id', 'mStream'],\n      format: 'floorid:%s mstrm:%s'\n    },\n    {\n      // any a= that we don't understand is kept verbatim on media.invalid\n      push: 'invalid',\n      names: ['value']\n    }\n  ]\n};\n\n// set sensible defaults to avoid polluting the grammar with boring details\nObject.keys(grammar).forEach(function (key) {\n  var objs = grammar[key];\n  objs.forEach(function (obj) {\n    if (!obj.reg) {\n      obj.reg = /(.*)/;\n    }\n    if (!obj.format) {\n      obj.format = '%s';\n    }\n  });\n});\n","var parser = require('./parser');\nvar writer = require('./writer');\n\nexports.write = writer;\nexports.parse = parser.parse;\nexports.parseParams = parser.parseParams;\nexports.parseFmtpConfig = parser.parseFmtpConfig; // Alias of parseParams().\nexports.parsePayloads = parser.parsePayloads;\nexports.parseRemoteCandidates = parser.parseRemoteCandidates;\nexports.parseImageAttributes = parser.parseImageAttributes;\nexports.parseSimulcastStreamList = parser.parseSimulcastStreamList;\n","var toIntIfInt = function (v) {\n  return String(Number(v)) === v ? Number(v) : v;\n};\n\nvar attachProperties = function (match, location, names, rawName) {\n  if (rawName && !names) {\n    location[rawName] = toIntIfInt(match[1]);\n  }\n  else {\n    for (var i = 0; i < names.length; i += 1) {\n      if (match[i+1] != null) {\n        location[names[i]] = toIntIfInt(match[i+1]);\n      }\n    }\n  }\n};\n\nvar parseReg = function (obj, location, content) {\n  var needsBlank = obj.name && obj.names;\n  if (obj.push && !location[obj.push]) {\n    location[obj.push] = [];\n  }\n  else if (needsBlank && !location[obj.name]) {\n    location[obj.name] = {};\n  }\n  var keyLocation = obj.push ?\n    {} :  // blank object that will be pushed\n    needsBlank ? location[obj.name] : location; // otherwise, named location or root\n\n  attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);\n\n  if (obj.push) {\n    location[obj.push].push(keyLocation);\n  }\n};\n\nvar grammar = require('./grammar');\nvar validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);\n\nexports.parse = function (sdp) {\n  var session = {}\n    , media = []\n    , location = session; // points at where properties go under (one of the above)\n\n  // parse lines we understand\n  sdp.split(/(\\r\\n|\\r|\\n)/).filter(validLine).forEach(function (l) {\n    var type = l[0];\n    var content = l.slice(2);\n    if (type === 'm') {\n      media.push({rtp: [], fmtp: []});\n      location = media[media.length-1]; // point at latest media line\n    }\n\n    for (var j = 0; j < (grammar[type] || []).length; j += 1) {\n      var obj = grammar[type][j];\n      if (obj.reg.test(content)) {\n        return parseReg(obj, location, content);\n      }\n    }\n  });\n\n  session.media = media; // link it up\n  return session;\n};\n\nvar paramReducer = function (acc, expr) {\n  var s = expr.split(/=(.+)/, 2);\n  if (s.length === 2) {\n    acc[s[0]] = toIntIfInt(s[1]);\n  } else if (s.length === 1 && expr.length > 1) {\n    acc[s[0]] = undefined;\n  }\n  return acc;\n};\n\nexports.parseParams = function (str) {\n  return str.split(/;\\s?/).reduce(paramReducer, {});\n};\n\n// For backward compatibility - alias will be removed in 3.0.0\nexports.parseFmtpConfig = exports.parseParams;\n\nexports.parsePayloads = function (str) {\n  return str.toString().split(' ').map(Number);\n};\n\nexports.parseRemoteCandidates = function (str) {\n  var candidates = [];\n  var parts = str.split(' ').map(toIntIfInt);\n  for (var i = 0; i < parts.length; i += 3) {\n    candidates.push({\n      component: parts[i],\n      ip: parts[i + 1],\n      port: parts[i + 2]\n    });\n  }\n  return candidates;\n};\n\nexports.parseImageAttributes = function (str) {\n  return str.split(' ').map(function (item) {\n    return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});\n  });\n};\n\nexports.parseSimulcastStreamList = function (str) {\n  return str.split(';').map(function (stream) {\n    return stream.split(',').map(function (format) {\n      var scid, paused = false;\n\n      if (format[0] !== '~') {\n        scid = toIntIfInt(format);\n      } else {\n        scid = toIntIfInt(format.substring(1, format.length));\n        paused = true;\n      }\n\n      return {\n        scid: scid,\n        paused: paused\n      };\n    });\n  });\n};\n","var grammar = require('./grammar');\n\n// customized util.format - discards excess arguments and can void middle ones\nvar formatRegExp = /%[sdv%]/g;\nvar format = function (formatStr) {\n  var i = 1;\n  var args = arguments;\n  var len = args.length;\n  return formatStr.replace(formatRegExp, function (x) {\n    if (i >= len) {\n      return x; // missing argument\n    }\n    var arg = args[i];\n    i += 1;\n    switch (x) {\n    case '%%':\n      return '%';\n    case '%s':\n      return String(arg);\n    case '%d':\n      return Number(arg);\n    case '%v':\n      return '';\n    }\n  });\n  // NB: we discard excess arguments - they are typically undefined from makeLine\n};\n\nvar makeLine = function (type, obj, location) {\n  var str = obj.format instanceof Function ?\n    (obj.format(obj.push ? location : location[obj.name])) :\n    obj.format;\n\n  var args = [type + '=' + str];\n  if (obj.names) {\n    for (var i = 0; i < obj.names.length; i += 1) {\n      var n = obj.names[i];\n      if (obj.name) {\n        args.push(location[obj.name][n]);\n      }\n      else { // for mLine and push attributes\n        args.push(location[obj.names[i]]);\n      }\n    }\n  }\n  else {\n    args.push(location[obj.name]);\n  }\n  return format.apply(null, args);\n};\n\n// RFC specified order\n// TODO: extend this with all the rest\nvar defaultOuterOrder = [\n  'v', 'o', 's', 'i',\n  'u', 'e', 'p', 'c',\n  'b', 't', 'r', 'z', 'a'\n];\nvar defaultInnerOrder = ['i', 'c', 'b', 'a'];\n\n\nmodule.exports = function (session, opts) {\n  opts = opts || {};\n  // ensure certain properties exist\n  if (session.version == null) {\n    session.version = 0; // 'v=0' must be there (only defined version atm)\n  }\n  if (session.name == null) {\n    session.name = ' '; // 's= ' must be there if no meaningful name set\n  }\n  session.media.forEach(function (mLine) {\n    if (mLine.payloads == null) {\n      mLine.payloads = '';\n    }\n  });\n\n  var outerOrder = opts.outerOrder || defaultOuterOrder;\n  var innerOrder = opts.innerOrder || defaultInnerOrder;\n  var sdp = [];\n\n  // loop through outerOrder for matching properties on session\n  outerOrder.forEach(function (type) {\n    grammar[type].forEach(function (obj) {\n      if (obj.name in session && session[obj.name] != null) {\n        sdp.push(makeLine(type, obj, session));\n      }\n      else if (obj.push in session && session[obj.push] != null) {\n        session[obj.push].forEach(function (el) {\n          sdp.push(makeLine(type, obj, el));\n        });\n      }\n    });\n  });\n\n  // then for each media line, follow the innerOrder\n  session.media.forEach(function (mLine) {\n    sdp.push(makeLine('m', grammar.m[0], mLine));\n\n    innerOrder.forEach(function (type) {\n      grammar[type].forEach(function (obj) {\n        if (obj.name in mLine && mLine[obj.name] != null) {\n          sdp.push(makeLine(type, obj, mLine));\n        }\n        else if (obj.push in mLine && mLine[obj.push] != null) {\n          mLine[obj.push].forEach(function (el) {\n            sdp.push(makeLine(type, obj, el));\n          });\n        }\n      });\n    });\n  });\n\n  return sdp.join('\\r\\n') + '\\r\\n';\n};\n","/* Copyright @ 2016 Atlassian Pty Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nvar transform = require('sdp-transform');\nvar transformUtils = require('./transform-utils');\nvar parseSsrcs = transformUtils.parseSsrcs;\nvar writeSsrcs = transformUtils.writeSsrcs;\n\n//region Constants\n\nvar DEFAULT_NUM_OF_LAYERS = 3;\n\n//endregion\n\nfunction getSsrcAttribute (mLine, ssrc, attributeName) {\n    return mLine\n        .ssrcs\n        .filter(function(ssrcInfo) { return ssrcInfo.id === ssrc; })\n        .filter(function(ssrcInfo) { return ssrcInfo.attribute === attributeName; })\n        .map(function(ssrcInfo) { return ssrcInfo.value; })[0];\n}\n\n//region Ctor\n\nfunction Simulcast(options) {\n\n    this.options = options ? options : {};\n\n    if (!this.options.numOfLayers) {\n        this.options.numOfLayers = DEFAULT_NUM_OF_LAYERS;\n    }\n    console.log(\"SdpSimulcast: using \" + this.options.numOfLayers + \" layers\");\n\n    /**\n     * An IN-ORDER list of the simulcast ssrcs\n     * @type {list<number>}\n     */\n    this.ssrcCache = [];\n}\n\n//endregion\n\n//region Stateless private utility functions\n\n/**\n * Returns a random integer between min (included) and max (excluded)\n * Using Math.round() gives a non-uniform distribution!\n * @returns {number}\n */\nfunction generateSSRC() {\n    var min = 0, max = 0xffffffff;\n    return Math.floor(Math.random() * (max - min)) + min;\n};\n\nfunction processVideo(session, action) {\n    if (session == null || !Array.isArray(session.media)) {\n        return;\n    }\n\n    session.media.forEach(function (mLine) {\n        if (mLine.type === 'video') {\n            action(mLine);\n        }\n    });\n};\n\nfunction validateDescription(desc)\n{\n    return desc && desc != null\n        && desc.type && desc.type != ''\n        && desc.sdp && desc.sdp != '';\n}\n\nfunction explodeRemoteSimulcast(mLine) {\n\n    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {\n        return;\n    }\n\n    var sources = parseSsrcs(mLine);\n    var order = [];\n\n    // Find the SIM group and explode its sources.\n    var j = mLine.ssrcGroups.length;\n    while (j--) {\n\n        if (mLine.ssrcGroups[j].semantics !== 'SIM') {\n            continue;\n        }\n\n        var simulcastSsrcs = mLine.ssrcGroups[j].ssrcs.split(' ');\n\n        for (var i = 0; i < simulcastSsrcs.length; i++) {\n\n            var ssrc = simulcastSsrcs[i];\n            order.push(ssrc);\n\n            var parts = sources[ssrc].msid.split(' ');\n            sources[ssrc].msid = [parts[0], '/', i, ' ', parts[1], '/', i].join('');\n            sources[ssrc].cname = [sources[ssrc].cname, '/', i].join('');\n\n            // Remove all the groups that this SSRC participates in.\n            mLine.ssrcGroups.forEach(function (relatedGroup) {\n                if (relatedGroup.semantics === 'SIM') {\n                    return;\n                }\n\n                var relatedSsrcs = relatedGroup.ssrcs.split(' ');\n                if (relatedSsrcs.indexOf(ssrc) === -1) {\n                    return;\n                }\n\n                // Nuke all the related SSRCs.\n                relatedSsrcs.forEach(function (relatedSSRC) {\n                    sources[relatedSSRC].msid = sources[ssrc].msid;\n                    sources[relatedSSRC].cname = sources[ssrc].cname;\n                    if (relatedSSRC !== ssrc) {\n                        order.push(relatedSSRC);\n                    }\n                });\n\n                // Schedule the related group for nuking.\n            })\n        }\n\n        mLine.ssrcs = writeSsrcs(sources, order);\n        mLine.ssrcGroups.splice(j, 1);\n    };\n}\n\nfunction implodeRemoteSimulcast(mLine) {\n\n    if (!mLine || !Array.isArray(mLine.ssrcGroups)) {\n        console.info('Halt: There are no SSRC groups in the remote ' +\n                'description.');\n        return;\n    }\n\n    var sources = parseSsrcs(mLine);\n\n    // Find the SIM group and nuke it.\n    mLine.ssrcGroups.forEach(function (simulcastGroup) {\n        if (simulcastGroup.semantics !== 'SIM') {\n            return;\n        }\n\n        console.info(\"Imploding SIM group: \" + simulcastGroup.ssrcs);\n        // Schedule the SIM group for nuking.\n        simulcastGroup.nuke = true;\n\n        var simulcastSsrcs = simulcastGroup.ssrcs.split(' ');\n\n        // Nuke all the higher layer SSRCs.\n        for (var i = 1; i < simulcastSsrcs.length; i++) {\n\n            var ssrc = simulcastSsrcs[i];\n            delete sources[ssrc];\n\n            // Remove all the groups that this SSRC participates in.\n            mLine.ssrcGroups.forEach(function (relatedGroup) {\n                if (relatedGroup.semantics === 'SIM') {\n                    return;\n                }\n\n                var relatedSsrcs = relatedGroup.ssrcs.split(' ');\n                if (relatedSsrcs.indexOf(ssrc) === -1) {\n                    return;\n                }\n\n                // Nuke all the related SSRCs.\n                relatedSsrcs.forEach(function (relatedSSRC) {\n                    delete sources[relatedSSRC];\n                });\n\n                // Schedule the related group for nuking.\n                relatedGroup.nuke = true;\n            })\n        }\n\n        return;\n    });\n\n    mLine.ssrcs = writeSsrcs(sources);\n\n    // Nuke all the scheduled groups.\n    var i = mLine.ssrcGroups.length;\n    while (i--) {\n        if (mLine.ssrcGroups[i].nuke) {\n            mLine.ssrcGroups.splice(i, 1);\n        }\n    }\n}\n\nfunction removeGoogConference(mLine) {\n    if (!mLine || typeof mLine.xGoogleFlag === 'undefined') {\n        return;\n    }\n\n    mLine.xGoogleFlag = undefined;\n}\n\nfunction assertGoogConference(mLine) {\n    if (!mLine) {\n        return;\n    }\n\n    if (!Array.isArray(mLine.invalid)) {\n        mLine.invalid = [];\n    }\n\n    if (!mLine.invalid.some(\n            function (i) { return i.value === 'x-google-flag:conference' })) {\n        mLine.invalid.push({'value': 'x-google-flag:conference'});\n    }\n}\n\nSimulcast.prototype.clearSsrcCache = function() {\n    this.ssrcCache = [];\n}\n\n/**\n * When we start as video muted, all of the video\n *  ssrcs get generated so we can include them as part\n *  of the original session-accept.  That means we\n *  need this library to restore to those same ssrcs\n *  the first time we unmute, so we need the ability to\n *  force its cache\n */\nSimulcast.prototype.setSsrcCache = function(ssrcs) {\n    this.ssrcCache = ssrcs;\n}\n\n//endregion\n\n//region \"Private\" functions\n\n/**\n * Given a video mLine, return a list of the video ssrcs\n *  in simulcast layer order (returns a list of just\n *  the primary ssrc if there are no simulcast layers)\n */\nSimulcast.prototype._parseSimLayers = function (mLine) {\n    var simGroup = mLine.ssrcGroups &&\n        mLine.ssrcGroups.find(function(group) { return group.semantics === \"SIM\"; });\n    if (simGroup) {\n        return simGroup.ssrcs\n            .split(\" \")\n            .map(function(ssrcStr) { return parseInt(ssrcStr) });\n    } else {\n        return [mLine.ssrcs[0].id];\n    }\n}\n\nSimulcast.prototype._buildNewToOldSsrcMap = function (newSsrcList, oldSsrcList) {\n    var ssrcMap = {};\n    for (var i = 0; i < newSsrcList.length; ++i) {\n        var newSsrc = newSsrcList[i];\n        var oldSsrc = oldSsrcList[i] || null;\n        ssrcMap[newSsrc] = oldSsrc;\n    }\n    return ssrcMap;\n}\n\nSimulcast.prototype._fillInSourceDataFromCache = function(mLine) {\n    console.log(\"SdpSimulcast restoring from cache: \", this.ssrcCache);\n    var newSimSsrcs = this._parseSimLayers(mLine);\n    console.log(\"SdpSimulcast Parsed new sim ssrcs: \", newSimSsrcs);\n    var newMsid = getSsrcAttribute(mLine, newSimSsrcs[0], \"msid\");\n    var newCname = getSsrcAttribute(mLine, newSimSsrcs[0], \"cname\");\n    var ssrcsToReplace = this._buildNewToOldSsrcMap(newSimSsrcs, this.ssrcCache);\n    console.log(\"SdpSimulcast built replacement map: \", ssrcsToReplace);\n    // New sdp might only have 1 layer, so not every cached ssrc will have a new one\n    //  to replace directly\n    var ssrcsToAdd = this.ssrcCache\n        .filter(function(ssrc) { return Object.values(ssrcsToReplace).indexOf(ssrc) === -1; });\n    console.log(\"SdpSimulcast built ssrcs to add: \", ssrcsToAdd);\n\n    // First do the replacements\n    mLine.ssrcs.forEach(function(ssrc) {\n        if (ssrcsToReplace[ssrc.id]) {\n            ssrc.id = ssrcsToReplace[ssrc.id];\n        }\n    });\n    // Now the adds\n    ssrcsToAdd.forEach(function(ssrc) {\n        mLine.ssrcs.push({\n            id: ssrc,\n            attribute: \"msid\",\n            value: newMsid\n        });\n        mLine.ssrcs.push({\n            id: ssrc,\n            attribute: \"cname\",\n            value: newCname\n        });\n    });\n    mLine.ssrcGroups = mLine.ssrcGroups || [];\n    mLine.ssrcGroups.push({\n        semantics: \"SIM\",\n        ssrcs: this.ssrcCache.join(\" \")\n    });\n    return mLine;\n}\n\nSimulcast.prototype._generateSourceData = function(mLine, primarySsrc) {\n    var addAssociatedStream = function(mLine, ssrc) {\n        mLine.ssrcs.push({\n            id: ssrc,\n            attribute: \"cname\",\n            value: primarySsrcCname\n        });\n        mLine.ssrcs.push({\n            id: ssrc,\n            attribute: \"msid\",\n            value: primarySsrcMsid\n        });\n    }\n    var primarySsrcMsid = getSsrcAttribute(mLine, primarySsrc, \"msid\");\n    var primarySsrcCname = getSsrcAttribute(mLine, primarySsrc, \"cname\");\n\n    // In Unified-plan mode, the a=ssrc lines with the msid attribute are not present\n    // in the answers that Chrome and Safari generate for an offer received from Jicofo.\n    // Generate these a=ssrc lines using the msid values from the a=msid line.\n    if (this.options.usesUnifiedPlan && !primarySsrcMsid) {\n        primarySsrcMsid = mLine.msid;\n        var primarySsrcs = mLine.ssrcs;\n        primarySsrcs.forEach(ssrc => {\n            mLine.ssrcs.push({\n                id: ssrc.id,\n                attribute: \"msid\",\n                value: primarySsrcMsid\n            });\n        });\n    }\n\n    // Generate sim layers\n    var simSsrcs = [];\n    for (var i = 0; i < this.options.numOfLayers - 1; ++i) {\n        var simSsrc = generateSSRC();\n        addAssociatedStream(mLine, simSsrc);\n        simSsrcs.push(simSsrc);\n    }\n    mLine.ssrcGroups = mLine.ssrcGroups || [];\n    mLine.ssrcGroups.push({\n        semantics: \"SIM\",\n        ssrcs: primarySsrc + \" \" + simSsrcs.join(\" \")\n    });\n    return mLine;\n}\n\n\n\n// Assumptions:\n//  1) 'mLine' contains only a single primary video source\n//   (i.e. it will not already have simulcast streams inserted)\n//  2) 'mLine' MAY already contain an RTX stream for its video source\n//  3) 'mLine' is in sendrecv or sendonly state\n// Guarantees:\n//  1) return mLine will contain 2 additional simulcast layers\n//   generated\n//  2) if the base video ssrc in mLine has been seen before,\n//   then the same generated simulcast streams from before will\n//   be used again\n//  3) if rtx is enabled for the mLine, all generated simulcast\n//   streams will have rtx streams generated as well\n//  4) if rtx has been generated for a src before, we will generate\n//   the same rtx stream again\nSimulcast.prototype._restoreSimulcast = function(mLine) {\n    // First, find the primary video source in the given\n    // mLine and see if we've seen it before.\n    var primarySsrc;\n    var numSsrcs = mLine.ssrcs && mLine.ssrcs\n        .map(function(ssrcInfo) { return ssrcInfo.id; })\n        .filter(function(ssrc, index, array) {\n            return array.indexOf(ssrc) === index;\n        })\n        .length || 0;\n    var numGroups = (mLine.ssrcGroups && mLine.ssrcGroups.length) || 0;\n\n    if (numSsrcs === 0 || numSsrcs > 2) {\n        // Unsupported scenario\n        return mLine;\n    }\n    if (numSsrcs == 2 && numGroups === 0) {\n        // Unsupported scenario\n        return mLine;\n    }\n\n    if (numSsrcs === 1) {\n        primarySsrc = mLine.ssrcs[0].id;\n    } else {\n        // There must be an FID group, so parse\n        //  that and pull the primary ssrc from there\n        var fidGroup = mLine.ssrcGroups.filter(function(group) { return group.semantics === \"FID\"; })[0];\n        if (fidGroup) {\n            primarySsrc = parseInt(fidGroup.ssrcs.split(\" \")[0]);\n        } else {\n            // Unsupported scenario\n            return mLine;\n        }\n    }\n    console.log(\"SdpSimulcast: current ssrc cache: \", this.ssrcCache);\n    console.log(\"SdpSimulcast: parsed primary ssrc \" + primarySsrc);\n\n    var seenPrimarySsrc = this.ssrcCache.indexOf(primarySsrc) !== -1;\n\n    if (seenPrimarySsrc) {\n        console.log(\"SdpSimulcast: Have seen primary ssrc before, \" +\n            \"filling in data from cache\");\n        mLine = this._fillInSourceDataFromCache(mLine);\n    } else {\n        console.log(\"SdpSimulcast: Have not seen primary ssrc before, \" +\n            \"generating source data\");\n        mLine = this._generateSourceData(mLine, primarySsrc);\n    }\n    // Now update the cache to match whatever we've just put into this sdp\n    this.ssrcCache = this._parseSimLayers(mLine);\n    return mLine;\n}\n\n//endregion\n\n//region \"Public\" functions\n\n/**\n *\n * @param desc\n * @param enableConferenceFlag\n * @returns {RTCSessionDescription}\n */\nSimulcast.prototype.mungeRemoteDescription = function (desc, enableConferenceFlag) {\n\n    if (!validateDescription(desc)) {\n        return desc;\n    }\n\n    var session = transform.parse(desc.sdp);\n\n    var self = this;\n    processVideo(session, function (mLine) {\n\n        // Handle simulcast reception.\n        if (self.options.explodeRemoteSimulcast) {\n            explodeRemoteSimulcast(mLine);\n        } else {\n            implodeRemoteSimulcast(mLine);\n        }\n\n        // Add or remove \"x-google-conference\" from the remote description based on whether the client\n        // has enabled simulcast for the local video source. For cases where we disable simulcast for desktop share,\n        // it is necessary to remove the flag so that Chrome stops sending T1 temporal layers. It also fixes other\n        // issues related to screensharing like https://bugs.chromium.org/p/chromium/issues/detail?id=1093819.\n        if (!self.options.usesUnifiedPlan && enableConferenceFlag) {\n            assertGoogConference(mLine);\n        } else {\n            removeGoogConference(mLine);\n        }\n    });\n\n    return new RTCSessionDescription({\n        type: desc.type,\n        sdp: transform.write(session)\n    });\n};\n\n/**\n *\n * NOTE this method should be called only if simulcast is supported by\n * the current browser, otherwise local SDP should not be munged.\n * @param desc\n * @returns {RTCSessionDescription}\n */\nSimulcast.prototype.mungeLocalDescription = function (desc) {\n\n    if (!validateDescription(desc)) {\n        return desc;\n    }\n\n    var session = transform.parse(desc.sdp);\n\n    var self = this;\n    processVideo(session, function (mLine) {\n        if (mLine.direction == 'recvonly' || mLine.direction == 'inactive')\n        {\n            return;\n        }\n        self._restoreSimulcast(mLine);\n    });\n\n    return new RTCSessionDescription({\n        type: desc.type,\n        sdp: transform.write(session)\n    });\n};\n\n//endregion\n\nmodule.exports = Simulcast;\n","/* Copyright @ 2015 Atlassian Pty Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * FIXME\n * @param sources FIXME\n * @param order An array of SSRCs which will be used to order the entries in\n * the returned array. Sources whose SSRC appears in 'order' will be added first,\n * in the specified order, and all other sources will be added afterwards (in\n * no specific order).\n * @returns {Array} FIXME\n */\nexports.writeSsrcs = function(sources, order) {\n  var ssrcs = [];\n\n  // expand sources to ssrcs\n  if (typeof sources !== 'undefined' &&\n      Object.keys(sources).length !== 0) {\n\n    if (!Array.isArray(order)) {\n      order = []\n    }\n\n    // Add the sources that appear in 'order' first.\n    for (var i = 0; i < order.length; i++) {\n      var ssrc = order[i];\n      var source = sources[ssrc];\n      Object.keys(source).forEach(function (attribute) {\n        ssrcs.push({\n          id: ssrc,\n          attribute: attribute,\n          value: source[attribute]\n        });\n      });\n    }\n\n    // Now add the rest of the sources.\n    Object.keys(sources).forEach(function (ssrc) {\n      ssrc = parseInt(ssrc); // Object.keys() returns string\n      if (order.indexOf(ssrc) >= 0) {\n        // Already added.\n        return;\n      }\n\n      var source = sources[ssrc];\n      Object.keys(source).forEach(function (attribute) {\n        ssrcs.push({\n          id: ssrc,\n          attribute: attribute,\n          value: source[attribute]\n        });\n      });\n    });\n  }\n\n  return ssrcs;\n};\n\nexports.parseSsrcs = function (mLine) {\n  var sources = {};\n  // group sources attributes by ssrc.\n  if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {\n    mLine.ssrcs.forEach(function (ssrc) {\n      if (!sources[ssrc.id])\n        sources[ssrc.id] = {};\n      sources[ssrc.id][ssrc.attribute] = ssrc.value;\n    });\n  }\n  return sources;\n};\n\n","/*!\n * async\n * https://github.com/caolan/async\n *\n * Copyright 2010-2014 Caolan McMahon\n * Released under the MIT license\n */\n/*jshint onevar: false, indent:4 */\n/*global setImmediate: false, setTimeout: false, console: false */\n(function () {\n\n    var async = {};\n\n    // global on the server, window in the browser\n    var root, previous_async;\n\n    root = this;\n    if (root != null) {\n      previous_async = root.async;\n    }\n\n    async.noConflict = function () {\n        root.async = previous_async;\n        return async;\n    };\n\n    function only_once(fn) {\n        var called = false;\n        return function() {\n            if (called) throw new Error(\"Callback was already called.\");\n            called = true;\n            fn.apply(root, arguments);\n        }\n    }\n\n    //// cross-browser compatiblity functions ////\n\n    var _toString = Object.prototype.toString;\n\n    var _isArray = Array.isArray || function (obj) {\n        return _toString.call(obj) === '[object Array]';\n    };\n\n    var _each = function (arr, iterator) {\n        if (arr.forEach) {\n            return arr.forEach(iterator);\n        }\n        for (var i = 0; i < arr.length; i += 1) {\n            iterator(arr[i], i, arr);\n        }\n    };\n\n    var _map = function (arr, iterator) {\n        if (arr.map) {\n            return arr.map(iterator);\n        }\n        var results = [];\n        _each(arr, function (x, i, a) {\n            results.push(iterator(x, i, a));\n        });\n        return results;\n    };\n\n    var _reduce = function (arr, iterator, memo) {\n        if (arr.reduce) {\n            return arr.reduce(iterator, memo);\n        }\n        _each(arr, function (x, i, a) {\n            memo = iterator(memo, x, i, a);\n        });\n        return memo;\n    };\n\n    var _keys = function (obj) {\n        if (Object.keys) {\n            return Object.keys(obj);\n        }\n        var keys = [];\n        for (var k in obj) {\n            if (obj.hasOwnProperty(k)) {\n                keys.push(k);\n            }\n        }\n        return keys;\n    };\n\n    //// exported async module functions ////\n\n    //// nextTick implementation with browser-compatible fallback ////\n    if (typeof process === 'undefined' || !(process.nextTick)) {\n        if (typeof setImmediate === 'function') {\n            async.nextTick = function (fn) {\n                // not a direct alias for IE10 compatibility\n                setImmediate(fn);\n            };\n            async.setImmediate = async.nextTick;\n        }\n        else {\n            async.nextTick = function (fn) {\n                setTimeout(fn, 0);\n            };\n            async.setImmediate = async.nextTick;\n        }\n    }\n    else {\n        async.nextTick = process.nextTick;\n        if (typeof setImmediate !== 'undefined') {\n            async.setImmediate = function (fn) {\n              // not a direct alias for IE10 compatibility\n              setImmediate(fn);\n            };\n        }\n        else {\n            async.setImmediate = async.nextTick;\n        }\n    }\n\n    async.each = function (arr, iterator, callback) {\n        callback = callback || function () {};\n        if (!arr.length) {\n            return callback();\n        }\n        var completed = 0;\n        _each(arr, function (x) {\n            iterator(x, only_once(done) );\n        });\n        function done(err) {\n          if (err) {\n              callback(err);\n              callback = function () {};\n          }\n          else {\n              completed += 1;\n              if (completed >= arr.length) {\n                  callback();\n              }\n          }\n        }\n    };\n    async.forEach = async.each;\n\n    async.eachSeries = function (arr, iterator, callback) {\n        callback = callback || function () {};\n        if (!arr.length) {\n            return callback();\n        }\n        var completed = 0;\n        var iterate = function () {\n            iterator(arr[completed], function (err) {\n                if (err) {\n                    callback(err);\n                    callback = function () {};\n                }\n                else {\n                    completed += 1;\n                    if (completed >= arr.length) {\n                        callback();\n                    }\n                    else {\n                        iterate();\n                    }\n                }\n            });\n        };\n        iterate();\n    };\n    async.forEachSeries = async.eachSeries;\n\n    async.eachLimit = function (arr, limit, iterator, callback) {\n        var fn = _eachLimit(limit);\n        fn.apply(null, [arr, iterator, callback]);\n    };\n    async.forEachLimit = async.eachLimit;\n\n    var _eachLimit = function (limit) {\n\n        return function (arr, iterator, callback) {\n            callback = callback || function () {};\n            if (!arr.length || limit <= 0) {\n                return callback();\n            }\n            var completed = 0;\n            var started = 0;\n            var running = 0;\n\n            (function replenish () {\n                if (completed >= arr.length) {\n                    return callback();\n                }\n\n                while (running < limit && started < arr.length) {\n                    started += 1;\n                    running += 1;\n                    iterator(arr[started - 1], function (err) {\n                        if (err) {\n                            callback(err);\n                            callback = function () {};\n                        }\n                        else {\n                            completed += 1;\n                            running -= 1;\n                            if (completed >= arr.length) {\n                                callback();\n                            }\n                            else {\n                                replenish();\n                            }\n                        }\n                    });\n                }\n            })();\n        };\n    };\n\n\n    var doParallel = function (fn) {\n        return function () {\n            var args = Array.prototype.slice.call(arguments);\n            return fn.apply(null, [async.each].concat(args));\n        };\n    };\n    var doParallelLimit = function(limit, fn) {\n        return function () {\n            var args = Array.prototype.slice.call(arguments);\n            return fn.apply(null, [_eachLimit(limit)].concat(args));\n        };\n    };\n    var doSeries = function (fn) {\n        return function () {\n            var args = Array.prototype.slice.call(arguments);\n            return fn.apply(null, [async.eachSeries].concat(args));\n        };\n    };\n\n\n    var _asyncMap = function (eachfn, arr, iterator, callback) {\n        arr = _map(arr, function (x, i) {\n            return {index: i, value: x};\n        });\n        if (!callback) {\n            eachfn(arr, function (x, callback) {\n                iterator(x.value, function (err) {\n                    callback(err);\n                });\n            });\n        } else {\n            var results = [];\n            eachfn(arr, function (x, callback) {\n                iterator(x.value, function (err, v) {\n                    results[x.index] = v;\n                    callback(err);\n                });\n            }, function (err) {\n                callback(err, results);\n            });\n        }\n    };\n    async.map = doParallel(_asyncMap);\n    async.mapSeries = doSeries(_asyncMap);\n    async.mapLimit = function (arr, limit, iterator, callback) {\n        return _mapLimit(limit)(arr, iterator, callback);\n    };\n\n    var _mapLimit = function(limit) {\n        return doParallelLimit(limit, _asyncMap);\n    };\n\n    // reduce only has a series version, as doing reduce in parallel won't\n    // work in many situations.\n    async.reduce = function (arr, memo, iterator, callback) {\n        async.eachSeries(arr, function (x, callback) {\n            iterator(memo, x, function (err, v) {\n                memo = v;\n                callback(err);\n            });\n        }, function (err) {\n            callback(err, memo);\n        });\n    };\n    // inject alias\n    async.inject = async.reduce;\n    // foldl alias\n    async.foldl = async.reduce;\n\n    async.reduceRight = function (arr, memo, iterator, callback) {\n        var reversed = _map(arr, function (x) {\n            return x;\n        }).reverse();\n        async.reduce(reversed, memo, iterator, callback);\n    };\n    // foldr alias\n    async.foldr = async.reduceRight;\n\n    var _filter = function (eachfn, arr, iterator, callback) {\n        var results = [];\n        arr = _map(arr, function (x, i) {\n            return {index: i, value: x};\n        });\n        eachfn(arr, function (x, callback) {\n            iterator(x.value, function (v) {\n                if (v) {\n                    results.push(x);\n                }\n                callback();\n            });\n        }, function (err) {\n            callback(_map(results.sort(function (a, b) {\n                return a.index - b.index;\n            }), function (x) {\n                return x.value;\n            }));\n        });\n    };\n    async.filter = doParallel(_filter);\n    async.filterSeries = doSeries(_filter);\n    // select alias\n    async.select = async.filter;\n    async.selectSeries = async.filterSeries;\n\n    var _reject = function (eachfn, arr, iterator, callback) {\n        var results = [];\n        arr = _map(arr, function (x, i) {\n            return {index: i, value: x};\n        });\n        eachfn(arr, function (x, callback) {\n            iterator(x.value, function (v) {\n                if (!v) {\n                    results.push(x);\n                }\n                callback();\n            });\n        }, function (err) {\n            callback(_map(results.sort(function (a, b) {\n                return a.index - b.index;\n            }), function (x) {\n                return x.value;\n            }));\n        });\n    };\n    async.reject = doParallel(_reject);\n    async.rejectSeries = doSeries(_reject);\n\n    var _detect = function (eachfn, arr, iterator, main_callback) {\n        eachfn(arr, function (x, callback) {\n            iterator(x, function (result) {\n                if (result) {\n                    main_callback(x);\n                    main_callback = function () {};\n                }\n                else {\n                    callback();\n                }\n            });\n        }, function (err) {\n            main_callback();\n        });\n    };\n    async.detect = doParallel(_detect);\n    async.detectSeries = doSeries(_detect);\n\n    async.some = function (arr, iterator, main_callback) {\n        async.each(arr, function (x, callback) {\n            iterator(x, function (v) {\n                if (v) {\n                    main_callback(true);\n                    main_callback = function () {};\n                }\n                callback();\n            });\n        }, function (err) {\n            main_callback(false);\n        });\n    };\n    // any alias\n    async.any = async.some;\n\n    async.every = function (arr, iterator, main_callback) {\n        async.each(arr, function (x, callback) {\n            iterator(x, function (v) {\n                if (!v) {\n                    main_callback(false);\n                    main_callback = function () {};\n                }\n                callback();\n            });\n        }, function (err) {\n            main_callback(true);\n        });\n    };\n    // all alias\n    async.all = async.every;\n\n    async.sortBy = function (arr, iterator, callback) {\n        async.map(arr, function (x, callback) {\n            iterator(x, function (err, criteria) {\n                if (err) {\n                    callback(err);\n                }\n                else {\n                    callback(null, {value: x, criteria: criteria});\n                }\n            });\n        }, function (err, results) {\n            if (err) {\n                return callback(err);\n            }\n            else {\n                var fn = function (left, right) {\n                    var a = left.criteria, b = right.criteria;\n                    return a < b ? -1 : a > b ? 1 : 0;\n                };\n                callback(null, _map(results.sort(fn), function (x) {\n                    return x.value;\n                }));\n            }\n        });\n    };\n\n    async.auto = function (tasks, callback) {\n        callback = callback || function () {};\n        var keys = _keys(tasks);\n        var remainingTasks = keys.length\n        if (!remainingTasks) {\n            return callback();\n        }\n\n        var results = {};\n\n        var listeners = [];\n        var addListener = function (fn) {\n            listeners.unshift(fn);\n        };\n        var removeListener = function (fn) {\n            for (var i = 0; i < listeners.length; i += 1) {\n                if (listeners[i] === fn) {\n                    listeners.splice(i, 1);\n                    return;\n                }\n            }\n        };\n        var taskComplete = function () {\n            remainingTasks--\n            _each(listeners.slice(0), function (fn) {\n                fn();\n            });\n        };\n\n        addListener(function () {\n            if (!remainingTasks) {\n                var theCallback = callback;\n                // prevent final callback from calling itself if it errors\n                callback = function () {};\n\n                theCallback(null, results);\n            }\n        });\n\n        _each(keys, function (k) {\n            var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];\n            var taskCallback = function (err) {\n                var args = Array.prototype.slice.call(arguments, 1);\n                if (args.length <= 1) {\n                    args = args[0];\n                }\n                if (err) {\n                    var safeResults = {};\n                    _each(_keys(results), function(rkey) {\n                        safeResults[rkey] = results[rkey];\n                    });\n                    safeResults[k] = args;\n                    callback(err, safeResults);\n                    // stop subsequent errors hitting callback multiple times\n                    callback = function () {};\n                }\n                else {\n                    results[k] = args;\n                    async.setImmediate(taskComplete);\n                }\n            };\n            var requires = task.slice(0, Math.abs(task.length - 1)) || [];\n            var ready = function () {\n                return _reduce(requires, function (a, x) {\n                    return (a && results.hasOwnProperty(x));\n                }, true) && !results.hasOwnProperty(k);\n            };\n            if (ready()) {\n                task[task.length - 1](taskCallback, results);\n            }\n            else {\n                var listener = function () {\n                    if (ready()) {\n                        removeListener(listener);\n                        task[task.length - 1](taskCallback, results);\n                    }\n                };\n                addListener(listener);\n            }\n        });\n    };\n\n    async.retry = function(times, task, callback) {\n        var DEFAULT_TIMES = 5;\n        var attempts = [];\n        // Use defaults if times not passed\n        if (typeof times === 'function') {\n            callback = task;\n            task = times;\n            times = DEFAULT_TIMES;\n        }\n        // Make sure times is a number\n        times = parseInt(times, 10) || DEFAULT_TIMES;\n        var wrappedTask = function(wrappedCallback, wrappedResults) {\n            var retryAttempt = function(task, finalAttempt) {\n                return function(seriesCallback) {\n                    task(function(err, result){\n                        seriesCallback(!err || finalAttempt, {err: err, result: result});\n                    }, wrappedResults);\n                };\n            };\n            while (times) {\n                attempts.push(retryAttempt(task, !(times-=1)));\n            }\n            async.series(attempts, function(done, data){\n                data = data[data.length - 1];\n                (wrappedCallback || callback)(data.err, data.result);\n            });\n        }\n        // If a callback is passed, run this as a controll flow\n        return callback ? wrappedTask() : wrappedTask\n    };\n\n    async.waterfall = function (tasks, callback) {\n        callback = callback || function () {};\n        if (!_isArray(tasks)) {\n          var err = new Error('First argument to waterfall must be an array of functions');\n          return callback(err);\n        }\n        if (!tasks.length) {\n            return callback();\n        }\n        var wrapIterator = function (iterator) {\n            return function (err) {\n                if (err) {\n                    callback.apply(null, arguments);\n                    callback = function () {};\n                }\n                else {\n                    var args = Array.prototype.slice.call(arguments, 1);\n                    var next = iterator.next();\n                    if (next) {\n                        args.push(wrapIterator(next));\n                    }\n                    else {\n                        args.push(callback);\n                    }\n                    async.setImmediate(function () {\n                        iterator.apply(null, args);\n                    });\n                }\n            };\n        };\n        wrapIterator(async.iterator(tasks))();\n    };\n\n    var _parallel = function(eachfn, tasks, callback) {\n        callback = callback || function () {};\n        if (_isArray(tasks)) {\n            eachfn.map(tasks, function (fn, callback) {\n                if (fn) {\n                    fn(function (err) {\n                        var args = Array.prototype.slice.call(arguments, 1);\n                        if (args.length <= 1) {\n                            args = args[0];\n                        }\n                        callback.call(null, err, args);\n                    });\n                }\n            }, callback);\n        }\n        else {\n            var results = {};\n            eachfn.each(_keys(tasks), function (k, callback) {\n                tasks[k](function (err) {\n                    var args = Array.prototype.slice.call(arguments, 1);\n                    if (args.length <= 1) {\n                        args = args[0];\n                    }\n                    results[k] = args;\n                    callback(err);\n                });\n            }, function (err) {\n                callback(err, results);\n            });\n        }\n    };\n\n    async.parallel = function (tasks, callback) {\n        _parallel({ map: async.map, each: async.each }, tasks, callback);\n    };\n\n    async.parallelLimit = function(tasks, limit, callback) {\n        _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);\n    };\n\n    async.series = function (tasks, callback) {\n        callback = callback || function () {};\n        if (_isArray(tasks)) {\n            async.mapSeries(tasks, function (fn, callback) {\n                if (fn) {\n                    fn(function (err) {\n                        var args = Array.prototype.slice.call(arguments, 1);\n                        if (args.length <= 1) {\n                            args = args[0];\n                        }\n                        callback.call(null, err, args);\n                    });\n                }\n            }, callback);\n        }\n        else {\n            var results = {};\n            async.eachSeries(_keys(tasks), function (k, callback) {\n                tasks[k](function (err) {\n                    var args = Array.prototype.slice.call(arguments, 1);\n                    if (args.length <= 1) {\n                        args = args[0];\n                    }\n                    results[k] = args;\n                    callback(err);\n                });\n            }, function (err) {\n                callback(err, results);\n            });\n        }\n    };\n\n    async.iterator = function (tasks) {\n        var makeCallback = function (index) {\n            var fn = function () {\n                if (tasks.length) {\n                    tasks[index].apply(null, arguments);\n                }\n                return fn.next();\n            };\n            fn.next = function () {\n                return (index < tasks.length - 1) ? makeCallback(index + 1): null;\n            };\n            return fn;\n        };\n        return makeCallback(0);\n    };\n\n    async.apply = function (fn) {\n        var args = Array.prototype.slice.call(arguments, 1);\n        return function () {\n            return fn.apply(\n                null, args.concat(Array.prototype.slice.call(arguments))\n            );\n        };\n    };\n\n    var _concat = function (eachfn, arr, fn, callback) {\n        var r = [];\n        eachfn(arr, function (x, cb) {\n            fn(x, function (err, y) {\n                r = r.concat(y || []);\n                cb(err);\n            });\n        }, function (err) {\n            callback(err, r);\n        });\n    };\n    async.concat = doParallel(_concat);\n    async.concatSeries = doSeries(_concat);\n\n    async.whilst = function (test, iterator, callback) {\n        if (test()) {\n            iterator(function (err) {\n                if (err) {\n                    return callback(err);\n                }\n                async.whilst(test, iterator, callback);\n            });\n        }\n        else {\n            callback();\n        }\n    };\n\n    async.doWhilst = function (iterator, test, callback) {\n        iterator(function (err) {\n            if (err) {\n                return callback(err);\n            }\n            var args = Array.prototype.slice.call(arguments, 1);\n            if (test.apply(null, args)) {\n                async.doWhilst(iterator, test, callback);\n            }\n            else {\n                callback();\n            }\n        });\n    };\n\n    async.until = function (test, iterator, callback) {\n        if (!test()) {\n            iterator(function (err) {\n                if (err) {\n                    return callback(err);\n                }\n                async.until(test, iterator, callback);\n            });\n        }\n        else {\n            callback();\n        }\n    };\n\n    async.doUntil = function (iterator, test, callback) {\n        iterator(function (err) {\n            if (err) {\n                return callback(err);\n            }\n            var args = Array.prototype.slice.call(arguments, 1);\n            if (!test.apply(null, args)) {\n                async.doUntil(iterator, test, callback);\n            }\n            else {\n                callback();\n            }\n        });\n    };\n\n    async.queue = function (worker, concurrency) {\n        if (concurrency === undefined) {\n            concurrency = 1;\n        }\n        function _insert(q, data, pos, callback) {\n          if (!q.started){\n            q.started = true;\n          }\n          if (!_isArray(data)) {\n              data = [data];\n          }\n          if(data.length == 0) {\n             // call drain immediately if there are no tasks\n             return async.setImmediate(function() {\n                 if (q.drain) {\n                     q.drain();\n                 }\n             });\n          }\n          _each(data, function(task) {\n              var item = {\n                  data: task,\n                  callback: typeof callback === 'function' ? callback : null\n              };\n\n              if (pos) {\n                q.tasks.unshift(item);\n              } else {\n                q.tasks.push(item);\n              }\n\n              if (q.saturated && q.tasks.length === q.concurrency) {\n                  q.saturated();\n              }\n              async.setImmediate(q.process);\n          });\n        }\n\n        var workers = 0;\n        var q = {\n            tasks: [],\n            concurrency: concurrency,\n            saturated: null,\n            empty: null,\n            drain: null,\n            started: false,\n            paused: false,\n            push: function (data, callback) {\n              _insert(q, data, false, callback);\n            },\n            kill: function () {\n              q.drain = null;\n              q.tasks = [];\n            },\n            unshift: function (data, callback) {\n              _insert(q, data, true, callback);\n            },\n            process: function () {\n                if (!q.paused && workers < q.concurrency && q.tasks.length) {\n                    var task = q.tasks.shift();\n                    if (q.empty && q.tasks.length === 0) {\n                        q.empty();\n                    }\n                    workers += 1;\n                    var next = function () {\n                        workers -= 1;\n                        if (task.callback) {\n                            task.callback.apply(task, arguments);\n                        }\n                        if (q.drain && q.tasks.length + workers === 0) {\n                            q.drain();\n                        }\n                        q.process();\n                    };\n                    var cb = only_once(next);\n                    worker(task.data, cb);\n                }\n            },\n            length: function () {\n                return q.tasks.length;\n            },\n            running: function () {\n                return workers;\n            },\n            idle: function() {\n                return q.tasks.length + workers === 0;\n            },\n            pause: function () {\n                if (q.paused === true) { return; }\n                q.paused = true;\n                q.process();\n            },\n            resume: function () {\n                if (q.paused === false) { return; }\n                q.paused = false;\n                q.process();\n            }\n        };\n        return q;\n    };\n    \n    async.priorityQueue = function (worker, concurrency) {\n        \n        function _compareTasks(a, b){\n          return a.priority - b.priority;\n        };\n        \n        function _binarySearch(sequence, item, compare) {\n          var beg = -1,\n              end = sequence.length - 1;\n          while (beg < end) {\n            var mid = beg + ((end - beg + 1) >>> 1);\n            if (compare(item, sequence[mid]) >= 0) {\n              beg = mid;\n            } else {\n              end = mid - 1;\n            }\n          }\n          return beg;\n        }\n        \n        function _insert(q, data, priority, callback) {\n          if (!q.started){\n            q.started = true;\n          }\n          if (!_isArray(data)) {\n              data = [data];\n          }\n          if(data.length == 0) {\n             // call drain immediately if there are no tasks\n             return async.setImmediate(function() {\n                 if (q.drain) {\n                     q.drain();\n                 }\n             });\n          }\n          _each(data, function(task) {\n              var item = {\n                  data: task,\n                  priority: priority,\n                  callback: typeof callback === 'function' ? callback : null\n              };\n              \n              q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);\n\n              if (q.saturated && q.tasks.length === q.concurrency) {\n                  q.saturated();\n              }\n              async.setImmediate(q.process);\n          });\n        }\n        \n        // Start with a normal queue\n        var q = async.queue(worker, concurrency);\n        \n        // Override push to accept second parameter representing priority\n        q.push = function (data, priority, callback) {\n          _insert(q, data, priority, callback);\n        };\n        \n        // Remove unshift function\n        delete q.unshift;\n\n        return q;\n    };\n\n    async.cargo = function (worker, payload) {\n        var working     = false,\n            tasks       = [];\n\n        var cargo = {\n            tasks: tasks,\n            payload: payload,\n            saturated: null,\n            empty: null,\n            drain: null,\n            drained: true,\n            push: function (data, callback) {\n                if (!_isArray(data)) {\n                    data = [data];\n                }\n                _each(data, function(task) {\n                    tasks.push({\n                        data: task,\n                        callback: typeof callback === 'function' ? callback : null\n                    });\n                    cargo.drained = false;\n                    if (cargo.saturated && tasks.length === payload) {\n                        cargo.saturated();\n                    }\n                });\n                async.setImmediate(cargo.process);\n            },\n            process: function process() {\n                if (working) return;\n                if (tasks.length === 0) {\n                    if(cargo.drain && !cargo.drained) cargo.drain();\n                    cargo.drained = true;\n                    return;\n                }\n\n                var ts = typeof payload === 'number'\n                            ? tasks.splice(0, payload)\n                            : tasks.splice(0, tasks.length);\n\n                var ds = _map(ts, function (task) {\n                    return task.data;\n                });\n\n                if(cargo.empty) cargo.empty();\n                working = true;\n                worker(ds, function () {\n                    working = false;\n\n                    var args = arguments;\n                    _each(ts, function (data) {\n                        if (data.callback) {\n                            data.callback.apply(null, args);\n                        }\n                    });\n\n                    process();\n                });\n            },\n            length: function () {\n                return tasks.length;\n            },\n            running: function () {\n                return working;\n            }\n        };\n        return cargo;\n    };\n\n    var _console_fn = function (name) {\n        return function (fn) {\n            var args = Array.prototype.slice.call(arguments, 1);\n            fn.apply(null, args.concat([function (err) {\n                var args = Array.prototype.slice.call(arguments, 1);\n                if (typeof console !== 'undefined') {\n                    if (err) {\n                        if (console.error) {\n                            console.error(err);\n                        }\n                    }\n                    else if (console[name]) {\n                        _each(args, function (x) {\n                            console[name](x);\n                        });\n                    }\n                }\n            }]));\n        };\n    };\n    async.log = _console_fn('log');\n    async.dir = _console_fn('dir');\n    /*async.info = _console_fn('info');\n    async.warn = _console_fn('warn');\n    async.error = _console_fn('error');*/\n\n    async.memoize = function (fn, hasher) {\n        var memo = {};\n        var queues = {};\n        hasher = hasher || function (x) {\n            return x;\n        };\n        var memoized = function () {\n            var args = Array.prototype.slice.call(arguments);\n            var callback = args.pop();\n            var key = hasher.apply(null, args);\n            if (key in memo) {\n                async.nextTick(function () {\n                    callback.apply(null, memo[key]);\n                });\n            }\n            else if (key in queues) {\n                queues[key].push(callback);\n            }\n            else {\n                queues[key] = [callback];\n                fn.apply(null, args.concat([function () {\n                    memo[key] = arguments;\n                    var q = queues[key];\n                    delete queues[key];\n                    for (var i = 0, l = q.length; i < l; i++) {\n                      q[i].apply(null, arguments);\n                    }\n                }]));\n            }\n        };\n        memoized.memo = memo;\n        memoized.unmemoized = fn;\n        return memoized;\n    };\n\n    async.unmemoize = function (fn) {\n      return function () {\n        return (fn.unmemoized || fn).apply(null, arguments);\n      };\n    };\n\n    async.times = function (count, iterator, callback) {\n        var counter = [];\n        for (var i = 0; i < count; i++) {\n            counter.push(i);\n        }\n        return async.map(counter, iterator, callback);\n    };\n\n    async.timesSeries = function (count, iterator, callback) {\n        var counter = [];\n        for (var i = 0; i < count; i++) {\n            counter.push(i);\n        }\n        return async.mapSeries(counter, iterator, callback);\n    };\n\n    async.seq = function (/* functions... */) {\n        var fns = arguments;\n        return function () {\n            var that = this;\n            var args = Array.prototype.slice.call(arguments);\n            var callback = args.pop();\n            async.reduce(fns, args, function (newargs, fn, cb) {\n                fn.apply(that, newargs.concat([function () {\n                    var err = arguments[0];\n                    var nextargs = Array.prototype.slice.call(arguments, 1);\n                    cb(err, nextargs);\n                }]))\n            },\n            function (err, results) {\n                callback.apply(that, [err].concat(results));\n            });\n        };\n    };\n\n    async.compose = function (/* functions... */) {\n      return async.seq.apply(null, Array.prototype.reverse.call(arguments));\n    };\n\n    var _applyEach = function (eachfn, fns /*args...*/) {\n        var go = function () {\n            var that = this;\n            var args = Array.prototype.slice.call(arguments);\n            var callback = args.pop();\n            return eachfn(fns, function (fn, cb) {\n                fn.apply(that, args.concat([cb]));\n            },\n            callback);\n        };\n        if (arguments.length > 2) {\n            var args = Array.prototype.slice.call(arguments, 2);\n            return go.apply(this, args);\n        }\n        else {\n            return go;\n        }\n    };\n    async.applyEach = doParallel(_applyEach);\n    async.applyEachSeries = doSeries(_applyEach);\n\n    async.forever = function (fn, callback) {\n        function next(err) {\n            if (err) {\n                if (callback) {\n                    return callback(err);\n                }\n                throw err;\n            }\n            fn(next);\n        }\n        next();\n    };\n\n    // Node.js\n    if (typeof module !== 'undefined' && module.exports) {\n        module.exports = async;\n    }\n    // AMD / RequireJS\n    else if (typeof define !== 'undefined' && define.amd) {\n        define([], function () {\n            return async;\n        });\n    }\n    // included directly via <script> tag\n    else {\n        root.async = async;\n    }\n\n}());\n","// For legacy purposes, preserve the UMD of the public API of the Jitsi Meet\n// library (a.k.a. JitsiMeetJS).\nmodule.exports = require('./JitsiMeetJS').default;\n","/**\n * A model for keeping track of each user's total\n * time as a dominant speaker. The model also\n * keeps track of the user's last known name\n * in case the user has left the meeting,\n * which is also tracked.\n */\nclass SpeakerStats {\n    /**\n     * Initializes a new SpeakerStats instance.\n     *\n     * @constructor\n     * @param {string} userId - The id of the user being tracked.\n     * @param {string} displayName - The name of the user being tracked.\n     * @param {boolean} isLocalStats - True if the stats model tracks\n     * the local user.\n     * @returns {void}\n     */\n    constructor(userId, displayName, isLocalStats) {\n        this._userId = userId;\n        this.setDisplayName(displayName);\n        this._isLocalStats = isLocalStats || false;\n        this.setDominantSpeaker(false);\n        this.totalDominantSpeakerTime = 0;\n        this._dominantSpeakerStart = 0;\n        this._hasLeft = false;\n        this._facialExpressions = {\n            happy: 0,\n            neutral: 0,\n            surprised: 0,\n            angry: 0,\n            fearful: 0,\n            disgusted: 0,\n            sad: 0\n        };\n    }\n\n    /**\n     * Get the user id being tracked.\n     *\n     * @returns {string} The user id.\n     */\n    getUserId() {\n        return this._userId;\n    }\n\n    /**\n     * Get the name of the user being tracked.\n     *\n     * @returns {string} The user name.\n     */\n    getDisplayName() {\n        return this.displayName;\n    }\n\n    /**\n     * Updates the last known name of the user being tracked.\n     *\n     * @param {string} - The user name.\n     * @returns {void}\n     */\n    setDisplayName(newName) {\n        this.displayName = newName;\n    }\n\n    /**\n     * Returns true if the stats are tracking the local user.\n     *\n     * @returns {boolean}\n     */\n    isLocalStats() {\n        return this._isLocalStats;\n    }\n\n    /**\n     * Returns true if the tracked user is currently a dominant speaker.\n     *\n     * @returns {boolean}\n     */\n    isDominantSpeaker() {\n        return this._dominantSpeakerStart > 0;\n    }\n\n    /**\n     * Returns true if the tracked user is currently a dominant speaker.\n     *\n     * @param {boolean} - If true, the user will being accumulating time\n     * as dominant speaker. If false, the user will not accumulate time\n     * and will record any time accumulated since starting as dominant speaker.\n     * @returns {void}\n     */\n    setDominantSpeaker(isNowDominantSpeaker) {\n        if (!this.isDominantSpeaker() && isNowDominantSpeaker) {\n            this._dominantSpeakerStart = Date.now();\n        } else if (this.isDominantSpeaker() && !isNowDominantSpeaker) {\n            const now = Date.now();\n            const timeElapsed = now - this._dominantSpeakerStart;\n\n            this.totalDominantSpeakerTime += timeElapsed;\n            this._dominantSpeakerStart = 0;\n        }\n    }\n\n    /**\n     * Get how long the tracked user has been dominant speaker.\n     *\n     * @returns {number} - The speaker time in milliseconds.\n     */\n    getTotalDominantSpeakerTime() {\n        let total = this.totalDominantSpeakerTime;\n\n        if (this.isDominantSpeaker()) {\n            total += Date.now() - this._dominantSpeakerStart;\n        }\n\n        return total;\n    }\n\n    /**\n     * Get whether or not the user is still in the meeting.\n     *\n     * @returns {boolean} True if the user is no longer in the meeting.\n     */\n    hasLeft() {\n        return this._hasLeft;\n    }\n\n    /**\n     * Set the user as having left the meeting.\n     *\n     * @returns {void}\n     */\n    markAsHasLeft() {\n        this._hasLeft = true;\n        this.setDominantSpeaker(false);\n    }\n\n    /**\n     * Gets the facial expressions of the user.\n     *\n     * @returns {Object}\n     */\n    getFacialExpressions() {\n        return this._facialExpressions;\n    }\n\n    /**\n     * Sets the facial expressions of the user.\n     *\n     * @param {Object} facialExpressions - object with facial expressions.\n     * @returns {void}\n     */\n    setFacialExpressions(facialExpressions) {\n        this._facialExpressions = facialExpressions;\n    }\n\n    /**\n     * Adds a new facial expression to speaker stats.\n     *\n     * @param  {string} facialExpression\n     * @param {number} duration\n     */\n    addFacialExpression(facialExpression, duration) {\n        this._facialExpressions[facialExpression] += duration;\n    }\n}\n\nmodule.exports = SpeakerStats;\n","const AuthUtil = {\n    /**\n     * Creates the URL pointing to JWT token authentication service. It is\n     * formatted from the 'urlPattern' argument which can contain the following\n     * constants:\n     * '{room}' - name of the conference room passed as <tt>roomName</tt>\n     * argument to this method.\n     * '{roleUpgrade}' - will contain 'true' if the URL will be used for\n     * the role upgrade scenario, where user connects from anonymous domain and\n     * then gets upgraded to the moderator by logging-in from the popup window.\n     *\n     * @param urlPattern a URL pattern pointing to the login service\n     * @param roomName the name of the conference room for which the user will\n     * be authenticated\n     * @param {bool} roleUpgrade <tt>true</tt> if the URL will be used for role\n     * upgrade scenario, where the user logs-in from the popup window in order\n     * to have the moderator rights granted\n     *\n     * @returns {string|null} the URL pointing to JWT login service or\n     * <tt>null</tt> if 'urlPattern' is not a string and the URL can not be\n     * constructed.\n     */\n    getTokenAuthUrl(urlPattern, roomName, roleUpgrade) {\n        const url = urlPattern;\n\n        if (typeof url !== 'string') {\n            return null;\n        }\n\n        return url.replace('{room}', roomName)\n            .replace('{roleUpgrade}', roleUpgrade === true);\n    }\n};\n\nmodule.exports = AuthUtil;\n","/**\n * Implements utility to forward events from one eventEmitter to another.\n * @param src {object} instance of EventEmitter or another class that implements\n * addListener method which will register listener to EventEmitter instance.\n * @param dest {object} instance of EventEmitter or another class that\n * implements emit method which will emit an event.\n */\nfunction EventEmitterForwarder(src, dest) {\n    if (!src || !dest || typeof src.addListener !== 'function'\n        || typeof dest.emit !== 'function') {\n        throw new Error('Invalid arguments passed to EventEmitterForwarder');\n    }\n    this.src = src;\n    this.dest = dest;\n}\n\n/**\n * Adds event to be forwarded from src to dest.\n * @param srcEvent {string} the event that EventEmitterForwarder is listening\n * for.\n * @param dstEvent {string} the event that will be fired from dest.\n * @param arguments all other passed arguments are going to be fired with\n * dstEvent.\n */\nEventEmitterForwarder.prototype.forward = function(...args) {\n    const srcEvent = args[0];\n\n    // This will be the \"this\" value for emit function.\n\n    args[0] = this.dest;\n\n    // Using bind.apply to pass the arguments as Array-like object (\"arguments\")\n    this.src.addListener(\n        srcEvent,\n        Function.prototype.bind.apply(this.dest.emit, args));\n};\n\nmodule.exports = EventEmitterForwarder;\n","/**\n * This utility class defines custom onerror and onunhandledrejection functions.\n * The custom error handlers respect the previously-defined error handlers.\n * GlobalOnErrorHandler class provides utilities to add many custom error\n * handlers and to execute the error handlers directly.\n */\n\n\n/**\n * List with global error handlers that will be executed.\n */\nconst handlers = [];\n\n// If an old handler exists, also fire its events.\nconst oldOnErrorHandler = window.onerror;\n\n/**\n * Custom error handler that calls the old global error handler and executes\n * all handlers that were previously added.\n */\nfunction JitsiGlobalErrorHandler(...args) {\n    handlers.forEach(handler => handler(...args));\n    oldOnErrorHandler && oldOnErrorHandler(...args);\n}\n\n// If an old handler exists, also fire its events.\nconst oldOnUnhandledRejection = window.onunhandledrejection;\n\n/**\n * Custom handler that calls the old global handler and executes all handlers\n * that were previously added. This handler handles rejected Promises.\n */\nfunction JitsiGlobalUnhandledRejection(event) {\n    handlers.forEach(handler => handler(null, null, null, null, event.reason));\n    oldOnUnhandledRejection && oldOnUnhandledRejection(event);\n}\n\n// Setting the custom error handlers.\nwindow.onerror = JitsiGlobalErrorHandler;\nwindow.onunhandledrejection = JitsiGlobalUnhandledRejection;\n\nconst GlobalOnErrorHandler = {\n    /**\n     * Adds new error handlers.\n     * @param handler the new handler.\n     */\n    addHandler(handler) {\n        handlers.push(handler);\n    },\n\n    /**\n     * Calls the global error handler if there is one.\n     * @param error the error to pass to the error handler\n     */\n    callErrorHandler(error) {\n        const errHandler = window.onerror;\n\n        if (!errHandler) {\n            return;\n        }\n        errHandler(null, null, null, null, error);\n    },\n\n    /**\n     * Calls the global rejection handler if there is one.\n     * @param error the error to pass to the rejection handler.\n     */\n    callUnhandledRejectionHandler(error) {\n        const errHandler = window.onunhandledrejection;\n\n        if (!errHandler) {\n            return;\n        }\n        errHandler(error);\n    }\n};\n\n\nmodule.exports = GlobalOnErrorHandler;\n","/**\n * @const\n */\nconst ALPHANUM\n    = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\n\n/**\n * Hexadecimal digits.\n * @const\n */\nconst HEX_DIGITS = '0123456789abcdef';\n\n/**\n * Generates random int within the range [min, max]\n * @param min the minimum value for the generated number\n * @param max the maximum value for the generated number\n * @returns random int number\n */\nfunction randomInt(min, max) {\n    return Math.floor(Math.random() * (max - min + 1)) + min;\n}\n\n/**\n * Get random element from array or string.\n * @param {Array|string} arr source\n * @returns array element or string character\n */\nfunction randomElement(arr) {\n    return arr[randomInt(0, arr.length - 1)];\n}\n\n/**\n * Generate random alphanumeric string.\n * @param {number} length expected string length\n * @returns {string} random string of specified length\n */\nfunction randomAlphanumStr(length) {\n    let result = '';\n\n    for (let i = 0; i < length; i += 1) {\n        result += randomElement(ALPHANUM);\n    }\n\n    return result;\n}\n\n/**\n * Exported interface.\n */\nconst RandomUtil = {\n    /**\n     * Returns a random hex digit.\n     * @returns {*}\n     */\n    randomHexDigit() {\n        return randomElement(HEX_DIGITS);\n    },\n\n    /**\n     * Returns a random string of hex digits with length 'len'.\n     * @param len the length.\n     */\n    randomHexString(len) {\n        let ret = '';\n\n        while (len--) { // eslint-disable-line no-param-reassign\n            ret += this.randomHexDigit();\n        }\n\n        return ret;\n    },\n    randomElement,\n    randomAlphanumStr,\n    randomInt\n};\n\nmodule.exports = RandomUtil;\n","const currentExecutingScript = require('current-executing-script');\n\n/* eslint-disable max-params */\n\n/**\n * Implements utility functions which facilitate the dealing with scripts such\n * as the download and execution of a JavaScript file.\n */\nconst ScriptUtil = {\n    /**\n     * Loads a script from a specific source.\n     *\n     * @param src the source from the which the script is to be (down)loaded\n     * @param async true to asynchronously load the script or false to\n     * synchronously load the script\n     * @param prepend true to schedule the loading of the script as soon as\n     * possible or false to schedule the loading of the script at the end of the\n     * scripts known at the time\n     * @param relativeURL whether we need load the library from url relative\n     * to the url that lib-jitsi-meet was loaded. Useful when sourcing the\n     * library from different location than the app that is using it\n     * @param loadCallback on load callback function\n     * @param errorCallback callback to be called on error loading the script\n     */\n    loadScript(\n            src,\n            async,\n            prepend,\n            relativeURL,\n            loadCallback,\n            errorCallback) {\n        const d = document;\n        const tagName = 'script';\n        const script = d.createElement(tagName);\n        const referenceNode = d.getElementsByTagName(tagName)[0];\n\n        script.async = async;\n\n        if (relativeURL) {\n            // finds the src url of the current loaded script\n            // and use it as base of the src supplied argument\n            const scriptEl = currentExecutingScript();\n\n            if (scriptEl) {\n                const scriptSrc = scriptEl.src;\n                const baseScriptSrc\n                    = scriptSrc.substring(0, scriptSrc.lastIndexOf('/') + 1);\n\n                if (scriptSrc && baseScriptSrc) {\n                    // eslint-disable-next-line no-param-reassign\n                    src = baseScriptSrc + src;\n                }\n            }\n        }\n\n        if (loadCallback) {\n            script.onload = loadCallback;\n        }\n        if (errorCallback) {\n            script.onerror = errorCallback;\n        }\n\n        script.src = src;\n        if (prepend) {\n            referenceNode.parentNode.insertBefore(script, referenceNode);\n        } else {\n            referenceNode.parentNode.appendChild(script);\n        }\n    }\n};\n\n/* eslint-enable max-params */\n\nmodule.exports = ScriptUtil;\n","/**\n * Implements a simple hash code for a string (see\n * https://en.wikipedia.org/wiki/Java_hashCode()).\n *\n * @param {string} The string to return a hash of.\n * @return {Number} the integer hash code of the string.\n */\nfunction integerHash(string) {\n    if (!string) {\n        return 0;\n    }\n\n    let char, hash = 0, i;\n\n    for (i = 0; i < string.length; i++) {\n        char = string.charCodeAt(i);\n        hash += char * Math.pow(31, string.length - 1 - i);\n        hash = Math.abs(hash | 0); // eslint-disable-line no-bitwise\n    }\n\n    return hash;\n}\n\nmodule.exports = { integerHash };\n","const RandomUtil = require('./RandomUtil');\n\n/**\n * from faker.js - Copyright (c) 2014-2015 Matthew Bergman & Marak Squires\n * MIT License\n * http://github.com/marak/faker.js/\n *\n * @const\n */\nconst names = [\n    'Aaliyah', 'Aaron', 'Abagail', 'Abbey', 'Abbie', 'Abbigail', 'Abby',\n    'Abdiel', 'Abdul', 'Abdullah', 'Abe', 'Abel', 'Abelardo', 'Abigail',\n    'Abigale', 'Abigayle', 'Abner', 'Abraham', 'Ada', 'Adah', 'Adalberto',\n    'Adaline', 'Adam', 'Adan', 'Addie', 'Addison', 'Adela', 'Adelbert', 'Adele',\n    'Adelia', 'Adeline', 'Adell', 'Adella', 'Adelle', 'Aditya', 'Adolf',\n    'Adolfo', 'Adolph', 'Adolphus', 'Adonis', 'Adrain', 'Adrian', 'Adriana',\n    'Adrianna', 'Adriel', 'Adrien', 'Adrienne', 'Afton', 'Aglae', 'Agnes',\n    'Agustin', 'Agustina', 'Ahmad', 'Ahmed', 'Aida', 'Aidan', 'Aiden', 'Aileen',\n    'Aisha', 'Aiyana', 'Akeem', 'Al', 'Alaina', 'Alan', 'Alana', 'Alanis',\n    'Alanna', 'Alayna', 'Alba', 'Albert', 'Alberta', 'Albertha', 'Alberto',\n    'Albin', 'Albina', 'Alda', 'Alden', 'Alec', 'Aleen', 'Alejandra',\n    'Alejandrin', 'Alek', 'Alena', 'Alene', 'Alessandra', 'Alessandro',\n    'Alessia', 'Aletha', 'Alex', 'Alexa', 'Alexander', 'Alexandra', 'Alexandre',\n    'Alexandrea', 'Alexandria', 'Alexandrine', 'Alexandro', 'Alexane',\n    'Alexanne', 'Alexie', 'Alexis', 'Alexys', 'Alexzander', 'Alf', 'Alfonso',\n    'Alfonzo', 'Alford', 'Alfred', 'Alfreda', 'Alfredo', 'Ali', 'Alia', 'Alice',\n    'Alicia', 'Alisa', 'Alisha', 'Alison', 'Alivia', 'Aliya', 'Aliyah', 'Aliza',\n    'Alize', 'Allan', 'Allen', 'Allene', 'Allie', 'Allison', 'Ally', 'Alphonso',\n    'Alta', 'Althea', 'Alva', 'Alvah', 'Alvena', 'Alvera', 'Alverta', 'Alvina',\n    'Alvis', 'Alyce', 'Alycia', 'Alysa', 'Alysha', 'Alyson', 'Alysson',\n    'Amalia', 'Amanda', 'Amani', 'Amara', 'Amari', 'Amaya', 'Amber', 'Ambrose',\n    'Amelia', 'Amelie', 'Amely', 'America', 'Americo', 'Amie', 'Amina', 'Amir',\n    'Amira', 'Amiya', 'Amos', 'Amparo', 'Amy', 'Amya', 'Ana', 'Anabel',\n    'Anabelle', 'Anahi', 'Anais', 'Anastacio', 'Anastasia', 'Anderson', 'Andre',\n    'Andreane', 'Andreanne', 'Andres', 'Andrew', 'Andy', 'Angel', 'Angela',\n    'Angelica', 'Angelina', 'Angeline', 'Angelita', 'Angelo', 'Angie', 'Angus',\n    'Anibal', 'Anika', 'Anissa', 'Anita', 'Aniya', 'Aniyah', 'Anjali', 'Anna',\n    'Annabel', 'Annabell', 'Annabelle', 'Annalise', 'Annamae', 'Annamarie',\n    'Anne', 'Annetta', 'Annette', 'Annie', 'Ansel', 'Ansley', 'Anthony',\n    'Antoinette', 'Antone', 'Antonetta', 'Antonette', 'Antonia', 'Antonietta',\n    'Antonina', 'Antonio', 'Antwan', 'Antwon', 'Anya', 'April', 'Ara',\n    'Araceli', 'Aracely', 'Arch', 'Archibald', 'Ardella', 'Arden', 'Ardith',\n    'Arely', 'Ari', 'Ariane', 'Arianna', 'Aric', 'Ariel', 'Arielle', 'Arjun',\n    'Arlene', 'Arlie', 'Arlo', 'Armand', 'Armando', 'Armani', 'Arnaldo', 'Arne',\n    'Arno', 'Arnold', 'Arnoldo', 'Arnulfo', 'Aron', 'Art', 'Arthur', 'Arturo',\n    'Arvel', 'Arvid', 'Arvilla', 'Aryanna', 'Asa', 'Asha', 'Ashlee', 'Ashleigh',\n    'Ashley', 'Ashly', 'Ashlynn', 'Ashton', 'Ashtyn', 'Asia', 'Assunta',\n    'Astrid', 'Athena', 'Aubree', 'Aubrey', 'Audie', 'Audra', 'Audreanne',\n    'Audrey', 'August', 'Augusta', 'Augustine', 'Augustus', 'Aurelia',\n    'Aurelie', 'Aurelio', 'Aurore', 'Austen', 'Austin', 'Austyn', 'Autumn',\n    'Ava', 'Avery', 'Avis', 'Axel', 'Ayana', 'Ayden', 'Ayla', 'Aylin', 'Baby',\n    'Bailee', 'Bailey', 'Barbara', 'Barney', 'Baron', 'Barrett', 'Barry',\n    'Bart', 'Bartholome', 'Barton', 'Baylee', 'Beatrice', 'Beau', 'Beaulah',\n    'Bell', 'Bella', 'Belle', 'Ben', 'Benedict', 'Benjamin', 'Bennett',\n    'Bennie', 'Benny', 'Benton', 'Berenice', 'Bernadette', 'Bernadine',\n    'Bernard', 'Bernardo', 'Berneice', 'Bernhard', 'Bernice', 'Bernie',\n    'Berniece', 'Bernita', 'Berry', 'Bert', 'Berta', 'Bertha', 'Bertram',\n    'Bertrand', 'Beryl', 'Bessie', 'Beth', 'Bethany', 'Bethel', 'Betsy',\n    'Bette', 'Bettie', 'Betty', 'Bettye', 'Beulah', 'Beverly', 'Bianka', 'Bill',\n    'Billie', 'Billy', 'Birdie', 'Blair', 'Blaise', 'Blake', 'Blanca',\n    'Blanche', 'Blaze', 'Bo', 'Bobbie', 'Bobby', 'Bonita', 'Bonnie', 'Boris',\n    'Boyd', 'Brad', 'Braden', 'Bradford', 'Bradley', 'Bradly', 'Brady',\n    'Braeden', 'Brain', 'Brandi', 'Brando', 'Brandon', 'Brandt', 'Brandy',\n    'Brandyn', 'Brannon', 'Branson', 'Brant', 'Braulio', 'Braxton', 'Brayan',\n    'Breana', 'Breanna', 'Breanne', 'Brenda', 'Brendan', 'Brenden', 'Brendon',\n    'Brenna', 'Brennan', 'Brennon', 'Brent', 'Bret', 'Brett', 'Bria', 'Brian',\n    'Briana', 'Brianne', 'Brice', 'Bridget', 'Bridgette', 'Bridie', 'Brielle',\n    'Brigitte', 'Brionna', 'Brisa', 'Britney', 'Brittany', 'Brock', 'Broderick',\n    'Brody', 'Brook', 'Brooke', 'Brooklyn', 'Brooks', 'Brown', 'Bruce',\n    'Bryana', 'Bryce', 'Brycen', 'Bryon', 'Buck', 'Bud', 'Buddy', 'Buford',\n    'Bulah', 'Burdette', 'Burley', 'Burnice', 'Buster', 'Cade', 'Caden',\n    'Caesar', 'Caitlyn', 'Cale', 'Caleb', 'Caleigh', 'Cali', 'Calista',\n    'Callie', 'Camden', 'Cameron', 'Camila', 'Camilla', 'Camille', 'Camren',\n    'Camron', 'Camryn', 'Camylle', 'Candace', 'Candelario', 'Candice',\n    'Candida', 'Candido', 'Cara', 'Carey', 'Carissa', 'Carlee', 'Carleton',\n    'Carley', 'Carli', 'Carlie', 'Carlo', 'Carlos', 'Carlotta', 'Carmel',\n    'Carmela', 'Carmella', 'Carmelo', 'Carmen', 'Carmine', 'Carol', 'Carolanne',\n    'Carole', 'Carolina', 'Caroline', 'Carolyn', 'Carolyne', 'Carrie',\n    'Carroll', 'Carson', 'Carter', 'Cary', 'Casandra', 'Casey', 'Casimer',\n    'Casimir', 'Casper', 'Cassandra', 'Cassandre', 'Cassidy', 'Cassie',\n    'Catalina', 'Caterina', 'Catharine', 'Catherine', 'Cathrine', 'Cathryn',\n    'Cathy', 'Cayla', 'Ceasar', 'Cecelia', 'Cecil', 'Cecile', 'Cecilia',\n    'Cedrick', 'Celestine', 'Celestino', 'Celia', 'Celine', 'Cesar', 'Chad',\n    'Chadd', 'Chadrick', 'Chaim', 'Chance', 'Chandler', 'Chanel', 'Chanelle',\n    'Charity', 'Charlene', 'Charles', 'Charley', 'Charlie', 'Charlotte',\n    'Chase', 'Chasity', 'Chauncey', 'Chaya', 'Chaz', 'Chelsea', 'Chelsey',\n    'Chelsie', 'Chesley', 'Chester', 'Chet', 'Cheyanne', 'Cheyenne', 'Chloe',\n    'Chris', 'Christ', 'Christa', 'Christelle', 'Christian', 'Christiana',\n    'Christina', 'Christine', 'Christop', 'Christophe', 'Christopher',\n    'Christy', 'Chyna', 'Ciara', 'Cicero', 'Cielo', 'Cierra', 'Cindy',\n    'Citlalli', 'Clair', 'Claire', 'Clara', 'Clarabelle', 'Clare', 'Clarissa',\n    'Clark', 'Claud', 'Claude', 'Claudia', 'Claudie', 'Claudine', 'Clay',\n    'Clemens', 'Clement', 'Clementina', 'Clementine', 'Clemmie', 'Cleo',\n    'Cleora', 'Cleta', 'Cletus', 'Cleve', 'Cleveland', 'Clifford', 'Clifton',\n    'Clint', 'Clinton', 'Clotilde', 'Clovis', 'Cloyd', 'Clyde', 'Coby', 'Cody',\n    'Colby', 'Cole', 'Coleman', 'Colin', 'Colleen', 'Collin', 'Colt', 'Colten',\n    'Colton', 'Columbus', 'Concepcion', 'Conner', 'Connie', 'Connor', 'Conor',\n    'Conrad', 'Constance', 'Constantin', 'Consuelo', 'Cooper', 'Cora',\n    'Coralie', 'Corbin', 'Cordelia', 'Cordell', 'Cordia', 'Cordie', 'Corene',\n    'Corine', 'Cornelius', 'Cornell', 'Corrine', 'Cortez', 'Cortney', 'Cory',\n    'Coty', 'Courtney', 'Coy', 'Craig', 'Crawford', 'Creola', 'Cristal',\n    'Cristian', 'Cristina', 'Cristobal', 'Cristopher', 'Cruz', 'Crystal',\n    'Crystel', 'Cullen', 'Curt', 'Curtis', 'Cydney', 'Cynthia', 'Cyril',\n    'Cyrus', 'Dagmar', 'Dahlia', 'Daija', 'Daisha', 'Daisy', 'Dakota', 'Dale',\n    'Dallas', 'Dallin', 'Dalton', 'Damaris', 'Dameon', 'Damian', 'Damien',\n    'Damion', 'Damon', 'Dan', 'Dana', 'Dandre', 'Dane', 'D\\'angelo', 'Dangelo',\n    'Danial', 'Daniela', 'Daniella', 'Danielle', 'Danika', 'Dannie', 'Danny',\n    'Dante', 'Danyka', 'Daphne', 'Daphnee', 'Daphney', 'Darby', 'Daren',\n    'Darian', 'Dariana', 'Darien', 'Dario', 'Darion', 'Darius', 'Darlene',\n    'Daron', 'Darrel', 'Darrell', 'Darren', 'Darrick', 'Darrin', 'Darrion',\n    'Darron', 'Darryl', 'Darwin', 'Daryl', 'Dashawn', 'Dasia', 'Dave', 'David',\n    'Davin', 'Davion', 'Davon', 'Davonte', 'Dawn', 'Dawson', 'Dax', 'Dayana',\n    'Dayna', 'Dayne', 'Dayton', 'Dean', 'Deangelo', 'Deanna', 'Deborah',\n    'Declan', 'Dedric', 'Dedrick', 'Dee', 'Deion', 'Deja', 'Dejah', 'Dejon',\n    'Dejuan', 'Delaney', 'Delbert', 'Delfina', 'Delia', 'Delilah', 'Dell',\n    'Della', 'Delmer', 'Delores', 'Delpha', 'Delphia', 'Delphine', 'Delta',\n    'Demarco', 'Demarcus', 'Demario', 'Demetris', 'Demetrius', 'Demond', 'Dena',\n    'Denis', 'Dennis', 'Deon', 'Deondre', 'Deontae', 'Deonte', 'Dereck',\n    'Derek', 'Derick', 'Deron', 'Derrick', 'Deshaun', 'Deshawn', 'Desiree',\n    'Desmond', 'Dessie', 'Destany', 'Destin', 'Destinee', 'Destiney', 'Destini',\n    'Destiny', 'Devan', 'Devante', 'Deven', 'Devin', 'Devon', 'Devonte',\n    'Devyn', 'Dewayne', 'Dewitt', 'Dexter', 'Diamond', 'Diana', 'Dianna',\n    'Diego', 'Dillan', 'Dillon', 'Dimitri', 'Dina', 'Dino', 'Dion', 'Dixie',\n    'Dock', 'Dolly', 'Dolores', 'Domenic', 'Domenica', 'Domenick', 'Domenico',\n    'Domingo', 'Dominic', 'Dominique', 'Don', 'Donald', 'Donato', 'Donavon',\n    'Donna', 'Donnell', 'Donnie', 'Donny', 'Dora', 'Dorcas', 'Dorian', 'Doris',\n    'Dorothea', 'Dorothy', 'Dorris', 'Dortha', 'Dorthy', 'Doug', 'Douglas',\n    'Dovie', 'Doyle', 'Drake', 'Drew', 'Duane', 'Dudley', 'Dulce', 'Duncan',\n    'Durward', 'Dustin', 'Dusty', 'Dwight', 'Dylan', 'Earl', 'Earlene',\n    'Earline', 'Earnest', 'Earnestine', 'Easter', 'Easton', 'Ebba', 'Ebony',\n    'Ed', 'Eda', 'Edd', 'Eddie', 'Eden', 'Edgar', 'Edgardo', 'Edison', 'Edmond',\n    'Edmund', 'Edna', 'Eduardo', 'Edward', 'Edwardo', 'Edwin', 'Edwina',\n    'Edyth', 'Edythe', 'Effie', 'Efrain', 'Efren', 'Eileen', 'Einar', 'Eino',\n    'Eladio', 'Elaina', 'Elbert', 'Elda', 'Eldon', 'Eldora', 'Eldred',\n    'Eldridge', 'Eleanora', 'Eleanore', 'Eleazar', 'Electa', 'Elena', 'Elenor',\n    'Elenora', 'Eleonore', 'Elfrieda', 'Eli', 'Elian', 'Eliane', 'Elias',\n    'Eliezer', 'Elijah', 'Elinor', 'Elinore', 'Elisa', 'Elisabeth', 'Elise',\n    'Eliseo', 'Elisha', 'Elissa', 'Eliza', 'Elizabeth', 'Ella', 'Ellen',\n    'Ellie', 'Elliot', 'Elliott', 'Ellis', 'Ellsworth', 'Elmer', 'Elmira',\n    'Elmo', 'Elmore', 'Elna', 'Elnora', 'Elody', 'Eloisa', 'Eloise', 'Elouise',\n    'Eloy', 'Elroy', 'Elsa', 'Else', 'Elsie', 'Elta', 'Elton', 'Elva', 'Elvera',\n    'Elvie', 'Elvis', 'Elwin', 'Elwyn', 'Elyse', 'Elyssa', 'Elza', 'Emanuel',\n    'Emelia', 'Emelie', 'Emely', 'Emerald', 'Emerson', 'Emery', 'Emie', 'Emil',\n    'Emile', 'Emilia', 'Emiliano', 'Emilie', 'Emilio', 'Emily', 'Emma',\n    'Emmalee', 'Emmanuel', 'Emmanuelle', 'Emmet', 'Emmett', 'Emmie', 'Emmitt',\n    'Emmy', 'Emory', 'Ena', 'Enid', 'Enoch', 'Enola', 'Enos', 'Enrico',\n    'Enrique', 'Ephraim', 'Era', 'Eriberto', 'Eric', 'Erica', 'Erich', 'Erick',\n    'Ericka', 'Erik', 'Erika', 'Erin', 'Erling', 'Erna', 'Ernest', 'Ernestina',\n    'Ernestine', 'Ernesto', 'Ernie', 'Ervin', 'Erwin', 'Eryn', 'Esmeralda',\n    'Esperanza', 'Esta', 'Esteban', 'Estefania', 'Estel', 'Estell', 'Estella',\n    'Estelle', 'Estevan', 'Esther', 'Estrella', 'Etha', 'Ethan', 'Ethel',\n    'Ethelyn', 'Ethyl', 'Ettie', 'Eudora', 'Eugene', 'Eugenia', 'Eula', 'Eulah',\n    'Eulalia', 'Euna', 'Eunice', 'Eusebio', 'Eva', 'Evalyn', 'Evan',\n    'Evangeline', 'Evans', 'Eve', 'Eveline', 'Evelyn', 'Everardo', 'Everett',\n    'Everette', 'Evert', 'Evie', 'Ewald', 'Ewell', 'Ezekiel', 'Ezequiel',\n    'Ezra', 'Fabian', 'Fabiola', 'Fae', 'Fannie', 'Fanny', 'Fatima', 'Faustino',\n    'Fausto', 'Favian', 'Fay', 'Faye', 'Federico', 'Felicia', 'Felicita',\n    'Felicity', 'Felipa', 'Felipe', 'Felix', 'Felton', 'Fermin', 'Fern',\n    'Fernando', 'Ferne', 'Fidel', 'Filiberto', 'Filomena', 'Finn', 'Fiona',\n    'Flavie', 'Flavio', 'Fleta', 'Fletcher', 'Flo', 'Florence', 'Florencio',\n    'Florian', 'Florida', 'Florine', 'Flossie', 'Floy', 'Floyd', 'Ford',\n    'Forest', 'Forrest', 'Foster', 'Frances', 'Francesca', 'Francesco',\n    'Francis', 'Francisca', 'Francisco', 'Franco', 'Frank', 'Frankie', 'Franz',\n    'Fred', 'Freda', 'Freddie', 'Freddy', 'Frederic', 'Frederick', 'Frederik',\n    'Frederique', 'Fredrick', 'Fredy', 'Freeda', 'Freeman', 'Freida', 'Frida',\n    'Frieda', 'Friedrich', 'Fritz', 'Furman', 'Gabe', 'Gabriel', 'Gabriella',\n    'Gabrielle', 'Gaetano', 'Gage', 'Gail', 'Gardner', 'Garett', 'Garfield',\n    'Garland', 'Garnet', 'Garnett', 'Garret', 'Garrett', 'Garrick', 'Garrison',\n    'Garry', 'Garth', 'Gaston', 'Gavin', 'Gay', 'Gayle', 'Gaylord', 'Gene',\n    'General', 'Genesis', 'Genevieve', 'Gennaro', 'Genoveva', 'Geo', 'Geoffrey',\n    'George', 'Georgette', 'Georgiana', 'Georgianna', 'Geovanni', 'Geovanny',\n    'Geovany', 'Gerald', 'Geraldine', 'Gerard', 'Gerardo', 'Gerda', 'Gerhard',\n    'Germaine', 'German', 'Gerry', 'Gerson', 'Gertrude', 'Gia', 'Gianni',\n    'Gideon', 'Gilbert', 'Gilberto', 'Gilda', 'Giles', 'Gillian', 'Gina',\n    'Gino', 'Giovani', 'Giovanna', 'Giovanni', 'Giovanny', 'Gisselle',\n    'Giuseppe', 'Gladyce', 'Gladys', 'Glen', 'Glenda', 'Glenna', 'Glennie',\n    'Gloria', 'Godfrey', 'Golda', 'Golden', 'Gonzalo', 'Gordon', 'Grace',\n    'Gracie', 'Graciela', 'Grady', 'Graham', 'Grant', 'Granville', 'Grayce',\n    'Grayson', 'Green', 'Greg', 'Gregg', 'Gregoria', 'Gregorio', 'Gregory',\n    'Greta', 'Gretchen', 'Greyson', 'Griffin', 'Grover', 'Guadalupe', 'Gudrun',\n    'Guido', 'Guillermo', 'Guiseppe', 'Gunnar', 'Gunner', 'Gus', 'Gussie',\n    'Gust', 'Gustave', 'Guy', 'Gwen', 'Gwendolyn', 'Hadley', 'Hailee', 'Hailey',\n    'Hailie', 'Hal', 'Haleigh', 'Haley', 'Halie', 'Halle', 'Hallie', 'Hank',\n    'Hanna', 'Hannah', 'Hans', 'Hardy', 'Harley', 'Harmon', 'Harmony', 'Harold',\n    'Harrison', 'Harry', 'Harvey', 'Haskell', 'Hassan', 'Hassie', 'Hattie',\n    'Haven', 'Hayden', 'Haylee', 'Hayley', 'Haylie', 'Hazel', 'Hazle', 'Heath',\n    'Heather', 'Heaven', 'Heber', 'Hector', 'Heidi', 'Helen', 'Helena',\n    'Helene', 'Helga', 'Hellen', 'Helmer', 'Heloise', 'Henderson', 'Henri',\n    'Henriette', 'Henry', 'Herbert', 'Herman', 'Hermann', 'Hermina', 'Herminia',\n    'Herminio', 'Hershel', 'Herta', 'Hertha', 'Hester', 'Hettie', 'Hilario',\n    'Hilbert', 'Hilda', 'Hildegard', 'Hillard', 'Hillary', 'Hilma', 'Hilton',\n    'Hipolito', 'Hiram', 'Hobart', 'Holden', 'Hollie', 'Hollis', 'Holly',\n    'Hope', 'Horace', 'Horacio', 'Hortense', 'Hosea', 'Houston', 'Howard',\n    'Howell', 'Hoyt', 'Hubert', 'Hudson', 'Hugh', 'Hulda', 'Humberto', 'Hunter',\n    'Hyman', 'Ian', 'Ibrahim', 'Icie', 'Ida', 'Idell', 'Idella', 'Ignacio',\n    'Ignatius', 'Ike', 'Ila', 'Ilene', 'Iliana', 'Ima', 'Imani', 'Imelda',\n    'Immanuel', 'Imogene', 'Ines', 'Irma', 'Irving', 'Irwin', 'Isaac', 'Isabel',\n    'Isabell', 'Isabella', 'Isabelle', 'Isac', 'Isadore', 'Isai', 'Isaiah',\n    'Isaias', 'Isidro', 'Ismael', 'Isobel', 'Isom', 'Israel', 'Issac', 'Itzel',\n    'Iva', 'Ivah', 'Ivory', 'Ivy', 'Izabella', 'Izaiah', 'Jabari', 'Jace',\n    'Jacey', 'Jacinthe', 'Jacinto', 'Jack', 'Jackeline', 'Jackie', 'Jacklyn',\n    'Jackson', 'Jacky', 'Jaclyn', 'Jacquelyn', 'Jacques', 'Jacynthe', 'Jada',\n    'Jade', 'Jaden', 'Jadon', 'Jadyn', 'Jaeden', 'Jaida', 'Jaiden', 'Jailyn',\n    'Jaime', 'Jairo', 'Jakayla', 'Jake', 'Jakob', 'Jaleel', 'Jalen', 'Jalon',\n    'Jalyn', 'Jamaal', 'Jamal', 'Jamar', 'Jamarcus', 'Jamel', 'Jameson',\n    'Jamey', 'Jamie', 'Jamil', 'Jamir', 'Jamison', 'Jammie', 'Jan', 'Jana',\n    'Janae', 'Jane', 'Janelle', 'Janessa', 'Janet', 'Janice', 'Janick', 'Janie',\n    'Janis', 'Janiya', 'Jannie', 'Jany', 'Jaquan', 'Jaquelin', 'Jaqueline',\n    'Jared', 'Jaren', 'Jarod', 'Jaron', 'Jarred', 'Jarrell', 'Jarret',\n    'Jarrett', 'Jarrod', 'Jarvis', 'Jasen', 'Jasmin', 'Jason', 'Jasper',\n    'Jaunita', 'Javier', 'Javon', 'Javonte', 'Jay', 'Jayce', 'Jaycee', 'Jayda',\n    'Jayde', 'Jayden', 'Jaydon', 'Jaylan', 'Jaylen', 'Jaylin', 'Jaylon',\n    'Jayme', 'Jayne', 'Jayson', 'Jazlyn', 'Jazmin', 'Jazmyn', 'Jazmyne', 'Jean',\n    'Jeanette', 'Jeanie', 'Jeanne', 'Jed', 'Jedediah', 'Jedidiah', 'Jeff',\n    'Jefferey', 'Jeffery', 'Jeffrey', 'Jeffry', 'Jena', 'Jenifer', 'Jennie',\n    'Jennifer', 'Jennings', 'Jennyfer', 'Jensen', 'Jerad', 'Jerald', 'Jeramie',\n    'Jeramy', 'Jerel', 'Jeremie', 'Jeremy', 'Jermain', 'Jermaine', 'Jermey',\n    'Jerod', 'Jerome', 'Jeromy', 'Jerrell', 'Jerrod', 'Jerrold', 'Jerry',\n    'Jess', 'Jesse', 'Jessica', 'Jessie', 'Jessika', 'Jessy', 'Jessyca',\n    'Jesus', 'Jett', 'Jettie', 'Jevon', 'Jewel', 'Jewell', 'Jillian', 'Jimmie',\n    'Jimmy', 'Jo', 'Joan', 'Joana', 'Joanie', 'Joanne', 'Joannie', 'Joanny',\n    'Joany', 'Joaquin', 'Jocelyn', 'Jodie', 'Jody', 'Joe', 'Joel', 'Joelle',\n    'Joesph', 'Joey', 'Johan', 'Johann', 'Johanna', 'Johathan', 'John',\n    'Johnathan', 'Johnathon', 'Johnnie', 'Johnny', 'Johnpaul', 'Johnson',\n    'Jolie', 'Jon', 'Jonas', 'Jonatan', 'Jonathan', 'Jonathon', 'Jordan',\n    'Jordane', 'Jordi', 'Jordon', 'Jordy', 'Jordyn', 'Jorge', 'Jose', 'Josefa',\n    'Josefina', 'Joseph', 'Josephine', 'Josh', 'Joshua', 'Joshuah', 'Josiah',\n    'Josiane', 'Josianne', 'Josie', 'Josue', 'Jovan', 'Jovani', 'Jovanny',\n    'Jovany', 'Joy', 'Joyce', 'Juana', 'Juanita', 'Judah', 'Judd', 'Jude',\n    'Judge', 'Judson', 'Judy', 'Jules', 'Julia', 'Julian', 'Juliana',\n    'Julianne', 'Julie', 'Julien', 'Juliet', 'Julio', 'Julius', 'June',\n    'Junior', 'Junius', 'Justen', 'Justice', 'Justina', 'Justine', 'Juston',\n    'Justus', 'Justyn', 'Juvenal', 'Juwan', 'Kacey', 'Kaci', 'Kacie', 'Kade',\n    'Kaden', 'Kadin', 'Kaela', 'Kaelyn', 'Kaia', 'Kailee', 'Kailey', 'Kailyn',\n    'Kaitlin', 'Kaitlyn', 'Kale', 'Kaleb', 'Kaleigh', 'Kaley', 'Kali', 'Kallie',\n    'Kameron', 'Kamille', 'Kamren', 'Kamron', 'Kamryn', 'Kane', 'Kara',\n    'Kareem', 'Karelle', 'Karen', 'Kari', 'Kariane', 'Karianne', 'Karina',\n    'Karine', 'Karl', 'Karlee', 'Karley', 'Karli', 'Karlie', 'Karolann',\n    'Karson', 'Kasandra', 'Kasey', 'Kassandra', 'Katarina', 'Katelin',\n    'Katelyn', 'Katelynn', 'Katharina', 'Katherine', 'Katheryn', 'Kathleen',\n    'Kathlyn', 'Kathryn', 'Kathryne', 'Katlyn', 'Katlynn', 'Katrina', 'Katrine',\n    'Kattie', 'Kavon', 'Kay', 'Kaya', 'Kaycee', 'Kayden', 'Kayla', 'Kaylah',\n    'Kaylee', 'Kayleigh', 'Kayley', 'Kayli', 'Kaylie', 'Kaylin', 'Keagan',\n    'Keanu', 'Keara', 'Keaton', 'Keegan', 'Keeley', 'Keely', 'Keenan', 'Keira',\n    'Keith', 'Kellen', 'Kelley', 'Kelli', 'Kellie', 'Kelly', 'Kelsi', 'Kelsie',\n    'Kelton', 'Kelvin', 'Ken', 'Kendall', 'Kendra', 'Kendrick', 'Kenna',\n    'Kennedi', 'Kennedy', 'Kenneth', 'Kennith', 'Kenny', 'Kenton', 'Kenya',\n    'Kenyatta', 'Kenyon', 'Keon', 'Keshaun', 'Keshawn', 'Keven', 'Kevin',\n    'Kevon', 'Keyon', 'Keyshawn', 'Khalid', 'Khalil', 'Kian', 'Kiana', 'Kianna',\n    'Kiara', 'Kiarra', 'Kiel', 'Kiera', 'Kieran', 'Kiley', 'Kim', 'Kimberly',\n    'King', 'Kip', 'Kira', 'Kirk', 'Kirsten', 'Kirstin', 'Kitty', 'Kobe',\n    'Koby', 'Kody', 'Kolby', 'Kole', 'Korbin', 'Korey', 'Kory', 'Kraig', 'Kris',\n    'Krista', 'Kristian', 'Kristin', 'Kristina', 'Kristofer', 'Kristoffer',\n    'Kristopher', 'Kristy', 'Krystal', 'Krystel', 'Krystina', 'Kurt', 'Kurtis',\n    'Kyla', 'Kyle', 'Kylee', 'Kyleigh', 'Kyler', 'Kylie', 'Kyra', 'Lacey',\n    'Lacy', 'Ladarius', 'Lafayette', 'Laila', 'Laisha', 'Lamar', 'Lambert',\n    'Lamont', 'Lance', 'Landen', 'Lane', 'Laney', 'Larissa', 'Laron', 'Larry',\n    'Larue', 'Laura', 'Laurel', 'Lauren', 'Laurence', 'Lauretta', 'Lauriane',\n    'Laurianne', 'Laurie', 'Laurine', 'Laury', 'Lauryn', 'Lavada', 'Lavern',\n    'Laverna', 'Laverne', 'Lavina', 'Lavinia', 'Lavon', 'Lavonne', 'Lawrence',\n    'Lawson', 'Layla', 'Layne', 'Lazaro', 'Lea', 'Leann', 'Leanna', 'Leanne',\n    'Leatha', 'Leda', 'Lee', 'Leif', 'Leila', 'Leilani', 'Lela', 'Lelah',\n    'Leland', 'Lelia', 'Lempi', 'Lemuel', 'Lenna', 'Lennie', 'Lenny', 'Lenora',\n    'Lenore', 'Leo', 'Leola', 'Leon', 'Leonard', 'Leonardo', 'Leone', 'Leonel',\n    'Leonie', 'Leonor', 'Leonora', 'Leopold', 'Leopoldo', 'Leora', 'Lera',\n    'Lesley', 'Leslie', 'Lesly', 'Lessie', 'Lester', 'Leta', 'Letha', 'Letitia',\n    'Levi', 'Lew', 'Lewis', 'Lexi', 'Lexie', 'Lexus', 'Lia', 'Liam', 'Liana',\n    'Libbie', 'Libby', 'Lila', 'Lilian', 'Liliana', 'Liliane', 'Lilla',\n    'Lillian', 'Lilliana', 'Lillie', 'Lilly', 'Lily', 'Lilyan', 'Lina',\n    'Lincoln', 'Linda', 'Lindsay', 'Lindsey', 'Linnea', 'Linnie', 'Linwood',\n    'Lionel', 'Lisa', 'Lisandro', 'Lisette', 'Litzy', 'Liza', 'Lizeth',\n    'Lizzie', 'Llewellyn', 'Lloyd', 'Logan', 'Lois', 'Lola', 'Lolita', 'Loma',\n    'Lon', 'London', 'Lonie', 'Lonnie', 'Lonny', 'Lonzo', 'Lora', 'Loraine',\n    'Loren', 'Lorena', 'Lorenz', 'Lorenza', 'Lorenzo', 'Lori', 'Lorine',\n    'Lorna', 'Lottie', 'Lou', 'Louie', 'Louisa', 'Lourdes', 'Louvenia',\n    'Lowell', 'Loy', 'Loyal', 'Loyce', 'Lucas', 'Luciano', 'Lucie', 'Lucienne',\n    'Lucile', 'Lucinda', 'Lucio', 'Lucious', 'Lucius', 'Lucy', 'Ludie',\n    'Ludwig', 'Lue', 'Luella', 'Luigi', 'Luis', 'Luisa', 'Lukas', 'Lula',\n    'Lulu', 'Luna', 'Lupe', 'Lura', 'Lurline', 'Luther', 'Luz', 'Lyda', 'Lydia',\n    'Lyla', 'Lynn', 'Lyric', 'Lysanne', 'Mabel', 'Mabelle', 'Mable', 'Mac',\n    'Macey', 'Maci', 'Macie', 'Mack', 'Mackenzie', 'Macy', 'Madaline',\n    'Madalyn', 'Maddison', 'Madeline', 'Madelyn', 'Madelynn', 'Madge', 'Madie',\n    'Madilyn', 'Madisen', 'Madison', 'Madisyn', 'Madonna', 'Madyson', 'Mae',\n    'Maegan', 'Maeve', 'Mafalda', 'Magali', 'Magdalen', 'Magdalena', 'Maggie',\n    'Magnolia', 'Magnus', 'Maia', 'Maida', 'Maiya', 'Major', 'Makayla',\n    'Makenna', 'Makenzie', 'Malachi', 'Malcolm', 'Malika', 'Malinda', 'Mallie',\n    'Mallory', 'Malvina', 'Mandy', 'Manley', 'Manuel', 'Manuela', 'Mara',\n    'Marc', 'Marcel', 'Marcelina', 'Marcelino', 'Marcella', 'Marcelle',\n    'Marcellus', 'Marcelo', 'Marcia', 'Marco', 'Marcos', 'Marcus', 'Margaret',\n    'Margarete', 'Margarett', 'Margaretta', 'Margarette', 'Margarita', 'Marge',\n    'Margie', 'Margot', 'Margret', 'Marguerite', 'Maria', 'Mariah', 'Mariam',\n    'Marian', 'Mariana', 'Mariane', 'Marianna', 'Marianne', 'Mariano',\n    'Maribel', 'Marie', 'Mariela', 'Marielle', 'Marietta', 'Marilie', 'Marilou',\n    'Marilyne', 'Marina', 'Mario', 'Marion', 'Marisa', 'Marisol', 'Maritza',\n    'Marjolaine', 'Marjorie', 'Marjory', 'Mark', 'Markus', 'Marlee', 'Marlen',\n    'Marlene', 'Marley', 'Marlin', 'Marlon', 'Marques', 'Marquis', 'Marquise',\n    'Marshall', 'Marta', 'Martin', 'Martina', 'Martine', 'Marty', 'Marvin',\n    'Mary', 'Maryam', 'Maryjane', 'Maryse', 'Mason', 'Mateo', 'Mathew',\n    'Mathias', 'Mathilde', 'Matilda', 'Matilde', 'Matt', 'Matteo', 'Mattie',\n    'Maud', 'Maude', 'Maudie', 'Maureen', 'Maurice', 'Mauricio', 'Maurine',\n    'Maverick', 'Mavis', 'Max', 'Maxie', 'Maxime', 'Maximilian', 'Maximillia',\n    'Maximillian', 'Maximo', 'Maximus', 'Maxine', 'Maxwell', 'May', 'Maya',\n    'Maybell', 'Maybelle', 'Maye', 'Maymie', 'Maynard', 'Mayra', 'Mazie',\n    'Mckayla', 'Mckenna', 'Mckenzie', 'Meagan', 'Meaghan', 'Meda', 'Megane',\n    'Meggie', 'Meghan', 'Mekhi', 'Melany', 'Melba', 'Melisa', 'Melissa',\n    'Mellie', 'Melody', 'Melvin', 'Melvina', 'Melyna', 'Melyssa', 'Mercedes',\n    'Meredith', 'Merl', 'Merle', 'Merlin', 'Merritt', 'Mertie', 'Mervin',\n    'Meta', 'Mia', 'Micaela', 'Micah', 'Michael', 'Michaela', 'Michale',\n    'Micheal', 'Michel', 'Michele', 'Michelle', 'Miguel', 'Mikayla', 'Mike',\n    'Mikel', 'Milan', 'Miles', 'Milford', 'Miller', 'Millie', 'Milo', 'Milton',\n    'Mina', 'Minerva', 'Minnie', 'Miracle', 'Mireille', 'Mireya', 'Misael',\n    'Missouri', 'Misty', 'Mitchel', 'Mitchell', 'Mittie', 'Modesta', 'Modesto',\n    'Mohamed', 'Mohammad', 'Mohammed', 'Moises', 'Mollie', 'Molly', 'Mona',\n    'Monica', 'Monique', 'Monroe', 'Monserrat', 'Monserrate', 'Montana',\n    'Monte', 'Monty', 'Morgan', 'Moriah', 'Morris', 'Mortimer', 'Morton',\n    'Mose', 'Moses', 'Moshe', 'Mossie', 'Mozell', 'Mozelle', 'Muhammad',\n    'Muriel', 'Murl', 'Murphy', 'Murray', 'Mustafa', 'Mya', 'Myah', 'Mylene',\n    'Myles', 'Myra', 'Myriam', 'Myrl', 'Myrna', 'Myron', 'Myrtice', 'Myrtie',\n    'Myrtis', 'Myrtle', 'Nadia', 'Nakia', 'Name', 'Nannie', 'Naomi', 'Naomie',\n    'Napoleon', 'Narciso', 'Nash', 'Nasir', 'Nat', 'Natalia', 'Natalie',\n    'Natasha', 'Nathan', 'Nathanael', 'Nathanial', 'Nathaniel', 'Nathen',\n    'Nayeli', 'Neal', 'Ned', 'Nedra', 'Neha', 'Neil', 'Nelda', 'Nella', 'Nelle',\n    'Nellie', 'Nels', 'Nelson', 'Neoma', 'Nestor', 'Nettie', 'Neva', 'Newell',\n    'Newton', 'Nia', 'Nicholas', 'Nicholaus', 'Nichole', 'Nick', 'Nicklaus',\n    'Nickolas', 'Nico', 'Nicola', 'Nicolas', 'Nicole', 'Nicolette', 'Nigel',\n    'Nikita', 'Nikki', 'Nikko', 'Niko', 'Nikolas', 'Nils', 'Nina', 'Noah',\n    'Noble', 'Noe', 'Noel', 'Noelia', 'Noemi', 'Noemie', 'Noemy', 'Nola',\n    'Nolan', 'Nona', 'Nora', 'Norbert', 'Norberto', 'Norene', 'Norma', 'Norris',\n    'Norval', 'Norwood', 'Nova', 'Novella', 'Nya', 'Nyah', 'Nyasia', 'Obie',\n    'Oceane', 'Ocie', 'Octavia', 'Oda', 'Odell', 'Odessa', 'Odie', 'Ofelia',\n    'Okey', 'Ola', 'Olaf', 'Ole', 'Olen', 'Oleta', 'Olga', 'Olin', 'Oliver',\n    'Ollie', 'Oma', 'Omari', 'Omer', 'Ona', 'Onie', 'Opal', 'Ophelia', 'Ora',\n    'Oral', 'Oran', 'Oren', 'Orie', 'Orin', 'Orion', 'Orland', 'Orlando',\n    'Orlo', 'Orpha', 'Orrin', 'Orval', 'Orville', 'Osbaldo', 'Osborne', 'Oscar',\n    'Osvaldo', 'Oswald', 'Oswaldo', 'Otha', 'Otho', 'Otilia', 'Otis', 'Ottilie',\n    'Ottis', 'Otto', 'Ova', 'Owen', 'Ozella', 'Pablo', 'Paige', 'Palma',\n    'Pamela', 'Pansy', 'Paolo', 'Paris', 'Parker', 'Pascale', 'Pasquale', 'Pat',\n    'Patience', 'Patricia', 'Patrick', 'Patsy', 'Pattie', 'Paul', 'Paula',\n    'Pauline', 'Paxton', 'Payton', 'Pearl', 'Pearlie', 'Pearline', 'Pedro',\n    'Peggie', 'Penelope', 'Percival', 'Percy', 'Perry', 'Pete', 'Peter',\n    'Petra', 'Peyton', 'Philip', 'Phoebe', 'Phyllis', 'Pierce', 'Pierre',\n    'Pietro', 'Pink', 'Pinkie', 'Piper', 'Polly', 'Porter', 'Precious',\n    'Presley', 'Preston', 'Price', 'Prince', 'Princess', 'Priscilla',\n    'Providenci', 'Prudence', 'Queen', 'Queenie', 'Quentin', 'Quincy', 'Quinn',\n    'Quinten', 'Quinton', 'Rachael', 'Rachel', 'Rachelle', 'Rae', 'Raegan',\n    'Rafael', 'Rafaela', 'Raheem', 'Rahsaan', 'Rahul', 'Raina', 'Raleigh',\n    'Ralph', 'Ramiro', 'Ramon', 'Ramona', 'Randal', 'Randall', 'Randi', 'Randy',\n    'Ransom', 'Raoul', 'Raphael', 'Raphaelle', 'Raquel', 'Rashad', 'Rashawn',\n    'Rasheed', 'Raul', 'Raven', 'Ray', 'Raymond', 'Raymundo', 'Reagan',\n    'Reanna', 'Reba', 'Rebeca', 'Rebecca', 'Rebeka', 'Rebekah', 'Reece', 'Reed',\n    'Reese', 'Regan', 'Reggie', 'Reginald', 'Reid', 'Reilly', 'Reina',\n    'Reinhold', 'Remington', 'Rene', 'Renee', 'Ressie', 'Reta', 'Retha',\n    'Retta', 'Reuben', 'Reva', 'Rex', 'Rey', 'Reyes', 'Reymundo', 'Reyna',\n    'Reynold', 'Rhea', 'Rhett', 'Rhianna', 'Rhiannon', 'Rhoda', 'Ricardo',\n    'Richard', 'Richie', 'Richmond', 'Rick', 'Rickey', 'Rickie', 'Ricky',\n    'Rico', 'Rigoberto', 'Riley', 'Rita', 'River', 'Robb', 'Robbie', 'Robert',\n    'Roberta', 'Roberto', 'Robin', 'Robyn', 'Rocio', 'Rocky', 'Rod', 'Roderick',\n    'Rodger', 'Rodolfo', 'Rodrick', 'Rodrigo', 'Roel', 'Rogelio', 'Roger',\n    'Rogers', 'Rolando', 'Rollin', 'Roma', 'Romaine', 'Roman', 'Ron', 'Ronaldo',\n    'Ronny', 'Roosevelt', 'Rory', 'Rosa', 'Rosalee', 'Rosalia', 'Rosalind',\n    'Rosalinda', 'Rosalyn', 'Rosamond', 'Rosanna', 'Rosario', 'Roscoe', 'Rose',\n    'Rosella', 'Roselyn', 'Rosemarie', 'Rosemary', 'Rosendo', 'Rosetta',\n    'Rosie', 'Rosina', 'Roslyn', 'Ross', 'Rossie', 'Rowan', 'Rowena', 'Rowland',\n    'Roxane', 'Roxanne', 'Roy', 'Royal', 'Royce', 'Rozella', 'Ruben', 'Rubie',\n    'Ruby', 'Rubye', 'Rudolph', 'Rudy', 'Rupert', 'Russ', 'Russel', 'Russell',\n    'Rusty', 'Ruth', 'Ruthe', 'Ruthie', 'Ryan', 'Ryann', 'Ryder', 'Rylan',\n    'Rylee', 'Ryleigh', 'Ryley', 'Sabina', 'Sabrina', 'Sabryna', 'Sadie',\n    'Sadye', 'Sage', 'Saige', 'Sallie', 'Sally', 'Salma', 'Salvador',\n    'Salvatore', 'Sam', 'Samanta', 'Samantha', 'Samara', 'Samir', 'Sammie',\n    'Sammy', 'Samson', 'Sandra', 'Sandrine', 'Sandy', 'Sanford', 'Santa',\n    'Santiago', 'Santina', 'Santino', 'Santos', 'Sarah', 'Sarai', 'Sarina',\n    'Sasha', 'Saul', 'Savanah', 'Savanna', 'Savannah', 'Savion', 'Scarlett',\n    'Schuyler', 'Scot', 'Scottie', 'Scotty', 'Seamus', 'Sean', 'Sebastian',\n    'Sedrick', 'Selena', 'Selina', 'Selmer', 'Serena', 'Serenity', 'Seth',\n    'Shad', 'Shaina', 'Shakira', 'Shana', 'Shane', 'Shanel', 'Shanelle',\n    'Shania', 'Shanie', 'Shaniya', 'Shanna', 'Shannon', 'Shanny', 'Shanon',\n    'Shany', 'Sharon', 'Shaun', 'Shawn', 'Shawna', 'Shaylee', 'Shayna',\n    'Shayne', 'Shea', 'Sheila', 'Sheldon', 'Shemar', 'Sheridan', 'Sherman',\n    'Sherwood', 'Shirley', 'Shyann', 'Shyanne', 'Sibyl', 'Sid', 'Sidney',\n    'Sienna', 'Sierra', 'Sigmund', 'Sigrid', 'Sigurd', 'Silas', 'Sim', 'Simeon',\n    'Simone', 'Sincere', 'Sister', 'Skye', 'Skyla', 'Skylar', 'Sofia',\n    'Soledad', 'Solon', 'Sonia', 'Sonny', 'Sonya', 'Sophia', 'Sophie',\n    'Spencer', 'Stacey', 'Stacy', 'Stan', 'Stanford', 'Stanley', 'Stanton',\n    'Stefan', 'Stefanie', 'Stella', 'Stephan', 'Stephania', 'Stephanie',\n    'Stephany', 'Stephen', 'Stephon', 'Sterling', 'Steve', 'Stevie', 'Stewart',\n    'Stone', 'Stuart', 'Summer', 'Sunny', 'Susan', 'Susana', 'Susanna', 'Susie',\n    'Suzanne', 'Sven', 'Syble', 'Sydnee', 'Sydney', 'Sydni', 'Sydnie', 'Sylvan',\n    'Sylvester', 'Sylvia', 'Tabitha', 'Tad', 'Talia', 'Talon', 'Tamara',\n    'Tamia', 'Tania', 'Tanner', 'Tanya', 'Tara', 'Taryn', 'Tate', 'Tatum',\n    'Tatyana', 'Taurean', 'Tavares', 'Taya', 'Taylor', 'Teagan', 'Ted', 'Telly',\n    'Terence', 'Teresa', 'Terrance', 'Terrell', 'Terrence', 'Terrill', 'Terry',\n    'Tess', 'Tessie', 'Tevin', 'Thad', 'Thaddeus', 'Thalia', 'Thea', 'Thelma',\n    'Theo', 'Theodora', 'Theodore', 'Theresa', 'Therese', 'Theresia', 'Theron',\n    'Thomas', 'Thora', 'Thurman', 'Tia', 'Tiana', 'Tianna', 'Tiara', 'Tierra',\n    'Tiffany', 'Tillman', 'Timmothy', 'Timmy', 'Timothy', 'Tina', 'Tito',\n    'Titus', 'Tobin', 'Toby', 'Tod', 'Tom', 'Tomas', 'Tomasa', 'Tommie',\n    'Toney', 'Toni', 'Tony', 'Torey', 'Torrance', 'Torrey', 'Toy', 'Trace',\n    'Tracey', 'Tracy', 'Travis', 'Travon', 'Tre', 'Tremaine', 'Tremayne',\n    'Trent', 'Trenton', 'Tressa', 'Tressie', 'Treva', 'Trever', 'Trevion',\n    'Trevor', 'Trey', 'Trinity', 'Trisha', 'Tristian', 'Tristin', 'Triston',\n    'Troy', 'Trudie', 'Trycia', 'Trystan', 'Turner', 'Twila', 'Tyler', 'Tyra',\n    'Tyree', 'Tyreek', 'Tyrel', 'Tyrell', 'Tyrese', 'Tyrique', 'Tyshawn',\n    'Tyson', 'Ubaldo', 'Ulices', 'Ulises', 'Una', 'Unique', 'Urban', 'Uriah',\n    'Uriel', 'Ursula', 'Vada', 'Valentin', 'Valentina', 'Valentine', 'Valerie',\n    'Vallie', 'Van', 'Vance', 'Vanessa', 'Vaughn', 'Veda', 'Velda', 'Vella',\n    'Velma', 'Velva', 'Vena', 'Verda', 'Verdie', 'Vergie', 'Verla', 'Verlie',\n    'Vern', 'Verna', 'Verner', 'Vernice', 'Vernie', 'Vernon', 'Verona',\n    'Veronica', 'Vesta', 'Vicenta', 'Vicente', 'Vickie', 'Vicky', 'Victor',\n    'Victoria', 'Vida', 'Vidal', 'Vilma', 'Vince', 'Vincent', 'Vincenza',\n    'Vincenzo', 'Vinnie', 'Viola', 'Violet', 'Violette', 'Virgie', 'Virgil',\n    'Virginia', 'Virginie', 'Vita', 'Vito', 'Viva', 'Vivian', 'Viviane',\n    'Vivianne', 'Vivien', 'Vivienne', 'Vladimir', 'Wade', 'Waino', 'Waldo',\n    'Walker', 'Wallace', 'Walter', 'Walton', 'Wanda', 'Ward', 'Warren',\n    'Watson', 'Wava', 'Waylon', 'Wayne', 'Webster', 'Weldon', 'Wellington',\n    'Wendell', 'Wendy', 'Werner', 'Westley', 'Weston', 'Whitney', 'Wilber',\n    'Wilbert', 'Wilburn', 'Wiley', 'Wilford', 'Wilfred', 'Wilfredo', 'Wilfrid',\n    'Wilhelm', 'Wilhelmine', 'Will', 'Willa', 'Willard', 'William', 'Willie',\n    'Willis', 'Willow', 'Willy', 'Wilma', 'Wilmer', 'Wilson', 'Wilton',\n    'Winfield', 'Winifred', 'Winnifred', 'Winona', 'Winston', 'Woodrow',\n    'Wyatt', 'Wyman', 'Xander', 'Xavier', 'Xzavier', 'Yadira', 'Yasmeen',\n    'Yasmin', 'Yasmine', 'Yazmin', 'Yesenia', 'Yessenia', 'Yolanda', 'Yoshiko',\n    'Yvette', 'Yvonne', 'Zachariah', 'Zachary', 'Zachery', 'Zack', 'Zackary',\n    'Zackery', 'Zakary', 'Zander', 'Zane', 'Zaria', 'Zechariah', 'Zelda',\n    'Zella', 'Zelma', 'Zena', 'Zetta', 'Zion', 'Zita', 'Zoe', 'Zoey', 'Zoie',\n    'Zoila', 'Zola', 'Zora', 'Zula'\n];\n\n/**\n * Generate random username.\n * @returns {string} random username\n */\nfunction generateUsername() {\n    const name = RandomUtil.randomElement(names);\n    const suffix = RandomUtil.randomAlphanumStr(3);\n\n    return `${name}-${suffix}`;\n}\n\nmodule.exports = {\n    generateUsername\n};\n","/**\n * Enumeration of the video types that are signaled to the bridge\n * @type {{CAMERA: string, DESKTOP: string, DESKTOP_HIGH_FPS: string, NONE: string}}\n */\nconst BridgeVideoType = {\n    /**\n     * The camera video type.\n     */\n    CAMERA: 'camera',\n\n    /**\n     * The low fps desktop video type.\n     */\n    DESKTOP: 'desktop',\n\n    /**\n     * The high fps desktop video type.\n     */\n    DESKTOP_HIGH_FPS: 'desktop_high_fps',\n\n    /**\n     * Video type when no local source is present.\n     */\n    NONE: 'none'\n};\n\nmodule.exports = BridgeVideoType;\n","/**\n * The possible camera facing modes. For now support only 'user' and\n * 'environment' because 'left' and 'right' are not used anywhere in our\n * projects at the time of this writing. For more information please refer to\n * https://w3c.github.io/mediacapture-main/getusermedia.html\n * #def-constraint-facingMode.\n *\n * @enum {string}\n */\nconst CameraFacingMode = {\n    /**\n     * The mode which specifies the environment-facing camera.\n     */\n    ENVIRONMENT: 'environment',\n\n    /**\n     * The mode which specifies the user-facing camera.\n     */\n    USER: 'user'\n};\n\nmodule.exports = CameraFacingMode;\n","/**\n * Enumeration of the codec mime types\n * @type {{H264: string, OPUS: string, ULPFEC: string, VP8: string, VP9: string}}\n */\nconst CodecMimeType = {\n    /**\n     * The h264 codec mime type.\n     */\n    H264: 'h264',\n\n    /**\n     * The opus codec mime type.\n     */\n    OPUS: 'opus',\n\n    /**\n     * The ulpfec codec mime type.\n     */\n    ULPFEC: 'ulpfec',\n\n    /**\n     * The vp8 codec mime type.\n     */\n    VP8: 'vp8',\n\n    /**\n     * The vp9 codec mime type.\n     */\n    VP9: 'vp9'\n\n};\n\nmodule.exports = CodecMimeType;\n","/**\n * Enumeration of the media direction types.\n * @type {{INACTIVE: string, RECVONLY: string, SENDONLY: string, SENDRECV: string}}\n */\nconst MediaDirection = {\n    /**\n     * Media is send and receive is suspended.\n     */\n    INACTIVE: 'inactive',\n\n    /**\n     * Media is only received from remote peer.\n     */\n    RECVONLY: 'recvonly',\n\n    /**\n     * Media is only sent to the remote peer.\n     */\n    SENDONLY: 'sendonly',\n\n    /**\n     * Media is sent and received.\n     */\n    SENDRECV: 'sendrecv'\n};\n\nmodule.exports = MediaDirection;\n","export enum RTCEvents {\n    /**\n     * Indicates error while create answer call.\n     */\n    CREATE_ANSWER_FAILED = 'rtc.create_answer_failed',\n\n    /**\n     * Indicates error while create offer call.\n     */\n    CREATE_OFFER_FAILED = 'rtc.create_offer_failed',\n    DATA_CHANNEL_OPEN = 'rtc.data_channel_open',\n    ENDPOINT_CONN_STATUS_CHANGED = 'rtc.endpoint_conn_status_changed',\n    DOMINANT_SPEAKER_CHANGED = 'rtc.dominant_speaker_changed',\n    LASTN_ENDPOINT_CHANGED = 'rtc.lastn_endpoint_changed',\n    FORWARDED_SOURCES_CHANGED = 'rtc.forwarded_sources_changed',\n\n    /**\n     * Event emitted when the user granted/blocked a permission for the camera / mic.\n     * Used to keep track of the granted permissions on browsers which don't\n     * support the Permissions API.\n     */\n    PERMISSIONS_CHANGED = 'rtc.permissions_changed',\n\n    SENDER_VIDEO_CONSTRAINTS_CHANGED = 'rtc.sender_video_constraints_changed',\n\n    /**\n     * Event emitted when {@link RTC.setLastN} method is called to update with\n     * the new value set.\n     * The first argument is the value passed to {@link RTC.setLastN}.\n     */\n    LASTN_VALUE_CHANGED = 'rtc.lastn_value_changed',\n\n    /**\n     * Event emitted when ssrc for a local track is extracted and stored\n     * in {@link TraceablePeerConnection}.\n     * @param {JitsiLocalTrack} track which ssrc was updated\n     * @param {string} ssrc that was stored\n     */\n    LOCAL_TRACK_SSRC_UPDATED = 'rtc.local_track_ssrc_updated',\n\n    /**\n     * The max enabled resolution of a local video track was changed.\n     */\n    LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED = 'rtc.local_track_max_enabled_resolution_changed',\n\n    TRACK_ATTACHED = 'rtc.track_attached',\n\n    /**\n     * Event fired when we remote track is added to the conference.\n     * 1st event argument is the added <tt>JitsiRemoteTrack</tt> instance.\n     **/\n    REMOTE_TRACK_ADDED = 'rtc.remote_track_added',\n\n    // FIXME get rid of this event in favour of NO_DATA_FROM_SOURCE event\n    // (currently implemented for local tracks only)\n    REMOTE_TRACK_MUTE = 'rtc.remote_track_mute',\n\n    /**\n     * Indicates that the remote track has been removed from the conference.\n     * 1st event argument is the removed {@link JitsiRemoteTrack} instance.\n     */\n    REMOTE_TRACK_REMOVED = 'rtc.remote_track_removed',\n\n    // FIXME get rid of this event in favour of NO_DATA_FROM_SOURCE event\n    // (currently implemented for local tracks only)\n    REMOTE_TRACK_UNMUTE = 'rtc.remote_track_unmute',\n\n    /**\n     * Indicates error while set local description.\n     */\n    SET_LOCAL_DESCRIPTION_FAILED = 'rtc.set_local_description_failed',\n\n    /**\n     * Indicates error while set remote description.\n     */\n    SET_REMOTE_DESCRIPTION_FAILED = 'rtc.set_remote_description_failed',\n    AUDIO_OUTPUT_DEVICE_CHANGED = 'rtc.audio_output_device_changed',\n    DEVICE_LIST_CHANGED = 'rtc.device_list_changed',\n\n    /**\n     * Indicates that the list with available devices will change.\n     */\n    DEVICE_LIST_WILL_CHANGE = 'rtc.device_list_will_change',\n    DEVICE_LIST_AVAILABLE = 'rtc.device_list_available',\n\n    /**\n     * Indicates that a message from another participant is received on\n     * data channel.\n     */\n    ENDPOINT_MESSAGE_RECEIVED = 'rtc.endpoint_message_received',\n\n    /**\n     * Indicates that the remote endpoint stats have been received on data channel.\n     */\n    ENDPOINT_STATS_RECEIVED = 'rtc.endpoint_stats_received',\n\n    /**\n     * Designates an event indicating that the local ICE username fragment of\n     * the jingle session has changed.\n     * The first argument of the vent is <tt>TraceablePeerConnection</tt> which\n     * is the source of the event.\n     * The second argument is the actual \"ufrag\" string.\n     */\n    LOCAL_UFRAG_CHANGED = 'rtc.local_ufrag_changed',\n\n    /**\n     * Designates an event indicating that the local ICE username fragment of\n     * the jingle session has changed.\n     * The first argument of the vent is <tt>TraceablePeerConnection</tt> which\n     * is the source of the event.\n     * The second argument is the actual \"ufrag\" string.\n     */\n    REMOTE_UFRAG_CHANGED = 'rtc.remote_ufrag_changed'\n};\n\nexport const CREATE_ANSWER_FAILED = RTCEvents.CREATE_ANSWER_FAILED;\nexport const CREATE_OFFER_FAILED = RTCEvents.CREATE_OFFER_FAILED;\nexport const DATA_CHANNEL_OPEN = RTCEvents.DATA_CHANNEL_OPEN;\nexport const ENDPOINT_CONN_STATUS_CHANGED = RTCEvents.ENDPOINT_CONN_STATUS_CHANGED;\nexport const DOMINANT_SPEAKER_CHANGED = RTCEvents.DOMINANT_SPEAKER_CHANGED;\nexport const LASTN_ENDPOINT_CHANGED = RTCEvents.LASTN_ENDPOINT_CHANGED;\nexport const FORWARDED_SOURCES_CHANGED = RTCEvents.FORWARDED_SOURCES_CHANGED;\nexport const PERMISSIONS_CHANGED = RTCEvents.PERMISSIONS_CHANGED;\nexport const SENDER_VIDEO_CONSTRAINTS_CHANGED = RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED;\nexport const LASTN_VALUE_CHANGED = RTCEvents.LASTN_VALUE_CHANGED;\nexport const LOCAL_TRACK_SSRC_UPDATED = RTCEvents.LOCAL_TRACK_SSRC_UPDATED;\nexport const LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED = RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED;\nexport const TRACK_ATTACHED = RTCEvents.TRACK_ATTACHED;\nexport const REMOTE_TRACK_ADDED = RTCEvents.REMOTE_TRACK_ADDED;\nexport const REMOTE_TRACK_MUTE = RTCEvents.REMOTE_TRACK_MUTE;\nexport const REMOTE_TRACK_REMOVED = RTCEvents.REMOTE_TRACK_REMOVED;\nexport const REMOTE_TRACK_UNMUTE = RTCEvents.REMOTE_TRACK_UNMUTE;\nexport const SET_LOCAL_DESCRIPTION_FAILED = RTCEvents.SET_LOCAL_DESCRIPTION_FAILED;\nexport const SET_REMOTE_DESCRIPTION_FAILED = RTCEvents.SET_REMOTE_DESCRIPTION_FAILED;\nexport const AUDIO_OUTPUT_DEVICE_CHANGED = RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED;\nexport const DEVICE_LIST_CHANGED = RTCEvents.DEVICE_LIST_CHANGED;\nexport const DEVICE_LIST_WILL_CHANGE = RTCEvents.DEVICE_LIST_WILL_CHANGE;\nexport const DEVICE_LIST_AVAILABLE = RTCEvents.DEVICE_LIST_AVAILABLE;\nexport const ENDPOINT_MESSAGE_RECEIVED = RTCEvents.ENDPOINT_MESSAGE_RECEIVED;\nexport const ENDPOINT_STATS_RECEIVED = RTCEvents.ENDPOINT_STATS_RECEIVED;\nexport const LOCAL_UFRAG_CHANGED = RTCEvents.LOCAL_UFRAG_CHANGED;\nexport const REMOTE_UFRAG_CHANGED = RTCEvents.REMOTE_UFRAG_CHANGED;\n\n// TODO: this was a pre-ES6 module using module.exports = RTCEvents which doesn't translate well\n// it is used in a number of places and should be updated to use the named export\n\nexport default RTCEvents;","const Resolutions = {\n    '2160': {\n        width: 3840,\n        height: 2160\n    },\n    '4k': {\n        width: 3840,\n        height: 2160\n    },\n    '1080': {\n        width: 1920,\n        height: 1080\n    },\n    'fullhd': {\n        width: 1920,\n        height: 1080\n    },\n    '720': {\n        width: 1280,\n        height: 720\n    },\n    'hd': {\n        width: 1280,\n        height: 720\n    },\n    '540': {\n        width: 960,\n        height: 540\n    },\n    'qhd': {\n        width: 960,\n        height: 540\n    },\n    '480': {\n        width: 640,\n        height: 480\n    },\n    'vga': {\n        width: 640,\n        height: 480\n    },\n    '360': {\n        width: 640,\n        height: 360\n    },\n    '240': {\n        width: 320,\n        height: 240\n    },\n    '180': {\n        width: 320,\n        height: 180\n    }\n};\n\nmodule.exports = Resolutions;\n","/**\n * Enumeration of the video types\n * @type {{CAMERA: string, DESKTOP: string}}\n */\nconst VideoType = {\n    /**\n     * The camera video type.\n     */\n    CAMERA: 'camera',\n\n    /**\n     * The desktop video type.\n     */\n    DESKTOP: 'desktop'\n};\n\nmodule.exports = VideoType;\n","const AuthenticationEvents = {\n    /**\n     * Event callback arguments:\n     * function(authenticationEnabled, userIdentity)\n     * authenticationEnabled - indicates whether authentication has been enabled\n     *                         in this session\n     * userIdentity - if user has been logged in then it contains user name. If\n     *                contains 'null' or 'undefined' then user is not logged in.\n     */\n    IDENTITY_UPDATED: 'authentication.identity_updated'\n};\n\nmodule.exports = AuthenticationEvents;\n","const Constants = {\n    LOCAL_JID: 'local'\n};\n\nmodule.exports = Constants;\n","export enum XMPPEvents {\n    /**\n     * Indicates error while adding ice candidate.\n     */\n    ADD_ICE_CANDIDATE_FAILED = 'xmpp.add_ice_candidate_failed',\n\n    // Designates an event indicating that the focus has asked us to mute our\n    // audio.\n    AUDIO_MUTED_BY_FOCUS = 'xmpp.audio_muted_by_focus',\n\n    // Designates an event indicating that the focus has asked us to disable our\n    // camera.\n    VIDEO_MUTED_BY_FOCUS = 'xmpp.video_muted_by_focus',\n    AUTHENTICATION_REQUIRED = 'xmpp.authentication_required',\n    BRIDGE_DOWN = 'xmpp.bridge_down',\n\n    /**\n     * Triggered when 'session-accept' is received from the responder.\n     */\n    CALL_ACCEPTED = 'xmpp.callaccepted.jingle',\n\n    // Designates an event indicating that an offer (e.g. Jingle\n    // session-initiate) was received.\n    CALL_INCOMING = 'xmpp.callincoming.jingle',\n\n    // Triggered when Jicofo kills our media session, this can happen while\n    // we're still in the MUC, when it decides to terminate the media session.\n    // For example when the session is idle for too long, because we're the only\n    // person in the conference room.\n    CALL_ENDED = 'xmpp.callended.jingle',\n    CHAT_ERROR_RECEIVED = 'xmpp.chat_error_received',\n    SETTINGS_ERROR_RECEIVED = 'xmpp.settings_error_received',\n\n    // The conference properties (as advertised by jicofo) have changed\n    CONFERENCE_PROPERTIES_CHANGED = 'xmpp.conference_properties_changed',\n\n    /**\n     * This event is triggered when the ICE connects for the first time.\n     */\n    CONNECTION_ESTABLISHED = 'xmpp.connection.connected',\n\n    // Designates an event indicating that the connection to the XMPP server\n    // failed.\n    CONNECTION_FAILED = 'xmpp.connection.failed',\n\n    // Designates an event indicating that the media (ICE) connection was\n    // interrupted. This should go to the RTC module.\n    CONNECTION_INTERRUPTED = 'xmpp.connection.interrupted',\n\n    // Designates an event indicating that the media (ICE) connection was\n    // restored. This should go to the RTC module.\n    CONNECTION_RESTORED = 'xmpp.connection.restored',\n\n    // Designates an event indicating that the media (ICE) connection failed.\n    // This should go to the RTC module.\n    CONNECTION_ICE_FAILED = 'xmpp.connection.ice.failed',\n\n    // Designates an event indicating that the call has been migrated to a different\n    // bridge and that the client needs to be restarted for a successful transition.\n    CONNECTION_RESTARTED = 'xmpp.connection.restart',\n\n    /**\n     * Designates an event indicating connection status changes.\n     */\n    CONNECTION_STATUS_CHANGED = 'xmpp.connection.status.changed',\n\n    // Designates an event indicating that the display name of a participant\n    // has changed.\n    DISPLAY_NAME_CHANGED = 'xmpp.display_name_changed',\n\n    /**\n     * Chat room instance have been added to Strophe.emuc plugin.\n     */\n    EMUC_ROOM_ADDED = 'xmpp.emuc_room_added',\n\n    /**\n     * Chat room instance have been removed from Strophe.emuc plugin.\n     */\n    EMUC_ROOM_REMOVED = 'xmpp.emuc_room_removed',\n    ETHERPAD = 'xmpp.etherpad',\n    FOCUS_DISCONNECTED = 'xmpp.focus_disconnected',\n    FOCUS_LEFT = 'xmpp.focus_left',\n    GRACEFUL_SHUTDOWN = 'xmpp.graceful_shutdown',\n\n    /**\n     * Event fired when 'transport-replace' Jingle message has been received,\n     * before the new offer is set on the PeerConnection.\n     */\n    ICE_RESTARTING = 'rtc.ice_restarting',\n\n    /**\n     * Event fired after the 'transport-replace' message has been processed\n     * and the new offer has been set successfully.\n     */\n    ICE_RESTART_SUCCESS = 'rtc.ice_restart_success',\n\n    /**\n     * Designates an event indicating that we were kicked from the XMPP MUC.\n     * @param {boolean} isSelfPresence - whether it is for local participant\n     * or another participant.\n     * @param {string} actorJid - the jid of the participant who was initiator\n     * of the kick.\n     * @param {?string} participantJid - when it is not a kick for local participant,\n     * this is the jid of the participant which was kicked.\n     */\n    KICKED = 'xmpp.kicked',\n\n    // Designates an event indicating that our role in the XMPP MUC has changed.\n    LOCAL_ROLE_CHANGED = 'xmpp.localrole_changed',\n\n    /**\n     * Event fired when the unique meeting id is set.\n     */\n    MEETING_ID_SET = 'xmpp.meeting_id_set',\n\n    // Designates an event indicating that an XMPP message in the MUC was\n    // received.\n    MESSAGE_RECEIVED = 'xmpp.message_received',\n\n    // Designates an event indicating that an invite XMPP message in the MUC was\n    // received.\n    INVITE_MESSAGE_RECEIVED = 'xmpp.invite_message_received',\n\n    // Designates an event indicating that a private XMPP message in the MUC was\n    // received.\n    PRIVATE_MESSAGE_RECEIVED = 'xmpp.private_message_received',\n\n    // Designates an event indicating that a bot participant type had changed\n    MUC_MEMBER_BOT_TYPE_CHANGED = 'xmpp.muc_member_bot_type_changed',\n\n    // Designates an event indicating that the XMPP MUC was destroyed.\n    MUC_DESTROYED = 'xmpp.muc_destroyed',\n\n    // Designates an event indicating that we are currently in process of joining the XMPP MUC.\n    MUC_JOIN_IN_PROGRESS = 'xmpp.muc_join_in_progress',\n\n    // Designates an event indicating that we have joined the XMPP MUC.\n    MUC_JOINED = 'xmpp.muc_joined',\n\n    // Designates an event indicating that a participant joined the XMPP MUC.\n    MUC_MEMBER_JOINED = 'xmpp.muc_member_joined',\n\n    // Designates an event indicating that a participant left the XMPP MUC.\n    MUC_MEMBER_LEFT = 'xmpp.muc_member_left',\n\n    // Designates an event indicating that a participant joined the lobby XMPP MUC.\n    MUC_LOBBY_MEMBER_JOINED = 'xmpp.muc_lobby_member_joined',\n\n    // Designates an event indicating that a participant in the lobby XMPP MUC has been updated\n    MUC_LOBBY_MEMBER_UPDATED = 'xmpp.muc_lobby_member_updated',\n\n    // Designates an event indicating that a participant left the XMPP MUC.\n    MUC_LOBBY_MEMBER_LEFT = 'xmpp.muc_lobby_member_left',\n\n    // Designates an event indicating that a participant was denied access to a conference from the lobby XMPP MUC.\n    MUC_DENIED_ACCESS = 'xmpp.muc_denied access',\n\n    // Designates an event indicating that local participant left the muc\n    MUC_LEFT = 'xmpp.muc_left',\n\n    // Designates an event indicating that the MUC role of a participant has\n    // changed.\n    MUC_ROLE_CHANGED = 'xmpp.muc_role_changed',\n\n    // Designates an event indicating that the MUC has been locked or unlocked.\n    MUC_LOCK_CHANGED = 'xmpp.muc_lock_changed',\n\n    // Designates an event indicating that the MUC members only config has changed.\n    MUC_MEMBERS_ONLY_CHANGED = 'xmpp.muc_members_only_changed',\n\n    // Designates an event indicating that a participant in the XMPP MUC has\n    // advertised that they have audio muted (or unmuted).\n    PARTICIPANT_AUDIO_MUTED = 'xmpp.audio_muted',\n\n    // Designates an event indicating that a participant in the XMPP MUC has\n    // advertised that they have video muted (or unmuted).\n    PARTICIPANT_VIDEO_MUTED = 'xmpp.video_muted',\n\n    // Designates an event indicating that the video type (e.g. 'camera' or\n    // 'screen') for a participant has changed.\n    // Note = currently this event fires every time we receive presence from\n    // someone (regardless of whether or not the \"video type\" changed).\n    PARTICIPANT_VIDEO_TYPE_CHANGED = 'xmpp.video_type',\n\n    /**\n     * Indicates that the features of the participant has been changed.\n     */\n    PARTICIPANT_FEATURES_CHANGED = 'xmpp.participant_features_changed',\n    PASSWORD_REQUIRED = 'xmpp.password_required',\n\n    /**\n     * Indicates that phone number changed.\n     */\n    PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged',\n    PRESENCE_RECEIVED = 'xmpp.presence_received',\n    PRESENCE_STATUS = 'xmpp.presence_status',\n    PROMPT_FOR_LOGIN = 'xmpp.prompt_for_login',\n\n    // xmpp is connected and obtained user media\n    READY_TO_JOIN = 'xmpp.ready_to_join',\n\n    /**\n     * Indicates that recording state changed.\n     */\n    RECORDER_STATE_CHANGED = 'xmpp.recorderStateChanged',\n\n    // Designates an event indicating that we received statistics from a\n    // participant in the MUC.\n    REMOTE_STATS = 'xmpp.remote_stats',\n\n    /**\n     * Indicates that the offer / answer renegotiation has failed.\n     */\n    RENEGOTIATION_FAILED = 'xmpp.renegotiation_failed',\n    RESERVATION_ERROR = 'xmpp.room_reservation_error',\n    ROOM_CONNECT_ERROR = 'xmpp.room_connect_error',\n    ROOM_CONNECT_NOT_ALLOWED_ERROR = 'xmpp.room_connect_error.not_allowed',\n    ROOM_JOIN_ERROR = 'xmpp.room_join_error',\n    ROOM_CONNECT_MEMBERS_ONLY_ERROR = 'xmpp.room_connect_error.members_only',\n\n    /**\n     * Indicates that max users limit has been reached.\n     */\n    ROOM_MAX_USERS_ERROR = 'xmpp.room_max_users_error',\n\n    // Designates an event indicating that we sent an XMPP message to the MUC.\n    SENDING_CHAT_MESSAGE = 'xmpp.sending_chat_message',\n\n    // Designates an event indicating that we sent a private XMPP message to\n    // a specific user of the muc.\n    SENDING_PRIVATE_CHAT_MESSAGE = 'xmpp.sending_private_chat_message',\n\n    /**\n     * Event fired after receiving the confirmation about session accept.\n     */\n    SESSION_ACCEPT = 'xmpp.session_accept',\n\n    /**\n     * Event fired if we receive an error after sending the session accept.\n     */\n    SESSION_ACCEPT_ERROR = 'xmpp.session_accept_error',\n\n    /**\n     * Event fired when we do not get our 'session-accept' acknowledged by\n     * Jicofo. It most likely means that there is serious problem with our\n     * connection or XMPP server and we should reload the conference.\n     *\n     * We have seen that to happen in BOSH requests race condition when the BOSH\n     * request table containing the 'session-accept' was discarded by Prosody.\n     * Jicofo does send the RESULT immediately without any condition, so missing\n     * packets means that most likely it has never seen our IQ.\n     */\n    SESSION_ACCEPT_TIMEOUT = 'xmpp.session_accept_timeout',\n\n    /**\n     * Event fired after successful sending of jingle source-add.\n     */\n    SOURCE_ADD = 'xmpp.source_add',\n\n    /**\n     * Event fired after receiving an error sending of jingle source-add.\n     */\n    SOURCE_ADD_ERROR = 'xmpp.source_add_error',\n\n    /**\n     * Event fired after successful sending of jingle source-remove.\n     */\n    SOURCE_REMOVE = 'xmpp.source_remove',\n\n    /**\n     * Event fired after receiving an error sending of jingle source-remove.\n     */\n    SOURCE_REMOVE_ERROR = 'xmpp.source_remove_error',\n\n    /**\n     * Event fired when speaker stats update message is received.\n     */\n    SPEAKER_STATS_RECEIVED = 'xmpp.speaker_stats_received',\n\n    /**\n     * Event fired when conference creation timestamp is received.\n     */\n    CONFERENCE_TIMESTAMP_RECEIVED = 'xmpp.conference_timestamp_received',\n\n    /**\n     * Event fired when we receive a message for AV moderation approved for the local participant.\n     */\n    AV_MODERATION_APPROVED = 'xmpp.av_moderation.approved',\n\n    /**\n    * Event fired when we receive a message for AV moderation rejected for the local participant.\n    */\n    AV_MODERATION_REJECTED = 'xmpp.av_moderation.rejected',\n\n    /**\n     * Event fired when we receive a message for AV moderation.\n     */\n    AV_MODERATION_RECEIVED = 'xmpp.av_moderation.received',\n\n    /**\n     * Event fired when the moderation enable/disable changes.\n     */\n    AV_MODERATION_CHANGED = 'xmpp.av_moderation.changed',\n\n    /**\n     * Event fired when we receive message that a new jid was approved.\n     */\n    AV_MODERATION_PARTICIPANT_APPROVED = 'xmpp.av_moderation.participant.approved',\n\n    /**\n     * Event fired when we receive message that a new jid was approved.\n     */\n    AV_MODERATION_PARTICIPANT_REJECTED = 'xmpp.av_moderation.participant.rejected',\n\n    /**\n     * Event fired when a participant is requested to join a given (breakout) room.\n     */\n    BREAKOUT_ROOMS_MOVE_TO_ROOM = 'xmpp.breakout-rooms.move-to-room',\n\n    /**\n     * Event fired when we receive a message for breakout rooms.\n     */\n    BREAKOUT_ROOMS_EVENT = 'xmpp.breakout-rooms.event',\n\n    /**\n     * Event fired when the breakout rooms data was updated.\n     */\n    BREAKOUT_ROOMS_UPDATED = 'xmpp.breakout-rooms.updated',\n\n    // Designates an event indicating that we should join the conference with\n    // audio and/or video muted.\n    START_MUTED_FROM_FOCUS = 'xmpp.start_muted_from_focus',\n\n    // Designates an event indicating that the subject of the XMPP MUC has\n    // changed.\n    SUBJECT_CHANGED = 'xmpp.subject_changed',\n\n    // FIXME: how does it belong to XMPP ? - it's detected by the PeerConnection\n    // suspending detected\n    SUSPEND_DETECTED = 'xmpp.suspend_detected',\n\n    /**\n     * Notifies for transcription status changes. The event provides the\n     * following parameters to its listeners:\n     *\n     * @param {String} status - The new status.\n     */\n    TRANSCRIPTION_STATUS_CHANGED = 'xmpp.transcription_status_changed',\n\n    /**\n     * Event fired when 'transport-info' with new ICE candidates is received.\n     */\n    TRANSPORT_INFO = 'xmpp.transportinfo.jingle',\n\n    /**\n     * Indicates that video SIP GW state changed.\n     *\n     * @param {VideoSIPGWConstants} status - Any of the following statuses:\n     * STATUS_BUSY, STATUS_AVAILABLE or STATUS_UNDEFINED.\n     */\n    VIDEO_SIP_GW_AVAILABILITY_CHANGED = 'xmpp.videoSIPGWAvailabilityChanged',\n\n    /**\n     * Indicates that video SIP GW Session state changed.\n     * The statuses are any of the following statuses:\n     * STATE_ON, STATE_OFF, STATE_PENDING, STATE_RETRYING, STATE_FAILED.\n     * {@see VideoSIPGWConstants}\n     *\n     * @param {options} event - {address, oldState, newState, displayName}.\n     */\n    VIDEO_SIP_GW_SESSION_STATE_CHANGED = 'xmpp.videoSIPGWSessionStateChanged',\n\n    // Designates an event indicating that the local ICE connection state has\n    // changed.\n    ICE_CONNECTION_STATE_CHANGED = 'xmpp.ice_connection_state_changed',\n\n    /**\n     * Event which is emitted when the body in an XMPP message in the MUC\n     * contains JSON\n     * TODO: this event contains a typo (xmmp vs xmpp) but it's unlikely this can be changed now\n     */\n    JSON_MESSAGE_RECEIVED = 'xmmp.json_message_received'\n};\n","'use strict'\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n  lookup[i] = code[i]\n  revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n  var len = b64.length\n\n  if (len % 4 > 0) {\n    throw new Error('Invalid string. Length must be a multiple of 4')\n  }\n\n  // Trim off extra bytes after placeholder bytes are found\n  // See: https://github.com/beatgammit/base64-js/issues/42\n  var validLen = b64.indexOf('=')\n  if (validLen === -1) validLen = len\n\n  var placeHoldersLen = validLen === len\n    ? 0\n    : 4 - (validLen % 4)\n\n  return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n  var lens = getLens(b64)\n  var validLen = lens[0]\n  var placeHoldersLen = lens[1]\n  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n  var tmp\n  var lens = getLens(b64)\n  var validLen = lens[0]\n  var placeHoldersLen = lens[1]\n\n  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n  var curByte = 0\n\n  // if there are placeholders, only get up to the last complete 4 chars\n  var len = placeHoldersLen > 0\n    ? validLen - 4\n    : validLen\n\n  var i\n  for (i = 0; i < len; i += 4) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 18) |\n      (revLookup[b64.charCodeAt(i + 1)] << 12) |\n      (revLookup[b64.charCodeAt(i + 2)] << 6) |\n      revLookup[b64.charCodeAt(i + 3)]\n    arr[curByte++] = (tmp >> 16) & 0xFF\n    arr[curByte++] = (tmp >> 8) & 0xFF\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  if (placeHoldersLen === 2) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 2) |\n      (revLookup[b64.charCodeAt(i + 1)] >> 4)\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  if (placeHoldersLen === 1) {\n    tmp =\n      (revLookup[b64.charCodeAt(i)] << 10) |\n      (revLookup[b64.charCodeAt(i + 1)] << 4) |\n      (revLookup[b64.charCodeAt(i + 2)] >> 2)\n    arr[curByte++] = (tmp >> 8) & 0xFF\n    arr[curByte++] = tmp & 0xFF\n  }\n\n  return arr\n}\n\nfunction tripletToBase64 (num) {\n  return lookup[num >> 18 & 0x3F] +\n    lookup[num >> 12 & 0x3F] +\n    lookup[num >> 6 & 0x3F] +\n    lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n  var tmp\n  var output = []\n  for (var i = start; i < end; i += 3) {\n    tmp =\n      ((uint8[i] << 16) & 0xFF0000) +\n      ((uint8[i + 1] << 8) & 0xFF00) +\n      (uint8[i + 2] & 0xFF)\n    output.push(tripletToBase64(tmp))\n  }\n  return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n  var tmp\n  var len = uint8.length\n  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n  var parts = []\n  var maxChunkLength = 16383 // must be multiple of 3\n\n  // go through the array every three bytes, we'll deal with trailing stuff later\n  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n    parts.push(encodeChunk(\n      uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n    ))\n  }\n\n  // pad the end with zeros, but make sure to not forget the extra bytes\n  if (extraBytes === 1) {\n    tmp = uint8[len - 1]\n    parts.push(\n      lookup[tmp >> 2] +\n      lookup[(tmp << 4) & 0x3F] +\n      '=='\n    )\n  } else if (extraBytes === 2) {\n    tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n    parts.push(\n      lookup[tmp >> 10] +\n      lookup[(tmp >> 4) & 0x3F] +\n      lookup[(tmp << 2) & 0x3F] +\n      '='\n    )\n  }\n\n  return parts.join('')\n}\n","!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t():\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof exports?exports.bowser=t():e.bowser=t()}(this,(function(){return function(e){var t={};function r(i){if(t[i])return t[i].exports;var n=t[i]={i:i,l:!1,exports:{}};return e[i].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,i){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},r.r=function(e){\"undefined\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\"Module\"}),Object.defineProperty(e,\"__esModule\",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&\"object\"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(r.r(i),Object.defineProperty(i,\"default\",{enumerable:!0,value:e}),2&t&&\"string\"!=typeof e)for(var n in e)r.d(i,n,function(t){return e[t]}.bind(null,n));return i},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,\"a\",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p=\"\",r(r.s=90)}({17:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i=r(18),n=function(){function e(){}return e.getFirstMatch=function(e,t){var r=t.match(e);return r&&r.length>0&&r[1]||\"\"},e.getSecondMatch=function(e,t){var r=t.match(e);return r&&r.length>1&&r[2]||\"\"},e.matchAndReturnConst=function(e,t,r){if(e.test(t))return r},e.getWindowsVersionName=function(e){switch(e){case\"NT\":return\"NT\";case\"XP\":return\"XP\";case\"NT 5.0\":return\"2000\";case\"NT 5.1\":return\"XP\";case\"NT 5.2\":return\"2003\";case\"NT 6.0\":return\"Vista\";case\"NT 6.1\":return\"7\";case\"NT 6.2\":return\"8\";case\"NT 6.3\":return\"8.1\";case\"NT 10.0\":return\"10\";default:return}},e.getMacOSVersionName=function(e){var t=e.split(\".\").splice(0,2).map((function(e){return parseInt(e,10)||0}));if(t.push(0),10===t[0])switch(t[1]){case 5:return\"Leopard\";case 6:return\"Snow Leopard\";case 7:return\"Lion\";case 8:return\"Mountain Lion\";case 9:return\"Mavericks\";case 10:return\"Yosemite\";case 11:return\"El Capitan\";case 12:return\"Sierra\";case 13:return\"High Sierra\";case 14:return\"Mojave\";case 15:return\"Catalina\";default:return}},e.getAndroidVersionName=function(e){var t=e.split(\".\").splice(0,2).map((function(e){return parseInt(e,10)||0}));if(t.push(0),!(1===t[0]&&t[1]<5))return 1===t[0]&&t[1]<6?\"Cupcake\":1===t[0]&&t[1]>=6?\"Donut\":2===t[0]&&t[1]<2?\"Eclair\":2===t[0]&&2===t[1]?\"Froyo\":2===t[0]&&t[1]>2?\"Gingerbread\":3===t[0]?\"Honeycomb\":4===t[0]&&t[1]<1?\"Ice Cream Sandwich\":4===t[0]&&t[1]<4?\"Jelly Bean\":4===t[0]&&t[1]>=4?\"KitKat\":5===t[0]?\"Lollipop\":6===t[0]?\"Marshmallow\":7===t[0]?\"Nougat\":8===t[0]?\"Oreo\":9===t[0]?\"Pie\":void 0},e.getVersionPrecision=function(e){return e.split(\".\").length},e.compareVersions=function(t,r,i){void 0===i&&(i=!1);var n=e.getVersionPrecision(t),s=e.getVersionPrecision(r),o=Math.max(n,s),a=0,u=e.map([t,r],(function(t){var r=o-e.getVersionPrecision(t),i=t+new Array(r+1).join(\".0\");return e.map(i.split(\".\"),(function(e){return new Array(20-e.length).join(\"0\")+e})).reverse()}));for(i&&(a=o-Math.min(n,s)),o-=1;o>=a;){if(u[0][o]>u[1][o])return 1;if(u[0][o]===u[1][o]){if(o===a)return 0;o-=1}else if(u[0][o]<u[1][o])return-1}},e.map=function(e,t){var r,i=[];if(Array.prototype.map)return Array.prototype.map.call(e,t);for(r=0;r<e.length;r+=1)i.push(t(e[r]));return i},e.getBrowserAlias=function(e){return i.BROWSER_ALIASES_MAP[e]},e.getBrowserTypeByAlias=function(e){return i.BROWSER_MAP[e]||\"\"},e}();t.default=n,e.exports=t.default},18:function(e,t,r){\"use strict\";t.__esModule=!0,t.ENGINE_MAP=t.OS_MAP=t.PLATFORMS_MAP=t.BROWSER_MAP=t.BROWSER_ALIASES_MAP=void 0;t.BROWSER_ALIASES_MAP={\"Amazon Silk\":\"amazon_silk\",\"Android Browser\":\"android\",Bada:\"bada\",BlackBerry:\"blackberry\",Chrome:\"chrome\",Chromium:\"chromium\",Epiphany:\"epiphany\",Firefox:\"firefox\",Focus:\"focus\",Generic:\"generic\",\"Google Search\":\"google_search\",Googlebot:\"googlebot\",\"Internet Explorer\":\"ie\",\"K-Meleon\":\"k_meleon\",Maxthon:\"maxthon\",\"Microsoft Edge\":\"edge\",\"MZ Browser\":\"mz\",\"NAVER Whale Browser\":\"naver\",Opera:\"opera\",\"Opera Coast\":\"opera_coast\",PhantomJS:\"phantomjs\",Puffin:\"puffin\",QupZilla:\"qupzilla\",QQ:\"qq\",QQLite:\"qqlite\",Safari:\"safari\",Sailfish:\"sailfish\",\"Samsung Internet for Android\":\"samsung_internet\",SeaMonkey:\"seamonkey\",Sleipnir:\"sleipnir\",Swing:\"swing\",Tizen:\"tizen\",\"UC Browser\":\"uc\",Vivaldi:\"vivaldi\",\"WebOS Browser\":\"webos\",WeChat:\"wechat\",\"Yandex Browser\":\"yandex\",Roku:\"roku\"};t.BROWSER_MAP={amazon_silk:\"Amazon Silk\",android:\"Android Browser\",bada:\"Bada\",blackberry:\"BlackBerry\",chrome:\"Chrome\",chromium:\"Chromium\",epiphany:\"Epiphany\",firefox:\"Firefox\",focus:\"Focus\",generic:\"Generic\",googlebot:\"Googlebot\",google_search:\"Google Search\",ie:\"Internet Explorer\",k_meleon:\"K-Meleon\",maxthon:\"Maxthon\",edge:\"Microsoft Edge\",mz:\"MZ Browser\",naver:\"NAVER Whale Browser\",opera:\"Opera\",opera_coast:\"Opera Coast\",phantomjs:\"PhantomJS\",puffin:\"Puffin\",qupzilla:\"QupZilla\",qq:\"QQ Browser\",qqlite:\"QQ Browser Lite\",safari:\"Safari\",sailfish:\"Sailfish\",samsung_internet:\"Samsung Internet for Android\",seamonkey:\"SeaMonkey\",sleipnir:\"Sleipnir\",swing:\"Swing\",tizen:\"Tizen\",uc:\"UC Browser\",vivaldi:\"Vivaldi\",webos:\"WebOS Browser\",wechat:\"WeChat\",yandex:\"Yandex Browser\"};t.PLATFORMS_MAP={tablet:\"tablet\",mobile:\"mobile\",desktop:\"desktop\",tv:\"tv\"};t.OS_MAP={WindowsPhone:\"Windows Phone\",Windows:\"Windows\",MacOS:\"macOS\",iOS:\"iOS\",Android:\"Android\",WebOS:\"WebOS\",BlackBerry:\"BlackBerry\",Bada:\"Bada\",Tizen:\"Tizen\",Linux:\"Linux\",ChromeOS:\"Chrome OS\",PlayStation4:\"PlayStation 4\",Roku:\"Roku\"};t.ENGINE_MAP={EdgeHTML:\"EdgeHTML\",Blink:\"Blink\",Trident:\"Trident\",Presto:\"Presto\",Gecko:\"Gecko\",WebKit:\"WebKit\"}},90:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i,n=(i=r(91))&&i.__esModule?i:{default:i},s=r(18);function o(e,t){for(var r=0;r<t.length;r++){var i=t[r];i.enumerable=i.enumerable||!1,i.configurable=!0,\"value\"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}var a=function(){function e(){}var t,r,i;return e.getParser=function(e,t){if(void 0===t&&(t=!1),\"string\"!=typeof e)throw new Error(\"UserAgent should be a string\");return new n.default(e,t)},e.parse=function(e){return new n.default(e).getResult()},t=e,i=[{key:\"BROWSER_MAP\",get:function(){return s.BROWSER_MAP}},{key:\"ENGINE_MAP\",get:function(){return s.ENGINE_MAP}},{key:\"OS_MAP\",get:function(){return s.OS_MAP}},{key:\"PLATFORMS_MAP\",get:function(){return s.PLATFORMS_MAP}}],(r=null)&&o(t.prototype,r),i&&o(t,i),e}();t.default=a,e.exports=t.default},91:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i=u(r(92)),n=u(r(93)),s=u(r(94)),o=u(r(95)),a=u(r(17));function u(e){return e&&e.__esModule?e:{default:e}}var d=function(){function e(e,t){if(void 0===t&&(t=!1),null==e||\"\"===e)throw new Error(\"UserAgent parameter can't be empty\");this._ua=e,this.parsedResult={},!0!==t&&this.parse()}var t=e.prototype;return t.getUA=function(){return this._ua},t.test=function(e){return e.test(this._ua)},t.parseBrowser=function(){var e=this;this.parsedResult.browser={};var t=i.default.find((function(t){if(\"function\"==typeof t.test)return t.test(e);if(t.test instanceof Array)return t.test.some((function(t){return e.test(t)}));throw new Error(\"Browser's test function is not valid\")}));return t&&(this.parsedResult.browser=t.describe(this.getUA())),this.parsedResult.browser},t.getBrowser=function(){return this.parsedResult.browser?this.parsedResult.browser:this.parseBrowser()},t.getBrowserName=function(e){return e?String(this.getBrowser().name).toLowerCase()||\"\":this.getBrowser().name||\"\"},t.getBrowserVersion=function(){return this.getBrowser().version},t.getOS=function(){return this.parsedResult.os?this.parsedResult.os:this.parseOS()},t.parseOS=function(){var e=this;this.parsedResult.os={};var t=n.default.find((function(t){if(\"function\"==typeof t.test)return t.test(e);if(t.test instanceof Array)return t.test.some((function(t){return e.test(t)}));throw new Error(\"Browser's test function is not valid\")}));return t&&(this.parsedResult.os=t.describe(this.getUA())),this.parsedResult.os},t.getOSName=function(e){var t=this.getOS().name;return e?String(t).toLowerCase()||\"\":t||\"\"},t.getOSVersion=function(){return this.getOS().version},t.getPlatform=function(){return this.parsedResult.platform?this.parsedResult.platform:this.parsePlatform()},t.getPlatformType=function(e){void 0===e&&(e=!1);var t=this.getPlatform().type;return e?String(t).toLowerCase()||\"\":t||\"\"},t.parsePlatform=function(){var e=this;this.parsedResult.platform={};var t=s.default.find((function(t){if(\"function\"==typeof t.test)return t.test(e);if(t.test instanceof Array)return t.test.some((function(t){return e.test(t)}));throw new Error(\"Browser's test function is not valid\")}));return t&&(this.parsedResult.platform=t.describe(this.getUA())),this.parsedResult.platform},t.getEngine=function(){return this.parsedResult.engine?this.parsedResult.engine:this.parseEngine()},t.getEngineName=function(e){return e?String(this.getEngine().name).toLowerCase()||\"\":this.getEngine().name||\"\"},t.parseEngine=function(){var e=this;this.parsedResult.engine={};var t=o.default.find((function(t){if(\"function\"==typeof t.test)return t.test(e);if(t.test instanceof Array)return t.test.some((function(t){return e.test(t)}));throw new Error(\"Browser's test function is not valid\")}));return t&&(this.parsedResult.engine=t.describe(this.getUA())),this.parsedResult.engine},t.parse=function(){return this.parseBrowser(),this.parseOS(),this.parsePlatform(),this.parseEngine(),this},t.getResult=function(){return Object.assign({},this.parsedResult)},t.satisfies=function(e){var t=this,r={},i=0,n={},s=0;if(Object.keys(e).forEach((function(t){var o=e[t];\"string\"==typeof o?(n[t]=o,s+=1):\"object\"==typeof o&&(r[t]=o,i+=1)})),i>0){var o=Object.keys(r),a=o.find((function(e){return t.isOS(e)}));if(a){var u=this.satisfies(r[a]);if(void 0!==u)return u}var d=o.find((function(e){return t.isPlatform(e)}));if(d){var c=this.satisfies(r[d]);if(void 0!==c)return c}}if(s>0){var f=Object.keys(n).find((function(e){return t.isBrowser(e,!0)}));if(void 0!==f)return this.compareVersion(n[f])}},t.isBrowser=function(e,t){void 0===t&&(t=!1);var r=this.getBrowserName().toLowerCase(),i=e.toLowerCase(),n=a.default.getBrowserTypeByAlias(i);return t&&n&&(i=n.toLowerCase()),i===r},t.compareVersion=function(e){var t=[0],r=e,i=!1,n=this.getBrowserVersion();if(\"string\"==typeof n)return\">\"===e[0]||\"<\"===e[0]?(r=e.substr(1),\"=\"===e[1]?(i=!0,r=e.substr(2)):t=[],\">\"===e[0]?t.push(1):t.push(-1)):\"=\"===e[0]?r=e.substr(1):\"~\"===e[0]&&(i=!0,r=e.substr(1)),t.indexOf(a.default.compareVersions(n,r,i))>-1},t.isOS=function(e){return this.getOSName(!0)===String(e).toLowerCase()},t.isPlatform=function(e){return this.getPlatformType(!0)===String(e).toLowerCase()},t.isEngine=function(e){return this.getEngineName(!0)===String(e).toLowerCase()},t.is=function(e){return this.isBrowser(e)||this.isOS(e)||this.isPlatform(e)},t.some=function(e){var t=this;return void 0===e&&(e=[]),e.some((function(e){return t.is(e)}))},e}();t.default=d,e.exports=t.default},92:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i,n=(i=r(17))&&i.__esModule?i:{default:i};var s=/version\\/(\\d+(\\.?_?\\d+)+)/i,o=[{test:[/googlebot/i],describe:function(e){var t={name:\"Googlebot\"},r=n.default.getFirstMatch(/googlebot\\/(\\d+(\\.\\d+))/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/opera/i],describe:function(e){var t={name:\"Opera\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:opera)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/opr\\/|opios/i],describe:function(e){var t={name:\"Opera\"},r=n.default.getFirstMatch(/(?:opr|opios)[\\s/](\\S+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/SamsungBrowser/i],describe:function(e){var t={name:\"Samsung Internet for Android\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:SamsungBrowser)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/Whale/i],describe:function(e){var t={name:\"NAVER Whale Browser\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:whale)[\\s/](\\d+(?:\\.\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/MZBrowser/i],describe:function(e){var t={name:\"MZ Browser\"},r=n.default.getFirstMatch(/(?:MZBrowser)[\\s/](\\d+(?:\\.\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/focus/i],describe:function(e){var t={name:\"Focus\"},r=n.default.getFirstMatch(/(?:focus)[\\s/](\\d+(?:\\.\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/swing/i],describe:function(e){var t={name:\"Swing\"},r=n.default.getFirstMatch(/(?:swing)[\\s/](\\d+(?:\\.\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/coast/i],describe:function(e){var t={name:\"Opera Coast\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:coast)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/yabrowser/i],describe:function(e){var t={name:\"Yandex Browser\"},r=n.default.getFirstMatch(/(?:yabrowser)[\\s/](\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/ucbrowser/i],describe:function(e){var t={name:\"UC Browser\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:ucbrowser)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/Maxthon|mxios/i],describe:function(e){var t={name:\"Maxthon\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:Maxthon|mxios)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/epiphany/i],describe:function(e){var t={name:\"Epiphany\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:epiphany)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/puffin/i],describe:function(e){var t={name:\"Puffin\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:puffin)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/sleipnir/i],describe:function(e){var t={name:\"Sleipnir\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:sleipnir)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/k-meleon/i],describe:function(e){var t={name:\"K-Meleon\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/(?:k-meleon)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/micromessenger/i],describe:function(e){var t={name:\"WeChat\"},r=n.default.getFirstMatch(/(?:micromessenger)[\\s/](\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/qqbrowser/i],describe:function(e){var t={name:/qqbrowserlite/i.test(e)?\"QQ Browser Lite\":\"QQ Browser\"},r=n.default.getFirstMatch(/(?:qqbrowserlite|qqbrowser)[/](\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/msie|trident/i],describe:function(e){var t={name:\"Internet Explorer\"},r=n.default.getFirstMatch(/(?:msie |rv:)(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/\\sedg\\//i],describe:function(e){var t={name:\"Microsoft Edge\"},r=n.default.getFirstMatch(/\\sedg\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/edg([ea]|ios)/i],describe:function(e){var t={name:\"Microsoft Edge\"},r=n.default.getSecondMatch(/edg([ea]|ios)\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/vivaldi/i],describe:function(e){var t={name:\"Vivaldi\"},r=n.default.getFirstMatch(/vivaldi\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/seamonkey/i],describe:function(e){var t={name:\"SeaMonkey\"},r=n.default.getFirstMatch(/seamonkey\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/sailfish/i],describe:function(e){var t={name:\"Sailfish\"},r=n.default.getFirstMatch(/sailfish\\s?browser\\/(\\d+(\\.\\d+)?)/i,e);return r&&(t.version=r),t}},{test:[/silk/i],describe:function(e){var t={name:\"Amazon Silk\"},r=n.default.getFirstMatch(/silk\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/phantom/i],describe:function(e){var t={name:\"PhantomJS\"},r=n.default.getFirstMatch(/phantomjs\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/slimerjs/i],describe:function(e){var t={name:\"SlimerJS\"},r=n.default.getFirstMatch(/slimerjs\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/blackberry|\\bbb\\d+/i,/rim\\stablet/i],describe:function(e){var t={name:\"BlackBerry\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/blackberry[\\d]+\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/(web|hpw)[o0]s/i],describe:function(e){var t={name:\"WebOS Browser\"},r=n.default.getFirstMatch(s,e)||n.default.getFirstMatch(/w(?:eb)?[o0]sbrowser\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/bada/i],describe:function(e){var t={name:\"Bada\"},r=n.default.getFirstMatch(/dolfin\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/tizen/i],describe:function(e){var t={name:\"Tizen\"},r=n.default.getFirstMatch(/(?:tizen\\s?)?browser\\/(\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/qupzilla/i],describe:function(e){var t={name:\"QupZilla\"},r=n.default.getFirstMatch(/(?:qupzilla)[\\s/](\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/firefox|iceweasel|fxios/i],describe:function(e){var t={name:\"Firefox\"},r=n.default.getFirstMatch(/(?:firefox|iceweasel|fxios)[\\s/](\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/chromium/i],describe:function(e){var t={name:\"Chromium\"},r=n.default.getFirstMatch(/(?:chromium)[\\s/](\\d+(\\.?_?\\d+)+)/i,e)||n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/chrome|crios|crmo/i],describe:function(e){var t={name:\"Chrome\"},r=n.default.getFirstMatch(/(?:chrome|crios|crmo)\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/GSA/i],describe:function(e){var t={name:\"Google Search\"},r=n.default.getFirstMatch(/(?:GSA)\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:function(e){var t=!e.test(/like android/i),r=e.test(/android/i);return t&&r},describe:function(e){var t={name:\"Android Browser\"},r=n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/playstation 4/i],describe:function(e){var t={name:\"PlayStation 4\"},r=n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/safari|applewebkit/i],describe:function(e){var t={name:\"Safari\"},r=n.default.getFirstMatch(s,e);return r&&(t.version=r),t}},{test:[/.*/i],describe:function(e){var t=-1!==e.search(\"\\\\(\")?/^(.*)\\/(.*)[ \\t]\\((.*)/:/^(.*)\\/(.*) /;return{name:n.default.getFirstMatch(t,e),version:n.default.getSecondMatch(t,e)}}}];t.default=o,e.exports=t.default},93:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i,n=(i=r(17))&&i.__esModule?i:{default:i},s=r(18);var o=[{test:[/Roku\\/DVP/],describe:function(e){var t=n.default.getFirstMatch(/Roku\\/DVP-(\\d+\\.\\d+)/i,e);return{name:s.OS_MAP.Roku,version:t}}},{test:[/windows phone/i],describe:function(e){var t=n.default.getFirstMatch(/windows phone (?:os)?\\s?(\\d+(\\.\\d+)*)/i,e);return{name:s.OS_MAP.WindowsPhone,version:t}}},{test:[/windows/i],describe:function(e){var t=n.default.getFirstMatch(/Windows ((NT|XP)( \\d\\d?.\\d)?)/i,e),r=n.default.getWindowsVersionName(t);return{name:s.OS_MAP.Windows,version:t,versionName:r}}},{test:[/macintosh/i],describe:function(e){var t=n.default.getFirstMatch(/mac os x (\\d+(\\.?_?\\d+)+)/i,e).replace(/[_\\s]/g,\".\"),r=n.default.getMacOSVersionName(t),i={name:s.OS_MAP.MacOS,version:t};return r&&(i.versionName=r),i}},{test:[/(ipod|iphone|ipad)/i],describe:function(e){var t=n.default.getFirstMatch(/os (\\d+([_\\s]\\d+)*) like mac os x/i,e).replace(/[_\\s]/g,\".\");return{name:s.OS_MAP.iOS,version:t}}},{test:function(e){var t=!e.test(/like android/i),r=e.test(/android/i);return t&&r},describe:function(e){var t=n.default.getFirstMatch(/android[\\s/-](\\d+(\\.\\d+)*)/i,e),r=n.default.getAndroidVersionName(t),i={name:s.OS_MAP.Android,version:t};return r&&(i.versionName=r),i}},{test:[/(web|hpw)[o0]s/i],describe:function(e){var t=n.default.getFirstMatch(/(?:web|hpw)[o0]s\\/(\\d+(\\.\\d+)*)/i,e),r={name:s.OS_MAP.WebOS};return t&&t.length&&(r.version=t),r}},{test:[/blackberry|\\bbb\\d+/i,/rim\\stablet/i],describe:function(e){var t=n.default.getFirstMatch(/rim\\stablet\\sos\\s(\\d+(\\.\\d+)*)/i,e)||n.default.getFirstMatch(/blackberry\\d+\\/(\\d+([_\\s]\\d+)*)/i,e)||n.default.getFirstMatch(/\\bbb(\\d+)/i,e);return{name:s.OS_MAP.BlackBerry,version:t}}},{test:[/bada/i],describe:function(e){var t=n.default.getFirstMatch(/bada\\/(\\d+(\\.\\d+)*)/i,e);return{name:s.OS_MAP.Bada,version:t}}},{test:[/tizen/i],describe:function(e){var t=n.default.getFirstMatch(/tizen[/\\s](\\d+(\\.\\d+)*)/i,e);return{name:s.OS_MAP.Tizen,version:t}}},{test:[/linux/i],describe:function(){return{name:s.OS_MAP.Linux}}},{test:[/CrOS/],describe:function(){return{name:s.OS_MAP.ChromeOS}}},{test:[/PlayStation 4/],describe:function(e){var t=n.default.getFirstMatch(/PlayStation 4[/\\s](\\d+(\\.\\d+)*)/i,e);return{name:s.OS_MAP.PlayStation4,version:t}}}];t.default=o,e.exports=t.default},94:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i,n=(i=r(17))&&i.__esModule?i:{default:i},s=r(18);var o=[{test:[/googlebot/i],describe:function(){return{type:\"bot\",vendor:\"Google\"}}},{test:[/huawei/i],describe:function(e){var t=n.default.getFirstMatch(/(can-l01)/i,e)&&\"Nova\",r={type:s.PLATFORMS_MAP.mobile,vendor:\"Huawei\"};return t&&(r.model=t),r}},{test:[/nexus\\s*(?:7|8|9|10).*/i],describe:function(){return{type:s.PLATFORMS_MAP.tablet,vendor:\"Nexus\"}}},{test:[/ipad/i],describe:function(){return{type:s.PLATFORMS_MAP.tablet,vendor:\"Apple\",model:\"iPad\"}}},{test:[/kftt build/i],describe:function(){return{type:s.PLATFORMS_MAP.tablet,vendor:\"Amazon\",model:\"Kindle Fire HD 7\"}}},{test:[/silk/i],describe:function(){return{type:s.PLATFORMS_MAP.tablet,vendor:\"Amazon\"}}},{test:[/tablet(?! pc)/i],describe:function(){return{type:s.PLATFORMS_MAP.tablet}}},{test:function(e){var t=e.test(/ipod|iphone/i),r=e.test(/like (ipod|iphone)/i);return t&&!r},describe:function(e){var t=n.default.getFirstMatch(/(ipod|iphone)/i,e);return{type:s.PLATFORMS_MAP.mobile,vendor:\"Apple\",model:t}}},{test:[/nexus\\s*[0-6].*/i,/galaxy nexus/i],describe:function(){return{type:s.PLATFORMS_MAP.mobile,vendor:\"Nexus\"}}},{test:[/[^-]mobi/i],describe:function(){return{type:s.PLATFORMS_MAP.mobile}}},{test:function(e){return\"blackberry\"===e.getBrowserName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.mobile,vendor:\"BlackBerry\"}}},{test:function(e){return\"bada\"===e.getBrowserName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.mobile}}},{test:function(e){return\"windows phone\"===e.getBrowserName()},describe:function(){return{type:s.PLATFORMS_MAP.mobile,vendor:\"Microsoft\"}}},{test:function(e){var t=Number(String(e.getOSVersion()).split(\".\")[0]);return\"android\"===e.getOSName(!0)&&t>=3},describe:function(){return{type:s.PLATFORMS_MAP.tablet}}},{test:function(e){return\"android\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.mobile}}},{test:function(e){return\"macos\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.desktop,vendor:\"Apple\"}}},{test:function(e){return\"windows\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.desktop}}},{test:function(e){return\"linux\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.desktop}}},{test:function(e){return\"playstation 4\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.tv}}},{test:function(e){return\"roku\"===e.getOSName(!0)},describe:function(){return{type:s.PLATFORMS_MAP.tv}}}];t.default=o,e.exports=t.default},95:function(e,t,r){\"use strict\";t.__esModule=!0,t.default=void 0;var i,n=(i=r(17))&&i.__esModule?i:{default:i},s=r(18);var o=[{test:function(e){return\"microsoft edge\"===e.getBrowserName(!0)},describe:function(e){if(/\\sedg\\//i.test(e))return{name:s.ENGINE_MAP.Blink};var t=n.default.getFirstMatch(/edge\\/(\\d+(\\.?_?\\d+)+)/i,e);return{name:s.ENGINE_MAP.EdgeHTML,version:t}}},{test:[/trident/i],describe:function(e){var t={name:s.ENGINE_MAP.Trident},r=n.default.getFirstMatch(/trident\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:function(e){return e.test(/presto/i)},describe:function(e){var t={name:s.ENGINE_MAP.Presto},r=n.default.getFirstMatch(/presto\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:function(e){var t=e.test(/gecko/i),r=e.test(/like gecko/i);return t&&!r},describe:function(e){var t={name:s.ENGINE_MAP.Gecko},r=n.default.getFirstMatch(/gecko\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}},{test:[/(apple)?webkit\\/537\\.36/i],describe:function(){return{name:s.ENGINE_MAP.Blink}}},{test:[/(apple)?webkit/i],describe:function(e){var t={name:s.ENGINE_MAP.WebKit},r=n.default.getFirstMatch(/webkit\\/(\\d+(\\.?_?\\d+)+)/i,e);return r&&(t.version=r),t}}];t.default=o,e.exports=t.default}})}));","/*!\n * currentExecutingScript\n * Get the currently executing script, regardless of its source/trigger/synchronicity. Similar to HTML5's `document.currentScript` but arguably much more useful!\n * Copyright (c) 2015 James M. Greene\n * Licensed MIT\n * https://github.com/JamesMGreene/currentExecutingScript\n * v0.1.3\n */\n(function(root, factory) {\n  if (typeof define === \"function\" && define.amd) {\n    // AMD. Register as an anonymous module.\n    define([], factory);\n  } else if (typeof exports === \"object\") {\n    // CommonJS-like environments that support `module.exports`,\n    // like Node.js. Does not work with strict CommonJS!\n    module.exports = factory();\n  } else {\n    // Browser globals (`root` is `window`)\n    root.currentExecutingScript = factory();\n  }\n}(\n  // Current context/scope\n  this || window,\n\n  // Factory function to return the export\n  function() {\n\nvar scriptReadyRegex = /^(interactive|loaded|complete)$/;\n\n// This page's URL (minus query string and fragment identifer hash, if any)\nvar fullPageUrl = !!window.location ? window.location.href : null;\nvar pageUrl = fullPageUrl ? fullPageUrl.replace(/#.*$/, \"\").replace(/\\?.*$/, \"\") || null : null;\n\n// Live NodeList collection\nvar scripts = document.getElementsByTagName(\"script\");\n\n// Check if the browser supports the `readyState` property on `script` elements\nvar supportsScriptReadyState = \"readyState\" in (scripts[0] || document.createElement(\"script\"));\n\n// Lousy browser detection for [not] Opera\nvar isNotOpera = !window.opera || window.opera.toString() !== \"[object Opera]\";\n\n// Detect if `document.currentScript` is supported\nvar hasNativeCurrentScriptAccessor = \"currentScript\" in document;\n\nvar originalStackDepthConfig;\n// Detect if the V8 Error Stack Trace API is supported\nif (\"stackTraceLimit\" in Error && Error.stackTraceLimit !== Infinity) {\n  originalStackDepthConfig = Error.stackTraceLimit;\n  Error.stackTraceLimit = Infinity;\n}\n\n\n// In some browsers (e.g. Chrome), you can get the current stack from an Error\n// object instance without needing to throw it. Avoiding an unnecessary\n// use of `throw` saves time and performance.\nvar hasStackBeforeThrowing = false,\n    hasStackAfterThrowing = false;\n(function() {\n  try {\n    var err = new Error();\n    hasStackBeforeThrowing = typeof err.stack === \"string\" && !!err.stack;\n    throw err;\n  }\n  catch (thrownErr) {\n    hasStackAfterThrowing = typeof thrownErr.stack === \"string\" && !!thrownErr.stack;\n  }\n})();\n\n\n// Normalize whitespace within a string\nfunction normalizeWhitespace(str) {\n  return str ? str.replace(/^\\s+$|\\s+$/g, \"\").replace(/\\s\\s+/g, \" \") : \"\";\n}\n\n// Get script object based on the `src` URL\nfunction getScriptFromUrl(url, eligibleScripts) {\n  var i,\n      script = null;\n\n  eligibleScripts = eligibleScripts || scripts;\n\n  if (typeof url === \"string\" && url) {\n    for (i = eligibleScripts.length; i--; ) {\n      if (eligibleScripts[i].src === url) {\n        // NOTE: Could check if the same script URL is used by more than one `script` element\n        // here... but let's not. That would yield less useful results in \"loose\" detection. ;)\n        script = eligibleScripts[i];\n        break;\n      }\n    }\n  }\n  return script;\n}\n\n// Get script object based on the caller function's source code body (text)\nfunction getInlineScriptFromCallerSource(callerFnSource, eligibleScripts) {\n  var i, inlineScriptText,\n      script = null,\n      callerSourceText = normalizeWhitespace(callerFnSource);\n\n  eligibleScripts = eligibleScripts || scripts;\n\n  if (callerFnSource && callerSourceText) {\n    for (i = eligibleScripts.length; i--; ) {\n      // Only look at inline scripts\n      if (!eligibleScripts[i].hasAttribute(\"src\")) {\n        inlineScriptText = normalizeWhitespace(eligibleScripts[i].text);\n        if (inlineScriptText.indexOf(callerSourceText) !== -1) {\n          // If more than one match is found, don't return any\n          if (script) {\n            script = null;\n            break;\n          }\n          script = eligibleScripts[i];\n        }\n      }\n    }\n  }\n\n  return script;\n}\n\n// If there is only a single inline script on the page, return it; otherwise `null`\nfunction getSoleInlineScript(eligibleScripts) {\n  var i, len,\n      script = null;\n  eligibleScripts = eligibleScripts || scripts;\n  for (i = 0, len = eligibleScripts.length; i < len; i++) {\n    if (!eligibleScripts[i].hasAttribute(\"src\")) {\n      if (script) {\n        script = null;\n        break;\n      }\n      script = eligibleScripts[i];\n    }\n  }\n  return script;\n}\n\n// Get the currently executing script URL from an Error stack trace\nfunction getScriptUrlFromStack(stack, skipStackDepth) {\n  var matches, remainingStack,\n      url = null,\n      ignoreMessage = typeof skipStackDepth === \"number\";\n  skipStackDepth = ignoreMessage ? Math.round(skipStackDepth) : 0;\n  if (typeof stack === \"string\" && stack) {\n    if (ignoreMessage) {\n      matches = stack.match(/(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n    }\n    else {\n      matches = stack.match(/^(?:|[^:@]*@|.+\\)@(?=data:text\\/javascript|blob|http[s]?|file)|.+?\\s+(?: at |@)(?:[^:\\(]+ )*[\\(]?)(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n\n      if (!(matches && matches[1])) {\n        matches = stack.match(/\\)@(data:text\\/javascript(?:;[^,]+)?,.+?|(?:|blob:)(?:http[s]?|file):\\/\\/[\\/]?.+?\\/[^:\\)]*?)(?::\\d+)(?::\\d+)?/);\n      }\n    }\n\n    if (matches && matches[1]) {\n      if (skipStackDepth > 0) {\n        remainingStack = stack.slice(stack.indexOf(matches[0]) + matches[0].length);\n        url = getScriptUrlFromStack(remainingStack, (skipStackDepth - 1));\n      }\n      else {\n        url = matches[1];\n      }\n    }\n\n    // TODO: Handle more edge cases!\n    // Fixes #1\n    // See https://github.com/JamesMGreene/currentExecutingScript/issues/1\n\n    // ???\n\n  }\n  return url;\n}\n\n\n// Get the farthest currently executing (i.e. yes, EXECUTING) `script` DOM\n// element for the caller function, regardless of whether it is that `script`\n// DOM element is currently being evaluated for the first time. The farthest\n// currently executing `script` DOM element would typically be considered the\n// originator of the current execution stack.\nfunction _farthestExecutingScript() {\n  /*jshint noarg:false */\n\n  // TODO: Implement!\n  // Fixes #3\n  // See https://github.com/JamesMGreene/currentExecutingScript/issues/3\n  return null;\n\n/*\n  // Yes, this IS possible, i.e. if a script removes other scripts (or itself)\n  if (scripts.length === 0) {\n    return null;\n  }\n\n  // Guaranteed accurate in IE 6-10.\n  // Not accurate/supported in any other browsers.\n  if (isNotOpera && supportsScriptReadyState) {\n    for (var i = scripts.length; i--; ) {\n      if (scripts[i].readyState === \"interactive\") {\n        return scripts[i];\n      }\n    }\n  }\n\n  var stack,\n      e = new Error();\n  if (hasStackBeforeThrowing) {\n    stack = e.stack;\n  }\n  if (!stack && hasStackAfterThrowing) {\n    try {\n      throw e;\n    }\n    catch (err) {\n      // NOTE: Cannot use `err.sourceURL` or `err.fileName` as they will always be THIS script\n      stack = err.stack;\n    }\n  }\n  if (stack) {\n    var url = getScriptUrlFromStack(stack, skipStackDepth);\n    var script = getScriptFromUrl(url, scripts );\n    if (!script && pageUrl && url === pageUrl) {\n      // Try to find the correct inline script by searching through\n      // inline scripts' text content for the caller function's source\n      // code to be present. If the caller function's source code is\n      // not available, see if there is only one inline script element\n      // in the DOM and return that (even though it may be wrong)\n\n      // TODO: Implement!\n      // Fixes #4 in part\n      // See https://github.com/JamesMGreene/currentExecutingScript/issues/4\n\n      var callerFn = _farthestExecutingScript.caller || null,\n          callerFnStack = [],\n          callerFnSource = null;\n\n      while (callerFn) {\n        callerFnStack.push(callerFn);\n        callerFn = callerFn.caller || null;\n      }\n      callerFn = callerFnStack.slice(-1)[0];\n      callerFnSource = callerFn ? (\"\" + callerFn) : null;\n\n\n      if (callerFnSource) {\n        script = getInlineScriptFromCallerSource(callerFnSource);\n      }\n      else {\n        // NOTE: This is a loose assumption that could be inaccurate!\n        //\n        // Inaccuracies:\n        //  - If the inline script that initiated the call was also removed from the DOM.\n        //  - If the call was initiated by an element's inline event handler,\n        //    e.g. `<a onclick=\"(function() { alert(currentExecutingScript()); }()\">click</a>`\n        script = getSoleInlineScript();\n      }\n    }\n    return script;\n  }\n\n  // NOTE: This is a loose assumption that could be inaccurate!\n  //\n  // Inaccuracies:\n  //  - If a script is created dynamically and appended to some position\n  //    other than the very end of the document.\n  //  - If multiple scripts are created dynamically and all appended to the\n  //    same position within the document (and do not have their `async` attributes\n  //    set to `false`, at least in browsers that support async script evaluation.\n  //    other than the very end of the document.\n  //  - If any scripts are added with the `async` attribute set to `true` in a browser\n  //    that supports it.\n  //  - May get confused by `script` elements within `svg` elements\n  return scripts[scripts.length - 1] || null;\n*/\n}\n\n\n// Get the originating currently executing (i.e. yes, EXECUTING) `script` DOM\n// element or attribute node (e.g. `onclick`) for the caller function,\n// regardless of whether it is that `script` DOM element is currently being\n// evaluated for the first time. The originating currently executing `script`\n// DOM element [or attribute node] is the originator of the current execution stack.\nfunction _originatingExecutingScript() {\n  // TODO: Implement!\n  // Fixes #2\n  // See https://github.com/JamesMGreene/currentExecutingScript/issues/2\n  return null;\n}\n\n// Get the nearest currently executing (i.e. yes, EXECUTING) `script` DOM\n// element for the caller function, regardless of whether it is that `script`\n// DOM element is currently being evaluated for the first time.\nfunction _nearestExecutingScript() {\n  /*jshint noarg:false */\n\n  // Yes, this IS possible, i.e. if a script removes other scripts (or itself)\n  if (scripts.length === 0) {\n    return null;\n  }\n\n  var i, e, stack, url, script,\n      eligibleScripts = [],\n      skipStackDepth = _nearestExecutingScript.skipStackDepth || 1,\n\n      // TODO: Implement!\n      // Fixes #4 in part\n      // See https://github.com/JamesMGreene/currentExecutingScript/issues/4\n      callerFnSource = null;  //(\"\" + (_nearestExecutingScript.caller || \"\")) || null;\n\n  // This part will only help in IE 6-10.\n  for (i = 0; i < scripts.length; i++) {\n    if (isNotOpera && supportsScriptReadyState) {\n      if (scriptReadyRegex.test(scripts[i].readyState)) {\n        eligibleScripts.push(scripts[i]);\n      }\n    }\n    else {\n      eligibleScripts.push(scripts[i]);\n    }\n  }\n\n  e = new Error();\n  if (hasStackBeforeThrowing) {\n    stack = e.stack;\n  }\n  if (!stack && hasStackAfterThrowing) {\n    try {\n      throw e;\n    }\n    catch (err) {\n      // NOTE: Cannot use `err.sourceURL` or `err.fileName` as they will always be THIS script\n      stack = err.stack;\n    }\n  }\n\n  if (stack) {\n    url = getScriptUrlFromStack(stack, skipStackDepth);\n    script = getScriptFromUrl(url, eligibleScripts);\n\n    if (!script && pageUrl && url === pageUrl) {\n      // Try to find the correct inline script by searching through\n      // inline scripts' text content for the caller function's source\n      // code to be present.\n      if (callerFnSource) {\n        script = getInlineScriptFromCallerSource(callerFnSource, eligibleScripts);\n      }\n      // If the caller function's source code is not available, see if\n      // there is only one inline script element in the DOM and return\n      // that (even though it may be wrong)...\n      else {\n        // NOTE: This is a loose assumption that could be inaccurate!\n        //\n        // Inaccuracies:\n        //  - If the inline script that initiated the call was also removed from the DOM.\n        //  - If the call was initiated by an element's inline event handler,\n        //    e.g. `<a onclick=\"(function() { alert(currentExecutingScript()); }()\">click</a>`\n        script = getSoleInlineScript(eligibleScripts);\n      }\n    }\n  }\n\n  //\n  // Welcome to the Island of Inaccurate Assumptions!\n  // NOTE: ALL of the following are loose assumptions that could be inaccurate!\n  //\n\n  if (!script) {\n    // Inaccuracies:\n    //  - If the inline script that initiated the call was also removed from the DOM.\n    //  - If the call was initiated by an element's inline event handler,\n    //    e.g. `<a onclick=\"(function() { alert(currentExecutingScript()); }()\">click</a>`\n    if (eligibleScripts.length === 1) {\n      script = eligibleScripts[0];\n    }\n  }\n\n  if (!script) {\n    // Inaccuracies:\n    //  - If script currently being synchronously evaluated by the parser is the\n    //    originator of this call stack but NOT the source script of the caller/invocation\n    //    e.g.\n    //    ```html\n    //    <script id=\"a\">\n    //    function getCurrentScriptCallerFn() {\n    //      return currentExecutingScript.near();\n    //    }\n    //    </script>\n    //    <script id=\"b\">\n    //    // Should get `script[id=\"a\"]` but will get `script[id=\"b\"]` instead\n    //    getCurrentScriptCallerFn();\n    //    </script>\n    if (hasNativeCurrentScriptAccessor) {\n      script = document.currentScript;\n    }\n  }\n\n  if (!script) {\n    // Inaccuracies:\n    //  - If script currently being synchronously evaluated by the parser is the\n    //    originator of this call stack but NOT the source script of the caller/invocation\n    //    e.g.\n    //    ```html\n    //    <script id=\"a\">\n    //    function getCurrentScriptCallerFn() {\n    //      return currentExecutingScript.near();\n    //    }\n    //    </script>\n    //    <script id=\"b\">\n    //    // Should get `script[id=\"a\"]` but will get `script[id=\"b\"]` instead\n    //    getCurrentScriptCallerFn();\n    //    </script>\n    if (isNotOpera && supportsScriptReadyState) {\n      for (i = eligibleScripts.length; i--; ) {\n        if (eligibleScripts[i].readyState === \"interactive\") {\n          script = eligibleScripts[i];\n          break;\n        }\n      }\n    }\n  }\n\n  if (!script) {\n    // Inaccuracies:\n    //  - If a script is created dynamically and appended to some position\n    //    other than the very end of the document.\n    //  - If multiple scripts are created dynamically and all appended to the\n    //    same position within the document (and do not have their `async` attributes\n    //    set to `false`, at least in browsers that support async script evaluation.\n    //    other than the very end of the document.\n    //  - If any scripts are added with the `async` attribute set to `true` in a browser\n    //    that supports it.\n    //  - May get confused by `script` elements within `svg` elements\n    //  - If script currently being synchronously evaluated by the parser is the\n    //    originator of this call stack but NOT the source script of the caller/invocation\n    //    e.g.\n    //    ```html\n    //    <script id=\"a\">\n    //    function getCurrentScriptCallerFn() {\n    //      return currentExecutingScript.near();\n    //    }\n    //    </script>\n    //    <script id=\"b\">\n    //    // Should get `script[id=\"a\"]` but will get `script[id=\"b\"]` instead\n    //    getCurrentScriptCallerFn();\n    //    </script>\n    //    ```\n    script = eligibleScripts[eligibleScripts.length - 1] || null;\n  }\n\n  return script;\n}\n\n// Default stack depth to skip over when analyzing call stack frames\n_nearestExecutingScript.skipStackDepth = 1;\n\n\n\n    //\n    // Export the API\n    //\n    var currentExecutingScript    = _nearestExecutingScript;      // default\n    currentExecutingScript.near   = _nearestExecutingScript;\n    currentExecutingScript.far    = _farthestExecutingScript;\n    currentExecutingScript.origin = _originatingExecutingScript;\n\n\n    // Just return a value to define the module export.\n    // This example returns an object, but the module\n    // can return a function as the exported value.\n    return currentExecutingScript;\n  })\n);\n","// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n'use strict';\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n  ? R.apply\n  : function ReflectApply(target, receiver, args) {\n    return Function.prototype.apply.call(target, receiver, args);\n  }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n  ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n  ReflectOwnKeys = function ReflectOwnKeys(target) {\n    return Object.getOwnPropertyNames(target)\n      .concat(Object.getOwnPropertySymbols(target));\n  };\n} else {\n  ReflectOwnKeys = function ReflectOwnKeys(target) {\n    return Object.getOwnPropertyNames(target);\n  };\n}\n\nfunction ProcessEmitWarning(warning) {\n  if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n  return value !== value;\n}\n\nfunction EventEmitter() {\n  EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n  if (typeof listener !== 'function') {\n    throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n  }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n  enumerable: true,\n  get: function() {\n    return defaultMaxListeners;\n  },\n  set: function(arg) {\n    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n      throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n    }\n    defaultMaxListeners = arg;\n  }\n});\n\nEventEmitter.init = function() {\n\n  if (this._events === undefined ||\n      this._events === Object.getPrototypeOf(this)._events) {\n    this._events = Object.create(null);\n    this._eventsCount = 0;\n  }\n\n  this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n    throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n  }\n  this._maxListeners = n;\n  return this;\n};\n\nfunction _getMaxListeners(that) {\n  if (that._maxListeners === undefined)\n    return EventEmitter.defaultMaxListeners;\n  return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n  return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n  var args = [];\n  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n  var doError = (type === 'error');\n\n  var events = this._events;\n  if (events !== undefined)\n    doError = (doError && events.error === undefined);\n  else if (!doError)\n    return false;\n\n  // If there is no 'error' event listener then throw.\n  if (doError) {\n    var er;\n    if (args.length > 0)\n      er = args[0];\n    if (er instanceof Error) {\n      // Note: The comments on the `throw` lines are intentional, they show\n      // up in Node's output if this results in an unhandled exception.\n      throw er; // Unhandled 'error' event\n    }\n    // At least give some kind of context to the user\n    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n    err.context = er;\n    throw err; // Unhandled 'error' event\n  }\n\n  var handler = events[type];\n\n  if (handler === undefined)\n    return false;\n\n  if (typeof handler === 'function') {\n    ReflectApply(handler, this, args);\n  } else {\n    var len = handler.length;\n    var listeners = arrayClone(handler, len);\n    for (var i = 0; i < len; ++i)\n      ReflectApply(listeners[i], this, args);\n  }\n\n  return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n  var m;\n  var events;\n  var existing;\n\n  checkListener(listener);\n\n  events = target._events;\n  if (events === undefined) {\n    events = target._events = Object.create(null);\n    target._eventsCount = 0;\n  } else {\n    // To avoid recursion in the case that type === \"newListener\"! Before\n    // adding it to the listeners, first emit \"newListener\".\n    if (events.newListener !== undefined) {\n      target.emit('newListener', type,\n                  listener.listener ? listener.listener : listener);\n\n      // Re-assign `events` because a newListener handler could have caused the\n      // this._events to be assigned to a new object\n      events = target._events;\n    }\n    existing = events[type];\n  }\n\n  if (existing === undefined) {\n    // Optimize the case of one listener. Don't need the extra array object.\n    existing = events[type] = listener;\n    ++target._eventsCount;\n  } else {\n    if (typeof existing === 'function') {\n      // Adding the second element, need to change to array.\n      existing = events[type] =\n        prepend ? [listener, existing] : [existing, listener];\n      // If we've already got an array, just append.\n    } else if (prepend) {\n      existing.unshift(listener);\n    } else {\n      existing.push(listener);\n    }\n\n    // Check for listener leak\n    m = _getMaxListeners(target);\n    if (m > 0 && existing.length > m && !existing.warned) {\n      existing.warned = true;\n      // No error code for this since it is a Warning\n      // eslint-disable-next-line no-restricted-syntax\n      var w = new Error('Possible EventEmitter memory leak detected. ' +\n                          existing.length + ' ' + String(type) + ' listeners ' +\n                          'added. Use emitter.setMaxListeners() to ' +\n                          'increase limit');\n      w.name = 'MaxListenersExceededWarning';\n      w.emitter = target;\n      w.type = type;\n      w.count = existing.length;\n      ProcessEmitWarning(w);\n    }\n  }\n\n  return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n  return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n    function prependListener(type, listener) {\n      return _addListener(this, type, listener, true);\n    };\n\nfunction onceWrapper() {\n  if (!this.fired) {\n    this.target.removeListener(this.type, this.wrapFn);\n    this.fired = true;\n    if (arguments.length === 0)\n      return this.listener.call(this.target);\n    return this.listener.apply(this.target, arguments);\n  }\n}\n\nfunction _onceWrap(target, type, listener) {\n  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n  var wrapped = onceWrapper.bind(state);\n  wrapped.listener = listener;\n  state.wrapFn = wrapped;\n  return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n  checkListener(listener);\n  this.on(type, _onceWrap(this, type, listener));\n  return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n    function prependOnceListener(type, listener) {\n      checkListener(listener);\n      this.prependListener(type, _onceWrap(this, type, listener));\n      return this;\n    };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n    function removeListener(type, listener) {\n      var list, events, position, i, originalListener;\n\n      checkListener(listener);\n\n      events = this._events;\n      if (events === undefined)\n        return this;\n\n      list = events[type];\n      if (list === undefined)\n        return this;\n\n      if (list === listener || list.listener === listener) {\n        if (--this._eventsCount === 0)\n          this._events = Object.create(null);\n        else {\n          delete events[type];\n          if (events.removeListener)\n            this.emit('removeListener', type, list.listener || listener);\n        }\n      } else if (typeof list !== 'function') {\n        position = -1;\n\n        for (i = list.length - 1; i >= 0; i--) {\n          if (list[i] === listener || list[i].listener === listener) {\n            originalListener = list[i].listener;\n            position = i;\n            break;\n          }\n        }\n\n        if (position < 0)\n          return this;\n\n        if (position === 0)\n          list.shift();\n        else {\n          spliceOne(list, position);\n        }\n\n        if (list.length === 1)\n          events[type] = list[0];\n\n        if (events.removeListener !== undefined)\n          this.emit('removeListener', type, originalListener || listener);\n      }\n\n      return this;\n    };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n    function removeAllListeners(type) {\n      var listeners, events, i;\n\n      events = this._events;\n      if (events === undefined)\n        return this;\n\n      // not listening for removeListener, no need to emit\n      if (events.removeListener === undefined) {\n        if (arguments.length === 0) {\n          this._events = Object.create(null);\n          this._eventsCount = 0;\n        } else if (events[type] !== undefined) {\n          if (--this._eventsCount === 0)\n            this._events = Object.create(null);\n          else\n            delete events[type];\n        }\n        return this;\n      }\n\n      // emit removeListener for all listeners on all events\n      if (arguments.length === 0) {\n        var keys = Object.keys(events);\n        var key;\n        for (i = 0; i < keys.length; ++i) {\n          key = keys[i];\n          if (key === 'removeListener') continue;\n          this.removeAllListeners(key);\n        }\n        this.removeAllListeners('removeListener');\n        this._events = Object.create(null);\n        this._eventsCount = 0;\n        return this;\n      }\n\n      listeners = events[type];\n\n      if (typeof listeners === 'function') {\n        this.removeListener(type, listeners);\n      } else if (listeners !== undefined) {\n        // LIFO order\n        for (i = listeners.length - 1; i >= 0; i--) {\n          this.removeListener(type, listeners[i]);\n        }\n      }\n\n      return this;\n    };\n\nfunction _listeners(target, type, unwrap) {\n  var events = target._events;\n\n  if (events === undefined)\n    return [];\n\n  var evlistener = events[type];\n  if (evlistener === undefined)\n    return [];\n\n  if (typeof evlistener === 'function')\n    return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n  return unwrap ?\n    unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n  return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n  return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n  if (typeof emitter.listenerCount === 'function') {\n    return emitter.listenerCount(type);\n  } else {\n    return listenerCount.call(emitter, type);\n  }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n  var events = this._events;\n\n  if (events !== undefined) {\n    var evlistener = events[type];\n\n    if (typeof evlistener === 'function') {\n      return 1;\n    } else if (evlistener !== undefined) {\n      return evlistener.length;\n    }\n  }\n\n  return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n  var copy = new Array(n);\n  for (var i = 0; i < n; ++i)\n    copy[i] = arr[i];\n  return copy;\n}\n\nfunction spliceOne(list, index) {\n  for (; index + 1 < list.length; index++)\n    list[index] = list[index + 1];\n  list.pop();\n}\n\nfunction unwrapListeners(arr) {\n  var ret = new Array(arr.length);\n  for (var i = 0; i < ret.length; ++i) {\n    ret[i] = arr[i].listener || arr[i];\n  }\n  return ret;\n}\n\nfunction once(emitter, name) {\n  return new Promise(function (resolve, reject) {\n    function errorListener(err) {\n      emitter.removeListener(name, resolver);\n      reject(err);\n    }\n\n    function resolver() {\n      if (typeof emitter.removeListener === 'function') {\n        emitter.removeListener('error', errorListener);\n      }\n      resolve([].slice.call(arguments));\n    };\n\n    eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });\n    if (name !== 'error') {\n      addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });\n    }\n  });\n}\n\nfunction addErrorHandlerIfEventEmitter(emitter, handler, flags) {\n  if (typeof emitter.on === 'function') {\n    eventTargetAgnosticAddListener(emitter, 'error', handler, flags);\n  }\n}\n\nfunction eventTargetAgnosticAddListener(emitter, name, listener, flags) {\n  if (typeof emitter.on === 'function') {\n    if (flags.once) {\n      emitter.once(name, listener);\n    } else {\n      emitter.on(name, listener);\n    }\n  } else if (typeof emitter.addEventListener === 'function') {\n    // EventTarget does not have `error` event semantics like Node\n    // EventEmitters, we do not listen for `error` events here.\n    emitter.addEventListener(name, function wrapListener(arg) {\n      // IE does not have builtin `{ once: true }` support so we\n      // have to do it manually.\n      if (flags.once) {\n        emitter.removeEventListener(name, wrapListener);\n      }\n      listener(arg);\n    });\n  } else {\n    throw new TypeError('The \"emitter\" argument must be of type EventEmitter. Received type ' + typeof emitter);\n  }\n}\n","/**\n * [js-md5]{@link https://github.com/emn178/js-md5}\n *\n * @namespace md5\n * @version 0.7.3\n * @author Chen, Yi-Cyuan [emn178@gmail.com]\n * @copyright Chen, Yi-Cyuan 2014-2017\n * @license MIT\n */\n(function () {\n  'use strict';\n\n  var ERROR = 'input is invalid type';\n  var WINDOW = typeof window === 'object';\n  var root = WINDOW ? window : {};\n  if (root.JS_MD5_NO_WINDOW) {\n    WINDOW = false;\n  }\n  var WEB_WORKER = !WINDOW && typeof self === 'object';\n  var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;\n  if (NODE_JS) {\n    root = global;\n  } else if (WEB_WORKER) {\n    root = self;\n  }\n  var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;\n  var AMD = typeof define === 'function' && define.amd;\n  var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';\n  var HEX_CHARS = '0123456789abcdef'.split('');\n  var EXTRA = [128, 32768, 8388608, -2147483648];\n  var SHIFT = [0, 8, 16, 24];\n  var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];\n  var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');\n\n  var blocks = [], buffer8;\n  if (ARRAY_BUFFER) {\n    var buffer = new ArrayBuffer(68);\n    buffer8 = new Uint8Array(buffer);\n    blocks = new Uint32Array(buffer);\n  }\n\n  if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {\n    Array.isArray = function (obj) {\n      return Object.prototype.toString.call(obj) === '[object Array]';\n    };\n  }\n\n  if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {\n    ArrayBuffer.isView = function (obj) {\n      return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;\n    };\n  }\n\n  /**\n   * @method hex\n   * @memberof md5\n   * @description Output hash as hex string\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {String} Hex string\n   * @example\n   * md5.hex('The quick brown fox jumps over the lazy dog');\n   * // equal to\n   * md5('The quick brown fox jumps over the lazy dog');\n   */\n  /**\n   * @method digest\n   * @memberof md5\n   * @description Output hash as bytes array\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {Array} Bytes array\n   * @example\n   * md5.digest('The quick brown fox jumps over the lazy dog');\n   */\n  /**\n   * @method array\n   * @memberof md5\n   * @description Output hash as bytes array\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {Array} Bytes array\n   * @example\n   * md5.array('The quick brown fox jumps over the lazy dog');\n   */\n  /**\n   * @method arrayBuffer\n   * @memberof md5\n   * @description Output hash as ArrayBuffer\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {ArrayBuffer} ArrayBuffer\n   * @example\n   * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');\n   */\n  /**\n   * @method buffer\n   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.\n   * @memberof md5\n   * @description Output hash as ArrayBuffer\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {ArrayBuffer} ArrayBuffer\n   * @example\n   * md5.buffer('The quick brown fox jumps over the lazy dog');\n   */\n  /**\n   * @method base64\n   * @memberof md5\n   * @description Output hash as base64 string\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {String} base64 string\n   * @example\n   * md5.base64('The quick brown fox jumps over the lazy dog');\n   */\n  var createOutputMethod = function (outputType) {\n    return function (message) {\n      return new Md5(true).update(message)[outputType]();\n    };\n  };\n\n  /**\n   * @method create\n   * @memberof md5\n   * @description Create Md5 object\n   * @returns {Md5} Md5 object.\n   * @example\n   * var hash = md5.create();\n   */\n  /**\n   * @method update\n   * @memberof md5\n   * @description Create and update Md5 object\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {Md5} Md5 object.\n   * @example\n   * var hash = md5.update('The quick brown fox jumps over the lazy dog');\n   * // equal to\n   * var hash = md5.create();\n   * hash.update('The quick brown fox jumps over the lazy dog');\n   */\n  var createMethod = function () {\n    var method = createOutputMethod('hex');\n    if (NODE_JS) {\n      method = nodeWrap(method);\n    }\n    method.create = function () {\n      return new Md5();\n    };\n    method.update = function (message) {\n      return method.create().update(message);\n    };\n    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {\n      var type = OUTPUT_TYPES[i];\n      method[type] = createOutputMethod(type);\n    }\n    return method;\n  };\n\n  var nodeWrap = function (method) {\n    var crypto = eval(\"require('crypto')\");\n    var Buffer = eval(\"require('buffer').Buffer\");\n    var nodeMethod = function (message) {\n      if (typeof message === 'string') {\n        return crypto.createHash('md5').update(message, 'utf8').digest('hex');\n      } else {\n        if (message === null || message === undefined) {\n          throw ERROR;\n        } else if (message.constructor === ArrayBuffer) {\n          message = new Uint8Array(message);\n        }\n      }\n      if (Array.isArray(message) || ArrayBuffer.isView(message) ||\n        message.constructor === Buffer) {\n        return crypto.createHash('md5').update(new Buffer(message)).digest('hex');\n      } else {\n        return method(message);\n      }\n    };\n    return nodeMethod;\n  };\n\n  /**\n   * Md5 class\n   * @class Md5\n   * @description This is internal class.\n   * @see {@link md5.create}\n   */\n  function Md5(sharedMemory) {\n    if (sharedMemory) {\n      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n      blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n      blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n      this.blocks = blocks;\n      this.buffer8 = buffer8;\n    } else {\n      if (ARRAY_BUFFER) {\n        var buffer = new ArrayBuffer(68);\n        this.buffer8 = new Uint8Array(buffer);\n        this.blocks = new Uint32Array(buffer);\n      } else {\n        this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];\n      }\n    }\n    this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;\n    this.finalized = this.hashed = false;\n    this.first = true;\n  }\n\n  /**\n   * @method update\n   * @memberof Md5\n   * @instance\n   * @description Update hash\n   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n   * @returns {Md5} Md5 object.\n   * @see {@link md5.update}\n   */\n  Md5.prototype.update = function (message) {\n    if (this.finalized) {\n      return;\n    }\n\n    var notString, type = typeof message;\n    if (type !== 'string') {\n      if (type === 'object') {\n        if (message === null) {\n          throw ERROR;\n        } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {\n          message = new Uint8Array(message);\n        } else if (!Array.isArray(message)) {\n          if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {\n            throw ERROR;\n          }\n        }\n      } else {\n        throw ERROR;\n      }\n      notString = true;\n    }\n    var code, index = 0, i, length = message.length, blocks = this.blocks;\n    var buffer8 = this.buffer8;\n\n    while (index < length) {\n      if (this.hashed) {\n        this.hashed = false;\n        blocks[0] = blocks[16];\n        blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n        blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n        blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n      }\n\n      if (notString) {\n        if (ARRAY_BUFFER) {\n          for (i = this.start; index < length && i < 64; ++index) {\n            buffer8[i++] = message[index];\n          }\n        } else {\n          for (i = this.start; index < length && i < 64; ++index) {\n            blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];\n          }\n        }\n      } else {\n        if (ARRAY_BUFFER) {\n          for (i = this.start; index < length && i < 64; ++index) {\n            code = message.charCodeAt(index);\n            if (code < 0x80) {\n              buffer8[i++] = code;\n            } else if (code < 0x800) {\n              buffer8[i++] = 0xc0 | (code >> 6);\n              buffer8[i++] = 0x80 | (code & 0x3f);\n            } else if (code < 0xd800 || code >= 0xe000) {\n              buffer8[i++] = 0xe0 | (code >> 12);\n              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);\n              buffer8[i++] = 0x80 | (code & 0x3f);\n            } else {\n              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));\n              buffer8[i++] = 0xf0 | (code >> 18);\n              buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);\n              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);\n              buffer8[i++] = 0x80 | (code & 0x3f);\n            }\n          }\n        } else {\n          for (i = this.start; index < length && i < 64; ++index) {\n            code = message.charCodeAt(index);\n            if (code < 0x80) {\n              blocks[i >> 2] |= code << SHIFT[i++ & 3];\n            } else if (code < 0x800) {\n              blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n            } else if (code < 0xd800 || code >= 0xe000) {\n              blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n            } else {\n              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));\n              blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];\n              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];\n            }\n          }\n        }\n      }\n      this.lastByteIndex = i;\n      this.bytes += i - this.start;\n      if (i >= 64) {\n        this.start = i - 64;\n        this.hash();\n        this.hashed = true;\n      } else {\n        this.start = i;\n      }\n    }\n    if (this.bytes > 4294967295) {\n      this.hBytes += this.bytes / 4294967296 << 0;\n      this.bytes = this.bytes % 4294967296;\n    }\n    return this;\n  };\n\n  Md5.prototype.finalize = function () {\n    if (this.finalized) {\n      return;\n    }\n    this.finalized = true;\n    var blocks = this.blocks, i = this.lastByteIndex;\n    blocks[i >> 2] |= EXTRA[i & 3];\n    if (i >= 56) {\n      if (!this.hashed) {\n        this.hash();\n      }\n      blocks[0] = blocks[16];\n      blocks[16] = blocks[1] = blocks[2] = blocks[3] =\n      blocks[4] = blocks[5] = blocks[6] = blocks[7] =\n      blocks[8] = blocks[9] = blocks[10] = blocks[11] =\n      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;\n    }\n    blocks[14] = this.bytes << 3;\n    blocks[15] = this.hBytes << 3 | this.bytes >>> 29;\n    this.hash();\n  };\n\n  Md5.prototype.hash = function () {\n    var a, b, c, d, bc, da, blocks = this.blocks;\n\n    if (this.first) {\n      a = blocks[0] - 680876937;\n      a = (a << 7 | a >>> 25) - 271733879 << 0;\n      d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;\n      d = (d << 12 | d >>> 20) + a << 0;\n      c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;\n      c = (c << 17 | c >>> 15) + d << 0;\n      b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;\n      b = (b << 22 | b >>> 10) + c << 0;\n    } else {\n      a = this.h0;\n      b = this.h1;\n      c = this.h2;\n      d = this.h3;\n      a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;\n      a = (a << 7 | a >>> 25) + b << 0;\n      d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;\n      d = (d << 12 | d >>> 20) + a << 0;\n      c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;\n      c = (c << 17 | c >>> 15) + d << 0;\n      b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;\n      b = (b << 22 | b >>> 10) + c << 0;\n    }\n\n    a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;\n    a = (a << 7 | a >>> 25) + b << 0;\n    d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;\n    d = (d << 12 | d >>> 20) + a << 0;\n    c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;\n    c = (c << 17 | c >>> 15) + d << 0;\n    b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;\n    b = (b << 22 | b >>> 10) + c << 0;\n    a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;\n    a = (a << 7 | a >>> 25) + b << 0;\n    d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;\n    d = (d << 12 | d >>> 20) + a << 0;\n    c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;\n    c = (c << 17 | c >>> 15) + d << 0;\n    b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;\n    b = (b << 22 | b >>> 10) + c << 0;\n    a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;\n    a = (a << 7 | a >>> 25) + b << 0;\n    d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;\n    d = (d << 12 | d >>> 20) + a << 0;\n    c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;\n    c = (c << 17 | c >>> 15) + d << 0;\n    b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;\n    b = (b << 22 | b >>> 10) + c << 0;\n    a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;\n    a = (a << 5 | a >>> 27) + b << 0;\n    d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;\n    d = (d << 9 | d >>> 23) + a << 0;\n    c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;\n    c = (c << 14 | c >>> 18) + d << 0;\n    b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;\n    b = (b << 20 | b >>> 12) + c << 0;\n    a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;\n    a = (a << 5 | a >>> 27) + b << 0;\n    d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;\n    d = (d << 9 | d >>> 23) + a << 0;\n    c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;\n    c = (c << 14 | c >>> 18) + d << 0;\n    b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;\n    b = (b << 20 | b >>> 12) + c << 0;\n    a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;\n    a = (a << 5 | a >>> 27) + b << 0;\n    d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;\n    d = (d << 9 | d >>> 23) + a << 0;\n    c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;\n    c = (c << 14 | c >>> 18) + d << 0;\n    b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;\n    b = (b << 20 | b >>> 12) + c << 0;\n    a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;\n    a = (a << 5 | a >>> 27) + b << 0;\n    d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;\n    d = (d << 9 | d >>> 23) + a << 0;\n    c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;\n    c = (c << 14 | c >>> 18) + d << 0;\n    b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;\n    b = (b << 20 | b >>> 12) + c << 0;\n    bc = b ^ c;\n    a += (bc ^ d) + blocks[5] - 378558;\n    a = (a << 4 | a >>> 28) + b << 0;\n    d += (bc ^ a) + blocks[8] - 2022574463;\n    d = (d << 11 | d >>> 21) + a << 0;\n    da = d ^ a;\n    c += (da ^ b) + blocks[11] + 1839030562;\n    c = (c << 16 | c >>> 16) + d << 0;\n    b += (da ^ c) + blocks[14] - 35309556;\n    b = (b << 23 | b >>> 9) + c << 0;\n    bc = b ^ c;\n    a += (bc ^ d) + blocks[1] - 1530992060;\n    a = (a << 4 | a >>> 28) + b << 0;\n    d += (bc ^ a) + blocks[4] + 1272893353;\n    d = (d << 11 | d >>> 21) + a << 0;\n    da = d ^ a;\n    c += (da ^ b) + blocks[7] - 155497632;\n    c = (c << 16 | c >>> 16) + d << 0;\n    b += (da ^ c) + blocks[10] - 1094730640;\n    b = (b << 23 | b >>> 9) + c << 0;\n    bc = b ^ c;\n    a += (bc ^ d) + blocks[13] + 681279174;\n    a = (a << 4 | a >>> 28) + b << 0;\n    d += (bc ^ a) + blocks[0] - 358537222;\n    d = (d << 11 | d >>> 21) + a << 0;\n    da = d ^ a;\n    c += (da ^ b) + blocks[3] - 722521979;\n    c = (c << 16 | c >>> 16) + d << 0;\n    b += (da ^ c) + blocks[6] + 76029189;\n    b = (b << 23 | b >>> 9) + c << 0;\n    bc = b ^ c;\n    a += (bc ^ d) + blocks[9] - 640364487;\n    a = (a << 4 | a >>> 28) + b << 0;\n    d += (bc ^ a) + blocks[12] - 421815835;\n    d = (d << 11 | d >>> 21) + a << 0;\n    da = d ^ a;\n    c += (da ^ b) + blocks[15] + 530742520;\n    c = (c << 16 | c >>> 16) + d << 0;\n    b += (da ^ c) + blocks[2] - 995338651;\n    b = (b << 23 | b >>> 9) + c << 0;\n    a += (c ^ (b | ~d)) + blocks[0] - 198630844;\n    a = (a << 6 | a >>> 26) + b << 0;\n    d += (b ^ (a | ~c)) + blocks[7] + 1126891415;\n    d = (d << 10 | d >>> 22) + a << 0;\n    c += (a ^ (d | ~b)) + blocks[14] - 1416354905;\n    c = (c << 15 | c >>> 17) + d << 0;\n    b += (d ^ (c | ~a)) + blocks[5] - 57434055;\n    b = (b << 21 | b >>> 11) + c << 0;\n    a += (c ^ (b | ~d)) + blocks[12] + 1700485571;\n    a = (a << 6 | a >>> 26) + b << 0;\n    d += (b ^ (a | ~c)) + blocks[3] - 1894986606;\n    d = (d << 10 | d >>> 22) + a << 0;\n    c += (a ^ (d | ~b)) + blocks[10] - 1051523;\n    c = (c << 15 | c >>> 17) + d << 0;\n    b += (d ^ (c | ~a)) + blocks[1] - 2054922799;\n    b = (b << 21 | b >>> 11) + c << 0;\n    a += (c ^ (b | ~d)) + blocks[8] + 1873313359;\n    a = (a << 6 | a >>> 26) + b << 0;\n    d += (b ^ (a | ~c)) + blocks[15] - 30611744;\n    d = (d << 10 | d >>> 22) + a << 0;\n    c += (a ^ (d | ~b)) + blocks[6] - 1560198380;\n    c = (c << 15 | c >>> 17) + d << 0;\n    b += (d ^ (c | ~a)) + blocks[13] + 1309151649;\n    b = (b << 21 | b >>> 11) + c << 0;\n    a += (c ^ (b | ~d)) + blocks[4] - 145523070;\n    a = (a << 6 | a >>> 26) + b << 0;\n    d += (b ^ (a | ~c)) + blocks[11] - 1120210379;\n    d = (d << 10 | d >>> 22) + a << 0;\n    c += (a ^ (d | ~b)) + blocks[2] + 718787259;\n    c = (c << 15 | c >>> 17) + d << 0;\n    b += (d ^ (c | ~a)) + blocks[9] - 343485551;\n    b = (b << 21 | b >>> 11) + c << 0;\n\n    if (this.first) {\n      this.h0 = a + 1732584193 << 0;\n      this.h1 = b - 271733879 << 0;\n      this.h2 = c - 1732584194 << 0;\n      this.h3 = d + 271733878 << 0;\n      this.first = false;\n    } else {\n      this.h0 = this.h0 + a << 0;\n      this.h1 = this.h1 + b << 0;\n      this.h2 = this.h2 + c << 0;\n      this.h3 = this.h3 + d << 0;\n    }\n  };\n\n  /**\n   * @method hex\n   * @memberof Md5\n   * @instance\n   * @description Output hash as hex string\n   * @returns {String} Hex string\n   * @see {@link md5.hex}\n   * @example\n   * hash.hex();\n   */\n  Md5.prototype.hex = function () {\n    this.finalize();\n\n    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;\n\n    return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +\n      HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +\n      HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +\n      HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +\n      HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +\n      HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +\n      HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +\n      HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +\n      HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +\n      HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +\n      HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +\n      HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +\n      HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +\n      HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +\n      HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +\n      HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];\n  };\n\n  /**\n   * @method toString\n   * @memberof Md5\n   * @instance\n   * @description Output hash as hex string\n   * @returns {String} Hex string\n   * @see {@link md5.hex}\n   * @example\n   * hash.toString();\n   */\n  Md5.prototype.toString = Md5.prototype.hex;\n\n  /**\n   * @method digest\n   * @memberof Md5\n   * @instance\n   * @description Output hash as bytes array\n   * @returns {Array} Bytes array\n   * @see {@link md5.digest}\n   * @example\n   * hash.digest();\n   */\n  Md5.prototype.digest = function () {\n    this.finalize();\n\n    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;\n    return [\n      h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,\n      h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,\n      h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,\n      h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF\n    ];\n  };\n\n  /**\n   * @method array\n   * @memberof Md5\n   * @instance\n   * @description Output hash as bytes array\n   * @returns {Array} Bytes array\n   * @see {@link md5.array}\n   * @example\n   * hash.array();\n   */\n  Md5.prototype.array = Md5.prototype.digest;\n\n  /**\n   * @method arrayBuffer\n   * @memberof Md5\n   * @instance\n   * @description Output hash as ArrayBuffer\n   * @returns {ArrayBuffer} ArrayBuffer\n   * @see {@link md5.arrayBuffer}\n   * @example\n   * hash.arrayBuffer();\n   */\n  Md5.prototype.arrayBuffer = function () {\n    this.finalize();\n\n    var buffer = new ArrayBuffer(16);\n    var blocks = new Uint32Array(buffer);\n    blocks[0] = this.h0;\n    blocks[1] = this.h1;\n    blocks[2] = this.h2;\n    blocks[3] = this.h3;\n    return buffer;\n  };\n\n  /**\n   * @method buffer\n   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.\n   * @memberof Md5\n   * @instance\n   * @description Output hash as ArrayBuffer\n   * @returns {ArrayBuffer} ArrayBuffer\n   * @see {@link md5.buffer}\n   * @example\n   * hash.buffer();\n   */\n  Md5.prototype.buffer = Md5.prototype.arrayBuffer;\n\n  /**\n   * @method base64\n   * @memberof Md5\n   * @instance\n   * @description Output hash as base64 string\n   * @returns {String} base64 string\n   * @see {@link md5.base64}\n   * @example\n   * hash.base64();\n   */\n  Md5.prototype.base64 = function () {\n    var v1, v2, v3, base64Str = '', bytes = this.array();\n    for (var i = 0; i < 15;) {\n      v1 = bytes[i++];\n      v2 = bytes[i++];\n      v3 = bytes[i++];\n      base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +\n        BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +\n        BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +\n        BASE64_ENCODE_CHAR[v3 & 63];\n    }\n    v1 = bytes[i];\n    base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +\n      BASE64_ENCODE_CHAR[(v1 << 4) & 63] +\n      '==';\n    return base64Str;\n  };\n\n  var exports = createMethod();\n\n  if (COMMON_JS) {\n    module.exports = exports;\n  } else {\n    /**\n     * @method md5\b\n     * @description Md5 hash function, export to global in browsers.\n     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash\n     * @returns {String} md5 hashes\n     * @example\n     * md5(''); // d41d8cd98f00b204e9800998ecf8427e\n     * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6\n     * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0\n     *\n     * // It also supports UTF-8 encoding\n     * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07\n     *\n     * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`\n     * md5([]); // d41d8cd98f00b204e9800998ecf8427e\n     * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e\n     */\n    root.md5 = exports;\n    if (AMD) {\n      define(function () {\n        return exports;\n      });\n    }\n  }\n})();\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n    arrayTag = '[object Array]',\n    boolTag = '[object Boolean]',\n    dateTag = '[object Date]',\n    errorTag = '[object Error]',\n    funcTag = '[object Function]',\n    genTag = '[object GeneratorFunction]',\n    mapTag = '[object Map]',\n    numberTag = '[object Number]',\n    objectTag = '[object Object]',\n    promiseTag = '[object Promise]',\n    regexpTag = '[object RegExp]',\n    setTag = '[object Set]',\n    stringTag = '[object String]',\n    symbolTag = '[object Symbol]',\n    weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n    dataViewTag = '[object DataView]',\n    float32Tag = '[object Float32Array]',\n    float64Tag = '[object Float64Array]',\n    int8Tag = '[object Int8Array]',\n    int16Tag = '[object Int16Array]',\n    int32Tag = '[object Int32Array]',\n    uint8Tag = '[object Uint8Array]',\n    uint8ClampedTag = '[object Uint8ClampedArray]',\n    uint16Tag = '[object Uint16Array]',\n    uint32Tag = '[object Uint32Array]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to match `RegExp` flags from their coerced string values. */\nvar reFlags = /\\w*$/;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values supported by `_.clone`. */\nvar cloneableTags = {};\ncloneableTags[argsTag] = cloneableTags[arrayTag] =\ncloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\ncloneableTags[boolTag] = cloneableTags[dateTag] =\ncloneableTags[float32Tag] = cloneableTags[float64Tag] =\ncloneableTags[int8Tag] = cloneableTags[int16Tag] =\ncloneableTags[int32Tag] = cloneableTags[mapTag] =\ncloneableTags[numberTag] = cloneableTags[objectTag] =\ncloneableTags[regexpTag] = cloneableTags[setTag] =\ncloneableTags[stringTag] = cloneableTags[symbolTag] =\ncloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\ncloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\ncloneableTags[errorTag] = cloneableTags[funcTag] =\ncloneableTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/**\n * Adds the key-value `pair` to `map`.\n *\n * @private\n * @param {Object} map The map to modify.\n * @param {Array} pair The key-value pair to add.\n * @returns {Object} Returns `map`.\n */\nfunction addMapEntry(map, pair) {\n  // Don't return `map.set` because it's not chainable in IE 11.\n  map.set(pair[0], pair[1]);\n  return map;\n}\n\n/**\n * Adds `value` to `set`.\n *\n * @private\n * @param {Object} set The set to modify.\n * @param {*} value The value to add.\n * @returns {Object} Returns `set`.\n */\nfunction addSetEntry(set, value) {\n  // Don't return `set.add` because it's not chainable in IE 11.\n  set.add(value);\n  return set;\n}\n\n/**\n * A specialized version of `_.forEach` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns `array`.\n */\nfunction arrayEach(array, iteratee) {\n  var index = -1,\n      length = array ? array.length : 0;\n\n  while (++index < length) {\n    if (iteratee(array[index], index, array) === false) {\n      break;\n    }\n  }\n  return array;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n  var index = -1,\n      length = values.length,\n      offset = array.length;\n\n  while (++index < length) {\n    array[offset + index] = values[index];\n  }\n  return array;\n}\n\n/**\n * A specialized version of `_.reduce` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} iteratee The function invoked per iteration.\n * @param {*} [accumulator] The initial value.\n * @param {boolean} [initAccum] Specify using the first element of `array` as\n *  the initial value.\n * @returns {*} Returns the accumulated value.\n */\nfunction arrayReduce(array, iteratee, accumulator, initAccum) {\n  var index = -1,\n      length = array ? array.length : 0;\n\n  if (initAccum && length) {\n    accumulator = array[++index];\n  }\n  while (++index < length) {\n    accumulator = iteratee(accumulator, array[index], index, array);\n  }\n  return accumulator;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n  var index = -1,\n      result = Array(n);\n\n  while (++index < n) {\n    result[index] = iteratee(index);\n  }\n  return result;\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n  return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n  // Many host objects are `Object` objects that can coerce to strings\n  // despite having improperly defined `toString` methods.\n  var result = false;\n  if (value != null && typeof value.toString != 'function') {\n    try {\n      result = !!(value + '');\n    } catch (e) {}\n  }\n  return result;\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n  var index = -1,\n      result = Array(map.size);\n\n  map.forEach(function(value, key) {\n    result[++index] = [key, value];\n  });\n  return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n  return function(arg) {\n    return func(transform(arg));\n  };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n  var index = -1,\n      result = Array(set.size);\n\n  set.forEach(function(value) {\n    result[++index] = value;\n  });\n  return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n    funcProto = Function.prototype,\n    objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n  return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n  .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n    Symbol = root.Symbol,\n    Uint8Array = root.Uint8Array,\n    getPrototype = overArg(Object.getPrototypeOf, Object),\n    objectCreate = Object.create,\n    propertyIsEnumerable = objectProto.propertyIsEnumerable,\n    splice = arrayProto.splice;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n    nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n    Map = getNative(root, 'Map'),\n    Promise = getNative(root, 'Promise'),\n    Set = getNative(root, 'Set'),\n    WeakMap = getNative(root, 'WeakMap'),\n    nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n    mapCtorString = toSource(Map),\n    promiseCtorString = toSource(Promise),\n    setCtorString = toSource(Set),\n    weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n  this.__data__ = nativeCreate ? nativeCreate(null) : {};\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n  return this.has(key) && delete this.__data__[key];\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n  var data = this.__data__;\n  if (nativeCreate) {\n    var result = data[key];\n    return result === HASH_UNDEFINED ? undefined : result;\n  }\n  return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n  var data = this.__data__;\n  return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n  var data = this.__data__;\n  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n  return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n  this.__data__ = [];\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    return false;\n  }\n  var lastIndex = data.length - 1;\n  if (index == lastIndex) {\n    data.pop();\n  } else {\n    splice.call(data, index, 1);\n  }\n  return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n  return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    data.push([key, value]);\n  } else {\n    data[index][1] = value;\n  }\n  return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n  var index = -1,\n      length = entries ? entries.length : 0;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n  this.__data__ = {\n    'hash': new Hash,\n    'map': new (Map || ListCache),\n    'string': new Hash\n  };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n  return getMapData(this, key)['delete'](key);\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n  return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n  return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n  getMapData(this, key).set(key, value);\n  return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n  this.__data__ = new ListCache(entries);\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n  this.__data__ = new ListCache;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n  return this.__data__['delete'](key);\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n  return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n  return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n  var cache = this.__data__;\n  if (cache instanceof ListCache) {\n    var pairs = cache.__data__;\n    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n      pairs.push([key, value]);\n      return this;\n    }\n    cache = this.__data__ = new MapCache(pairs);\n  }\n  cache.set(key, value);\n  return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n  // Safari 9 makes `arguments.length` enumerable in strict mode.\n  var result = (isArray(value) || isArguments(value))\n    ? baseTimes(value.length, String)\n    : [];\n\n  var length = result.length,\n      skipIndexes = !!length;\n\n  for (var key in value) {\n    if ((inherited || hasOwnProperty.call(value, key)) &&\n        !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * Assigns `value` to `key` of `object` if the existing value is not equivalent\n * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * for equality comparisons.\n *\n * @private\n * @param {Object} object The object to modify.\n * @param {string} key The key of the property to assign.\n * @param {*} value The value to assign.\n */\nfunction assignValue(object, key, value) {\n  var objValue = object[key];\n  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n      (value === undefined && !(key in object))) {\n    object[key] = value;\n  }\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n  var length = array.length;\n  while (length--) {\n    if (eq(array[length][0], key)) {\n      return length;\n    }\n  }\n  return -1;\n}\n\n/**\n * The base implementation of `_.assign` without support for multiple sources\n * or `customizer` functions.\n *\n * @private\n * @param {Object} object The destination object.\n * @param {Object} source The source object.\n * @returns {Object} Returns `object`.\n */\nfunction baseAssign(object, source) {\n  return object && copyObject(source, keys(source), object);\n}\n\n/**\n * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n * traversed objects.\n *\n * @private\n * @param {*} value The value to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @param {boolean} [isFull] Specify a clone including symbols.\n * @param {Function} [customizer] The function to customize cloning.\n * @param {string} [key] The key of `value`.\n * @param {Object} [object] The parent object of `value`.\n * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n * @returns {*} Returns the cloned value.\n */\nfunction baseClone(value, isDeep, isFull, customizer, key, object, stack) {\n  var result;\n  if (customizer) {\n    result = object ? customizer(value, key, object, stack) : customizer(value);\n  }\n  if (result !== undefined) {\n    return result;\n  }\n  if (!isObject(value)) {\n    return value;\n  }\n  var isArr = isArray(value);\n  if (isArr) {\n    result = initCloneArray(value);\n    if (!isDeep) {\n      return copyArray(value, result);\n    }\n  } else {\n    var tag = getTag(value),\n        isFunc = tag == funcTag || tag == genTag;\n\n    if (isBuffer(value)) {\n      return cloneBuffer(value, isDeep);\n    }\n    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n      if (isHostObject(value)) {\n        return object ? value : {};\n      }\n      result = initCloneObject(isFunc ? {} : value);\n      if (!isDeep) {\n        return copySymbols(value, baseAssign(result, value));\n      }\n    } else {\n      if (!cloneableTags[tag]) {\n        return object ? value : {};\n      }\n      result = initCloneByTag(value, tag, baseClone, isDeep);\n    }\n  }\n  // Check for circular references and return its corresponding clone.\n  stack || (stack = new Stack);\n  var stacked = stack.get(value);\n  if (stacked) {\n    return stacked;\n  }\n  stack.set(value, result);\n\n  if (!isArr) {\n    var props = isFull ? getAllKeys(value) : keys(value);\n  }\n  arrayEach(props || value, function(subValue, key) {\n    if (props) {\n      key = subValue;\n      subValue = value[key];\n    }\n    // Recursively populate clone (susceptible to call stack limits).\n    assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));\n  });\n  return result;\n}\n\n/**\n * The base implementation of `_.create` without support for assigning\n * properties to the created object.\n *\n * @private\n * @param {Object} prototype The object to inherit from.\n * @returns {Object} Returns the new object.\n */\nfunction baseCreate(proto) {\n  return isObject(proto) ? objectCreate(proto) : {};\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n  var result = keysFunc(object);\n  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `getTag`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n  return objectToString.call(value);\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n *  else `false`.\n */\nfunction baseIsNative(value) {\n  if (!isObject(value) || isMasked(value)) {\n    return false;\n  }\n  var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n  return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n  if (!isPrototype(object)) {\n    return nativeKeys(object);\n  }\n  var result = [];\n  for (var key in Object(object)) {\n    if (hasOwnProperty.call(object, key) && key != 'constructor') {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * Creates a clone of  `buffer`.\n *\n * @private\n * @param {Buffer} buffer The buffer to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Buffer} Returns the cloned buffer.\n */\nfunction cloneBuffer(buffer, isDeep) {\n  if (isDeep) {\n    return buffer.slice();\n  }\n  var result = new buffer.constructor(buffer.length);\n  buffer.copy(result);\n  return result;\n}\n\n/**\n * Creates a clone of `arrayBuffer`.\n *\n * @private\n * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n * @returns {ArrayBuffer} Returns the cloned array buffer.\n */\nfunction cloneArrayBuffer(arrayBuffer) {\n  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n  new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n  return result;\n}\n\n/**\n * Creates a clone of `dataView`.\n *\n * @private\n * @param {Object} dataView The data view to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned data view.\n */\nfunction cloneDataView(dataView, isDeep) {\n  var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n}\n\n/**\n * Creates a clone of `map`.\n *\n * @private\n * @param {Object} map The map to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned map.\n */\nfunction cloneMap(map, isDeep, cloneFunc) {\n  var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);\n  return arrayReduce(array, addMapEntry, new map.constructor);\n}\n\n/**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\nfunction cloneRegExp(regexp) {\n  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n  result.lastIndex = regexp.lastIndex;\n  return result;\n}\n\n/**\n * Creates a clone of `set`.\n *\n * @private\n * @param {Object} set The set to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned set.\n */\nfunction cloneSet(set, isDeep, cloneFunc) {\n  var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);\n  return arrayReduce(array, addSetEntry, new set.constructor);\n}\n\n/**\n * Creates a clone of the `symbol` object.\n *\n * @private\n * @param {Object} symbol The symbol object to clone.\n * @returns {Object} Returns the cloned symbol object.\n */\nfunction cloneSymbol(symbol) {\n  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n}\n\n/**\n * Creates a clone of `typedArray`.\n *\n * @private\n * @param {Object} typedArray The typed array to clone.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the cloned typed array.\n */\nfunction cloneTypedArray(typedArray, isDeep) {\n  var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n}\n\n/**\n * Copies the values of `source` to `array`.\n *\n * @private\n * @param {Array} source The array to copy values from.\n * @param {Array} [array=[]] The array to copy values to.\n * @returns {Array} Returns `array`.\n */\nfunction copyArray(source, array) {\n  var index = -1,\n      length = source.length;\n\n  array || (array = Array(length));\n  while (++index < length) {\n    array[index] = source[index];\n  }\n  return array;\n}\n\n/**\n * Copies properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy properties from.\n * @param {Array} props The property identifiers to copy.\n * @param {Object} [object={}] The object to copy properties to.\n * @param {Function} [customizer] The function to customize copied values.\n * @returns {Object} Returns `object`.\n */\nfunction copyObject(source, props, object, customizer) {\n  object || (object = {});\n\n  var index = -1,\n      length = props.length;\n\n  while (++index < length) {\n    var key = props[index];\n\n    var newValue = customizer\n      ? customizer(object[key], source[key], key, object, source)\n      : undefined;\n\n    assignValue(object, key, newValue === undefined ? source[key] : newValue);\n  }\n  return object;\n}\n\n/**\n * Copies own symbol properties of `source` to `object`.\n *\n * @private\n * @param {Object} source The object to copy symbols from.\n * @param {Object} [object={}] The object to copy symbols to.\n * @returns {Object} Returns `object`.\n */\nfunction copySymbols(source, object) {\n  return copyObject(source, getSymbols(source), object);\n}\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n  return baseGetAllKeys(object, keys, getSymbols);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n  var data = map.__data__;\n  return isKeyable(key)\n    ? data[typeof key == 'string' ? 'string' : 'hash']\n    : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n  var value = getValue(object, key);\n  return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Creates an array of the own enumerable symbol properties of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray;\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11,\n// for data views in Edge < 14, and promises in Node.js.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n    (Map && getTag(new Map) != mapTag) ||\n    (Promise && getTag(Promise.resolve()) != promiseTag) ||\n    (Set && getTag(new Set) != setTag) ||\n    (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n  getTag = function(value) {\n    var result = objectToString.call(value),\n        Ctor = result == objectTag ? value.constructor : undefined,\n        ctorString = Ctor ? toSource(Ctor) : undefined;\n\n    if (ctorString) {\n      switch (ctorString) {\n        case dataViewCtorString: return dataViewTag;\n        case mapCtorString: return mapTag;\n        case promiseCtorString: return promiseTag;\n        case setCtorString: return setTag;\n        case weakMapCtorString: return weakMapTag;\n      }\n    }\n    return result;\n  };\n}\n\n/**\n * Initializes an array clone.\n *\n * @private\n * @param {Array} array The array to clone.\n * @returns {Array} Returns the initialized clone.\n */\nfunction initCloneArray(array) {\n  var length = array.length,\n      result = array.constructor(length);\n\n  // Add properties assigned by `RegExp#exec`.\n  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n    result.index = array.index;\n    result.input = array.input;\n  }\n  return result;\n}\n\n/**\n * Initializes an object clone.\n *\n * @private\n * @param {Object} object The object to clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneObject(object) {\n  return (typeof object.constructor == 'function' && !isPrototype(object))\n    ? baseCreate(getPrototype(object))\n    : {};\n}\n\n/**\n * Initializes an object clone based on its `toStringTag`.\n *\n * **Note:** This function only supports cloning values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to clone.\n * @param {string} tag The `toStringTag` of the object to clone.\n * @param {Function} cloneFunc The function to clone values.\n * @param {boolean} [isDeep] Specify a deep clone.\n * @returns {Object} Returns the initialized clone.\n */\nfunction initCloneByTag(object, tag, cloneFunc, isDeep) {\n  var Ctor = object.constructor;\n  switch (tag) {\n    case arrayBufferTag:\n      return cloneArrayBuffer(object);\n\n    case boolTag:\n    case dateTag:\n      return new Ctor(+object);\n\n    case dataViewTag:\n      return cloneDataView(object, isDeep);\n\n    case float32Tag: case float64Tag:\n    case int8Tag: case int16Tag: case int32Tag:\n    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n      return cloneTypedArray(object, isDeep);\n\n    case mapTag:\n      return cloneMap(object, isDeep, cloneFunc);\n\n    case numberTag:\n    case stringTag:\n      return new Ctor(object);\n\n    case regexpTag:\n      return cloneRegExp(object);\n\n    case setTag:\n      return cloneSet(object, isDeep, cloneFunc);\n\n    case symbolTag:\n      return cloneSymbol(object);\n  }\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n  length = length == null ? MAX_SAFE_INTEGER : length;\n  return !!length &&\n    (typeof value == 'number' || reIsUint.test(value)) &&\n    (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n  var type = typeof value;\n  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n    ? (value !== '__proto__')\n    : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n  return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n  var Ctor = value && value.constructor,\n      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n  return value === proto;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n  if (func != null) {\n    try {\n      return funcToString.call(func);\n    } catch (e) {}\n    try {\n      return (func + '');\n    } catch (e) {}\n  }\n  return '';\n}\n\n/**\n * This method is like `_.clone` except that it recursively clones `value`.\n *\n * @static\n * @memberOf _\n * @since 1.0.0\n * @category Lang\n * @param {*} value The value to recursively clone.\n * @returns {*} Returns the deep cloned value.\n * @see _.clone\n * @example\n *\n * var objects = [{ 'a': 1 }, { 'b': 2 }];\n *\n * var deep = _.cloneDeep(objects);\n * console.log(deep[0] === objects[0]);\n * // => false\n */\nfunction cloneDeep(value) {\n  return baseClone(value, true, true);\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n  return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n *  else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n  // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n  return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n    (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n  return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n *  else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n  return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n  // The use of `Object#toString` avoids issues with the `typeof` operator\n  // in Safari 8-9 which returns 'object' for typed array and other constructors.\n  var tag = isObject(value) ? objectToString.call(value) : '';\n  return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n  return typeof value == 'number' &&\n    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n  var type = typeof value;\n  return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n  return !!value && typeof value == 'object';\n}\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n *   this.a = 1;\n *   this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n  return [];\n}\n\n/**\n * This method returns `false`.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {boolean} Returns `false`.\n * @example\n *\n * _.times(2, _.stubFalse);\n * // => [false, false]\n */\nfunction stubFalse() {\n  return false;\n}\n\nmodule.exports = cloneDeep;\n","/**\n * lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used as references for various `Number` constants. */\nvar NAN = 0 / 0;\n\n/** `Object#toString` result references. */\nvar symbolTag = '[object Symbol]';\n\n/** Used to match leading and trailing whitespace. */\nvar reTrim = /^\\s+|\\s+$/g;\n\n/** Used to detect bad signed hexadecimal string values. */\nvar reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n/** Used to detect binary string values. */\nvar reIsBinary = /^0b[01]+$/i;\n\n/** Used to detect octal string values. */\nvar reIsOctal = /^0o[0-7]+$/i;\n\n/** Built-in method references without a dependency on `root`. */\nvar freeParseInt = parseInt;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Used for built-in method references. */\nvar objectProto = Object.prototype;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeMax = Math.max,\n    nativeMin = Math.min;\n\n/**\n * Gets the timestamp of the number of milliseconds that have elapsed since\n * the Unix epoch (1 January 1970 00:00:00 UTC).\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Date\n * @returns {number} Returns the timestamp.\n * @example\n *\n * _.defer(function(stamp) {\n *   console.log(_.now() - stamp);\n * }, _.now());\n * // => Logs the number of milliseconds it took for the deferred invocation.\n */\nvar now = function() {\n  return root.Date.now();\n};\n\n/**\n * Creates a debounced function that delays invoking `func` until after `wait`\n * milliseconds have elapsed since the last time the debounced function was\n * invoked. The debounced function comes with a `cancel` method to cancel\n * delayed `func` invocations and a `flush` method to immediately invoke them.\n * Provide `options` to indicate whether `func` should be invoked on the\n * leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n * with the last arguments provided to the debounced function. Subsequent\n * calls to the debounced function return the result of the last `func`\n * invocation.\n *\n * **Note:** If `leading` and `trailing` options are `true`, `func` is\n * invoked on the trailing edge of the timeout only if the debounced function\n * is invoked more than once during the `wait` timeout.\n *\n * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred\n * until to the next tick, similar to `setTimeout` with a timeout of `0`.\n *\n * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n * for details over the differences between `_.debounce` and `_.throttle`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to debounce.\n * @param {number} [wait=0] The number of milliseconds to delay.\n * @param {Object} [options={}] The options object.\n * @param {boolean} [options.leading=false]\n *  Specify invoking on the leading edge of the timeout.\n * @param {number} [options.maxWait]\n *  The maximum time `func` is allowed to be delayed before it's invoked.\n * @param {boolean} [options.trailing=true]\n *  Specify invoking on the trailing edge of the timeout.\n * @returns {Function} Returns the new debounced function.\n * @example\n *\n * // Avoid costly calculations while the window size is in flux.\n * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n *\n * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n * jQuery(element).on('click', _.debounce(sendMail, 300, {\n *   'leading': true,\n *   'trailing': false\n * }));\n *\n * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n * var source = new EventSource('/stream');\n * jQuery(source).on('message', debounced);\n *\n * // Cancel the trailing debounced invocation.\n * jQuery(window).on('popstate', debounced.cancel);\n */\nfunction debounce(func, wait, options) {\n  var lastArgs,\n      lastThis,\n      maxWait,\n      result,\n      timerId,\n      lastCallTime,\n      lastInvokeTime = 0,\n      leading = false,\n      maxing = false,\n      trailing = true;\n\n  if (typeof func != 'function') {\n    throw new TypeError(FUNC_ERROR_TEXT);\n  }\n  wait = toNumber(wait) || 0;\n  if (isObject(options)) {\n    leading = !!options.leading;\n    maxing = 'maxWait' in options;\n    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n    trailing = 'trailing' in options ? !!options.trailing : trailing;\n  }\n\n  function invokeFunc(time) {\n    var args = lastArgs,\n        thisArg = lastThis;\n\n    lastArgs = lastThis = undefined;\n    lastInvokeTime = time;\n    result = func.apply(thisArg, args);\n    return result;\n  }\n\n  function leadingEdge(time) {\n    // Reset any `maxWait` timer.\n    lastInvokeTime = time;\n    // Start the timer for the trailing edge.\n    timerId = setTimeout(timerExpired, wait);\n    // Invoke the leading edge.\n    return leading ? invokeFunc(time) : result;\n  }\n\n  function remainingWait(time) {\n    var timeSinceLastCall = time - lastCallTime,\n        timeSinceLastInvoke = time - lastInvokeTime,\n        result = wait - timeSinceLastCall;\n\n    return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n  }\n\n  function shouldInvoke(time) {\n    var timeSinceLastCall = time - lastCallTime,\n        timeSinceLastInvoke = time - lastInvokeTime;\n\n    // Either this is the first call, activity has stopped and we're at the\n    // trailing edge, the system time has gone backwards and we're treating\n    // it as the trailing edge, or we've hit the `maxWait` limit.\n    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||\n      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n  }\n\n  function timerExpired() {\n    var time = now();\n    if (shouldInvoke(time)) {\n      return trailingEdge(time);\n    }\n    // Restart the timer.\n    timerId = setTimeout(timerExpired, remainingWait(time));\n  }\n\n  function trailingEdge(time) {\n    timerId = undefined;\n\n    // Only invoke if we have `lastArgs` which means `func` has been\n    // debounced at least once.\n    if (trailing && lastArgs) {\n      return invokeFunc(time);\n    }\n    lastArgs = lastThis = undefined;\n    return result;\n  }\n\n  function cancel() {\n    if (timerId !== undefined) {\n      clearTimeout(timerId);\n    }\n    lastInvokeTime = 0;\n    lastArgs = lastCallTime = lastThis = timerId = undefined;\n  }\n\n  function flush() {\n    return timerId === undefined ? result : trailingEdge(now());\n  }\n\n  function debounced() {\n    var time = now(),\n        isInvoking = shouldInvoke(time);\n\n    lastArgs = arguments;\n    lastThis = this;\n    lastCallTime = time;\n\n    if (isInvoking) {\n      if (timerId === undefined) {\n        return leadingEdge(lastCallTime);\n      }\n      if (maxing) {\n        // Handle invocations in a tight loop.\n        timerId = setTimeout(timerExpired, wait);\n        return invokeFunc(lastCallTime);\n      }\n    }\n    if (timerId === undefined) {\n      timerId = setTimeout(timerExpired, wait);\n    }\n    return result;\n  }\n  debounced.cancel = cancel;\n  debounced.flush = flush;\n  return debounced;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n  var type = typeof value;\n  return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n  return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n  return typeof value == 'symbol' ||\n    (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Converts `value` to a number.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {number} Returns the number.\n * @example\n *\n * _.toNumber(3.2);\n * // => 3.2\n *\n * _.toNumber(Number.MIN_VALUE);\n * // => 5e-324\n *\n * _.toNumber(Infinity);\n * // => Infinity\n *\n * _.toNumber('3.2');\n * // => 3.2\n */\nfunction toNumber(value) {\n  if (typeof value == 'number') {\n    return value;\n  }\n  if (isSymbol(value)) {\n    return NAN;\n  }\n  if (isObject(value)) {\n    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;\n    value = isObject(other) ? (other + '') : other;\n  }\n  if (typeof value != 'string') {\n    return value === 0 ? value : +value;\n  }\n  value = value.replace(reTrim, '');\n  var isBinary = reIsBinary.test(value);\n  return (isBinary || reIsOctal.test(value))\n    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n    : (reIsBadHex.test(value) ? NAN : +value);\n}\n\nmodule.exports = debounce;\n","/**\n * Lodash (Custom Build) <https://lodash.com/>\n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright JS Foundation and other contributors <https://js.foundation/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used to compose bitmasks for value comparisons. */\nvar COMPARE_PARTIAL_FLAG = 1,\n    COMPARE_UNORDERED_FLAG = 2;\n\n/** Used as references for various `Number` constants. */\nvar MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n    arrayTag = '[object Array]',\n    asyncTag = '[object AsyncFunction]',\n    boolTag = '[object Boolean]',\n    dateTag = '[object Date]',\n    errorTag = '[object Error]',\n    funcTag = '[object Function]',\n    genTag = '[object GeneratorFunction]',\n    mapTag = '[object Map]',\n    numberTag = '[object Number]',\n    nullTag = '[object Null]',\n    objectTag = '[object Object]',\n    promiseTag = '[object Promise]',\n    proxyTag = '[object Proxy]',\n    regexpTag = '[object RegExp]',\n    setTag = '[object Set]',\n    stringTag = '[object String]',\n    symbolTag = '[object Symbol]',\n    undefinedTag = '[object Undefined]',\n    weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n    dataViewTag = '[object DataView]',\n    float32Tag = '[object Float32Array]',\n    float64Tag = '[object Float64Array]',\n    int8Tag = '[object Int8Array]',\n    int16Tag = '[object Int16Array]',\n    int32Tag = '[object Int32Array]',\n    uint8Tag = '[object Uint8Array]',\n    uint8ClampedTag = '[object Uint8ClampedArray]',\n    uint16Tag = '[object Uint16Array]',\n    uint32Tag = '[object Uint32Array]';\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values of typed arrays. */\nvar typedArrayTags = {};\ntypedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\ntypedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\ntypedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\ntypedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\ntypedArrayTags[uint32Tag] = true;\ntypedArrayTags[argsTag] = typedArrayTags[arrayTag] =\ntypedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\ntypedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\ntypedArrayTags[errorTag] = typedArrayTags[funcTag] =\ntypedArrayTags[mapTag] = typedArrayTags[numberTag] =\ntypedArrayTags[objectTag] = typedArrayTags[regexpTag] =\ntypedArrayTags[setTag] = typedArrayTags[stringTag] =\ntypedArrayTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Detect free variable `process` from Node.js. */\nvar freeProcess = moduleExports && freeGlobal.process;\n\n/** Used to access faster Node.js helpers. */\nvar nodeUtil = (function() {\n  try {\n    return freeProcess && freeProcess.binding && freeProcess.binding('util');\n  } catch (e) {}\n}());\n\n/* Node.js helper references. */\nvar nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n/**\n * A specialized version of `_.filter` for arrays without support for\n * iteratee shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {Array} Returns the new filtered array.\n */\nfunction arrayFilter(array, predicate) {\n  var index = -1,\n      length = array == null ? 0 : array.length,\n      resIndex = 0,\n      result = [];\n\n  while (++index < length) {\n    var value = array[index];\n    if (predicate(value, index, array)) {\n      result[resIndex++] = value;\n    }\n  }\n  return result;\n}\n\n/**\n * Appends the elements of `values` to `array`.\n *\n * @private\n * @param {Array} array The array to modify.\n * @param {Array} values The values to append.\n * @returns {Array} Returns `array`.\n */\nfunction arrayPush(array, values) {\n  var index = -1,\n      length = values.length,\n      offset = array.length;\n\n  while (++index < length) {\n    array[offset + index] = values[index];\n  }\n  return array;\n}\n\n/**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n *  else `false`.\n */\nfunction arraySome(array, predicate) {\n  var index = -1,\n      length = array == null ? 0 : array.length;\n\n  while (++index < length) {\n    if (predicate(array[index], index, array)) {\n      return true;\n    }\n  }\n  return false;\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n  var index = -1,\n      result = Array(n);\n\n  while (++index < n) {\n    result[index] = iteratee(index);\n  }\n  return result;\n}\n\n/**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\nfunction baseUnary(func) {\n  return function(value) {\n    return func(value);\n  };\n}\n\n/**\n * Checks if a `cache` value for `key` exists.\n *\n * @private\n * @param {Object} cache The cache to query.\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction cacheHas(cache, key) {\n  return cache.has(key);\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n  return object == null ? undefined : object[key];\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n  var index = -1,\n      result = Array(map.size);\n\n  map.forEach(function(value, key) {\n    result[++index] = [key, value];\n  });\n  return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n  return function(arg) {\n    return func(transform(arg));\n  };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n  var index = -1,\n      result = Array(set.size);\n\n  set.forEach(function(value) {\n    result[++index] = value;\n  });\n  return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n    funcProto = Function.prototype,\n    objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n  return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar nativeObjectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n  .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Buffer = moduleExports ? root.Buffer : undefined,\n    Symbol = root.Symbol,\n    Uint8Array = root.Uint8Array,\n    propertyIsEnumerable = objectProto.propertyIsEnumerable,\n    splice = arrayProto.splice,\n    symToStringTag = Symbol ? Symbol.toStringTag : undefined;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeGetSymbols = Object.getOwnPropertySymbols,\n    nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined,\n    nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n    Map = getNative(root, 'Map'),\n    Promise = getNative(root, 'Promise'),\n    Set = getNative(root, 'Set'),\n    WeakMap = getNative(root, 'WeakMap'),\n    nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n    mapCtorString = toSource(Map),\n    promiseCtorString = toSource(Promise),\n    setCtorString = toSource(Set),\n    weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n  var index = -1,\n      length = entries == null ? 0 : entries.length;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n  this.__data__ = nativeCreate ? nativeCreate(null) : {};\n  this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n  var result = this.has(key) && delete this.__data__[key];\n  this.size -= result ? 1 : 0;\n  return result;\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n  var data = this.__data__;\n  if (nativeCreate) {\n    var result = data[key];\n    return result === HASH_UNDEFINED ? undefined : result;\n  }\n  return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n  var data = this.__data__;\n  return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n  var data = this.__data__;\n  this.size += this.has(key) ? 0 : 1;\n  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n  return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n  var index = -1,\n      length = entries == null ? 0 : entries.length;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n  this.__data__ = [];\n  this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    return false;\n  }\n  var lastIndex = data.length - 1;\n  if (index == lastIndex) {\n    data.pop();\n  } else {\n    splice.call(data, index, 1);\n  }\n  --this.size;\n  return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n  return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n  var data = this.__data__,\n      index = assocIndexOf(data, key);\n\n  if (index < 0) {\n    ++this.size;\n    data.push([key, value]);\n  } else {\n    data[index][1] = value;\n  }\n  return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n  var index = -1,\n      length = entries == null ? 0 : entries.length;\n\n  this.clear();\n  while (++index < length) {\n    var entry = entries[index];\n    this.set(entry[0], entry[1]);\n  }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n  this.size = 0;\n  this.__data__ = {\n    'hash': new Hash,\n    'map': new (Map || ListCache),\n    'string': new Hash\n  };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n  var result = getMapData(this, key)['delete'](key);\n  this.size -= result ? 1 : 0;\n  return result;\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n  return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n  return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n  var data = getMapData(this, key),\n      size = data.size;\n\n  data.set(key, value);\n  this.size += data.size == size ? 0 : 1;\n  return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\nfunction SetCache(values) {\n  var index = -1,\n      length = values == null ? 0 : values.length;\n\n  this.__data__ = new MapCache;\n  while (++index < length) {\n    this.add(values[index]);\n  }\n}\n\n/**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\nfunction setCacheAdd(value) {\n  this.__data__.set(value, HASH_UNDEFINED);\n  return this;\n}\n\n/**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\nfunction setCacheHas(value) {\n  return this.__data__.has(value);\n}\n\n// Add methods to `SetCache`.\nSetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\nSetCache.prototype.has = setCacheHas;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n  var data = this.__data__ = new ListCache(entries);\n  this.size = data.size;\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n  this.__data__ = new ListCache;\n  this.size = 0;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n  var data = this.__data__,\n      result = data['delete'](key);\n\n  this.size = data.size;\n  return result;\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n  return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n  return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n  var data = this.__data__;\n  if (data instanceof ListCache) {\n    var pairs = data.__data__;\n    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n      pairs.push([key, value]);\n      this.size = ++data.size;\n      return this;\n    }\n    data = this.__data__ = new MapCache(pairs);\n  }\n  data.set(key, value);\n  this.size = data.size;\n  return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n  var isArr = isArray(value),\n      isArg = !isArr && isArguments(value),\n      isBuff = !isArr && !isArg && isBuffer(value),\n      isType = !isArr && !isArg && !isBuff && isTypedArray(value),\n      skipIndexes = isArr || isArg || isBuff || isType,\n      result = skipIndexes ? baseTimes(value.length, String) : [],\n      length = result.length;\n\n  for (var key in value) {\n    if ((inherited || hasOwnProperty.call(value, key)) &&\n        !(skipIndexes && (\n           // Safari 9 has enumerable `arguments.length` in strict mode.\n           key == 'length' ||\n           // Node.js 0.10 has enumerable non-index properties on buffers.\n           (isBuff && (key == 'offset' || key == 'parent')) ||\n           // PhantomJS 2 has enumerable non-index properties on typed arrays.\n           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||\n           // Skip index properties.\n           isIndex(key, length)\n        ))) {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n  var length = array.length;\n  while (length--) {\n    if (eq(array[length][0], key)) {\n      return length;\n    }\n  }\n  return -1;\n}\n\n/**\n * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n * symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Function} keysFunc The function to get the keys of `object`.\n * @param {Function} symbolsFunc The function to get the symbols of `object`.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction baseGetAllKeys(object, keysFunc, symbolsFunc) {\n  var result = keysFunc(object);\n  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));\n}\n\n/**\n * The base implementation of `getTag` without fallbacks for buggy environments.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n  if (value == null) {\n    return value === undefined ? undefinedTag : nullTag;\n  }\n  return (symToStringTag && symToStringTag in Object(value))\n    ? getRawTag(value)\n    : objectToString(value);\n}\n\n/**\n * The base implementation of `_.isArguments`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n */\nfunction baseIsArguments(value) {\n  return isObjectLike(value) && baseGetTag(value) == argsTag;\n}\n\n/**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {boolean} bitmask The bitmask flags.\n *  1 - Unordered comparison\n *  2 - Partial comparison\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\nfunction baseIsEqual(value, other, bitmask, customizer, stack) {\n  if (value === other) {\n    return true;\n  }\n  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {\n    return value !== value && other !== other;\n  }\n  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);\n}\n\n/**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {\n  var objIsArr = isArray(object),\n      othIsArr = isArray(other),\n      objTag = objIsArr ? arrayTag : getTag(object),\n      othTag = othIsArr ? arrayTag : getTag(other);\n\n  objTag = objTag == argsTag ? objectTag : objTag;\n  othTag = othTag == argsTag ? objectTag : othTag;\n\n  var objIsObj = objTag == objectTag,\n      othIsObj = othTag == objectTag,\n      isSameTag = objTag == othTag;\n\n  if (isSameTag && isBuffer(object)) {\n    if (!isBuffer(other)) {\n      return false;\n    }\n    objIsArr = true;\n    objIsObj = false;\n  }\n  if (isSameTag && !objIsObj) {\n    stack || (stack = new Stack);\n    return (objIsArr || isTypedArray(object))\n      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)\n      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);\n  }\n  if (!(bitmask & COMPARE_PARTIAL_FLAG)) {\n    var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n        othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n    if (objIsWrapped || othIsWrapped) {\n      var objUnwrapped = objIsWrapped ? object.value() : object,\n          othUnwrapped = othIsWrapped ? other.value() : other;\n\n      stack || (stack = new Stack);\n      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);\n    }\n  }\n  if (!isSameTag) {\n    return false;\n  }\n  stack || (stack = new Stack);\n  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n *  else `false`.\n */\nfunction baseIsNative(value) {\n  if (!isObject(value) || isMasked(value)) {\n    return false;\n  }\n  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;\n  return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\nfunction baseIsTypedArray(value) {\n  return isObjectLike(value) &&\n    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n  if (!isPrototype(object)) {\n    return nativeKeys(object);\n  }\n  var result = [];\n  for (var key in Object(object)) {\n    if (hasOwnProperty.call(object, key) && key != 'constructor') {\n      result.push(key);\n    }\n  }\n  return result;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\nfunction equalArrays(array, other, bitmask, customizer, equalFunc, stack) {\n  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n      arrLength = array.length,\n      othLength = other.length;\n\n  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n    return false;\n  }\n  // Assume cyclic values are equal.\n  var stacked = stack.get(array);\n  if (stacked && stack.get(other)) {\n    return stacked == other;\n  }\n  var index = -1,\n      result = true,\n      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;\n\n  stack.set(array, other);\n  stack.set(other, array);\n\n  // Ignore non-index properties.\n  while (++index < arrLength) {\n    var arrValue = array[index],\n        othValue = other[index];\n\n    if (customizer) {\n      var compared = isPartial\n        ? customizer(othValue, arrValue, index, other, array, stack)\n        : customizer(arrValue, othValue, index, array, other, stack);\n    }\n    if (compared !== undefined) {\n      if (compared) {\n        continue;\n      }\n      result = false;\n      break;\n    }\n    // Recursively compare arrays (susceptible to call stack limits).\n    if (seen) {\n      if (!arraySome(other, function(othValue, othIndex) {\n            if (!cacheHas(seen, othIndex) &&\n                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {\n              return seen.push(othIndex);\n            }\n          })) {\n        result = false;\n        break;\n      }\n    } else if (!(\n          arrValue === othValue ||\n            equalFunc(arrValue, othValue, bitmask, customizer, stack)\n        )) {\n      result = false;\n      break;\n    }\n  }\n  stack['delete'](array);\n  stack['delete'](other);\n  return result;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {\n  switch (tag) {\n    case dataViewTag:\n      if ((object.byteLength != other.byteLength) ||\n          (object.byteOffset != other.byteOffset)) {\n        return false;\n      }\n      object = object.buffer;\n      other = other.buffer;\n\n    case arrayBufferTag:\n      if ((object.byteLength != other.byteLength) ||\n          !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n        return false;\n      }\n      return true;\n\n    case boolTag:\n    case dateTag:\n    case numberTag:\n      // Coerce booleans to `1` or `0` and dates to milliseconds.\n      // Invalid dates are coerced to `NaN`.\n      return eq(+object, +other);\n\n    case errorTag:\n      return object.name == other.name && object.message == other.message;\n\n    case regexpTag:\n    case stringTag:\n      // Coerce regexes to strings and treat strings, primitives and objects,\n      // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n      // for more details.\n      return object == (other + '');\n\n    case mapTag:\n      var convert = mapToArray;\n\n    case setTag:\n      var isPartial = bitmask & COMPARE_PARTIAL_FLAG;\n      convert || (convert = setToArray);\n\n      if (object.size != other.size && !isPartial) {\n        return false;\n      }\n      // Assume cyclic values are equal.\n      var stacked = stack.get(object);\n      if (stacked) {\n        return stacked == other;\n      }\n      bitmask |= COMPARE_UNORDERED_FLAG;\n\n      // Recursively compare objects (susceptible to call stack limits).\n      stack.set(object, other);\n      var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);\n      stack['delete'](object);\n      return result;\n\n    case symbolTag:\n      if (symbolValueOf) {\n        return symbolValueOf.call(object) == symbolValueOf.call(other);\n      }\n  }\n  return false;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.\n * @param {Function} customizer The function to customize comparisons.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalObjects(object, other, bitmask, customizer, equalFunc, stack) {\n  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,\n      objProps = getAllKeys(object),\n      objLength = objProps.length,\n      othProps = getAllKeys(other),\n      othLength = othProps.length;\n\n  if (objLength != othLength && !isPartial) {\n    return false;\n  }\n  var index = objLength;\n  while (index--) {\n    var key = objProps[index];\n    if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n      return false;\n    }\n  }\n  // Assume cyclic values are equal.\n  var stacked = stack.get(object);\n  if (stacked && stack.get(other)) {\n    return stacked == other;\n  }\n  var result = true;\n  stack.set(object, other);\n  stack.set(other, object);\n\n  var skipCtor = isPartial;\n  while (++index < objLength) {\n    key = objProps[index];\n    var objValue = object[key],\n        othValue = other[key];\n\n    if (customizer) {\n      var compared = isPartial\n        ? customizer(othValue, objValue, key, other, object, stack)\n        : customizer(objValue, othValue, key, object, other, stack);\n    }\n    // Recursively compare objects (susceptible to call stack limits).\n    if (!(compared === undefined\n          ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))\n          : compared\n        )) {\n      result = false;\n      break;\n    }\n    skipCtor || (skipCtor = key == 'constructor');\n  }\n  if (result && !skipCtor) {\n    var objCtor = object.constructor,\n        othCtor = other.constructor;\n\n    // Non `Object` object instances with different constructors are not equal.\n    if (objCtor != othCtor &&\n        ('constructor' in object && 'constructor' in other) &&\n        !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n          typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n      result = false;\n    }\n  }\n  stack['delete'](object);\n  stack['delete'](other);\n  return result;\n}\n\n/**\n * Creates an array of own enumerable property names and symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names and symbols.\n */\nfunction getAllKeys(object) {\n  return baseGetAllKeys(object, keys, getSymbols);\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n  var data = map.__data__;\n  return isKeyable(key)\n    ? data[typeof key == 'string' ? 'string' : 'hash']\n    : data.map;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n  var value = getValue(object, key);\n  return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the raw `toStringTag`.\n */\nfunction getRawTag(value) {\n  var isOwn = hasOwnProperty.call(value, symToStringTag),\n      tag = value[symToStringTag];\n\n  try {\n    value[symToStringTag] = undefined;\n    var unmasked = true;\n  } catch (e) {}\n\n  var result = nativeObjectToString.call(value);\n  if (unmasked) {\n    if (isOwn) {\n      value[symToStringTag] = tag;\n    } else {\n      delete value[symToStringTag];\n    }\n  }\n  return result;\n}\n\n/**\n * Creates an array of the own enumerable symbols of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of symbols.\n */\nvar getSymbols = !nativeGetSymbols ? stubArray : function(object) {\n  if (object == null) {\n    return [];\n  }\n  object = Object(object);\n  return arrayFilter(nativeGetSymbols(object), function(symbol) {\n    return propertyIsEnumerable.call(object, symbol);\n  });\n};\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n    (Map && getTag(new Map) != mapTag) ||\n    (Promise && getTag(Promise.resolve()) != promiseTag) ||\n    (Set && getTag(new Set) != setTag) ||\n    (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n  getTag = function(value) {\n    var result = baseGetTag(value),\n        Ctor = result == objectTag ? value.constructor : undefined,\n        ctorString = Ctor ? toSource(Ctor) : '';\n\n    if (ctorString) {\n      switch (ctorString) {\n        case dataViewCtorString: return dataViewTag;\n        case mapCtorString: return mapTag;\n        case promiseCtorString: return promiseTag;\n        case setCtorString: return setTag;\n        case weakMapCtorString: return weakMapTag;\n      }\n    }\n    return result;\n  };\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n  length = length == null ? MAX_SAFE_INTEGER : length;\n  return !!length &&\n    (typeof value == 'number' || reIsUint.test(value)) &&\n    (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n  var type = typeof value;\n  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n    ? (value !== '__proto__')\n    : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n  return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n  var Ctor = value && value.constructor,\n      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n  return value === proto;\n}\n\n/**\n * Converts `value` to a string using `Object.prototype.toString`.\n *\n * @private\n * @param {*} value The value to convert.\n * @returns {string} Returns the converted string.\n */\nfunction objectToString(value) {\n  return nativeObjectToString.call(value);\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to convert.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n  if (func != null) {\n    try {\n      return funcToString.call(func);\n    } catch (e) {}\n    try {\n      return (func + '');\n    } catch (e) {}\n  }\n  return '';\n}\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n  return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n *  else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nvar isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {\n  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&\n    !propertyIsEnumerable.call(value, 'callee');\n};\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n  return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * Checks if `value` is a buffer.\n *\n * @static\n * @memberOf _\n * @since 4.3.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n * @example\n *\n * _.isBuffer(new Buffer(2));\n * // => true\n *\n * _.isBuffer(new Uint8Array(2));\n * // => false\n */\nvar isBuffer = nativeIsBuffer || stubFalse;\n\n/**\n * Performs a deep comparison between two values to determine if they are\n * equivalent.\n *\n * **Note:** This method supports comparing arrays, array buffers, booleans,\n * date objects, error objects, maps, numbers, `Object` objects, regexes,\n * sets, strings, symbols, and typed arrays. `Object` objects are compared\n * by their own, not inherited, enumerable properties. Functions and DOM\n * nodes are compared by strict equality, i.e. `===`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.isEqual(object, other);\n * // => true\n *\n * object === other;\n * // => false\n */\nfunction isEqual(value, other) {\n  return baseIsEqual(value, other);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n  if (!isObject(value)) {\n    return false;\n  }\n  // The use of `Object#toString` avoids issues with the `typeof` operator\n  // in Safari 9 which returns 'object' for typed arrays and other constructors.\n  var tag = baseGetTag(value);\n  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n  return typeof value == 'number' &&\n    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n  var type = typeof value;\n  return value != null && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n  return value != null && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\nvar isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n *   this.a = 1;\n *   this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns a new empty array.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {Array} Returns the new empty array.\n * @example\n *\n * var arrays = _.times(2, _.stubArray);\n *\n * console.log(arrays);\n * // => [[], []]\n *\n * console.log(arrays[0] === arrays[1]);\n * // => false\n */\nfunction stubArray() {\n  return [];\n}\n\n/**\n * This method returns `false`.\n *\n * @static\n * @memberOf _\n * @since 4.13.0\n * @category Util\n * @returns {boolean} Returns `false`.\n * @example\n *\n * _.times(2, _.stubFalse);\n * // => [false, false]\n */\nfunction stubFalse() {\n  return false;\n}\n\nmodule.exports = isEqual;\n","var grammar = module.exports = {\n  v: [{\n    name: 'version',\n    reg: /^(\\d*)$/\n  }],\n  o: [{ //o=- 20518 0 IN IP4 203.0.113.1\n    // NB: sessionId will be a String in most cases because it is huge\n    name: 'origin',\n    reg: /^(\\S*) (\\d*) (\\d*) (\\S*) IP(\\d) (\\S*)/,\n    names: ['username', 'sessionId', 'sessionVersion', 'netType', 'ipVer', 'address'],\n    format: '%s %s %d %s IP%d %s'\n  }],\n  // default parsing of these only (though some of these feel outdated)\n  s: [{ name: 'name' }],\n  i: [{ name: 'description' }],\n  u: [{ name: 'uri' }],\n  e: [{ name: 'email' }],\n  p: [{ name: 'phone' }],\n  z: [{ name: 'timezones' }], // TODO: this one can actually be parsed properly..\n  r: [{ name: 'repeats' }],   // TODO: this one can also be parsed properly\n  //k: [{}], // outdated thing ignored\n  t: [{ //t=0 0\n    name: 'timing',\n    reg: /^(\\d*) (\\d*)/,\n    names: ['start', 'stop'],\n    format: '%d %d'\n  }],\n  c: [{ //c=IN IP4 10.47.197.26\n    name: 'connection',\n    reg: /^IN IP(\\d) (\\S*)/,\n    names: ['version', 'ip'],\n    format: 'IN IP%d %s'\n  }],\n  b: [{ //b=AS:4000\n    push: 'bandwidth',\n    reg: /^(TIAS|AS|CT|RR|RS):(\\d*)/,\n    names: ['type', 'limit'],\n    format: '%s:%s'\n  }],\n  m: [{ //m=video 51744 RTP/AVP 126 97 98 34 31\n    // NB: special - pushes to session\n    // TODO: rtp/fmtp should be filtered by the payloads found here?\n    reg: /^(\\w*) (\\d*) ([\\w\\/]*)(?: (.*))?/,\n    names: ['type', 'port', 'protocol', 'payloads'],\n    format: '%s %d %s %s'\n  }],\n  a: [\n    { //a=rtpmap:110 opus/48000/2\n      push: 'rtp',\n      reg: /^rtpmap:(\\d*) ([\\w\\-\\.]*)(?:\\s*\\/(\\d*)(?:\\s*\\/(\\S*))?)?/,\n      names: ['payload', 'codec', 'rate', 'encoding'],\n      format: function (o) {\n        return (o.encoding) ?\n          'rtpmap:%d %s/%s/%s':\n          o.rate ?\n          'rtpmap:%d %s/%s':\n          'rtpmap:%d %s';\n      }\n    },\n    { //a=fmtp:108 profile-level-id=24;object=23;bitrate=64000\n      //a=fmtp:111 minptime=10; useinbandfec=1\n      push: 'fmtp',\n      reg: /^fmtp:(\\d*) ([\\S| ]*)/,\n      names: ['payload', 'config'],\n      format: 'fmtp:%d %s'\n    },\n    { //a=control:streamid=0\n      name: 'control',\n      reg: /^control:(.*)/,\n      format: 'control:%s'\n    },\n    { //a=rtcp:65179 IN IP4 193.84.77.194\n      name: 'rtcp',\n      reg: /^rtcp:(\\d*)(?: (\\S*) IP(\\d) (\\S*))?/,\n      names: ['port', 'netType', 'ipVer', 'address'],\n      format: function (o) {\n        return (o.address != null) ?\n          'rtcp:%d %s IP%d %s':\n          'rtcp:%d';\n      }\n    },\n    { //a=rtcp-fb:98 trr-int 100\n      push: 'rtcpFbTrrInt',\n      reg: /^rtcp-fb:(\\*|\\d*) trr-int (\\d*)/,\n      names: ['payload', 'value'],\n      format: 'rtcp-fb:%d trr-int %d'\n    },\n    { //a=rtcp-fb:98 nack rpsi\n      push: 'rtcpFb',\n      reg: /^rtcp-fb:(\\*|\\d*) ([\\w-_]*)(?: ([\\w-_]*))?/,\n      names: ['payload', 'type', 'subtype'],\n      format: function (o) {\n        return (o.subtype != null) ?\n          'rtcp-fb:%s %s %s':\n          'rtcp-fb:%s %s';\n      }\n    },\n    { //a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n      //a=extmap:1/recvonly URI-gps-string\n      push: 'ext',\n      reg: /^extmap:(\\d+)(?:\\/(\\w+))? (\\S*)(?: (\\S*))?/,\n      names: ['value', 'direction', 'uri', 'config'],\n      format: function (o) {\n        return 'extmap:%d' + (o.direction ? '/%s' : '%v') + ' %s' + (o.config ? ' %s' : '');\n      }\n    },\n    { //a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:PS1uQCVeeCFCanVmcjkpPywjNWhcYD0mXXtxaVBR|2^20|1:32\n      push: 'crypto',\n      reg: /^crypto:(\\d*) ([\\w_]*) (\\S*)(?: (\\S*))?/,\n      names: ['id', 'suite', 'config', 'sessionConfig'],\n      format: function (o) {\n        return (o.sessionConfig != null) ?\n          'crypto:%d %s %s %s':\n          'crypto:%d %s %s';\n      }\n    },\n    { //a=setup:actpass\n      name: 'setup',\n      reg: /^setup:(\\w*)/,\n      format: 'setup:%s'\n    },\n    { //a=mid:1\n      name: 'mid',\n      reg: /^mid:([^\\s]*)/,\n      format: 'mid:%s'\n    },\n    { //a=msid:0c8b064d-d807-43b4-b434-f92a889d8587 98178685-d409-46e0-8e16-7ef0db0db64a\n      name: 'msid',\n      reg: /^msid:(.*)/,\n      format: 'msid:%s'\n    },\n    { //a=ptime:20\n      name: 'ptime',\n      reg: /^ptime:(\\d*)/,\n      format: 'ptime:%d'\n    },\n    { //a=maxptime:60\n      name: 'maxptime',\n      reg: /^maxptime:(\\d*)/,\n      format: 'maxptime:%d'\n    },\n    { //a=sendrecv\n      name: 'direction',\n      reg: /^(sendrecv|recvonly|sendonly|inactive)/\n    },\n    { //a=ice-lite\n      name: 'icelite',\n      reg: /^(ice-lite)/\n    },\n    { //a=ice-ufrag:F7gI\n      name: 'iceUfrag',\n      reg: /^ice-ufrag:(\\S*)/,\n      format: 'ice-ufrag:%s'\n    },\n    { //a=ice-pwd:x9cml/YzichV2+XlhiMu8g\n      name: 'icePwd',\n      reg: /^ice-pwd:(\\S*)/,\n      format: 'ice-pwd:%s'\n    },\n    { //a=fingerprint:SHA-1 00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33\n      name: 'fingerprint',\n      reg: /^fingerprint:(\\S*) (\\S*)/,\n      names: ['type', 'hash'],\n      format: 'fingerprint:%s %s'\n    },\n    { //a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host\n      //a=candidate:1162875081 1 udp 2113937151 192.168.34.75 60017 typ host generation 0 network-id 3 network-cost 10\n      //a=candidate:3289912957 2 udp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 generation 0 network-id 3 network-cost 10\n      //a=candidate:229815620 1 tcp 1518280447 192.168.150.19 60017 typ host tcptype active generation 0 network-id 3 network-cost 10\n      //a=candidate:3289912957 2 tcp 1845501695 193.84.77.194 60017 typ srflx raddr 192.168.34.75 rport 60017 tcptype passive generation 0 network-id 3 network-cost 10\n      push:'candidates',\n      reg: /^candidate:(\\S*) (\\d*) (\\S*) (\\d*) (\\S*) (\\d*) typ (\\S*)(?: raddr (\\S*) rport (\\d*))?(?: tcptype (\\S*))?(?: generation (\\d*))?(?: network-id (\\d*))?(?: network-cost (\\d*))?/,\n      names: ['foundation', 'component', 'transport', 'priority', 'ip', 'port', 'type', 'raddr', 'rport', 'tcptype', 'generation', 'network-id', 'network-cost'],\n      format: function (o) {\n        var str = 'candidate:%s %d %s %d %s %d typ %s';\n\n        str += (o.raddr != null) ? ' raddr %s rport %d' : '%v%v';\n\n        // NB: candidate has three optional chunks, so %void middles one if it's missing\n        str += (o.tcptype != null) ? ' tcptype %s' : '%v';\n\n        if (o.generation != null) {\n          str += ' generation %d';\n        }\n\n        str += (o['network-id'] != null) ? ' network-id %d' : '%v';\n        str += (o['network-cost'] != null) ? ' network-cost %d' : '%v';\n        return str;\n      }\n    },\n    { //a=end-of-candidates (keep after the candidates line for readability)\n      name: 'endOfCandidates',\n      reg: /^(end-of-candidates)/\n    },\n    { //a=remote-candidates:1 203.0.113.1 54400 2 203.0.113.1 54401 ...\n      name: 'remoteCandidates',\n      reg: /^remote-candidates:(.*)/,\n      format: 'remote-candidates:%s'\n    },\n    { //a=ice-options:google-ice\n      name: 'iceOptions',\n      reg: /^ice-options:(\\S*)/,\n      format: 'ice-options:%s'\n    },\n    { //a=ssrc:2566107569 cname:t9YU8M1UxTF8Y1A1\n      push: 'ssrcs',\n      reg: /^ssrc:(\\d*) ([\\w_]*)(?::(.*))?/,\n      names: ['id', 'attribute', 'value'],\n      format: function (o) {\n        var str = 'ssrc:%d';\n        if (o.attribute != null) {\n          str += ' %s';\n          if (o.value != null) {\n            str += ':%s';\n          }\n        }\n        return str;\n      }\n    },\n    { //a=ssrc-group:FEC 1 2\n      //a=ssrc-group:FEC-FR 3004364195 1080772241\n      push: 'ssrcGroups',\n      // token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E\n      reg: /^ssrc-group:([\\x21\\x23\\x24\\x25\\x26\\x27\\x2A\\x2B\\x2D\\x2E\\w]*) (.*)/,\n      names: ['semantics', 'ssrcs'],\n      format: 'ssrc-group:%s %s'\n    },\n    { //a=msid-semantic: WMS Jvlam5X3SX1OP6pn20zWogvaKJz5Hjf9OnlV\n      name: 'msidSemantic',\n      reg: /^msid-semantic:\\s?(\\w*) (\\S*)/,\n      names: ['semantic', 'token'],\n      format: 'msid-semantic: %s %s' // space after ':' is not accidental\n    },\n    { //a=group:BUNDLE audio video\n      push: 'groups',\n      reg: /^group:(\\w*) (.*)/,\n      names: ['type', 'mids'],\n      format: 'group:%s %s'\n    },\n    { //a=rtcp-mux\n      name: 'rtcpMux',\n      reg: /^(rtcp-mux)/\n    },\n    { //a=rtcp-rsize\n      name: 'rtcpRsize',\n      reg: /^(rtcp-rsize)/\n    },\n    { //a=sctpmap:5000 webrtc-datachannel 1024\n      name: 'sctpmap',\n      reg: /^sctpmap:([\\w_\\/]*) (\\S*)(?: (\\S*))?/,\n      names: ['sctpmapNumber', 'app', 'maxMessageSize'],\n      format: function (o) {\n        return (o.maxMessageSize != null) ?\n          'sctpmap:%s %s %s' :\n          'sctpmap:%s %s';\n      }\n    },\n    { //a=x-google-flag:conference\n      name: 'xGoogleFlag',\n      reg: /^x-google-flag:([^\\s]*)/,\n      format: 'x-google-flag:%s'\n    },\n    { //a=rid:1 send max-width=1280;max-height=720;max-fps=30;depend=0\n      push: 'rids',\n      reg: /^rid:([\\d\\w]+) (\\w+)(?: ([\\S| ]*))?/,\n      names: ['id', 'direction', 'params'],\n      format: function (o) {\n        return (o.params) ? 'rid:%s %s %s' : 'rid:%s %s';\n      }\n    },\n    { //a=imageattr:97 send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320] recv [x=330,y=250]\n      //a=imageattr:* send [x=800,y=640] recv *\n      //a=imageattr:100 recv [x=320,y=240]\n      push: 'imageattrs',\n      reg: new RegExp(\n        //a=imageattr:97\n        '^imageattr:(\\\\d+|\\\\*)' +\n        //send [x=800,y=640,sar=1.1,q=0.6] [x=480,y=320]\n        '[\\\\s\\\\t]+(send|recv)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*)' +\n        //recv [x=330,y=250]\n        '(?:[\\\\s\\\\t]+(recv|send)[\\\\s\\\\t]+(\\\\*|\\\\[\\\\S+\\\\](?:[\\\\s\\\\t]+\\\\[\\\\S+\\\\])*))?'\n      ),\n      names: ['pt', 'dir1', 'attrs1', 'dir2', 'attrs2'],\n      format: function (o) {\n        return 'imageattr:%s %s %s' + (o.dir2 ? ' %s %s' : '');\n      }\n    },\n    { //a=simulcast:send 1,2,3;~4,~5 recv 6;~7,~8\n      //a=simulcast:recv 1;4,5 send 6;7\n      name: 'simulcast',\n      reg: new RegExp(\n        //a=simulcast:\n        '^simulcast:' +\n        //send 1,2,3;~4,~5\n        '(send|recv) ([a-zA-Z0-9\\\\-_~;,]+)' +\n        //space + recv 6;~7,~8\n        '(?:\\\\s?(send|recv) ([a-zA-Z0-9\\\\-_~;,]+))?' +\n        //end\n        '$'\n      ),\n      names: ['dir1', 'list1', 'dir2', 'list2'],\n      format: function (o) {\n        return 'simulcast:%s %s' + (o.dir2 ? ' %s %s' : '');\n      }\n    },\n    { //Old simulcast draft 03 (implemented by Firefox)\n      //  https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-03\n      //a=simulcast: recv pt=97;98 send pt=97\n      //a=simulcast: send rid=5;6;7 paused=6,7\n      name: 'simulcast_03',\n      reg: /^simulcast:[\\s\\t]+([\\S+\\s\\t]+)$/,\n      names: ['value'],\n      format: 'simulcast: %s'\n    },\n    {\n      //a=framerate:25\n      //a=framerate:29.97\n      name: 'framerate',\n      reg: /^framerate:(\\d+(?:$|\\.\\d+))/,\n      format: 'framerate:%s'\n    },\n    { // any a= that we don't understand is kepts verbatim on media.invalid\n      push: 'invalid',\n      names: ['value']\n    }\n  ]\n};\n\n// set sensible defaults to avoid polluting the grammar with boring details\nObject.keys(grammar).forEach(function (key) {\n  var objs = grammar[key];\n  objs.forEach(function (obj) {\n    if (!obj.reg) {\n      obj.reg = /(.*)/;\n    }\n    if (!obj.format) {\n      obj.format = '%s';\n    }\n  });\n});\n","var parser = require('./parser');\nvar writer = require('./writer');\n\nexports.write = writer;\nexports.parse = parser.parse;\nexports.parseFmtpConfig = parser.parseFmtpConfig;\nexports.parseParams = parser.parseParams;\nexports.parsePayloads = parser.parsePayloads;\nexports.parseRemoteCandidates = parser.parseRemoteCandidates;\nexports.parseImageAttributes = parser.parseImageAttributes;\nexports.parseSimulcastStreamList = parser.parseSimulcastStreamList;\n","var toIntIfInt = function (v) {\n  return String(Number(v)) === v ? Number(v) : v;\n};\n\nvar attachProperties = function (match, location, names, rawName) {\n  if (rawName && !names) {\n    location[rawName] = toIntIfInt(match[1]);\n  }\n  else {\n    for (var i = 0; i < names.length; i += 1) {\n      if (match[i+1] != null) {\n        location[names[i]] = toIntIfInt(match[i+1]);\n      }\n    }\n  }\n};\n\nvar parseReg = function (obj, location, content) {\n  var needsBlank = obj.name && obj.names;\n  if (obj.push && !location[obj.push]) {\n    location[obj.push] = [];\n  }\n  else if (needsBlank && !location[obj.name]) {\n    location[obj.name] = {};\n  }\n  var keyLocation = obj.push ?\n    {} :  // blank object that will be pushed\n    needsBlank ? location[obj.name] : location; // otherwise, named location or root\n\n  attachProperties(content.match(obj.reg), keyLocation, obj.names, obj.name);\n\n  if (obj.push) {\n    location[obj.push].push(keyLocation);\n  }\n};\n\nvar grammar = require('./grammar');\nvar validLine = RegExp.prototype.test.bind(/^([a-z])=(.*)/);\n\nexports.parse = function (sdp) {\n  var session = {}\n    , media = []\n    , location = session; // points at where properties go under (one of the above)\n\n  // parse lines we understand\n  sdp.split(/(\\r\\n|\\r|\\n)/).filter(validLine).forEach(function (l) {\n    var type = l[0];\n    var content = l.slice(2);\n    if (type === 'm') {\n      media.push({rtp: [], fmtp: []});\n      location = media[media.length-1]; // point at latest media line\n    }\n\n    for (var j = 0; j < (grammar[type] || []).length; j += 1) {\n      var obj = grammar[type][j];\n      if (obj.reg.test(content)) {\n        return parseReg(obj, location, content);\n      }\n    }\n  });\n\n  session.media = media; // link it up\n  return session;\n};\n\nvar paramReducer = function (acc, expr) {\n  var s = expr.split(/=(.+)/, 2);\n  if (s.length === 2) {\n    acc[s[0]] = toIntIfInt(s[1]);\n  }\n  return acc;\n};\n\nexports.parseParams = function (str) {\n  return str.split(/\\;\\s?/).reduce(paramReducer, {});\n};\n\n// For backward compatibility - alias will be removed in 3.0.0\nexports.parseFmtpConfig = exports.parseParams;\n\nexports.parsePayloads = function (str) {\n  return str.split(' ').map(Number);\n};\n\nexports.parseRemoteCandidates = function (str) {\n  var candidates = [];\n  var parts = str.split(' ').map(toIntIfInt);\n  for (var i = 0; i < parts.length; i += 3) {\n    candidates.push({\n      component: parts[i],\n      ip: parts[i + 1],\n      port: parts[i + 2]\n    });\n  }\n  return candidates;\n};\n\nexports.parseImageAttributes = function (str) {\n  return str.split(' ').map(function (item) {\n    return item.substring(1, item.length-1).split(',').reduce(paramReducer, {});\n  });\n};\n\nexports.parseSimulcastStreamList = function (str) {\n  return str.split(';').map(function (stream) {\n    return stream.split(',').map(function (format) {\n      var scid, paused = false;\n\n      if (format[0] !== '~') {\n        scid = toIntIfInt(format);\n      } else {\n        scid = toIntIfInt(format.substring(1, format.length));\n        paused = true;\n      }\n\n      return {\n        scid: scid,\n        paused: paused\n      };\n    });\n  });\n};\n","var grammar = require('./grammar');\n\n// customized util.format - discards excess arguments and can void middle ones\nvar formatRegExp = /%[sdv%]/g;\nvar format = function (formatStr) {\n  var i = 1;\n  var args = arguments;\n  var len = args.length;\n  return formatStr.replace(formatRegExp, function (x) {\n    if (i >= len) {\n      return x; // missing argument\n    }\n    var arg = args[i];\n    i += 1;\n    switch (x) {\n    case '%%':\n      return '%';\n    case '%s':\n      return String(arg);\n    case '%d':\n      return Number(arg);\n    case '%v':\n      return '';\n    }\n  });\n  // NB: we discard excess arguments - they are typically undefined from makeLine\n};\n\nvar makeLine = function (type, obj, location) {\n  var str = obj.format instanceof Function ?\n    (obj.format(obj.push ? location : location[obj.name])) :\n    obj.format;\n\n  var args = [type + '=' + str];\n  if (obj.names) {\n    for (var i = 0; i < obj.names.length; i += 1) {\n      var n = obj.names[i];\n      if (obj.name) {\n        args.push(location[obj.name][n]);\n      }\n      else { // for mLine and push attributes\n        args.push(location[obj.names[i]]);\n      }\n    }\n  }\n  else {\n    args.push(location[obj.name]);\n  }\n  return format.apply(null, args);\n};\n\n// RFC specified order\n// TODO: extend this with all the rest\nvar defaultOuterOrder = [\n  'v', 'o', 's', 'i',\n  'u', 'e', 'p', 'c',\n  'b', 't', 'r', 'z', 'a'\n];\nvar defaultInnerOrder = ['i', 'c', 'b', 'a'];\n\n\nmodule.exports = function (session, opts) {\n  opts = opts || {};\n  // ensure certain properties exist\n  if (session.version == null) {\n    session.version = 0; // 'v=0' must be there (only defined version atm)\n  }\n  if (session.name == null) {\n    session.name = ' '; // 's= ' must be there if no meaningful name set\n  }\n  session.media.forEach(function (mLine) {\n    if (mLine.payloads == null) {\n      mLine.payloads = '';\n    }\n  });\n\n  var outerOrder = opts.outerOrder || defaultOuterOrder;\n  var innerOrder = opts.innerOrder || defaultInnerOrder;\n  var sdp = [];\n\n  // loop through outerOrder for matching properties on session\n  outerOrder.forEach(function (type) {\n    grammar[type].forEach(function (obj) {\n      if (obj.name in session && session[obj.name] != null) {\n        sdp.push(makeLine(type, obj, session));\n      }\n      else if (obj.push in session && session[obj.push] != null) {\n        session[obj.push].forEach(function (el) {\n          sdp.push(makeLine(type, obj, el));\n        });\n      }\n    });\n  });\n\n  // then for each media line, follow the innerOrder\n  session.media.forEach(function (mLine) {\n    sdp.push(makeLine('m', grammar.m[0], mLine));\n\n    innerOrder.forEach(function (type) {\n      grammar[type].forEach(function (obj) {\n        if (obj.name in mLine && mLine[obj.name] != null) {\n          sdp.push(makeLine(type, obj, mLine));\n        }\n        else if (obj.push in mLine && mLine[obj.push] != null) {\n          mLine[obj.push].forEach(function (el) {\n            sdp.push(makeLine(type, obj, el));\n          });\n        }\n      });\n    });\n  });\n\n  return sdp.join('\\r\\n') + '\\r\\n';\n};\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n  return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n  return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n  const parts = blob.split('\\nm=');\n  return parts.map((part, index) => (index > 0 ?\n    'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n  const sections = SDPUtils.splitSections(blob);\n  return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n  const sections = SDPUtils.splitSections(blob);\n  sections.shift();\n  return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n  return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n  let parts;\n  // Parse both variants.\n  if (line.indexOf('a=candidate:') === 0) {\n    parts = line.substring(12).split(' ');\n  } else {\n    parts = line.substring(10).split(' ');\n  }\n\n  const candidate = {\n    foundation: parts[0],\n    component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n    protocol: parts[2].toLowerCase(),\n    priority: parseInt(parts[3], 10),\n    ip: parts[4],\n    address: parts[4], // address is an alias for ip.\n    port: parseInt(parts[5], 10),\n    // skip parts[6] == 'typ'\n    type: parts[7],\n  };\n\n  for (let i = 8; i < parts.length; i += 2) {\n    switch (parts[i]) {\n      case 'raddr':\n        candidate.relatedAddress = parts[i + 1];\n        break;\n      case 'rport':\n        candidate.relatedPort = parseInt(parts[i + 1], 10);\n        break;\n      case 'tcptype':\n        candidate.tcpType = parts[i + 1];\n        break;\n      case 'ufrag':\n        candidate.ufrag = parts[i + 1]; // for backward compatibility.\n        candidate.usernameFragment = parts[i + 1];\n        break;\n      default: // extension handling, in particular ufrag. Don't overwrite.\n        if (candidate[parts[i]] === undefined) {\n          candidate[parts[i]] = parts[i + 1];\n        }\n        break;\n    }\n  }\n  return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n  const sdp = [];\n  sdp.push(candidate.foundation);\n\n  const component = candidate.component;\n  if (component === 'rtp') {\n    sdp.push(1);\n  } else if (component === 'rtcp') {\n    sdp.push(2);\n  } else {\n    sdp.push(component);\n  }\n  sdp.push(candidate.protocol.toUpperCase());\n  sdp.push(candidate.priority);\n  sdp.push(candidate.address || candidate.ip);\n  sdp.push(candidate.port);\n\n  const type = candidate.type;\n  sdp.push('typ');\n  sdp.push(type);\n  if (type !== 'host' && candidate.relatedAddress &&\n      candidate.relatedPort) {\n    sdp.push('raddr');\n    sdp.push(candidate.relatedAddress);\n    sdp.push('rport');\n    sdp.push(candidate.relatedPort);\n  }\n  if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n    sdp.push('tcptype');\n    sdp.push(candidate.tcpType);\n  }\n  if (candidate.usernameFragment || candidate.ufrag) {\n    sdp.push('ufrag');\n    sdp.push(candidate.usernameFragment || candidate.ufrag);\n  }\n  return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n  return line.substr(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n  let parts = line.substr(9).split(' ');\n  const parsed = {\n    payloadType: parseInt(parts.shift(), 10), // was: id\n  };\n\n  parts = parts[0].split('/');\n\n  parsed.name = parts[0];\n  parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n  parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n  // legacy alias, got renamed back to channels in ORTC.\n  parsed.numChannels = parsed.channels;\n  return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n  let pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  const channels = codec.channels || codec.numChannels || 1;\n  return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n      (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n  const parts = line.substr(9).split(' ');\n  return {\n    id: parseInt(parts[0], 10),\n    direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n    uri: parts[1],\n  };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n  return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n      (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n        ? '/' + headerExtension.direction\n        : '') +\n      ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n  const parsed = {};\n  let kv;\n  const parts = line.substr(line.indexOf(' ') + 1).split(';');\n  for (let j = 0; j < parts.length; j++) {\n    kv = parts[j].trim().split('=');\n    parsed[kv[0].trim()] = kv[1];\n  }\n  return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n  let line = '';\n  let pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.parameters && Object.keys(codec.parameters).length) {\n    const params = [];\n    Object.keys(codec.parameters).forEach(param => {\n      if (codec.parameters[param] !== undefined) {\n        params.push(param + '=' + codec.parameters[param]);\n      } else {\n        params.push(param);\n      }\n    });\n    line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n  }\n  return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n  const parts = line.substr(line.indexOf(' ') + 1).split(' ');\n  return {\n    type: parts.shift(),\n    parameter: parts.join(' '),\n  };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n  let lines = '';\n  let pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n    // FIXME: special handling for trr-int?\n    codec.rtcpFeedback.forEach(fb => {\n      lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n      (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n          '\\r\\n';\n    });\n  }\n  return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n  const sp = line.indexOf(' ');\n  const parts = {\n    ssrc: parseInt(line.substr(7, sp - 7), 10),\n  };\n  const colon = line.indexOf(':', sp);\n  if (colon > -1) {\n    parts.attribute = line.substr(sp + 1, colon - sp - 1);\n    parts.value = line.substr(colon + 1);\n  } else {\n    parts.attribute = line.substr(sp + 1);\n  }\n  return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n  const parts = line.substr(13).split(' ');\n  return {\n    semantics: parts.shift(),\n    ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n  };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n  const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n  if (mid) {\n    return mid.substr(6);\n  }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n  const parts = line.substr(14).split(' ');\n  return {\n    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n    value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n  };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n//   get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n  const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=fingerprint:');\n  // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n  return {\n    role: 'auto',\n    fingerprints: lines.map(SDPUtils.parseFingerprint),\n  };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n  let sdp = 'a=setup:' + setupType + '\\r\\n';\n  params.fingerprints.forEach(fp => {\n    sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n  });\n  return sdp;\n};\n\n// Parses a=crypto lines into\n//   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n  const parts = line.substr(9).split(' ');\n  return {\n    tag: parseInt(parts[0], 10),\n    cryptoSuite: parts[1],\n    keyParams: parts[2],\n    sessionParams: parts.slice(3),\n  };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n  return 'a=crypto:' + parameters.tag + ' ' +\n    parameters.cryptoSuite + ' ' +\n    (typeof parameters.keyParams === 'object'\n      ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n      : parameters.keyParams) +\n    (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n    '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n//   https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n  if (keyParams.indexOf('inline:') !== 0) {\n    return null;\n  }\n  const parts = keyParams.substr(7).split('|');\n  return {\n    keyMethod: 'inline',\n    keySalt: parts[0],\n    lifeTime: parts[1],\n    mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n    mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n  };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n  return keyParams.keyMethod + ':'\n    + keyParams.keySalt +\n    (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n    (keyParams.mkiValue && keyParams.mkiLength\n      ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n      : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n  const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=crypto:');\n  return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n//   get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n  const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=ice-ufrag:')[0];\n  const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=ice-pwd:')[0];\n  if (!(ufrag && pwd)) {\n    return null;\n  }\n  return {\n    usernameFragment: ufrag.substr(12),\n    password: pwd.substr(10),\n  };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n  let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n      'a=ice-pwd:' + params.password + '\\r\\n';\n  if (params.iceLite) {\n    sdp += 'a=ice-lite\\r\\n';\n  }\n  return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n  const description = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: [],\n    rtcp: [],\n  };\n  const lines = SDPUtils.splitLines(mediaSection);\n  const mline = lines[0].split(' ');\n  for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n    const pt = mline[i];\n    const rtpmapline = SDPUtils.matchPrefix(\n      mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n    if (rtpmapline) {\n      const codec = SDPUtils.parseRtpMap(rtpmapline);\n      const fmtps = SDPUtils.matchPrefix(\n        mediaSection, 'a=fmtp:' + pt + ' ');\n      // Only the first a=fmtp:<pt> is considered.\n      codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n      codec.rtcpFeedback = SDPUtils.matchPrefix(\n        mediaSection, 'a=rtcp-fb:' + pt + ' ')\n        .map(SDPUtils.parseRtcpFb);\n      description.codecs.push(codec);\n      // parse FEC mechanisms from rtpmap lines.\n      switch (codec.name.toUpperCase()) {\n        case 'RED':\n        case 'ULPFEC':\n          description.fecMechanisms.push(codec.name.toUpperCase());\n          break;\n        default: // only RED and ULPFEC are recognized as FEC mechanisms.\n          break;\n      }\n    }\n  }\n  SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n    description.headerExtensions.push(SDPUtils.parseExtmap(line));\n  });\n  // FIXME: parse rtcp.\n  return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n  let sdp = '';\n\n  // Build the mline.\n  sdp += 'm=' + kind + ' ';\n  sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n  sdp += ' UDP/TLS/RTP/SAVPF ';\n  sdp += caps.codecs.map(codec => {\n    if (codec.preferredPayloadType !== undefined) {\n      return codec.preferredPayloadType;\n    }\n    return codec.payloadType;\n  }).join(' ') + '\\r\\n';\n\n  sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n  sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n  // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n  caps.codecs.forEach(codec => {\n    sdp += SDPUtils.writeRtpMap(codec);\n    sdp += SDPUtils.writeFmtp(codec);\n    sdp += SDPUtils.writeRtcpFb(codec);\n  });\n  let maxptime = 0;\n  caps.codecs.forEach(codec => {\n    if (codec.maxptime > maxptime) {\n      maxptime = codec.maxptime;\n    }\n  });\n  if (maxptime > 0) {\n    sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n  }\n\n  if (caps.headerExtensions) {\n    caps.headerExtensions.forEach(extension => {\n      sdp += SDPUtils.writeExtmap(extension);\n    });\n  }\n  // FIXME: write fecMechanisms.\n  return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n  const encodingParameters = [];\n  const description = SDPUtils.parseRtpParameters(mediaSection);\n  const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n  const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n  // filter a=ssrc:... cname:, ignore PlanB-msid\n  const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(line => SDPUtils.parseSsrcMedia(line))\n    .filter(parts => parts.attribute === 'cname');\n  const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n  let secondarySsrc;\n\n  const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n    .map(line => {\n      const parts = line.substr(17).split(' ');\n      return parts.map(part => parseInt(part, 10));\n    });\n  if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n    secondarySsrc = flows[0][1];\n  }\n\n  description.codecs.forEach(codec => {\n    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n      let encParam = {\n        ssrc: primarySsrc,\n        codecPayloadType: parseInt(codec.parameters.apt, 10),\n      };\n      if (primarySsrc && secondarySsrc) {\n        encParam.rtx = {ssrc: secondarySsrc};\n      }\n      encodingParameters.push(encParam);\n      if (hasRed) {\n        encParam = JSON.parse(JSON.stringify(encParam));\n        encParam.fec = {\n          ssrc: primarySsrc,\n          mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n        };\n        encodingParameters.push(encParam);\n      }\n    }\n  });\n  if (encodingParameters.length === 0 && primarySsrc) {\n    encodingParameters.push({\n      ssrc: primarySsrc,\n    });\n  }\n\n  // we support both b=AS and b=TIAS but interpret AS as TIAS.\n  let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n  if (bandwidth.length) {\n    if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n      bandwidth = parseInt(bandwidth[0].substr(7), 10);\n    } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n      // use formula from JSEP to convert b=AS to TIAS value.\n      bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n          - (50 * 40 * 8);\n    } else {\n      bandwidth = undefined;\n    }\n    encodingParameters.forEach(params => {\n      params.maxBitrate = bandwidth;\n    });\n  }\n  return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n  const rtcpParameters = {};\n\n  // Gets the first SSRC. Note that with RTX there might be multiple\n  // SSRCs.\n  const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(line => SDPUtils.parseSsrcMedia(line))\n    .filter(obj => obj.attribute === 'cname')[0];\n  if (remoteSsrc) {\n    rtcpParameters.cname = remoteSsrc.value;\n    rtcpParameters.ssrc = remoteSsrc.ssrc;\n  }\n\n  // Edge uses the compound attribute instead of reducedSize\n  // compound is !reducedSize\n  const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n  rtcpParameters.reducedSize = rsize.length > 0;\n  rtcpParameters.compound = rsize.length === 0;\n\n  // parses the rtcp-mux attrіbute.\n  // Note that Edge does not support unmuxed RTCP.\n  const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n  rtcpParameters.mux = mux.length > 0;\n\n  return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n  let sdp = '';\n  if (rtcpParameters.reducedSize) {\n    sdp += 'a=rtcp-rsize\\r\\n';\n  }\n  if (rtcpParameters.mux) {\n    sdp += 'a=rtcp-mux\\r\\n';\n  }\n  if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n    sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n      ' cname:' + rtcpParameters.cname + '\\r\\n';\n  }\n  return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n  let parts;\n  const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n  if (spec.length === 1) {\n    parts = spec[0].substr(7).split(' ');\n    return {stream: parts[0], track: parts[1]};\n  }\n  const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(line => SDPUtils.parseSsrcMedia(line))\n    .filter(msidParts => msidParts.attribute === 'msid');\n  if (planB.length > 0) {\n    parts = planB[0].value.split(' ');\n    return {stream: parts[0], track: parts[1]};\n  }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n  const mline = SDPUtils.parseMLine(mediaSection);\n  const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n  let maxMessageSize;\n  if (maxSizeLine.length > 0) {\n    maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n  }\n  if (isNaN(maxMessageSize)) {\n    maxMessageSize = 65536;\n  }\n  const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n  if (sctpPort.length > 0) {\n    return {\n      port: parseInt(sctpPort[0].substr(12), 10),\n      protocol: mline.fmt,\n      maxMessageSize,\n    };\n  }\n  const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n  if (sctpMapLines.length > 0) {\n    const parts = sctpMapLines[0]\n      .substr(10)\n      .split(' ');\n    return {\n      port: parseInt(parts[0], 10),\n      protocol: parts[1],\n      maxMessageSize,\n    };\n  }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n  let output = [];\n  if (media.protocol !== 'DTLS/SCTP') {\n    output = [\n      'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n      'c=IN IP4 0.0.0.0\\r\\n',\n      'a=sctp-port:' + sctp.port + '\\r\\n',\n    ];\n  } else {\n    output = [\n      'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n      'c=IN IP4 0.0.0.0\\r\\n',\n      'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n    ];\n  }\n  if (sctp.maxMessageSize !== undefined) {\n    output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n  }\n  return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n  return Math.random().toString().substr(2, 21);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n  let sessionId;\n  const version = sessVer !== undefined ? sessVer : 2;\n  if (sessId) {\n    sessionId = sessId;\n  } else {\n    sessionId = SDPUtils.generateSessionId();\n  }\n  const user = sessUser || 'thisisadapterortc';\n  // FIXME: sess-id should be an NTP timestamp.\n  return 'v=0\\r\\n' +\n      'o=' + user + ' ' + sessionId + ' ' + version +\n        ' IN IP4 127.0.0.1\\r\\n' +\n      's=-\\r\\n' +\n      't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n  // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n  const lines = SDPUtils.splitLines(mediaSection);\n  for (let i = 0; i < lines.length; i++) {\n    switch (lines[i]) {\n      case 'a=sendrecv':\n      case 'a=sendonly':\n      case 'a=recvonly':\n      case 'a=inactive':\n        return lines[i].substr(2);\n      default:\n        // FIXME: What should happen here?\n    }\n  }\n  if (sessionpart) {\n    return SDPUtils.getDirection(sessionpart);\n  }\n  return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n  const lines = SDPUtils.splitLines(mediaSection);\n  const mline = lines[0].split(' ');\n  return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n  return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n  const lines = SDPUtils.splitLines(mediaSection);\n  const parts = lines[0].substr(2).split(' ');\n  return {\n    kind: parts[0],\n    port: parseInt(parts[1], 10),\n    protocol: parts[2],\n    fmt: parts.slice(3).join(' '),\n  };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n  const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n  const parts = line.substr(2).split(' ');\n  return {\n    username: parts[0],\n    sessionId: parts[1],\n    sessionVersion: parseInt(parts[2], 10),\n    netType: parts[3],\n    addressType: parts[4],\n    address: parts[5],\n  };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n  if (typeof blob !== 'string' || blob.length === 0) {\n    return false;\n  }\n  const lines = SDPUtils.splitLines(blob);\n  for (let i = 0; i < lines.length; i++) {\n    if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n      return false;\n    }\n    // TODO: check the modifier a bit more.\n  }\n  return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n  module.exports = SDPUtils;\n}\n","/**\n * The errors for the conference.\n */\n\nexport enum JitsiConferenceErrors {\n    /**\n     * Indicates that client must be authenticated to create the conference.\n     */\n    AUTHENTICATION_REQUIRED = 'conference.authenticationRequired',\n\n    /**\n     * Indicates that chat error occurred.\n     */\n    CHAT_ERROR = 'conference.chatError',\n\n    /**\n     * Indicates that a settings error occurred.\n     */\n    SETTINGS_ERROR = 'conference.settingsError',\n\n    /**\n     * Indicates that conference has been destroyed.\n     */\n    CONFERENCE_DESTROYED = 'conference.destroyed',\n\n    /**\n     * Indicates that max users limit has been reached.\n     */\n    CONFERENCE_MAX_USERS = 'conference.max_users',\n\n    /**\n     * Indicates that a connection error occurred when trying to join a conference.\n     */\n    CONNECTION_ERROR = 'conference.connectionError',\n\n    /**\n     * Indicates that the client has been forced to restart by jicofo when the\n     * conference was migrated from one bridge to another.\n     */\n    CONFERENCE_RESTARTED = 'conference.restarted',\n\n    /**\n     * Indicates that a connection error is due to not allowed,\n     * occurred when trying to join a conference.\n     */\n    NOT_ALLOWED_ERROR = 'conference.connectionError.notAllowed',\n\n    /**\n     * Indicates that a connection error is due to not allowed,\n     * occurred when trying to join a conference, only approved members are allowed to join.\n     */\n    MEMBERS_ONLY_ERROR = 'conference.connectionError.membersOnly',\n\n    /**\n     * Indicates that a connection error is due to denied access to the room,\n     * occurred after joining a lobby room and access is denied by the room moderators.\n     */\n    CONFERENCE_ACCESS_DENIED = 'conference.connectionError.accessDenied',\n\n    /**\n     * Indicates that focus error happened.\n     */\n    FOCUS_DISCONNECTED = 'conference.focusDisconnected',\n\n    /**\n     * Indicates that focus left the conference.\n     */\n    FOCUS_LEFT = 'conference.focusLeft',\n\n    /**\n     * Indicates that graceful shutdown happened.\n     */\n    GRACEFUL_SHUTDOWN = 'conference.gracefulShutdown',\n\n    /**\n     * Indicates that the media connection has failed.\n     */\n    ICE_FAILED = 'conference.iceFailed',\n\n    /**\n     * Indicates that the versions of the server side components are incompatible\n     * with the client side.\n     */\n    INCOMPATIBLE_SERVER_VERSIONS = 'conference.incompatible_server_versions',\n\n    /**\n     * Indicates that offer/answer had failed.\n     */\n    OFFER_ANSWER_FAILED = 'conference.offerAnswerFailed',\n\n    /**\n     * Indicates that password cannot be set for this conference.\n     */\n    PASSWORD_NOT_SUPPORTED = 'conference.passwordNotSupported',\n\n    /**\n     * Indicates that a password is required in order to join the conference.\n     */\n    PASSWORD_REQUIRED = 'conference.passwordRequired',\n\n    /**\n     * Indicates that reservation system returned error.\n     */\n    RESERVATION_ERROR = 'conference.reservationError',\n\n    /**\n     * Indicates that there is no available videobridge.\n     */\n    VIDEOBRIDGE_NOT_AVAILABLE = 'conference.videobridgeNotAvailable'\n};\n\n// exported for backward compatibility\nexport const AUTHENTICATION_REQUIRED = JitsiConferenceErrors.AUTHENTICATION_REQUIRED;\nexport const CHAT_ERROR = JitsiConferenceErrors.CHAT_ERROR;\nexport const SETTINGS_ERROR = JitsiConferenceErrors.SETTINGS_ERROR;\nexport const CONFERENCE_DESTROYED = JitsiConferenceErrors.CONFERENCE_DESTROYED;\nexport const CONFERENCE_MAX_USERS = JitsiConferenceErrors.CONFERENCE_MAX_USERS;\nexport const CONNECTION_ERROR = JitsiConferenceErrors.CONNECTION_ERROR;\nexport const CONFERENCE_RESTARTED = JitsiConferenceErrors.CONFERENCE_RESTARTED;\nexport const NOT_ALLOWED_ERROR = JitsiConferenceErrors.NOT_ALLOWED_ERROR;\nexport const MEMBERS_ONLY_ERROR = JitsiConferenceErrors.MEMBERS_ONLY_ERROR;\nexport const CONFERENCE_ACCESS_DENIED = JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED;\nexport const FOCUS_DISCONNECTED = JitsiConferenceErrors.FOCUS_DISCONNECTED;\nexport const FOCUS_LEFT = JitsiConferenceErrors.FOCUS_LEFT;\nexport const GRACEFUL_SHUTDOWN = JitsiConferenceErrors.GRACEFUL_SHUTDOWN;\nexport const ICE_FAILED = JitsiConferenceErrors.ICE_FAILED;\nexport const INCOMPATIBLE_SERVER_VERSIONS = JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS;\nexport const OFFER_ANSWER_FAILED = JitsiConferenceErrors.OFFER_ANSWER_FAILED;\nexport const PASSWORD_NOT_SUPPORTED = JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED;\nexport const PASSWORD_REQUIRED = JitsiConferenceErrors.PASSWORD_REQUIRED;\nexport const RESERVATION_ERROR = JitsiConferenceErrors.RESERVATION_ERROR;\nexport const VIDEOBRIDGE_NOT_AVAILABLE = JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE;\n","/**\n * The events for the conference.\n */\n\nexport enum JitsiConferenceEvents {\n    /**\n     * Event indicates that the current conference audio input switched between audio\n     * input states,i.e. with or without audio input.\n     */\n    AUDIO_INPUT_STATE_CHANGE = 'conference.audio_input_state_changed',\n\n    /**\n     * Event indicates that the permission for unmuting audio has changed based on the number of audio senders in the call\n     * and the audio sender limit configured in Jicofo.\n     */\n    AUDIO_UNMUTE_PERMISSIONS_CHANGED = 'conference.audio_unmute_permissions_changed',\n\n    /**\n     * Indicates that authentication status changed.\n     */\n    AUTH_STATUS_CHANGED = 'conference.auth_status_changed',\n\n    /**\n     * Fired just before the statistics module is disposed and it's the last chance\n     * to submit some logs to the statistics service (ex. CallStats if enabled),\n     * before it's disconnected.\n     */\n    BEFORE_STATISTICS_DISPOSED = 'conference.beforeStatisticsDisposed',\n\n    /**\n     * Indicates that an error occurred.\n     */\n    CONFERENCE_ERROR = 'conference.error',\n\n    /**\n     * Indicates that conference failed.\n     */\n    CONFERENCE_FAILED = 'conference.failed',\n\n    /**\n     * Indicates that conference is in progress of joining.\n     */\n    CONFERENCE_JOIN_IN_PROGRESS = 'conference.join_in_progress',\n\n    /**\n     * Indicates that conference has been joined. The event does NOT provide any\n     * parameters to its listeners.\n     */\n    CONFERENCE_JOINED = 'conference.joined',\n\n    /**\n     * Indicates that conference has been left.\n     */\n    CONFERENCE_LEFT = 'conference.left',\n\n    /**\n     * Indicates that the conference unique identifier has been set.\n     */\n    CONFERENCE_UNIQUE_ID_SET = 'conference.unique_id_set',\n\n    /**\n     * Indicates that the connection to the conference has been established\n     * XXX This is currently fired when the *ICE* connection enters 'connected'\n     * state for the first time.\n     */\n    CONNECTION_ESTABLISHED = 'conference.connectionEstablished',\n\n    /**\n     * Indicates that the connection to the conference has been interrupted for some\n     * reason.\n     * XXX This is currently fired when the *ICE* connection is interrupted.\n     */\n    CONNECTION_INTERRUPTED = 'conference.connectionInterrupted',\n\n    /**\n     * Indicates that the connection to the conference has been restored.\n     * XXX This is currently fired when the *ICE* connection is restored.\n     */\n    CONNECTION_RESTORED = 'conference.connectionRestored',\n\n    /**\n     * A connection to the video bridge's data channel has been established.\n     */\n    DATA_CHANNEL_OPENED = 'conference.dataChannelOpened',\n\n    /**\n     * A user has changed it display name\n     */\n    DISPLAY_NAME_CHANGED = 'conference.displayNameChanged',\n\n    /**\n     * The dominant speaker was changed.\n     */\n    DOMINANT_SPEAKER_CHANGED = 'conference.dominantSpeaker',\n\n    /**\n     * UTC conference timestamp when first participant joined.\n     */\n    CONFERENCE_CREATED_TIMESTAMP = 'conference.createdTimestamp',\n\n    /**\n     * Indicates that DTMF support changed.\n     */\n    DTMF_SUPPORT_CHANGED = 'conference.dtmfSupportChanged',\n\n    /**\n     * Indicates that a message from another participant is received on data\n     * channel.\n     */\n    ENDPOINT_MESSAGE_RECEIVED = 'conference.endpoint_message_received',\n\n    /**\n     * Indicates that a message for the remote endpoint statistics has been received on the bridge channel.\n     */\n    ENDPOINT_STATS_RECEIVED = 'conference.endpoint_stats_received',\n\n    /**\n     * NOTE This is lib-jitsi-meet internal event and can be removed at any time !\n     *\n     * Event emitted when conference transits, between one to one and multiparty JVB\n     * conference. If the conference switches to P2P it's neither one to one nor\n     * a multiparty JVB conference, but P2P (the status argument of this event will\n     * be <tt>false</tt>).\n     *\n     * The first argument is a boolean which carries the previous value and\n     * the seconds argument is a boolean with the new status. The event is emitted\n     * only if the previous and the new values are different.\n     *\n     * @type {string}\n     */\n    JVB121_STATUS = 'conference.jvb121Status',\n\n    /**\n     * You are kicked from the conference.\n     * @param {JitsiParticipant} the participant that initiated the kick.\n     */\n    KICKED = 'conference.kicked',\n\n    /**\n     * Participant was kicked from the conference.\n     * @param {JitsiParticipant} the participant that initiated the kick.\n     * @param {JitsiParticipant} the participant that was kicked.\n     */\n    PARTICIPANT_KICKED = 'conference.participant_kicked',\n\n    /**\n     * The Last N set is changed.\n     *\n     * @param {Array<string>|null} leavingEndpointIds the ids of all the endpoints\n     * which are leaving Last N\n     * @param {Array<string>|null} enteringEndpointIds the ids of all the endpoints\n     * which are entering Last N\n     */\n    LAST_N_ENDPOINTS_CHANGED = 'conference.lastNEndpointsChanged',\n\n    /**\n     * The forwarded sources set is changed.\n     *\n     * @param {Array<string>} leavingForwardedSources the sourceNames of all the tracks which are leaving forwarded\n     * sources\n     * @param {Array<string>} enteringForwardedSources the sourceNames of all the tracks which are entering forwarded\n     * sources\n     */\n    FORWARDED_SOURCES_CHANGED = 'conference.forwardedSourcesChanged',\n\n    /**\n     * Indicates that the room has been locked or unlocked.\n     */\n    LOCK_STATE_CHANGED = 'conference.lock_state_changed',\n\n    /**\n     * Indicates that the region of the media server (jitsi-videobridge) that we\n     * are connected to changed (or was initially set).\n     * @type {string} the region.\n     */\n    SERVER_REGION_CHANGED = 'conference.server_region_changed',\n\n    /**\n     * An event(library-private) fired when a new media session is added to the conference.\n     * @type {string}\n     * @private\n     */\n    _MEDIA_SESSION_STARTED = 'conference.media_session.started',\n\n    /**\n     * An event(library-private) fired when the conference switches the currently active media session.\n     * @type {string}\n     * @private\n     */\n    _MEDIA_SESSION_ACTIVE_CHANGED = 'conference.media_session.active_changed',\n\n    /**\n     * Indicates that the conference had changed to members only enabled/disabled.\n     * The first argument of this event is a <tt>boolean</tt> which when set to\n     * <tt>true</tt> means that the conference is running in members only mode.\n     * You may need to use Lobby if supported to ask for permissions to enter the conference.\n     */\n    MEMBERS_ONLY_CHANGED = 'conference.membersOnlyChanged',\n\n    /**\n     * New text message was received.\n     */\n    MESSAGE_RECEIVED = 'conference.messageReceived',\n\n    /**\n     * Event indicates that the current selected input device has no signal\n     */\n    NO_AUDIO_INPUT = 'conference.no_audio_input',\n\n    /**\n     * Event indicates that the current microphone used by the conference is noisy.\n     */\n    NOISY_MIC = 'conference.noisy_mic',\n\n    /**\n     * Indicates that a message from the local user or from the Prosody backend\n     * was received on the data channel.\n     */\n    NON_PARTICIPANT_MESSAGE_RECEIVED = 'conference.non_participant_message_received',\n\n    /**\n     * New private text message was received.\n     */\n    PRIVATE_MESSAGE_RECEIVED = 'conference.privateMessageReceived',\n\n    /**\n     * Event fired when JVB sends notification about interrupted/restored user's\n     * ICE connection status or we detect local problem with the video track.\n     * First argument is the ID of the participant and\n     * the seconds is a string indicating if the connection is currently\n     * - active - the connection is active\n     * - inactive - the connection is inactive, was intentionally interrupted by\n     * the bridge\n     * - interrupted - a network problem occurred\n     * - restoring - the connection was inactive and is restoring now\n     *\n     * The current status value can be obtained by calling\n     * JitsiParticipant.getConnectionStatus().\n     */\n    PARTICIPANT_CONN_STATUS_CHANGED = 'conference.participant_conn_status_changed',\n\n    /**\n     * Indicates that the features of the participant has been changed.\n     * TODO: there is a spelling mistake in this event name and associated constants\n     */\n    PARTCIPANT_FEATURES_CHANGED = 'conference.partcipant_features_changed',\n\n    /**\n     * Indicates that a the value of a specific property of a specific participant\n     * has changed.\n     */\n    PARTICIPANT_PROPERTY_CHANGED = 'conference.participant_property_changed',\n\n    /**\n     * Indicates that the conference has switched between JVB and P2P connections.\n     * The first argument of this event is a <tt>boolean</tt> which when set to\n     * <tt>true</tt> means that the conference is running on the P2P connection.\n     */\n    P2P_STATUS = 'conference.p2pStatus',\n\n    /**\n     * Indicates that phone number changed.\n     */\n    PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged',\n\n    /**\n     * The conference properties changed.\n     * @type {string}\n     */\n    PROPERTIES_CHANGED = 'conference.propertiesChanged',\n\n    /**\n     * Indicates that recording state changed.\n     */\n    RECORDER_STATE_CHANGED = 'conference.recorderStateChanged',\n\n    /**\n     * Indicates that video SIP GW state changed.\n     * @param {VideoSIPGWConstants} status.\n     */\n    VIDEO_SIP_GW_AVAILABILITY_CHANGED = 'conference.videoSIPGWAvailabilityChanged',\n\n    /**\n     * Indicates that video SIP GW Session state changed.\n     * @param {options} event - {\n     *     {string} address,\n     *     {VideoSIPGWConstants} oldState,\n     *     {VideoSIPGWConstants} newState,\n     *     {string} displayName}\n     * }.\n     */\n    VIDEO_SIP_GW_SESSION_STATE_CHANGED = 'conference.videoSIPGWSessionStateChanged',\n\n    /**\n     * Indicates that start muted settings changed.\n     */\n    START_MUTED_POLICY_CHANGED = 'conference.start_muted_policy_changed',\n\n    /**\n     * Indicates that the local user has started muted.\n     */\n    STARTED_MUTED = 'conference.started_muted',\n\n    /**\n     * Indicates that subject of the conference has changed.\n     */\n    SUBJECT_CHANGED = 'conference.subjectChanged',\n\n    /**\n     * Indicates that DTMF support changed.\n     */\n    SUSPEND_DETECTED = 'conference.suspendDetected',\n\n    /**\n     * Event indicates that local user is talking while he muted himself\n     */\n    TALK_WHILE_MUTED = 'conference.talk_while_muted',\n\n    /**\n     * A new media track was added to the conference. The event provides the\n     * following parameters to its listeners:\n     *\n     * @param {JitsiTrack} track the added JitsiTrack\n     */\n    TRACK_ADDED = 'conference.trackAdded',\n\n    /**\n     * Audio levels of a media track ( attached to the conference) was changed.\n     */\n    TRACK_AUDIO_LEVEL_CHANGED = 'conference.audioLevelsChanged',\n\n    /**\n     * A media track ( attached to the conference) mute status was changed.\n     * @param {JitsiParticipant|null} the participant that initiated the mute\n     * if it is a remote mute.\n     */\n    TRACK_MUTE_CHANGED = 'conference.trackMuteChanged',\n\n    /**\n     * The media track was removed from the conference. The event provides the\n     * following parameters to its listeners:\n     *\n     * @param {JitsiTrack} track the removed JitsiTrack\n     */\n    TRACK_REMOVED = 'conference.trackRemoved',\n\n    /**\n     * The source-add for unmuting of a media track was rejected by Jicofo.\n     *\n     */\n    TRACK_UNMUTE_REJECTED = 'conference.trackUnmuteRejected',\n\n    /**\n     * Notifies for transcription status changes. The event provides the\n     * following parameters to its listeners:\n     *\n     * @param {String} status - The new status.\n     */\n    TRANSCRIPTION_STATUS_CHANGED = 'conference.transcriptionStatusChanged',\n\n    /**\n     * A new user joined the conference.\n     */\n    USER_JOINED = 'conference.userJoined',\n\n    /**\n     * A user has left the conference.\n     */\n    USER_LEFT = 'conference.userLeft',\n\n    /**\n     * User role changed.\n     */\n    USER_ROLE_CHANGED = 'conference.roleChanged',\n\n    /**\n     * User status changed.\n     */\n    USER_STATUS_CHANGED = 'conference.statusChanged',\n\n    /**\n     * Event indicates that the permission for unmuting video has changed based on the number of video senders in the call\n     * and the video sender limit configured in Jicofo.\n     */\n    VIDEO_UNMUTE_PERMISSIONS_CHANGED = 'conference.video_unmute_permissions_changed',\n\n    /**\n     * Event indicates that the bot participant type changed.\n     */\n    BOT_TYPE_CHANGED = 'conference.bot_type_changed',\n\n    /**\n     * A new user joined the lobby room.\n     */\n    LOBBY_USER_JOINED = 'conference.lobby.userJoined',\n\n    /**\n     * A user from the lobby room has been update.\n     */\n    LOBBY_USER_UPDATED = 'conference.lobby.userUpdated',\n\n    /**\n     * A user left the lobby room.\n     */\n    LOBBY_USER_LEFT = 'conference.lobby.userLeft',\n\n    /**\n     * The local participant was approved to be able to unmute.\n     * @param {options} event - {\n     *     {MediaType} mediaType\n     * }.\n     */\n    AV_MODERATION_APPROVED = 'conference.av_moderation.approved',\n\n    /**\n     * The local participant was blocked to be able to unmute.\n     * @param {options} event - {\n     *     {MediaType} mediaType\n     * }.\n     */\n    AV_MODERATION_REJECTED = 'conference.av_moderation.rejected',\n\n    /**\n     * AV Moderation was enabled/disabled. The actor is the participant that is currently in the meeting,\n     * or undefined if that participant has left the meeting.\n     *\n     * @param {options} event - {\n     *     {boolean} enabled,\n     *     {MediaType} mediaType,\n     *     {JitsiParticipant} actor\n     * }.\n     */\n    AV_MODERATION_CHANGED = 'conference.av_moderation.changed',\n\n    /**\n     * AV Moderation, report for user being approved to unmute.\n     * @param {options} event - {\n     *     {JitsiParticipant} participant,\n     *     {MediaType} mediaType\n     * }.\n     */\n    AV_MODERATION_PARTICIPANT_APPROVED = 'conference.av_moderation.participant.approved',\n\n    /**\n     * AV Moderation, report for user being blocked to unmute.\n     * @param {options} event - {\n     *     {JitsiParticipant} participant,\n     *     {MediaType} mediaType\n     * }.\n     */\n    AV_MODERATION_PARTICIPANT_REJECTED = 'conference.av_moderation.participant.rejected',\n\n    /**\n     * A new facial expression is added with its duration for a participant\n     */\n    FACIAL_EXPRESSION_ADDED = 'conference.facial_expression.added',\n\n    /**\n     * Event fired when a participant is requested to join a given (breakout) room.\n     */\n    BREAKOUT_ROOMS_MOVE_TO_ROOM = 'conference.breakout-rooms.move-to-room',\n\n    /**\n     * Event fired when the breakout rooms data was updated.\n     */\n    BREAKOUT_ROOMS_UPDATED = 'conference.breakout-rooms.updated'\n};\n\n// exported for backward compatibility\nexport const AUDIO_INPUT_STATE_CHANGE = JitsiConferenceEvents.AUDIO_INPUT_STATE_CHANGE;\nexport const AUDIO_UNMUTE_PERMISSIONS_CHANGED = JitsiConferenceEvents.AUDIO_UNMUTE_PERMISSIONS_CHANGED;\nexport const AUTH_STATUS_CHANGED = JitsiConferenceEvents.AUTH_STATUS_CHANGED;\nexport const BEFORE_STATISTICS_DISPOSED = JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED;\nexport const CONFERENCE_ERROR = JitsiConferenceEvents.CONFERENCE_ERROR;\nexport const CONFERENCE_FAILED = JitsiConferenceEvents.CONFERENCE_FAILED;\nexport const CONFERENCE_JOIN_IN_PROGRESS = JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS;\nexport const CONFERENCE_JOINED = JitsiConferenceEvents.CONFERENCE_JOINED;\nexport const CONFERENCE_LEFT = JitsiConferenceEvents.CONFERENCE_LEFT;\nexport const CONFERENCE_UNIQUE_ID_SET = JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET;\nexport const CONNECTION_ESTABLISHED = JitsiConferenceEvents.CONNECTION_ESTABLISHED;\nexport const CONNECTION_INTERRUPTED = JitsiConferenceEvents.CONNECTION_INTERRUPTED;\nexport const CONNECTION_RESTORED = JitsiConferenceEvents.CONNECTION_RESTORED;\nexport const DATA_CHANNEL_OPENED = JitsiConferenceEvents.DATA_CHANNEL_OPENED;\nexport const DISPLAY_NAME_CHANGED = JitsiConferenceEvents.DISPLAY_NAME_CHANGED;\nexport const DOMINANT_SPEAKER_CHANGED = JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED;\nexport const CONFERENCE_CREATED_TIMESTAMP = JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP;\nexport const DTMF_SUPPORT_CHANGED = JitsiConferenceEvents.DTMF_SUPPORT_CHANGED;\nexport const ENDPOINT_MESSAGE_RECEIVED = JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED;\nexport const ENDPOINT_STATS_RECEIVED = JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED;\nexport const JVB121_STATUS = JitsiConferenceEvents.JVB121_STATUS;\nexport const KICKED = JitsiConferenceEvents.KICKED;\nexport const PARTICIPANT_KICKED = JitsiConferenceEvents.PARTICIPANT_KICKED;\nexport const LAST_N_ENDPOINTS_CHANGED = JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED;\nexport const FORWARDED_SOURCES_CHANGED = JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED;\nexport const LOCK_STATE_CHANGED = JitsiConferenceEvents.LOCK_STATE_CHANGED;\nexport const SERVER_REGION_CHANGED = JitsiConferenceEvents.SERVER_REGION_CHANGED;\nexport const _MEDIA_SESSION_STARTED = JitsiConferenceEvents._MEDIA_SESSION_STARTED;\nexport const _MEDIA_SESSION_ACTIVE_CHANGED = JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED;\nexport const MEMBERS_ONLY_CHANGED = JitsiConferenceEvents.MEMBERS_ONLY_CHANGED;\nexport const MESSAGE_RECEIVED = JitsiConferenceEvents.MESSAGE_RECEIVED;\nexport const NO_AUDIO_INPUT = JitsiConferenceEvents.NO_AUDIO_INPUT;\nexport const NOISY_MIC = JitsiConferenceEvents.NOISY_MIC;\nexport const NON_PARTICIPANT_MESSAGE_RECEIVED = JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED;\nexport const PRIVATE_MESSAGE_RECEIVED = JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED;\nexport const PARTICIPANT_CONN_STATUS_CHANGED = JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED;\nexport const PARTCIPANT_FEATURES_CHANGED = JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED;\nexport const PARTICIPANT_PROPERTY_CHANGED = JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED;\nexport const P2P_STATUS = JitsiConferenceEvents.P2P_STATUS;\nexport const PHONE_NUMBER_CHANGED = JitsiConferenceEvents.PHONE_NUMBER_CHANGED;\nexport const PROPERTIES_CHANGED = JitsiConferenceEvents.PROPERTIES_CHANGED;\nexport const RECORDER_STATE_CHANGED = JitsiConferenceEvents.RECORDER_STATE_CHANGED;\nexport const VIDEO_SIP_GW_AVAILABILITY_CHANGED = JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED;\nexport const VIDEO_SIP_GW_SESSION_STATE_CHANGED = JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED;\nexport const START_MUTED_POLICY_CHANGED = JitsiConferenceEvents.START_MUTED_POLICY_CHANGED;\nexport const STARTED_MUTED = JitsiConferenceEvents.STARTED_MUTED;\nexport const SUBJECT_CHANGED = JitsiConferenceEvents.SUBJECT_CHANGED;\nexport const SUSPEND_DETECTED = JitsiConferenceEvents.SUSPEND_DETECTED;\nexport const TALK_WHILE_MUTED = JitsiConferenceEvents.TALK_WHILE_MUTED;\nexport const TRACK_ADDED = JitsiConferenceEvents.TRACK_ADDED;\nexport const TRACK_AUDIO_LEVEL_CHANGED = JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED;\nexport const TRACK_MUTE_CHANGED = JitsiConferenceEvents.TRACK_MUTE_CHANGED;\nexport const TRACK_REMOVED = JitsiConferenceEvents.TRACK_REMOVED;\nexport const TRACK_UNMUTE_REJECTED = JitsiConferenceEvents.TRACK_UNMUTE_REJECTED;\nexport const TRANSCRIPTION_STATUS_CHANGED = JitsiConferenceEvents.TRANSCRIPTION_STATUS_CHANGED;\nexport const USER_JOINED = JitsiConferenceEvents.USER_JOINED;\nexport const USER_LEFT = JitsiConferenceEvents.USER_LEFT;\nexport const USER_ROLE_CHANGED = JitsiConferenceEvents.USER_ROLE_CHANGED;\nexport const USER_STATUS_CHANGED = JitsiConferenceEvents.USER_STATUS_CHANGED;\nexport const VIDEO_UNMUTE_PERMISSIONS_CHANGED = JitsiConferenceEvents.VIDEO_UNMUTE_PERMISSIONS_CHANGED;\nexport const BOT_TYPE_CHANGED = JitsiConferenceEvents.BOT_TYPE_CHANGED;\nexport const LOBBY_USER_JOINED = JitsiConferenceEvents.LOBBY_USER_JOINED;\nexport const LOBBY_USER_UPDATED = JitsiConferenceEvents.LOBBY_USER_UPDATED;\nexport const LOBBY_USER_LEFT = JitsiConferenceEvents.LOBBY_USER_LEFT;\nexport const AV_MODERATION_APPROVED = JitsiConferenceEvents.AV_MODERATION_APPROVED;\nexport const AV_MODERATION_REJECTED = JitsiConferenceEvents.AV_MODERATION_REJECTED;\nexport const AV_MODERATION_CHANGED = JitsiConferenceEvents.AV_MODERATION_CHANGED;\nexport const AV_MODERATION_PARTICIPANT_APPROVED = JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED;\nexport const AV_MODERATION_PARTICIPANT_REJECTED = JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED;\nexport const FACIAL_EXPRESSION_ADDED = JitsiConferenceEvents.FACIAL_EXPRESSION_ADDED;\nexport const BREAKOUT_ROOMS_MOVE_TO_ROOM = JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM;\nexport const BREAKOUT_ROOMS_UPDATED = JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED;\n","export const CALLSTATS_SCRIPT_URL = 'https://api.callstats.io/static/callstats-ws.min.js';\n\n/**\n * The number of remote speakers for which the audio levels will be calculated using\n * RTCRtpReceiver#getSynchronizationSources. Limit the number of endpoints to save cpu on the client as this API call\n * is known to take longer to execute when there are many audio receivers.\n */\nexport const SPEAKERS_AUDIO_LEVELS = 5;\n","/**\n * The errors for the JitsiTrack objects.\n */\n\nexport enum JitsiTrackErrors {\n    /**\n     * An error which indicates that some of requested constraints in\n     * getUserMedia call were not satisfied.\n     */\n    CONSTRAINT_FAILED = 'gum.constraint_failed',\n\n    /**\n     * A generic error which indicates an error occurred while selecting\n     * a DesktopCapturerSource from the electron app.\n     */\n    ELECTRON_DESKTOP_PICKER_ERROR = 'gum.electron_desktop_picker_error',\n\n    /**\n     * An error which indicates a custom desktop picker could not be detected\n     * for the electron app.\n     */\n    ELECTRON_DESKTOP_PICKER_NOT_FOUND = 'gum.electron_desktop_picker_not_found',\n\n    /**\n     * Generic getUserMedia error.\n     */\n    GENERAL = 'gum.general',\n\n    /**\n     * An error which indicates that requested device was not found.\n     */\n    NOT_FOUND = 'gum.not_found',\n\n    /**\n     * An error which indicates that user denied permission to share requested\n     * device.\n     */\n    PERMISSION_DENIED = 'gum.permission_denied',\n\n    /**\n     * Generic error for screensharing failure.\n     */\n    SCREENSHARING_GENERIC_ERROR = 'gum.screensharing_generic_error',\n\n    /**\n     * An error which indicates that user canceled screen sharing window\n     * selection dialog.\n     */\n    SCREENSHARING_USER_CANCELED = 'gum.screensharing_user_canceled',\n\n    /**\n     * Indicates that the timeout passed to the obtainAudioAndVideoPermissions has expired without GUM resolving.\n     */\n    TIMEOUT = 'gum.timeout',\n\n    /**\n     * An error which indicates that track has been already disposed and cannot\n     * be longer used.\n     */\n    TRACK_IS_DISPOSED = 'track.track_is_disposed',\n\n    /**\n     * An error which indicates that track has no MediaStream associated.\n     */\n    TRACK_NO_STREAM_FOUND = 'track.no_stream_found',\n\n    /**\n     * An error which indicates that requested video resolution is not supported\n     * by a webcam.\n     */\n    UNSUPPORTED_RESOLUTION = 'gum.unsupported_resolution'\n}\n\n// exported for backward compatibility\nexport const CONSTRAINT_FAILED = JitsiTrackErrors.CONSTRAINT_FAILED;\nexport const ELECTRON_DESKTOP_PICKER_ERROR = JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR;\nexport const ELECTRON_DESKTOP_PICKER_NOT_FOUND = JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND;\nexport const GENERAL = JitsiTrackErrors.GENERAL;\nexport const NOT_FOUND = JitsiTrackErrors.NOT_FOUND;\nexport const PERMISSION_DENIED = JitsiTrackErrors.PERMISSION_DENIED;\nexport const SCREENSHARING_GENERIC_ERROR = JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR;\nexport const SCREENSHARING_USER_CANCELED = JitsiTrackErrors.SCREENSHARING_USER_CANCELED;\nexport const TIMEOUT = JitsiTrackErrors.TIMEOUT;\nexport const TRACK_IS_DISPOSED = JitsiTrackErrors.TRACK_IS_DISPOSED;\nexport const TRACK_NO_STREAM_FOUND = JitsiTrackErrors.TRACK_NO_STREAM_FOUND;\nexport const UNSUPPORTED_RESOLUTION = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;\n","import * as JitsiTrackErrors from './JitsiTrackErrors';\n\nconst TRACK_ERROR_TO_MESSAGE_MAP = {};\n\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]\n    = 'Video resolution is not supported: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.SCREENSHARING_USER_CANCELED]\n    = 'User canceled screen sharing prompt';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR]\n    = 'Unknown error from screensharing';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR]\n    = 'Unkown error from desktop picker';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND]\n    = 'Failed to detect desktop picker';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.GENERAL]\n    = 'Generic getUserMedia error';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.PERMISSION_DENIED]\n    = 'User denied permission to use device(s): ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.NOT_FOUND]\n    = 'Requested device(s) was/were not found: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.CONSTRAINT_FAILED]\n    = 'Constraint could not be satisfied: ';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TIMEOUT]\n    = 'Could not start media source. Timeout occured!';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_IS_DISPOSED]\n    = 'Track has been already disposed';\nTRACK_ERROR_TO_MESSAGE_MAP[JitsiTrackErrors.TRACK_NO_STREAM_FOUND]\n    = 'Track does not have an associated Media Stream';\n\n// FIXME: Using prototype inheritance because otherwise instanceof is not\n// working properly (see https://github.com/babel/babel/issues/3083)\n\n/**\n *\n * Represents an error that occurred to a JitsiTrack. Can represent various\n * types of errors. For error descriptions (@see JitsiTrackErrors).\n *\n * @extends Error\n *\n *\n * @constructor\n * @param {Object|string} error - error object or error name\n * @param {Object|string} (options) - getUserMedia constraints object or\n * error message\n * @param {('audio'|'video'|'desktop'|'screen'|'audiooutput')[]} (devices) -\n * list of getUserMedia requested devices\n */\nfunction JitsiTrackError(error, options, devices) {\n    if (typeof error === 'object' && typeof error.name !== 'undefined') {\n        /**\n         * Additional information about original getUserMedia error\n         * and constraints.\n         * @type {{\n         *     error: Object,\n         *     constraints: Object,\n         *     devices: Array.<'audio'|'video'|'desktop'|'screen'>\n         * }}\n         */\n        this.gum = {\n            error,\n            constraints: options,\n            devices: devices && Array.isArray(devices)\n                ? devices.slice(0)\n                : undefined\n        };\n\n        switch (error.name) {\n        case 'NotAllowedError':\n        case 'PermissionDeniedError':\n        case 'SecurityError':\n            this.name = JitsiTrackErrors.PERMISSION_DENIED;\n            this.message\n                = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n                    + (this.gum.devices || []).join(', ');\n            break;\n        case 'DevicesNotFoundError':\n        case 'NotFoundError':\n            this.name = JitsiTrackErrors.NOT_FOUND;\n            this.message\n                = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n                    + (this.gum.devices || []).join(', ');\n            break;\n        case 'ConstraintNotSatisfiedError':\n        case 'OverconstrainedError': {\n            const constraintName = error.constraintName || error.constraint;\n\n            // we treat deviceId as unsupported resolution, as we want to\n            // retry and finally if everything fails to remove deviceId from\n            // mandatory constraints\n            if (options\n                    && options.video\n                    && (!devices || devices.indexOf('video') > -1)\n                    && (constraintName === 'minWidth'\n                        || constraintName === 'maxWidth'\n                        || constraintName === 'minHeight'\n                        || constraintName === 'maxHeight'\n                        || constraintName === 'width'\n                        || constraintName === 'height'\n                        || constraintName === 'deviceId')) {\n                this.name = JitsiTrackErrors.UNSUPPORTED_RESOLUTION;\n                this.message\n                    = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n                        + getResolutionFromFailedConstraint(\n                            constraintName,\n                            options);\n            } else {\n                this.name = JitsiTrackErrors.CONSTRAINT_FAILED;\n                this.message\n                    = TRACK_ERROR_TO_MESSAGE_MAP[this.name]\n                        + error.constraintName;\n            }\n            break;\n        }\n\n        default:\n            this.name = JitsiTrackErrors.GENERAL;\n            this.message\n                = error.message || TRACK_ERROR_TO_MESSAGE_MAP[this.name];\n            break;\n        }\n    } else if (typeof error === 'string') {\n        if (TRACK_ERROR_TO_MESSAGE_MAP[error]) {\n            this.name = error;\n            this.message = options || TRACK_ERROR_TO_MESSAGE_MAP[error];\n        } else {\n            // this is some generic error that do not fit any of our\n            // pre-defined errors, so don't give it any specific name, just\n            // store message\n            this.message = error;\n        }\n    } else {\n        throw new Error('Invalid arguments');\n    }\n\n    this.stack = error.stack || new Error().stack;\n}\n\nJitsiTrackError.prototype = Object.create(Error.prototype);\nJitsiTrackError.prototype.constructor = JitsiTrackError;\n\n/**\n * Gets failed resolution constraint from corresponding object.\n * @param {string} failedConstraintName\n * @param {Object} constraints\n * @returns {string|number}\n */\nfunction getResolutionFromFailedConstraint(failedConstraintName, constraints) {\n    if (constraints && constraints.video && constraints.video.mandatory) {\n        switch (failedConstraintName) {\n        case 'width':\n            return constraints.video.mandatory.minWidth;\n        case 'height':\n            return constraints.video.mandatory.minHeight;\n        default:\n            return constraints.video.mandatory[failedConstraintName] || '';\n        }\n    }\n\n    return '';\n}\n\nexport default JitsiTrackError;\n","/**\n * This class exports constants and factory methods related to the analytics\n * API provided by AnalyticsAdapter. In order for entries in a database to be\n * somewhat easily traceable back to the code which produced them, events sent\n * through analytics should be defined here.\n *\n * Since the AnalyticsAdapter API can be used in different ways, for some events\n * it is more convenient to just define the event name as a constant. For other\n * events a factory function is easier.\n *\n * A general approach for adding a new event:\n * 1. Determine the event type: track, UI, page, or operational. If in doubt use\n * operational.\n * 2. Determine whether the event is related to other existing events, and\n * which fields are desired to be set: name, action, actionSubject, source.\n * 3. If the name is sufficient (the other fields are not important), use a\n * constant. Otherwise use a factory function.\n *\n * Note that the AnalyticsAdapter uses the events passed to its functions for\n * its own purposes, and might modify them. Because of this, factory functions\n * should create new objects.\n *\n */\n\nexport enum AnalyticsEvents {\n    /**\n     * The constant which identifies an event of type \"operational\".\n     */\n    TYPE_OPERATIONAL = 'operational',\n\n    /**\n     * The constant which identifies an event of type \"page\".\n     */\n    TYPE_PAGE = 'page',\n\n    /**\n     * The constant which identifies an event of type \"track\".\n     */\n    TYPE_TRACK = 'track',\n\n    /**\n     * The constant which identifies an event of type \"ui\".\n     */\n    TYPE_UI = 'ui',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that the Jingle session\n     * was restarted (TODO: verify/fix the documentation)\n     */\n    ACTION_JINGLE_RESTART = 'restart',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that a session-accept\n     * timed out (TODO: verify/fix the documentation)\n     */\n    ACTION_JINGLE_SA_TIMEOUT = 'session-accept.timeout',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that a session-initiate\n     * was received.\n     */\n    ACTION_JINGLE_SI_RECEIVED = 'session-initiate.received',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that a session-initiate\n     * not arrived within a timeout (the value is specified in\n     * the {@link JingleSessionPC}.\n     */\n    ACTION_JINGLE_SI_TIMEOUT = 'session-initiate.timeout',\n\n    /**\n     * A constant for the \"terminate\" action for Jingle events. TODO: verify/fix\n     * the documentation)\n     */\n    ACTION_JINGLE_TERMINATE = 'terminate',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that a transport-replace\n     * was received.\n     */\n    ACTION_JINGLE_TR_RECEIVED = 'transport-replace.received',\n\n    /**\n     * The \"action\" value for Jingle events which indicates that a transport-replace\n     * succeeded (TODO: verify/fix the documentation)\n     */\n    ACTION_JINGLE_TR_SUCCESS = 'transport-replace.success',\n\n    /**\n     * The \"action\" value for P2P events which indicates that P2P session initiate message has been rejected by the client\n     * because the mandatory requirements were not met.\n     */\n    ACTION_P2P_DECLINED = 'decline',\n\n    /**\n     * The \"action\" value for P2P events which indicates that a connection was\n     * established (TODO: verify/fix the documentation)\n     */\n    ACTION_P2P_ESTABLISHED = 'established',\n\n    /**\n     * The \"action\" value for P2P events which indicates that something failed.\n     */\n    ACTION_P2P_FAILED = 'failed',\n\n    /**\n     * The \"action\" value for P2P events which indicates that a switch to\n     * jitsi-videobridge happened.\n     */\n    ACTION_P2P_SWITCH_TO_JVB = 'switch.to.jvb',\n\n    /**\n     * The name of an event which indicates an available device. We send one such\n     * event per available device once when the available devices are first known,\n     * and every time that they change\n     *\n     * Properties:\n     *      audio_input_device_count: the number of audio input devices available at\n     *          the time the event was sent.\n     *      audio_output_device_count: the number of audio output devices available\n     *          at the time the event was sent.\n     *      video_input_device_count: the number of video input devices available at\n     *          the time the event was sent.\n     *      video_output_device_count: the number of video output devices available\n     *          at the time the event was sent.\n     *      device_id: an identifier of the device described in this event.\n     *      device_group_id:\n     *      device_kind: one of 'audioinput', 'audiooutput', 'videoinput' or\n     *          'videooutput'.\n     *      device_label: a string which describes the device.\n     */\n    AVAILABLE_DEVICE = 'available.device',\n\n    /**\n     * This appears to be fired only in certain cases when the XMPP connection\n     * disconnects (and it was intentional?). It is currently never observed to\n     * fire in production.\n     *\n     * TODO: document\n     *\n     * Properties:\n     *      message: an error message\n     */\n    CONNECTION_DISCONNECTED = 'connection.disconnected',\n\n    /**\n     * Indicates that the user of the application provided feedback in terms of a\n     * rating (an integer from 1 to 5) and an optional comment.\n     * Properties:\n     *      value: the user's rating (an integer from 1 to 5)\n     *      comment: the user's comment\n     */\n    FEEDBACK = 'feedback',\n\n    /**\n     * Indicates the duration of a particular phase of the ICE connectivity\n     * establishment.\n     *\n     * Properties:\n     *      phase: the ICE phase (e.g. 'gathering', 'checking', 'establishment')\n     *      value: the duration in milliseconds.\n     *      p2p: whether the associated ICE connection is p2p or towards a\n     *          jitsi-videobridge\n     *      initiator: whether the local Jingle peer is the initiator or responder\n     *          in the Jingle session. XXX we probably actually care about the ICE\n     *          role (controlling vs controlled), and we assume that this correlates\n     *          with the Jingle initiator.\n     */\n    ICE_DURATION = 'ice.duration',\n\n    /**\n     * Indicates the difference in milliseconds between the ICE establishment time\n     * for the P2P and JVB connections (e.g. a value of 10 would indicate that the\n     * P2P connection took 10ms more than JVB connection to establish).\n     *\n     * Properties:\n     *      value: the difference in establishment durations in milliseconds.\n     *\n     */\n    ICE_ESTABLISHMENT_DURATION_DIFF = 'ice.establishment.duration.diff',\n\n    /**\n     * Indicates that the ICE state has changed.\n     *\n     * Properties:\n     *      state: the ICE state which was entered (e.g. 'checking', 'connected',\n     *          'completed', etc).\n     *      value: the time in milliseconds (as reported by\n     *          window.performance.now()) that the state change occurred.\n     *      p2p: whether the associated ICE connection is p2p or towards a\n     *          jitsi-videobridge\n     *      signalingState: The signaling state of the associated PeerConnection\n     *      reconnect: whether the associated Jingle session is in the process of\n     *          reconnecting (or is it ICE? TODO: verify/fix the documentation)\n     */\n    ICE_STATE_CHANGED = 'ice.state.changed',\n\n    /**\n     * Indicates that no bytes have been sent for the track.\n     *\n     * Properties:\n     *      mediaType: the media type of the local track ('audio' or 'video').\n     */\n    NO_BYTES_SENT = 'track.no-bytes-sent',\n\n    /**\n     * Indicates that a track was unmuted (?).\n     *\n     * Properties:\n     *      mediaType: the media type of the local track ('audio' or 'video').\n     *      trackType: the type of the track ('local' or 'remote').\n     *      value: TODO: document\n     */\n    TRACK_UNMUTED = 'track.unmuted'\n}\n\n// exported for backward compatibility\nexport const TYPE_OPERATIONAL = AnalyticsEvents.TYPE_OPERATIONAL;\nexport const TYPE_PAGE = AnalyticsEvents.TYPE_PAGE;\nexport const TYPE_TRACK = AnalyticsEvents.TYPE_TRACK;\nexport const TYPE_UI = AnalyticsEvents.TYPE_UI;\nexport const ACTION_JINGLE_RESTART = AnalyticsEvents.ACTION_JINGLE_RESTART;\nexport const ACTION_JINGLE_SA_TIMEOUT = AnalyticsEvents.ACTION_JINGLE_SA_TIMEOUT;\nexport const ACTION_JINGLE_SI_RECEIVED = AnalyticsEvents.ACTION_JINGLE_SI_RECEIVED;\nexport const ACTION_JINGLE_SI_TIMEOUT = AnalyticsEvents.ACTION_JINGLE_SI_TIMEOUT;\nexport const ACTION_JINGLE_TERMINATE = AnalyticsEvents.ACTION_JINGLE_TERMINATE;\nexport const ACTION_JINGLE_TR_RECEIVED = AnalyticsEvents.ACTION_JINGLE_TR_RECEIVED;\nexport const ACTION_JINGLE_TR_SUCCESS = AnalyticsEvents.ACTION_JINGLE_TR_SUCCESS;\nexport const ACTION_P2P_DECLINED = AnalyticsEvents.ACTION_P2P_DECLINED;\nexport const ACTION_P2P_ESTABLISHED = AnalyticsEvents.ACTION_P2P_ESTABLISHED;\nexport const ACTION_P2P_FAILED = AnalyticsEvents.ACTION_P2P_FAILED;\nexport const ACTION_P2P_SWITCH_TO_JVB = AnalyticsEvents.ACTION_P2P_SWITCH_TO_JVB;\nexport const AVAILABLE_DEVICE = AnalyticsEvents.AVAILABLE_DEVICE;\nexport const CONNECTION_DISCONNECTED = AnalyticsEvents.CONNECTION_DISCONNECTED;\nexport const FEEDBACK = AnalyticsEvents.FEEDBACK;\nexport const ICE_DURATION = AnalyticsEvents.ICE_DURATION;\nexport const ICE_ESTABLISHMENT_DURATION_DIFF = AnalyticsEvents.ICE_ESTABLISHMENT_DURATION_DIFF;\nexport const ICE_STATE_CHANGED = AnalyticsEvents.ICE_STATE_CHANGED;\nexport const NO_BYTES_SENT = AnalyticsEvents.NO_BYTES_SENT;\nexport const TRACK_UNMUTED = AnalyticsEvents.TRACK_UNMUTED;\n\n/**\n * Creates an operational event which indicates that we have received a\n * \"bridge down\" event from jicofo.\n */\nexport const createBridgeDownEvent = () => ( {\n    action: 'bridge.down',\n    actionSubject: 'bridge.down',\n    type: TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an event which indicates that the XMPP connection failed\n * @param errorType TODO\n * @param errorMessage TODO\n * @param detail connection failed details.\n */\nexport const createConnectionFailedEvent = ( errorType: unknown, errorMessage: unknown, details: object ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'connection.failed',\n    attributes: {\n        'error_type': errorType,\n        'error_message': errorMessage,\n        ...details\n    }\n} );\n\n/**\n * Creates a conference event.\n *\n * @param action - The action of the event.\n * @param attributes - The attributes to be added to the event.\n */\nexport const createConferenceEvent = ( action: string, attributes: object ) => ( {\n    action,\n    attributes,\n    source: 'conference',\n    type: AnalyticsEvents.TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an operational event which indicates that a particular connection\n * stage was reached (i.e. the XMPP connection transitioned to the \"connected\"\n * state).\n *\n * @param stage the stage which was reached\n * @param attributes additional attributes for the event. This should be an\n * object with a \"value\" property indicating a timestamp in milliseconds\n * relative to the beginning of the document's lifetime.\n *\n */\nexport const createConnectionStageReachedEvent = ( stage: unknown, attributes: object ) => ( {\n    action: 'connection.stage.reached',\n    actionSubject: stage,\n    attributes,\n    source: 'connection.stage.reached',\n    type: AnalyticsEvents.TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an operational event for the end-to-end round trip time to a\n * specific remote participant.\n * @param participantId the ID of the remote participant.\n * @param region the region of the remote participant\n * @param rtt the rtt\n */\nexport const createE2eRttEvent = ( participantId: unknown, region: unknown, rtt: unknown ) => ( {\n    attributes: {\n        'participant_id': participantId,\n        region,\n        rtt\n    },\n    name: 'e2e_rtt',\n    type: AnalyticsEvents.TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an event which indicates that the focus has left the MUC.\n */\nexport const createFocusLeftEvent = () => ( {\n    action: 'focus.left',\n    actionSubject: 'focus.left',\n    type: AnalyticsEvents.TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an event related to a getUserMedia call.\n *\n * @param action the type of the result that the event represents: 'error',\n * 'success', 'warning', etc.\n * @param attributes the attributes to attach to the event.\n */\nexport const createGetUserMediaEvent = ( action: 'error' | 'success' | 'warning' | string, attributes: object = {} ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    source: 'get.user.media',\n    action,\n    attributes\n} );\n\n/**\n * Creates an event related to remote participant connection status changes.\n *\n * @param attributes the attributes to attach to the event.\n */\nexport const createParticipantConnectionStatusEvent = ( attributes: object = {} ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    source: 'peer.conn.status',\n    action: 'duration',\n    attributes\n} );\n\n/**\n * Creates an event related to remote track streaming status changes.\n *\n * @param attributes the attributes to attach to the event.\n */\nexport const createTrackStreamingStatusEvent = ( attributes: object = {} ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    source: 'track.streaming.status',\n    action: 'duration',\n    attributes\n} );\n\n/**\n * Creates an event for a Jingle-related event.\n * @param action the action of the event\n * @param attributes attributes to add to the event.\n */\nexport const createJingleEvent = ( action: unknown, attributes: object = {} ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action,\n    source: 'jingle',\n    attributes\n} );\n\n/**\n * Creates an event which indicates that a local track was not able to read\n * data from its source (a camera or a microphone).\n *\n * @param mediaType the media type of the local track ('audio' or\n * 'video').\n */\nexport const createNoDataFromSourceEvent = ( mediaType: 'audio' | 'video' | string, value: unknown ) => ( {\n    attributes: {\n        'media_type': mediaType,\n        value\n    },\n    action: 'track.no.data.from.source',\n    type: AnalyticsEvents.TYPE_OPERATIONAL\n} );\n\n/**\n * Creates an event for a p2p-related event.\n * @param action the action of the event\n * @param attributes attributes to add to the event.\n */\nexport const createP2PEvent = ( action: unknown, attributes: object = {} ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action,\n    source: 'p2p',\n    attributes\n} )\n\n/**\n * Indicates that we received a remote command to mute.\n */\nexport const createRemotelyMutedEvent = ( mediaType: unknown ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'remotely.muted',\n    mediaType\n} );\n\n/**\n * Creates an event which contains RTP statistics such as RTT and packet loss.\n *\n * All average RTP stats are currently reported under 1 event name, but with\n * different properties that allows to distinguish between a P2P call, a\n * call relayed through TURN or the JVB, and multiparty vs 1:1.\n *\n * The structure of the event is:\n *\n * {\n *      p2p: true,\n *      conferenceSize: 2,\n *      localCandidateType: \"relay\",\n *      remoteCandidateType: \"relay\",\n *      transportType: \"udp\",\n *\n *      // Average RTT of 200ms\n *      \"rtt.avg\": 200,\n *      \"rtt.samples\": \"[100, 200, 300]\",\n *\n *      // Average packet loss of 10%\n *      \"packet.loss.avg\": 10,\n *      \"packet.loss.samples\": '[5, 10, 15]'\n *\n *      // Difference in milliseconds in the end-to-end RTT between p2p and jvb.\n *      // The e2e RTT through jvb is 15ms shorter:\n *      \"rtt.diff\": 15,\n *\n *      // End-to-end RTT through JVB is ms.\n *      \"end2end.rtt.avg\" = 100\n * }\n *\n * Note that the value of the \"samples\" properties are (JSON encoded) strings,\n * and not JSON arrays, as events' attributes can not be nested. The samples are\n * currently included for debug purposes only and can be removed anytime soon\n * from the structure.\n *\n * Also note that not all of values are present in each event, as values are\n * obtained and calculated as part of different process/event pipe. For example\n * {@link ConnectionAvgStats} instances are doing the reports for each\n * {@link TraceablePeerConnection} and work independently from the main stats\n * pipe.\n */\nexport const createRtpStatsEvent = ( attributes: object ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'rtp.stats',\n    attributes\n} );\n\n/**\n * Creates an event which contains the round trip time (RTT) to a set of\n * regions.\n *\n * @param attributes\n */\nexport const createRttByRegionEvent = ( attributes: object ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'rtt.by.region',\n    attributes\n} );\n\n/**\n * Creates an event which contains the local and remote ICE candidate types\n * for the transport that is currently selected.\n *\n * @param attributes\n */\nexport const createTransportStatsEvent = ( attributes: object ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'transport.stats',\n    attributes\n} );\n\n/**\n * Creates an event which contains information about the audio output problem (the user id of the affected participant,\n * the local audio levels and the remote audio levels that triggered the event).\n *\n * @param userID - The user id of the affected participant.\n * @param localAudioLevels - The local audio levels.\n * @param remoteAudioLevels - The audio levels received from the participant.\n */\nexport const createAudioOutputProblemEvent = ( userID: string, localAudioLevels: unknown, remoteAudioLevels: unknown ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'audio.output.problem',\n    attributes: {\n        userID,\n        localAudioLevels,\n        remoteAudioLevels\n    }\n} );\n\n/**\n * Creates an event which contains an information related to the bridge channel close event.\n *\n * @param code - A code from {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}\n * @param reason - A string which describes the reason for closing the bridge channel.\n */\nexport const createBridgeChannelClosedEvent = ( code: string, reason: string ) => ( {\n    type: AnalyticsEvents.TYPE_OPERATIONAL,\n    action: 'bridge-channel.error',\n    attributes: {\n        code,\n        reason\n    }\n} );\n\n/**\n * Creates an event which indicates the Time To First Media (TTFM).\n * It is measured in milliseconds relative to the beginning of the document's\n * lifetime (i.e. the origin used by window.performance.now()), and it excludes\n * the following:\n * 1. The delay due to getUserMedia()\n * 2. The period between the MUC being joined and the reception of the Jingle\n * session-initiate from jicofo. This is because jicofo will not start a Jingle\n * session until there are at least 2 participants in the room.\n *\n * @param attributes the attributes to add to the event. Currently used fields:\n *      mediaType: the media type of the local track ('audio' or 'video').\n *      muted: whether the track has ever been muted (?)\n *      value: the TTMF in milliseconds.\n */\nexport const createTtfmEvent = ( attributes: object ) => createConnectionStageReachedEvent( 'ttfm', attributes );\n","export enum Events {\n    /**\n     * Notifies about audio level in RTP statistics by SSRC.\n     *\n     * @param ssrc - The synchronization source identifier (SSRC) of the\n     * endpoint/participant whose audio level is being reported.\n     * @param {number} audioLevel - The audio level of <tt>ssrc</tt> according to\n     * RTP statistics.\n     * @param {boolean} isLocal - <tt>true</tt> if <tt>ssrc</tt> identifies the\n     * local endpoint/participant; otherwise, <tt>false</tt>.\n     */\n    AUDIO_LEVEL = 'statistics.audioLevel',\n\n    /**\n     * An event fired just before the statistics module gets disposes and it's\n     * the last chance to submit some logs that will end up in stats services like\n     * CallStats (if enabled).\n     */\n    BEFORE_DISPOSED = 'statistics.before_disposed',\n\n    /**\n     * An event carrying all statistics by ssrc.\n     */\n    BYTE_SENT_STATS = 'statistics.byte_sent_stats',\n\n    /**\n     * An event carrying connection statistics.\n     *\n     * @param {object} connectionStats - The connection statistics carried by the\n     * event such as <tt>bandwidth</tt>, <tt>bitrate</tt>, <tt>packetLoss</tt>,\n     * <tt>resolution</tt>, and <tt>transport</tt>.\n     */\n    CONNECTION_STATS = 'statistics.connectionstats',\n\n    /**\n     * An event carrying performance stats.\n     */\n    LONG_TASKS_STATS = 'statistics.long_tasks_stats'\n};\n\n// exported for backward compatibility\nexport const AUDIO_LEVEL = Events.AUDIO_LEVEL;\nexport const BEFORE_DISPOSED = Events.BEFORE_DISPOSED;\nexport const BYTE_SENT_STATS = Events.BYTE_SENT_STATS;\nexport const CONNECTION_STATS = Events.CONNECTION_STATS;\nexport const LONG_TASKS_STATS = Events.LONG_TASKS_STATS;\n","// TODO: Maybe fix the values to 'Chrome', 'Internet Explorer', etc. Currently\n// this values needs to be as they are becuse they are going to analytics,\n// callstats, etc.\n\nexport const CHROME = 'chrome';\n\nexport const OPERA = 'opera';\n\nexport const FIREFOX = 'firefox';\n\nexport const INTERNET_EXPLORER = 'iexplorer';\n\nexport const SAFARI = 'safari';\n\nexport const NWJS = 'nwjs';\n\nexport const ELECTRON = 'electron';\n\nexport const REACT_NATIVE = 'react-native';\n\nexport const UNKNOWN = 'unknown';\n","import Bowser from 'bowser';\n\nimport {\n    CHROME,\n    OPERA,\n    FIREFOX,\n    INTERNET_EXPLORER,\n    SAFARI,\n    NWJS,\n    ELECTRON,\n    REACT_NATIVE,\n    UNKNOWN\n} from './browsers.js';\n\n/**\n * Maps the names of the browsers from bowser to the internal names defined in\n * ./browsers.js\n */\nconst bowserNameToJitsiName = {\n    'Chrome': CHROME,\n    'Chromium': CHROME,\n    'Opera': OPERA,\n    'Firefox': FIREFOX,\n    'Internet Explorer': INTERNET_EXPLORER,\n    'Safari': SAFARI\n};\n\n/**\n * Detects a Chromium based environent.\n *\n * NOTE: Here we cannot check solely for \"Chrome\" in the UA, because Edge has\n * it too. We need to check explicitly for chromium based Edge first and then\n * detect other chromium based browsers.\n *\n * @returns {Object|undefined} - The name (CHROME) and version.\n */\nfunction _detectChromiumBased() {\n    const userAgent = navigator.userAgent;\n    const browserInfo = {\n        name: UNKNOWN,\n        version: undefined\n    };\n\n    if (userAgent.match(/Chrome/) && !userAgent.match(/Edge/)) {\n        // Edge is currenly supported only on desktop and android.\n        if (userAgent.match(/Edg(A?)/)) {\n            // Compare the underlying chromium version.\n            const version = userAgent.match(/Chrome\\/([\\d.]+)/)[1];\n\n            if (Number.parseInt(version, 10) > 72) {\n                browserInfo.name = CHROME;\n                browserInfo.version = version;\n            }\n        } else {\n            browserInfo.name = CHROME;\n            browserInfo.version = userAgent.match(/Chrome\\/([\\d.]+)/)[1];\n        }\n    }\n\n    return browserInfo;\n}\n\n/**\n * Detects Electron environment.\n *\n * @returns {Object|undefined} - The name (ELECTRON) and version.\n */\nfunction _detectElectron() {\n    const userAgent = navigator.userAgent;\n\n    if (userAgent.match(/Electron/)) {\n        const version = userAgent.match(/Electron(?:\\s|\\/)([\\d.]+)/)[1];\n\n        return {\n            name: ELECTRON,\n            version\n        };\n    } else if (typeof window.JitsiMeetElectron !== 'undefined') {\n        return {\n            name: ELECTRON,\n            version: undefined\n        };\n    }\n}\n\n/**\n * Detects NWJS environment.\n *\n * @returns {Object|undefined} - The name (NWJS) and version.\n */\nfunction _detectNWJS() {\n    const userAgent = navigator.userAgent;\n\n    if (userAgent.match(/JitsiMeetNW/)) {\n        const version = userAgent.match(/JitsiMeetNW\\/([\\d.]+)/)[1];\n\n        return {\n            name: NWJS,\n            version\n        };\n    }\n}\n\n/**\n * Detects React Native environment.\n * @returns {Object|undefined} - The name (REACT_NATIVE) and version.\n */\nfunction _detectReactNative() {\n    const match\n        = navigator.userAgent.match(/\\b(react[ \\t_-]*native)(?:\\/(\\S+))?/i);\n    let version;\n\n    // If we're remote debugging a React Native app, it may be treated as\n    // Chrome. Check navigator.product as well and always return some version\n    // even if we can't get the real one.\n\n    if (match || navigator.product === 'ReactNative') {\n        let name;\n\n        if (match && match.length > 2) {\n            name = match[1];\n            version = match[2];\n        }\n        name || (name = 'react-native');\n        version || (version = 'unknown');\n\n        return {\n            name: REACT_NATIVE,\n            version\n        };\n    }\n}\n\n/**\n * Returns information about the current browser.\n * @param {Object} - The bowser instance.\n * @returns {Object} - The name and version of the browser.\n */\nfunction _detect(bowser) {\n    let browserInfo;\n    const detectors = [\n        _detectReactNative,\n        _detectElectron,\n        _detectNWJS\n    ];\n\n    // Try all browser detectors\n    for (let i = 0; i < detectors.length; i++) {\n        browserInfo = detectors[i]();\n        if (browserInfo) {\n            return browserInfo;\n        }\n    }\n\n    const name = bowser.getBrowserName();\n\n    if (name in bowserNameToJitsiName) {\n        return {\n            name: bowserNameToJitsiName[name],\n            version: bowser.getBrowserVersion()\n        };\n    }\n\n    // Detect other browsers with the Chrome engine, such as Vivaldi and Brave.\n    browserInfo = _detectChromiumBased();\n    if (browserInfo) {\n        return browserInfo;\n    }\n\n    return {\n        name: UNKNOWN,\n        version: undefined\n    };\n}\n\n/**\n * Implements browser detection.\n */\nexport default class BrowserDetection {\n    /**\n     * Creates new BrowserDetection instance.\n     *\n     * @param {Object} [browserInfo] - Information about the browser.\n     * @param {string} browserInfo.name - The name of the browser.\n     * @param {string} browserInfo.version - The version of the browser.\n     */\n    constructor(browserInfo) {\n        let name, version;\n\n        this._bowser = Bowser.getParser(navigator.userAgent);\n        if (typeof browserInfo === 'undefined') {\n            const detectedBrowserInfo = _detect(this._bowser);\n\n            name = detectedBrowserInfo.name;\n            version = detectedBrowserInfo.version;\n        } else if (browserInfo.name in bowserNameToJitsiName) {\n            name = bowserNameToJitsiName[browserInfo.name];\n            version = browserInfo.version;\n        } else {\n            name = UNKNOWN;\n            version = undefined;\n        }\n\n        this._name = name;\n        this._version = version;\n    }\n\n    /**\n     * Gets current browser name.\n     * @returns {string}\n     */\n    getName() {\n        return this._name;\n    }\n\n    /**\n     * Checks if current browser is Chrome.\n     * @returns {boolean}\n     */\n    isChrome() {\n        return this._name === CHROME;\n    }\n\n    /**\n     * Checks if current browser is Opera.\n     * @returns {boolean}\n     */\n    isOpera() {\n        return this._name === OPERA;\n    }\n\n    /**\n     * Checks if current browser is Firefox.\n     * @returns {boolean}\n     */\n    isFirefox() {\n        return this._name === FIREFOX;\n    }\n\n    /**\n     * Checks if current browser is Internet Explorer.\n     * @returns {boolean}\n     */\n    isIExplorer() {\n        return this._name === INTERNET_EXPLORER;\n    }\n\n    /**\n     * Checks if current browser is Safari.\n     * @returns {boolean}\n     */\n    isSafari() {\n        return this._name === SAFARI;\n    }\n\n    /**\n     * Checks if current environment is NWJS.\n     * @returns {boolean}\n     */\n    isNWJS() {\n        return this._name === NWJS;\n    }\n\n    /**\n     * Checks if current environment is Electron.\n     * @returns {boolean}\n     */\n    isElectron() {\n        return this._name === ELECTRON;\n    }\n\n    /**\n     * Checks if current environment is React Native.\n     * @returns {boolean}\n     */\n    isReactNative() {\n        return this._name === REACT_NATIVE;\n    }\n\n    /**\n     * Returns the version of the current browser.\n     * @returns {string}\n     */\n    getVersion() {\n        return this._version;\n    }\n\n    /**\n     * Check if the parsed browser matches the passed condition.\n     *\n     * @param {Object} checkTree - It's one or two layered object, which can include a\n     * platform or an OS on the first layer and should have browsers specs on the\n     * bottom layer.\n     * Eg. { chrome: '>71.1.0' }\n     *     { windows: { chrome: '<70.2' } }\n     * @returns {boolean | undefined} - Returns true if the browser satisfies the set\n     * conditions, false if not and undefined when the browser is not defined in the\n     * checktree object or when the current browser's version is unknown.\n     * @private\n     */\n    _checkCondition(checkTree) {\n        if (this._version) {\n            return this._bowser.satisfies(checkTree);\n        }\n    }\n\n    /**\n     * Compares the passed version with the current browser version.\n     *\n     * @param {*} version - The version to compare with. Anything different\n     * than string will be converted to string.\n     * @returns {boolean|undefined} - Returns true if the current version is\n     * greater than the passed version and false otherwise. Returns undefined if\n     * the current browser version is unknown.\n     */\n    isVersionGreaterThan(version) {\n        return this._checkCondition({ [this._name]: `>${version}` });\n    }\n\n    /**\n     * Compares the passed version with the current browser version.\n     *\n     * @param {*} version - The version to compare with. Anything different\n     * than string will be converted to string.\n     * @returns {boolean|undefined} - Returns true if the current version is\n     * lower than the passed version and false otherwise. Returns undefined if\n     * the current browser version is unknown.\n     */\n    isVersionLessThan(version) {\n        return this._checkCondition({ [this._name]: `<${version}` });\n    }\n\n    /**\n     * Compares the passed version with the current browser version.\n     *\n     * @param {*} version - The version to compare with. Anything different\n     * than string will be converted to string.\n     * @returns {boolean|undefined} - Returns true if the current version is\n     * equal to the passed version and false otherwise. Returns undefined if\n     * the current browser version is unknown.\n     * A loose-equality operator is used here so that it matches the sub-versions as well.\n     */\n    isVersionEqualTo(version) {\n        return this._checkCondition({ [this._name]: `~${version}` });\n    }\n}\n","import EventEmitter from 'events';\n\n/**\n * Dummy implementation of Storage interface.\n */\nclass DummyLocalStorage extends EventEmitter {\n\n    /**\n     * The object used for storage.\n     */\n    _storage = {};\n\n    /**\n     * Empties all keys out of the storage.\n     *\n     * @returns {void}\n     */\n    clear() {\n        this._storage = {};\n    }\n\n    /**\n     * Returns the number of data items stored in the Storage object.\n     *\n     * @returns {number} - The number of data items stored in the Storage object.\n     */\n    get length() {\n        return Object.keys(this._storage).length;\n    }\n\n    /**\n     * Will return that key's value associated to the passed key name.\n     *\n     * @param {string} keyName - The key name.\n     * @returns {*} - The key value.\n     */\n    getItem(keyName) {\n        return this._storage[keyName];\n    }\n\n    /**\n     * When passed a key name and value, will add that key to the storage,\n     * or update that key's value if it already exists.\n     *\n     * @param {string} keyName - The key name.\n     * @param {*} keyValue - The key value.\n     * @returns {void}\n     */\n    setItem(keyName, keyValue) {\n        this._storage[keyName] = keyValue;\n    }\n\n    /**\n     * When passed a key name, will remove that key from the storage.\n     *\n     * @param {string} keyName - The key name.\n     * @returns {void}\n     */\n    removeItem(keyName) {\n        delete this._storage[keyName];\n    }\n\n    /**\n     * When passed a number n, this method will return the name of the nth key in the storage.\n     *\n     * @param {number} idx - The index of the key.\n     * @returns {string} - The nth key name.\n     */\n    key(n) {\n        const keys = Object.keys(this._storage);\n\n        if (keys.length <= n) {\n            return undefined;\n        }\n\n        return keys[n];\n    }\n\n    /**\n     * Serializes the content of the storage.\n     *\n     * @returns {string} - The serialized content.\n     */\n    serialize() {\n        return JSON.stringify(this._storage);\n    }\n}\n\n/**\n * Wrapper class for browser's local storage object.\n */\nclass JitsiLocalStorage extends EventEmitter {\n    /**\n     * @constructor\n     * @param {Storage} storage browser's local storage object.\n     */\n    constructor() {\n        super();\n\n        try {\n            this._storage = window.localStorage;\n            this._localStorageDisabled = false;\n        } catch (ignore) {\n            // localStorage throws an exception.\n        }\n\n        if (!this._storage) { // Handles the case when window.localStorage is undefined or throws an exception.\n            console.warn('Local storage is disabled.');\n            this._storage = new DummyLocalStorage();\n            this._localStorageDisabled = true;\n        }\n    }\n\n    /**\n     * Returns true if window.localStorage is disabled and false otherwise.\n     *\n     * @returns {boolean} - True if window.localStorage is disabled and false otherwise.\n     */\n    isLocalStorageDisabled() {\n        return this._localStorageDisabled;\n    }\n\n    /**\n     * Empties all keys out of the storage.\n     *\n     * @returns {void}\n     */\n    clear() {\n        this._storage.clear();\n        this.emit('changed');\n    }\n\n    /**\n     * Returns the number of data items stored in the Storage object.\n     *\n     * @returns {number} - The number of data items stored in the Storage object.\n     */\n    get length() {\n        return this._storage.length;\n    }\n\n    /**\n     * Returns that passed key's value.\n     * @param {string} keyName the name of the key you want to retrieve\n     * the value of.\n     * @returns {String|null} the value of the key. If the key does not exist,\n     * null is returned.\n     */\n    getItem(keyName) {\n        return this._storage.getItem(keyName);\n    }\n\n    /**\n     * Adds a key to the storage, or update key's value if it already exists.\n     * @param {string} keyName - the name of the key you want to create/update.\n     * @param {string} keyValue - the value you want to give the key you are\n     * creating/updating.\n     * @param {boolean} dontEmitChangedEvent - If true a changed event won't be emitted.\n     */\n    setItem(keyName, keyValue, dontEmitChangedEvent = false) {\n        this._storage.setItem(keyName, keyValue);\n\n        if (!dontEmitChangedEvent) {\n            this.emit('changed');\n        }\n    }\n\n    /**\n     * Remove a key from the storage.\n     * @param {string} keyName the name of the key you want to remove.\n     */\n    removeItem(keyName) {\n        this._storage.removeItem(keyName);\n        this.emit('changed');\n    }\n\n    /**\n     * Returns the name of the nth key in the list, or null if n is greater\n     * than or equal to the number of key/value pairs in the object.\n     *\n     * @param {number} i - The index of the key in the list.\n     * @returns {string}\n     */\n    key(i) {\n        return this._storage.key(i);\n    }\n\n    /**\n     * Serializes the content of the storage.\n     *\n     * @returns {string} - The serialized content.\n     */\n    serialize() {\n        if (this.isLocalStorageDisabled()) {\n            return this._storage.serialize();\n        }\n\n        const length = this._storage.length;\n        const localStorageContent = {};\n\n        for (let i = 0; i < length; i++) {\n            const key = this._storage.key(i);\n\n            localStorageContent[key] = this._storage.getItem(key);\n        }\n\n        return JSON.stringify(localStorageContent);\n    }\n}\n\nexport const jitsiLocalStorage = new JitsiLocalStorage();\n","import { BrowserDetection } from '@jitsi/js-utils';\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\n/* Minimum required Chrome / Chromium version. This applies also to derivatives. */\nconst MIN_REQUIRED_CHROME_VERSION = 72;\nconst MIN_REQUIRED_SAFARI_VERSION = 14;\nconst MIN_REQUIRED_IOS_VERSION = 14;\n\n// TODO: Move this code to js-utils.\n\n// NOTE: Now we are extending BrowserDetection in order to preserve\n// RTCBrowserType interface but maybe it worth exporting BrowserCapabilities\n// and BrowserDetection as separate objects in future.\n\n/**\n * Implements browser capabilities for lib-jitsi-meet.\n */\nexport default class BrowserCapabilities extends BrowserDetection {\n    /**\n     * Creates new BrowserCapabilities instance.\n     */\n    constructor() {\n        super();\n        logger.info(\n            `This appears to be ${this.getName()}, ver: ${this.getVersion()}`);\n    }\n\n    /**\n     * Tells whether or not the <tt>MediaStream/tt> is removed from the <tt>PeerConnection</tt> and disposed on video\n     * mute (in order to turn off the camera device). This is needed on Firefox because of the following bug\n     * https://bugzilla.mozilla.org/show_bug.cgi?id=1735951\n     *\n     * @return {boolean} <tt>true</tt> if the current browser supports this strategy or <tt>false</tt> otherwise.\n     */\n    doesVideoMuteByStreamRemove() {\n        return this.isChromiumBased() || this.isWebKitBased() || this.isFirefox();\n    }\n\n    /**\n     * Checks if the current browser is Chromium based, i.e., it's either Chrome / Chromium or uses it as its engine,\n     * but doesn't identify as Chrome.\n     *\n     * This includes the following browsers:\n     * - Chrome and Chromium.\n     * - Other browsers which use the Chrome engine, but are detected as Chrome, such as Brave and Vivaldi.\n     * - Browsers which are NOT Chrome but use it as their engine, and have custom detection code: Opera, Electron\n     *   and NW.JS.\n     * This excludes\n     * - Chrome on iOS since it uses WKWebView.\n     */\n    isChromiumBased() {\n        return (this.isChrome()\n            || this.isElectron()\n            || this.isNWJS()\n            || this.isOpera())\n            && !this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the current platform is iOS.\n     *\n     * @returns {boolean}\n     */\n    isIosBrowser() {\n        const { userAgent, maxTouchPoints, platform } = navigator;\n\n        return Boolean(userAgent.match(/iP(ad|hone|od)/i))\n            || (maxTouchPoints && maxTouchPoints > 2 && /MacIntel/.test(platform));\n    }\n\n    /**\n     * Checks if the current browser is WebKit based. It's either\n     * Safari or uses WebKit as its engine.\n     *\n     * This includes Chrome and Firefox on iOS\n     *\n     * @returns {boolean}\n     */\n    isWebKitBased() {\n        // https://trac.webkit.org/changeset/236144/webkit/trunk/LayoutTests/webrtc/video-addLegacyTransceiver.html\n        return this._bowser.isEngine('webkit')\n            && typeof navigator.mediaDevices !== 'undefined'\n            && typeof navigator.mediaDevices.getUserMedia !== 'undefined'\n            && typeof window.RTCRtpTransceiver !== 'undefined'\n            // eslint-disable-next-line no-undef\n            && Object.keys(RTCRtpTransceiver.prototype).indexOf('currentDirection') > -1;\n    }\n\n    /**\n     * Checks whether current running context is a Trusted Web Application.\n     *\n     * @returns {boolean} Whether the current context is a TWA.\n     */\n    isTwa() {\n        return 'matchMedia' in window && window.matchMedia('(display-mode:standalone)').matches;\n    }\n\n    /**\n     * Checks if the current browser is supported.\n     *\n     * @returns {boolean} true if the browser is supported, false otherwise.\n     */\n    isSupported() {\n        if (this.isSafari() && this._getSafariVersion() < MIN_REQUIRED_SAFARI_VERSION) {\n            return false;\n        }\n\n        return (this.isChromiumBased() && this._getChromiumBasedVersion() >= MIN_REQUIRED_CHROME_VERSION)\n            || this.isFirefox()\n            || this.isReactNative()\n            || this.isWebKitBased();\n    }\n\n    /**\n     * Returns whether the browser is supported for Android\n     * @returns {boolean} true if the browser is supported for Android devices\n     */\n    isSupportedAndroidBrowser() {\n        return this.isChromiumBased() || this.isFirefox();\n    }\n\n    /**\n     * Returns whether the browser is supported for iOS\n     * @returns {boolean} true if the browser is supported for iOS devices\n     */\n    isSupportedIOSBrowser() {\n        return this._getIOSVersion() >= MIN_REQUIRED_IOS_VERSION;\n    }\n\n    /**\n     * Returns whether or not the current environment needs a user interaction\n     * with the page before any unmute can occur.\n     *\n     * @returns {boolean}\n     */\n    isUserInteractionRequiredForUnmute() {\n        return this.isFirefox() && this.isVersionLessThan('68');\n    }\n\n    /**\n     * Checks if the current browser triggers 'onmute'/'onunmute' events when\n     * user's connection is interrupted and the video stops playback.\n     * @returns {*|boolean} 'true' if the event is supported or 'false'\n     * otherwise.\n     */\n    supportsVideoMuteOnConnInterrupted() {\n        return this.isChromiumBased() || this.isReactNative();\n    }\n\n    /**\n     * Checks if the current browser reports upload and download bandwidth\n     * statistics.\n     * @return {boolean}\n     */\n    supportsBandwidthStatistics() {\n        // FIXME bandwidth stats are currently not implemented for FF on our\n        // side, but not sure if not possible ?\n        return !this.isFirefox() && !this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the current browser supports setting codec preferences on the transceiver.\n     * @returns {boolean}\n     */\n    supportsCodecPreferences() {\n        return Boolean(window.RTCRtpTransceiver\n            && 'setCodecPreferences' in window.RTCRtpTransceiver.prototype\n            && window.RTCRtpReceiver\n            && typeof window.RTCRtpReceiver.getCapabilities !== 'undefined')\n\n            // this is not working on Safari because of the following bug\n            // https://bugs.webkit.org/show_bug.cgi?id=215567\n            && !this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the current browser support the device change event.\n     * @return {boolean}\n     */\n    supportsDeviceChangeEvent() {\n        return navigator.mediaDevices\n            && typeof navigator.mediaDevices.ondevicechange !== 'undefined'\n            && typeof navigator.mediaDevices.addEventListener !== 'undefined';\n    }\n\n    /**\n     * Checks if the current browser supports RTT statistics for srflx local\n     * candidates through the legacy getStats() API.\n     */\n    supportsLocalCandidateRttStatistics() {\n        return this.isChromiumBased() || this.isReactNative() || this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the current browser supports the Long Tasks API that lets us observe\n     * performance measurement events and be notified of tasks that take longer than\n     * 50ms to execute on the main thread.\n     */\n    supportsPerformanceObserver() {\n        return typeof window.PerformanceObserver !== 'undefined'\n            && PerformanceObserver.supportedEntryTypes.indexOf('longtask') > -1;\n    }\n\n    /**\n     * Checks if the current browser supports audio level stats on the receivers.\n     */\n    supportsReceiverStats() {\n        return typeof window.RTCRtpReceiver !== 'undefined'\n            && Object.keys(RTCRtpReceiver.prototype).indexOf('getSynchronizationSources') > -1\n\n            // Disable this on Safari because it is reporting 0.000001 as the audio levels for all\n            // remote audio tracks.\n            && !this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the current browser reports round trip time statistics for\n     * the ICE candidate pair.\n     * @return {boolean}\n     */\n    supportsRTTStatistics() {\n        // Firefox does not seem to report RTT for ICE candidate pair:\n        // eslint-disable-next-line max-len\n        // https://www.w3.org/TR/webrtc-stats/#dom-rtcicecandidatepairstats-currentroundtriptime\n        // It does report mozRTT for RTP streams, but at the time of this\n        // writing it's value does not make sense most of the time\n        // (is reported as 1):\n        // https://bugzilla.mozilla.org/show_bug.cgi?id=1241066\n        // For Chrome and others we rely on 'googRtt'.\n        return !this.isFirefox();\n    }\n\n    /**\n     * Returns true if VP9 is supported by the client on the browser. VP9 is currently disabled on Firefox and Safari\n     * because of issues with rendering. Please check https://bugzilla.mozilla.org/show_bug.cgi?id=1492500,\n     * https://bugs.webkit.org/show_bug.cgi?id=231071 and https://bugs.webkit.org/show_bug.cgi?id=231074 for details.\n     */\n    supportsVP9() {\n        return this.isChromiumBased() || this.isReactNative();\n    }\n\n    /**\n     * Checks if the browser uses SDP munging for turning on simulcast.\n     *\n     * @returns {boolean}\n     */\n    usesSdpMungingForSimulcast() {\n        return this.isChromiumBased() || this.isReactNative() || this.isWebKitBased();\n    }\n\n    /**\n     * Checks if the browser uses webrtc-adapter. All browsers except React Native do.\n     *\n     * @returns {boolean}\n     */\n    usesAdapter() {\n        return !this.isReactNative();\n    }\n\n    /**\n     * Checks if the browser uses RIDs/MIDs for siganling the simulcast streams\n     * to the bridge instead of the ssrcs.\n     */\n    usesRidsForSimulcast() {\n        return false;\n    }\n\n    /**\n     * Checks if the browser supports getDisplayMedia.\n     * @returns {boolean} {@code true} if the browser supports getDisplayMedia.\n     */\n    supportsGetDisplayMedia() {\n        return typeof navigator.getDisplayMedia !== 'undefined'\n            || (typeof navigator.mediaDevices !== 'undefined'\n                && typeof navigator.mediaDevices.getDisplayMedia\n                    !== 'undefined');\n    }\n\n    /**\n     * Checks if the browser supports WebRTC Encoded Transform, an alternative\n     * to insertable streams.\n     *\n     * NOTE: At the time of this writing the only browser supporting this is\n     * Safari / WebKit, behind a flag.\n     *\n     * @returns {boolean} {@code true} if the browser supports it.\n     */\n    supportsEncodedTransform() {\n        return Boolean(window.RTCRtpScriptTransform);\n    }\n\n    /**\n     * Checks if the browser supports insertable streams, needed for E2EE.\n     * @returns {boolean} {@code true} if the browser supports insertable streams.\n     */\n    supportsInsertableStreams() {\n        if (!(typeof window.RTCRtpSender !== 'undefined'\n            && window.RTCRtpSender.prototype.createEncodedStreams)) {\n            return false;\n        }\n\n        // Feature-detect transferable streams which we need to operate in a worker.\n        // See https://groups.google.com/a/chromium.org/g/blink-dev/c/1LStSgBt6AM/m/hj0odB8pCAAJ\n        const stream = new ReadableStream();\n\n        try {\n            window.postMessage(stream, '*', [ stream ]);\n\n            return true;\n        } catch {\n            return false;\n        }\n    }\n\n    /**\n     * Whether the browser supports the RED format for audio.\n     */\n    supportsAudioRed() {\n        return Boolean(window.RTCRtpSender\n            && window.RTCRtpSender.getCapabilities\n            && window.RTCRtpSender.getCapabilities('audio').codecs.some(codec => codec.mimeType === 'audio/red')\n            && window.RTCRtpReceiver\n            && window.RTCRtpReceiver.getCapabilities\n            && window.RTCRtpReceiver.getCapabilities('audio').codecs.some(codec => codec.mimeType === 'audio/red'));\n    }\n\n    /**\n     * Checks if the browser supports unified plan.\n     *\n     * @returns {boolean}\n     */\n    supportsUnifiedPlan() {\n        return !this.isReactNative();\n    }\n\n    /**\n     * Checks if the browser supports voice activity detection via the @type {VADAudioAnalyser} service.\n     *\n     * @returns {boolean}\n     */\n    supportsVADDetection() {\n        return this.isChromiumBased();\n    }\n\n    /**\n     * Check if the browser supports the RTP RTX feature (and it is usable).\n     *\n     * @returns {boolean}\n     */\n    supportsRTX() {\n        // Disable RTX on Firefox up to 96 because we prefer simulcast over RTX\n        // see https://bugzilla.mozilla.org/show_bug.cgi?id=1738504\n        return !(this.isFirefox() && this.isVersionLessThan('96'));\n    }\n\n    /**\n     * Returns the version of a Chromium based browser.\n     *\n     * @returns {Number}\n     */\n    _getChromiumBasedVersion() {\n        if (this.isChromiumBased()) {\n            // NW.JS doesn't expose the Chrome version in the UA string.\n            if (this.isNWJS()) {\n                // eslint-disable-next-line no-undef\n                return Number.parseInt(process.versions.chromium, 10);\n            }\n\n            // Here we process all browsers which use the Chrome engine but\n            // don't necessarily identify as Chrome. We cannot use the version\n            // comparing functions because the Electron, Opera and NW.JS\n            // versions are inconsequential here, as we need to know the actual\n            // Chrome engine version.\n            const ua = navigator.userAgent;\n\n            if (ua.match(/Chrome/)) {\n                const version\n                    = Number.parseInt(ua.match(/Chrome\\/([\\d.]+)/)[1], 10);\n\n                return version;\n            }\n        }\n\n        return -1;\n    }\n\n    /**\n     * Returns the version of a Safari browser.\n     *\n     * @returns {Number}\n     */\n    _getSafariVersion() {\n        if (this.isSafari()) {\n            return Number.parseInt(this.getVersion(), 10);\n        }\n\n        return -1;\n    }\n\n    /**\n     * Returns the version of an ios browser.\n     *\n     * @returns {Number}\n     */\n    _getIOSVersion() {\n        if (this.isWebKitBased()) {\n            return Number.parseInt(this.getVersion(), 10);\n        }\n\n        return -1;\n    }\n}\n","import BrowserCapabilities from './BrowserCapabilities';\n\nexport default new BrowserCapabilities();\n","import { getLogger } from '@jitsi/logger';\n\nimport {\n    TYPE_OPERATIONAL,\n    TYPE_PAGE,\n    TYPE_TRACK,\n    TYPE_UI\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\n\nconst MAX_CACHE_SIZE = 100;\n\n// eslist-disable-line no-undef\nconst logger = getLogger(__filename);\n\n/**\n * This class provides an API to lib-jitsi-meet and its users for sending\n * analytics events. It serves as a bridge to different backend implementations\n * (\"analytics handlers\") and a cache for events attempted to be sent before\n * the analytics handlers were enabled.\n *\n * The API is designed to be an easy replacement for the previous version of\n * this adapter, and is meant to be extended with more convenience methods.\n *\n *\n * The API calls are translated to objects with the following structure, which\n * are then passed to the sendEvent(event) function of the underlying handlers:\n *\n * {\n *    type,\n *\n *    action,\n *    actionSubject,\n *    actionSubjectId,\n *    attributes,\n *    categories,\n *    containerId,\n *    containerType,\n *    name,\n *    objectId,\n *    objectType,\n *    source,\n *    tags\n * }\n *\n * The 'type' is one of 'operational', 'page', 'track' or 'ui', and some of the\n * other properties are considered required according to the type.\n *\n * For events with type 'page', the required properties are: name.\n *\n * For events with type 'operational' and 'ui', the required properties are:\n * action, actionSubject, source\n *\n * For events with type 'page', the required properties are:\n * action, actionSubject, source, containerType, containerId, objectType,\n * objectId\n */\nclass AnalyticsAdapter {\n    /**\n     * Creates new AnalyticsAdapter instance.\n     */\n    constructor() {\n        this.reset();\n    }\n\n    /**\n     * Reset the state to the initial one.\n     *\n     * @returns {void}\n     */\n    reset() {\n        /**\n         * Whether this AnalyticsAdapter has been disposed of or not. Once this\n         * is set to true, the AnalyticsAdapter is disabled and does not accept\n         * any more events, and it can not be re-enabled.\n         * @type {boolean}\n         */\n        this.disposed = false;\n\n        /**\n         * The set of handlers to which events will be sent.\n         * @type {Set<any>}\n         */\n        this.analyticsHandlers = new Set();\n\n        /**\n         * The cache of events which are not sent yet. The cache is enabled\n         * while this field is truthy, and disabled otherwise.\n         * @type {Array}\n         */\n        this.cache = [];\n\n        /**\n         * Map of properties that will be added to every event. Note that the\n         * keys will be prefixed with \"permanent.\".\n         */\n        this.permanentProperties = {};\n\n        /**\n         * The name of the conference that this AnalyticsAdapter is associated\n         * with.\n         * @type {null}\n         */\n        this.conferenceName = '';\n\n        this.addPermanentProperties({\n            'user_agent': navigator.userAgent,\n            'browser_name': browser.getName()\n        });\n    }\n\n    /**\n     * Dispose analytics. Clears all handlers.\n     */\n    dispose() {\n        logger.warn('Disposing of analytics adapter.');\n\n        if (this.analyticsHandlers && this.analyticsHandlers.size > 0) {\n            this.analyticsHandlers.forEach(handler => {\n                if (typeof handler.dispose === 'function') {\n                    handler.dispose();\n                }\n            });\n        }\n\n        this.setAnalyticsHandlers([]);\n        this.disposed = true;\n    }\n\n    /**\n     * Sets the handlers that are going to be used to send analytics. Sends any\n     * cached events.\n     * @param {Array} handlers the handlers\n     */\n    setAnalyticsHandlers(handlers) {\n        if (this.disposed) {\n            return;\n        }\n\n        this.analyticsHandlers = new Set(handlers);\n\n        this._setUserProperties();\n\n        // Note that we disable the cache even if the set of handlers is empty.\n        const cache = this.cache;\n\n        this.cache = null;\n        if (cache) {\n            cache.forEach(event => this._sendEvent(event));\n        }\n    }\n\n    /**\n     * Set the user properties to the analytics handlers.\n     *\n     * @returns {void}\n     */\n    _setUserProperties() {\n        this.analyticsHandlers.forEach(handler => {\n            try {\n                handler.setUserProperties(this.permanentProperties);\n            } catch (error) {\n                logger.warn('Error in setUserProperties method of one of the '\n                    + `analytics handlers: ${error}`);\n            }\n        });\n    }\n\n    /**\n     * Adds a set of permanent properties to this this AnalyticsAdapter.\n     * Permanent properties will be added as \"attributes\" to events sent to\n     * the underlying \"analytics handlers\", and their keys will be prefixed\n     * by \"permanent_\", i.e. adding a permanent property {key: \"value\"} will\n     * result in {\"permanent_key\": \"value\"} object to be added to the\n     * \"attributes\" field of events.\n     *\n     * @param {Object} properties the properties to add\n     */\n    addPermanentProperties(properties) {\n        this.permanentProperties = {\n            ...this.permanentProperties,\n            ...properties\n        };\n\n        this._setUserProperties();\n    }\n\n    /**\n     * Sets the name of the conference that this AnalyticsAdapter is associated\n     * with.\n     * @param name the name to set.\n     */\n    setConferenceName(name) {\n        this.conferenceName = name;\n        this.addPermanentProperties({ 'conference_name': name });\n    }\n\n    /**\n     * Sends an event with a given name and given properties. The first\n     * parameter is either a string or an object. If it is a string, it is used\n     * as the event name and the second parameter is used at the attributes to\n     * attach to the event. If it is an object, it represents the whole event,\n     * including any desired attributes, and the second parameter is ignored.\n     *\n     * @param {String|Object} eventName either a string to be used as the name\n     * of the event, or an event object. If an event object is passed, the\n     * properties parameters is ignored.\n     * @param {Object} properties the properties/attributes to attach to the\n     * event, if eventName is a string.\n     */\n    sendEvent(eventName, properties = {}) {\n        if (this.disposed) {\n            return;\n        }\n\n        let event = null;\n\n        if (typeof eventName === 'string') {\n            event = {\n                type: TYPE_OPERATIONAL,\n                action: eventName,\n                actionSubject: eventName,\n                source: eventName,\n                attributes: properties\n            };\n        } else if (typeof eventName === 'object') {\n            event = eventName;\n        }\n\n        if (!this._verifyRequiredFields(event)) {\n            logger.error(\n                `Dropping a mis-formatted event: ${JSON.stringify(event)}`);\n\n            return;\n        }\n\n        this._sendEvent(event);\n    }\n\n    /**\n     * Checks whether an event has all of the required fields set, and tries\n     * to fill in some of the missing fields with reasonable default values.\n     * Returns true if after this operation the event has all of the required\n     * fields set, and false otherwise (if some of the required fields were not\n     * set and the attempt to fill them in with a default failed).\n     *\n     * @param event the event object.\n     * @return {boolean} true if the event (after the call to this function)\n     * contains all of the required fields, and false otherwise.\n     * @private\n     */\n    _verifyRequiredFields(event) {\n        if (!event) {\n            return false;\n        }\n\n        if (!event.type) {\n            event.type = TYPE_OPERATIONAL;\n        }\n\n        const type = event.type;\n\n        if (type !== TYPE_OPERATIONAL && type !== TYPE_PAGE\n            && type !== TYPE_UI && type !== TYPE_TRACK) {\n            logger.error(`Unknown event type: ${type}`);\n\n            return false;\n        }\n\n        if (type === TYPE_PAGE) {\n            return Boolean(event.name);\n        }\n\n        // Try to set some reasonable default values in case some of the\n        // parameters required by the handler API are missing.\n        event.action = event.action || event.name || event.actionSubject;\n        event.actionSubject = event.actionSubject || event.name || event.action;\n        event.source = event.source || event.name || event.action\n            || event.actionSubject;\n\n        if (!event.action || !event.actionSubject || !event.source) {\n            logger.error(\n                'Required field missing (action, actionSubject or source)');\n\n            return false;\n        }\n\n        // Track events have additional required fields.\n        if (type === TYPE_TRACK) {\n            event.objectType = event.objectType || 'generic-object-type';\n            event.containerType = event.containerType || 'conference';\n            if (event.containerType === 'conference' && !event.containerId) {\n                event.containerId = this.conferenceName;\n            }\n\n\n            if (!event.objectType || !event.objectId\n                || !event.containerType || !event.containerId) {\n                logger.error(\n                    'Required field missing (containerId, containerType, '\n                        + 'objectId or objectType)');\n\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /**\n     * Saves an event to the cache, if the cache is enabled.\n     * @param event the event to save.\n     * @returns {boolean} true if the event was saved, and false otherwise (i.e.\n     * if the cache was disabled).\n     * @private\n     */\n    _maybeCacheEvent(event) {\n        if (this.cache) {\n            this.cache.push(event);\n\n            // We limit the size of the cache, in case the user fails to ever\n            // set the analytics handlers.\n            if (this.cache.length > MAX_CACHE_SIZE) {\n                this.cache.splice(0, 1);\n            }\n\n            return true;\n        }\n\n        return false;\n\n    }\n\n    /**\n     *\n     * @param event\n     * @private\n     */\n    _sendEvent(event) {\n        if (this._maybeCacheEvent(event)) {\n            // The event was consumed by the cache.\n        } else {\n            this.analyticsHandlers.forEach(handler => {\n                try {\n                    handler.sendEvent(event);\n                } catch (e) {\n                    logger.warn(`Error sending analytics event: ${e}`);\n                }\n            });\n        }\n    }\n}\n\nexport default new AnalyticsAdapter();\n","/* global callstats */\n\nimport browser from '../browser';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * We define enumeration of wrtcFuncNames as we need them before\n * callstats is initialized to queue events.\n * @const\n * @see http://www.callstats.io/api/#enumeration-of-wrtcfuncnames\n */\nconst wrtcFuncNames = {\n    createOffer: 'createOffer',\n    createAnswer: 'createAnswer',\n    setLocalDescription: 'setLocalDescription',\n    setRemoteDescription: 'setRemoteDescription',\n    addIceCandidate: 'addIceCandidate',\n    getUserMedia: 'getUserMedia',\n    iceConnectionFailure: 'iceConnectionFailure',\n    signalingError: 'signalingError',\n    applicationLog: 'applicationLog'\n};\n\n/**\n * We define enumeration of fabricEvent as we need them before\n * callstats is initialized to queue events.\n * @const\n * @see http://www.callstats.io/api/#enumeration-of-fabricevent\n */\nconst fabricEvent = {\n    fabricHold: 'fabricHold',\n    fabricResume: 'fabricResume',\n    audioMute: 'audioMute',\n    audioUnmute: 'audioUnmute',\n    videoPause: 'videoPause',\n    videoResume: 'videoResume',\n    fabricUsageEvent: 'fabricUsageEvent',\n    fabricStats: 'fabricStats',\n    fabricTerminated: 'fabricTerminated',\n    screenShareStart: 'screenShareStart',\n    screenShareStop: 'screenShareStop',\n    dominantSpeaker: 'dominantSpeaker',\n    activeDeviceList: 'activeDeviceList'\n};\n\n/**\n * The user id to report to callstats as destination.\n * @type {string}\n */\nconst DEFAULT_REMOTE_USER = 'jitsi';\n\n/**\n * Type of pending reports, can be event or an error.\n * @type {{ERROR: string, EVENT: string}}\n */\nconst reportType = {\n    ERROR: 'error',\n    EVENT: 'event',\n    MST_WITH_USERID: 'mstWithUserID'\n};\n\n/**\n * Set of currently existing {@link CallStats} instances.\n * @type {Set<CallStats>}\n */\nlet _fabrics;\n\n/**\n * An instance of this class is a wrapper for the CallStats API fabric. A fabric\n * reports one peer connection to the CallStats backend and is allocated with\n * {@link callstats.addNewFabric}. It has a bunch of instance methods for\n * reporting various events. A fabric is considered disposed when\n * {@link CallStats.sendTerminateEvent} is executed.\n *\n * Currently only one backend instance can be created ever and it's done using\n * {@link CallStats.initBackend}. At the time of this writing there is no way to\n * explicitly shutdown the backend, but it's supposed to close it's connection\n * automatically, after all fabrics have been terminated.\n */\nexport default class CallStats {\n    /**\n     * A callback passed to {@link callstats.addNewFabric}.\n     * @param {string} error 'success' means ok\n     * @param {string} msg some more details\n     * @private\n     */\n    static _addNewFabricCallback(error, msg) {\n        if (CallStats.backend && error !== 'success') {\n            logger.error(`Monitoring status: ${error} msg: ${msg}`);\n        }\n    }\n\n    /**\n     * Callback passed to {@link callstats.initialize} (backend initialization)\n     * @param {string} error 'success' means ok\n     * @param {String} msg\n     * @private\n     */\n    static _initCallback(error, msg) {\n        logger.log(`CallStats Status: err=${error} msg=${msg}`);\n\n        // there is no lib, nothing to report to\n        if (error !== 'success') {\n            return;\n        }\n\n        CallStats.backendInitialized = true;\n\n        // I hate that\n        let atLeastOneFabric = false;\n        let defaultInstance = null;\n\n        for (const callStatsInstance of CallStats.fabrics.values()) {\n            if (!callStatsInstance.hasFabric) {\n                logger.debug('addNewFabric - initCallback');\n                if (callStatsInstance._addNewFabric()) {\n                    atLeastOneFabric = true;\n                    if (!defaultInstance) {\n                        defaultInstance = callStatsInstance;\n                    }\n                }\n            }\n        }\n\n        if (!atLeastOneFabric) {\n            return;\n        }\n\n        CallStats._emptyReportQueue(defaultInstance);\n    }\n\n    /**\n     * Empties report queue.\n     *\n     * @param {CallStats} csInstance - The callstats instance.\n     * @private\n     */\n    static _emptyReportQueue(csInstance) {\n        // There is no conference ID nor a PeerConnection available when some of\n        // the events are scheduled on the reportsQueue, so those will be\n        // reported on the first initialized fabric.\n        const defaultConfID = csInstance.confID;\n        const defaultPC = csInstance.peerconnection;\n\n        // notify callstats about failures if there were any\n        for (const report of CallStats.reportsQueue) {\n            if (report.type === reportType.ERROR) {\n                const errorData = report.data;\n\n                CallStats._reportError(\n                    csInstance,\n                    errorData.type,\n                    errorData.error,\n                    errorData.pc || defaultPC);\n            } else if (report.type === reportType.EVENT) {\n                // if we have and event to report and we failed to add\n                // fabric this event will not be reported anyway, returning\n                // an error\n                const eventData = report.data;\n\n                CallStats.backend.sendFabricEvent(\n                    report.pc || defaultPC,\n                    eventData.event,\n                    defaultConfID,\n                    eventData.eventData);\n            } else if (report.type === reportType.MST_WITH_USERID) {\n                const data = report.data;\n\n                CallStats.backend.associateMstWithUserID(\n                    report.pc || defaultPC,\n                    data.callStatsId,\n                    defaultConfID,\n                    data.ssrc,\n                    data.usageLabel,\n                    data.containerId\n                );\n            }\n        }\n        CallStats.reportsQueue.length = 0;\n    }\n\n    /* eslint-disable max-params */\n    /**\n     * Reports an error to callstats.\n     *\n     * @param {CallStats} [cs]\n     * @param type the type of the error, which will be one of the wrtcFuncNames\n     * @param error the error\n     * @param pc the peerconnection\n     * @private\n     */\n    static _reportError(cs, type, error, pc) {\n        let _error = error;\n\n        if (!_error) {\n            logger.warn('No error is passed!');\n            _error = new Error('Unknown error');\n        }\n        if (CallStats.backendInitialized && cs) {\n            CallStats.backend.reportError(pc, cs.confID, type, _error);\n        } else {\n            CallStats.reportsQueue.push({\n                type: reportType.ERROR,\n                data: {\n                    error: _error,\n                    pc,\n                    type\n                }\n            });\n        }\n\n        // else just ignore it\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Reports an error to callstats.\n     *\n     * @param {CallStats} cs\n     * @param event the type of the event, which will be one of the fabricEvent\n     * @param eventData additional data to pass to event\n     * @private\n     */\n    static _reportEvent(cs, event, eventData) {\n        const pc = cs && cs.peerconnection;\n        const confID = cs && cs.confID;\n\n        if (CallStats.backendInitialized && cs) {\n            CallStats.backend.sendFabricEvent(pc, event, confID, eventData);\n        } else {\n            CallStats.reportsQueue.push({\n                confID,\n                pc,\n                type: reportType.EVENT,\n                data: { event,\n                    eventData }\n            });\n        }\n    }\n\n    /**\n     * Wraps some of the CallStats API method and logs their calls with\n     * arguments on the debug logging level. Also wraps some of the backend\n     * methods execution into try catch blocks to not crash the app in case\n     * there is a problem with the backend itself.\n     * @param {callstats} theBackend\n     * @private\n     */\n    static _traceAndCatchBackendCalls(theBackend) {\n        const tryCatchMethods = [\n            'associateMstWithUserID',\n            'sendFabricEvent',\n            'sendUserFeedback'\n\n            // 'reportError', - this one needs special handling - see code below\n        ];\n\n        for (const methodName of tryCatchMethods) {\n            const originalMethod = theBackend[methodName];\n\n            theBackend[methodName] = function(...theArguments) {\n                try {\n                    return originalMethod.apply(theBackend, theArguments);\n                } catch (e) {\n                    GlobalOnErrorHandler.callErrorHandler(e);\n                }\n            };\n        }\n        const debugMethods = [\n            'associateMstWithUserID',\n            'sendFabricEvent',\n            'sendUserFeedback'\n\n            // 'reportError', - this one needs special handling - see code below\n        ];\n\n        for (const methodName of debugMethods) {\n            const originalMethod = theBackend[methodName];\n\n            theBackend[methodName] = function(...theArguments) {\n                logger.debug(methodName, theArguments);\n                originalMethod.apply(theBackend, theArguments);\n            };\n        }\n        const originalReportError = theBackend.reportError;\n\n        /* eslint-disable max-params */\n        theBackend.reportError = function(pc, cs, type, ...args) {\n            // Logs from the logger are submitted on the applicationLog event\n            // \"type\". Logging the arguments on the logger will create endless\n            // loop, because it will put all the logs to the logger queue again.\n            if (type === wrtcFuncNames.applicationLog) {\n                // NOTE otherArguments are not logged to the console on purpose\n                // to not log the whole log batch\n                // FIXME check the current logging level (currently not exposed\n                // by the logger implementation)\n                // NOTE it is not safe to log whole objects on react-native as\n                // those contain too many circular references and may crash\n                // the app.\n                if (!browser.isReactNative()) {\n                    console && console.debug('reportError', pc, cs, type);\n                }\n            } else {\n                logger.debug('reportError', pc, cs, type, ...args);\n            }\n            try {\n                originalReportError.call(theBackend, pc, cs, type, ...args);\n            } catch (exception) {\n                if (type === wrtcFuncNames.applicationLog) {\n                    console && console.error('reportError', exception);\n                } else {\n                    GlobalOnErrorHandler.callErrorHandler(exception);\n                }\n            }\n        };\n\n        /* eslint-enable max-params */\n    }\n\n    /**\n     * Returns the Set with the currently existing {@link CallStats} instances.\n     * Lazily initializes the Set to allow any Set polyfills to be applied.\n     * @type {Set<CallStats>}\n     */\n    static get fabrics() {\n        if (!_fabrics) {\n            _fabrics = new Set();\n        }\n\n        return _fabrics;\n    }\n\n    /**\n     * Initializes the CallStats backend. Should be called only if\n     * {@link CallStats.isBackendInitialized} returns <tt>false</tt>.\n     * @param {object} options\n     * @param {String} options.callStatsID CallStats credentials - ID\n     * @param {String} options.callStatsSecret CallStats credentials - secret\n     * @param {string} options.aliasName the <tt>aliasName</tt> part of\n     * the <tt>userID</tt> aka endpoint ID, see CallStats docs for more info.\n     * @param {string} options.userName the <tt>userName</tt> part of\n     * the <tt>userID</tt> aka display name, see CallStats docs for more info.\n     * @param {object} options.configParams the set of parameters\n     * to enable/disable certain features in the library. See CallStats docs for more info.\n     *\n     */\n    static initBackend(options) {\n        if (CallStats.backend) {\n            throw new Error('CallStats backend has been initialized already!');\n        }\n        try {\n            const CallStatsBackend = callstats;\n\n            CallStats.backend = new CallStatsBackend();\n            CallStats._traceAndCatchBackendCalls(CallStats.backend);\n            CallStats.userID = {\n                aliasName: options.aliasName,\n                userName: options.userName\n            };\n            CallStats.callStatsID = options.callStatsID;\n            CallStats.callStatsSecret = options.callStatsSecret;\n\n            const configParams = { ...options.configParams };\n\n            if (options.applicationName) {\n                configParams.applicationVersion = `${options.applicationName} (${browser.getName()})`;\n            }\n\n            if (options.confID) {\n                // we first check is there a tenant in the confID\n                const match = options.confID.match(/.*\\/(.*)\\/.*/);\n\n                // if there is no tenant, we will just set '/'\n                configParams.siteID = options.siteID || (match && match[1]) || '/';\n            }\n\n            // userID is generated or given by the origin server\n            CallStats.backend.initialize(\n                CallStats.callStatsID,\n                CallStats.callStatsSecret,\n                CallStats.userID,\n                CallStats._initCallback,\n                undefined,\n                configParams);\n\n            const getWiFiStatsMethod = options.getWiFiStatsMethod;\n\n            if (getWiFiStatsMethod) {\n                CallStats.backend.attachWifiStatsHandler(getWiFiStatsMethod);\n\n                getWiFiStatsMethod().then(result => {\n                    if (result) {\n                        logger.info('Reported wifi addresses:'\n                            , JSON.parse(result).addresses);\n                    }\n                })\n                .catch(() => {});// eslint-disable-line no-empty-function\n            }\n\n            return true;\n        } catch (e) {\n            // The callstats.io API failed to initialize (e.g. because its\n            // download did not succeed in general or on time). Further attempts\n            // to utilize it cannot possibly succeed.\n            GlobalOnErrorHandler.callErrorHandler(e);\n            CallStats.backend = null;\n            logger.error(e);\n\n            return false;\n        }\n    }\n\n    /**\n     * Checks if the CallStats backend has been created. It does not mean that\n     * it has been initialized, but only that the API instance has been\n     * allocated successfully.\n     * @return {boolean} <tt>true</tt> if backend exists or <tt>false</tt>\n     * otherwise\n     */\n    static isBackendInitialized() {\n        return Boolean(CallStats.backend);\n    }\n\n    /**\n     * Notifies CallStats about active device.\n     * @param {{deviceList: {String:String}}} devicesData list of devices with\n     * their data\n     * @param {CallStats} cs callstats instance related to the event\n     */\n    static sendActiveDeviceListEvent(devicesData, cs) {\n        CallStats._reportEvent(cs, fabricEvent.activeDeviceList, devicesData);\n    }\n\n    /**\n     * Notifies CallStats that there is a log we want to report.\n     *\n     * @param {Error} e error to send or {String} message\n     * @param {CallStats} cs callstats instance related to the error (optional)\n     */\n    static sendApplicationLog(e, cs) {\n        try {\n            CallStats._reportError(\n                cs,\n                wrtcFuncNames.applicationLog,\n                e,\n                cs && cs.peerconnection);\n        } catch (error) {\n            // If sendApplicationLog fails it should not be printed to\n            // the logger, because it will try to push the logs again\n            // (through sendApplicationLog) and an endless loop is created.\n            if (console && (typeof console.error === 'function')) {\n                // FIXME send analytics event as well\n                console.error('sendApplicationLog failed', error);\n            }\n        }\n    }\n\n    /**\n     * Sends the given feedback through CallStats.\n     *\n     * @param {string} conferenceID the conference ID for which the feedback\n     * will be reported.\n     * @param overall an integer between 1 and 5 indicating the\n     * user feedback\n     * @param comment detailed feedback from the user.\n     */\n    static sendFeedback(conferenceID, overall, comment) {\n        return new Promise((resolve, reject) => {\n            if (CallStats.backend) {\n                CallStats.backend.sendUserFeedback(\n                    conferenceID,\n                    {\n                        userID: CallStats.userID,\n                        overall,\n                        comment\n                    },\n                    (status, message) => {\n                        if (status === 'success') {\n                            resolve(message);\n                        } else {\n                            reject(message);\n                        }\n                    });\n            } else {\n                const reason = 'Failed to submit feedback to CallStats - no backend';\n\n                logger.error(reason);\n                reject(reason);\n            }\n        });\n    }\n\n    /**\n     * Notifies CallStats that getUserMedia failed.\n     *\n     * @param {Error} e error to send\n     * @param {CallStats} cs callstats instance related to the error (optional)\n     */\n    static sendGetUserMediaFailed(e, cs) {\n        CallStats._reportError(cs, wrtcFuncNames.getUserMedia, e, null);\n    }\n\n    /**\n     * Notifies CallStats for mute events\n     * @param mute {boolean} true for muted and false for not muted\n     * @param type {String} \"audio\"/\"video\"\n     * @param {CallStats} cs callstats instance related to the event\n     */\n    static sendMuteEvent(mute, type, cs) {\n        let event;\n\n        if (type === 'video') {\n            event = mute ? fabricEvent.videoPause : fabricEvent.videoResume;\n        } else {\n            event = mute ? fabricEvent.audioMute : fabricEvent.audioUnmute;\n        }\n\n        CallStats._reportEvent(cs, event);\n    }\n\n    /**\n     * Creates new CallStats instance that handles all callstats API calls for\n     * given {@link TraceablePeerConnection}. Each instance is meant to handle\n     * one CallStats fabric added with 'addFabric' API method for the\n     * {@link TraceablePeerConnection} instance passed in the constructor.\n     * @param {TraceablePeerConnection} tpc\n     * @param {Object} options\n     * @param {string} options.confID the conference ID that wil be used to\n     * report the session.\n     * @param {string} [options.remoteUserID='jitsi'] the remote user ID to\n     * which given <tt>tpc</tt> is connected.\n     */\n    constructor(tpc, options) {\n        this.confID = options.confID;\n        this.tpc = tpc;\n        this.peerconnection = tpc.peerconnection;\n        this.remoteUserID = options.remoteUserID || DEFAULT_REMOTE_USER;\n        this.hasFabric = false;\n\n        CallStats.fabrics.add(this);\n\n        if (CallStats.backendInitialized) {\n            this._addNewFabric();\n\n            // if this is the first fabric let's try to empty the\n            // report queue. Reports all events that we recorded between\n            // backend initialization and receiving the first fabric\n            if (CallStats.fabrics.size === 1) {\n                CallStats._emptyReportQueue(this);\n            }\n        }\n    }\n\n    /**\n     * Initializes CallStats fabric by calling \"addNewFabric\" for\n     * the peer connection associated with this instance.\n     * @return {boolean} true if the call was successful or false otherwise.\n     */\n    _addNewFabric() {\n        logger.info('addNewFabric', this.remoteUserID);\n        try {\n            const fabricAttributes = {\n                remoteEndpointType:\n                    this.tpc.isP2P\n                        ? CallStats.backend.endpointType.peer\n                        : CallStats.backend.endpointType.server\n            };\n            const ret\n                = CallStats.backend.addNewFabric(\n                    this.peerconnection,\n                    this.remoteUserID,\n                    CallStats.backend.fabricUsage.multiplex,\n                    this.confID,\n                    fabricAttributes,\n                    CallStats._addNewFabricCallback);\n\n            this.hasFabric = true;\n\n            const success = ret.status === 'success';\n\n            if (!success) {\n                logger.error('callstats fabric not initilized', ret.message);\n            }\n\n            return success;\n\n        } catch (error) {\n            GlobalOnErrorHandler.callErrorHandler(error);\n\n            return false;\n        }\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     * Lets CallStats module know where is given SSRC rendered by providing\n     * renderer tag ID.\n     * If the lib is not initialized yet queue the call for later, when it's\n     * ready.\n     * @param {number} ssrc the SSRC of the stream\n     * @param {boolean} isLocal indicates whether this the stream is local\n     * @param {string|null} streamEndpointId if the stream is not local the it\n     * needs to contain the stream owner's ID\n     * @param {string} usageLabel meaningful usage label of this stream like\n     *        'microphone', 'camera' or 'screen'.\n     * @param {string} containerId  the id of media 'audio' or 'video' tag which\n     *        renders the stream.\n     */\n    associateStreamWithVideoTag(\n            ssrc,\n            isLocal,\n            streamEndpointId,\n            usageLabel,\n            containerId) {\n        if (!CallStats.backend) {\n            return;\n        }\n\n        const callStatsId = isLocal ? CallStats.userID : streamEndpointId;\n\n        if (CallStats.backendInitialized) {\n            CallStats.backend.associateMstWithUserID(\n                this.peerconnection,\n                callStatsId,\n                this.confID,\n                ssrc,\n                usageLabel,\n                containerId);\n        } else {\n            CallStats.reportsQueue.push({\n                type: reportType.MST_WITH_USERID,\n                pc: this.peerconnection,\n                data: {\n                    callStatsId,\n                    containerId,\n                    ssrc,\n                    usageLabel\n                }\n            });\n        }\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Notifies CallStats that we are the new dominant speaker in the\n     * conference.\n     */\n    sendDominantSpeakerEvent() {\n        CallStats._reportEvent(this, fabricEvent.dominantSpeaker);\n    }\n\n    /**\n     * Notifies CallStats that the fabric for the underlying peerconnection was\n     * closed and no evens should be reported, after this call.\n     */\n    sendTerminateEvent() {\n        if (CallStats.backendInitialized) {\n            CallStats.backend.sendFabricEvent(\n                this.peerconnection,\n                CallStats.backend.fabricEvent.fabricTerminated,\n                this.confID);\n        }\n        CallStats.fabrics.delete(this);\n    }\n\n    /**\n     * Notifies CallStats for ice connection failed\n     */\n    sendIceConnectionFailedEvent() {\n        CallStats._reportError(\n            this,\n            wrtcFuncNames.iceConnectionFailure,\n            null,\n            this.peerconnection);\n    }\n\n    /**\n     * Notifies CallStats that peer connection failed to create offer.\n     *\n     * @param {Error} e error to send\n     */\n    sendCreateOfferFailed(e) {\n        CallStats._reportError(\n            this, wrtcFuncNames.createOffer, e, this.peerconnection);\n    }\n\n    /**\n     * Notifies CallStats that peer connection failed to create answer.\n     *\n     * @param {Error} e error to send\n     */\n    sendCreateAnswerFailed(e) {\n        CallStats._reportError(\n            this, wrtcFuncNames.createAnswer, e, this.peerconnection);\n    }\n\n    /**\n     * Sends either resume or hold event for the fabric associated with\n     * the underlying peerconnection.\n     * @param {boolean} isResume true to resume or false to hold\n     */\n    sendResumeOrHoldEvent(isResume) {\n        CallStats._reportEvent(\n            this,\n            isResume ? fabricEvent.fabricResume : fabricEvent.fabricHold);\n    }\n\n    /**\n     * Notifies CallStats for screen sharing events\n     * @param {boolean} start true for starting screen sharing and\n     * false for not stopping\n     * @param {string|null} ssrc - optional ssrc value, used only when\n     * starting screen sharing.\n     */\n    sendScreenSharingEvent(start, ssrc) {\n        let eventData;\n\n        if (ssrc) {\n            eventData = { ssrc };\n        }\n\n        CallStats._reportEvent(\n            this,\n            start ? fabricEvent.screenShareStart : fabricEvent.screenShareStop,\n            eventData);\n    }\n\n    /**\n     * Notifies CallStats that peer connection failed to set local description.\n     *\n     * @param {Error} e error to send\n     */\n    sendSetLocalDescFailed(e) {\n        CallStats._reportError(\n            this, wrtcFuncNames.setLocalDescription, e, this.peerconnection);\n    }\n\n    /**\n     * Notifies CallStats that peer connection failed to set remote description.\n     *\n     * @param {Error} e error to send\n     */\n    sendSetRemoteDescFailed(e) {\n        CallStats._reportError(\n            this, wrtcFuncNames.setRemoteDescription, e, this.peerconnection);\n    }\n\n    /**\n     * Notifies CallStats that peer connection failed to add ICE candidate.\n     *\n     * @param {Error} e error to send\n     */\n    sendAddIceCandidateFailed(e) {\n        CallStats._reportError(\n            this, wrtcFuncNames.addIceCandidate, e, this.peerconnection);\n    }\n}\n\n/**\n * The CallStats API backend instance\n * @type {callstats}\n */\nCallStats.backend = null;\n\n// some errors/events may happen before CallStats init\n// in this case we accumulate them in this array\n// and send them to callstats on init\nCallStats.reportsQueue = [];\n\n/**\n * Whether the library was successfully initialized(the backend) using its\n * initialize method.\n * @type {boolean}\n */\nCallStats.backendInitialized = false;\n\n/**\n * Part of the CallStats credentials - application ID\n * @type {string}\n */\nCallStats.callStatsID = null;\n\n/**\n * Part of the CallStats credentials - application secret\n * @type {string}\n */\nCallStats.callStatsSecret = null;\n\n/**\n * Local CallStats user ID structure. Can be set only once when\n * {@link backend} is initialized, so it's static for the time being.\n * See CallStats API for more info:\n * https://www.callstats.io/api/#userid\n * @type {object}\n */\nCallStats.userID = null;\n","/**\n * Provides statistics for the local stream.\n */\n\n/**\n * Size of the webaudio analyzer buffer.\n * @type {number}\n */\nconst WEBAUDIO_ANALYZER_FFT_SIZE = 2048;\n\n/**\n * Value of the webaudio analyzer smoothing time parameter.\n * @type {number}\n */\nconst WEBAUDIO_ANALYZER_SMOOTING_TIME = 0.8;\n\nwindow.AudioContext = window.AudioContext || window.webkitAudioContext;\n\nlet context = null;\n\nif (window.AudioContext) {\n    context = new AudioContext();\n\n    // XXX Not all browsers define a suspend method on AudioContext. As the\n    // invocation is at the (ES6 module) global execution level, it breaks the\n    // loading of the lib-jitsi-meet library in such browsers and, consequently,\n    // the loading of the very Web app that uses the lib-jitsi-meet library. For\n    // example, Google Chrome 40 on Android does not define the method but we\n    // still want to be able to load the lib-jitsi-meet library there and\n    // display a page which notifies the user that the Web app is not supported\n    // there.\n    context.suspend && context.suspend();\n}\n\n/**\n * Converts time domain data array to audio level.\n * @param samples the time domain data array.\n * @returns {number} the audio level\n */\nfunction timeDomainDataToAudioLevel(samples) {\n\n    let maxVolume = 0;\n\n    const length = samples.length;\n\n    for (let i = 0; i < length; i++) {\n        if (maxVolume < samples[i]) {\n            maxVolume = samples[i];\n        }\n    }\n\n    return parseFloat(((maxVolume - 127) / 128).toFixed(3));\n}\n\n/**\n * Animates audio level change\n * @param newLevel the new audio level\n * @param lastLevel the last audio level\n * @returns {Number} the audio level to be set\n */\nfunction animateLevel(newLevel, lastLevel) {\n    let value = 0;\n    const diff = lastLevel - newLevel;\n\n    if (diff > 0.2) {\n        value = lastLevel - 0.2;\n    } else if (diff < -0.4) {\n        value = lastLevel + 0.4;\n    } else {\n        value = newLevel;\n    }\n\n    return parseFloat(value.toFixed(3));\n}\n\n\n/**\n * <tt>LocalStatsCollector</tt> calculates statistics for the local stream.\n *\n * @param stream the local stream\n * @param interval stats refresh interval given in ms.\n * @param callback function that receives the audio levels.\n * @constructor\n */\nexport default function LocalStatsCollector(stream, interval, callback) {\n    this.stream = stream;\n    this.intervalId = null;\n    this.intervalMilis = interval;\n    this.audioLevel = 0;\n    this.callback = callback;\n}\n\n/**\n * Starts the collecting the statistics.\n */\nLocalStatsCollector.prototype.start = function() {\n    if (!LocalStatsCollector.isLocalStatsSupported()) {\n        return;\n    }\n    context.resume();\n    const analyser = context.createAnalyser();\n\n    analyser.smoothingTimeConstant = WEBAUDIO_ANALYZER_SMOOTING_TIME;\n    analyser.fftSize = WEBAUDIO_ANALYZER_FFT_SIZE;\n\n    const source = context.createMediaStreamSource(this.stream);\n\n    source.connect(analyser);\n\n    this.intervalId = setInterval(\n        () => {\n            const array = new Uint8Array(analyser.frequencyBinCount);\n\n            analyser.getByteTimeDomainData(array);\n            const audioLevel = timeDomainDataToAudioLevel(array);\n\n            // Set the audio levels always as NoAudioSignalDetection now\n            // uses audio levels from LocalStatsCollector and waits for\n            // atleast 4 secs for a no audio signal before displaying the\n            // notification on the UI.\n            this.audioLevel = animateLevel(audioLevel, this.audioLevel);\n            this.callback(this.audioLevel);\n        },\n        this.intervalMilis\n    );\n};\n\n/**\n * Stops collecting the statistics.\n */\nLocalStatsCollector.prototype.stop = function() {\n    if (this.intervalId) {\n        clearInterval(this.intervalId);\n        this.intervalId = null;\n    }\n};\n\n/**\n * Checks if the environment has the necessary conditions to support\n * collecting stats from local streams.\n *\n * @returns {boolean}\n */\nLocalStatsCollector.isLocalStatsSupported = function() {\n    return Boolean(context);\n};\n","\n/**\n * The method will increase the given number by 1. If the given counter is equal\n * or greater to {@link Number.MAX_SAFE_INTEGER} then it will be rolled back to\n * 1.\n * @param {number} number - An integer counter value to be incremented.\n * @return {number} the next counter value increased by 1 (see the description\n * above for exception).\n */\nexport function safeCounterIncrement(number) {\n    let nextValue = number;\n\n    if (number >= Number.MAX_SAFE_INTEGER) {\n        nextValue = 0;\n    }\n\n    return nextValue + 1;\n}\n\n/**\n * Calculates the average value of am Array of numbers.\n *\n * @param {Float32Array} valueArray - Array of numbers.\n * @returns {number} - Number array average.\n */\nexport function calculateAverage(valueArray) {\n    return valueArray.length > 0 ? valueArray.reduce((a, b) => a + b) / valueArray.length : 0;\n}\n\n/**\n * Calculates a unique hash for a given string similar to Java's\n * implementation of String.hashCode()\n *\n * @param {String} string - String whose hash has to be calculated.\n * @returns {number} - Unique hash code calculated.\n */\nexport function hashString(string) {\n    let hash = 0;\n\n    for (let i = 0; i < string.length; i++) {\n        hash += Math.pow(string.charCodeAt(i) * 31, string.length - i);\n\n        /* eslint-disable no-bitwise */\n        hash = hash & hash; // Convert to 32bit integer\n    }\n\n    return Math.abs(hash);\n}\n\n/**\n * Returns only the positive values from an array of numbers.\n *\n * @param {Float32Array} valueArray - Array of vad scores.\n * @returns {Array} - Array of positive numbers.\n */\nexport function filterPositiveValues(valueArray) {\n    return valueArray.filter(value => value >= 0);\n}\n\n/**\n * This class calculates a simple running average that continually changes\n * as more data points are collected and added.\n */\nexport class RunningAverage {\n    /**\n     * Creates an instance of the running average calculator.\n     */\n    constructor() {\n        this.average = 0;\n        this.n = 0;\n    }\n\n    /**\n     * Adds a new data point to the existing set of values and recomputes\n     * the running average.\n     * @param {number} value\n     * @returns {void}\n     */\n    addNext(value) {\n        if (typeof value !== 'number') {\n            return;\n        }\n        this.n += 1;\n        this.average = this.average + ((value - this.average) / this.n);\n    }\n\n    /**\n     * Obtains the average value for the current subset of values.\n     * @returns {number} - computed average.\n     */\n    getAverage() {\n        return this.average;\n    }\n}\n","\nimport { getLogger } from '@jitsi/logger';\n\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport { RunningAverage } from '../util/MathUtil';\n\nconst logger = getLogger(__filename);\nconst MILLI_SECONDS = 1000;\nconst SECONDS = 60;\n\n/**\n * This class creates an observer that monitors browser's performance measurement events\n * as they are recorded in the browser's performance timeline and computes an average and\n * a maximum value for the long task events. Tasks are classified as long tasks if they take\n * longer than 50ms to execute on the main thread.\n */\nexport class PerformanceObserverStats {\n    /**\n     * Creates a new instance of Performance observer statistics.\n     *\n     * @param {*} emitter Event emitter for emitting stats periodically\n     * @param {*} statsInterval interval for calculating the stats\n     */\n    constructor(emitter, statsInterval) {\n        this.eventEmitter = emitter;\n        this.longTasks = 0;\n        this.maxDuration = 0;\n        this.performanceStatsInterval = statsInterval;\n        this.stats = new RunningAverage();\n    }\n\n    /**\n     * Obtains the average rate of long tasks observed per min and the\n     * duration of the longest task recorded by the observer.\n     * @returns {Object}\n     */\n    getLongTasksStats() {\n        return {\n            avgRatePerMinute: (this.stats.getAverage() * SECONDS).toFixed(2), // calc rate per min\n            maxDurationMs: this.maxDuration\n        };\n    }\n\n    /**\n     * Starts the performance observer by registering the callback function\n     * that calculates the performance statistics periodically.\n     * @returns {void}\n     */\n    startObserver() {\n        // Create a handler for when the long task event is fired.\n        this.longTaskEventHandler = list => {\n            const entries = list.getEntries();\n\n            for (const task of entries) {\n                this.longTasks++;\n                this.maxDuration = Math.max(this.maxDuration, task.duration).toFixed(3);\n            }\n        };\n\n        // Create an observer for monitoring long tasks.\n        logger.info('Creating a Performance Observer for monitoring Long Tasks');\n        this.observer = new PerformanceObserver(this.longTaskEventHandler);\n        this.observer.observe({ type: 'longtask',\n            buffered: true });\n        const startTime = Date.now();\n\n        // Calculate the average # of events/sec and emit a stats event.\n        this.longTasksIntervalId = setInterval(() => {\n            const now = Date.now();\n            const interval = this._lastTimeStamp\n                ? (now - this._lastTimeStamp) / MILLI_SECONDS\n                : (now - startTime) / MILLI_SECONDS;\n            const rate = this.longTasks / interval;\n\n            this.stats.addNext(rate);\n            this.eventEmitter.emit(\n                StatisticsEvents.LONG_TASKS_STATS, this.getLongTasksStats());\n\n            // Reset the counter and start counting events again.\n            this.longTasks = 0;\n            this._lastTimeStamp = Date.now();\n        }, this.performanceStatsInterval);\n    }\n\n    /**\n     * Stops the performance observer.\n     * @returns {void}\n     */\n    stopObserver() {\n        this.observer && this.observer.disconnect();\n        this.longTaskEventHandler = null;\n        if (this.longTasksIntervalId) {\n            clearInterval(this.longTasksIntervalId);\n            this.longTasksIntervalId = null;\n        }\n    }\n}\n","export enum MediaType {\n    /**\n     * The audio type.\n     */\n    AUDIO = 'audio',\n\n    /**\n     * The presenter type.\n     */\n    PRESENTER = 'presenter',\n\n    /**\n     * The video type.\n     */\n    VIDEO = 'video'\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport browser from '../browser';\n\nconst GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');\n\nconst logger = getLogger(__filename);\n\n/**\n * Calculates packet lost percent using the number of lost packets and the\n * number of all packet.\n * @param lostPackets the number of lost packets\n * @param totalPackets the number of all packets.\n * @returns {number} packet loss percent\n */\nfunction calculatePacketLoss(lostPackets, totalPackets) {\n    if (!totalPackets || totalPackets <= 0\n            || !lostPackets || lostPackets <= 0) {\n        return 0;\n    }\n\n    return Math.round((lostPackets / totalPackets) * 100);\n}\n\n/**\n * Holds \"statistics\" for a single SSRC.\n * @constructor\n */\nfunction SsrcStats() {\n    this.loss = {};\n    this.bitrate = {\n        download: 0,\n        upload: 0\n    };\n    this.resolution = {};\n    this.framerate = 0;\n    this.codec = '';\n}\n\n/**\n * Sets the \"loss\" object.\n * @param loss the value to set.\n */\nSsrcStats.prototype.setLoss = function(loss) {\n    this.loss = loss || {};\n};\n\n/**\n * Sets resolution that belong to the ssrc represented by this instance.\n * @param resolution new resolution value to be set.\n */\nSsrcStats.prototype.setResolution = function(resolution) {\n    this.resolution = resolution || {};\n};\n\n/**\n * Adds the \"download\" and \"upload\" fields from the \"bitrate\" parameter to\n * the respective fields of the \"bitrate\" field of this object.\n * @param bitrate an object holding the values to add.\n */\nSsrcStats.prototype.addBitrate = function(bitrate) {\n    this.bitrate.download += bitrate.download;\n    this.bitrate.upload += bitrate.upload;\n};\n\n/**\n * Resets the bit rate for given <tt>ssrc</tt> that belong to the peer\n * represented by this instance.\n */\nSsrcStats.prototype.resetBitrate = function() {\n    this.bitrate.download = 0;\n    this.bitrate.upload = 0;\n};\n\n/**\n * Sets the \"framerate\".\n * @param framerate the value to set.\n */\nSsrcStats.prototype.setFramerate = function(framerate) {\n    this.framerate = framerate || 0;\n};\n\nSsrcStats.prototype.setCodec = function(codec) {\n    this.codec = codec || '';\n};\n\n/**\n *\n */\nfunction ConferenceStats() {\n\n    /**\n     * The bandwidth\n     * @type {{}}\n     */\n    this.bandwidth = {};\n\n    /**\n     * The bit rate\n     * @type {{}}\n     */\n    this.bitrate = {};\n\n    /**\n     * The packet loss rate\n     * @type {{}}\n     */\n    this.packetLoss = null;\n\n    /**\n     * Array with the transport information.\n     * @type {Array}\n     */\n    this.transport = [];\n}\n\n/* eslint-disable max-params */\n\n/**\n * <tt>StatsCollector</tt> registers for stats updates of given\n * <tt>peerconnection</tt> in given <tt>interval</tt>. On each update particular\n * stats are extracted and put in {@link SsrcStats} objects. Once the processing\n * is done <tt>audioLevelsUpdateCallback</tt> is called with <tt>this</tt>\n * instance as an event source.\n *\n * @param peerconnection WebRTC PeerConnection object.\n * @param audioLevelsInterval\n * @param statsInterval stats refresh interval given in ms.\n * @param eventEmitter\n * @constructor\n */\nexport default function StatsCollector(peerconnection, audioLevelsInterval, statsInterval, eventEmitter) {\n    this.peerconnection = peerconnection;\n    this.baselineAudioLevelsReport = null;\n    this.currentAudioLevelsReport = null;\n    this.currentStatsReport = null;\n    this.previousStatsReport = null;\n    this.audioLevelReportHistory = {};\n    this.audioLevelsIntervalId = null;\n    this.eventEmitter = eventEmitter;\n    this.conferenceStats = new ConferenceStats();\n\n    // Updates stats interval\n    this.audioLevelsIntervalMilis = audioLevelsInterval;\n\n    this.speakerList = [];\n    this.statsIntervalId = null;\n    this.statsIntervalMilis = statsInterval;\n\n    /**\n     * Maps SSRC numbers to {@link SsrcStats}.\n     * @type {Map<number,SsrcStats}\n     */\n    this.ssrc2stats = new Map();\n}\n\n/**\n * Set the list of the remote speakers for which audio levels are to be calculated.\n *\n * @param {Array<string>} speakerList - Endpoint ids.\n * @returns {void}\n */\nStatsCollector.prototype.setSpeakerList = function(speakerList) {\n    this.speakerList = speakerList;\n};\n\n/**\n * Stops stats updates.\n */\nStatsCollector.prototype.stop = function() {\n    if (this.audioLevelsIntervalId) {\n        clearInterval(this.audioLevelsIntervalId);\n        this.audioLevelsIntervalId = null;\n    }\n\n    if (this.statsIntervalId) {\n        clearInterval(this.statsIntervalId);\n        this.statsIntervalId = null;\n    }\n};\n\n/**\n * Callback passed to <tt>getStats</tt> method.\n * @param error an error that occurred on <tt>getStats</tt> call.\n */\nStatsCollector.prototype.errorCallback = function(error) {\n    GlobalOnErrorHandler.callErrorHandler(error);\n    logger.error('Get stats error', error);\n    this.stop();\n};\n\n/**\n * Starts stats updates.\n */\nStatsCollector.prototype.start = function(startAudioLevelStats) {\n    if (startAudioLevelStats) {\n        if (browser.supportsReceiverStats()) {\n            logger.info('Using RTCRtpSynchronizationSource for remote audio levels');\n        }\n        this.audioLevelsIntervalId = setInterval(\n            () => {\n                if (browser.supportsReceiverStats()) {\n                    const audioLevels = this.peerconnection.getAudioLevels(this.speakerList);\n\n                    for (const ssrc in audioLevels) {\n                        if (audioLevels.hasOwnProperty(ssrc)) {\n                            // Use a scaling factor of 2.5 to report the same\n                            // audio levels that getStats reports.\n                            const audioLevel = audioLevels[ssrc] * 2.5;\n\n                            this.eventEmitter.emit(\n                                StatisticsEvents.AUDIO_LEVEL,\n                                this.peerconnection,\n                                Number.parseInt(ssrc, 10),\n                                audioLevel,\n                                false /* isLocal */);\n                        }\n                    }\n                } else {\n                    // Interval updates\n                    this.peerconnection.getStats()\n                        .then(report => {\n                            this.currentAudioLevelsReport = typeof report?.result === 'function'\n                                ? report.result()\n                                : report;\n                            this.processAudioLevelReport();\n                            this.baselineAudioLevelsReport = this.currentAudioLevelsReport;\n                        })\n                        .catch(error => this.errorCallback(error));\n                }\n            },\n            this.audioLevelsIntervalMilis\n        );\n    }\n\n    const processStats = () => {\n        // Interval updates\n        this.peerconnection.getStats()\n            .then(report => {\n                this.currentStatsReport = typeof report?.result === 'function'\n                    ? report.result()\n                    : report;\n\n                try {\n                    this.processStatsReport();\n                } catch (error) {\n                    GlobalOnErrorHandler.callErrorHandler(error);\n                    logger.error('Processing of RTP stats failed:', error);\n                }\n                this.previousStatsReport = this.currentStatsReport;\n            })\n            .catch(error => this.errorCallback(error));\n    };\n\n    processStats();\n    this.statsIntervalId = setInterval(processStats, this.statsIntervalMilis);\n};\n\n/**\n *\n */\nStatsCollector.prototype._processAndEmitReport = function() {\n    // process stats\n    const totalPackets = {\n        download: 0,\n        upload: 0\n    };\n    const lostPackets = {\n        download: 0,\n        upload: 0\n    };\n    let bitrateDownload = 0;\n    let bitrateUpload = 0;\n    const resolutions = {};\n    const framerates = {};\n    const codecs = {};\n    let audioBitrateDownload = 0;\n    let audioBitrateUpload = 0;\n    let audioCodec;\n    let videoBitrateDownload = 0;\n    let videoBitrateUpload = 0;\n    let videoCodec;\n\n    for (const [ ssrc, ssrcStats ] of this.ssrc2stats) {\n        // process packet loss stats\n        const loss = ssrcStats.loss;\n        const type = loss.isDownloadStream ? 'download' : 'upload';\n\n        totalPackets[type] += loss.packetsTotal;\n        lostPackets[type] += loss.packetsLost;\n\n        // process bitrate stats\n        bitrateDownload += ssrcStats.bitrate.download;\n        bitrateUpload += ssrcStats.bitrate.upload;\n\n        // collect resolutions and framerates\n        const track = this.peerconnection.getTrackBySSRC(ssrc);\n\n        if (track) {\n            if (track.isAudioTrack()) {\n                audioBitrateDownload += ssrcStats.bitrate.download;\n                audioBitrateUpload += ssrcStats.bitrate.upload;\n                audioCodec = ssrcStats.codec;\n            } else {\n                videoBitrateDownload += ssrcStats.bitrate.download;\n                videoBitrateUpload += ssrcStats.bitrate.upload;\n                videoCodec = ssrcStats.codec;\n            }\n\n            const participantId = track.getParticipantId();\n\n            if (participantId) {\n                const resolution = ssrcStats.resolution;\n\n                if (resolution.width\n                        && resolution.height\n                        && resolution.width !== -1\n                        && resolution.height !== -1) {\n                    const userResolutions = resolutions[participantId] || {};\n\n                    userResolutions[ssrc] = resolution;\n                    resolutions[participantId] = userResolutions;\n                }\n                if (ssrcStats.framerate !== 0) {\n                    const userFramerates = framerates[participantId] || {};\n\n                    userFramerates[ssrc] = ssrcStats.framerate;\n                    framerates[participantId] = userFramerates;\n                }\n                if (audioCodec && videoCodec) {\n                    const codecDesc = {\n                        'audio': audioCodec,\n                        'video': videoCodec\n                    };\n\n                    const userCodecs = codecs[participantId] || {};\n\n                    userCodecs[ssrc] = codecDesc;\n                    codecs[participantId] = userCodecs;\n                }\n            } else {\n                logger.error(`No participant ID returned by ${track}`);\n            }\n        }\n\n        ssrcStats.resetBitrate();\n    }\n\n    this.conferenceStats.bitrate = {\n        'upload': bitrateUpload,\n        'download': bitrateDownload\n    };\n\n    this.conferenceStats.bitrate.audio = {\n        'upload': audioBitrateUpload,\n        'download': audioBitrateDownload\n    };\n\n    this.conferenceStats.bitrate.video = {\n        'upload': videoBitrateUpload,\n        'download': videoBitrateDownload\n    };\n\n    this.conferenceStats.packetLoss = {\n        total:\n            calculatePacketLoss(\n                lostPackets.download + lostPackets.upload,\n                totalPackets.download + totalPackets.upload),\n        download:\n            calculatePacketLoss(lostPackets.download, totalPackets.download),\n        upload:\n            calculatePacketLoss(lostPackets.upload, totalPackets.upload)\n    };\n\n    const avgAudioLevels = {};\n    let localAvgAudioLevels;\n\n    Object.keys(this.audioLevelReportHistory).forEach(ssrc => {\n        const { data, isLocal } = this.audioLevelReportHistory[ssrc];\n        const avgAudioLevel = data.reduce((sum, currentValue) => sum + currentValue) / data.length;\n\n        if (isLocal) {\n            localAvgAudioLevels = avgAudioLevel;\n        } else {\n            const track = this.peerconnection.getTrackBySSRC(Number(ssrc));\n\n            if (track) {\n                const participantId = track.getParticipantId();\n\n                if (participantId) {\n                    avgAudioLevels[participantId] = avgAudioLevel;\n                }\n            }\n        }\n    });\n    this.audioLevelReportHistory = {};\n\n    this.eventEmitter.emit(\n        StatisticsEvents.CONNECTION_STATS,\n        this.peerconnection,\n        {\n            'bandwidth': this.conferenceStats.bandwidth,\n            'bitrate': this.conferenceStats.bitrate,\n            'packetLoss': this.conferenceStats.packetLoss,\n            'resolution': resolutions,\n            'framerate': framerates,\n            'codec': codecs,\n            'transport': this.conferenceStats.transport,\n            localAvgAudioLevels,\n            avgAudioLevels\n        });\n    this.conferenceStats.transport = [];\n};\n\n/**\n * Converts the value to a non-negative number.\n * If the value is either invalid or negative then 0 will be returned.\n * @param {*} v\n * @return {number}\n * @private\n */\nStatsCollector.prototype.getNonNegativeValue = function(v) {\n    let value = v;\n\n    if (typeof value !== 'number') {\n        value = Number(value);\n    }\n\n    if (isNaN(value)) {\n        return 0;\n    }\n\n    return Math.max(0, value);\n};\n\n/**\n * Calculates bitrate between before and now using a supplied field name and its\n * value in the stats.\n * @param {RTCInboundRtpStreamStats|RTCSentRtpStreamStats} now the current stats\n * @param {RTCInboundRtpStreamStats|RTCSentRtpStreamStats} before the\n * previous stats.\n * @param fieldName the field to use for calculations.\n * @return {number} the calculated bitrate between now and before.\n * @private\n */\nStatsCollector.prototype._calculateBitrate = function(now, before, fieldName) {\n    const bytesNow = this.getNonNegativeValue(now[fieldName]);\n    const bytesBefore = this.getNonNegativeValue(before[fieldName]);\n    const bytesProcessed = Math.max(0, bytesNow - bytesBefore);\n\n    const timeMs = now.timestamp - before.timestamp;\n    let bitrateKbps = 0;\n\n    if (timeMs > 0) {\n        // TODO is there any reason to round here?\n        bitrateKbps = Math.round((bytesProcessed * 8) / timeMs);\n    }\n\n    return bitrateKbps;\n};\n\n/**\n * Stats processing for spec-compliant RTCPeerConnection#getStats.\n */\nStatsCollector.prototype.processStatsReport = function() {\n    if (!this.previousStatsReport) {\n        return;\n    }\n    const byteSentStats = {};\n\n    this.currentStatsReport.forEach(now => {\n        // RTCIceCandidatePairStats - https://w3c.github.io/webrtc-stats/#candidatepair-dict*\n        if (now.type === 'candidate-pair' && now.nominated && now.state === 'succeeded') {\n            const availableIncomingBitrate = now.availableIncomingBitrate;\n            const availableOutgoingBitrate = now.availableOutgoingBitrate;\n\n            if (availableIncomingBitrate || availableOutgoingBitrate) {\n                this.conferenceStats.bandwidth = {\n                    'download': Math.round(availableIncomingBitrate / 1000),\n                    'upload': Math.round(availableOutgoingBitrate / 1000)\n                };\n            }\n\n            const remoteUsedCandidate = this.currentStatsReport.get(now.remoteCandidateId);\n            const localUsedCandidate = this.currentStatsReport.get(now.localCandidateId);\n\n            // RTCIceCandidateStats\n            // https://w3c.github.io/webrtc-stats/#icecandidate-dict*\n            if (remoteUsedCandidate && localUsedCandidate) {\n                const remoteIpAddress = browser.isChromiumBased()\n                    ? remoteUsedCandidate.ip\n                    : remoteUsedCandidate.address;\n                const remotePort = remoteUsedCandidate.port;\n                const ip = `${remoteIpAddress}:${remotePort}`;\n\n                const localIpAddress = browser.isChromiumBased()\n                    ? localUsedCandidate.ip\n                    : localUsedCandidate.address;\n                const localPort = localUsedCandidate.port;\n                const localip = `${localIpAddress}:${localPort}`;\n                const type = remoteUsedCandidate.protocol;\n\n                // Save the address unless it has been saved already.\n                const conferenceStatsTransport = this.conferenceStats.transport;\n\n                if (!conferenceStatsTransport.some(t =>\n                    t.ip === ip\n                    && t.type === type\n                    && t.localip === localip)) {\n                    conferenceStatsTransport.push({\n                        ip,\n                        type,\n                        localip,\n                        p2p: this.peerconnection.isP2P,\n                        localCandidateType: localUsedCandidate.candidateType,\n                        remoteCandidateType: remoteUsedCandidate.candidateType,\n                        networkType: localUsedCandidate.networkType,\n                        rtt: now.currentRoundTripTime * 1000\n                    });\n                }\n            }\n\n        // RTCReceivedRtpStreamStats\n        // https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*\n        // RTCSentRtpStreamStats\n        // https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*\n        } else if (now.type === 'inbound-rtp' || now.type === 'outbound-rtp') {\n            const before = this.previousStatsReport.get(now.id);\n            const ssrc = this.getNonNegativeValue(now.ssrc);\n\n            if (!before || !ssrc) {\n                return;\n            }\n\n            let ssrcStats = this.ssrc2stats.get(ssrc);\n\n            if (!ssrcStats) {\n                ssrcStats = new SsrcStats();\n                this.ssrc2stats.set(ssrc, ssrcStats);\n            }\n\n            let isDownloadStream = true;\n            let key = 'packetsReceived';\n\n            if (now.type === 'outbound-rtp') {\n                isDownloadStream = false;\n                key = 'packetsSent';\n            }\n\n            let packetsNow = now[key];\n\n            if (!packetsNow || packetsNow < 0) {\n                packetsNow = 0;\n            }\n\n            const packetsBefore = this.getNonNegativeValue(before[key]);\n            const packetsDiff = Math.max(0, packetsNow - packetsBefore);\n\n            const packetsLostNow = this.getNonNegativeValue(now.packetsLost);\n            const packetsLostBefore = this.getNonNegativeValue(before.packetsLost);\n            const packetsLostDiff = Math.max(0, packetsLostNow - packetsLostBefore);\n\n            ssrcStats.setLoss({\n                packetsTotal: packetsDiff + packetsLostDiff,\n                packetsLost: packetsLostDiff,\n                isDownloadStream\n            });\n\n            // Get the resolution and framerate for only remote video sources here. For the local video sources,\n            // 'track' stats will be used since they have the updated resolution based on the simulcast streams\n            // currently being sent. Promise based getStats reports three 'outbound-rtp' streams and there will be\n            // more calculations needed to determine what is the highest resolution stream sent by the client if the\n            // 'outbound-rtp' stats are used.\n            if (now.type === 'inbound-rtp') {\n                const resolution = {\n                    height: now.frameHeight,\n                    width: now.frameWidth\n                };\n                const frameRate = now.framesPerSecond;\n\n                if (resolution.height && resolution.width) {\n                    ssrcStats.setResolution(resolution);\n                }\n                ssrcStats.setFramerate(Math.round(frameRate || 0));\n\n                ssrcStats.addBitrate({\n                    'download': this._calculateBitrate(now, before, 'bytesReceived'),\n                    'upload': 0\n                });\n            } else {\n                byteSentStats[ssrc] = this.getNonNegativeValue(now.bytesSent);\n                ssrcStats.addBitrate({\n                    'download': 0,\n                    'upload': this._calculateBitrate(now, before, 'bytesSent')\n                });\n            }\n\n            const codec = this.currentStatsReport.get(now.codecId);\n\n            if (codec) {\n                /**\n                 * The mime type has the following form: video/VP8 or audio/ISAC,\n                 * so we what to keep just the type after the '/', audio and video\n                 * keys will be added on the processing side.\n                 */\n                const codecShortType = codec.mimeType.split('/')[1];\n\n                codecShortType && ssrcStats.setCodec(codecShortType);\n            }\n\n        // Use track stats for resolution and framerate of the local video source.\n        // RTCVideoHandlerStats - https://w3c.github.io/webrtc-stats/#vststats-dict*\n        // RTCMediaHandlerStats - https://w3c.github.io/webrtc-stats/#mststats-dict*\n        } else if (now.type === 'track' && now.kind === MediaType.VIDEO && !now.remoteSource) {\n            const resolution = {\n                height: now.frameHeight,\n                width: now.frameWidth\n            };\n            const localVideoTracks = this.peerconnection.getLocalTracks(MediaType.VIDEO);\n\n            if (!localVideoTracks?.length) {\n                return;\n            }\n\n            const ssrc = this.peerconnection.getLocalSSRC(localVideoTracks[0]);\n\n            if (!ssrc) {\n                return;\n            }\n            let ssrcStats = this.ssrc2stats.get(ssrc);\n\n            if (!ssrcStats) {\n                ssrcStats = new SsrcStats();\n                this.ssrc2stats.set(ssrc, ssrcStats);\n            }\n            if (resolution.height && resolution.width) {\n                ssrcStats.setResolution(resolution);\n            }\n\n            // Calculate the frame rate. 'framesSent' is the total aggregate value for all the simulcast streams.\n            // Therefore, it needs to be divided by the total number of active simulcast streams.\n            let frameRate = now.framesPerSecond;\n\n            if (!frameRate) {\n                const before = this.previousStatsReport.get(now.id);\n\n                if (before) {\n                    const timeMs = now.timestamp - before.timestamp;\n\n                    if (timeMs > 0 && now.framesSent) {\n                        const numberOfFramesSinceBefore = now.framesSent - before.framesSent;\n\n                        frameRate = (numberOfFramesSinceBefore / timeMs) * 1000;\n                    }\n                }\n\n                if (!frameRate) {\n                    return;\n                }\n            }\n\n            // Get the number of simulcast streams currently enabled from TPC.\n            const numberOfActiveStreams = this.peerconnection.getActiveSimulcastStreams();\n\n            // Reset frame rate to 0 when video is suspended as a result of endpoint falling out of last-n.\n            frameRate = numberOfActiveStreams ? Math.round(frameRate / numberOfActiveStreams) : 0;\n            ssrcStats.setFramerate(frameRate);\n        }\n    });\n\n    this.eventEmitter.emit(StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);\n    this._processAndEmitReport();\n};\n\n/**\n * Stats processing logic.\n */\nStatsCollector.prototype.processAudioLevelReport = function() {\n    if (!this.baselineAudioLevelsReport) {\n        return;\n    }\n\n    this.currentAudioLevelsReport.forEach(now => {\n        if (now.type !== 'track') {\n            return;\n        }\n\n        // Audio level\n        const audioLevel = now.audioLevel;\n\n        if (!audioLevel) {\n            return;\n        }\n\n        const trackIdentifier = now.trackIdentifier;\n        const ssrc = this.peerconnection.getSsrcByTrackId(trackIdentifier);\n\n        if (ssrc) {\n            const isLocal\n                = ssrc === this.peerconnection.getLocalSSRC(\n                this.peerconnection.getLocalTracks(MediaType.AUDIO));\n\n            this.eventEmitter.emit(\n                StatisticsEvents.AUDIO_LEVEL,\n                this.peerconnection,\n                ssrc,\n                audioLevel,\n                isLocal);\n        }\n    });\n};\n\n","import EventEmitter from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport JitsiTrackError from '../../JitsiTrackError';\nimport { FEEDBACK } from '../../service/statistics/AnalyticsEvents';\nimport * as StatisticsEvents from '../../service/statistics/Events';\nimport browser from '../browser';\nimport ScriptUtil from '../util/ScriptUtil';\n\nimport analytics from './AnalyticsAdapter';\nimport CallStats from './CallStats';\nimport LocalStats from './LocalStatsCollector';\nimport { PerformanceObserverStats } from './PerformanceObserverStats';\nimport RTPStats from './RTPStatsCollector';\nimport { CALLSTATS_SCRIPT_URL } from './constants';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * Stores all active {@link Statistics} instances.\n * @type {Set<Statistics>}\n */\nlet _instances;\n\n/**\n * True if callstats API is loaded\n */\nlet isCallstatsLoaded = false;\n\n/**\n * Since callstats.io is a third party, we cannot guarantee the quality of their\n * service. More specifically, their server may take noticeably long time to\n * respond. Consequently, it is in our best interest (in the sense that the\n * intergration of callstats.io is pretty important to us but not enough to\n * allow it to prevent people from joining a conference) to (1) start\n * downloading their API as soon as possible and (2) do the downloading\n * asynchronously.\n *\n * @param {StatisticsOptions} options - Options to use for downloading and\n * initializing callstats backend.\n */\nfunction loadCallStatsAPI(options) {\n    if (!isCallstatsLoaded) {\n        ScriptUtil.loadScript(\n            options.customScriptUrl || CALLSTATS_SCRIPT_URL,\n            /* async */ true,\n            /* prepend */ true,\n            /* relativeURL */ undefined,\n            /* loadCallback */ () => _initCallStatsBackend(options)\n        );\n        isCallstatsLoaded = true;\n    }\n}\n\n/**\n * Initializes Callstats backend.\n *\n * @param {StatisticsOptions} options - The options to use for initializing\n * callstats backend.\n * @private\n */\nfunction _initCallStatsBackend(options) {\n    if (CallStats.isBackendInitialized()) {\n        return;\n    }\n\n    if (!CallStats.initBackend({\n        callStatsID: options.callStatsID,\n        callStatsSecret: options.callStatsSecret,\n        userName: options.userName,\n        aliasName: options.aliasName,\n        applicationName: options.applicationName,\n        getWiFiStatsMethod: options.getWiFiStatsMethod,\n        confID: options.confID,\n        siteID: options.siteID,\n        configParams: options.configParams\n    })) {\n        logger.error('CallStats Backend initialization failed bad');\n    }\n}\n\n/**\n * callstats strips any additional fields from Error except for \"name\", \"stack\",\n * \"message\" and \"constraintName\". So we need to bundle additional information\n * from JitsiTrackError into error passed to callstats to preserve valuable\n * information about error.\n * @param {JitsiTrackError} error\n */\nfunction formatJitsiTrackErrorForCallStats(error) {\n    const err = new Error();\n\n    // Just copy original stack from error\n    err.stack = error.stack;\n\n    // Combine name from error's name plus (possibly) name of original GUM error\n    err.name = (error.name || 'Unknown error') + (error.gum && error.gum.error\n        && error.gum.error.name ? ` - ${error.gum.error.name}` : '');\n\n    // Put all constraints into this field. For constraint failed errors we will\n    // still know which exactly constraint failed as it will be a part of\n    // message.\n    err.constraintName = error.gum && error.gum.constraints\n        ? JSON.stringify(error.gum.constraints) : '';\n\n    // Just copy error's message.\n    err.message = error.message;\n\n    return err;\n}\n\n/**\n * Init statistic options\n * @param options\n */\nStatistics.init = function(options) {\n    Statistics.audioLevelsEnabled = !options.disableAudioLevels;\n    if (typeof options.pcStatsInterval === 'number') {\n        Statistics.pcStatsInterval = options.pcStatsInterval;\n    }\n\n    if (typeof options.audioLevelsInterval === 'number') {\n        Statistics.audioLevelsInterval = options.audioLevelsInterval;\n    }\n\n    if (typeof options.longTasksStatsInterval === 'number') {\n        Statistics.longTasksStatsInterval = options.longTasksStatsInterval;\n    }\n\n    Statistics.disableThirdPartyRequests = options.disableThirdPartyRequests;\n};\n\n/**\n * The options to configure Statistics.\n * @typedef {Object} StatisticsOptions\n * @property {string} applicationName - The application name to pass to\n * callstats.\n * @property {string} aliasName - The alias name to use when initializing callstats.\n * @property {string} userName - The user name to use when initializing callstats.\n * @property {string} confID - The callstats conference ID to use.\n * @property {string} callStatsID - Callstats credentials - the id.\n * @property {string} callStatsSecret - Callstats credentials - the secret.\n * @property {string} customScriptUrl - A custom lib url to use when downloading\n * callstats library.\n * @property {string} roomName - The room name we are currently in.\n * @property {string} configParams - The set of parameters\n * to enable/disable certain features in the library. See CallStats docs for more info.\n */\n/**\n *\n * @param xmpp\n * @param {StatisticsOptions} options - The options to use creating the\n * Statistics.\n */\nexport default function Statistics(xmpp, options) {\n    /**\n     * {@link RTPStats} mapped by {@link TraceablePeerConnection.id} which\n     * collect RTP statistics for each peerconnection.\n     * @type {Map<string, RTPStats}\n     */\n    this.rtpStatsMap = new Map();\n    this.eventEmitter = new EventEmitter();\n    this.xmpp = xmpp;\n    this.options = options || {};\n\n    this.callStatsIntegrationEnabled\n        = this.options.callStatsID && this.options.callStatsSecret && this.options.enableCallStats\n\n            // Even though AppID and AppSecret may be specified, the integration\n            // of callstats.io may be disabled because of globally-disallowed\n            // requests to any third parties.\n            && (Statistics.disableThirdPartyRequests !== true);\n    if (this.callStatsIntegrationEnabled) {\n        this.callStatsApplicationLogsDisabled\n            = this.options.callStatsApplicationLogsDisabled;\n        if (browser.isReactNative()) {\n            _initCallStatsBackend(this.options);\n        } else {\n            loadCallStatsAPI(this.options);\n        }\n\n        if (!this.options.confID) {\n            logger.warn('\"confID\" is not defined');\n        }\n    }\n\n    /**\n     * Stores {@link CallStats} instances for each\n     * {@link TraceablePeerConnection} (one {@link CallStats} instance serves\n     * one TPC). The instances are mapped by {@link TraceablePeerConnection.id}.\n     * @type {Map<number, CallStats>}\n     */\n    this.callsStatsInstances = new Map();\n\n    Statistics.instances.add(this);\n}\nStatistics.audioLevelsEnabled = false;\nStatistics.audioLevelsInterval = 200;\nStatistics.pcStatsInterval = 10000;\nStatistics.disableThirdPartyRequests = false;\nStatistics.analytics = analytics;\n\nObject.defineProperty(Statistics, 'instances', {\n    /**\n     * Returns the Set holding all active {@link Statistics} instances. Lazily\n     * initializes the Set to allow any Set polyfills to be applied.\n     * @type {Set<Statistics>}\n     */\n    get() {\n        if (!_instances) {\n            _instances = new Set();\n        }\n\n        return _instances;\n    }\n});\n\n/**\n * Starts collecting RTP stats for given peerconnection.\n * @param {TraceablePeerConnection} peerconnection\n */\nStatistics.prototype.startRemoteStats = function(peerconnection) {\n    this.stopRemoteStats(peerconnection);\n\n    try {\n        const rtpStats\n            = new RTPStats(\n                peerconnection,\n                Statistics.audioLevelsInterval,\n                Statistics.pcStatsInterval,\n                this.eventEmitter);\n\n        rtpStats.start(Statistics.audioLevelsEnabled);\n        this.rtpStatsMap.set(peerconnection.id, rtpStats);\n    } catch (e) {\n        logger.error(`Failed to start collecting remote statistics: ${e}`);\n    }\n};\n\nStatistics.localStats = [];\n\nStatistics.startLocalStats = function(stream, callback) {\n    if (!Statistics.audioLevelsEnabled) {\n        return;\n    }\n    const localStats = new LocalStats(stream, Statistics.audioLevelsInterval,\n        callback);\n\n    this.localStats.push(localStats);\n    localStats.start();\n};\n\nStatistics.prototype.addAudioLevelListener = function(listener) {\n    if (!Statistics.audioLevelsEnabled) {\n        return;\n    }\n    this.eventEmitter.on(StatisticsEvents.AUDIO_LEVEL, listener);\n};\n\nStatistics.prototype.removeAudioLevelListener = function(listener) {\n    if (!Statistics.audioLevelsEnabled) {\n        return;\n    }\n    this.eventEmitter.removeListener(StatisticsEvents.AUDIO_LEVEL, listener);\n};\n\nStatistics.prototype.addBeforeDisposedListener = function(listener) {\n    this.eventEmitter.on(StatisticsEvents.BEFORE_DISPOSED, listener);\n};\n\nStatistics.prototype.removeBeforeDisposedListener = function(listener) {\n    this.eventEmitter.removeListener(\n        StatisticsEvents.BEFORE_DISPOSED, listener);\n};\n\nStatistics.prototype.addConnectionStatsListener = function(listener) {\n    this.eventEmitter.on(StatisticsEvents.CONNECTION_STATS, listener);\n};\n\nStatistics.prototype.removeConnectionStatsListener = function(listener) {\n    this.eventEmitter.removeListener(\n        StatisticsEvents.CONNECTION_STATS,\n        listener);\n};\n\nStatistics.prototype.addByteSentStatsListener = function(listener) {\n    this.eventEmitter.on(StatisticsEvents.BYTE_SENT_STATS, listener);\n};\n\nStatistics.prototype.removeByteSentStatsListener = function(listener) {\n    this.eventEmitter.removeListener(StatisticsEvents.BYTE_SENT_STATS,\n        listener);\n};\n\n/**\n * Add a listener that would be notified on a LONG_TASKS_STATS event.\n *\n * @param {Function} listener a function that would be called when notified.\n * @returns {void}\n */\nStatistics.prototype.addLongTasksStatsListener = function(listener) {\n    this.eventEmitter.on(StatisticsEvents.LONG_TASKS_STATS, listener);\n};\n\n/**\n * Creates an instance of {@link PerformanceObserverStats} and starts the\n * observer that records the stats periodically.\n *\n * @returns {void}\n */\nStatistics.prototype.attachLongTasksStats = function(conference) {\n    if (!browser.supportsPerformanceObserver()) {\n        logger.warn('Performance observer for long tasks not supported by browser!');\n\n        return;\n    }\n\n    this.performanceObserverStats = new PerformanceObserverStats(\n        this.eventEmitter,\n        Statistics.longTasksStatsInterval);\n\n    conference.on(\n        JitsiConferenceEvents.CONFERENCE_JOINED,\n        () => this.performanceObserverStats.startObserver());\n    conference.on(\n        JitsiConferenceEvents.CONFERENCE_LEFT,\n        () => this.performanceObserverStats.stopObserver());\n};\n\n/**\n * Obtains the current value of the LongTasks event statistics.\n *\n * @returns {Object|null} stats object if the observer has been\n * created, null otherwise.\n */\nStatistics.prototype.getLongTasksStats = function() {\n    return this.performanceObserverStats\n        ? this.performanceObserverStats.getLongTasksStats()\n        : null;\n};\n\n/**\n * Removes the given listener for the LONG_TASKS_STATS event.\n *\n * @param {Function} listener the listener we want to remove.\n * @returns {void}\n */\nStatistics.prototype.removeLongTasksStatsListener = function(listener) {\n    this.eventEmitter.removeListener(StatisticsEvents.LONG_TASKS_STATS, listener);\n};\n\n/**\n * Updates the list of speakers for which the audio levels are to be calculated. This is needed for the jvb pc only.\n *\n * @param {Array<string>} speakerList The list of remote endpoint ids.\n * @returns {void}\n */\nStatistics.prototype.setSpeakerList = function(speakerList) {\n    for (const rtpStats of Array.from(this.rtpStatsMap.values())) {\n        if (!rtpStats.peerconnection.isP2P) {\n            rtpStats.setSpeakerList(speakerList);\n        }\n    }\n};\n\nStatistics.prototype.dispose = function() {\n    try {\n        // NOTE Before reading this please see the comment in stopCallStats...\n        //\n        // Here we prevent from emitting the event twice in case it will be\n        // triggered from stopCallStats.\n        // If the event is triggered from here it means that the logs will not\n        // be submitted anyway (because there is no CallStats instance), but\n        // we're doing that for the sake of some kind of consistency.\n        if (!this.callsStatsInstances.size) {\n            this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);\n        }\n        for (const callStats of this.callsStatsInstances.values()) {\n            this.stopCallStats(callStats.tpc);\n        }\n        for (const tpcId of this.rtpStatsMap.keys()) {\n            this._stopRemoteStats(tpcId);\n        }\n        if (this.eventEmitter) {\n            this.eventEmitter.removeAllListeners();\n        }\n    } finally {\n        Statistics.instances.delete(this);\n    }\n};\n\nStatistics.stopLocalStats = function(stream) {\n    if (!Statistics.audioLevelsEnabled) {\n        return;\n    }\n\n    for (let i = 0; i < Statistics.localStats.length; i++) {\n        if (Statistics.localStats[i].stream === stream) {\n            const localStats = Statistics.localStats.splice(i, 1);\n\n            localStats[0].stop();\n            break;\n        }\n    }\n};\n\n/**\n * Stops remote RTP stats for given peerconnection ID.\n * @param {string} tpcId {@link TraceablePeerConnection.id}\n * @private\n */\nStatistics.prototype._stopRemoteStats = function(tpcId) {\n    const rtpStats = this.rtpStatsMap.get(tpcId);\n\n    if (rtpStats) {\n        rtpStats.stop();\n        this.rtpStatsMap.delete(tpcId);\n    }\n};\n\n/**\n * Stops collecting RTP stats for given peerconnection\n * @param {TraceablePeerConnection} tpc\n */\nStatistics.prototype.stopRemoteStats = function(tpc) {\n    this._stopRemoteStats(tpc.id);\n};\n\n// CALSTATS METHODS\n\n/**\n * Initializes the callstats.io API.\n * @param {TraceablePeerConnection} tpc the {@link TraceablePeerConnection}\n * instance for which CalStats will be started.\n * @param {string} remoteUserID\n */\nStatistics.prototype.startCallStats = function(tpc, remoteUserID) {\n    if (!this.callStatsIntegrationEnabled) {\n        return;\n    } else if (this.callsStatsInstances.has(tpc.id)) {\n        logger.error('CallStats instance for ${tpc} exists already');\n\n        return;\n    }\n\n    logger.info(`Starting CallStats for ${tpc}...`);\n\n    const newInstance\n        = new CallStats(\n            tpc,\n            {\n                confID: this.options.confID,\n                remoteUserID\n            });\n\n    this.callsStatsInstances.set(tpc.id, newInstance);\n};\n\n/**\n * Obtains the list of *all* {@link CallStats} instances collected from every\n * valid {@link Statistics} instance.\n * @return {Set<CallStats>}\n * @private\n */\nStatistics._getAllCallStatsInstances = function() {\n    const csInstances = new Set();\n\n    for (const statistics of Statistics.instances) {\n        for (const cs of statistics.callsStatsInstances.values()) {\n            csInstances.add(cs);\n        }\n    }\n\n    return csInstances;\n};\n\n/**\n * Removes the callstats.io instances.\n */\nStatistics.prototype.stopCallStats = function(tpc) {\n    const callStatsInstance = this.callsStatsInstances.get(tpc.id);\n\n    if (callStatsInstance) {\n        // FIXME the original purpose of adding BEFORE_DISPOSED event was to be\n        // able to submit the last log batch from jitsi-meet to CallStats. After\n        // recent changes we dispose the CallStats earlier\n        // (before Statistics.dispose), so we need to emit this event here to\n        // give this last chance for final log batch submission.\n        //\n        // Eventually there should be a separate module called \"log storage\"\n        // which should emit proper events when it's underlying\n        // CallStats instance is going away.\n        if (this.callsStatsInstances.size === 1) {\n            this.eventEmitter.emit(StatisticsEvents.BEFORE_DISPOSED);\n        }\n        this.callsStatsInstances.delete(tpc.id);\n\n        // The fabric needs to be terminated when being stopped\n        callStatsInstance.sendTerminateEvent();\n    }\n};\n\n/**\n * Returns true if the callstats integration is enabled, otherwise returns\n * false.\n *\n * @returns true if the callstats integration is enabled, otherwise returns\n * false.\n */\nStatistics.prototype.isCallstatsEnabled = function() {\n    return this.callStatsIntegrationEnabled;\n};\n\n/**\n * Logs either resume or hold event for the given peer connection.\n * @param {TraceablePeerConnection} tpc the connection for which event will be\n * reported\n * @param {boolean} isResume true for resume or false for hold\n */\nStatistics.prototype.sendConnectionResumeOrHoldEvent = function(tpc, isResume) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendResumeOrHoldEvent(isResume);\n    }\n};\n\n/**\n * Notifies CallStats and analytics (if present) for ice connection failed\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendIceConnectionFailedEvent = function(tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendIceConnectionFailedEvent();\n    }\n};\n\n/**\n * Notifies CallStats for mute events\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n * @param {boolean} muted true for muted and false for not muted\n * @param {String} type \"audio\"/\"video\"\n */\nStatistics.prototype.sendMuteEvent = function(tpc, muted, type) {\n    const instance = tpc && this.callsStatsInstances.get(tpc.id);\n\n    CallStats.sendMuteEvent(muted, type, instance);\n};\n\n/**\n * Notifies CallStats for screen sharing events\n * @param start {boolean} true for starting screen sharing and\n * false for not stopping\n * @param {string|null} ssrc - optional ssrc value, used only when\n * starting screen sharing.\n */\nStatistics.prototype.sendScreenSharingEvent\n    = function(start, ssrc) {\n        for (const cs of this.callsStatsInstances.values()) {\n            cs.sendScreenSharingEvent(start, ssrc);\n        }\n    };\n\n/**\n * Notifies the statistics module that we are now the dominant speaker of the\n * conference.\n * @param {String} roomJid - The room jid where the speaker event occurred.\n */\nStatistics.prototype.sendDominantSpeakerEvent = function(roomJid) {\n    for (const cs of this.callsStatsInstances.values()) {\n        cs.sendDominantSpeakerEvent();\n    }\n\n    // xmpp send dominant speaker event\n    this.xmpp.sendDominantSpeakerEvent(roomJid);\n};\n\n/**\n * Notifies about active device.\n * @param {{deviceList: {String:String}}} devicesData - list of devices with\n *      their data\n */\nStatistics.sendActiveDeviceListEvent = function(devicesData) {\n    const globalSet = Statistics._getAllCallStatsInstances();\n\n    if (globalSet.size) {\n        for (const cs of globalSet) {\n            CallStats.sendActiveDeviceListEvent(devicesData, cs);\n        }\n    } else {\n        CallStats.sendActiveDeviceListEvent(devicesData, null);\n    }\n};\n\n/* eslint-disable max-params */\n\n/**\n * Lets the underlying statistics module know where is given SSRC rendered by\n * providing renderer tag ID.\n * @param {TraceablePeerConnection} tpc the connection to which the stream\n * belongs to\n * @param {number} ssrc the SSRC of the stream\n * @param {boolean} isLocal\n * @param {string} userId\n * @param {string} usageLabel  meaningful usage label of this stream like\n *        'microphone', 'camera' or 'screen'.\n * @param {string} containerId the id of media 'audio' or 'video' tag which\n *        renders the stream.\n */\nStatistics.prototype.associateStreamWithVideoTag = function(\n        tpc,\n        ssrc,\n        isLocal,\n        userId,\n        usageLabel,\n        containerId) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.associateStreamWithVideoTag(\n            ssrc,\n            isLocal,\n            userId,\n            usageLabel,\n            containerId);\n    }\n};\n\n/* eslint-enable max-params */\n\n/**\n * Notifies CallStats that getUserMedia failed.\n *\n * @param {Error} e error to send\n */\nStatistics.sendGetUserMediaFailed = function(e) {\n    const error\n        = e instanceof JitsiTrackError\n            ? formatJitsiTrackErrorForCallStats(e) : e;\n    const globalSet = Statistics._getAllCallStatsInstances();\n\n    if (globalSet.size) {\n        for (const cs of globalSet) {\n            CallStats.sendGetUserMediaFailed(error, cs);\n        }\n    } else {\n        CallStats.sendGetUserMediaFailed(error, null);\n    }\n};\n\n/**\n * Notifies CallStats that peer connection failed to create offer.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendCreateOfferFailed = function(e, tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendCreateOfferFailed(e);\n    }\n};\n\n/**\n * Notifies CallStats that peer connection failed to create answer.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occured.\n */\nStatistics.prototype.sendCreateAnswerFailed = function(e, tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendCreateAnswerFailed(e);\n    }\n};\n\n/**\n * Notifies CallStats that peer connection failed to set local description.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendSetLocalDescFailed = function(e, tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendSetLocalDescFailed(e);\n    }\n};\n\n/**\n * Notifies CallStats that peer connection failed to set remote description.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendSetRemoteDescFailed = function(e, tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendSetRemoteDescFailed(e);\n    }\n};\n\n/**\n * Notifies CallStats that peer connection failed to add ICE candidate.\n *\n * @param {Error} e error to send\n * @param {TraceablePeerConnection} tpc connection on which failure occurred.\n */\nStatistics.prototype.sendAddIceCandidateFailed = function(e, tpc) {\n    const instance = this.callsStatsInstances.get(tpc.id);\n\n    if (instance) {\n        instance.sendAddIceCandidateFailed(e);\n    }\n};\n\n/**\n * Adds to CallStats an application log.\n *\n * @param {String} m a log message to send or an {Error} object to be reported\n */\nStatistics.sendLog = function(m) {\n    const globalSubSet = new Set();\n\n    // FIXME we don't want to duplicate logs over P2P instance, but\n    // here we should go over instances and call this method for each\n    // unique conference ID rather than selecting the first one.\n    // We don't have such use case though, so leaving as is for now.\n    for (const stats of Statistics.instances) {\n        if (stats.callStatsApplicationLogsDisabled) {\n            return;\n        }\n\n        if (stats.callsStatsInstances.size) {\n            globalSubSet.add(stats.callsStatsInstances.values().next().value);\n        }\n    }\n\n    if (globalSubSet.size) {\n        for (const csPerStats of globalSubSet) {\n            CallStats.sendApplicationLog(m, csPerStats);\n        }\n    } else {\n        CallStats.sendApplicationLog(m, null);\n    }\n};\n\n/**\n * Sends the given feedback through CallStats.\n *\n * @param overall an integer between 1 and 5 indicating the user's rating.\n * @param comment the comment from the user.\n * @returns {Promise} Resolves when callstats feedback has been submitted\n * successfully.\n */\nStatistics.prototype.sendFeedback = function(overall, comment) {\n    // Statistics.analytics.sendEvent is currently fire and forget, without\n    // confirmation of successful send.\n    Statistics.analytics.sendEvent(\n        FEEDBACK,\n        {\n            rating: overall,\n            comment\n        });\n\n    return CallStats.sendFeedback(this.options.confID, overall, comment);\n};\n\nStatistics.LOCAL_JID = require('../../service/statistics/constants').LOCAL_JID;\n\n/**\n * Reports global error to CallStats.\n *\n * @param {Error} error\n */\nStatistics.reportGlobalError = function(error) {\n    if (error instanceof JitsiTrackError && error.gum) {\n        Statistics.sendGetUserMediaFailed(error);\n    } else {\n        Statistics.sendLog(error);\n    }\n};\n\n/**\n * Sends event to analytics and logs a message to the logger/console. Console\n * messages might also be logged to callstats automatically.\n *\n * @param {string | Object} event the event name, or an object which\n * represents the entire event.\n * @param {Object} properties properties to attach to the event (if an event\n * name as opposed to an event object is provided).\n */\nStatistics.sendAnalyticsAndLog = function(event, properties = {}) {\n    if (!event) {\n        logger.warn('No event or event name given.');\n\n        return;\n    }\n\n    let eventToLog;\n\n    // Also support an API with a single object as an event.\n    if (typeof event === 'object') {\n        eventToLog = event;\n    } else {\n        eventToLog = {\n            name: event,\n            properties\n        };\n    }\n\n    logger.log(JSON.stringify(eventToLog));\n\n    // We do this last, because it may modify the object which is passed.\n    this.analytics.sendEvent(event, properties);\n};\n\n/**\n * Sends event to analytics.\n *\n * @param {string | Object} eventName the event name, or an object which\n * represents the entire event.\n * @param {Object} properties properties to attach to the event\n */\nStatistics.sendAnalytics = function(eventName, properties = {}) {\n    this.analytics.sendEvent(eventName, properties);\n};\n","import { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport { SPEAKERS_AUDIO_LEVELS } from './modules/statistics/constants';\nimport Statistics from './modules/statistics/statistics';\nimport EventEmitterForwarder from './modules/util/EventEmitterForwarder';\nimport { MediaType } from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\nimport VideoType from './service/RTC/VideoType';\nimport AuthenticationEvents\n    from './service/authentication/AuthenticationEvents';\nimport {\n    ACTION_JINGLE_SA_TIMEOUT,\n    createBridgeDownEvent,\n    createConnectionStageReachedEvent,\n    createFocusLeftEvent,\n    createJingleEvent,\n    createRemotelyMutedEvent\n} from './service/statistics/AnalyticsEvents';\nimport { XMPPEvents } from './service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * Setups all event listeners related to conference\n * @param conference {JitsiConference} the conference\n */\nexport default function JitsiConferenceEventManager(conference) {\n    this.conference = conference;\n    this.xmppListeners = {};\n\n    // Listeners related to the conference only\n    conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED,\n        track => {\n            if (!track.isLocal() || !conference.statistics) {\n                return;\n            }\n            const session\n                = track.isP2P\n                    ? conference.p2pJingleSession : conference.jvbJingleSession;\n\n            // TPC will be null, before the conference starts, but the event\n            // still should be queued\n            const tpc = (session && session.peerconnection) || null;\n\n            conference.statistics.sendMuteEvent(\n                tpc,\n                track.isMuted(),\n                track.getType());\n        });\n}\n\n/**\n * Setups event listeners related to conference.chatRoom\n */\nJitsiConferenceEventManager.prototype.setupChatRoomListeners = function() {\n    const conference = this.conference;\n    const chatRoom = conference.room;\n\n    this.chatRoomForwarder = new EventEmitterForwarder(chatRoom,\n        this.conference.eventEmitter);\n\n    chatRoom.addListener(XMPPEvents.ICE_RESTARTING, jingleSession => {\n        if (!jingleSession.isP2P) {\n            // If using DataChannel as bridge channel, it must be closed\n            // before ICE restart, otherwise Chrome will not trigger \"opened\"\n            // event for the channel established with the new bridge.\n            // TODO: This may be bypassed when using a WebSocket as bridge\n            // channel.\n            conference.rtc.closeBridgeChannel();\n        }\n\n        // else: there are no DataChannels in P2P session (at least for now)\n    });\n\n    chatRoom.addListener(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, (from, features) => {\n        const participant = conference.getParticipantById(Strophe.getResourceFromJid(from));\n\n        if (participant) {\n            participant.setFeatures(features);\n            conference.eventEmitter.emit(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED, participant);\n        }\n    });\n\n    chatRoom.addListener(\n        XMPPEvents.ICE_RESTART_SUCCESS,\n        (jingleSession, offerIq) => {\n            // The JVB data chanel needs to be reopened in case the conference\n            // has been moved to a new bridge.\n            !jingleSession.isP2P\n                && conference._setBridgeChannel(\n                    offerIq, jingleSession.peerconnection);\n        });\n\n\n    chatRoom.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS,\n        actor => {\n            // TODO: Add a way to differentiate between commands which caused\n            // us to mute and those that did not change our state (i.e. we were\n            // already muted).\n            Statistics.sendAnalytics(createRemotelyMutedEvent(MediaType.AUDIO));\n\n            conference.mutedByFocusActor = actor;\n\n            // set isMutedByFocus when setAudioMute Promise ends\n            conference.rtc.setAudioMute(true).then(\n                () => {\n                    conference.isMutedByFocus = true;\n                    conference.mutedByFocusActor = null;\n                })\n                .catch(\n                    error => {\n                        conference.mutedByFocusActor = null;\n                        logger.warn(\n                            'Error while audio muting due to focus request', error);\n                    });\n        }\n    );\n\n    chatRoom.addListener(XMPPEvents.VIDEO_MUTED_BY_FOCUS,\n        actor => {\n            // TODO: Add a way to differentiate between commands which caused\n            // us to mute and those that did not change our state (i.e. we were\n            // already muted).\n            Statistics.sendAnalytics(createRemotelyMutedEvent(MediaType.VIDEO));\n\n            conference.mutedVideoByFocusActor = actor;\n\n            // set isVideoMutedByFocus when setVideoMute Promise ends\n            conference.rtc.setVideoMute(true).then(\n                () => {\n                    conference.isVideoMutedByFocus = true;\n                    conference.mutedVideoByFocusActor = null;\n                })\n                .catch(\n                    error => {\n                        conference.mutedVideoByFocusActor = null;\n                        logger.warn(\n                            'Error while video muting due to focus request', error);\n                    });\n        }\n    );\n\n    this.chatRoomForwarder.forward(XMPPEvents.SUBJECT_CHANGED,\n        JitsiConferenceEvents.SUBJECT_CHANGED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_JOINED,\n        JitsiConferenceEvents.CONFERENCE_JOINED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_JOIN_IN_PROGRESS,\n        JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS);\n\n    this.chatRoomForwarder.forward(XMPPEvents.MEETING_ID_SET,\n        JitsiConferenceEvents.CONFERENCE_UNIQUE_ID_SET);\n\n    // send some analytics events\n    chatRoom.addListener(XMPPEvents.MUC_JOINED,\n        () => {\n            this.conference._onMucJoined();\n\n            this.conference.isJvbConnectionInterrupted = false;\n\n            // TODO: Move all of the 'connectionTimes' logic to its own module.\n            Object.keys(chatRoom.connectionTimes).forEach(key => {\n                const event\n                    = createConnectionStageReachedEvent(\n                        `conference_${key}`,\n                        { value: chatRoom.connectionTimes[key] });\n\n                Statistics.sendAnalytics(event);\n            });\n\n            // TODO: Move all of the 'connectionTimes' logic to its own module.\n            Object.keys(chatRoom.xmpp.connectionTimes).forEach(key => {\n                const event\n                    = createConnectionStageReachedEvent(\n                        `xmpp_${key}`,\n                        { value: chatRoom.xmpp.connectionTimes[key] });\n\n                Statistics.sendAnalytics(event);\n            });\n        });\n\n    chatRoom.addListener(XMPPEvents.RENEGOTIATION_FAILED, (e, session) => {\n        if (!session.isP2P) {\n            conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n                JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n        }\n    });\n\n    this.chatRoomForwarder.forward(XMPPEvents.ROOM_JOIN_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.CONNECTION_ERROR);\n\n    this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.CONNECTION_ERROR);\n    this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.NOT_ALLOWED_ERROR);\n    this.chatRoomForwarder.forward(XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.MEMBERS_ONLY_ERROR);\n\n    this.chatRoomForwarder.forward(XMPPEvents.ROOM_MAX_USERS_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.CONFERENCE_MAX_USERS);\n\n    this.chatRoomForwarder.forward(XMPPEvents.PASSWORD_REQUIRED,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.PASSWORD_REQUIRED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.AUTHENTICATION_REQUIRED,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.AUTHENTICATION_REQUIRED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.BRIDGE_DOWN,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);\n    chatRoom.addListener(\n        XMPPEvents.BRIDGE_DOWN,\n        () => Statistics.sendAnalytics(createBridgeDownEvent()));\n\n    chatRoom.addListener(XMPPEvents.CONNECTION_RESTARTED,\n        jingleSession => {\n            conference._onConferenceRestarted(jingleSession);\n        });\n\n    this.chatRoomForwarder.forward(XMPPEvents.RESERVATION_ERROR,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.RESERVATION_ERROR);\n    chatRoom.addListener(XMPPEvents.RESERVATION_ERROR,\n        () => {\n            conference.xmpp.connection.emuc.doLeave(conference.room.roomjid);\n        });\n\n    this.chatRoomForwarder.forward(XMPPEvents.GRACEFUL_SHUTDOWN,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.GRACEFUL_SHUTDOWN);\n\n    chatRoom.addListener(XMPPEvents.CONNECTION_ICE_FAILED,\n        jingleSession => {\n            conference._onIceConnectionFailed(jingleSession);\n        });\n\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_DESTROYED,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.CONFERENCE_DESTROYED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.CHAT_ERROR_RECEIVED,\n        JitsiConferenceEvents.CONFERENCE_ERROR,\n        JitsiConferenceErrors.CHAT_ERROR);\n\n    this.chatRoomForwarder.forward(XMPPEvents.SETTINGS_ERROR_RECEIVED,\n        JitsiConferenceEvents.CONFERENCE_ERROR,\n        JitsiConferenceErrors.SETTINGS_ERROR);\n\n    this.chatRoomForwarder.forward(XMPPEvents.FOCUS_DISCONNECTED,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.FOCUS_DISCONNECTED);\n\n    chatRoom.addListener(XMPPEvents.FOCUS_LEFT,\n        () => {\n            Statistics.sendAnalytics(createFocusLeftEvent());\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.CONFERENCE_FAILED,\n                JitsiConferenceErrors.FOCUS_LEFT);\n        });\n\n    chatRoom.addListener(XMPPEvents.SESSION_ACCEPT_TIMEOUT,\n        jingleSession => {\n            Statistics.sendAnalyticsAndLog(\n                createJingleEvent(\n                    ACTION_JINGLE_SA_TIMEOUT,\n                    { p2p: jingleSession.isP2P }));\n        });\n\n    chatRoom.addListener(XMPPEvents.RECORDER_STATE_CHANGED,\n        (session, jid) => {\n\n            if (jid) {\n                const resource = Strophe.getResourceFromJid(jid);\n                const participant = conference.getParticipantById(resource) || resource;\n\n                if (session.getStatus() === 'off') {\n                    session.setTerminator(participant);\n                } else if (session.getStatus() === 'on') {\n                    session.setInitiator(participant);\n                }\n            }\n\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.RECORDER_STATE_CHANGED,\n                session);\n        });\n\n    this.chatRoomForwarder.forward(XMPPEvents.TRANSCRIPTION_STATUS_CHANGED,\n        JitsiConferenceEvents.TRANSCRIPTION_STATUS_CHANGED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED,\n        JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED);\n\n    this.chatRoomForwarder.forward(\n        XMPPEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,\n        JitsiConferenceEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.PHONE_NUMBER_CHANGED,\n        JitsiConferenceEvents.PHONE_NUMBER_CHANGED);\n\n    chatRoom.setParticipantPropertyListener((node, from) => {\n        const participant = conference.getParticipantById(from);\n\n        if (!participant) {\n            return;\n        }\n\n        participant.setProperty(\n            node.tagName.substring('jitsi_participant_'.length),\n            node.value);\n    });\n\n    chatRoom.addListener(XMPPEvents.KICKED,\n        conference.onMemberKicked.bind(conference));\n    chatRoom.addListener(XMPPEvents.SUSPEND_DETECTED,\n        conference.onSuspendDetected.bind(conference));\n\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_LOCK_CHANGED,\n        JitsiConferenceEvents.LOCK_STATE_CHANGED);\n\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_MEMBERS_ONLY_CHANGED,\n        JitsiConferenceEvents.MEMBERS_ONLY_CHANGED);\n\n    chatRoom.addListener(XMPPEvents.MUC_MEMBER_JOINED,\n        conference.onMemberJoined.bind(conference));\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_JOINED,\n        JitsiConferenceEvents.LOBBY_USER_JOINED);\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_UPDATED,\n        JitsiConferenceEvents.LOBBY_USER_UPDATED);\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_LOBBY_MEMBER_LEFT,\n        JitsiConferenceEvents.LOBBY_USER_LEFT);\n    chatRoom.addListener(XMPPEvents.MUC_MEMBER_BOT_TYPE_CHANGED,\n        conference._onMemberBotTypeChanged.bind(conference));\n    chatRoom.addListener(XMPPEvents.MUC_MEMBER_LEFT,\n        conference.onMemberLeft.bind(conference));\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_LEFT,\n        JitsiConferenceEvents.CONFERENCE_LEFT);\n    this.chatRoomForwarder.forward(XMPPEvents.MUC_DENIED_ACCESS,\n        JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.CONFERENCE_ACCESS_DENIED);\n\n    chatRoom.addListener(XMPPEvents.DISPLAY_NAME_CHANGED,\n        conference.onDisplayNameChanged.bind(conference));\n\n    chatRoom.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, role => {\n        conference.onLocalRoleChanged(role);\n\n        // log all events for the recorder operated by the moderator\n        if (conference.statistics && conference.isModerator()) {\n            conference.on(JitsiConferenceEvents.RECORDER_STATE_CHANGED,\n                recorderSession => {\n                    const logObject = {\n                        error: recorderSession.getError(),\n                        id: 'recorder_status',\n                        status: recorderSession.getStatus()\n                    };\n\n                    Statistics.sendLog(JSON.stringify(logObject));\n                });\n        }\n    });\n\n    chatRoom.addListener(XMPPEvents.MUC_ROLE_CHANGED,\n        conference.onUserRoleChanged.bind(conference));\n\n    chatRoom.addListener(AuthenticationEvents.IDENTITY_UPDATED,\n        (authEnabled, authIdentity) => {\n            conference.authEnabled = authEnabled;\n            conference.authIdentity = authIdentity;\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.AUTH_STATUS_CHANGED, authEnabled,\n                authIdentity);\n        });\n\n    chatRoom.addListener(\n        XMPPEvents.MESSAGE_RECEIVED,\n\n        // eslint-disable-next-line max-params\n        (jid, txt, myJid, ts) => {\n            const id = Strophe.getResourceFromJid(jid);\n\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.MESSAGE_RECEIVED,\n                id, txt, ts);\n        });\n\n    chatRoom.addListener(\n        XMPPEvents.PRIVATE_MESSAGE_RECEIVED,\n\n        // eslint-disable-next-line max-params\n        (jid, txt, myJid, ts) => {\n            const id = Strophe.getResourceFromJid(jid);\n\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.PRIVATE_MESSAGE_RECEIVED,\n                id, txt, ts);\n        });\n\n    chatRoom.addListener(XMPPEvents.PRESENCE_STATUS,\n        (jid, status) => {\n            const id = Strophe.getResourceFromJid(jid);\n            const participant = conference.getParticipantById(id);\n\n            if (!participant || participant._status === status) {\n                return;\n            }\n            participant._status = status;\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.USER_STATUS_CHANGED, id, status);\n        });\n\n    chatRoom.addListener(XMPPEvents.JSON_MESSAGE_RECEIVED,\n        (from, payload) => {\n            const id = Strophe.getResourceFromJid(from);\n            const participant = conference.getParticipantById(id);\n\n            if (participant) {\n                conference.eventEmitter.emit(\n                    JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n                    participant, payload);\n            } else {\n                conference.eventEmitter.emit(\n                    JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,\n                    id, payload);\n            }\n        });\n\n    chatRoom.addPresenceListener('startmuted', (data, from) => {\n        let isModerator = false;\n\n        if (conference.myUserId() === from && conference.isModerator()) {\n            isModerator = true;\n        } else {\n            const participant = conference.getParticipantById(from);\n\n            if (participant && participant.isModerator()) {\n                isModerator = true;\n            }\n        }\n\n        if (!isModerator) {\n            return;\n        }\n\n        const startAudioMuted = data.attributes.audio === 'true';\n        const startVideoMuted = data.attributes.video === 'true';\n\n        let updated = false;\n\n        if (startAudioMuted !== conference.startMutedPolicy.audio) {\n            conference.startMutedPolicy.audio = startAudioMuted;\n            updated = true;\n        }\n\n        if (startVideoMuted !== conference.startMutedPolicy.video) {\n            conference.startMutedPolicy.video = startVideoMuted;\n            updated = true;\n        }\n\n        if (updated) {\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,\n                conference.startMutedPolicy\n            );\n        }\n    });\n\n    if (conference.statistics) {\n        // FIXME ICE related events should end up in RTCEvents eventually\n        chatRoom.addListener(XMPPEvents.CONNECTION_ICE_FAILED,\n            session => {\n                conference.statistics.sendIceConnectionFailedEvent(\n                    session.peerconnection);\n            });\n\n        // FIXME XMPPEvents.ADD_ICE_CANDIDATE_FAILED is never emitted\n        chatRoom.addListener(XMPPEvents.ADD_ICE_CANDIDATE_FAILED,\n            (e, pc) => {\n                conference.statistics.sendAddIceCandidateFailed(e, pc);\n            });\n    }\n\n    // Breakout rooms.\n    this.chatRoomForwarder.forward(XMPPEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM,\n        JitsiConferenceEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM);\n    this.chatRoomForwarder.forward(XMPPEvents.BREAKOUT_ROOMS_UPDATED,\n        JitsiConferenceEvents.BREAKOUT_ROOMS_UPDATED);\n};\n\n/**\n * Setups event listeners related to conference.rtc\n */\nJitsiConferenceEventManager.prototype.setupRTCListeners = function() {\n    const conference = this.conference;\n    const rtc = conference.rtc;\n\n    rtc.addListener(\n        RTCEvents.REMOTE_TRACK_ADDED,\n        conference.onRemoteTrackAdded.bind(conference));\n\n    rtc.addListener(\n        RTCEvents.REMOTE_TRACK_REMOVED,\n        conference.onRemoteTrackRemoved.bind(conference));\n\n    rtc.addListener(RTCEvents.DOMINANT_SPEAKER_CHANGED,\n        (dominant, previous) => {\n            if (conference.lastDominantSpeaker !== dominant && conference.room) {\n                conference.lastDominantSpeaker = dominant;\n                conference.eventEmitter.emit(\n                    JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, dominant, previous);\n\n                if (previous && previous.length) {\n                    const speakerList = previous.slice(0);\n\n                    // Add the dominant speaker to the top of the list (exclude self).\n                    if (conference.myUserId !== dominant) {\n                        speakerList.splice(0, 0, dominant);\n                    }\n\n                    // Trim the list to the top 5 speakers only.\n                    if (speakerList.length > SPEAKERS_AUDIO_LEVELS) {\n                        speakerList.splice(SPEAKERS_AUDIO_LEVELS, speakerList.length - SPEAKERS_AUDIO_LEVELS);\n                    }\n                    conference.statistics && conference.statistics.setSpeakerList(speakerList);\n                }\n                if (conference.statistics && conference.myUserId() === dominant) {\n                    // We are the new dominant speaker.\n                    conference.statistics.sendDominantSpeakerEvent(conference.room.roomjid);\n                }\n            }\n        });\n\n    rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {\n        const now = window.performance.now();\n        const key = 'data.channel.opened';\n\n        // TODO: Move all of the 'connectionTimes' logic to its own module.\n        logger.log(`(TIME) ${key}:\\t`, now);\n        conference.room.connectionTimes[key] = now;\n        Statistics.sendAnalytics(\n            createConnectionStageReachedEvent(key, { value: now }));\n\n        conference.eventEmitter.emit(JitsiConferenceEvents.DATA_CHANNEL_OPENED);\n    });\n\n    rtc.addListener(RTCEvents.ENDPOINT_MESSAGE_RECEIVED,\n        (from, payload) => {\n            const participant = conference.getParticipantById(from);\n\n            if (participant) {\n                conference.eventEmitter.emit(\n                    JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n                    participant, payload);\n            } else {\n                logger.warn(\n                    'Ignored ENDPOINT_MESSAGE_RECEIVED for not existing '\n                        + `participant: ${from}`,\n                    payload);\n            }\n        });\n\n    rtc.addListener(RTCEvents.ENDPOINT_STATS_RECEIVED,\n        (from, payload) => {\n            const participant = conference.getParticipantById(from);\n\n            if (participant) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.ENDPOINT_STATS_RECEIVED, participant, payload);\n            } else {\n                logger.warn(`Ignoring ENDPOINT_STATS_RECEIVED for a non-existant participant: ${from}`);\n            }\n        });\n\n    rtc.addListener(RTCEvents.LOCAL_UFRAG_CHANGED,\n        (tpc, ufrag) => {\n            if (!tpc.isP2P) {\n                Statistics.sendLog(\n                    JSON.stringify({\n                        id: 'local_ufrag',\n                        value: ufrag\n                    }));\n            }\n        });\n    rtc.addListener(RTCEvents.REMOTE_UFRAG_CHANGED,\n        (tpc, ufrag) => {\n            if (!tpc.isP2P) {\n                Statistics.sendLog(\n                    JSON.stringify({\n                        id: 'remote_ufrag',\n                        value: ufrag\n                    }));\n            }\n        });\n\n    rtc.addListener(RTCEvents.CREATE_ANSWER_FAILED,\n        (e, tpc) => {\n            conference.statistics.sendCreateAnswerFailed(e, tpc);\n            if (!tpc.isP2P) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n                    JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n            }\n        });\n\n    rtc.addListener(RTCEvents.CREATE_OFFER_FAILED,\n        (e, tpc) => {\n            conference.statistics.sendCreateOfferFailed(e, tpc);\n            if (!tpc.isP2P) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n                    JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n            }\n        });\n\n    rtc.addListener(RTCEvents.SET_LOCAL_DESCRIPTION_FAILED,\n        (e, tpc) => {\n            conference.statistics.sendSetLocalDescFailed(e, tpc);\n            if (!tpc.isP2P) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n                    JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n            }\n        });\n\n    rtc.addListener(RTCEvents.SET_REMOTE_DESCRIPTION_FAILED,\n        (e, tpc) => {\n            conference.statistics.sendSetRemoteDescFailed(e, tpc);\n            if (!tpc.isP2P) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n                    JitsiConferenceErrors.OFFER_ANSWER_FAILED, e);\n            }\n        });\n\n    rtc.addListener(RTCEvents.LOCAL_TRACK_SSRC_UPDATED,\n        (track, ssrc) => {\n            // when starting screen sharing, the track is created and when\n            // we do set local description and we process the ssrc we\n            // will be notified for it and we will report it with the event\n            // for screen sharing\n            if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP) {\n                conference.statistics.sendScreenSharingEvent(true, ssrc);\n            }\n        });\n};\n\n/**\n * Removes event listeners related to conference.xmpp\n */\nJitsiConferenceEventManager.prototype.removeXMPPListeners = function() {\n    const conference = this.conference;\n\n    Object.keys(this.xmppListeners).forEach(eventName => {\n        conference.xmpp.removeListener(\n            eventName,\n            this.xmppListeners[eventName]);\n    });\n    this.xmppListeners = {};\n};\n\n\n/**\n * Setups event listeners related to conference.xmpp\n */\nJitsiConferenceEventManager.prototype.setupXMPPListeners = function() {\n    const conference = this.conference;\n\n    this._addConferenceXMPPListener(\n        XMPPEvents.CALL_INCOMING,\n        conference.onIncomingCall.bind(conference));\n    this._addConferenceXMPPListener(\n        XMPPEvents.CALL_ACCEPTED,\n        conference.onCallAccepted.bind(conference));\n    this._addConferenceXMPPListener(\n        XMPPEvents.TRANSPORT_INFO,\n        conference.onTransportInfo.bind(conference));\n    this._addConferenceXMPPListener(\n        XMPPEvents.CALL_ENDED,\n        conference.onCallEnded.bind(conference));\n\n    this._addConferenceXMPPListener(XMPPEvents.START_MUTED_FROM_FOCUS,\n        (audioMuted, videoMuted) => {\n            if (conference.options.config.ignoreStartMuted) {\n                return;\n            }\n\n            conference.startAudioMuted = audioMuted;\n            conference.startVideoMuted = videoMuted;\n\n            // mute existing local tracks because this is initial mute from\n            // Jicofo\n            conference.getLocalTracks().forEach(track => {\n                switch (track.getType()) {\n                case MediaType.AUDIO:\n                    conference.startAudioMuted && track.mute();\n                    break;\n                case MediaType.VIDEO:\n                    conference.startVideoMuted && track.mute();\n                    break;\n                }\n            });\n\n            conference.eventEmitter.emit(JitsiConferenceEvents.STARTED_MUTED);\n        });\n\n    this._addConferenceXMPPListener(XMPPEvents.CONFERENCE_TIMESTAMP_RECEIVED,\n        createdTimestamp => {\n            conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_CREATED_TIMESTAMP, createdTimestamp);\n        });\n\n    this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_CHANGED,\n        (value, mediaType, actorJid) => {\n            const actorParticipant = conference.getParticipants().find(p => p.getJid() === actorJid);\n\n            conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_CHANGED, {\n                enabled: value,\n                mediaType,\n                actor: actorParticipant\n            });\n        });\n    this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_PARTICIPANT_APPROVED,\n        (mediaType, jid) => {\n            const participant = conference.getParticipantById(Strophe.getResourceFromJid(jid));\n\n            if (participant) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED, {\n                    participant,\n                    mediaType\n                });\n            }\n        });\n    this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_PARTICIPANT_REJECTED,\n        (mediaType, jid) => {\n            const participant = conference.getParticipantById(Strophe.getResourceFromJid(jid));\n\n            if (participant) {\n                conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_REJECTED, {\n                    participant,\n                    mediaType\n                });\n            }\n        });\n    this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_APPROVED,\n        value => conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_APPROVED, { mediaType: value }));\n    this._addConferenceXMPPListener(XMPPEvents.AV_MODERATION_REJECTED,\n        value => {\n            conference.eventEmitter.emit(JitsiConferenceEvents.AV_MODERATION_REJECTED, { mediaType: value });\n        });\n};\n\n/**\n * Add XMPP listener and save its reference for remove on leave conference.\n */\nJitsiConferenceEventManager.prototype._addConferenceXMPPListener = function(\n        eventName, listener) {\n    this.xmppListeners[eventName] = listener;\n    this.conference.xmpp.addListener(eventName, listener);\n};\n\n/**\n * Setups event listeners related to conference.statistics\n */\nJitsiConferenceEventManager.prototype.setupStatisticsListeners = function() {\n    const conference = this.conference;\n\n    if (!conference.statistics) {\n        return;\n    }\n\n    /* eslint-disable max-params */\n    conference.statistics.addAudioLevelListener((tpc, ssrc, level, isLocal) => {\n        conference.rtc.setAudioLevel(tpc, ssrc, level, isLocal);\n    });\n\n    /* eslint-enable max-params */\n\n    // Forward the \"before stats disposed\" event\n    conference.statistics.addBeforeDisposedListener(() => {\n        conference.eventEmitter.emit(\n            JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED);\n    });\n\n    // if we are in startSilent mode we will not be sending/receiving so nothing to detect\n    if (!conference.options.config.startSilent) {\n        conference.statistics.addByteSentStatsListener((tpc, stats) => {\n            conference.getLocalTracks(MediaType.AUDIO).forEach(track => {\n                const ssrc = tpc.getLocalSSRC(track);\n\n                if (!ssrc || !stats.hasOwnProperty(ssrc)) {\n                    return;\n                }\n\n                track.onByteSentStatsReceived(tpc, stats[ssrc]);\n            });\n        });\n    }\n};\n","/**\n * The media track was removed to the conference.\n */\nexport const LOCAL_TRACK_STOPPED = 'track.stopped';\n\n/**\n * Audio levels of a this track was changed.\n * The first argument is a number with audio level value in range [0, 1].\n * The second argument is a <tt>TraceablePeerConnection</tt> which is the peer\n * connection which measured the audio level (one audio track can be added\n * to multiple peer connection at the same time). This argument is optional for\n * local tracks for which we can measure audio level without the peer\n * connection (the value will be <tt>undefined</tt>).\n *\n * NOTE The second argument should be treated as library internal and can be\n * removed at any time.\n */\nexport const TRACK_AUDIO_LEVEL_CHANGED = 'track.audioLevelsChanged';\n\n/**\n * The audio output of the track was changed.\n */\nexport const TRACK_AUDIO_OUTPUT_CHANGED = 'track.audioOutputChanged';\n\n/**\n * A media track mute status was changed.\n */\nexport const TRACK_MUTE_CHANGED = 'track.trackMuteChanged';\n\n/**\n * The video type(\"camera\" or \"desktop\") of the track was changed.\n */\nexport const TRACK_VIDEOTYPE_CHANGED = 'track.videoTypeChanged';\n\n/**\n * Indicates that the track is not receiving any data even though we expect it\n * to receive data (i.e. the stream is not stopped).\n */\nexport const NO_DATA_FROM_SOURCE = 'track.no_data_from_source';\n\n/**\n * Indicates that the local audio track is not receiving any audio input from\n * the microphone that is currently selected.\n */\nexport const NO_AUDIO_INPUT = 'track.no_audio_input';\n\n/**\n * Event fired whenever video track's streaming changes.\n * First argument is the sourceName of the track and the second is a string indicating if the connection is currently\n * - active - the connection is active.\n * - inactive - the connection is inactive, was intentionally interrupted by the bridge because of low BWE or because\n *   of the endpoint falling out of last N.\n * - interrupted - a network problem occurred.\n * - restoring - the connection was inactive and is restoring now.\n *\n * The current status value can be obtained by calling JitsiRemoteTrack.getTrackStreamingStatus().\n */\nexport const TRACK_STREAMING_STATUS_CHANGED = 'track.streaming_status_changed';\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createParticipantConnectionStatusEvent } from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * Default value of 500 milliseconds for {@link ParticipantConnectionStatus.outOfLastNTimeout}.\n *\n * @type {number}\n */\nconst DEFAULT_NOT_IN_LAST_N_TIMEOUT = 500;\n\n/**\n * Default value of 2500 milliseconds for {@link ParticipantConnectionStatus.p2pRtcMuteTimeout}.\n */\nconst DEFAULT_P2P_RTC_MUTE_TIMEOUT = 2500;\n\n/**\n * Default value of 10000 milliseconds for {@link ParticipantConnectionStatus.rtcMuteTimeout}.\n *\n * @type {number}\n */\nconst DEFAULT_RTC_MUTE_TIMEOUT = 10000;\n\n/**\n * The time to wait a track to be restored. Track which was out of lastN\n * should be inactive and when entering lastN it becomes restoring and when\n * data is received from bridge it will become active, but if no data is\n * received for some time we set status of that participant connection to\n * interrupted.\n * @type {number}\n */\nconst DEFAULT_RESTORING_TIMEOUT = 10000;\n\n/**\n * Participant connection statuses.\n *\n * @type {{\n *      ACTIVE: string,\n *      INACTIVE: string,\n *      INTERRUPTED: string,\n *      RESTORING: string\n * }}\n */\nexport const ParticipantConnectionStatus = {\n    /**\n     * Status indicating that connection is currently active.\n     */\n    ACTIVE: 'active',\n\n    /**\n     * Status indicating that connection is currently inactive.\n     * Inactive means the connection was stopped on purpose from the bridge,\n     * like exiting lastN or adaptivity decided to drop video because of not\n     * enough bandwidth.\n     */\n    INACTIVE: 'inactive',\n\n    /**\n     * Status indicating that connection is currently interrupted.\n     */\n    INTERRUPTED: 'interrupted',\n\n    /**\n     * Status indicating that connection is currently restoring.\n     */\n    RESTORING: 'restoring'\n};\n\n/**\n * Class is responsible for emitting\n * JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED events.\n */\nexport default class ParticipantConnectionStatusHandler {\n    /* eslint-disable max-params*/\n    /**\n     * Calculates the new {@link ParticipantConnectionStatus} based on\n     * the values given for some specific remote user. It is assumed that\n     * the conference is currently in the JVB mode (in contrary to the P2P mode)\n     * @param {boolean} isConnectionActiveByJvb true if the JVB did not get any\n     * data from the user for the last 15 seconds.\n     * @param {boolean} isInLastN indicates whether the user is in the last N\n     * set. When set to false it means that JVB is not sending any video for\n     * the user.\n     * @param {boolean} isRestoringTimedout if true it means that the user has\n     * been outside of last N too long to be considered\n     * {@link ParticipantConnectionStatus.RESTORING}.\n     * @param {boolean} isVideoMuted true if the user is video muted and we\n     * should not expect to receive any video.\n     * @param {boolean} isVideoTrackFrozen if the current browser support video\n     * frozen detection then it will be set to true when the video track is\n     * frozen. If the current browser does not support frozen detection the it's\n     * always false.\n     * @return {ParticipantConnectionStatus} the new connection status for\n     * the user for whom the values above were provided.\n     * @private\n     */\n    static _getNewStateForJvbMode(\n            isConnectionActiveByJvb,\n            isInLastN,\n            isRestoringTimedout,\n            isVideoMuted,\n            isVideoTrackFrozen) {\n        if (!isConnectionActiveByJvb) {\n            // when there is a connection problem signaled from jvb\n            // it means no media was flowing for at least 15secs, so both audio\n            // and video are most likely interrupted\n            return ParticipantConnectionStatus.INTERRUPTED;\n        } else if (isVideoMuted) {\n            // If the connection is active according to JVB and the user is\n            // video muted there is no way for the connection to be inactive,\n            // because the detection logic below only makes sense for video.\n            return ParticipantConnectionStatus.ACTIVE;\n        }\n\n        // Logic when isVideoTrackFrozen is supported\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n            if (!isVideoTrackFrozen) {\n                // If the video is playing we're good\n                return ParticipantConnectionStatus.ACTIVE;\n            } else if (isInLastN) {\n                return isRestoringTimedout\n                    ? ParticipantConnectionStatus.INTERRUPTED\n                    : ParticipantConnectionStatus.RESTORING;\n            }\n\n            return ParticipantConnectionStatus.INACTIVE;\n        }\n\n        // Because this browser is incapable of detecting frozen video we must\n        // rely on the lastN value\n        return isInLastN\n            ? ParticipantConnectionStatus.ACTIVE\n            : ParticipantConnectionStatus.INACTIVE;\n    }\n\n    /* eslint-enable max-params*/\n\n    /**\n     * In P2P mode we don't care about any values coming from the JVB and\n     * the connection status can be only active or interrupted.\n     * @param {boolean} isVideoMuted the user if video muted\n     * @param {boolean} isVideoTrackFrozen true if the video track for\n     * the remote user is currently frozen. If the current browser does not\n     * support video frozen detection then it's always false.\n     * @return {ParticipantConnectionStatus}\n     * @private\n     */\n    static _getNewStateForP2PMode(isVideoMuted, isVideoTrackFrozen) {\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            // There's no way to detect problems in P2P when there's no video\n            // track frozen detection...\n            return ParticipantConnectionStatus.ACTIVE;\n        }\n\n        return isVideoMuted || !isVideoTrackFrozen\n            ? ParticipantConnectionStatus.ACTIVE\n            : ParticipantConnectionStatus.INTERRUPTED;\n    }\n\n    /**\n     * Creates new instance of <tt>ParticipantConnectionStatus</tt>.\n     *\n     * @constructor\n     * @param {RTC} rtc the RTC service instance\n     * @param {JitsiConference} conference parent conference instance\n     * @param {Object} options\n     * @param {number} [options.p2pRtcMuteTimeout=2500] custom value for\n     * {@link ParticipantConnectionStatus.p2pRtcMuteTimeout}.\n     * @param {number} [options.rtcMuteTimeout=2000] custom value for\n     * {@link ParticipantConnectionStatus.rtcMuteTimeout}.\n     * @param {number} [options.outOfLastNTimeout=500] custom value for\n     * {@link ParticipantConnectionStatus.outOfLastNTimeout}.\n     */\n    constructor(rtc, conference, options) {\n        this.rtc = rtc;\n        this.conference = conference;\n\n        /**\n         * A map of the \"endpoint ID\"(which corresponds to the resource part\n         * of MUC JID(nickname)) to the timeout callback IDs scheduled using\n         * window.setTimeout.\n         * @type {Object.<string, number>}\n         */\n        this.trackTimers = {};\n\n        /**\n         * This map holds the endpoint connection status received from the JVB\n         * (as it might be different than the one stored in JitsiParticipant).\n         * Required for getting back in sync when remote video track is removed.\n         * @type {Object.<string, boolean>}\n         */\n        this.connStatusFromJvb = { };\n\n        /**\n         * If video track frozen detection through RTC mute event is supported,\n         * we wait some time until video track is considered frozen. But because\n         * when the user falls out of last N it is expected for the video to\n         * freeze this timeout must be significantly reduced in \"out of last N\"\n         * case.\n         *\n         * Basically this value is used instead of {@link rtcMuteTimeout} when\n         * user is not in last N.\n         * @type {number}\n         */\n        this.outOfLastNTimeout\n            = typeof options.outOfLastNTimeout === 'number'\n                ? options.outOfLastNTimeout : DEFAULT_NOT_IN_LAST_N_TIMEOUT;\n\n        /**\n         * How long we are going to wait for the corresponding signaling mute event after the RTC video track muted\n         * event is fired on the Media stream, before the connection interrupted is fired. The default value is\n         * {@link DEFAULT_P2P_RTC_MUTE_TIMEOUT}.\n         *\n         * @type {number} amount of time in milliseconds.\n         */\n        this.p2pRtcMuteTimeout = typeof options.p2pRtcMuteTimeout === 'number'\n            ? options.p2pRtcMuteTimeout : DEFAULT_P2P_RTC_MUTE_TIMEOUT;\n\n        /**\n         * How long we're going to wait after the RTC video track muted event\n         * for the corresponding signalling mute event, before the connection\n         * interrupted is fired. The default value is\n         * {@link DEFAULT_RTC_MUTE_TIMEOUT}.\n         *\n         * @type {number} amount of time in milliseconds\n         */\n        this.rtcMuteTimeout\n            = typeof options.rtcMuteTimeout === 'number'\n                ? options.rtcMuteTimeout : DEFAULT_RTC_MUTE_TIMEOUT;\n\n        /**\n         * This map holds a timestamp indicating  when participant's video track\n         * was RTC muted (it is assumed that each participant can have only 1\n         * video track at a time). The purpose of storing the timestamp is to\n         * avoid the transition to disconnected status in case of legitimate\n         * video mute operation where the signalling video muted event can\n         * arrive shortly after RTC muted event.\n         *\n         * The key is participant's ID which is the same as endpoint id in\n         * the Colibri conference allocated on the JVB.\n         *\n         * The value is a timestamp measured in milliseconds obtained with\n         * <tt>Date.now()</tt>.\n         *\n         * FIXME merge this logic with NO_DATA_FROM_SOURCE event\n         *       implemented in JitsiLocalTrack by extending the event to\n         *       the remote track and allowing to set different timeout for\n         *       local and remote tracks.\n         *\n         * @type {Object.<string, number>}\n         */\n        this.rtcMutedTimestamp = { };\n        logger.info(`RtcMuteTimeout set to: ${this.rtcMuteTimeout}`);\n\n        /**\n         * This map holds the timestamps indicating when participant's video\n         * entered lastN set. Participants entering lastN will have connection\n         * status restoring and when we start receiving video will become\n         * active, but if video is not received for certain time\n         * {@link DEFAULT_RESTORING_TIMEOUT} that participant connection status\n         * will become interrupted.\n         *\n         * @type {Map<string, number>}\n         */\n        this.enteredLastNTimestamp = new Map();\n\n        /**\n         * A map of the \"endpoint ID\"(which corresponds to the resource part\n         * of MUC JID(nickname)) to the restoring timeout callback IDs\n         * scheduled using window.setTimeout.\n         *\n         * @type {Map<string, number>}\n         */\n        this.restoringTimers = new Map();\n\n        /**\n         * A map that holds the current connection status (along with all the internal events that happen\n         * while in that state).\n         *\n         * The goal is to send this information to the analytics backend for post-mortem analysis.\n         */\n        this.connectionStatusMap = new Map();\n    }\n\n    /**\n     * Gets the video frozen timeout for given user.\n     * @param {string} id endpoint/participant ID\n     * @return {number} how long are we going to wait since RTC video muted\n     * even, before a video track is considered frozen.\n     * @private\n     */\n    _getVideoFrozenTimeout(id) {\n        return this.rtc.isInLastN(id)\n            ? this.rtcMuteTimeout\n            : this.conference.isP2PActive() ? this.p2pRtcMuteTimeout : this.outOfLastNTimeout;\n    }\n\n    /**\n     * Initializes <tt>ParticipantConnectionStatus</tt> and bind required event\n     * listeners.\n     */\n    init() {\n\n        this._onEndpointConnStatusChanged\n            = this.onEndpointConnStatusChanged.bind(this);\n\n        this.rtc.addListener(\n            RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,\n            this._onEndpointConnStatusChanged);\n\n        // Handles P2P status changes\n        this._onP2PStatus = this.refreshConnectionStatusForAll.bind(this);\n        this.conference.on(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n        // Used to send analytics events for the participant that left the call.\n        this._onUserLeft = this.onUserLeft.bind(this);\n        this.conference.on(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n        // On some browsers MediaStreamTrack trigger \"onmute\"/\"onunmute\"\n        // events for video type tracks when they stop receiving data which is\n        // often a sign that remote user is having connectivity issues\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n\n            this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);\n            this.rtc.addListener(\n                RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n\n            this._onTrackRtcUnmuted = this.onTrackRtcUnmuted.bind(this);\n            this.rtc.addListener(\n                RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n            // Track added/removed listeners are used to bind \"mute\"/\"unmute\"\n            // event handlers\n            this._onRemoteTrackAdded = this.onRemoteTrackAdded.bind(this);\n            this.conference.on(\n                JitsiConferenceEvents.TRACK_ADDED,\n                this._onRemoteTrackAdded);\n\n            this._onRemoteTrackRemoved = this.onRemoteTrackRemoved.bind(this);\n            this.conference.on(\n                JitsiConferenceEvents.TRACK_REMOVED,\n                this._onRemoteTrackRemoved);\n\n            // Listened which will be bound to JitsiRemoteTrack to listen for\n            // signalling mute/unmute events.\n            this._onSignallingMuteChanged\n                = this.onSignallingMuteChanged.bind(this);\n\n            // Used to send an analytics event when the video type changes.\n            this._onTrackVideoTypeChanged\n                = this.onTrackVideoTypeChanged.bind(this);\n        }\n\n        this._onLastNChanged = this._onLastNChanged.bind(this);\n        this.conference.on(\n            JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n            this._onLastNChanged);\n\n        this._onLastNValueChanged\n            = this.refreshConnectionStatusForAll.bind(this);\n        this.rtc.on(\n            RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n    }\n\n    /**\n     * Removes all event listeners and disposes of all resources held by this\n     * instance.\n     */\n    dispose() {\n\n        this.rtc.removeListener(\n            RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,\n            this._onEndpointConnStatusChanged);\n\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n            this.rtc.removeListener(\n                RTCEvents.REMOTE_TRACK_MUTE,\n                this._onTrackRtcMuted);\n            this.rtc.removeListener(\n                RTCEvents.REMOTE_TRACK_UNMUTE,\n                this._onTrackRtcUnmuted);\n\n            this.conference.off(\n                JitsiConferenceEvents.TRACK_ADDED,\n                this._onRemoteTrackAdded);\n            this.conference.off(\n                JitsiConferenceEvents.TRACK_REMOVED,\n                this._onRemoteTrackRemoved);\n        }\n\n        this.conference.off(\n            JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n            this._onLastNChanged);\n\n        this.rtc.removeListener(\n            RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n\n        this.conference.off(\n            JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n        this.conference.off(\n            JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n        const participantIds = Object.keys(this.trackTimers);\n\n        for (const participantId of participantIds) {\n            this.clearTimeout(participantId);\n            this.clearRtcMutedTimestamp(participantId);\n        }\n\n        for (const id in this.connectionStatusMap) {\n            if (this.connectionStatusMap.hasOwnProperty(id)) {\n                this.onUserLeft(id);\n            }\n        }\n\n        // Clear RTC connection status cache\n        this.connStatusFromJvb = {};\n    }\n\n    /**\n     * Handles RTCEvents.ENDPOINT_CONN_STATUS_CHANGED triggered when we receive\n     * notification over the data channel from the bridge about endpoint's\n     * connection status update.\n     * @param {string} endpointId - The endpoint ID(MUC nickname/resource JID).\n     * @param {boolean} isActive - true if the connection is OK or false otherwise.\n     */\n    onEndpointConnStatusChanged(endpointId, isActive) {\n\n        logger.debug(\n            `Detector RTCEvents.ENDPOINT_CONN_STATUS_CHANGED(${Date.now()}): ${\n                endpointId}: ${isActive}`);\n\n        // Filter out events for the local JID for now\n        if (endpointId !== this.conference.myUserId()) {\n            // Store the status received over the data channels\n            this.connStatusFromJvb[endpointId] = isActive;\n            this.figureOutConnectionStatus(endpointId);\n        }\n    }\n\n    /**\n     * Changes connection status.\n     * @param {JitsiParticipant} participant\n     * @param newStatus\n     */\n    _changeConnectionStatus(participant, newStatus) {\n        if (participant.getConnectionStatus() !== newStatus) {\n\n            const endpointId = participant.getId();\n\n            participant._setConnectionStatus(newStatus);\n\n            logger.debug(\n                `Emit endpoint conn status(${Date.now()}) ${endpointId}: ${\n                    newStatus}`);\n\n            // Log the event on CallStats\n            Statistics.sendLog(\n                JSON.stringify({\n                    id: 'peer.conn.status',\n                    participant: endpointId,\n                    status: newStatus\n                }));\n\n\n            this.conference.eventEmitter.emit(\n                JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,\n                endpointId, newStatus);\n        }\n    }\n\n    /**\n     * Reset the postponed \"connection interrupted\" event which was previously\n     * scheduled as a timeout on RTC 'onmute' event.\n     *\n     * @param {string} participantId - The participant for which the \"connection\n     * interrupted\" timeout was scheduled.\n     */\n    clearTimeout(participantId) {\n        if (this.trackTimers[participantId]) {\n            window.clearTimeout(this.trackTimers[participantId]);\n            this.trackTimers[participantId] = null;\n        }\n    }\n\n    /**\n     * Clears the timestamp of the RTC muted event for participant's video track\n     * @param {string} participantId the id of the conference participant which\n     * is the same as the Colibri endpoint ID of the video channel allocated for\n     * the user on the videobridge.\n     */\n    clearRtcMutedTimestamp(participantId) {\n        this.rtcMutedTimestamp[participantId] = null;\n    }\n\n    /**\n     * Bind signalling mute event listeners for video {JitsiRemoteTrack} when\n     * a new one is added to the conference.\n     *\n     * @param {JitsiTrack} remoteTrack - The {JitsiTrack} which is being added to\n     * the conference.\n     */\n    onRemoteTrackAdded(remoteTrack) {\n        if (!remoteTrack.isLocal()\n                && remoteTrack.getType() === MediaType.VIDEO) {\n\n            logger.debug(\n                `Detector on remote track added for: ${\n                    remoteTrack.getParticipantId()}`);\n\n            remoteTrack.on(\n                JitsiTrackEvents.TRACK_MUTE_CHANGED,\n                this._onSignallingMuteChanged);\n            remoteTrack.on(\n                JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,\n                videoType => this._onTrackVideoTypeChanged(remoteTrack, videoType));\n        }\n    }\n\n    /**\n     * Removes all event listeners bound to the remote video track and clears\n     * any related timeouts.\n     *\n     * @param {JitsiRemoteTrack} remoteTrack - The remote track which is being\n     * removed from the conference.\n     */\n    onRemoteTrackRemoved(remoteTrack) {\n        if (!remoteTrack.isLocal()\n                && remoteTrack.getType() === MediaType.VIDEO) {\n\n            const endpointId = remoteTrack.getParticipantId();\n\n            logger.debug(`Detector on remote track removed: ${endpointId}`);\n\n            remoteTrack.off(\n                JitsiTrackEvents.TRACK_MUTE_CHANGED,\n                this._onSignallingMuteChanged);\n\n            this.clearTimeout(endpointId);\n            this.clearRtcMutedTimestamp(endpointId);\n\n            this.figureOutConnectionStatus(endpointId);\n        }\n    }\n\n    /**\n     * Checks if given participant's video is considered frozen.\n     * @param {JitsiParticipant} participant - The participant.\n     * @return {boolean} <tt>true</tt> if the video has frozen for given\n     * participant or <tt>false</tt> when it's either not considered frozen\n     * (yet) or if freeze detection is not supported by the current browser.\n     *\n     * FIXME merge this logic with NO_DATA_FROM_SOURCE event\n     *       implemented in JitsiLocalTrack by extending the event to\n     *       the remote track and allowing to set different timeout for\n     *       local and remote tracks.\n     *\n     */\n    isVideoTrackFrozen(participant) {\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            return false;\n        }\n\n        const id = participant.getId();\n        const hasAnyVideoRTCMuted = participant.hasAnyVideoTrackWebRTCMuted();\n        const rtcMutedTimestamp = this.rtcMutedTimestamp[id];\n        const timeout = this._getVideoFrozenTimeout(id);\n\n        return hasAnyVideoRTCMuted\n            && typeof rtcMutedTimestamp === 'number'\n            && (Date.now() - rtcMutedTimestamp) >= timeout;\n    }\n\n    /**\n     * Goes over every participant and updates connectivity status.\n     * Should be called when a parameter which affects all of the participants\n     * is changed (P2P for example).\n     */\n    refreshConnectionStatusForAll() {\n        const participants = this.conference.getParticipants();\n\n        for (const participant of participants) {\n            this.figureOutConnectionStatus(participant.getId());\n        }\n    }\n\n    /**\n     * Figures out (and updates) the current connectivity status for\n     * the participant identified by the given id.\n     *\n     * @param {string} id - The participant's id (MUC nickname or Colibri endpoint ID).\n     */\n    figureOutConnectionStatus(id) {\n        const participant = this.conference.getParticipantById(id);\n\n        if (!participant) {\n            // Probably the participant is no longer in the conference\n            // (at the time of writing this code, participant is\n            // detached from the conference and TRACK_REMOVED events are\n            // fired),\n            // so we don't care, but let's print a log message for debugging purposes.\n            logger.debug(`figure out conn status - no participant for: ${id}`);\n\n            return;\n        }\n\n        const inP2PMode = this.conference.isP2PActive();\n        const isRestoringTimedOut = this._isRestoringTimedout(id);\n        const audioOnlyMode = this.conference.getLastN() === 0;\n\n        // NOTE Overriding videoMuted to true for audioOnlyMode should disable\n        // any detection based on video playback or the last N.\n        const isVideoMuted = participant.isVideoMuted() || audioOnlyMode;\n        const isVideoTrackFrozen = this.isVideoTrackFrozen(participant);\n        const isInLastN = this.rtc.isInLastN(id);\n        let isConnActiveByJvb = this.connStatusFromJvb[id];\n\n        if (typeof isConnActiveByJvb !== 'boolean') {\n            // If no status was received from the JVB it means that it's active\n            // (the bridge does not send notification unless there is a problem)\n            isConnActiveByJvb = true;\n        }\n\n        const newState\n            = inP2PMode\n                ? ParticipantConnectionStatusHandler._getNewStateForP2PMode(\n                    isVideoMuted,\n                    isVideoTrackFrozen)\n                : ParticipantConnectionStatusHandler._getNewStateForJvbMode(\n                    isConnActiveByJvb,\n                    isInLastN,\n                    isRestoringTimedOut,\n                    isVideoMuted,\n                    isVideoTrackFrozen);\n\n        // if the new state is not restoring clear timers and timestamps\n        // that we use to track the restoring state\n        if (newState !== ParticipantConnectionStatus.RESTORING) {\n            this._clearRestoringTimer(id);\n        }\n\n        logger.debug(\n            `Figure out conn status for ${id}, is video muted: ${\n                isVideoMuted} is active(jvb): ${\n                isConnActiveByJvb} video track frozen: ${\n                isVideoTrackFrozen} p2p mode: ${\n                inP2PMode} is in last N: ${\n                isInLastN} currentStatus => newStatus: ${\n                participant.getConnectionStatus()} => ${newState}`);\n\n        const oldConnectionStatus = this.connectionStatusMap[id] || {};\n\n        // Send an analytics event (guard on either the p2p flag or the connection status has changed\n        // since the last time this code block run).\n        if (!('p2p' in oldConnectionStatus)\n            || !('connectionStatus' in oldConnectionStatus)\n            || oldConnectionStatus.p2p !== inP2PMode\n            || oldConnectionStatus.connectionStatus !== newState) {\n\n            const nowMs = Date.now();\n\n            this.maybeSendParticipantConnectionStatusEvent(id, nowMs);\n\n            this.connectionStatusMap[id] = {\n                ...oldConnectionStatus,\n                connectionStatus: newState,\n                p2p: inP2PMode,\n                startedMs: nowMs\n            };\n\n            // sometimes (always?) we're late to hook the TRACK_VIDEOTYPE_CHANGED event and the\n            // video type is not in oldConnectionStatus.\n            if (!('videoType' in this.connectionStatusMap[id])) {\n                const videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n\n                if (Array.isArray(videoTracks) && videoTracks.length !== 0) {\n                    this.connectionStatusMap[id].videoType = videoTracks[0].videoType;\n                }\n            }\n        }\n        this._changeConnectionStatus(participant, newState);\n    }\n\n    /**\n     * Computes the duration of the current connection status for the participant with the specified id (i.e. 15 seconds\n     * in the INTERRUPTED state) and sends a participant connection status event.\n     * @param {string} id - The jid of the participant.\n     * @param {Number} nowMs - The current time (in millis).\n     * @returns {void}\n     */\n    maybeSendParticipantConnectionStatusEvent(id, nowMs) {\n        const participantConnectionStatus = this.connectionStatusMap[id];\n\n        if (participantConnectionStatus\n            && 'startedMs' in participantConnectionStatus\n            && 'videoType' in participantConnectionStatus\n            && 'connectionStatus' in participantConnectionStatus\n            && 'p2p' in participantConnectionStatus) {\n            participantConnectionStatus.value = nowMs - participantConnectionStatus.startedMs;\n            Statistics.sendAnalytics(\n                createParticipantConnectionStatusEvent(participantConnectionStatus));\n        }\n    }\n\n    /**\n     * On change in Last N set check all leaving and entering participants to\n     * change their corresponding statuses.\n     *\n     * @param {Array<string>} leavingLastN - The array of ids leaving lastN.\n     * @param {Array<string>} enteringLastN - The array of ids entering lastN.\n     * @private\n     */\n    _onLastNChanged(leavingLastN = [], enteringLastN = []) {\n        const now = Date.now();\n\n        logger.debug(`LastN endpoints changed leaving=${leavingLastN}, entering=${enteringLastN} at ${now}`);\n\n        // If the browser doesn't fire the mute/onmute events when the remote peer stops/starts sending media,\n        // calculate the connection status for all the endpoints since it won't get triggered automatically on\n        // the endpoint that has started/stopped receiving media.\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            this.refreshConnectionStatusForAll();\n        }\n\n        for (const id of leavingLastN) {\n            this.enteredLastNTimestamp.delete(id);\n            this._clearRestoringTimer(id);\n            browser.supportsVideoMuteOnConnInterrupted() && this.figureOutConnectionStatus(id);\n        }\n        for (const id of enteringLastN) {\n            // store the timestamp this id is entering lastN\n            this.enteredLastNTimestamp.set(id, now);\n            browser.supportsVideoMuteOnConnInterrupted() && this.figureOutConnectionStatus(id);\n        }\n    }\n\n    /**\n     * Clears the restoring timer for participant's video track and the\n     * timestamp for entering lastN.\n     *\n     * @param {string} participantId - The id of the conference participant which\n     * is the same as the Colibri endpoint ID of the video channel allocated for\n     * the user on the videobridge.\n     */\n    _clearRestoringTimer(participantId) {\n        const rTimer = this.restoringTimers.get(participantId);\n\n        if (rTimer) {\n            clearTimeout(rTimer);\n            this.restoringTimers.delete(participantId);\n        }\n    }\n\n    /**\n     * Checks whether a track had stayed enough in restoring state, compares\n     * current time and the time the track entered in lastN. If it hasn't\n     * timedout and there is no timer added, add new timer in order to give it\n     * more time to become active or mark it as interrupted on next check.\n     *\n     * @param {string} participantId - The id of the conference participant which\n     * is the same as the Colibri endpoint ID of the video channel allocated for\n     * the user on the videobridge.\n     * @returns {boolean} <tt>true</tt> if the track was in restoring state\n     * more than the timeout ({@link DEFAULT_RESTORING_TIMEOUT}.) in order to\n     * set its status to interrupted.\n     * @private\n     */\n    _isRestoringTimedout(participantId) {\n        const enteredLastNTimestamp\n            = this.enteredLastNTimestamp.get(participantId);\n\n        if (enteredLastNTimestamp\n            && (Date.now() - enteredLastNTimestamp)\n                >= DEFAULT_RESTORING_TIMEOUT) {\n            return true;\n        }\n\n        // still haven't reached timeout, if there is no timer scheduled,\n        // schedule one so we can track the restoring state and change it after\n        // reaching the timeout\n        const rTimer = this.restoringTimers.get(participantId);\n\n        if (!rTimer) {\n            this.restoringTimers.set(participantId, setTimeout(\n                () => this.figureOutConnectionStatus(participantId),\n                DEFAULT_RESTORING_TIMEOUT));\n        }\n\n        return false;\n    }\n\n    /**\n     * Sends a last/final participant connection status event for the participant that left the conference.\n     * @param {string} id - The id of the participant that left the conference.\n     * @returns {void}\n     */\n    onUserLeft(id) {\n        this.maybeSendParticipantConnectionStatusEvent(id, Date.now());\n        delete this.connectionStatusMap[id];\n    }\n\n    /**\n     * Handles RTC 'onmute' event for the video track.\n     *\n     * @param {JitsiRemoteTrack} track - The video track for which 'onmute' event\n     * will be processed.\n     */\n    onTrackRtcMuted(track) {\n        const participantId = track.getParticipantId();\n        const participant = this.conference.getParticipantById(participantId);\n\n        logger.debug(`Detector track RTC muted: ${participantId}`, Date.now());\n        if (!participant) {\n            logger.error(`No participant for id: ${participantId}`);\n\n            return;\n        }\n        this.rtcMutedTimestamp[participantId] = Date.now();\n        if (!participant.isVideoMuted()) {\n            // If the user is not muted according to the signalling we'll give\n            // it some time, before the connection interrupted event is\n            // triggered.\n            this.clearTimeout(participantId);\n\n            // The timeout is reduced when user is not in the last N\n            const timeout = this._getVideoFrozenTimeout(participantId);\n\n            this.trackTimers[participantId] = window.setTimeout(() => {\n                logger.debug(\n                    `Set RTC mute timeout for: ${participantId}\\\n                     of ${timeout} ms`);\n                this.clearTimeout(participantId);\n                this.figureOutConnectionStatus(participantId);\n            }, timeout);\n        }\n    }\n\n    /**\n     * Handles RTC 'onunmute' event for the video track.\n     *\n     * @param {JitsiRemoteTrack} track - The video track for which 'onunmute'\n     * event will be processed.\n     */\n    onTrackRtcUnmuted(track) {\n        const participantId = track.getParticipantId();\n\n        logger.debug(\n            `Detector track RTC unmuted: ${participantId}`, Date.now());\n\n        this.clearTimeout(participantId);\n        this.clearRtcMutedTimestamp(participantId);\n\n        this.figureOutConnectionStatus(participantId);\n    }\n\n    /**\n     * Here the signalling \"mute\"/\"unmute\" events are processed.\n     *\n     * @param {JitsiRemoteTrack} track - The remote video track for which\n     * the signalling mute/unmute event will be processed.\n     */\n    onSignallingMuteChanged(track) {\n        const participantId = track.getParticipantId();\n\n        logger.debug(\n            `Detector on track signalling mute changed: ${participantId}`,\n            track.isMuted());\n\n        this.figureOutConnectionStatus(participantId);\n    }\n\n    /**\n     * Sends a participant connection status event as a result of the video type\n     * changing.\n     * @param {JitsiRemoteTrack} track - The track.\n     * @param {VideoType} type - The video type.\n     * @returns {void}\n     */\n    onTrackVideoTypeChanged(track, type) {\n        const id = track.getParticipantId();\n        const nowMs = Date.now();\n\n        this.maybeSendParticipantConnectionStatusEvent(id, nowMs);\n\n        this.connectionStatusMap[id] = {\n            ...this.connectionStatusMap[id] || {},\n            videoType: type,\n            startedMs: nowMs\n        };\n    }\n}\n","\nimport { Strophe } from 'strophe.js';\n\n\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport { ParticipantConnectionStatus }\n    from './modules/connectivity/ParticipantConnectionStatus';\nimport { MediaType } from './service/RTC/MediaType';\n\n/**\n * Represents a participant in (i.e. a member of) a conference.\n */\nexport default class JitsiParticipant {\n\n    /* eslint-disable max-params */\n\n    /**\n     * Initializes a new JitsiParticipant instance.\n     *\n     * @constructor\n     * @param jid the conference XMPP jid\n     * @param conference\n     * @param displayName\n     * @param {Boolean} hidden - True if the new JitsiParticipant instance is to\n     * represent a hidden participant; otherwise, false.\n     * @param {string} statsID - optional participant statsID\n     * @param {string} status - the initial status if any.\n     * @param {object} identity - the xmpp identity\n     * @param {boolean?} isReplacing - whether this is a participant replacing another into the meeting.\n     * @param {boolean?} isReplaced - whether this is a participant to be kicked and replaced into the meeting.\n     */\n    constructor(jid, conference, displayName, hidden, statsID, status, identity, isReplacing, isReplaced) {\n        this._jid = jid;\n        this._id = Strophe.getResourceFromJid(jid);\n        this._conference = conference;\n        this._displayName = displayName;\n        this._supportsDTMF = false;\n        this._tracks = [];\n        this._role = 'none';\n        this._status = status;\n        this._hidden = hidden;\n        this._statsID = statsID;\n        this._connectionStatus = ParticipantConnectionStatus.ACTIVE;\n        this._properties = {};\n        this._identity = identity;\n        this._isReplacing = isReplacing;\n        this._isReplaced = isReplaced;\n        this._features = new Set();\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * @returns {JitsiConference} The conference that this participant belongs\n     * to.\n     */\n    getConference() {\n        return this._conference;\n    }\n\n    /**\n     * Gets the value of a property of this participant.\n     */\n    getProperty(name) {\n        return this._properties[name];\n    }\n\n    /**\n     * Checks whether this <tt>JitsiParticipant</tt> has any video tracks which\n     * are muted according to their underlying WebRTC <tt>MediaStreamTrack</tt>\n     * muted status.\n     * @return {boolean} <tt>true</tt> if this <tt>participant</tt> contains any\n     * video <tt>JitsiTrack</tt>s which are muted as defined in\n     * {@link JitsiTrack.isWebRTCTrackMuted}.\n     */\n    hasAnyVideoTrackWebRTCMuted() {\n        return (\n            this.getTracks().some(\n                jitsiTrack =>\n                    jitsiTrack.getType() === MediaType.VIDEO\n                        && jitsiTrack.isWebRTCTrackMuted()));\n    }\n\n    /**\n     * Updates participant's connection status.\n     * @param {string} state the current participant connection state.\n     * {@link ParticipantConnectionStatus}.\n     * @private\n     */\n    _setConnectionStatus(status) {\n        this._connectionStatus = status;\n    }\n\n    /**\n     * Return participant's connectivity status.\n     *\n     * @returns {string} the connection status\n     * <tt>ParticipantConnectionStatus</tt> of the user.\n     * {@link ParticipantConnectionStatus}.\n     */\n    getConnectionStatus() {\n        return this._connectionStatus;\n    }\n\n    /**\n     * Sets the value of a property of this participant, and fires an event if\n     * the value has changed.\n     * @name the name of the property.\n     * @value the value to set.\n     */\n    setProperty(name, value) {\n        const oldValue = this._properties[name];\n\n        if (value !== oldValue) {\n            this._properties[name] = value;\n            this._conference.eventEmitter.emit(\n                JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n                this,\n                name,\n                oldValue,\n                value);\n        }\n    }\n\n    /**\n     * @returns {Array.<JitsiTrack>} The list of media tracks for this\n     * participant.\n     */\n    getTracks() {\n        return this._tracks.slice();\n    }\n\n    /**\n     * @param {MediaType} mediaType\n     * @returns {Array.<JitsiTrack>} an array of media tracks for this\n     * participant, for given media type.\n     */\n    getTracksByMediaType(mediaType) {\n        return this.getTracks().filter(track => track.getType() === mediaType);\n    }\n\n    /**\n     * @returns {String} The ID of this participant.\n     */\n    getId() {\n        return this._id;\n    }\n\n    /**\n     * @returns {String} The JID of this participant.\n     */\n    getJid() {\n        return this._jid;\n    }\n\n    /**\n     * @returns {String} The human-readable display name of this participant.\n     */\n    getDisplayName() {\n        return this._displayName;\n    }\n\n    /**\n     * @returns {String} The stats ID of this participant.\n     */\n    getStatsID() {\n        return this._statsID;\n    }\n\n    /**\n     * @returns {String} The status of the participant.\n     */\n    getStatus() {\n        return this._status;\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant is a moderator or not.\n     */\n    isModerator() {\n        return this._role === 'moderator';\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant is a hidden participant. Some\n     * special system participants may want to join hidden (like for example the\n     * recorder).\n     */\n    isHidden() {\n        return this._hidden;\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant is a hidden participant. Some\n     * special system participants may want to join hidden (like for example the\n     * recorder).\n     */\n    isHiddenFromRecorder() {\n        return Boolean(this._identity?.user?.['hidden-from-recorder']);\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant replaces another participant\n     * from the meeting.\n     */\n    isReplacing() {\n        return this._isReplacing;\n    }\n\n    /**\n     * @returns {Boolean} Wheter this participants will be replaced by another\n     * participant in the meeting.\n     */\n    isReplaced() {\n        return this._isReplaced;\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant has muted their audio.\n     */\n    isAudioMuted() {\n        return this._isMediaTypeMuted(MediaType.AUDIO);\n    }\n\n    /**\n     * Determines whether all JitsiTracks which are of a specific MediaType and\n     * which belong to this JitsiParticipant are muted.\n     *\n     * @param {MediaType} mediaType - The MediaType of the JitsiTracks to be\n     * checked.\n     * @private\n     * @returns {Boolean} True if all JitsiTracks which are of the specified\n     * mediaType and which belong to this JitsiParticipant are muted; otherwise,\n     * false.\n     */\n    _isMediaTypeMuted(mediaType) {\n        return this.getTracks().reduce(\n            (muted, track) =>\n                muted && (track.getType() !== mediaType || track.isMuted()),\n            true);\n    }\n\n    /**\n     * @returns {Boolean} Whether this participant has muted their video.\n     */\n    isVideoMuted() {\n        return this._isMediaTypeMuted(MediaType.VIDEO);\n    }\n\n    /**\n     * @returns {String} The role of this participant.\n     */\n    getRole() {\n        return this._role;\n    }\n\n    /**\n     * Sets a new participant role.\n     * @param {String} newRole - the new role.\n     */\n    setRole(newRole) {\n        this._role = newRole;\n    }\n\n    /**\n     * Sets whether participant is replacing another based on jwt.\n     * @param {String} newIsReplacing - whether is replacing.\n     */\n    setIsReplacing(newIsReplacing) {\n        this._isReplacing = newIsReplacing;\n    }\n\n    /**\n     * Sets whether participant is being replaced by another based on jwt.\n     * @param {boolean} newIsReplaced - whether is being replaced.\n     */\n    setIsReplaced(newIsReplaced) {\n        this._isReplaced = newIsReplaced;\n    }\n\n    /**\n     *\n     */\n    supportsDTMF() {\n        return this._supportsDTMF;\n    }\n\n    /**\n     * Returns a set with the features for the participant.\n     * @returns {Promise<Set<String>, Error>}\n     */\n    getFeatures() {\n        return Promise.resolve(this._features);\n    }\n\n    /**\n     * Checks current set features.\n     * @param {String} feature - the feature to check.\n     * @return {boolean} <tt>true</tt> if this <tt>participant</tt> contains the\n     * <tt>feature</tt>.\n     */\n    hasFeature(feature) {\n        return this._features.has(feature);\n    }\n\n    /**\n     * Set new features.\n     * @param {Set<String>|undefined} newFeatures - Sets new features.\n     */\n    setFeatures(newFeatures) {\n        this._features = newFeatures || new Set();\n    }\n\n    /**\n     * Returns the bot type for the participant.\n     *\n     * @returns {string|undefined} - The bot type of the participant.\n     */\n    getBotType() {\n        return this._botType;\n    }\n\n    /**\n     * Sets the bot type for the participant.\n     * @param {String} newBotType - The new bot type to set.\n     */\n    setBotType(newBotType) {\n        this._botType = newBotType;\n    }\n}\n","/**\n * The events for the connection.\n */\n\nexport enum JitsiConnectionEvents {\n    /**\n     * Indicates that the connection has been disconnected. The event provides\n     * the following parameters to its listeners:\n     *\n     * @param msg {string} a message associated with the disconnect such as the\n     * last (known) error message\n     */\n    CONNECTION_DISCONNECTED = 'connection.connectionDisconnected',\n\n    /**\n     * Indicates that the connection has been established. The event provides\n     * the following parameters to its listeners:\n     *\n     * @param id {string} the ID of the local endpoint/participant/peer (within\n     * the context of the established connection)\n     */\n    CONNECTION_ESTABLISHED = 'connection.connectionEstablished',\n\n    /**\n     * Indicates that the connection has been failed for some reason. The event\n     * provides the following parameters to its listeners:\n     *\n     * @param errType {JitsiConnectionErrors} the type of error associated with\n     * the failure\n     * @param errReason {string} the error (message) associated with the failure\n     * @param credentials {object} the credentials used to connect (if any)\n     * @param errReasonDetails {object} an optional object with details about\n     * the error, like shard moving, suspending. Used for analytics purposes.\n     */\n    CONNECTION_FAILED = 'connection.connectionFailed',\n\n    /**\n     * Indicates that the performed action cannot be executed because the\n     * connection is not in the correct state(connected, disconnected, etc.)\n     */\n    WRONG_STATE = 'connection.wrongState',\n\n    /**\n     * Indicates that the display name is required over this connection and need to be supplied when\n     * joining the room.\n     * There are cases like lobby room where display name is required.\n     */\n    DISPLAY_NAME_REQUIRED = 'connection.display_name_required'\n};\n\n// exported for backward compatibility\nexport const CONNECTION_DISCONNECTED = JitsiConnectionEvents.CONNECTION_DISCONNECTED;\nexport const CONNECTION_ESTABLISHED = JitsiConnectionEvents.CONNECTION_ESTABLISHED;\nexport const CONNECTION_FAILED = JitsiConnectionEvents.CONNECTION_FAILED;\nexport const WRONG_STATE = JitsiConnectionEvents.WRONG_STATE;\nexport const DISPLAY_NAME_REQUIRED = JitsiConnectionEvents.DISPLAY_NAME_REQUIRED;\n","/**\n * The errors for the connection.\n */\n\nexport enum JitsiConnectionErrors {\n    /**\n     * Indicates that the connection was dropped with an error which was most likely\n     * caused by some networking issues. The dropped term in this context means that\n     * the connection was closed unexpectedly (not on user's request).\n     *\n     * One example is 'item-not-found' error thrown by Prosody when the BOSH session\n     * times out after 60 seconds of inactivity. On the other hand 'item-not-found'\n     * could also happen when BOSH request is sent to the server with the session-id\n     * that is not know to the server. But this should not happen in lib-jitsi-meet\n     * case as long as the service is configured correctly (there is no bug).\n     */\n    CONNECTION_DROPPED_ERROR = 'connection.droppedError',\n\n    /**\n     * Not specified errors.\n     */\n    OTHER_ERROR = 'connection.otherError',\n\n    /**\n     * Indicates that a password is required in order to join the conference.\n     */\n    PASSWORD_REQUIRED = 'connection.passwordRequired',\n\n    /**\n     * Indicates that the connection was dropped, because of too many 5xx HTTP\n     * errors on BOSH requests.\n     */\n    SERVER_ERROR = 'connection.serverError'\n};\n\n// exported for backward compatibility\nexport const CONNECTION_DROPPED_ERROR = JitsiConnectionErrors.CONNECTION_DROPPED_ERROR;\nexport const OTHER_ERROR = JitsiConnectionErrors.OTHER_ERROR;\nexport const PASSWORD_REQUIRED = JitsiConnectionErrors.PASSWORD_REQUIRED;\nexport const SERVER_ERROR = JitsiConnectionErrors.SERVER_ERROR;\n","\n/**\n * Promise-like object which can be passed around for resolving it later. It\n * implements the \"thenable\" interface, so it can be used wherever a Promise\n * could be used.\n *\n * In addition a \"reject on timeout\" functionality is provided.\n */\nexport default class Deferred {\n    /**\n     * Instantiates a Deferred object.\n     */\n    constructor() {\n        this.promise = new Promise((resolve, reject) => {\n            this.resolve = (...args) => {\n                this.clearRejectTimeout();\n                resolve(...args);\n            };\n            this.reject = (...args) => {\n                this.clearRejectTimeout();\n                reject(...args);\n            };\n        });\n        this.then = this.promise.then.bind(this.promise);\n        this.catch = this.promise.catch.bind(this.promise);\n    }\n\n    /**\n     * Clears the reject timeout.\n     */\n    clearRejectTimeout() {\n        clearTimeout(this._timeout);\n    }\n\n    /**\n     * Rejects the promise after the given timeout.\n     */\n    setRejectTimeout(ms) {\n        this._timeout = setTimeout(() => {\n            this.reject(new Error('timeout'));\n        }, ms);\n    }\n}\n","import EventEmitter from 'events';\n\n/**\n * The class implements basic event operations - add/remove listener.\n * NOTE: The purpose of the class is to be extended in order to add\n * this functionality to other classes.\n */\nexport default class Listenable {\n    /**\n     * Creates new instance.\n     * @param {EventEmitter} eventEmitter\n     * @constructor\n     */\n    constructor(eventEmitter = new EventEmitter()) {\n        this.eventEmitter = eventEmitter;\n\n        // aliases for addListener/removeListener\n        this.addEventListener = this.on = this.addListener;\n        this.removeEventListener = this.off = this.removeListener;\n    }\n\n    /**\n     * Adds new listener.\n     * @param {String} eventName the name of the event\n     * @param {Function} listener the listener.\n     * @returns {Function} - The unsubscribe function.\n     */\n    addListener(eventName, listener) {\n        this.eventEmitter.addListener(eventName, listener);\n\n        return () => this.removeEventListener(eventName, listener);\n    }\n\n    /**\n     * Removes listener.\n     * @param {String} eventName the name of the event that triggers the\n     * listener\n     * @param {Function} listener the listener.\n     */\n    removeListener(eventName, listener) {\n        this.eventEmitter.removeListener(eventName, listener);\n    }\n}\n","/* global RTCRtpScriptTransform */\n\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\n// Flag to set on senders / receivers to avoid setting up the encryption transform\n// more than once.\nconst kJitsiE2EE = Symbol('kJitsiE2EE');\n\n/**\n * Context encapsulating the cryptography bits required for E2EE.\n * This uses the WebRTC Insertable Streams API which is explained in\n *   https://github.com/alvestrand/webrtc-media-streams/blob/master/explainer.md\n * that provides access to the encoded frames and allows them to be transformed.\n *\n * The encoded frame format is explained below in the _encodeFunction method.\n * High level design goals were:\n * - do not require changes to existing SFUs and retain (VP8) metadata.\n * - allow the SFU to rewrite SSRCs, timestamp, pictureId.\n * - allow for the key to be rotated frequently.\n */\nexport default class E2EEcontext {\n    /**\n     * Build a new E2EE context instance, which will be used in a given conference.\n     * @param {boolean} [options.sharedKey] - whether there is a uniques key shared amoung all participants.\n     */\n    constructor({ sharedKey } = {}) {\n        // Determine the URL for the worker script. Relative URLs are relative to\n        // the entry point, not the script that launches the worker.\n        let baseUrl = '';\n        const ljm = document.querySelector('script[src*=\"lib-jitsi-meet\"]');\n\n        if (ljm) {\n            const idx = ljm.src.lastIndexOf('/');\n\n            baseUrl = `${ljm.src.substring(0, idx)}/`;\n        }\n\n        let workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;\n\n        // If there is no baseUrl then we create the worker in a normal way\n        // as you cant load scripts inside blobs from relative paths.\n        // See: https://www.html5rocks.com/en/tutorials/workers/basics/#toc-inlineworkers-loadingscripts\n        if (baseUrl && baseUrl !== '/') {\n            // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it\n            // synchronously load the JS.\n            const workerBlob\n                = new Blob([ `importScripts(\"${workerUrl}\");` ], { type: 'application/javascript' });\n\n            workerUrl = window.URL.createObjectURL(workerBlob);\n        }\n\n        this._worker = new Worker(workerUrl, { name: 'E2EE Worker' });\n\n        this._worker.onerror = e => logger.error(e);\n\n        this._worker.postMessage({\n            operation: 'initialize',\n            sharedKey\n        });\n    }\n\n    /**\n     * Cleans up all state associated with the given participant. This is needed when a\n     * participant leaves the current conference.\n     *\n     * @param {string} participantId - The participant that just left.\n     */\n    cleanup(participantId) {\n        this._worker.postMessage({\n            operation: 'cleanup',\n            participantId\n        });\n    }\n\n    /**\n     * Cleans up all state associated with all participants in the conference. This is needed when disabling e2ee.\n     *\n     */\n    cleanupAll() {\n        this._worker.postMessage({\n            operation: 'cleanupAll'\n        });\n    }\n\n    /**\n     * Handles the given {@code RTCRtpReceiver} by creating a {@code TransformStream} which will inject\n     * a frame decoder.\n     *\n     * @param {RTCRtpReceiver} receiver - The receiver which will get the decoding function injected.\n     * @param {string} kind - The kind of track this receiver belongs to.\n     * @param {string} participantId - The participant id that this receiver belongs to.\n     */\n    handleReceiver(receiver, kind, participantId) {\n        if (receiver[kJitsiE2EE]) {\n            return;\n        }\n        receiver[kJitsiE2EE] = true;\n\n        if (window.RTCRtpScriptTransform) {\n            const options = {\n                operation: 'decode',\n                participantId\n            };\n\n            receiver.transform = new RTCRtpScriptTransform(this._worker, options);\n        } else {\n            const receiverStreams = receiver.createEncodedStreams();\n\n            this._worker.postMessage({\n                operation: 'decode',\n                readableStream: receiverStreams.readable,\n                writableStream: receiverStreams.writable,\n                participantId\n            }, [ receiverStreams.readable, receiverStreams.writable ]);\n        }\n    }\n\n    /**\n     * Handles the given {@code RTCRtpSender} by creating a {@code TransformStream} which will inject\n     * a frame encoder.\n     *\n     * @param {RTCRtpSender} sender - The sender which will get the encoding function injected.\n     * @param {string} kind - The kind of track this sender belongs to.\n     * @param {string} participantId - The participant id that this sender belongs to.\n     */\n    handleSender(sender, kind, participantId) {\n        if (sender[kJitsiE2EE]) {\n            return;\n        }\n        sender[kJitsiE2EE] = true;\n\n        if (window.RTCRtpScriptTransform) {\n            const options = {\n                operation: 'encode',\n                participantId\n            };\n\n            sender.transform = new RTCRtpScriptTransform(this._worker, options);\n        } else {\n            const senderStreams = sender.createEncodedStreams();\n\n            this._worker.postMessage({\n                operation: 'encode',\n                readableStream: senderStreams.readable,\n                writableStream: senderStreams.writable,\n                participantId\n            }, [ senderStreams.readable, senderStreams.writable ]);\n        }\n    }\n\n    /**\n     * Set the E2EE key for the specified participant.\n     *\n     * @param {string} participantId - the ID of the participant who's key we are setting.\n     * @param {Uint8Array | boolean} key - they key for the given participant.\n     * @param {Number} keyIndex - the key index.\n     */\n    setKey(participantId, key, keyIndex) {\n        this._worker.postMessage({\n            operation: 'setKey',\n            key,\n            keyIndex,\n            participantId\n        });\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\nimport Deferred from '../util/Deferred';\nimport Listenable from '../util/Listenable';\n\nimport E2EEContext from './E2EEContext';\n\nconst logger = getLogger(__filename);\n\n/**\n * Abstract class that integrates {@link E2EEContext} with a key management system.\n */\nexport class KeyHandler extends Listenable {\n    /**\n     * Build a new KeyHandler instance, which will be used in a given conference.\n     * @param {JitsiConference} conference - the current conference.\n     * @param {object} options - the options passed to {E2EEContext}, see implemention.\n     */\n    constructor(conference, options = {}) {\n        super();\n\n        this.conference = conference;\n        this.e2eeCtx = new E2EEContext(options);\n\n        this.enabled = false;\n        this._enabling = undefined;\n\n        // Conference media events in order to attach the encryptor / decryptor.\n        // FIXME add events to TraceablePeerConnection which will allow to see when there's new receiver or sender\n        // added instead of shenanigans around conference track events and track muted.\n        //\n\n        this.conference.on(\n            JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n            this._onMediaSessionStarted.bind(this));\n        this.conference.on(\n            JitsiConferenceEvents.TRACK_ADDED,\n            track => track.isLocal() && this._onLocalTrackAdded(track));\n        this.conference.rtc.on(\n            RTCEvents.REMOTE_TRACK_ADDED,\n            (track, tpc) => this._setupReceiverE2EEForTrack(tpc, track));\n        this.conference.on(\n            JitsiConferenceEvents.TRACK_MUTE_CHANGED,\n            this._trackMuteChanged.bind(this));\n    }\n\n    /**\n     * Indicates whether E2EE is currently enabled or not.\n     *\n     * @returns {boolean}\n     */\n    isEnabled() {\n        return this.enabled;\n    }\n\n    /**\n     * Enables / disables End-To-End encryption.\n     *\n     * @param {boolean} enabled - whether E2EE should be enabled or not.\n     * @returns {void}\n     */\n    async setEnabled(enabled) {\n        if (enabled === this.enabled) {\n            return;\n        }\n\n        this._enabling && await this._enabling;\n\n        this._enabling = new Deferred();\n\n        this.enabled = enabled;\n\n        if (!enabled) {\n            this.e2eeCtx.cleanupAll();\n        }\n\n        this._setEnabled && await this._setEnabled(enabled);\n\n        this.conference.setLocalParticipantProperty('e2ee.enabled', enabled);\n\n        this.conference._restartMediaSessions();\n\n        this._enabling.resolve();\n    }\n\n    /**\n     * Sets the key for End-to-End encryption.\n     *\n     * @returns {void}\n     */\n    setEncryptionKey() {\n        throw new Error('Not implemented by subclass');\n    }\n\n    /**\n     * Setup E2EE on the new track that has been added to the conference, apply it on all the open peerconnections.\n     * @param {JitsiLocalTrack} track - the new track that's being added to the conference.\n     * @private\n     */\n    _onLocalTrackAdded(track) {\n        for (const session of this.conference.getMediaSessions()) {\n            this._setupSenderE2EEForTrack(session, track);\n        }\n    }\n\n    /**\n     * Setups E2E encryption for the new session.\n     * @param {JingleSessionPC} session - the new media session.\n     * @private\n     */\n    _onMediaSessionStarted(session) {\n        const localTracks = this.conference.getLocalTracks();\n\n        for (const track of localTracks) {\n            this._setupSenderE2EEForTrack(session, track);\n        }\n    }\n\n    /**\n     * Setup E2EE for the receiving side.\n     *\n     * @private\n     */\n    _setupReceiverE2EEForTrack(tpc, track) {\n        if (!this.enabled) {\n            return;\n        }\n\n        const receiver = tpc.findReceiverForTrack(track.track);\n\n        if (receiver) {\n            this.e2eeCtx.handleReceiver(receiver, track.getType(), track.getParticipantId());\n        } else {\n            logger.warn(`Could not handle E2EE for ${track}: receiver not found in: ${tpc}`);\n        }\n    }\n\n    /**\n     * Setup E2EE for the sending side.\n     *\n     * @param {JingleSessionPC} session - the session which sends the media produced by the track.\n     * @param {JitsiLocalTrack} track - the local track for which e2e encoder will be configured.\n     * @private\n     */\n    _setupSenderE2EEForTrack(session, track) {\n        if (!this.enabled) {\n            return;\n        }\n\n        const pc = session.peerconnection;\n        const sender = pc && pc.findSenderForTrack(track.track);\n\n        if (sender) {\n            this.e2eeCtx.handleSender(sender, track.getType(), track.getParticipantId());\n        } else {\n            logger.warn(`Could not handle E2EE for ${track}: sender not found in ${pc}`);\n        }\n    }\n\n    /**\n     * Setup E2EE on the sender that is created for the unmuted local video track.\n     * @param {JitsiLocalTrack} track - the track for which muted status has changed.\n     * @private\n     */\n    _trackMuteChanged(track) {\n        if (browser.doesVideoMuteByStreamRemove() && track.isLocal() && track.isVideoTrack() && !track.isMuted()) {\n            for (const session of this.conference.getMediaSessions()) {\n                this._setupSenderE2EEForTrack(session, track);\n            }\n        }\n    }\n}\n","import { KeyHandler } from './KeyHandler';\n\n/**\n * This module integrates {@link E2EEContext} with {external} in order to set the keys for encryption.\n */\nexport class ExternallyManagedKeyHandler extends KeyHandler {\n    /**\n     * Build a new ExternallyManagedKeyHandler instance, which will be used in a given conference.\n     * @param conference - the current conference.\n     */\n    constructor(conference) {\n        super(conference, { sharedKey: true });\n    }\n\n    /**\n     * Sets the key and index for End-to-End encryption.\n     *\n     * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n     * @param {Number} [keyInfo.index] - the index of the encryption key.\n     * @returns {void}\n     */\n    setKey(keyInfo) {\n        this.e2eeCtx.setKey(undefined, { encryptionKey: keyInfo.encryptionKey }, keyInfo.index);\n    }\n}\n","import uuid from './dist/index.js';\nexport const v1 = uuid.v1;\nexport const v3 = uuid.v3;\nexport const v4 = uuid.v4;\nexport const v5 = uuid.v5;\n","/* global Olm */\n\nimport { getLogger } from '@jitsi/logger';\nimport base64js from 'base64-js';\nimport isEqual from 'lodash.isequal';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport Deferred from '../util/Deferred';\nimport Listenable from '../util/Listenable';\nimport { FEATURE_E2EE, JITSI_MEET_MUC_TYPE } from '../xmpp/xmpp';\n\nconst logger = getLogger(__filename);\n\nconst REQ_TIMEOUT = 5 * 1000;\nconst OLM_MESSAGE_TYPE = 'olm';\nconst OLM_MESSAGE_TYPES = {\n    ERROR: 'error',\n    KEY_INFO: 'key-info',\n    KEY_INFO_ACK: 'key-info-ack',\n    SESSION_ACK: 'session-ack',\n    SESSION_INIT: 'session-init'\n};\n\nconst kOlmData = Symbol('OlmData');\n\nconst OlmAdapterEvents = {\n    OLM_ID_KEY_READY: 'olm.id_key_ready',\n    PARTICIPANT_E2EE_CHANNEL_READY: 'olm.participant_e2ee_channel_ready',\n    PARTICIPANT_KEY_UPDATED: 'olm.partitipant_key_updated'\n};\n\n/**\n * This class implements an End-to-End Encrypted communication channel between every two peers\n * in the conference. This channel uses libolm to achieve E2EE.\n *\n * The created channel is then used to exchange the secret key that each participant will use\n * to encrypt the actual media (see {@link E2EEContext}).\n *\n * A simple JSON message based protocol is implemented, which follows a request - response model:\n * - session-init: Initiates an olm session establishment procedure. This message will be sent\n *                 by the participant who just joined, to everyone else.\n * - session-ack: Completes the olm session etablishment. This messsage may contain ancilliary\n *                encrypted data, more specifically the sender's current key.\n * - key-info: Includes the sender's most up to date key information.\n * - key-info-ack: Acknowledges the reception of a key-info request. In addition, it may contain\n *                 the sender's key information, if available.\n * - error: Indicates a request processing error has occurred.\n *\n * These requessts and responses are transport independent. Currently they are sent using XMPP\n * MUC private messages.\n */\nexport class OlmAdapter extends Listenable {\n    /**\n     * Creates an adapter instance for the given conference.\n     */\n    constructor(conference) {\n        super();\n\n        this._conf = conference;\n        this._init = new Deferred();\n        this._key = undefined;\n        this._keyIndex = -1;\n        this._reqs = new Map();\n        this._sessionInitialization = undefined;\n\n        if (OlmAdapter.isSupported()) {\n            this._bootstrapOlm();\n\n            this._conf.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, this._onEndpointMessageReceived.bind(this));\n            this._conf.on(JitsiConferenceEvents.CONFERENCE_LEFT, this._onConferenceLeft.bind(this));\n            this._conf.on(JitsiConferenceEvents.USER_LEFT, this._onParticipantLeft.bind(this));\n            this._conf.on(JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n                this._onParticipantPropertyChanged.bind(this));\n        } else {\n            this._init.reject(new Error('Olm not supported'));\n        }\n    }\n\n    /**\n     * Starts new olm sessions with every other participant that has the participantId \"smaller\" the localParticipantId.\n     */\n    async initSessions() {\n        if (this._sessionInitialization) {\n            throw new Error('OlmAdapter initSessions called multiple times');\n        } else {\n            this._sessionInitialization = new Deferred();\n\n            await this._init;\n\n            const promises = [];\n            const localParticipantId = this._conf.myUserId();\n\n            for (const participant of this._conf.getParticipants()) {\n                if (participant.hasFeature(FEATURE_E2EE) && localParticipantId < participant.getId()) {\n                    promises.push(this._sendSessionInit(participant));\n                }\n            }\n\n            await Promise.allSettled(promises);\n\n            // TODO: retry failed ones.\n\n            this._sessionInitialization.resolve();\n            this._sessionInitialization = undefined;\n        }\n    }\n\n    /**\n     * Indicates if olm is supported on the current platform.\n     *\n     * @returns {boolean}\n     */\n    static isSupported() {\n        return typeof window.Olm !== 'undefined';\n    }\n\n    /**\n     * Updates the current participant key and distributes it to all participants in the conference\n     * by sending a key-info message.\n     *\n     * @param {Uint8Array|boolean} key - The new key.\n     * @retrns {Promise<Number>}\n     */\n    async updateKey(key) {\n        // Store it locally for new sessions.\n        this._key = key;\n        this._keyIndex++;\n\n        // Broadcast it.\n        const promises = [];\n\n        for (const participant of this._conf.getParticipants()) {\n            const pId = participant.getId();\n            const olmData = this._getParticipantOlmData(participant);\n\n            // TODO: skip those who don't support E2EE.\n            if (!olmData.session) {\n                logger.warn(`Tried to send key to participant ${pId} but we have no session`);\n\n                // eslint-disable-next-line no-continue\n                continue;\n            }\n\n            const uuid = uuidv4();\n            const data = {\n                [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n                olm: {\n                    type: OLM_MESSAGE_TYPES.KEY_INFO,\n                    data: {\n                        ciphertext: this._encryptKeyInfo(olmData.session),\n                        uuid\n                    }\n                }\n            };\n            const d = new Deferred();\n\n            d.setRejectTimeout(REQ_TIMEOUT);\n            d.catch(() => {\n                this._reqs.delete(uuid);\n            });\n            this._reqs.set(uuid, d);\n            promises.push(d);\n\n            this._sendMessage(data, pId);\n        }\n\n        await Promise.allSettled(promises);\n\n        // TODO: retry failed ones?\n\n        return this._keyIndex;\n    }\n\n    /**\n     * Updates the current participant key.\n     * @param {Uint8Array|boolean} key - The new key.\n     * @returns {number}\n    */\n    updateCurrentKey(key) {\n        this._key = key;\n\n        return this._keyIndex;\n    }\n\n    /**\n     * Frees the olmData session for the given participant.\n     *\n     */\n    clearParticipantSession(participant) {\n        const olmData = this._getParticipantOlmData(participant);\n\n        if (olmData.session) {\n            olmData.session.free();\n            olmData.session = undefined;\n        }\n    }\n\n\n    /**\n     * Frees the olmData sessions for all participants.\n     *\n     */\n    clearAllParticipantsSessions() {\n        for (const participant of this._conf.getParticipants()) {\n            this.clearParticipantSession(participant);\n        }\n    }\n\n    /**\n     * Internal helper to bootstrap the olm library.\n     *\n     * @returns {Promise<void>}\n     * @private\n     */\n    async _bootstrapOlm() {\n        logger.debug('Initializing Olm...');\n\n        try {\n            await Olm.init();\n\n            this._olmAccount = new Olm.Account();\n            this._olmAccount.create();\n\n            const idKeys = JSON.parse(this._olmAccount.identity_keys());\n\n            this._idKey = idKeys.curve25519;\n\n            logger.debug(`Olm ${Olm.get_library_version().join('.')} initialized`);\n            this._init.resolve();\n            this._onIdKeyReady(this._idKey);\n        } catch (e) {\n            logger.error('Failed to initialize Olm', e);\n            this._init.reject(e);\n        }\n\n    }\n\n    /**\n     * Publishes our own Olmn id key in presence.\n     * @private\n     */\n    _onIdKeyReady(idKey) {\n        logger.debug(`Olm id key ready: ${idKey}`);\n\n        // Publish it in presence.\n        this._conf.setLocalParticipantProperty('e2ee.idKey', idKey);\n    }\n\n    /**\n     * Event posted when the E2EE signalling channel has been established with the given participant.\n     * @private\n     */\n    _onParticipantE2EEChannelReady(id) {\n        logger.debug(`E2EE channel with participant ${id} is ready`);\n    }\n\n    /**\n     * Internal helper for encrypting the current key information for a given participant.\n     *\n     * @param {Olm.Session} session - Participant's session.\n     * @returns {string} - The encrypted text with the key information.\n     * @private\n     */\n    _encryptKeyInfo(session) {\n        const keyInfo = {};\n\n        if (this._key !== undefined) {\n            keyInfo.key = this._key ? base64js.fromByteArray(this._key) : false;\n            keyInfo.keyIndex = this._keyIndex;\n        }\n\n        return session.encrypt(JSON.stringify(keyInfo));\n    }\n\n    /**\n     * Internal helper for getting the olm related data associated with a participant.\n     *\n     * @param {JitsiParticipant} participant - Participant whose data wants to be extracted.\n     * @returns {Object}\n     * @private\n     */\n    _getParticipantOlmData(participant) {\n        participant[kOlmData] = participant[kOlmData] || {};\n\n        return participant[kOlmData];\n    }\n\n    /**\n     * Handles leaving the conference, cleaning up olm sessions.\n     *\n     * @private\n     */\n    async _onConferenceLeft() {\n        logger.debug('Conference left');\n\n        await this._init;\n\n        for (const participant of this._conf.getParticipants()) {\n            this._onParticipantLeft(participant.getId(), participant);\n        }\n\n        if (this._olmAccount) {\n            this._olmAccount.free();\n            this._olmAccount = undefined;\n        }\n    }\n\n    /**\n     * Main message handler. Handles 1-to-1 messages received from other participants\n     * and send the appropriate replies.\n     *\n     * @private\n     */\n    async _onEndpointMessageReceived(participant, payload) {\n        if (payload[JITSI_MEET_MUC_TYPE] !== OLM_MESSAGE_TYPE) {\n            return;\n        }\n\n        if (!payload.olm) {\n            logger.warn('Incorrectly formatted message');\n\n            return;\n        }\n\n        await this._init;\n\n        const msg = payload.olm;\n        const pId = participant.getId();\n        const olmData = this._getParticipantOlmData(participant);\n\n        switch (msg.type) {\n        case OLM_MESSAGE_TYPES.SESSION_INIT: {\n            if (olmData.session) {\n                logger.warn(`Participant ${pId} already has a session`);\n\n                this._sendError(participant, 'Session already established');\n            } else {\n                // Create a session for communicating with this participant.\n\n                const session = new Olm.Session();\n\n                session.create_outbound(this._olmAccount, msg.data.idKey, msg.data.otKey);\n                olmData.session = session;\n\n                // Send ACK\n                const ack = {\n                    [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n                    olm: {\n                        type: OLM_MESSAGE_TYPES.SESSION_ACK,\n                        data: {\n                            ciphertext: this._encryptKeyInfo(session),\n                            uuid: msg.data.uuid\n                        }\n                    }\n                };\n\n                this._sendMessage(ack, pId);\n                this._onParticipantE2EEChannelReady(pId);\n            }\n            break;\n        }\n        case OLM_MESSAGE_TYPES.SESSION_ACK: {\n            if (olmData.session) {\n                logger.warn(`Participant ${pId} already has a session`);\n\n                this._sendError(participant, 'No session found');\n            } else if (msg.data.uuid === olmData.pendingSessionUuid) {\n                const { ciphertext } = msg.data;\n                const d = this._reqs.get(msg.data.uuid);\n                const session = new Olm.Session();\n\n                session.create_inbound(this._olmAccount, ciphertext.body);\n\n                // Remove OT keys that have been used to setup this session.\n                this._olmAccount.remove_one_time_keys(session);\n\n                // Decrypt first message.\n                const data = session.decrypt(ciphertext.type, ciphertext.body);\n\n                olmData.session = session;\n                olmData.pendingSessionUuid = undefined;\n\n                this._onParticipantE2EEChannelReady(pId);\n\n                this._reqs.delete(msg.data.uuid);\n                d.resolve();\n\n                const json = safeJsonParse(data);\n\n                if (json.key) {\n                    const key = base64js.toByteArray(json.key);\n                    const keyIndex = json.keyIndex;\n\n                    olmData.lastKey = key;\n                    this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n                }\n            } else {\n                logger.warn('Received ACK with the wrong UUID');\n\n                this._sendError(participant, 'Invalid UUID');\n            }\n            break;\n        }\n        case OLM_MESSAGE_TYPES.ERROR: {\n            logger.error(msg.data.error);\n\n            break;\n        }\n        case OLM_MESSAGE_TYPES.KEY_INFO: {\n            if (olmData.session) {\n                const { ciphertext } = msg.data;\n                const data = olmData.session.decrypt(ciphertext.type, ciphertext.body);\n                const json = safeJsonParse(data);\n\n                if (json.key !== undefined && json.keyIndex !== undefined) {\n                    const key = json.key ? base64js.toByteArray(json.key) : false;\n                    const keyIndex = json.keyIndex;\n\n                    if (!isEqual(olmData.lastKey, key)) {\n                        olmData.lastKey = key;\n                        this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n                    }\n\n                    // Send ACK.\n                    const ack = {\n                        [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n                        olm: {\n                            type: OLM_MESSAGE_TYPES.KEY_INFO_ACK,\n                            data: {\n                                ciphertext: this._encryptKeyInfo(olmData.session),\n                                uuid: msg.data.uuid\n                            }\n                        }\n                    };\n\n                    this._sendMessage(ack, pId);\n                }\n            } else {\n                logger.debug(`Received key info message from ${pId} but we have no session for them!`);\n\n                this._sendError(participant, 'No session found while processing key-info');\n            }\n            break;\n        }\n        case OLM_MESSAGE_TYPES.KEY_INFO_ACK: {\n            if (olmData.session) {\n                const { ciphertext } = msg.data;\n                const data = olmData.session.decrypt(ciphertext.type, ciphertext.body);\n                const json = safeJsonParse(data);\n\n                if (json.key !== undefined && json.keyIndex !== undefined) {\n                    const key = json.key ? base64js.toByteArray(json.key) : false;\n                    const keyIndex = json.keyIndex;\n\n                    if (!isEqual(olmData.lastKey, key)) {\n                        olmData.lastKey = key;\n                        this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_KEY_UPDATED, pId, key, keyIndex);\n                    }\n                }\n\n                const d = this._reqs.get(msg.data.uuid);\n\n                this._reqs.delete(msg.data.uuid);\n                d.resolve();\n            } else {\n                logger.debug(`Received key info ack message from ${pId} but we have no session for them!`);\n\n                this._sendError(participant, 'No session found while processing key-info-ack');\n            }\n            break;\n        }\n        }\n    }\n\n    /**\n     * Handles a participant leaving. When a participant leaves their olm session is destroyed.\n     *\n     * @private\n     */\n    _onParticipantLeft(id, participant) {\n        logger.debug(`Participant ${id} left`);\n\n        this.clearParticipantSession(participant);\n    }\n\n    /**\n    * Handles an update in a participant's presence property.\n    *\n    * @param {JitsiParticipant} participant - The participant.\n    * @param {string} name - The name of the property that changed.\n    * @param {*} oldValue - The property's previous value.\n    * @param {*} newValue - The property's new value.\n    * @private\n    */\n    async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {\n        switch (name) {\n        case 'e2ee.enabled':\n            if (newValue && this._conf.isE2EEEnabled()) {\n                const localParticipantId = this._conf.myUserId();\n                const participantId = participant.getId();\n                const participantFeatures = await participant.getFeatures();\n\n                if (participantFeatures.has(FEATURE_E2EE) && localParticipantId < participantId) {\n                    if (this._sessionInitialization) {\n                        await this._sessionInitialization;\n                    }\n                    await this._sendSessionInit(participant);\n\n                    const olmData = this._getParticipantOlmData(participant);\n                    const uuid = uuidv4();\n                    const data = {\n                        [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n                        olm: {\n                            type: OLM_MESSAGE_TYPES.KEY_INFO,\n                            data: {\n                                ciphertext: this._encryptKeyInfo(olmData.session),\n                                uuid\n                            }\n                        }\n                    };\n\n                    this._sendMessage(data, participantId);\n                }\n            }\n            break;\n        }\n    }\n\n    /**\n     * Builds and sends an error message to the target participant.\n     *\n     * @param {JitsiParticipant} participant - The target participant.\n     * @param {string} error - The error message.\n     * @returns {void}\n     */\n    _sendError(participant, error) {\n        const pId = participant.getId();\n        const err = {\n            [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n            olm: {\n                type: OLM_MESSAGE_TYPES.ERROR,\n                data: {\n                    error\n                }\n            }\n        };\n\n        this._sendMessage(err, pId);\n    }\n\n    /**\n     * Internal helper to send the given object to the given participant ID.\n     * This function merely exists so the transport can be easily swapped.\n     * Currently messages are transmitted via XMPP MUC private messages.\n     *\n     * @param {object} data - The data that will be sent to the target participant.\n     * @param {string} participantId - ID of the target participant.\n     */\n    _sendMessage(data, participantId) {\n        this._conf.sendMessage(data, participantId);\n    }\n\n    /**\n     * Builds and sends the session-init request to the target participant.\n     *\n     * @param {JitsiParticipant} participant - Participant to whom we'll send the request.\n     * @returns {Promise} - The promise will be resolved when the session-ack is received.\n     * @private\n     */\n    _sendSessionInit(participant) {\n        const pId = participant.getId();\n        const olmData = this._getParticipantOlmData(participant);\n\n        if (olmData.session) {\n            logger.warn(`Tried to send session-init to ${pId} but we already have a session`);\n\n            return Promise.reject();\n        }\n\n        if (olmData.pendingSessionUuid !== undefined) {\n            logger.warn(`Tried to send session-init to ${pId} but we already have a pending session`);\n\n            return Promise.reject();\n        }\n\n        // Generate a One Time Key.\n        this._olmAccount.generate_one_time_keys(1);\n\n        const otKeys = JSON.parse(this._olmAccount.one_time_keys());\n        const otKey = Object.values(otKeys.curve25519)[0];\n\n        if (!otKey) {\n            return Promise.reject(new Error('No one-time-keys generated'));\n        }\n\n        // Mark the OT keys (one really) as published so they are not reused.\n        this._olmAccount.mark_keys_as_published();\n\n        const uuid = uuidv4();\n        const init = {\n            [JITSI_MEET_MUC_TYPE]: OLM_MESSAGE_TYPE,\n            olm: {\n                type: OLM_MESSAGE_TYPES.SESSION_INIT,\n                data: {\n                    idKey: this._idKey,\n                    otKey,\n                    uuid\n                }\n            }\n        };\n\n        const d = new Deferred();\n\n        d.setRejectTimeout(REQ_TIMEOUT);\n        d.catch(() => {\n            this._reqs.delete(uuid);\n            olmData.pendingSessionUuid = undefined;\n        });\n        this._reqs.set(uuid, d);\n\n        this._sendMessage(init, pId);\n\n        // Store the UUID for matching with the ACK.\n        olmData.pendingSessionUuid = uuid;\n\n        return d;\n    }\n}\n\n/**\n * Helper to ensure JSON parsing always returns an object.\n *\n * @param {string} data - The data that needs to be parsed.\n * @returns {object} - Parsed data or empty object in case of failure.\n */\nfunction safeJsonParse(data) {\n    try {\n        return JSON.parse(data);\n    } catch (e) {\n        return {};\n    }\n}\n\nOlmAdapter.events = OlmAdapterEvents;\n","import { getLogger } from '@jitsi/logger';\nimport debounce from 'lodash.debounce';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nimport { KeyHandler } from './KeyHandler';\nimport { OlmAdapter } from './OlmAdapter';\nimport { importKey, ratchet } from './crypto-utils';\n\nconst logger = getLogger(__filename);\n\n// Period which we'll wait before updating / rotating our keys when a participant\n// joins or leaves.\nconst DEBOUNCE_PERIOD = 5000;\n\n/**\n * This module integrates {@link E2EEContext} with {@link OlmAdapter} in order to distribute the keys for encryption.\n */\nexport class ManagedKeyHandler extends KeyHandler {\n    /**\n     * Build a new AutomaticKeyHandler instance, which will be used in a given conference.\n     */\n    constructor(conference) {\n        super(conference);\n\n        this._key = undefined;\n        this._conferenceJoined = false;\n\n        this._olmAdapter = new OlmAdapter(conference);\n\n        this._rotateKey = debounce(this._rotateKeyImpl, DEBOUNCE_PERIOD);\n        this._ratchetKey = debounce(this._ratchetKeyImpl, DEBOUNCE_PERIOD);\n\n        // Olm signalling events.\n        this._olmAdapter.on(\n            OlmAdapter.events.PARTICIPANT_KEY_UPDATED,\n            this._onParticipantKeyUpdated.bind(this));\n\n        this.conference.on(\n            JitsiConferenceEvents.PARTICIPANT_PROPERTY_CHANGED,\n            this._onParticipantPropertyChanged.bind(this));\n        this.conference.on(\n            JitsiConferenceEvents.USER_JOINED,\n            this._onParticipantJoined.bind(this));\n        this.conference.on(\n            JitsiConferenceEvents.USER_LEFT,\n            this._onParticipantLeft.bind(this));\n        this.conference.on(\n                JitsiConferenceEvents.CONFERENCE_JOINED,\n                () => {\n                    this._conferenceJoined = true;\n                });\n    }\n\n    /**\n     * When E2EE is enabled it initializes sessions and sets the key.\n     * Cleans up the sessions when disabled.\n     *\n     * @param {boolean} enabled - whether E2EE should be enabled or not.\n     * @returns {void}\n     */\n    async _setEnabled(enabled) {\n        if (enabled) {\n            await this._olmAdapter.initSessions();\n        } else {\n            this._olmAdapter.clearAllParticipantsSessions();\n        }\n\n        // Generate a random key in case we are enabling.\n        this._key = enabled ? this._generateKey() : false;\n\n        // Send it to others using the E2EE olm channel.\n        const index = await this._olmAdapter.updateKey(this._key);\n\n        // Set our key so we begin encrypting.\n        this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n    }\n\n    /**\n     * Handles an update in a participant's presence property.\n     *\n     * @param {JitsiParticipant} participant - The participant.\n     * @param {string} name - The name of the property that changed.\n     * @param {*} oldValue - The property's previous value.\n     * @param {*} newValue - The property's new value.\n     * @private\n     */\n    async _onParticipantPropertyChanged(participant, name, oldValue, newValue) {\n        switch (name) {\n        case 'e2ee.idKey':\n            logger.debug(`Participant ${participant.getId()} updated their id key: ${newValue}`);\n            break;\n        case 'e2ee.enabled':\n            if (!newValue && this.enabled) {\n                this._olmAdapter.clearParticipantSession(participant);\n            }\n            break;\n        }\n    }\n\n    /**\n     * Advances (using ratcheting) the current key when a new participant joins the conference.\n     * @private\n     */\n    _onParticipantJoined() {\n        if (this._conferenceJoined && this.enabled) {\n            this._ratchetKey();\n        }\n    }\n\n    /**\n     * Rotates the current key when a participant leaves the conference.\n     * @private\n     */\n    _onParticipantLeft(id) {\n        this.e2eeCtx.cleanup(id);\n\n        if (this.enabled) {\n            this._rotateKey();\n        }\n    }\n\n    /**\n     * Rotates the local key. Rotating the key implies creating a new one, then distributing it\n     * to all participants and once they all received it, start using it.\n     *\n     * @private\n     */\n    async _rotateKeyImpl() {\n        logger.debug('Rotating key');\n\n        this._key = this._generateKey();\n        const index = await this._olmAdapter.updateKey(this._key);\n\n        this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n    }\n\n    /**\n     * Advances the current key by using ratcheting.\n     *\n     * @private\n     */\n    async _ratchetKeyImpl() {\n        logger.debug('Ratchetting key');\n\n        const material = await importKey(this._key);\n        const newKey = await ratchet(material);\n\n        this._key = new Uint8Array(newKey);\n\n        const index = this._olmAdapter.updateCurrentKey(this._key);\n\n        this.e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n    }\n\n    /**\n     * Handles an update in a participant's key.\n     *\n     * @param {string} id - The participant ID.\n     * @param {Uint8Array | boolean} key - The new key for the participant.\n     * @param {Number} index - The new key's index.\n     * @private\n     */\n    _onParticipantKeyUpdated(id, key, index) {\n        logger.debug(`Participant ${id} updated their key`);\n\n        this.e2eeCtx.setKey(id, key, index);\n    }\n\n    /**\n     * Generates a new 256 bit random key.\n     *\n     * @returns {Uint8Array}\n     * @private\n     */\n    _generateKey() {\n        return window.crypto.getRandomValues(new Uint8Array(32));\n    }\n}\n","/**\n * Derives a set of keys from the master key.\n * @param {CryptoKey} material - master key to derive from\n *\n * See https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.1\n */\nexport async function deriveKeys(material) {\n    const info = new ArrayBuffer();\n    const textEncoder = new TextEncoder();\n\n    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveKey#HKDF\n    // https://developer.mozilla.org/en-US/docs/Web/API/HkdfParams\n    const encryptionKey = await crypto.subtle.deriveKey({\n        name: 'HKDF',\n        salt: textEncoder.encode('JFrameEncryptionKey'),\n        hash: 'SHA-256',\n        info\n    }, material, {\n        name: 'AES-GCM',\n        length: 128\n    }, false, [ 'encrypt', 'decrypt' ]);\n\n    return {\n        material,\n        encryptionKey\n    };\n}\n\n/**\n * Ratchets a key. See\n * https://tools.ietf.org/html/draft-omara-sframe-00#section-4.3.5.1\n * @param {CryptoKey} material - base key material\n * @returns {ArrayBuffer} - ratcheted key material\n */\nexport async function ratchet(material) {\n    const textEncoder = new TextEncoder();\n\n    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits\n    return crypto.subtle.deriveBits({\n        name: 'HKDF',\n        salt: textEncoder.encode('JFrameRatchetKey'),\n        hash: 'SHA-256',\n        info: new ArrayBuffer()\n    }, material, 256);\n}\n\n/**\n * Converts a raw key into a WebCrypto key object with default options\n * suitable for our usage.\n * @param {ArrayBuffer} keyBytes - raw key\n * @param {Array} keyUsages - key usages, see importKey documentation\n * @returns {CryptoKey} - the WebCrypto key.\n */\nexport async function importKey(keyBytes) {\n    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey\n    return crypto.subtle.importKey('raw', keyBytes, 'HKDF', false, [ 'deriveBits', 'deriveKey' ]);\n}\n","import browser from '../browser';\n\nimport { ExternallyManagedKeyHandler } from './ExternallyManagedKeyHandler';\nimport { ManagedKeyHandler } from './ManagedKeyHandler';\nimport { OlmAdapter } from './OlmAdapter';\n\n/**\n * This module integrates {@link KeyHandler} with {@link JitsiConference} in order to enable E2E encryption.\n */\nexport class E2EEncryption {\n    /**\n     * A constructor.\n     * @param {JitsiConference} conference - The conference instance for which E2E encryption is to be enabled.\n     */\n    constructor(conference) {\n        const { e2ee = {} } = conference.options.config;\n\n        this._externallyManaged = e2ee.externallyManagedKey;\n\n        if (this._externallyManaged) {\n            this._keyHandler = new ExternallyManagedKeyHandler(conference);\n        } else {\n            this._keyHandler = new ManagedKeyHandler(conference);\n        }\n    }\n\n    /**\n     * Indicates if E2EE is supported in the current platform.\n     *\n     * @param {object} config - Global configuration.\n     * @returns {boolean}\n     */\n    static isSupported(config) {\n        const { e2ee = {} } = config;\n\n        if (!e2ee.externallyManagedKey && !OlmAdapter.isSupported()) {\n            return false;\n        }\n\n        return !(config.testing && config.testing.disableE2EE)\n            && (browser.supportsInsertableStreams()\n                || (config.enableEncodedTransformSupport && browser.supportsEncodedTransform()));\n    }\n\n    /**\n     * Indicates whether E2EE is currently enabled or not.\n     *\n     * @returns {boolean}\n     */\n    isEnabled() {\n        return this._keyHandler.isEnabled();\n    }\n\n    /**\n     * Enables / disables End-To-End encryption.\n     *\n     * @param {boolean} enabled - whether E2EE should be enabled or not.\n     * @returns {void}\n     */\n    async setEnabled(enabled) {\n        await this._keyHandler.setEnabled(enabled);\n    }\n\n    /**\n     * Sets the key and index for End-to-End encryption.\n     *\n     * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n     * @param {Number} [keyInfo.index] - the index of the encryption key.\n     * @returns {void}\n     */\n    setEncryptionKey(keyInfo) {\n        this._keyHandler.setKey(keyInfo);\n    }\n}\n","/* global $ */\n\nimport { b64_sha1, Strophe } from 'strophe.js'; // eslint-disable-line camelcase\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport Listenable from '../util/Listenable';\n\n/**\n * The property\n */\nconst IDENTITY_PROPERTIES = [ 'category', 'type', 'lang', 'name' ];\nconst IDENTITY_PROPERTIES_FOR_COMPARE = [ 'category', 'type', 'lang' ];\nconst HASH = 'sha-1';\n\n/**\n *\n * @param a\n * @param b\n */\nfunction compareIdentities(a, b) {\n    let res = 0;\n\n    IDENTITY_PROPERTIES_FOR_COMPARE.some(key =>\n        (res = ((a[key] > b[key]) && 1) || ((a[key] < b[key]) && -1)) !== 0\n    );\n\n    return res;\n}\n\n/**\n * Produces a sha-1 from provided identity and features values.\n *\n * @param {Array<Object>} identities - The identity objects.\n * @param {Array<string>} features - The features.\n * @returns {string}\n */\nfunction generateSha(identities, features) {\n    const sortedIdentities = identities.sort(compareIdentities).reduce(\n        (accumulatedValue, identity) => `${\n            IDENTITY_PROPERTIES.reduce(\n                (tmp, key, idx) =>\n                    tmp\n                        + (idx === 0 ? '' : '/')\n                        + (identity[key] ? identity[key] : ''),\n                '')\n        }<`, '');\n    const sortedFeatures = features.sort().reduce(\n        (tmp, feature) => `${tmp + feature}<`, '');\n\n    return b64_sha1(sortedIdentities + sortedFeatures);\n}\n\n/**\n * Parses the disco-info node and returns the sets of features and identities.\n * @param {String} node The node with results to parse.\n * @returns {{features: Set<any>, identities: Set<any>}}\n */\nexport function parseDiscoInfo(node) {\n    const features = new Set();\n    const identities = new Set();\n\n    $(node).find('>query>feature')\n        .each((_, el) => features.add(el.getAttribute('var')));\n    $(node).find('>query>identity')\n        .each((_, el) => identities.add({\n            type: el.getAttribute('type'),\n            name: el.getAttribute('name'),\n            category: el.getAttribute('category')\n        }));\n\n    return {\n        features,\n        identities\n    };\n}\n\n/**\n * Implements xep-0115 ( http://xmpp.org/extensions/xep-0115.html )\n */\nexport default class Caps extends Listenable {\n    /**\n     * Constructs new Caps instance.\n     * @param {Strophe.Connection} connection the strophe connection object\n     * @param {String} node the value of the node attribute of the \"c\" xml node\n     * that will be sent to the other participants\n     */\n    constructor(connection = {}, node = 'http://jitsi.org/jitsimeet') {\n        super();\n        this.node = node;\n        this.disco = connection.disco;\n        if (!this.disco) {\n            throw new Error(\n                'Missing strophe-plugins '\n                + '(disco plugin is required)!');\n        }\n\n        this.version = '';\n        this.rooms = new Set();\n\n        // We keep track of features added outside the library and we publish them\n        // in the presence of the participant for simplicity, avoiding the disco info request-response.\n        this.externalFeatures = new Set();\n\n        const emuc = connection.emuc;\n\n        emuc.addListener(XMPPEvents.EMUC_ROOM_ADDED,\n            room => this._addChatRoom(room));\n        emuc.addListener(XMPPEvents.EMUC_ROOM_REMOVED,\n            room => this._removeChatRoom(room));\n        Object.keys(emuc.rooms).forEach(jid => {\n            this._addChatRoom(emuc.rooms[jid]);\n        });\n\n        Strophe.addNamespace('CAPS', 'http://jabber.org/protocol/caps');\n        this.disco.addFeature(Strophe.NS.CAPS);\n    }\n\n    /**\n     * Adds new feature to the list of supported features for the local\n     * participant\n     * @param {String} feature the name of the feature.\n     * @param {boolean} submit if true - new presence with updated \"c\" node\n     * will be sent.\n     * @param {boolean} external whether this feature was added externally to the library.\n     * We put features used directly by the clients (is jibri, remote-control enabled etc.) in the presence\n     * to avoid additional disco-info queries by those clients.\n     */\n    addFeature(feature, submit = false, external = false) {\n        this.disco.addFeature(feature);\n        this._generateVersion();\n\n        if (external && !this.externalFeatures.has(feature)) {\n            this.externalFeatures.add(feature);\n            this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));\n        }\n\n        if (submit) {\n            this.submit();\n        }\n    }\n\n    /**\n     * Removes a feature from the list of supported features for the local\n     * participant\n     * @param {String} feature the name of the feature.\n     * @param {boolean} submit if true - new presence with updated \"c\" node\n     * will be sent.\n     * @param {boolean} external whether this feature was added externally to the library.\n     */\n    removeFeature(feature, submit = false, external = false) {\n        this.disco.removeFeature(feature);\n        this._generateVersion();\n\n        if (external && this.externalFeatures.has(feature)) {\n            this.externalFeatures.delete(feature);\n            this.rooms.forEach(room => this._updateRoomWithExternalFeatures(room));\n        }\n\n        if (submit) {\n            this.submit();\n        }\n    }\n\n    /**\n     * Sends new presence stanza for every room from the list of rooms.\n     */\n    submit() {\n        this.rooms.forEach(room => room.sendPresence());\n    }\n\n    /**\n     * Updates the presences in the room based on the current values in externalFeatures.\n     * @param {ChatRoom} room the room to update.\n     * @private\n     */\n    _updateRoomWithExternalFeatures(room) {\n        if (this.externalFeatures.size === 0) {\n            room.removeFromPresence('features');\n        } else {\n            const children = [];\n\n            this.externalFeatures.forEach(f => {\n                children.push({\n                    'tagName': 'feature',\n                    attributes: { 'var': f }\n                });\n            });\n\n            room.addOrReplaceInPresence('features', { children });\n        }\n    }\n\n    /**\n     * Returns a set with the features for a host.\n     * @param {String} jid the jid of the host\n     * @param {int} timeout the timeout in ms for reply from the host.\n     * @returns {Promise<Set<String>, Error>}\n     */\n    getFeaturesAndIdentities(jid, node, timeout = 5000) {\n        return this._getDiscoInfo(jid, node, timeout);\n    }\n\n    /**\n     * Returns a set with the features and identities for a host.\n     * @param {String} jid the jid of the host\n     * @param {String|null} node the node to query\n     * @param {int} timeout the timeout in ms for reply from the host.\n     * @returns {Promise<Object>}\n     * @private\n     */\n    _getDiscoInfo(jid, node, timeout) {\n        return new Promise((resolve, reject) =>\n            this.disco.info(jid, node, response => {\n                resolve(parseDiscoInfo(response));\n            }, reject, timeout)\n        );\n    }\n\n    /**\n     * Adds ChatRoom instance to the list of rooms. Adds listeners to the room\n     * and adds \"c\" element to the presences of the room.\n     * @param {ChatRoom} room the room.\n     */\n    _addChatRoom(room) {\n        this.rooms.add(room);\n        this._fixChatRoomPresenceMap(room);\n\n        this._updateRoomWithExternalFeatures(room);\n    }\n\n    /**\n     * Removes ChatRoom instance from the list of rooms. Removes listeners\n     * added from the Caps class.\n     * @param {ChatRoom} room the room.\n     */\n    _removeChatRoom(room) {\n        this.rooms.delete(room);\n    }\n\n    /**\n     * Creates/updates the \"c\" xml node into the presence of the passed room.\n     * @param {ChatRoom} room the room.\n     */\n    _fixChatRoomPresenceMap(room) {\n        room.addOrReplaceInPresence('c', {\n            attributes: {\n                xmlns: Strophe.NS.CAPS,\n                hash: HASH,\n                node: this.node,\n                ver: this.version\n            }\n        });\n    }\n\n    /**\n     * Handles this.version changes.\n     */\n    _notifyVersionChanged() {\n        // update the version for all rooms\n        this.rooms.forEach(room => this._fixChatRoomPresenceMap(room));\n    }\n\n    /**\n     * Generates the value for the \"ver\" attribute.\n     */\n    _generateVersion() {\n        this.version\n            = generateSha(this.disco._identities, this.disco._features);\n\n        this._notifyVersionChanged();\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport Listenable from '../util/Listenable';\n\nexport const NETWORK_INFO_EVENT = 'NETWORK_INFO_CHANGED';\n\nconst logger = getLogger(__filename);\n\n/**\n * Module provides information about the current status of the internet\n * connection. Lib-jitsi-meet doesn't have any logic for detecting internet\n * online/offline, but rather it relies on the information supplied by the app\n * that uses it. By default the online state is assumed and the lib acts as if\n * it was connected. See {@link JitsiMeetJS.setNetworkInfo}.\n */\nexport class NetworkInfo extends Listenable {\n    /**\n     * Creates new {@link NetworkInfo} instance.\n     */\n    constructor() {\n        super();\n        this._current = {\n            isOnline: true\n        };\n    }\n\n    /**\n     * Updates the network info state.\n     * @param {boolean} isOnline - {@code true} if internet is online or {@code false} otherwise.\n     */\n    updateNetworkInfo({ isOnline }) {\n        logger.debug('updateNetworkInfo', { isOnline });\n        this._current = {\n            isOnline: isOnline === true\n        };\n        this.eventEmitter.emit(NETWORK_INFO_EVENT, this._current);\n    }\n\n    /**\n     * Returns the online/offline internet status. By default the value is {@code true} and changes only if\n     * the lib's user wires the state through {@link JitsiMeetJS.setNetworkInfo} like the jitsi-meet does. Because of\n     * that any logic should still assume that the internet may be offline and should handle the failure gracefully.\n     * It's only a good hint in the other way around: to pause internet operations until it comes back online.\n     * @returns {boolean}\n     */\n    isOnline() {\n        return this._current.isOnline === true;\n    }\n}\n\nconst networkInfo = new NetworkInfo();\n\nexport default networkInfo;\n","import { getLogger } from '@jitsi/logger';\n\nimport {\n    default as NetworkInfo,\n    NETWORK_INFO_EVENT\n} from '../connectivity/NetworkInfo';\nimport { getJitterDelay } from '../util/Retry';\n\nconst logger = getLogger(__filename);\n\n/**\n * The class contains the logic for triggering connection resume via XEP-0198 stream management.\n * It does two things, the first one is it tracks the internet online/offline status and it makes sure that\n * the reconnect is attempted only while online. The seconds thing is that it tracks the retry attempts and extends\n * the retry interval using the full jitter pattern.\n */\nexport default class ResumeTask {\n    /**\n     * Initializes new {@code RetryTask}.\n     * @param {Strophe.Connection} stropheConnection - The Strophe connection instance.\n     */\n    constructor(stropheConnection) {\n        this._stropheConn = stropheConnection;\n\n        /**\n         * The counter increased before each resume retry attempt, used to calculate exponential backoff.\n         * @type {number}\n         * @private\n         */\n        this._resumeRetryN = 0;\n\n        this._retryDelay = undefined;\n    }\n\n    /**\n     * @returns {number|undefined} - How much the app will wait before trying to resume the XMPP connection. When\n     * 'undefined' it means that no resume task was not scheduled.\n     */\n    get retryDelay() {\n        return this._retryDelay;\n    }\n\n    /**\n     * Called by {@link XmppConnection} when the connection drops and it's a signal it wants to schedule a reconnect.\n     *\n     * @returns {void}\n     */\n    schedule() {\n        this._cancelResume();\n\n        this._resumeRetryN += 1;\n\n        this._networkOnlineListener\n            = NetworkInfo.addEventListener(\n                NETWORK_INFO_EVENT,\n                ({ isOnline }) => {\n                    if (isOnline) {\n                        this._scheduleResume();\n                    } else {\n                        this._cancelResume();\n                    }\n                });\n\n        NetworkInfo.isOnline() && this._scheduleResume();\n    }\n\n    /**\n     * Schedules a delayed timeout which will execute the resume action.\n     * @private\n     * @returns {void}\n     */\n    _scheduleResume() {\n        if (this._resumeTimeout) {\n\n            // NO-OP\n            return;\n        }\n\n        // The retry delay will be:\n        //   1st retry: 1.5s - 3s\n        //   2nd retry: 3s - 9s\n        //   3rd and next retry: 4.5s - 27s\n        this._resumeRetryN = Math.min(3, this._resumeRetryN);\n        this._retryDelay = getJitterDelay(\n            /* retry */ this._resumeRetryN,\n            /* minDelay */ this._resumeRetryN * 1500,\n            3);\n\n        logger.info(`Will try to resume the XMPP connection in ${this.retryDelay}ms`);\n\n        this._resumeTimeout = setTimeout(() => this._resumeConnection(), this.retryDelay);\n    }\n\n    /**\n     * Cancels the delayed resume task.\n     *\n     * @private\n     * @returns {void}\n     */\n    _cancelResume() {\n        if (this._resumeTimeout) {\n            logger.info('Canceling connection resume task');\n            clearTimeout(this._resumeTimeout);\n            this._resumeTimeout = undefined;\n            this._retryDelay = undefined;\n        }\n    }\n\n    /**\n     * Resumes the XMPP connection using the stream management plugin.\n     *\n     * @private\n     * @returns {void}\n     */\n    _resumeConnection() {\n        const { streamManagement } = this._stropheConn;\n        const resumeToken = streamManagement.getResumeToken();\n\n        // Things may have changed since when the task was scheduled\n        if (!resumeToken) {\n            return;\n        }\n\n        logger.info('Trying to resume the XMPP connection');\n\n        const url = new URL(this._stropheConn.service);\n        let { search } = url;\n        const pattern = /(previd=)([\\w-]+)/;\n        const oldToken = search.match(pattern);\n\n        // Replace previd if the previd value has changed.\n        if (oldToken && oldToken.indexOf(resumeToken) === -1) {\n            search = search.replace(pattern, `$1${resumeToken}`);\n\n        // Append previd if it doesn't exist.\n        } else if (!oldToken) {\n            search += search.indexOf('?') === -1 ? `?previd=${resumeToken}` : `&previd=${resumeToken}`;\n        }\n\n        url.search = search;\n\n        this._stropheConn.service = url.toString();\n\n        streamManagement.resume();\n    }\n\n    /**\n     * Cancels the retry task. It's called by {@link XmppConnection} when it's no longer interested in reconnecting for\n     * example when the disconnect method is called.\n     *\n     * @returns {void}\n     */\n    cancel() {\n        this._cancelResume();\n        this._resumeRetryN = 0;\n        if (this._networkOnlineListener) {\n            this._networkOnlineListener();\n            this._networkOnlineListener = null;\n        }\n    }\n}\n","/**\n* Gets next timeout using the full jitter pattern.\n*\n* NOTE that there are no checks for argument correctness, so either do the math or use defaults.\n*\n* @param {number} retry - The retry number.\n* @param {number} minDelay - The minimal delay in milliseconds.\n* @param {number} base - The exponent base.\n* @returns {number} - The amount of waiting before trying another time given in milliseconds.\n* @private\n*/\nexport function getJitterDelay(retry, minDelay = 500, base = 2) {\n    return Math.floor((Math.random() * ((Math.pow(base, retry) * 1000) - minDelay)) + minDelay);\n}\n","/**\n * Attaches to the {@link Strophe.Connection.rawInput} which is called whenever any data is received from the server.\n */\nexport default class LastRequestTracker {\n    /**\n     * Initializes new instance.\n     */\n    constructor() {\n        this._lastSuccess = null;\n        this._lastFailedMessage = null;\n    }\n\n    /**\n     * Starts tracking requests on the given connection.\n     *\n     * @param {XmppConnection} xmppConnection - The XMPP connection which manages the given {@code stropheConnection}.\n     * @param {Object} stropheConnection - Strophe connection instance.\n     */\n    startTracking(xmppConnection, stropheConnection) {\n        const originalRawInput = stropheConnection.rawInput;\n\n        stropheConnection.rawInput = (...args) => {\n            const rawMessage = args[0];\n\n            if (rawMessage.includes('failure')) {\n                this._lastFailedMessage = rawMessage;\n            }\n\n            // It's okay to use rawInput callback only once the connection has been established, otherwise it will\n            // treat 'item-not-found' or other connection error on websocket reconnect as successful stanza received.\n            if (xmppConnection.connected) {\n                this._lastSuccess = Date.now();\n            }\n            originalRawInput.apply(stropheConnection, args);\n        };\n    }\n\n    /**\n     * Returns the last raw failed incoming message on the xmpp connection.\n     *\n     * @returns {string|null}\n     */\n    getLastFailedMessage() {\n        return this._lastFailedMessage;\n    }\n\n    /**\n     * Returns how many milliseconds have passed since the last successful BOSH request.\n     *\n     * @returns {number|null}\n     */\n    getTimeSinceLastSuccess() {\n        return this._lastSuccess\n            ? Date.now() - this._lastSuccess\n            : null;\n    }\n}\n","import Listenable from '../util/Listenable';\n\n/**\n * Creates ConnectionPlugin class that extends the passed class.\n * @param {Class} base the definition of the class that will be extended by\n * ConnectionPlugin\n */\nfunction getConnectionPluginDefinition(base = class {}) {\n    /**\n     * Base class for strophe connection plugins.\n     */\n    return class extends base {\n        /**\n         *\n         */\n        constructor(...args) {\n            super(...args);\n            this.connection = null;\n        }\n\n        /**\n         *\n         * @param connection\n         */\n        init(connection) {\n            this.connection = connection;\n        }\n    };\n}\n\n/**\n * ConnectionPlugin class.\n */\nexport default getConnectionPluginDefinition();\n\n/**\n * ConnectionPlugin class that extends Listenable.\n */\nexport const ConnectionPluginListenable\n    = getConnectionPluginDefinition(Listenable);\n","import { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * Default ping every 10 sec\n */\nconst PING_DEFAULT_INTERVAL = 10000;\n\n/**\n * Default ping timeout error after 5 sec of waiting.\n */\nconst PING_DEFAULT_TIMEOUT = 5000;\n\n/**\n * Default value for how many ping failures will be tolerated before the WebSocket connection is killed.\n * The worst case scenario in case of ping timing out without a response is (25 seconds at the time of this writing):\n * PING_THRESHOLD * PING_INTERVAL + PING_TIMEOUT\n */\nconst PING_DEFAULT_THRESHOLD = 2;\n\n/**\n * XEP-0199 ping plugin.\n *\n * Registers \"urn:xmpp:ping\" namespace under Strophe.NS.PING.\n */\nexport default class PingConnectionPlugin extends ConnectionPlugin {\n    /**\n     * Constructs new object\n     * @param {Object} options\n     * @param {Function} options.onPingThresholdExceeded - Callback called when ping fails too many times (controlled\n     * by the {@link PING_THRESHOLD} constant).\n     * @param {Function} options._getTimeSinceLastServerResponse - A function to obtain the last seen\n     * response from the server.\n     * @param {Object} options.pingOptions - The ping options if any.\n     * @constructor\n     */\n    constructor({ getTimeSinceLastServerResponse, onPingThresholdExceeded, pingOptions = {} }) {\n        super();\n        this.failedPings = 0;\n        this._onPingThresholdExceeded = onPingThresholdExceeded;\n        this._getTimeSinceLastServerResponse = getTimeSinceLastServerResponse;\n\n        this.pingInterval = typeof pingOptions.interval === 'number' ? pingOptions.interval : PING_DEFAULT_INTERVAL;\n        this.pingTimeout = typeof pingOptions.timeout === 'number' ? pingOptions.timeout : PING_DEFAULT_TIMEOUT;\n        this.pingThreshold = typeof pingOptions.threshold === 'number'\n            ? pingOptions.threshold : PING_DEFAULT_THRESHOLD;\n\n        // The number of timestamps of send pings to keep.\n        // The current value is 2 minutes.\n        this.pingTimestampsToKeep = Math.round(120000 / this.pingInterval);\n        this.pingExecIntervals = new Array(this.pingTimestampsToKeep);\n    }\n\n    /**\n     * Initializes the plugin. Method called by Strophe.\n     * @param connection Strophe connection instance.\n     */\n    init(connection) {\n        super.init(connection);\n        Strophe.addNamespace('PING', 'urn:xmpp:ping');\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     * Sends \"ping\" to given <tt>jid</tt>\n     * @param jid the JID to which ping request will be sent.\n     * @param success callback called on success.\n     * @param error callback called on error.\n     * @param timeout ms how long are we going to wait for the response. On\n     * timeout <tt>error<//t> callback is called with undefined error argument.\n     */\n    ping(jid, success, error, timeout) {\n        this._addPingExecutionTimestamp();\n\n        const iq = $iq({\n            type: 'get',\n            to: jid\n        });\n\n        iq.c('ping', { xmlns: Strophe.NS.PING });\n        this.connection.sendIQ2(iq, { timeout })\n            .then(success, error);\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Starts to send ping in given interval to specified remote JID.\n     * This plugin supports only one such task and <tt>stopInterval</tt>\n     * must be called before starting a new one.\n     * @param remoteJid remote JID to which ping requests will be sent to.\n     */\n    startInterval(remoteJid) {\n        clearInterval(this.intervalId);\n        this.intervalId = window.setInterval(() => {\n\n            // when there were some server responses in the interval since the last time we checked (_lastServerCheck)\n            // let's skip the ping\n\n            const now = Date.now();\n\n            if (this._getTimeSinceLastServerResponse() < now - this._lastServerCheck) {\n                // do this just to keep in sync the intervals so we can detect suspended device\n                this._addPingExecutionTimestamp();\n\n                this._lastServerCheck = now;\n                this.failedPings = 0;\n\n                return;\n            }\n\n            this.ping(remoteJid, () => {\n                // server response is measured on raw input and ping response time is measured after all the xmpp\n                // processing is done in js, so there can be some misalignment when we do the check above.\n                // That's why we store the last time we got the response\n                this._lastServerCheck = this._getTimeSinceLastServerResponse() + Date.now();\n\n                this.failedPings = 0;\n            }, error => {\n                this.failedPings += 1;\n                const errmsg = `Ping ${error ? 'error' : 'timeout'}`;\n\n                if (this.failedPings >= this.pingThreshold) {\n                    GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n                    logger.error(errmsg, error);\n                    this._onPingThresholdExceeded && this._onPingThresholdExceeded();\n                } else {\n                    logger.warn(errmsg, error);\n                }\n            }, this.pingTimeout);\n        }, this.pingInterval);\n        logger.info(`XMPP pings will be sent every ${this.pingInterval} ms`);\n    }\n\n    /**\n     * Stops current \"ping\"  interval task.\n     */\n    stopInterval() {\n        if (this.intervalId) {\n            window.clearInterval(this.intervalId);\n            this.intervalId = null;\n            this.failedPings = 0;\n            logger.info('Ping interval cleared');\n        }\n    }\n\n    /**\n     * Adds the current time to the array of send ping timestamps.\n     * @private\n     */\n    _addPingExecutionTimestamp() {\n        this.pingExecIntervals.push(new Date().getTime());\n\n        // keep array length to PING_TIMESTAMPS_TO_KEEP\n        if (this.pingExecIntervals.length > this.pingTimestampsToKeep) {\n            this.pingExecIntervals.shift();\n        }\n    }\n\n    /**\n     * Returns the maximum time between the recent sent pings, if there is a\n     * big value it means the computer was inactive for some time(suspended).\n     * Checks the maximum gap between sending pings, considering and the\n     * current time. Trying to detect computer inactivity (sleep).\n     *\n     * @returns {int} the time ping was suspended, if it was not 0 is returned.\n     */\n    getPingSuspendTime() {\n        const pingIntervals = this.pingExecIntervals.slice();\n\n        // we need current time, as if ping was sent now\n        // if computer sleeps we will get correct interval after next\n        // scheduled ping, bet we sometimes need that interval before waiting\n        // for the next ping, on closing the connection on error.\n        pingIntervals.push(new Date().getTime());\n\n        let maxInterval = 0;\n        let previousTS = pingIntervals[0];\n\n        pingIntervals.forEach(e => {\n            const currentInterval = e - previousTS;\n\n            if (currentInterval > maxInterval) {\n                maxInterval = currentInterval;\n            }\n\n            previousTS = e;\n        });\n\n        // remove the interval between the ping sent\n        // this way in normal execution there is no suspend and the return\n        // will be 0 or close to 0.\n        maxInterval -= this.pingInterval;\n\n        // make sure we do not return less than 0\n        return Math.max(maxInterval, 0);\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $pres, Strophe } from 'strophe.js';\nimport 'strophejs-plugin-stream-management';\n\nimport Listenable from '../util/Listenable';\n\nimport ResumeTask from './ResumeTask';\nimport LastSuccessTracker from './StropheLastSuccess';\nimport PingConnectionPlugin from './strophe.ping';\n\nconst logger = getLogger(__filename);\n\n/**\n * The lib-jitsi-meet layer for {@link Strophe.Connection}.\n */\nexport default class XmppConnection extends Listenable {\n    /**\n     * The list of {@link XmppConnection} events.\n     *\n     * @returns {Object}\n     */\n    static get Events() {\n        return {\n            CONN_STATUS_CHANGED: 'CONN_STATUS_CHANGED',\n            CONN_SHARD_CHANGED: 'CONN_SHARD_CHANGED'\n        };\n    }\n\n    /**\n     * The list of Xmpp connection statuses.\n     *\n     * @returns {Strophe.Status}\n     */\n    static get Status() {\n        return Strophe.Status;\n    }\n\n    /**\n     * Initializes new connection instance.\n     *\n     * @param {Object} options\n     * @param {String} options.serviceUrl - The BOSH or WebSocket service URL.\n     * @param {String} options.shard - The BOSH or WebSocket is connecting to this shard.\n     * Useful for detecting when shard changes.\n     * @param {String} [options.enableWebsocketResume=true] - True/false to control the stream resumption functionality.\n     * It will enable automatically by default if supported by the XMPP server.\n     * @param {Number} [options.websocketKeepAlive=60000] - The websocket keep alive interval.\n     * It's the interval + a up to a minute of jitter. Pass -1 to disable.\n     * The keep alive is HTTP GET request to {@link options.serviceUrl} or to {@link options.websocketKeepAliveUrl}.\n     * @param {Number} [options.websocketKeepAliveUrl] - The websocket keep alive url to use if any,\n     * if missing the serviceUrl url will be used.\n     * @param {Object} [options.xmppPing] - The xmpp ping settings.\n     */\n    constructor({ enableWebsocketResume, websocketKeepAlive, websocketKeepAliveUrl, serviceUrl, shard, xmppPing }) {\n        super();\n        this._options = {\n            enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,\n            pingOptions: xmppPing,\n            shard,\n            websocketKeepAlive: typeof websocketKeepAlive === 'undefined' ? 60 * 1000 : Number(websocketKeepAlive),\n            websocketKeepAliveUrl\n        };\n\n        this._stropheConn = new Strophe.Connection(serviceUrl);\n        this._usesWebsocket = serviceUrl.startsWith('ws:') || serviceUrl.startsWith('wss:');\n\n        // The default maxRetries is 5, which is too long.\n        this._stropheConn.maxRetries = 3;\n\n        this._rawInputTracker = new LastSuccessTracker();\n        this._rawInputTracker.startTracking(this, this._stropheConn);\n\n        this._resumeTask = new ResumeTask(this._stropheConn);\n\n        /**\n         * @typedef DeferredSendIQ Object\n         * @property {Element} iq - The IQ to send.\n         * @property {function} resolve - The resolve method of the deferred Promise.\n         * @property {function} reject - The reject method of the deferred Promise.\n         * @property {number} timeout - The ID of the timeout task that needs to be cleared, before sending the IQ.\n         */\n        /**\n         * Deferred IQs to be sent upon reconnect.\n         * @type {Array<DeferredSendIQ>}\n         * @private\n         */\n        this._deferredIQs = [];\n\n        // Ping plugin is mandatory for the Websocket mode to work correctly. It's used to detect when the connection\n        // is broken (WebSocket/TCP connection not closed gracefully).\n        this.addConnectionPlugin(\n            'ping',\n            new PingConnectionPlugin({\n                getTimeSinceLastServerResponse: () => this.getTimeSinceLastSuccess(),\n                onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded(),\n                pingOptions: xmppPing\n            }));\n\n        // tracks whether this is the initial connection or a reconnect\n        this._oneSuccessfulConnect = false;\n    }\n\n    /**\n     * A getter for the connected state.\n     *\n     * @returns {boolean}\n     */\n    get connected() {\n        const websocket = this._stropheConn && this._stropheConn._proto && this._stropheConn._proto.socket;\n\n        return (this._status === Strophe.Status.CONNECTED || this._status === Strophe.Status.ATTACHED)\n            && (!this.isUsingWebSocket || (websocket && websocket.readyState === WebSocket.OPEN));\n    }\n\n    /**\n     * Retrieves the feature discovery plugin instance.\n     *\n     * @returns {Strophe.Connection.disco}\n     */\n    get disco() {\n        return this._stropheConn.disco;\n    }\n\n    /**\n     * A getter for the disconnecting state.\n     *\n     * @returns {boolean}\n     */\n    get disconnecting() {\n        return this._stropheConn.disconnecting === true;\n    }\n\n    /**\n     * A getter for the domain.\n     *\n     * @returns {string|null}\n     */\n    get domain() {\n        return this._stropheConn.domain;\n    }\n\n    /**\n     * Tells if Websocket is used as the transport for the current XMPP connection. Returns true for Websocket or false\n     * for BOSH.\n     * @returns {boolean}\n     */\n    get isUsingWebSocket() {\n        return this._usesWebsocket;\n    }\n\n    /**\n     * A getter for the JID.\n     *\n     * @returns {string|null}\n     */\n    get jid() {\n        return this._stropheConn.jid;\n    }\n\n    /**\n     * Returns headers for the last BOSH response received.\n     *\n     * @returns {string}\n     */\n    get lastResponseHeaders() {\n        return this._stropheConn._proto && this._stropheConn._proto.lastResponseHeaders;\n    }\n\n    /**\n     * A getter for the logger plugin instance.\n     *\n     * @returns {*}\n     */\n    get logger() {\n        return this._stropheConn.logger;\n    }\n\n    /**\n     * A getter for the connection options.\n     *\n     * @returns {*}\n     */\n    get options() {\n        return this._stropheConn.options;\n    }\n\n    /**\n     * A getter for the domain to be used for ping.\n     */\n    get pingDomain() {\n        return this._options.pingOptions?.domain || this.domain;\n    }\n\n    /**\n     * A getter for the service URL.\n     *\n     * @returns {string}\n     */\n    get service() {\n        return this._stropheConn.service;\n    }\n\n    /**\n     * Sets new value for shard.\n     * @param value the new shard value.\n     */\n    set shard(value) {\n        this._options.shard = value;\n\n        // shard setting changed so let's schedule a new keep-alive check if connected\n        if (this._oneSuccessfulConnect) {\n            this._maybeStartWSKeepAlive();\n        }\n    }\n\n    /**\n     * Returns the current connection status.\n     *\n     * @returns {Strophe.Status}\n     */\n    get status() {\n        return this._status;\n    }\n\n    /**\n     * Adds a connection plugin to this instance.\n     *\n     * @param {string} name - The name of the plugin or rather a key under which it will be stored on this connection\n     * instance.\n     * @param {ConnectionPluginListenable} plugin - The plugin to add.\n     */\n    addConnectionPlugin(name, plugin) {\n        this[name] = plugin;\n        plugin.init(this);\n    }\n\n    /**\n     * See {@link Strophe.Connection.addHandler}\n     *\n     * @returns {void}\n     */\n    addHandler(...args) {\n        this._stropheConn.addHandler(...args);\n    }\n\n    /* eslint-disable max-params */\n    /**\n     * Wraps {@link Strophe.Connection.attach} method in order to intercept the connection status updates.\n     * See {@link Strophe.Connection.attach} for the params description.\n     *\n     * @returns {void}\n     */\n    attach(jid, sid, rid, callback, ...args) {\n        this._stropheConn.attach(jid, sid, rid, this._stropheConnectionCb.bind(this, callback), ...args);\n    }\n\n    /**\n     * Wraps Strophe.Connection.connect method in order to intercept the connection status updates.\n     * See {@link Strophe.Connection.connect} for the params description.\n     *\n     * @returns {void}\n     */\n    connect(jid, pass, callback, ...args) {\n        this._stropheConn.connect(jid, pass, this._stropheConnectionCb.bind(this, callback), ...args);\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Handles {@link Strophe.Status} updates for the current connection.\n     *\n     * @param {function} targetCallback - The callback passed by the {@link XmppConnection} consumer to one of\n     * the connect methods.\n     * @param {Strophe.Status} status - The new connection status.\n     * @param {*} args - The rest of the arguments passed by Strophe.\n     * @private\n     */\n    _stropheConnectionCb(targetCallback, status, ...args) {\n        this._status = status;\n\n        let blockCallback = false;\n\n        if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {\n            this._maybeEnableStreamResume();\n\n            // after connecting - immediately check whether shard changed,\n            // we need this only when using websockets as bosh checks headers from every response\n            if (this._usesWebsocket && this._oneSuccessfulConnect) {\n                this._keepAliveAndCheckShard();\n            }\n            this._oneSuccessfulConnect = true;\n\n            this._maybeStartWSKeepAlive();\n            this._processDeferredIQs();\n            this._resumeTask.cancel();\n            this.ping.startInterval(this._options.pingOptions?.domain || this.domain);\n        } else if (status === Strophe.Status.DISCONNECTED) {\n            this.ping.stopInterval();\n\n            // FIXME add RECONNECTING state instead of blocking the DISCONNECTED update\n            blockCallback = this._tryResumingConnection();\n            if (!blockCallback) {\n                clearTimeout(this._wsKeepAlive);\n            }\n        }\n\n        if (!blockCallback) {\n            targetCallback(status, ...args);\n            this.eventEmitter.emit(XmppConnection.Events.CONN_STATUS_CHANGED, status);\n        }\n    }\n\n    /**\n     * Clears the list of IQs and rejects deferred Promises with an error.\n     *\n     * @private\n     */\n    _clearDeferredIQs() {\n        for (const deferred of this._deferredIQs) {\n            deferred.reject(new Error('disconnect'));\n        }\n        this._deferredIQs = [];\n    }\n\n    /**\n     * The method is meant to be used for testing. It's a shortcut for closing the WebSocket.\n     *\n     * @returns {void}\n     */\n    closeWebsocket() {\n        if (this._stropheConn && this._stropheConn._proto) {\n            this._stropheConn._proto._closeSocket();\n            this._stropheConn._proto._onClose(null);\n        }\n    }\n\n    /**\n     * See {@link Strophe.Connection.disconnect}.\n     *\n     * @returns {void}\n     */\n    disconnect(...args) {\n        this._resumeTask.cancel();\n        clearTimeout(this._wsKeepAlive);\n        this._clearDeferredIQs();\n        this._stropheConn.disconnect(...args);\n    }\n\n    /**\n     * See {@link Strophe.Connection.flush}.\n     *\n     * @returns {void}\n     */\n    flush(...args) {\n        this._stropheConn.flush(...args);\n    }\n\n    /**\n     * See {@link LastRequestTracker.getTimeSinceLastSuccess}.\n     *\n     * @returns {number|null}\n     */\n    getTimeSinceLastSuccess() {\n        return this._rawInputTracker.getTimeSinceLastSuccess();\n    }\n\n    /**\n     * See {@link LastRequestTracker.getLastFailedMessage}.\n     *\n     * @returns {string|null}\n     */\n    getLastFailedMessage() {\n        return this._rawInputTracker.getLastFailedMessage();\n    }\n\n    /**\n     * Requests a resume token from the server if enabled and all requirements are met.\n     *\n     * @private\n     */\n    _maybeEnableStreamResume() {\n        if (!this._options.enableWebsocketResume) {\n\n            return;\n        }\n\n        const { streamManagement } = this._stropheConn;\n\n        if (!this.isUsingWebSocket) {\n            logger.warn('Stream resume enabled, but WebSockets are not enabled');\n        } else if (!streamManagement) {\n            logger.warn('Stream resume enabled, but Strophe streamManagement plugin is not installed');\n        } else if (!streamManagement.isSupported()) {\n            logger.warn('Stream resume enabled, but XEP-0198 is not supported by the server');\n        } else if (!streamManagement.getResumeToken()) {\n            logger.info('Enabling XEP-0198 stream management');\n            streamManagement.enable(/* resume */ true);\n        }\n    }\n\n    /**\n     * Starts the Websocket keep alive if enabled.\n     *\n     * @private\n     * @returns {void}\n     */\n    _maybeStartWSKeepAlive() {\n        const { websocketKeepAlive } = this._options;\n\n        if (this._usesWebsocket && websocketKeepAlive > 0) {\n            this._wsKeepAlive || logger.info(`WebSocket keep alive interval: ${websocketKeepAlive}ms`);\n            clearTimeout(this._wsKeepAlive);\n\n            const intervalWithJitter = /* base */ websocketKeepAlive + /* jitter */ (Math.random() * 60 * 1000);\n\n            logger.debug(`Scheduling next WebSocket keep-alive in ${intervalWithJitter}ms`);\n\n            this._wsKeepAlive = setTimeout(\n                () => this._keepAliveAndCheckShard()\n                    .then(() => this._maybeStartWSKeepAlive()),\n                intervalWithJitter);\n        }\n    }\n\n    /**\n     * Do a http GET to the shard and if shard change will throw an event.\n     *\n     * @private\n     * @returns {Promise}\n     */\n    _keepAliveAndCheckShard() {\n        const { shard, websocketKeepAliveUrl } = this._options;\n        const url = websocketKeepAliveUrl ? websocketKeepAliveUrl\n            : this.service.replace('wss://', 'https://').replace('ws://', 'http://');\n\n        return fetch(url)\n            .then(response => {\n\n                // skips header checking if there is no info in options\n                if (!shard) {\n                    return;\n                }\n\n                const responseShard = response.headers.get('x-jitsi-shard');\n\n                if (responseShard !== shard) {\n                    logger.error(\n                        `Detected that shard changed from ${shard} to ${responseShard}`);\n                    this.eventEmitter.emit(XmppConnection.Events.CONN_SHARD_CHANGED);\n                }\n            })\n            .catch(error => {\n                logger.error(`Websocket Keep alive failed for url: ${url}`, { error });\n            });\n    }\n\n    /**\n     * Goes over the list of {@link DeferredSendIQ} tasks and sends them.\n     *\n     * @private\n     * @returns {void}\n     */\n    _processDeferredIQs() {\n        for (const deferred of this._deferredIQs) {\n            if (deferred.iq) {\n                clearTimeout(deferred.timeout);\n\n                const timeLeft = Date.now() - deferred.start;\n\n                this.sendIQ(\n                    deferred.iq,\n                    result => deferred.resolve(result),\n                    error => deferred.reject(error),\n                    timeLeft);\n            }\n        }\n\n        this._deferredIQs = [];\n    }\n\n    /**\n     * Send a stanza. This function is called to push data onto the send queue to go out over the wire.\n     *\n     * @param {Element|Strophe.Builder} stanza - The stanza to send.\n     * @returns {void}\n     */\n    send(stanza) {\n        if (!this.connected) {\n            throw new Error('Not connected');\n        }\n        this._stropheConn.send(stanza);\n    }\n\n    /**\n     * Helper function to send IQ stanzas.\n     *\n     * @param {Element} elem - The stanza to send.\n     * @param {Function} callback - The callback function for a successful request.\n     * @param {Function} errback - The callback function for a failed or timed out request.  On timeout, the stanza will\n     * be null.\n     * @param {number} timeout - The time specified in milliseconds for a timeout to occur.\n     * @returns {number} - The id used to send the IQ.\n     */\n    sendIQ(elem, callback, errback, timeout) {\n        if (!this.connected) {\n            errback('Not connected');\n\n            return;\n        }\n\n        return this._stropheConn.sendIQ(elem, callback, errback, timeout);\n    }\n\n    /**\n     * Sends an IQ immediately if connected or puts it on the send queue otherwise(in contrary to other send methods\n     * which would fail immediately if disconnected).\n     *\n     * @param {Element} iq - The IQ to send.\n     * @param {number} timeout - How long to wait for the response. The time when the connection is reconnecting is\n     * included, which means that the IQ may never be sent and still fail with a timeout.\n     */\n    sendIQ2(iq, { timeout }) {\n        return new Promise((resolve, reject) => {\n            if (this.connected) {\n                this.sendIQ(\n                    iq,\n                    result => resolve(result),\n                    error => reject(error),\n                    timeout);\n            } else {\n                const deferred = {\n                    iq,\n                    resolve,\n                    reject,\n                    start: Date.now(),\n                    timeout: setTimeout(() => {\n                        // clears the IQ on timeout and invalidates the deferred task\n                        deferred.iq = undefined;\n\n                        // Strophe calls with undefined on timeout\n                        reject(undefined);\n                    }, timeout)\n                };\n\n                this._deferredIQs.push(deferred);\n            }\n        });\n    }\n\n    /**\n     * Called by the ping plugin when ping fails too many times.\n     *\n     * @returns {void}\n     */\n    _onPingErrorThresholdExceeded() {\n        if (this.isUsingWebSocket) {\n            logger.warn('Ping error threshold exceeded - killing the WebSocket');\n            this.closeWebsocket();\n        }\n    }\n\n    /**\n     *  Helper function to send presence stanzas. The main benefit is for sending presence stanzas for which you expect\n     *  a responding presence stanza with the same id (for example when leaving a chat room).\n     *\n     * @param {Element} elem - The stanza to send.\n     * @param {Function} callback - The callback function for a successful request.\n     * @param {Function} errback - The callback function for a failed or timed out request. On timeout, the stanza will\n     * be null.\n     * @param {number} timeout - The time specified in milliseconds for a timeout to occur.\n     * @returns {number} - The id used to send the presence.\n     */\n    sendPresence(elem, callback, errback, timeout) {\n        if (!this.connected) {\n            errback('Not connected');\n\n            return;\n        }\n        this._stropheConn.sendPresence(elem, callback, errback, timeout);\n    }\n\n    /**\n     * The method gracefully closes the BOSH connection by using 'navigator.sendBeacon'.\n     *\n     * @returns {boolean} - true if the beacon was sent.\n     */\n    sendUnavailableBeacon() {\n        if (!navigator.sendBeacon || this._stropheConn.disconnecting || !this._stropheConn.connected) {\n            return false;\n        }\n\n        this._stropheConn._changeConnectStatus(Strophe.Status.DISCONNECTING);\n        this._stropheConn.disconnecting = true;\n\n        const body = this._stropheConn._proto._buildBody()\n            .attrs({\n                type: 'terminate'\n            });\n        const pres = $pres({\n            xmlns: Strophe.NS.CLIENT,\n            type: 'unavailable'\n        });\n\n        body.cnode(pres.tree());\n\n        const res = navigator.sendBeacon(\n            this.service.indexOf('https://') === -1 ? `https:${this.service}` : this.service,\n            Strophe.serialize(body.tree()));\n\n        logger.info(`Successfully send unavailable beacon ${res}`);\n\n        this._stropheConn._proto._abortAllRequests();\n        this._stropheConn._doDisconnect();\n\n        return true;\n    }\n\n    /**\n     * Tries to use stream management plugin to resume dropped XMPP connection. The streamManagement plugin clears\n     * the resume token if any connection error occurs which would put it in unrecoverable state, so as long as\n     * the token is present it means the connection can be resumed.\n     *\n     * @private\n     * @returns {boolean}\n     */\n    _tryResumingConnection() {\n        const { streamManagement } = this._stropheConn;\n        const resumeToken = streamManagement && streamManagement.getResumeToken();\n\n        if (resumeToken) {\n            this._resumeTask.schedule();\n\n            return true;\n        }\n\n        return false;\n    }\n}\n","export enum JitsiTranscriptionStatus {\n    /**\n     * The transcription is on.\n     */\n    ON = 'on',\n\n    /**\n     * The transcription is off.\n     */\n    OFF = 'off'\n}\n\n// exported for backward compatibility\nexport const ON = JitsiTranscriptionStatus.ON;\nexport const OFF = JitsiTranscriptionStatus.OFF;\n","import { getLogger } from '@jitsi/logger';\nimport { $msg } from 'strophe.js';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * The AVModeration logic.\n */\nexport default class AVModeration {\n\n    /**\n     * Constructs AV moderation room.\n     *\n     * @param {ChatRoom} room the main room.\n     */\n    constructor(room) {\n        this._xmpp = room.xmpp;\n\n        this._mainRoom = room;\n\n        this._moderationEnabledByType = {\n            [MediaType.AUDIO]: false,\n            [MediaType.VIDEO]: false\n        };\n\n        this._whitelistAudio = [];\n        this._whitelistVideo = [];\n\n        this._onMessage = this._onMessage.bind(this);\n        this._xmpp.addListener(XMPPEvents.AV_MODERATION_RECEIVED, this._onMessage);\n    }\n\n    /**\n     * Stops listening for events.\n     */\n    dispose() {\n        this._xmpp.removeListener(XMPPEvents.AV_MODERATION_RECEIVED, this._onMessage);\n    }\n\n    /**\n     * Whether AV moderation is supported on backend.\n     *\n     * @returns {boolean} whether AV moderation is supported on backend.\n     */\n    isSupported() {\n        return Boolean(this._xmpp.avModerationComponentAddress);\n    }\n\n    /**\n     * Enables or disables AV Moderation by sending a msg with command to the component.\n     */\n    enable(state, mediaType) {\n        if (!this.isSupported() || !this._mainRoom.isModerator()) {\n            logger.error(`Cannot enable:${state} AV moderation supported:${this.isSupported()},\n                moderator:${this._mainRoom.isModerator()}`);\n\n            return;\n        }\n\n        if (state === this._moderationEnabledByType[mediaType]) {\n            logger.warn(`Moderation already in state:${state} for mediaType:${mediaType}`);\n\n            return;\n        }\n\n        // send the enable/disable message\n        const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n        msg.c('av_moderation', {\n            enable: state,\n            mediaType\n        }).up();\n\n        this._xmpp.connection.send(msg);\n    }\n\n    /**\n     * Approves that a participant can unmute by sending a msg with its jid to the component.\n     */\n    approve(mediaType, jid) {\n        if (!this.isSupported() || !this._mainRoom.isModerator()) {\n            logger.error(`Cannot approve in AV moderation supported:${this.isSupported()},\n                moderator:${this._mainRoom.isModerator()}`);\n\n            return;\n        }\n\n        // send a message to whitelist the jid and approve it to unmute\n        const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n        msg.c('av_moderation', {\n            mediaType,\n            jidToWhitelist: jid }).up();\n\n        this._xmpp.connection.send(msg);\n    }\n\n    /**\n     * Rejects that a participant can unmute by sending a msg with its jid to the component.\n     */\n    reject(mediaType, jid) {\n        if (!this.isSupported() || !this._mainRoom.isModerator()) {\n            logger.error(`Cannot reject in AV moderation supported:${this.isSupported()},\n                moderator:${this._mainRoom.isModerator()}`);\n\n            return;\n        }\n\n        // send a message to remove from whitelist the jid and reject it to unmute\n        const msg = $msg({ to: this._xmpp.avModerationComponentAddress });\n\n        msg.c('av_moderation', {\n            mediaType,\n            jidToBlacklist: jid\n        }).up();\n\n        this._xmpp.connection.send(msg);\n    }\n\n    /**\n     * Receives av_moderation parsed messages as json.\n     * @param obj the parsed json content of the message to process.\n     * @private\n     */\n    _onMessage(obj) {\n        const { removed, mediaType: media, enabled, approved, actor, whitelists: newWhitelists } = obj;\n\n        if (newWhitelists) {\n            const oldList = media === MediaType.AUDIO\n                ? this._whitelistAudio\n                : this._whitelistVideo;\n            const newList = Array.isArray(newWhitelists[media]) ? newWhitelists[media] : [];\n\n            if (removed) {\n                oldList.filter(x => !newList.includes(x))\n                    .forEach(jid => this._xmpp.eventEmitter\n                        .emit(XMPPEvents.AV_MODERATION_PARTICIPANT_REJECTED, media, jid));\n            } else {\n                newList.filter(x => !oldList.includes(x))\n                    .forEach(jid => this._xmpp.eventEmitter\n                        .emit(XMPPEvents.AV_MODERATION_PARTICIPANT_APPROVED, media, jid));\n            }\n\n            if (media === MediaType.AUDIO) {\n                this._whitelistAudio = newList;\n            } else {\n                this._whitelistVideo = newList;\n            }\n        } else if (enabled !== undefined && this._moderationEnabledByType[media] !== enabled) {\n            this._moderationEnabledByType[media] = enabled;\n\n            this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_CHANGED, enabled, media, actor);\n        } else if (removed) {\n            this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_REJECTED, media);\n        } else if (approved) {\n            this._xmpp.eventEmitter.emit(XMPPEvents.AV_MODERATION_APPROVED, media);\n        }\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $msg } from 'strophe.js';\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nconst FEATURE_KEY = 'features/breakout-rooms';\nconst BREAKOUT_ROOM_ACTIONS = {\n    ADD: `${FEATURE_KEY}/add`,\n    REMOVE: `${FEATURE_KEY}/remove`,\n    MOVE_TO_ROOM: `${FEATURE_KEY}/move-to-room`\n};\nconst BREAKOUT_ROOM_EVENTS = {\n    MOVE_TO_ROOM: `${FEATURE_KEY}/move-to-room`,\n    UPDATE: `${FEATURE_KEY}/update`\n};\n\nconst logger = getLogger(__filename);\n\n/**\n * Helper class for handling breakout rooms.\n */\nexport default class BreakoutRooms {\n\n    /**\n     * Constructs lobby room.\n     *\n     * @param {ChatRoom} room the room we are in.\n     */\n    constructor(room) {\n        this.room = room;\n\n        this._handleMessages = this._handleMessages.bind(this);\n        this.room.xmpp.addListener(XMPPEvents.BREAKOUT_ROOMS_EVENT, this._handleMessages);\n\n        this._rooms = {};\n    }\n\n    /**\n     * Stops listening for events.\n     */\n    dispose() {\n        this.room.xmpp.removeListener(XMPPEvents.BREAKOUT_ROOMS_EVENT, this._handleMessages);\n    }\n\n    /**\n     * Creates a breakout room with the given subject.\n     *\n     * @param {string} subject - A subject for the breakout room.\n     */\n    createBreakoutRoom(subject) {\n        if (!this.isSupported() || !this.room.isModerator()) {\n            logger.error(`Cannot create breakout room - supported:${this.isSupported()},\n                moderator:${this.room.isModerator()}`);\n\n            return;\n        }\n\n        const message = {\n            type: BREAKOUT_ROOM_ACTIONS.ADD,\n            subject\n        };\n\n        this._sendMessage(message);\n    }\n\n    /**\n     * Removes a breakout room.\n     *\n     * @param {string} breakoutRoomJid - JID of the room to be removed.\n     */\n    removeBreakoutRoom(breakoutRoomJid) {\n        if (!this.isSupported() || !this.room.isModerator()) {\n            logger.error(`Cannot remove breakout room - supported:${this.isSupported()},\n                moderator:${this.room.isModerator()}`);\n\n            return;\n        }\n\n        const message = {\n            type: BREAKOUT_ROOM_ACTIONS.REMOVE,\n            breakoutRoomJid\n        };\n\n        this._sendMessage(message);\n    }\n\n    /**\n     * Sends the given participant to the given room.\n     *\n     * @param {string} participantJid - JID of the participant to be sent to a room.\n     * @param {string} roomJid - JID of the target room.\n     */\n    sendParticipantToRoom(participantJid, roomJid) {\n        if (!this.isSupported() || !this.room.isModerator()) {\n            logger.error(`Cannot send participant to room - supported:${this.isSupported()},\n                moderator:${this.room.isModerator()}`);\n\n            return;\n        }\n\n        const message = {\n            type: BREAKOUT_ROOM_ACTIONS.MOVE_TO_ROOM,\n            participantJid,\n            roomJid\n        };\n\n        this._sendMessage(message);\n    }\n\n    /**\n     * Whether Breakout Rooms support is enabled in the backend or not.\n     */\n    isSupported() {\n        return Boolean(this.getComponentAddress());\n    }\n\n    /**\n     * Gets the address of the Breakout Rooms XMPP component.\n     *\n     * @returns The address of the component.\n     */\n    getComponentAddress() {\n        return this.room.xmpp.breakoutRoomsComponentAddress;\n    }\n\n    /**\n     * Stores if the current room is a breakout room.\n     *\n     * @param {boolean} isBreakoutRoom - Whether this room is a breakout room.\n     */\n    _setIsBreakoutRoom(isBreakoutRoom) {\n        this._isBreakoutRoom = isBreakoutRoom;\n    }\n\n    /**\n     * Checks whether this room is a breakout room.\n     *\n     * @returns True if the room is a breakout room, false otherwise.\n     */\n    isBreakoutRoom() {\n        return this._isBreakoutRoom;\n    }\n\n    /**\n     * Sets the main room JID associated with this breakout room. Only applies when\n     * in a breakout room.\n     *\n     * @param {string} jid - The main room JID.\n     */\n    _setMainRoomJid(jid) {\n        this._mainRoomJid = jid;\n    }\n\n    /**\n     * Gets the main room's JID associated with this breakout room.\n     *\n     * @returns The main room JID.\n     */\n    getMainRoomJid() {\n        return this._mainRoomJid;\n    }\n\n    /**\n     * Handles a message for managing breakout rooms.\n     *\n     * @param {object} payload - Arbitrary data.\n     */\n    _handleMessages(payload) {\n        switch (payload.event) {\n        case BREAKOUT_ROOM_EVENTS.MOVE_TO_ROOM:\n            this.room.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_MOVE_TO_ROOM, payload.roomJid);\n            break;\n        case BREAKOUT_ROOM_EVENTS.UPDATE: {\n            this._rooms = payload.rooms;\n            this.room.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_UPDATED, payload);\n            break;\n        }\n        }\n    }\n\n    /**\n     * Helper to send a breakout rooms message to the component.\n     *\n     * @param {Object} message - Command that needs to be sent.\n     */\n    _sendMessage(message) {\n        const msg = $msg({ to: this.getComponentAddress() });\n\n        msg.c('breakout_rooms', message).up();\n\n        this.room.xmpp.connection.send(msg);\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { $msg, Strophe } from 'strophe.js';\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * The command type for updating a lobby participant's e-mail address.\n *\n * @type {string}\n */\nconst EMAIL_COMMAND = 'email';\n\n/**\n * The Lobby room implementation. Setting a room to members only, joining the lobby room\n * approving or denying access to participants from the lobby room.\n */\nexport default class Lobby {\n\n    /**\n     * Constructs lobby room.\n     *\n     * @param {ChatRoom} room the main room.\n     */\n    constructor(room) {\n        this.xmpp = room.xmpp;\n        this.mainRoom = room;\n\n        const maybeJoinLobbyRoom = this._maybeJoinLobbyRoom.bind(this);\n\n        this.mainRoom.addEventListener(\n            XMPPEvents.LOCAL_ROLE_CHANGED,\n            maybeJoinLobbyRoom);\n\n        this.mainRoom.addEventListener(\n            XMPPEvents.MUC_MEMBERS_ONLY_CHANGED,\n            maybeJoinLobbyRoom);\n\n        this.mainRoom.addEventListener(\n            XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR,\n            jid => {\n                this.lobbyRoomJid = jid;\n            });\n    }\n\n    /**\n     * Whether lobby is supported on backend.\n     *\n     * @returns {boolean} whether lobby is supported on backend.\n     */\n    isSupported() {\n        return this.xmpp.lobbySupported;\n    }\n\n    /**\n     * Enables lobby by setting the main room to be members only and joins the lobby chat room.\n     *\n     * @returns {Promise}\n     */\n    enable() {\n        if (!this.isSupported()) {\n            return Promise.reject(new Error('Lobby not supported!'));\n        }\n\n        return new Promise((resolve, reject) => {\n            this.mainRoom.setMembersOnly(true, resolve, reject);\n        });\n    }\n\n    /**\n     * Disable lobby by setting the main room to be non members only and levaes the lobby chat room if joined.\n     *\n     * @returns {void}\n     */\n    disable() {\n        if (!this.isSupported() || !this.mainRoom.isModerator()\n                || !this.lobbyRoom || !this.mainRoom.membersOnlyEnabled) {\n            return;\n        }\n\n        this.mainRoom.setMembersOnly(false);\n    }\n\n    /**\n     * Broadcast a message to all participants in the lobby room\n     * @param {Object} message The message to send\n     *\n     * @returns {void}\n     */\n    sendMessage(message) {\n        if (this.lobbyRoom) {\n            this.lobbyRoom.sendMessage(JSON.stringify(message), 'json-message');\n        }\n    }\n\n    /**\n     * Sends a private message to a participant in a lobby room.\n     * @param {string} id The message to send\n     * @param {Object} message The message to send\n     *\n     * @returns {void}\n     */\n    sendPrivateMessage(id, message) {\n        if (this.lobbyRoom) {\n            this.lobbyRoom.sendPrivateMessage(id, JSON.stringify(message), 'json-message');\n        }\n    }\n\n    /**\n     * Gets the local id for a participant in a lobby room.\n     * This is used for lobby room private chat messages.\n     *\n     * @returns {string}\n     */\n    getLocalId() {\n        if (this.lobbyRoom) {\n            return Strophe.getResourceFromJid(this.lobbyRoom.myroomjid);\n        }\n    }\n\n    /**\n     * Adds a message listener to the lobby room.\n     * @param {Function} listener The listener function,\n     * called when a new message is received in the lobby room.\n     *\n     * @returns {Function} Handler returned to be able to remove it later.\n     */\n    addMessageListener(listener) {\n        if (this.lobbyRoom) {\n            const handler = (participantId, message) => {\n                listener(message, Strophe.getResourceFromJid(participantId));\n            };\n\n            this.lobbyRoom.on(XMPPEvents.JSON_MESSAGE_RECEIVED, handler);\n\n            return handler;\n        }\n    }\n\n    /**\n     * Remove a message handler from the lobby room.\n     * @param {Function} handler The handler function to remove.\n     *\n     * @returns {void}\n     */\n    removeMessageHandler(handler) {\n        if (this.lobbyRoom) {\n            this.lobbyRoom.off(XMPPEvents.JSON_MESSAGE_RECEIVED, handler);\n        }\n    }\n\n    /**\n     * Leaves the lobby room.\n     *\n     * @returns {Promise}\n     */\n    leave() {\n        if (this.lobbyRoom) {\n            return this.lobbyRoom.leave()\n                .then(() => {\n                    this.lobbyRoom = undefined;\n                    logger.info('Lobby room left!');\n                })\n                .catch(() => {}); // eslint-disable-line no-empty-function\n        }\n\n        return Promise.reject(\n                new Error('The lobby has already been left'));\n    }\n\n    /**\n     * We had received a jid for the lobby room.\n     *\n     * @param jid the lobby room jid to join.\n     */\n    setLobbyRoomJid(jid) {\n        this.lobbyRoomJid = jid;\n    }\n\n    /**\n     * Checks the state of mainRoom, lobbyRoom and current user role to decide whether to join lobby room.\n     * @private\n     */\n    _maybeJoinLobbyRoom() {\n        if (!this.isSupported()) {\n            return;\n        }\n\n        const isModerator = this.mainRoom.joined && this.mainRoom.isModerator();\n\n        if (isModerator && this.mainRoom.membersOnlyEnabled && !this.lobbyRoom) {\n            // join the lobby\n            this.join()\n                .then(() => logger.info('Joined lobby room'))\n                .catch(e => logger.error('Failed joining lobby', e));\n        }\n    }\n\n    /**\n     * Joins a lobby room setting display name and eventually avatar(using the email provided).\n     *\n     * @param {string} username is required.\n     * @param {string} email is optional.\n     * @returns {Promise} resolves once we join the room.\n     */\n    join(displayName, email) {\n        const isModerator = this.mainRoom.joined && this.mainRoom.isModerator();\n\n        if (!this.lobbyRoomJid) {\n            return Promise.reject(new Error('Missing lobbyRoomJid, cannot join lobby room.'));\n        }\n\n        const roomName = Strophe.getNodeFromJid(this.lobbyRoomJid);\n        const customDomain = Strophe.getDomainFromJid(this.lobbyRoomJid);\n\n        this.lobbyRoom = this.xmpp.createRoom(\n            roomName, {\n                customDomain,\n                disableDiscoInfo: true,\n                disableFocus: true,\n                enableLobby: false\n            }\n        );\n\n        if (displayName) {\n            // remove previously set nickname\n            this.lobbyRoom.addOrReplaceInPresence('nick', {\n                attributes: { xmlns: 'http://jabber.org/protocol/nick' },\n                value: displayName\n            });\n        }\n\n        if (isModerator) {\n            this.lobbyRoom.addPresenceListener(EMAIL_COMMAND, (node, from) => {\n                this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_LOBBY_MEMBER_UPDATED, from, { email: node.value });\n            });\n            this.lobbyRoom.addEventListener(\n                XMPPEvents.MUC_MEMBER_JOINED,\n                // eslint-disable-next-line max-params\n                (from, nick, role, isHiddenDomain, statsID, status, identity, botType, jid) => {\n                    // we need to ignore joins on lobby for participants that are already in the main room\n                    if (Object.values(this.mainRoom.members).find(m => m.jid === jid)) {\n                        return;\n                    }\n\n                    // Check if the user is a member if any breakout room.\n                    for (const room of Object.values(this.mainRoom.getBreakoutRooms()._rooms)) {\n                        if (Object.values(room.participants).find(p => p.jid === jid)) {\n                            return;\n                        }\n                    }\n\n                    // we emit the new event on the main room so we can propagate\n                    // events to the conference\n                    this.mainRoom.eventEmitter.emit(\n                        XMPPEvents.MUC_LOBBY_MEMBER_JOINED,\n                        Strophe.getResourceFromJid(from),\n                        nick,\n                        identity ? identity.avatar : undefined\n                    );\n                });\n            this.lobbyRoom.addEventListener(\n                XMPPEvents.MUC_MEMBER_LEFT, from => {\n                    // we emit the new event on the main room so we can propagate\n                    // events to the conference\n                    this.mainRoom.eventEmitter.emit(\n                        XMPPEvents.MUC_LOBBY_MEMBER_LEFT,\n                        Strophe.getResourceFromJid(from)\n                    );\n                });\n            this.lobbyRoom.addEventListener(\n                XMPPEvents.MUC_DESTROYED,\n                () => {\n                    // let's make sure we emit that all lobby users had left\n                    Object.keys(this.lobbyRoom.members)\n                        .forEach(j => this.mainRoom.eventEmitter.emit(\n                            XMPPEvents.MUC_LOBBY_MEMBER_LEFT, Strophe.getResourceFromJid(j)));\n\n                    this.lobbyRoom.clean();\n\n                    this.lobbyRoom = undefined;\n                    logger.info('Lobby room left(destroyed)!');\n                });\n        } else {\n            // this should only be handled by those waiting in lobby\n            this.lobbyRoom.addEventListener(XMPPEvents.KICKED, isSelfPresence => {\n                if (isSelfPresence) {\n                    this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_DENIED_ACCESS);\n\n                    this.lobbyRoom.clean();\n\n                    return;\n                }\n            });\n\n            // As there is still reference of the main room\n            // the invite will be detected and addressed to its eventEmitter, even though we are not in it\n            // the invite message should be received directly to the xmpp conn in general\n            this.mainRoom.addEventListener(\n                XMPPEvents.INVITE_MESSAGE_RECEIVED,\n                (roomJid, from, txt, invitePassword) => {\n                    logger.debug(`Received approval to join ${roomJid} ${from} ${txt}`);\n                    if (roomJid === this.mainRoom.roomjid) {\n                        // we are now allowed, so let's join\n                        this.mainRoom.join(invitePassword);\n                    }\n                });\n            this.lobbyRoom.addEventListener(\n                XMPPEvents.MUC_DESTROYED,\n                (reason, jid) => {\n                    // we are receiving the jid of the main room\n                    // means we are invited to join, maybe lobby was disabled\n                    if (jid) {\n                        this.mainRoom.join();\n\n                        return;\n                    }\n\n                    this.lobbyRoom.clean();\n\n                    this.mainRoom.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason);\n                });\n\n            // If participant retries joining shared password while waiting in the lobby\n            // and succeeds make sure we leave lobby\n            this.mainRoom.addEventListener(\n                XMPPEvents.MUC_JOINED,\n                () => {\n                    this.leave();\n                });\n        }\n\n        return new Promise((resolve, reject) => {\n            this.lobbyRoom.addEventListener(XMPPEvents.MUC_JOINED, () => {\n                resolve();\n\n                // send our email, as we do not handle this on initial presence we need a second one\n                if (email && !isModerator) {\n                    this.lobbyRoom.addOrReplaceInPresence(EMAIL_COMMAND, { value: email })\n                        && this.lobbyRoom.sendPresence();\n                }\n            });\n            this.lobbyRoom.addEventListener(XMPPEvents.ROOM_JOIN_ERROR, reject);\n            this.lobbyRoom.addEventListener(XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR, reject);\n            this.lobbyRoom.addEventListener(XMPPEvents.ROOM_CONNECT_ERROR, reject);\n\n            this.lobbyRoom.join();\n        });\n\n    }\n\n    /**\n     * Should be possible only for moderators.\n     * @param id\n     */\n    denyAccess(id) {\n        if (!this.isSupported() || !this.mainRoom.isModerator()) {\n            return;\n        }\n\n        const jid = Object.keys(this.lobbyRoom.members)\n            .find(j => Strophe.getResourceFromJid(j) === id);\n\n        if (jid) {\n            this.lobbyRoom.kick(jid);\n        } else {\n            logger.error(`Not found member for ${id} in lobby room.`);\n        }\n    }\n\n    /**\n     * Should be possible only for moderators.\n     * @param id\n     */\n    approveAccess(id) {\n        if (!this.isSupported() || !this.mainRoom.isModerator()) {\n            return;\n        }\n\n        // Get the main room JID. If we are in a breakout room we'll use the main\n        // room's lobby.\n        let mainRoomJid = this.mainRoom.roomjid;\n\n        if (this.mainRoom.getBreakoutRooms().isBreakoutRoom()) {\n            mainRoomJid = this.mainRoom.getBreakoutRooms().getMainRoomJid();\n        }\n\n        const memberRoomJid = Object.keys(this.lobbyRoom.members)\n            .find(j => Strophe.getResourceFromJid(j) === id);\n\n        if (memberRoomJid) {\n            const jid = this.lobbyRoom.members[memberRoomJid].jid;\n            const msgToSend\n                = $msg({ to: mainRoomJid })\n                    .c('x', { xmlns: 'http://jabber.org/protocol/muc#user' })\n                    .c('invite', { to: jid });\n\n            this.xmpp.connection.sendIQ(msgToSend,\n                () => { }, // eslint-disable-line no-empty-function\n                e => {\n                    logger.error(`Error sending invite for ${jid}`, e);\n                });\n        } else {\n            logger.error(`Not found member for ${memberRoomJid} in lobby room.`);\n        }\n    }\n}\n","import { jitsiLocalStorage } from '@jitsi/js-utils';\nimport { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger(__filename);\n\nimport UsernameGenerator from '../util/UsernameGenerator';\n\nlet _callStatsUserName;\n\nlet _machineId;\n\n/**\n *\n */\nexport default {\n\n    /**\n     * The storage used to store the settings.\n     */\n    _storage: jitsiLocalStorage,\n\n    /**\n     * Initializes the Settings class.\n     *\n     * @param {Storage|undefined} externalStorage - Object that implements the Storage interface. This object will be\n     * used for storing data instead of jitsiLocalStorage if specified.\n     */\n    init(externalStorage) {\n        this._storage = externalStorage || jitsiLocalStorage;\n    },\n\n    /**\n     * Returns fake username for callstats\n     * @returns {string} fake username for callstats\n     */\n    get callStatsUserName() {\n        if (!_callStatsUserName) {\n            _callStatsUserName = this._storage.getItem('callStatsUserName');\n            if (!_callStatsUserName) {\n                _callStatsUserName = generateCallStatsUserName();\n                this._storage.setItem('callStatsUserName', _callStatsUserName);\n            }\n        }\n\n        return _callStatsUserName;\n    },\n\n    /**\n     * Returns current machine id.\n     * @returns {string} machine id\n     */\n    get machineId() {\n        if (!_machineId) {\n            const amDid = this._storage.getItem('billingId');\n\n            _machineId = amDid || this._storage.getItem('jitsiMeetId');\n\n            if (amDid) {\n                this._storage.setItem('jitsiMeetId', amDid);\n            } else if (!_machineId) {\n                _machineId = generateJitsiMeetId();\n                this._storage.setItem('jitsiMeetId', _machineId);\n            }\n        }\n\n        return _machineId;\n    },\n\n    /**\n     * Returns current session id.\n     * @returns {string} current session id\n     */\n    get sessionId() {\n        // We may update sessionId in localStorage from another JitsiConference\n        // instance and that's why we should always re-read it.\n        return this._storage.getItem('sessionId');\n    },\n\n    /**\n     * Save current session id.\n     * @param {string} sessionId session id\n     */\n    set sessionId(sessionId) {\n        if (sessionId) {\n            this._storage.setItem('sessionId', sessionId);\n        } else {\n            this._storage.removeItem('sessionId');\n        }\n    }\n};\n\n/**\n * Generate fake username for callstats.\n * @returns {string} fake random username\n */\nfunction generateCallStatsUserName() {\n    const username = UsernameGenerator.generateUsername();\n\n    logger.log('generated callstats uid', username);\n\n    return username;\n}\n\n/**\n * Generate unique id.\n * @returns {string} random unique id\n */\nfunction generateJitsiMeetId() {\n    const jitsiMeetId = generateUniqueId();\n\n    logger.log('generated id', jitsiMeetId);\n\n    return jitsiMeetId;\n}\n\n/**\n *\n */\nfunction generateUniqueId() {\n    return _p8() + _p8() + _p8() + _p8();\n}\n\n/**\n *\n */\nfunction _p8() {\n    return `${Math.random().toString(16)}000000000`.substr(2, 8);\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport Settings from '../settings/Settings';\n\nconst AuthenticationEvents\n    = require('../../service/authentication/AuthenticationEvents');\nconst { XMPPEvents } = require('../../service/xmpp/XMPPEvents');\nconst GlobalOnErrorHandler = require('../util/GlobalOnErrorHandler');\n\nconst logger = getLogger(__filename);\n\n/**\n *\n * @param step\n */\nfunction createExpBackoffTimer(step) {\n    let count = 1;\n\n    return function(reset) {\n        // Reset call\n        if (reset) {\n            count = 1;\n\n            return;\n        }\n\n        // Calculate next timeout\n        const timeout = Math.pow(2, count - 1);\n\n        count += 1;\n\n        return timeout * step;\n    };\n}\n\n/* eslint-disable max-params */\n\n/**\n *\n * @param roomName\n * @param xmpp\n * @param emitter\n * @param options\n */\nexport default function Moderator(roomName, xmpp, emitter, options) {\n    this.roomName = roomName;\n    this.xmppService = xmpp;\n    this.getNextTimeout = createExpBackoffTimer(1000);\n    this.getNextErrorTimeout = createExpBackoffTimer(1000);\n\n    // External authentication stuff\n    this.externalAuthEnabled = false;\n    this.options = options;\n\n    // Whether SIP gateway (jigasi) support is enabled. This is set\n    // based on conference properties received in presence.\n    this.sipGatewayEnabled = false;\n\n    this.eventEmitter = emitter;\n\n    this.connection = this.xmppService.connection;\n\n    // FIXME: Message listener that talks to POPUP window\n    /**\n     *\n     * @param event\n     */\n    function listener(event) {\n        if (event.data && event.data.sessionId) {\n            if (event.origin !== window.location.origin) {\n                logger.warn(\n                    `Ignoring sessionId from different origin: ${\n                        event.origin}`);\n\n                return;\n            }\n            Settings.sessionId = event.data.sessionId;\n\n            // After popup is closed we will authenticate\n        }\n    }\n\n    // Register\n    if (window.addEventListener) {\n        window.addEventListener('message', listener, false);\n    } else {\n        window.attachEvent('onmessage', listener);\n    }\n}\n\n/* eslint-enable max-params */\n\nModerator.prototype.isExternalAuthEnabled = function() {\n    return this.externalAuthEnabled;\n};\n\nModerator.prototype.isSipGatewayEnabled = function() {\n    return this.sipGatewayEnabled;\n};\n\nModerator.prototype.onMucMemberLeft = function(jid) {\n    const resource = Strophe.getResourceFromJid(jid);\n\n    if (resource === 'focus') {\n        logger.info(\n            'Focus has left the room - leaving conference');\n        this.eventEmitter.emit(XMPPEvents.FOCUS_LEFT);\n    }\n};\n\nModerator.prototype.setFocusUserJid = function(focusJid) {\n    if (!this.focusUserJid) {\n        this.focusUserJid = focusJid;\n        logger.info(`Focus jid set to:  ${this.focusUserJid}`);\n    }\n};\n\nModerator.prototype.getFocusUserJid = function() {\n    return this.focusUserJid;\n};\n\nModerator.prototype.getFocusComponent = function() {\n    // Get focus component address\n    let focusComponent = this.options.connection.hosts.focus;\n\n    // If not specified use default:  'focus.domain'\n\n    if (!focusComponent) {\n        focusComponent = `focus.${this.options.connection.hosts.domain}`;\n    }\n\n    return focusComponent;\n};\n\nModerator.prototype.createConferenceIq = function() {\n    // Generate create conference IQ\n    const elem = $iq({ to: this.getFocusComponent(),\n        type: 'set' });\n\n    // Session Id used for authentication\n    const { sessionId } = Settings;\n    const machineUID = Settings.machineId;\n    const config = this.options.conference;\n\n    logger.info(`Session ID: ${sessionId} machine UID: ${machineUID}`);\n\n    elem.c('conference', {\n        xmlns: 'http://jitsi.org/protocol/focus',\n        room: this.roomName,\n        'machine-uid': machineUID\n    });\n\n    if (sessionId) {\n        elem.attrs({ 'session-id': sessionId });\n    }\n\n    elem.c(\n        'property', {\n            name: 'disableRtx',\n            value: Boolean(config.disableRtx)\n        }).up();\n\n    if (config.audioPacketDelay !== undefined) {\n        elem.c(\n            'property', {\n                name: 'audioPacketDelay',\n                value: config.audioPacketDelay\n            }).up();\n    }\n    if (config.startBitrate) {\n        elem.c(\n            'property', {\n                name: 'startBitrate',\n                value: config.startBitrate\n            }).up();\n    }\n    if (config.minBitrate) {\n        elem.c(\n            'property', {\n                name: 'minBitrate',\n                value: config.minBitrate\n            }).up();\n    }\n\n    if (this.options.conference.startAudioMuted !== undefined) {\n        elem.c(\n            'property', {\n                name: 'startAudioMuted',\n                value: this.options.conference.startAudioMuted\n            }).up();\n    }\n    if (this.options.conference.startVideoMuted !== undefined) {\n        elem.c(\n            'property', {\n                name: 'startVideoMuted',\n                value: this.options.conference.startVideoMuted\n            }).up();\n    }\n\n    // this flag determines whether the bridge will include this call in its\n    // rtcstats reporting or not. If the site admin hasn't set the flag in\n    // config.js, then the client defaults to false (see\n    // react/features/rtcstats/functions.js in jitsi-meet). The server-side\n    // components default to true to match the pre-existing behavior so we only\n    // signal if false.\n    const rtcstatsEnabled = this.options.conference?.analytics?.rtcstatsEnabled ?? false;\n\n    if (!rtcstatsEnabled) {\n        elem.c(\n            'property', {\n                name: 'rtcstatsEnabled',\n                value: rtcstatsEnabled\n            }).up();\n    }\n\n    const { callStatsID, callStatsSecret, disableThirdPartyRequests, enableCallStats } = this.options.conference;\n    const callstatsDisabled = !callStatsID || !callStatsSecret || !enableCallStats\n\n        // Even though AppID and AppSecret may be specified, the integration\n        // of callstats.io may be disabled because of globally-disallowed\n        // requests to any third parties.\n        || disableThirdPartyRequests === true;\n\n    // since the default is true across all the server-side components, only signal if false.\n    if (callstatsDisabled) {\n        elem.c(\n            'property', {\n                name: 'callstatsEnabled',\n                value: !callstatsDisabled\n            }).up();\n    }\n    elem.up();\n\n    return elem;\n};\n\n\nModerator.prototype.parseSessionId = function(resultIq) {\n    // eslint-disable-next-line newline-per-chained-call\n    const sessionId = $(resultIq).find('conference').attr('session-id');\n\n    if (sessionId) {\n        logger.info(`Received sessionId:  ${sessionId}`);\n        Settings.sessionId = sessionId;\n    }\n};\n\nModerator.prototype.parseConfigOptions = function(resultIq) {\n    // eslint-disable-next-line newline-per-chained-call\n    this.setFocusUserJid($(resultIq).find('conference').attr('focusjid'));\n\n    const authenticationEnabled\n        = $(resultIq).find(\n            '>conference>property'\n            + '[name=\\'authentication\\'][value=\\'true\\']').length > 0;\n\n    logger.info(`Authentication enabled: ${authenticationEnabled}`);\n\n    this.externalAuthEnabled = $(resultIq).find(\n        '>conference>property'\n            + '[name=\\'externalAuth\\'][value=\\'true\\']').length > 0;\n\n    logger.info(\n        `External authentication enabled: ${this.externalAuthEnabled}`);\n\n    if (!this.externalAuthEnabled) {\n        // We expect to receive sessionId in 'internal' authentication mode\n        this.parseSessionId(resultIq);\n    }\n\n    // eslint-disable-next-line newline-per-chained-call\n    const authIdentity = $(resultIq).find('>conference').attr('identity');\n\n    this.eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,\n        authenticationEnabled, authIdentity);\n\n    // Check if jicofo has jigasi support enabled.\n    if ($(resultIq).find(\n        '>conference>property'\n        + '[name=\\'sipGatewayEnabled\\'][value=\\'true\\']').length) {\n        this.sipGatewayEnabled = true;\n    }\n\n    logger.info(`Sip gateway enabled:  ${this.sipGatewayEnabled}`);\n};\n\n// FIXME We need to show the fact that we're waiting for the focus to the user\n// (or that the focus is not available)\n/**\n * Allocates the conference focus.\n *\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n * @returns {Promise} - Resolved when Jicofo allows to join the room. It's never\n * rejected and it'll keep on pinging Jicofo forever.\n */\nModerator.prototype.allocateConferenceFocus = function() {\n    return new Promise(resolve => {\n        // Try to use focus user JID from the config\n        this.setFocusUserJid(this.options.connection.focusUserJid);\n\n        // Send create conference IQ\n        this.connection.sendIQ(\n            this.createConferenceIq(),\n            result => this._allocateConferenceFocusSuccess(result, resolve),\n            error => this._allocateConferenceFocusError(error, resolve));\n\n        // XXX We're pressed for time here because we're beginning a complex\n        // and/or lengthy conference-establishment process which supposedly\n        // involves multiple RTTs. We don't have the time to wait for Strophe to\n        // decide to send our IQ.\n        this.connection.flush();\n    });\n};\n\n/**\n * Invoked by {@link #allocateConferenceFocus} upon its request receiving an\n * error result.\n *\n * @param error - the error result of the request that\n * {@link #allocateConferenceFocus} sent\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n */\nModerator.prototype._allocateConferenceFocusError = function(error, callback) {\n    // If the session is invalid, remove and try again without session ID to get\n    // a new one\n    const invalidSession\n        = $(error).find('>error>session-invalid').length\n            || $(error).find('>error>not-acceptable').length;\n\n    if (invalidSession) {\n        logger.info('Session expired! - removing');\n        Settings.sessionId = undefined;\n    }\n    if ($(error).find('>error>graceful-shutdown').length) {\n        this.eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);\n\n        return;\n    }\n\n    // Check for error returned by the reservation system\n    const reservationErr = $(error).find('>error>reservation-error');\n\n    if (reservationErr.length) {\n        // Trigger error event\n        const errorCode = reservationErr.attr('error-code');\n        const errorTextNode = $(error).find('>error>text');\n        let errorMsg;\n\n        if (errorTextNode) {\n            errorMsg = errorTextNode.text();\n        }\n        this.eventEmitter.emit(\n            XMPPEvents.RESERVATION_ERROR,\n            errorCode,\n            errorMsg);\n\n        return;\n    }\n\n    // Not authorized to create new room\n    if ($(error).find('>error>not-authorized').length) {\n        logger.warn('Unauthorized to start the conference', error);\n        const toDomain = Strophe.getDomainFromJid(error.getAttribute('to'));\n\n        if (toDomain !== this.options.connection.hosts.anonymousdomain) {\n            // FIXME \"is external\" should come either from the focus or\n            // config.js\n            this.externalAuthEnabled = true;\n        }\n        this.eventEmitter.emit(XMPPEvents.AUTHENTICATION_REQUIRED);\n\n        return;\n    }\n    const waitMs = this.getNextErrorTimeout();\n    const errmsg = `Focus error, retry after ${waitMs}`;\n\n    GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n    logger.error(errmsg, error);\n\n    // Show message\n    const focusComponent = this.getFocusComponent();\n    const retrySec = waitMs / 1000;\n\n    // FIXME: message is duplicated ? Do not show in case of session invalid\n    // which means just a retry\n\n    if (!invalidSession) {\n        this.eventEmitter.emit(\n            XMPPEvents.FOCUS_DISCONNECTED,\n            focusComponent,\n            retrySec);\n    }\n\n    // Reset response timeout\n    this.getNextTimeout(true);\n    window.setTimeout(\n        () => this.allocateConferenceFocus().then(callback),\n        waitMs);\n};\n\n/**\n * Invoked by {@link #allocateConferenceFocus} upon its request receiving a\n * success (i.e. non-error) result.\n *\n * @param result - the success (i.e. non-error) result of the request that\n * {@link #allocateConferenceFocus} sent\n * @param {Function} callback - the function to be called back upon the\n * successful allocation of the conference focus\n */\nModerator.prototype._allocateConferenceFocusSuccess = function(\n        result,\n        callback) {\n    // Setup config options\n    this.parseConfigOptions(result);\n\n    // Reset the error timeout (because we haven't failed here).\n    this.getNextErrorTimeout(true);\n\n    // eslint-disable-next-line newline-per-chained-call\n    if ($(result).find('conference').attr('ready') === 'true') {\n        // Reset the non-error timeout (because we've succeeded here).\n        this.getNextTimeout(true);\n\n        // Exec callback\n        callback();\n    } else {\n        const waitMs = this.getNextTimeout();\n\n        logger.info(`Waiting for the focus... ${waitMs}`);\n        window.setTimeout(\n            () => this.allocateConferenceFocus().then(callback),\n            waitMs);\n    }\n};\n\nModerator.prototype.authenticate = function() {\n    return new Promise((resolve, reject) => {\n        this.connection.sendIQ(\n            this.createConferenceIq(),\n            result => {\n                this.parseSessionId(result);\n                resolve();\n            },\n            errorIq => reject({\n                error: $(errorIq).find('iq>error :first')\n                    .prop('tagName'),\n                message: $(errorIq).find('iq>error>text')\n                    .text()\n            })\n        );\n    });\n};\n\nModerator.prototype.getLoginUrl = function(urlCallback, failureCallback) {\n    this._getLoginUrl(/* popup */ false, urlCallback, failureCallback);\n};\n\n/**\n *\n * @param {boolean} popup false for {@link Moderator#getLoginUrl} or true for\n * {@link Moderator#getPopupLoginUrl}\n * @param urlCb\n * @param failureCb\n */\nModerator.prototype._getLoginUrl = function(popup, urlCb, failureCb) {\n    const iq = $iq({ to: this.getFocusComponent(),\n        type: 'get' });\n    const attrs = {\n        xmlns: 'http://jitsi.org/protocol/focus',\n        room: this.roomName,\n        'machine-uid': Settings.machineId\n    };\n    let str = 'auth url'; // for logger\n\n    if (popup) {\n        attrs.popup = true;\n        str = `POPUP ${str}`;\n    }\n    iq.c('login-url', attrs);\n\n    /**\n     * Implements a failure callback which reports an error message and an error\n     * through (1) GlobalOnErrorHandler, (2) logger, and (3) failureCb.\n     *\n     * @param {string} errmsg the error messsage to report\n     * @param {*} error the error to report (in addition to errmsg)\n     */\n    function reportError(errmsg, err) {\n        GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n        logger.error(errmsg, err);\n        failureCb(err);\n    }\n    this.connection.sendIQ(\n        iq,\n        result => {\n            // eslint-disable-next-line newline-per-chained-call\n            let url = $(result).find('login-url').attr('url');\n\n            url = decodeURIComponent(url);\n            if (url) {\n                logger.info(`Got ${str}: ${url}`);\n                urlCb(url);\n            } else {\n                reportError(`Failed to get ${str} from the focus`, result);\n            }\n        },\n        reportError.bind(undefined, `Get ${str} error`)\n    );\n};\n\nModerator.prototype.getPopupLoginUrl = function(urlCallback, failureCallback) {\n    this._getLoginUrl(/* popup */ true, urlCallback, failureCallback);\n};\n\nModerator.prototype.logout = function(callback) {\n    const iq = $iq({ to: this.getFocusComponent(),\n        type: 'set' });\n    const { sessionId } = Settings;\n\n    if (!sessionId) {\n        callback();\n\n        return;\n    }\n    iq.c('logout', {\n        xmlns: 'http://jitsi.org/protocol/focus',\n        'session-id': sessionId\n    });\n    this.connection.sendIQ(\n        iq,\n        result => {\n            // eslint-disable-next-line newline-per-chained-call\n            let logoutUrl = $(result).find('logout').attr('logout-url');\n\n            if (logoutUrl) {\n                logoutUrl = decodeURIComponent(logoutUrl);\n            }\n            logger.info(`Log out OK, url: ${logoutUrl}`, result);\n            Settings.sessionId = undefined;\n            callback(logoutUrl);\n        },\n        error => {\n            const errmsg = 'Logout error';\n\n            GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n            logger.error(errmsg, error);\n        }\n    );\n};\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\nimport { $iq, $msg, $pres, Strophe } from 'strophe.js';\n\nimport * as JitsiTranscriptionStatus from '../../JitsiTranscriptionStatus';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\n\nimport AVModeration from './AVModeration';\nimport BreakoutRooms from './BreakoutRooms';\nimport Lobby from './Lobby';\nimport XmppConnection from './XmppConnection';\nimport Moderator from './moderator';\n\nconst logger = getLogger(__filename);\n\nexport const parser = {\n    packet2JSON(xmlElement, nodes) {\n        for (const child of Array.from(xmlElement.children)) {\n            const node = {\n                attributes: {},\n                children: [],\n                tagName: child.tagName\n            };\n\n            for (const attr of Array.from(child.attributes)) {\n                node.attributes[attr.name] = attr.value;\n            }\n            const text = Strophe.getText(child);\n\n            if (text) {\n                // Using Strophe.getText will do work for traversing all direct\n                // child text nodes but returns an escaped value, which is not\n                // desirable at this point.\n                node.value = Strophe.xmlunescape(text);\n            }\n            nodes.push(node);\n            this.packet2JSON(child, node.children);\n        }\n    },\n    json2packet(nodes, packet) {\n        for (let i = 0; i < nodes.length; i++) {\n            const node = nodes[i];\n\n            if (node) {\n                packet.c(node.tagName, node.attributes);\n                if (node.value) {\n                    packet.t(node.value);\n                }\n                if (node.children) {\n                    this.json2packet(node.children, packet);\n                }\n                packet.up();\n            }\n        }\n\n        // packet.up();\n    }\n};\n\n/**\n * Returns array of JS objects from the presence JSON associated with the passed\n / nodeName\n * @param pres the presence JSON\n * @param nodeName the name of the node (videomuted, audiomuted, etc)\n */\nexport function filterNodeFromPresenceJSON(pres, nodeName) {\n    const res = [];\n\n    for (let i = 0; i < pres.length; i++) {\n        if (pres[i].tagName === nodeName) {\n            res.push(pres[i]);\n        }\n    }\n\n    return res;\n}\n\n// XXX As ChatRoom constructs XMPP stanzas and Strophe is build around the idea\n// of chaining function calls, allow long function call chains.\n/* eslint-disable newline-per-chained-call */\n\n/**\n * Array of affiliations that are allowed in members only room.\n * @type {string[]}\n */\nconst MEMBERS_AFFILIATIONS = [ 'owner', 'admin', 'member' ];\n\n/**\n *\n */\nexport default class ChatRoom extends Listenable {\n\n    /* eslint-disable max-params */\n\n    /**\n     *\n     * @param {XmppConnection} connection - The XMPP connection instance.\n     * @param jid\n     * @param password\n     * @param XMPP\n     * @param options\n     * @param {boolean} options.disableFocus - when set to {@code false} will\n     * not invite Jicofo into the room.\n     * @param {boolean} options.disableDiscoInfo - when set to {@code false} will skip disco info.\n     * This is intended to be used only for lobby rooms.\n     * @param {boolean} options.enableLobby - when set to {@code false} will skip creating lobby room.\n     * @param {boolean} options.hiddenFromRecorderFeatureEnabled - when set to {@code true} we will check identity tag\n     * for node presence.\n     */\n    constructor(connection, jid, password, XMPP, options) {\n        super();\n        this.xmpp = XMPP;\n        this.connection = connection;\n        this.roomjid = Strophe.getBareJidFromJid(jid);\n        this.myroomjid = jid;\n        this.password = password;\n        this.replaceParticipant = false;\n        logger.info(`Joined MUC as ${this.myroomjid}`);\n        this.members = {};\n        this.presMap = {};\n        this.presHandlers = {};\n        this._removeConnListeners = [];\n        this.joined = false;\n        this.inProgressEmitted = false;\n        this.role = null;\n        this.focusMucJid = null;\n        this.noBridgeAvailable = false;\n        this.options = options || {};\n        this.moderator\n            = new Moderator(this.roomjid, this.xmpp, this.eventEmitter, {\n                connection: this.xmpp.options,\n                conference: this.options\n            });\n        if (typeof this.options.enableLobby === 'undefined' || this.options.enableLobby) {\n            this.lobby = new Lobby(this);\n        }\n        this.avModeration = new AVModeration(this);\n        this.breakoutRooms = new BreakoutRooms(this);\n        this.initPresenceMap(options);\n        this.lastPresences = {};\n        this.phoneNumber = null;\n        this.phonePin = null;\n        this.connectionTimes = {};\n        this.participantPropertyListener = null;\n\n        this.locked = false;\n        this.transcriptionStatus = JitsiTranscriptionStatus.OFF;\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     *\n     */\n    initPresenceMap(options = {}) {\n        this.presMap.to = this.myroomjid;\n        this.presMap.xns = 'http://jabber.org/protocol/muc';\n        this.presMap.nodes = [];\n\n        if (options.statsId) {\n            this.presMap.nodes.push({\n                'tagName': 'stats-id',\n                'value': options.statsId\n            });\n        }\n\n        this.presenceUpdateTime = Date.now();\n    }\n\n    /**\n     * Joins the chat room.\n     * @param {string} password - Password to unlock room on joining.\n     * @returns {Promise} - resolved when join completes. At the time of this\n     * writing it's never rejected.\n     */\n    join(password, replaceParticipant) {\n        this.password = password;\n        this.replaceParticipant = replaceParticipant;\n\n        return new Promise(resolve => {\n            this.options.disableFocus\n                && logger.info(`Conference focus disabled for ${this.roomjid}`);\n\n            const preJoin\n                = this.options.disableFocus\n                    ? Promise.resolve()\n                    : this.moderator.allocateConferenceFocus();\n\n            preJoin.then(() => {\n                this.sendPresence(true);\n                this._removeConnListeners.push(\n                    this.connection.addEventListener(\n                        XmppConnection.Events.CONN_STATUS_CHANGED,\n                        this.onConnStatusChanged.bind(this))\n                );\n                resolve();\n            });\n        });\n    }\n\n    /**\n     *\n     * @param fromJoin - Whether this is initial presence to join the room.\n     */\n    sendPresence(fromJoin) {\n        const to = this.presMap.to;\n\n        if (!this.connection || !this.connection.connected || !to || (!this.joined && !fromJoin)) {\n            // Too early to send presence - not initialized\n            return;\n        }\n\n        const pres = $pres({ to });\n\n        // xep-0045 defines: \"including in the initial presence stanza an empty\n        // <x/> element qualified by the 'http://jabber.org/protocol/muc'\n        // namespace\" and subsequent presences should not include that or it can\n        // be considered as joining, and server can send us the message history\n        // for the room on every presence\n        if (fromJoin) {\n            if (this.replaceParticipant) {\n                pres.c('flip_device').up();\n            }\n\n            pres.c('x', { xmlns: this.presMap.xns });\n\n            if (this.password) {\n                pres.c('password').t(this.password).up();\n            }\n            if (this.options.billingId) {\n                pres.c('billingid').t(this.options.billingId).up();\n            }\n\n            pres.up();\n        }\n\n        parser.json2packet(this.presMap.nodes, pres);\n\n        // we store time we last synced presence state\n        this.presenceSyncTime = Date.now();\n\n        this.connection.send(pres);\n        if (fromJoin) {\n            // XXX We're pressed for time here because we're beginning a complex\n            // and/or lengthy conference-establishment process which supposedly\n            // involves multiple RTTs. We don't have the time to wait for\n            // Strophe to decide to send our IQ.\n            this.connection.flush();\n        }\n    }\n\n    /**\n     * Sends the presence unavailable, signaling the server\n     * we want to leave the room.\n     */\n    doLeave() {\n        logger.log('do leave', this.myroomjid);\n        const pres = $pres({ to: this.myroomjid,\n            type: 'unavailable' });\n\n        this.presMap.length = 0;\n\n        // XXX Strophe is asynchronously sending by default. Unfortunately, that\n        // means that there may not be enough time to send the unavailable\n        // presence. Switching Strophe to synchronous sending is not much of an\n        // option because it may lead to a noticeable delay in navigating away\n        // from the current location. As a compromise, we will try to increase\n        // the chances of sending the unavailable presence within the short time\n        // span that we have upon unloading by invoking flush() on the\n        // connection. We flush() once before sending/queuing the unavailable\n        // presence in order to attemtp to have the unavailable presence at the\n        // top of the send queue. We flush() once more after sending/queuing the\n        // unavailable presence in order to attempt to have it sent as soon as\n        // possible.\n        // FIXME do not use Strophe.Connection in the ChatRoom directly\n        !this.connection.isUsingWebSocket && this.connection.flush();\n        this.connection.send(pres);\n        this.connection.flush();\n    }\n\n    /**\n     *\n     */\n    discoRoomInfo() {\n        // https://xmpp.org/extensions/xep-0045.html#disco-roominfo\n\n        const getInfo\n            = $iq({\n                type: 'get',\n                to: this.roomjid\n            })\n                .c('query', { xmlns: Strophe.NS.DISCO_INFO });\n\n        this.connection.sendIQ(getInfo, result => {\n            const locked\n                = $(result).find('>query>feature[var=\"muc_passwordprotected\"]')\n                    .length\n                    === 1;\n\n            if (locked !== this.locked) {\n                this.eventEmitter.emit(XMPPEvents.MUC_LOCK_CHANGED, locked);\n                this.locked = locked;\n            }\n\n            const meetingIdValEl\n                = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_meetingId\"]>value');\n\n            if (meetingIdValEl.length) {\n                this.setMeetingId(meetingIdValEl.text());\n            } else {\n                logger.warn('No meeting ID from backend');\n            }\n\n            const membersOnly = $(result).find('>query>feature[var=\"muc_membersonly\"]').length === 1;\n\n            const lobbyRoomField\n                = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_lobbyroom\"]>value');\n\n            if (this.lobby) {\n                this.lobby.setLobbyRoomJid(lobbyRoomField && lobbyRoomField.length ? lobbyRoomField.text() : undefined);\n            }\n\n            const isBreakoutField\n                = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_isbreakout\"]>value');\n            const isBreakoutRoom = Boolean(isBreakoutField?.text());\n\n            this.breakoutRooms._setIsBreakoutRoom(isBreakoutRoom);\n\n            const breakoutMainRoomField\n                = $(result).find('>query>x[type=\"result\"]>field[var=\"muc#roominfo_breakout_main_room\"]>value');\n\n            if (breakoutMainRoomField?.length) {\n                this.breakoutRooms._setMainRoomJid(breakoutMainRoomField.text());\n            }\n\n            if (membersOnly !== this.membersOnlyEnabled) {\n                this.membersOnlyEnabled = membersOnly;\n                this.eventEmitter.emit(XMPPEvents.MUC_MEMBERS_ONLY_CHANGED, membersOnly);\n            }\n\n        }, error => {\n            GlobalOnErrorHandler.callErrorHandler(error);\n            logger.error('Error getting room info: ', error);\n        });\n    }\n\n    /**\n     * Sets the meeting unique Id (received from the backend).\n     *\n     * @param {string} meetingId - The new meetings id.\n     * @returns {void}\n     */\n    setMeetingId(meetingId) {\n        if (this.meetingId !== meetingId) {\n            if (this.meetingId) {\n                logger.warn(`Meeting Id changed from:${this.meetingId} to:${meetingId}`);\n            }\n            this.meetingId = meetingId;\n            this.eventEmitter.emit(XMPPEvents.MEETING_ID_SET, meetingId);\n        }\n    }\n\n    /**\n     *\n     */\n    createNonAnonymousRoom() {\n        // http://xmpp.org/extensions/xep-0045.html#createroom-reserved\n\n        if (this.options.disableDiscoInfo) {\n            return;\n        }\n\n        const getForm = $iq({ type: 'get',\n            to: this.roomjid })\n            .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' })\n            .c('x', { xmlns: 'jabber:x:data',\n                type: 'submit' });\n\n        this.connection.sendIQ(getForm, form => {\n            if (!$(form).find(\n                    '>query>x[xmlns=\"jabber:x:data\"]'\n                    + '>field[var=\"muc#roomconfig_whois\"]').length) {\n                const errmsg = 'non-anonymous rooms not supported';\n\n                GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n                logger.error(errmsg);\n\n                return;\n            }\n\n            const formSubmit = $iq({ to: this.roomjid,\n                type: 'set' })\n                .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' });\n\n            formSubmit.c('x', { xmlns: 'jabber:x:data',\n                type: 'submit' });\n\n            formSubmit.c('field', { 'var': 'FORM_TYPE' })\n                .c('value')\n                .t('http://jabber.org/protocol/muc#roomconfig').up().up();\n\n            formSubmit.c('field', { 'var': 'muc#roomconfig_whois' })\n                .c('value').t('anyone').up().up();\n\n            this.connection.sendIQ(formSubmit);\n\n        }, error => {\n            GlobalOnErrorHandler.callErrorHandler(error);\n            logger.error('Error getting room configuration form: ', error);\n        });\n    }\n\n    /**\n     * Handles Xmpp Connection status updates.\n     *\n     * @param {Strophe.Status} status - The Strophe connection status.\n     */\n    onConnStatusChanged(status) {\n        // Send cached presence when the XMPP connection is re-established.\n        if (status === XmppConnection.Status.CONNECTED) {\n            this.sendPresence();\n        }\n    }\n\n    /**\n     *\n     * @param pres\n     */\n    onPresence(pres) {\n        const from = pres.getAttribute('from');\n        const member = {};\n        const statusEl = pres.getElementsByTagName('status')[0];\n\n        if (statusEl) {\n            member.status = statusEl.textContent || '';\n        }\n        let hasStatusUpdate = false;\n        let hasVersionUpdate = false;\n        const xElement\n            = pres.getElementsByTagNameNS(\n                'http://jabber.org/protocol/muc#user', 'x')[0];\n        const mucUserItem\n            = xElement && xElement.getElementsByTagName('item')[0];\n\n        member.isReplaceParticipant\n            = pres.getElementsByTagName('flip_device').length;\n\n        member.affiliation\n            = mucUserItem && mucUserItem.getAttribute('affiliation');\n        member.role = mucUserItem && mucUserItem.getAttribute('role');\n\n        // Focus recognition\n        const jid = mucUserItem && mucUserItem.getAttribute('jid');\n\n        member.jid = jid;\n        member.isFocus\n            = jid && jid.indexOf(`${this.moderator.getFocusUserJid()}/`) === 0;\n        member.isHiddenDomain\n            = jid && jid.indexOf('@') > 0\n                && this.options.hiddenDomain\n                    === jid.substring(jid.indexOf('@') + 1, jid.indexOf('/'));\n\n        this.eventEmitter.emit(XMPPEvents.PRESENCE_RECEIVED, {\n            fromHiddenDomain: member.isHiddenDomain,\n            presence: pres\n        });\n\n        const xEl = pres.querySelector('x');\n\n        if (xEl) {\n            xEl.remove();\n        }\n\n        const nodes = [];\n\n        parser.packet2JSON(pres, nodes);\n        this.lastPresences[from] = nodes;\n\n        // process nodes to extract data needed for MUC_JOINED and\n        // MUC_MEMBER_JOINED events\n        const extractIdentityInformation = node => {\n            const identity = {};\n            const userInfo = node.children.find(c => c.tagName === 'user');\n\n            if (userInfo) {\n                identity.user = {};\n                const tags = [ 'id', 'name', 'avatar' ];\n\n                if (this.options.hiddenFromRecorderFeatureEnabled) {\n                    tags.push('hidden-from-recorder');\n                }\n\n                for (const tag of tags) {\n                    const child\n                        = userInfo.children.find(c => c.tagName === tag);\n\n                    if (child) {\n                        identity.user[tag] = child.value;\n                    }\n                }\n            }\n            const groupInfo = node.children.find(c => c.tagName === 'group');\n\n            if (groupInfo) {\n                identity.group = groupInfo.value;\n            }\n\n            return identity;\n        };\n\n        for (let i = 0; i < nodes.length; i++) {\n            const node = nodes[i];\n\n            switch (node.tagName) {\n            case 'bot': {\n                const { attributes } = node;\n\n                if (!attributes) {\n                    break;\n                }\n                const { type } = attributes;\n\n                member.botType = type;\n                break;\n            }\n            case 'nick':\n                member.nick = node.value;\n                break;\n            case 'userId':\n                member.id = node.value;\n                break;\n            case 'stats-id':\n                member.statsID = node.value;\n                break;\n            case 'identity':\n                member.identity = extractIdentityInformation(node);\n                break;\n            case 'features': {\n                member.features = this._extractFeatures(node);\n                break;\n            }\n            case 'stat': {\n                const { attributes } = node;\n\n                if (!attributes) {\n                    break;\n                }\n                const { name } = attributes;\n\n                if (name === 'version') {\n                    member.version = attributes.value;\n                }\n                break;\n            }\n            }\n        }\n\n        if (!this.joined && !this.inProgressEmitted) {\n            const now = this.connectionTimes['muc.join.started'] = window.performance.now();\n\n            logger.log('(TIME) MUC join started:\\t', now);\n\n            this.eventEmitter.emit(XMPPEvents.MUC_JOIN_IN_PROGRESS);\n            this.inProgressEmitted = true;\n        }\n\n        if (from === this.myroomjid) {\n            const newRole\n                = member.affiliation === 'owner' ? member.role : 'none';\n\n            if (this.role !== newRole) {\n                this.role = newRole;\n                this.eventEmitter.emit(\n                    XMPPEvents.LOCAL_ROLE_CHANGED,\n                    this.role);\n            }\n            if (!this.joined) {\n                this.joined = true;\n                const now = this.connectionTimes['muc.joined']\n                    = window.performance.now();\n\n                logger.log('(TIME) MUC joined:\\t', now);\n\n                // set correct initial state of locked\n                if (this.password) {\n                    this.locked = true;\n                }\n\n                // Re-send presence in case any presence updates were added,\n                // but blocked from sending, during the join process.\n                // send the presence only if there was a modification after we had synced it\n                if (this.presenceUpdateTime >= this.presenceSyncTime) {\n                    this.sendPresence();\n                }\n\n                this.eventEmitter.emit(XMPPEvents.MUC_JOINED);\n\n                // Now let's check the disco-info to retrieve the\n                // meeting Id if any\n                !this.options.disableDiscoInfo && this.discoRoomInfo();\n            }\n        } else if (jid === undefined) {\n            logger.info('Ignoring member with undefined JID');\n        } else if (this.members[from] === undefined) {\n            // new participant\n            this.members[from] = member;\n            logger.log('entered', from, member);\n            hasStatusUpdate = member.status !== undefined;\n            hasVersionUpdate = member.version !== undefined;\n            if (member.isFocus) {\n                this._initFocus(from, member.features);\n            } else {\n                // identity is being added to member joined, so external\n                // services can be notified for that (currently identity is\n                // not used inside library)\n                this.eventEmitter.emit(\n                    XMPPEvents.MUC_MEMBER_JOINED,\n                    from,\n                    member.nick,\n                    member.role,\n                    member.isHiddenDomain,\n                    member.statsID,\n                    member.status,\n                    member.identity,\n                    member.botType,\n                    member.jid,\n                    member.features,\n                    member.isReplaceParticipant);\n\n                // we are reporting the status with the join\n                // so we do not want a second event about status update\n                hasStatusUpdate = false;\n            }\n        } else {\n            // Presence update for existing participant\n            // Watch role change:\n            const memberOfThis = this.members[from];\n\n            if (memberOfThis.role !== member.role) {\n                memberOfThis.role = member.role;\n                this.eventEmitter.emit(\n                    XMPPEvents.MUC_ROLE_CHANGED, from, member.role);\n            }\n\n            // affiliation changed\n            if (memberOfThis.affiliation !== member.affiliation) {\n                memberOfThis.affiliation = member.affiliation;\n            }\n\n            // fire event that botType had changed\n            if (memberOfThis.botType !== member.botType) {\n                memberOfThis.botType = member.botType;\n                this.eventEmitter.emit(\n                    XMPPEvents.MUC_MEMBER_BOT_TYPE_CHANGED,\n                    from,\n                    member.botType);\n            }\n\n            if (member.isFocus) {\n                // From time to time first few presences of the focus are not\n                // containing it's jid. That way we can mark later the focus\n                // member instead of not marking it at all and not starting the\n                // conference.\n                // FIXME: Maybe there is a better way to handle this issue. It\n                // seems there is some period of time in prosody that the\n                // configuration form is received but not applied. And if any\n                // participant joins during that period of time the first\n                // presence from the focus won't contain\n                // <item jid=\"focus...\" />.\n                // By default we are disabling the waiting for form submission in order to use the room\n                // and we had enabled by default that jids are public in the room ,\n                // so this case should not happen, if public jid is turned off we will receive the jid\n                // when we become moderator in the room\n                memberOfThis.isFocus = true;\n                this._initFocus(from, member.features);\n            }\n\n            // store the new display name\n            if (member.displayName) {\n                memberOfThis.displayName = member.displayName;\n            }\n\n            // update stored status message to be able to detect changes\n            if (memberOfThis.status !== member.status) {\n                hasStatusUpdate = true;\n                memberOfThis.status = member.status;\n            }\n\n            if (memberOfThis.version !== member.version) {\n                hasVersionUpdate = true;\n                memberOfThis.version = member.version;\n            }\n\n            if (!isEqual(memberOfThis.features, member.features)) {\n                memberOfThis.features = member.features;\n                this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from, member.features);\n            }\n        }\n\n        // after we had fired member or room joined events, lets fire events\n        // for the rest info we got in presence\n        for (let i = 0; i < nodes.length; i++) {\n            const node = nodes[i];\n\n            switch (node.tagName) {\n            case 'nick':\n                if (!member.isFocus) {\n                    const displayName\n                        = this.xmpp.options.displayJids\n                            ? Strophe.getResourceFromJid(from)\n                            : member.nick;\n\n                    this.eventEmitter.emit(\n                        XMPPEvents.DISPLAY_NAME_CHANGED,\n                        from,\n                        displayName);\n                }\n                break;\n            case 'bridgeNotAvailable':\n                if (member.isFocus && !this.noBridgeAvailable) {\n                    this.noBridgeAvailable = true;\n                    this.eventEmitter.emit(XMPPEvents.BRIDGE_DOWN);\n                }\n                break;\n            case 'conference-properties':\n                if (member.isFocus) {\n                    const properties = {};\n\n                    for (let j = 0; j < node.children.length; j++) {\n                        const { attributes } = node.children[j];\n\n                        if (attributes && attributes.key) {\n                            properties[attributes.key] = attributes.value;\n                        }\n                    }\n\n                    this.eventEmitter.emit(XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, properties);\n\n                    // Log if Jicofo supports restart by terminate only once. This conference property does not change\n                    // during the call.\n                    if (typeof this.restartByTerminateSupported === 'undefined') {\n                        this.restartByTerminateSupported = properties['support-terminate-restart'] === 'true';\n                        logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);\n                    }\n                }\n                break;\n            case 'transcription-status': {\n                const { attributes } = node;\n\n                if (!attributes) {\n                    break;\n                }\n\n                const { status } = attributes;\n\n                if (status && status !== this.transcriptionStatus) {\n                    this.transcriptionStatus = status;\n                    this.eventEmitter.emit(\n                        XMPPEvents.TRANSCRIPTION_STATUS_CHANGED,\n                        status\n                    );\n                }\n\n\n                break;\n            }\n            case 'call-control': {\n                const att = node.attributes;\n\n                if (!att) {\n                    break;\n                }\n                this.phoneNumber = att.phone || null;\n                this.phonePin = att.pin || null;\n                this.eventEmitter.emit(XMPPEvents.PHONE_NUMBER_CHANGED);\n                break;\n            }\n            default:\n                this.processNode(node, from);\n            }\n        }\n\n        // Trigger status message update if necessary\n        if (hasStatusUpdate) {\n            this.eventEmitter.emit(\n                XMPPEvents.PRESENCE_STATUS,\n                from,\n                member.status);\n        }\n\n        if (hasVersionUpdate) {\n            logger.info(`Received version for ${jid}: ${member.version}`);\n        }\n    }\n\n    /**\n     * Extracts the features from the presence.\n     * @param node the node to process.\n     * @return features the Set of features where extracted data is added.\n     * @private\n     */\n    _extractFeatures(node) {\n        const features = new Set();\n\n        for (let j = 0; j < node.children.length; j++) {\n            const { attributes } = node.children[j];\n\n            if (attributes && attributes.var) {\n                features.add(attributes.var);\n            }\n        }\n\n        return features;\n    }\n\n    /**\n     * Initialize some properties when the focus participant is verified.\n     * @param from jid of the focus\n     * @param features the features reported in jicofo presence\n     */\n    _initFocus(from, features) {\n        this.focusMucJid = from;\n        this.focusFeatures = features;\n    }\n\n    /**\n     * Sets the special listener to be used for \"command\"s whose name starts\n     * with \"jitsi_participant_\".\n     */\n    setParticipantPropertyListener(listener) {\n        this.participantPropertyListener = listener;\n    }\n\n    /**\n     * Checks if Jicofo supports restarting Jingle session after 'session-terminate'.\n     * @returns {boolean}\n     */\n    supportsRestartByTerminate() {\n        return this.restartByTerminateSupported;\n    }\n\n    /**\n     *\n     * @param node\n     * @param from\n     */\n    processNode(node, from) {\n        // make sure we catch all errors coming from any handler\n        // otherwise we can remove the presence handler from strophe\n        try {\n            let tagHandlers = this.presHandlers[node.tagName];\n\n            if (node.tagName.startsWith('jitsi_participant_')) {\n                tagHandlers = [ this.participantPropertyListener ];\n            }\n\n            if (tagHandlers) {\n                tagHandlers.forEach(handler => {\n                    handler(node, Strophe.getResourceFromJid(from), from);\n                });\n            }\n        } catch (e) {\n            GlobalOnErrorHandler.callErrorHandler(e);\n            logger.error(`Error processing:${node.tagName} node.`, e);\n        }\n    }\n\n    /**\n     * Send text message to the other participants in the conference\n     * @param message\n     * @param elementName\n     */\n    sendMessage(message, elementName) {\n        const msg = $msg({ to: this.roomjid,\n            type: 'groupchat' });\n\n        // We are adding the message in a packet extension. If this element\n        // is different from 'body', we add a custom namespace.\n        // e.g. for 'json-message' extension of message stanza.\n        if (elementName === 'body') {\n            msg.c(elementName, {}, message);\n        } else {\n            msg.c(elementName, { xmlns: 'http://jitsi.org/jitmeet' }, message);\n        }\n\n        this.connection.send(msg);\n        this.eventEmitter.emit(XMPPEvents.SENDING_CHAT_MESSAGE, message);\n    }\n\n    /* eslint-disable max-params */\n    /**\n     * Send private text message to another participant of the conference\n     * @param id id/muc resource of the receiver\n     * @param message\n     * @param elementName\n     */\n    sendPrivateMessage(id, message, elementName) {\n        const msg = $msg({ to: `${this.roomjid}/${id}`,\n            type: 'chat' });\n\n        // We are adding the message in packet. If this element is different\n        // from 'body', we add our custom namespace for the same.\n        // e.g. for 'json-message' message extension.\n        if (elementName === 'body') {\n            msg.c(elementName, message).up();\n        } else {\n            msg.c(elementName, { xmlns: 'http://jitsi.org/jitmeet' }, message)\n                .up();\n        }\n\n        this.connection.send(msg);\n        this.eventEmitter.emit(\n            XMPPEvents.SENDING_PRIVATE_CHAT_MESSAGE, message);\n    }\n    /* eslint-enable max-params */\n\n    /**\n     *\n     * @param subject\n     */\n    setSubject(subject) {\n        const msg = $msg({ to: this.roomjid,\n            type: 'groupchat' });\n\n        msg.c('subject', subject);\n        this.connection.send(msg);\n    }\n\n    /**\n     * Called when participant leaves.\n     * @param jid the jid of the participant that leaves\n     * @param skipEvents optional params to skip any events, including check\n     * whether this is the focus that left\n     */\n    onParticipantLeft(jid, skipEvents) {\n        delete this.lastPresences[jid];\n\n        if (skipEvents) {\n            return;\n        }\n\n        this.eventEmitter.emit(XMPPEvents.MUC_MEMBER_LEFT, jid);\n\n        this.moderator.onMucMemberLeft(jid);\n    }\n\n    /**\n     *\n     * @param pres\n     * @param from\n     */\n    onPresenceUnavailable(pres, from) {\n        // ignore presence\n        if ($(pres).find('>ignore[xmlns=\"http://jitsi.org/jitmeet/\"]').length) {\n            return true;\n        }\n\n        // room destroyed ?\n        const destroySelect = $(pres).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>destroy');\n\n        if (destroySelect.length) {\n            let reason;\n            const reasonSelect\n                = $(pres).find(\n                    '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n                        + '>destroy>reason');\n\n            if (reasonSelect.length) {\n                reason = reasonSelect.text();\n            }\n\n            this.eventEmitter.emit(XMPPEvents.MUC_DESTROYED, reason, destroySelect.attr('jid'));\n            this.connection.emuc.doLeave(this.roomjid);\n\n            return true;\n        }\n\n        // Status code 110 indicates that this notification is \"self-presence\".\n        const isSelfPresence\n            = $(pres)\n                .find(\n                    '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>'\n                        + 'status[code=\"110\"]')\n                .length;\n        const isKick\n            = $(pres)\n                .find(\n                    '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n                        + '>status[code=\"307\"]')\n                .length;\n        const membersKeys = Object.keys(this.members);\n        const isReplaceParticipant = $(pres).find('flip_device').length;\n\n        if (isKick) {\n            const actorSelect\n                = $(pres)\n                .find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>item>actor');\n            let actorNick;\n\n            if (actorSelect.length) {\n                actorNick = actorSelect.attr('nick');\n            }\n\n            let reason;\n            const reasonSelect\n                = $(pres).find(\n                '>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n                + '>item>reason');\n\n            if (reasonSelect.length) {\n                reason = reasonSelect.text();\n            }\n\n            // we first fire the kicked so we can show the participant\n            // who kicked, before notifying that participant left\n            // we fire kicked for us and for any participant kicked\n            this.eventEmitter.emit(\n                XMPPEvents.KICKED,\n                isSelfPresence,\n                actorNick,\n                Strophe.getResourceFromJid(from),\n                reason,\n                isReplaceParticipant);\n        }\n\n        if (isSelfPresence) {\n            // If the status code is 110 this means we're leaving and we would\n            // like to remove everyone else from our view, so we trigger the\n            // event.\n            membersKeys.forEach(jid => {\n                const member = this.members[jid];\n\n                delete this.members[jid];\n                this.onParticipantLeft(jid, member.isFocus);\n            });\n            this.connection.emuc.doLeave(this.roomjid);\n\n            // we fire muc_left only if this is not a kick,\n            // kick has both statuses 110 and 307.\n            if (!isKick) {\n                this.eventEmitter.emit(XMPPEvents.MUC_LEFT);\n            }\n        } else {\n            delete this.members[from];\n            this.onParticipantLeft(from, false);\n        }\n    }\n\n    /**\n     *\n     * @param msg\n     * @param from\n     */\n    onMessage(msg, from) {\n        const type = msg.getAttribute('type');\n\n        if (type === 'error') {\n            const settingsErrorMsg = $(msg).find('>settings-error>text').text();\n\n            if (settingsErrorMsg.length) {\n                this.eventEmitter.emit(XMPPEvents.SETTINGS_ERROR_RECEIVED, settingsErrorMsg);\n\n                return true;\n            }\n            const errorMsg = $(msg).find('>error>text').text();\n\n            this.eventEmitter.emit(XMPPEvents.CHAT_ERROR_RECEIVED, errorMsg);\n\n            return true;\n        }\n\n        const txt = $(msg).find('>body').text();\n        const subject = $(msg).find('>subject');\n\n        if (subject.length) {\n            const subjectText = subject.text();\n\n            if (subjectText || subjectText === '') {\n                this.eventEmitter.emit(XMPPEvents.SUBJECT_CHANGED, subjectText);\n                logger.log(`Subject is changed to ${subjectText}`);\n            }\n        }\n\n        // xep-0203 delay\n        let stamp = $(msg).find('>delay').attr('stamp');\n\n        if (!stamp) {\n            // or xep-0091 delay, UTC timestamp\n            stamp = $(msg).find('>[xmlns=\"jabber:x:delay\"]').attr('stamp');\n\n            if (stamp) {\n                // the format is CCYYMMDDThh:mm:ss\n                const dateParts\n                    = stamp.match(/(\\d{4})(\\d{2})(\\d{2}T\\d{2}:\\d{2}:\\d{2})/);\n\n                stamp = `${dateParts[1]}-${dateParts[2]}-${dateParts[3]}Z`;\n            }\n        }\n\n        if (from === this.roomjid) {\n            let invite;\n\n            if ($(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>status[code=\"104\"]').length) {\n                this.discoRoomInfo();\n            } else if ((invite = $(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>invite'))\n                        && invite.length) {\n                const passwordSelect = $(msg).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>password');\n                let password;\n\n                if (passwordSelect && passwordSelect.length) {\n                    password = passwordSelect.text();\n                }\n\n                this.eventEmitter.emit(XMPPEvents.INVITE_MESSAGE_RECEIVED,\n                    from, invite.attr('from'), txt, password);\n            }\n        }\n\n        const jsonMessage = $(msg).find('>json-message').text();\n\n        if (jsonMessage) {\n            const parsedJson = this.xmpp.tryParseJSONAndVerify(jsonMessage);\n\n            // We emit this event if the message is a valid json, and is not\n            // delivered after a delay, i.e. stamp is undefined.\n            // e.g. - subtitles should not be displayed if delayed.\n            if (parsedJson && stamp === undefined) {\n                this.eventEmitter.emit(XMPPEvents.JSON_MESSAGE_RECEIVED,\n                    from, parsedJson);\n\n                return;\n            }\n        }\n\n        if (txt) {\n            if (type === 'chat') {\n                this.eventEmitter.emit(XMPPEvents.PRIVATE_MESSAGE_RECEIVED,\n                        from, txt, this.myroomjid, stamp);\n            } else if (type === 'groupchat') {\n                this.eventEmitter.emit(XMPPEvents.MESSAGE_RECEIVED,\n                        from, txt, this.myroomjid, stamp);\n            }\n        }\n    }\n\n    /**\n     *\n     * @param pres\n     * @param from\n     */\n    onPresenceError(pres, from) {\n        if ($(pres)\n                .find(\n                    '>error[type=\"auth\"]'\n                        + '>not-authorized['\n                        + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]')\n                .length) {\n            logger.log('on password required', from);\n            this.eventEmitter.emit(XMPPEvents.PASSWORD_REQUIRED);\n        } else if ($(pres)\n                .find(\n                    '>error[type=\"cancel\"]'\n                        + '>not-allowed['\n                        + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]')\n                .length) {\n            const toDomain = Strophe.getDomainFromJid(pres.getAttribute('to'));\n\n            if (toDomain === this.xmpp.options.hosts.anonymousdomain) {\n                // enter the room by replying with 'not-authorized'. This would\n                // result in reconnection from authorized domain.\n                // We're either missing Jicofo/Prosody config for anonymous\n                // domains or something is wrong.\n                this.eventEmitter.emit(XMPPEvents.ROOM_JOIN_ERROR);\n\n            } else {\n                logger.warn('onPresError ', pres);\n                this.eventEmitter.emit(\n                    XMPPEvents.ROOM_CONNECT_NOT_ALLOWED_ERROR);\n            }\n        } else if ($(pres).find('>error>service-unavailable').length) {\n            logger.warn('Maximum users limit for the room has been reached',\n                pres);\n            this.eventEmitter.emit(XMPPEvents.ROOM_MAX_USERS_ERROR);\n            this.connection.emuc.doLeave(this.roomjid);\n        } else if ($(pres)\n            .find(\n                '>error[type=\"auth\"]'\n                + '>registration-required['\n                + 'xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\"]').length) {\n\n            // let's extract the lobby jid from the custom field\n            const lobbyRoomNode = $(pres).find('>error[type=\"auth\"]>lobbyroom');\n            let lobbyRoomJid;\n\n            if (lobbyRoomNode.length) {\n                lobbyRoomJid = lobbyRoomNode.text();\n            } else {\n                // let's fallback to old location of lobbyroom node, TODO: to be removed in the future once\n                // everything is updated\n                const lobbyRoomOldNode = $(pres).find('>lobbyroom');\n\n                if (lobbyRoomOldNode.length) {\n                    lobbyRoomJid = lobbyRoomOldNode.text();\n                }\n            }\n\n            this.eventEmitter.emit(XMPPEvents.ROOM_CONNECT_MEMBERS_ONLY_ERROR, lobbyRoomJid);\n        } else {\n            logger.warn('onPresError ', pres);\n            this.eventEmitter.emit(XMPPEvents.ROOM_CONNECT_ERROR);\n        }\n    }\n\n    /**\n     *\n     * @param jid\n     * @param affiliation\n     */\n    setAffiliation(jid, affiliation) {\n        const grantIQ = $iq({\n            to: this.roomjid,\n            type: 'set'\n        })\n        .c('query', { xmlns: 'http://jabber.org/protocol/muc#admin' })\n        .c('item', {\n            affiliation,\n            nick: Strophe.getResourceFromJid(jid)\n        })\n        .c('reason').t(`Your affiliation has been changed to '${affiliation}'.`)\n        .up().up().up();\n\n        this.connection.sendIQ(\n            grantIQ,\n            result => logger.log('Set affiliation of participant with jid: ', jid, 'to', affiliation, result),\n            error => logger.log('Set affiliation of participant error: ', error));\n    }\n\n    /**\n     *\n     * @param jid\n     * @param reason\n     */\n    kick(jid, reason = 'You have been kicked.') {\n        const kickIQ = $iq({ to: this.roomjid,\n            type: 'set' })\n            .c('query', { xmlns: 'http://jabber.org/protocol/muc#admin' })\n            .c('item', { nick: Strophe.getResourceFromJid(jid),\n                role: 'none' })\n            .c('reason').t(reason).up().up().up();\n\n        this.connection.sendIQ(\n            kickIQ,\n            result => logger.log('Kick participant with jid: ', jid, result),\n            error => logger.log('Kick participant error: ', error));\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     *\n     * @param key\n     * @param onSuccess\n     * @param onError\n     * @param onNotSupported\n     */\n    lockRoom(key, onSuccess, onError, onNotSupported) {\n        // http://xmpp.org/extensions/xep-0045.html#roomconfig\n        this.connection.sendIQ(\n            $iq({\n                to: this.roomjid,\n                type: 'get'\n            })\n                .c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' }),\n            res => {\n                if ($(res)\n                        .find(\n                            '>query>x[xmlns=\"jabber:x:data\"]'\n                                + '>field[var=\"muc#roomconfig_roomsecret\"]')\n                        .length) {\n                    const formsubmit\n                        = $iq({\n                            to: this.roomjid,\n                            type: 'set'\n                        })\n                            .c('query', {\n                                xmlns: 'http://jabber.org/protocol/muc#owner'\n                            });\n\n                    formsubmit.c('x', {\n                        xmlns: 'jabber:x:data',\n                        type: 'submit'\n                    });\n                    formsubmit\n                        .c('field', { 'var': 'FORM_TYPE' })\n                        .c('value')\n                        .t('http://jabber.org/protocol/muc#roomconfig')\n                        .up()\n                        .up();\n                    formsubmit\n                        .c('field', { 'var': 'muc#roomconfig_roomsecret' })\n                        .c('value')\n                        .t(key)\n                        .up()\n                        .up();\n                    formsubmit\n                        .c('field',\n                             { 'var': 'muc#roomconfig_passwordprotectedroom' })\n                        .c('value')\n                        .t(key === null || key.length === 0 ? '0' : '1')\n                        .up()\n                        .up();\n\n                    // if members only enabled\n                    if (this.membersOnlyEnabled) {\n                        formsubmit\n                            .c('field', { 'var': 'muc#roomconfig_membersonly' })\n                            .c('value')\n                            .t('true')\n                            .up()\n                            .up();\n                    }\n\n                    // Fixes a bug in prosody 0.9.+\n                    // https://prosody.im/issues/issue/373\n                    formsubmit\n                        .c('field', { 'var': 'muc#roomconfig_whois' })\n                        .c('value')\n                        .t('anyone')\n                        .up()\n                        .up();\n\n                    this.connection.sendIQ(\n                        formsubmit,\n                        () => {\n\n                            // we set the password in chat room so we can use it\n                            // later when dialing out\n                            this.password = key;\n                            onSuccess();\n                        },\n                        onError);\n                } else {\n                    onNotSupported();\n                }\n            },\n            onError);\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Turns off or on the members only config for the main room.\n     *\n     * @param {boolean} enabled - Whether to turn it on or off.\n     * @param onSuccess - optional callback.\n     * @param onError - optional callback.\n     */\n    setMembersOnly(enabled, onSuccess, onError) {\n        if (enabled && Object.values(this.members).filter(m => !m.isFocus).length) {\n            // first grant membership to all that are in the room\n            // currently there is a bug in prosody where it handles only the first item\n            // that's why we will send iq per member\n            Object.values(this.members).forEach(m => {\n                if (m.jid && !MEMBERS_AFFILIATIONS.includes(m.affiliation)) {\n                    this.xmpp.connection.sendIQ(\n                        $iq({\n                            to: this.roomjid,\n                            type: 'set' })\n                        .c('query', {\n                            xmlns: 'http://jabber.org/protocol/muc#admin' })\n                        .c('item', {\n                            'affiliation': 'member',\n                            'jid': m.jid\n                        }).up().up());\n                }\n            });\n        }\n\n        const errorCallback = onError ? onError : () => {}; // eslint-disable-line no-empty-function\n\n        this.xmpp.connection.sendIQ(\n            $iq({\n                to: this.roomjid,\n                type: 'get'\n            }).c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' }),\n            res => {\n                if ($(res).find('>query>x[xmlns=\"jabber:x:data\"]>field[var=\"muc#roomconfig_membersonly\"]').length) {\n                    const formToSubmit\n                        = $iq({\n                            to: this.roomjid,\n                            type: 'set'\n                        }).c('query', { xmlns: 'http://jabber.org/protocol/muc#owner' });\n\n                    formToSubmit.c('x', {\n                        xmlns: 'jabber:x:data',\n                        type: 'submit'\n                    });\n                    formToSubmit\n                        .c('field', { 'var': 'FORM_TYPE' })\n                        .c('value')\n                        .t('http://jabber.org/protocol/muc#roomconfig')\n                        .up()\n                        .up();\n                    formToSubmit\n                        .c('field', { 'var': 'muc#roomconfig_membersonly' })\n                        .c('value')\n                        .t(enabled ? 'true' : 'false')\n                        .up()\n                        .up();\n\n                    // if room is locked from other participant or we are locking it\n                    if (this.locked) {\n                        formToSubmit\n                            .c('field',\n                                { 'var': 'muc#roomconfig_passwordprotectedroom' })\n                            .c('value')\n                            .t('1')\n                            .up()\n                            .up();\n                    }\n\n                    this.xmpp.connection.sendIQ(formToSubmit, onSuccess, errorCallback);\n                } else {\n                    errorCallback(new Error('Setting members only room not supported!'));\n                }\n            },\n            errorCallback);\n    }\n\n    /**\n     * Adds the key to the presence map, overriding any previous value.\n     * This method is used by jibri.\n     *\n     * @param key The key to add or replace.\n     * @param values The new values.\n     * @returns {boolean|null} <tt>true</tt> if the operation succeeded or <tt>false</tt> when no add or replce was\n     * performed as the value was already there.\n     * @deprecated Use 'addOrReplaceInPresence' instead. TODO: remove it from here and jibri.\n     */\n    addToPresence(key, values) {\n        return this.addOrReplaceInPresence(key, values);\n    }\n\n    /**\n     * Adds the key to the presence map, overriding any previous value.\n     * @param key The key to add or replace.\n     * @param values The new values.\n     * @returns {boolean|null} <tt>true</tt> if the operation succeeded or <tt>false</tt> when no add or replace was\n     * performed as the value was already there.\n     */\n    addOrReplaceInPresence(key, values) {\n        values.tagName = key;\n\n        const matchingNodes = this.presMap.nodes.filter(node => key === node.tagName);\n\n        // if we have found just one, let's check is it the same\n        if (matchingNodes.length === 1 && isEqual(matchingNodes[0], values)) {\n            return false;\n        }\n\n        this.removeFromPresence(key);\n        this.presMap.nodes.push(values);\n        this.presenceUpdateTime = Date.now();\n\n        return true;\n    }\n\n    /**\n     * Retrieves a value from the presence map.\n     *\n     * @param {string} key - The key to find the value for.\n     * @returns {Object?}\n     */\n    getFromPresence(key) {\n        return this.presMap.nodes.find(node => key === node.tagName);\n    }\n\n    /**\n     * Removes a key from the presence map.\n     * @param key\n     */\n    removeFromPresence(key) {\n        const nodes = this.presMap.nodes.filter(node => key !== node.tagName);\n\n        this.presMap.nodes = nodes;\n        this.presenceUpdateTime = Date.now();\n    }\n\n    /**\n     *\n     * @param name\n     * @param handler\n     */\n    addPresenceListener(name, handler) {\n        if (typeof handler !== 'function') {\n            throw new Error('\"handler\" is not a function');\n        }\n        let tagHandlers = this.presHandlers[name];\n\n        if (!tagHandlers) {\n            this.presHandlers[name] = tagHandlers = [];\n        }\n        if (tagHandlers.indexOf(handler) === -1) {\n            tagHandlers.push(handler);\n        } else {\n            logger.warn(\n                `Trying to add the same handler more than once for: ${name}`);\n        }\n    }\n\n    /**\n     *\n     * @param name\n     * @param handler\n     */\n    removePresenceListener(name, handler) {\n        const tagHandlers = this.presHandlers[name];\n        const handlerIdx = tagHandlers ? tagHandlers.indexOf(handler) : -1;\n\n        // eslint-disable-next-line no-negated-condition\n        if (handlerIdx !== -1) {\n            tagHandlers.splice(handlerIdx, 1);\n        } else {\n            logger.warn(`Handler for: ${name} was not registered`);\n        }\n    }\n\n    /**\n     * Checks if the user identified by given <tt>mucJid</tt> is the conference\n     * focus.\n     * @param mucJid the full MUC address of the user to be checked.\n     * @returns {boolean|null} <tt>true</tt> if MUC user is the conference focus\n     * or <tt>false</tt> if is not. When given <tt>mucJid</tt> does not exist in\n     * the MUC then <tt>null</tt> is returned.\n     */\n    isFocus(mucJid) {\n        const member = this.members[mucJid];\n\n        if (member) {\n            return member.isFocus;\n        }\n\n        return null;\n    }\n\n    /**\n     *\n     */\n    isModerator() {\n        return this.role === 'moderator';\n    }\n\n    /**\n     *\n     * @param peerJid\n     */\n    getMemberRole(peerJid) {\n        if (this.members[peerJid]) {\n            return this.members[peerJid].role;\n        }\n\n        return null;\n    }\n\n    /**\n     *\n     * @param mute\n     */\n    addAudioInfoToPresence(mute) {\n        const audioMutedTagName = 'audiomuted';\n\n        // we skip adding it as muted is default value\n        if (mute && !this.getFromPresence(audioMutedTagName)) {\n            return false;\n        }\n\n        return this.addOrReplaceInPresence(\n            audioMutedTagName,\n            {\n                value: mute.toString()\n            });\n    }\n\n    /**\n     *\n     * @param mute\n     */\n    addVideoInfoToPresence(mute) {\n        const videoMutedTagName = 'videomuted';\n\n        // we skip adding it as muted is default value\n        if (mute && !this.getFromPresence(videoMutedTagName)) {\n            return false;\n        }\n\n        return this.addOrReplaceInPresence(\n            videoMutedTagName,\n            {\n                value: mute.toString()\n            });\n    }\n\n    /**\n     * Obtains the info about given media advertised in the MUC presence of\n     * the participant identified by the given endpoint JID.\n     * @param {string} endpointId the endpoint ID mapped to the participant\n     * which corresponds to MUC nickname.\n     * @param {MediaType} mediaType the type of the media for which presence\n     * info will be obtained.\n     * @return {PeerMediaInfo} presenceInfo an object with media presence\n     * info or <tt>null</tt> either if there is no presence available or if\n     * the media type given is invalid.\n     */\n    getMediaPresenceInfo(endpointId, mediaType) {\n        // Will figure out current muted status by looking up owner's presence\n        const pres = this.lastPresences[`${this.roomjid}/${endpointId}`];\n\n        if (!pres) {\n            // No presence available\n            return null;\n        }\n        const data = {\n            muted: true, // muted by default\n            videoType: VideoType.CAMERA // 'camera' by default\n        };\n        let mutedNode = null;\n\n        if (mediaType === MediaType.AUDIO) {\n            mutedNode = filterNodeFromPresenceJSON(pres, 'audiomuted');\n        } else if (mediaType === MediaType.VIDEO) {\n            mutedNode = filterNodeFromPresenceJSON(pres, 'videomuted');\n            const codecTypeNode = filterNodeFromPresenceJSON(pres, 'jitsi_participant_codecType');\n            const videoTypeNode = filterNodeFromPresenceJSON(pres, 'videoType');\n\n            if (videoTypeNode.length > 0) {\n                data.videoType = videoTypeNode[0].value;\n            }\n            if (codecTypeNode.length > 0) {\n                data.codecType = codecTypeNode[0].value;\n            }\n        } else {\n            logger.error(`Unsupported media type: ${mediaType}`);\n\n            return null;\n        }\n\n        if (mutedNode.length > 0) {\n            data.muted = mutedNode[0].value === 'true';\n        }\n\n        return data;\n    }\n\n    /**\n     * Returns the last presence advertised by a MUC member.\n     * @param {string} mucNick\n     * @returns {*}\n     */\n    getLastPresence(mucNick) {\n        return this.lastPresences[`${this.roomjid}/${mucNick}`];\n    }\n\n    /**\n     * Returns true if the SIP calls are supported and false otherwise\n     */\n    isSIPCallingSupported() {\n        if (this.moderator) {\n            return this.moderator.isSipGatewayEnabled();\n        }\n\n        return false;\n    }\n\n    /**\n     * Dials a number.\n     * @param number the number\n     */\n    dial(number) {\n        return this.connection.rayo.dial(number, 'fromnumber',\n            Strophe.getBareJidFromJid(this.myroomjid), this.password,\n            this.focusMucJid);\n    }\n\n    /**\n     * Hangup an existing call\n     */\n    hangup() {\n        return this.connection.rayo.hangup();\n    }\n\n    /**\n     *\n     * @returns {Lobby}\n     */\n    getLobby() {\n        return this.lobby;\n    }\n\n    /**\n     * @returns {AVModeration}\n     */\n    getAVModeration() {\n        return this.avModeration;\n    }\n\n    /**\n     * @returns {BreakoutRooms}\n     */\n    getBreakoutRooms() {\n        return this.breakoutRooms;\n    }\n\n    /**\n     * Returns the phone number for joining the conference.\n     */\n    getPhoneNumber() {\n        return this.phoneNumber;\n    }\n\n    /**\n     * Returns the pin for joining the conference with phone.\n     */\n    getPhonePin() {\n        return this.phonePin;\n    }\n\n    /**\n     * Returns the meeting unique ID if any came from backend.\n     *\n     * @returns {string} - The meeting ID.\n     */\n    getMeetingId() {\n        return this.meetingId;\n    }\n\n    /**\n     * Mutes remote participant.\n     * @param jid of the participant\n     * @param mute\n     * @param mediaType\n     */\n    muteParticipant(jid, mute, mediaType) {\n        logger.info('set mute', mute, jid);\n        const iqToFocus = $iq(\n            { to: this.focusMucJid,\n                type: 'set' })\n            .c('mute', {\n                xmlns: `http://jitsi.org/jitmeet/${mediaType}`,\n                jid\n            })\n            .t(mute.toString())\n            .up();\n\n        this.connection.sendIQ(\n            iqToFocus,\n            result => logger.log('set mute', result),\n            error => logger.log('set mute error', error));\n    }\n\n    /**\n     * TODO: Document\n     * @param iq\n     */\n    onMute(iq) {\n        const from = iq.getAttribute('from');\n\n        if (from !== this.focusMucJid) {\n            logger.warn('Ignored mute from non focus peer');\n\n            return;\n        }\n        const mute = $(iq).find('mute');\n\n        if (mute.length && mute.text() === 'true') {\n            this.eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, mute.attr('actor'));\n        } else {\n            // XXX Why do we support anything but muting? Why do we encode the\n            // value in the text of the element? Why do we use a separate XML\n            // namespace?\n            logger.warn('Ignoring a mute request which does not explicitly '\n                + 'specify a positive mute command.');\n        }\n    }\n\n    /**\n     * TODO: Document\n     * @param iq\n     */\n    onMuteVideo(iq) {\n        const from = iq.getAttribute('from');\n\n        if (from !== this.focusMucJid) {\n            logger.warn('Ignored mute from non focus peer');\n\n            return;\n        }\n        const mute = $(iq).find('mute');\n\n        if (mute.length && mute.text() === 'true') {\n            this.eventEmitter.emit(XMPPEvents.VIDEO_MUTED_BY_FOCUS, mute.attr('actor'));\n        } else {\n            // XXX Why do we support anything but muting? Why do we encode the\n            // value in the text of the element? Why do we use a separate XML\n            // namespace?\n            logger.warn('Ignoring a mute request which does not explicitly '\n                + 'specify a positive mute command.');\n        }\n    }\n\n    /**\n     * Clean any listeners or resources, executed on leaving.\n     */\n    clean() {\n        this._removeConnListeners.forEach(remove => remove());\n        this._removeConnListeners = [];\n\n        this.joined = false;\n        this.inProgressEmitted = false;\n    }\n\n    /**\n     * Leaves the room. Closes the jingle session.\n     * @returns {Promise} which is resolved if XMPPEvents.MUC_LEFT is received\n     * less than 5s after sending presence unavailable. Otherwise the promise is\n     * rejected.\n     */\n    leave() {\n        this.avModeration.dispose();\n        this.breakoutRooms.dispose();\n\n        const promises = [];\n\n        this.lobby?.lobbyRoom && promises.push(this.lobby.leave());\n\n        promises.push(new Promise((resolve, reject) => {\n            let timeout = -1;\n\n            const onMucLeft = (doReject = false) => {\n                this.eventEmitter.removeListener(XMPPEvents.MUC_LEFT, onMucLeft);\n                clearTimeout(timeout);\n                if (doReject) {\n                    // The timeout expired. Make sure we clean the EMUC state.\n                    this.connection.emuc.doLeave(this.roomjid);\n                    reject(new Error('The timeout for the confirmation about leaving the room expired.'));\n                } else {\n                    resolve();\n                }\n            };\n\n            timeout = setTimeout(() => onMucLeft(true), 5000);\n\n            this.clean();\n            this.eventEmitter.on(XMPPEvents.MUC_LEFT, onMucLeft);\n            this.doLeave();\n        }));\n\n        return Promise.allSettled(promises);\n    }\n}\n\n/* eslint-enable newline-per-chained-call */\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nimport ChatRoom from './ChatRoom';\nimport { ConnectionPluginListenable } from './ConnectionPlugin';\n\nconst logger = getLogger(__filename);\n\n/**\n * MUC connection plugin.\n */\nexport default class MucConnectionPlugin extends ConnectionPluginListenable {\n    /**\n     *\n     * @param xmpp\n     */\n    constructor(xmpp) {\n        super();\n        this.xmpp = xmpp;\n        this.rooms = {};\n    }\n\n    /**\n     *\n     * @param connection\n     */\n    init(connection) {\n        super.init(connection);\n\n        // add handlers (just once)\n        this.connection.addHandler(this.onPresence.bind(this), null,\n            'presence', null, null, null, null);\n        this.connection.addHandler(this.onPresenceUnavailable.bind(this),\n            null, 'presence', 'unavailable', null);\n        this.connection.addHandler(this.onPresenceError.bind(this), null,\n            'presence', 'error', null);\n        this.connection.addHandler(this.onMessage.bind(this), null,\n            'message', null, null);\n        this.connection.addHandler(this.onMute.bind(this),\n            'http://jitsi.org/jitmeet/audio', 'iq', 'set', null, null);\n        this.connection.addHandler(this.onMuteVideo.bind(this),\n            'http://jitsi.org/jitmeet/video', 'iq', 'set', null, null);\n    }\n\n    /**\n     *\n     * @param jid\n     * @param password\n     * @param options\n     */\n    createRoom(jid, password, options) {\n        const roomJid = Strophe.getBareJidFromJid(jid);\n\n        if (this.isRoomCreated(roomJid)) {\n            const errmsg = 'You are already in the room!';\n\n            logger.error(errmsg);\n            throw new Error(errmsg);\n        }\n        this.rooms[roomJid] = new ChatRoom(this.connection, jid,\n            password, this.xmpp, options);\n        this.eventEmitter.emit(\n            XMPPEvents.EMUC_ROOM_ADDED, this.rooms[roomJid]);\n\n        return this.rooms[roomJid];\n    }\n\n    /**\n     *  Check if a room with the passed JID is already created.\n     *\n     * @param {string} roomJid - The JID of the room.\n     * @returns {boolean}\n     */\n    isRoomCreated(roomJid) {\n        return roomJid in this.rooms;\n    }\n\n    /**\n     *\n     * @param jid\n     */\n    doLeave(jid) {\n        this.eventEmitter.emit(\n            XMPPEvents.EMUC_ROOM_REMOVED, this.rooms[jid]);\n        delete this.rooms[jid];\n    }\n\n    /**\n     *\n     * @param pres\n     */\n    onPresence(pres) {\n        const from = pres.getAttribute('from');\n\n        // What is this for? A workaround for something?\n        if (pres.getAttribute('type')) {\n            return true;\n        }\n\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        if (!room) {\n            return true;\n        }\n\n        // Parse status.\n        if ($(pres).find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]'\n            + '>status[code=\"201\"]').length) {\n            room.createNonAnonymousRoom();\n        }\n\n        room.onPresence(pres);\n\n        return true;\n    }\n\n    /**\n     *\n     * @param pres\n     */\n    onPresenceUnavailable(pres) {\n        const from = pres.getAttribute('from');\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        if (!room) {\n            return true;\n        }\n\n        room.onPresenceUnavailable(pres, from);\n\n        return true;\n    }\n\n    /**\n     *\n     * @param pres\n     */\n    onPresenceError(pres) {\n        const from = pres.getAttribute('from');\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        if (!room) {\n            return true;\n        }\n\n        room.onPresenceError(pres, from);\n\n        return true;\n    }\n\n    /**\n     *\n     * @param msg\n     */\n    onMessage(msg) {\n        // FIXME: this is a hack. but jingle on muc makes nickchanges hard\n        const from = msg.getAttribute('from');\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        if (!room) {\n            return true;\n        }\n\n        room.onMessage(msg, from);\n\n        return true;\n    }\n\n    /**\n     * TODO: Document\n     * @param iq\n     */\n    onMute(iq) {\n        const from = iq.getAttribute('from');\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        // Returning false would result in the listener being deregistered by Strophe\n        if (!room) {\n            return true;\n        }\n\n        room.onMute(iq);\n\n        return true;\n    }\n\n    /**\n     * TODO: Document\n     * @param iq\n     */\n    onMuteVideo(iq) {\n        const from = iq.getAttribute('from');\n        const room = this.rooms[Strophe.getBareJidFromJid(from)];\n\n        // Returning false would result in the listener being deregistered by Strophe\n        if (!room) {\n            return true;\n        }\n\n        room.onMuteVideo(iq);\n\n        return true;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nconst logger = getLogger('FeatureFlags');\n\n/**\n * A global module for accessing information about different feature flags state.\n */\nclass FeatureFlags {\n    /**\n     * Configures the module.\n     *\n     * @param {boolean} flags.sourceNameSignaling - Enables source names in the signaling.\n     */\n    init(flags) {\n        this._sourceNameSignaling = Boolean(flags.sourceNameSignaling);\n        this._sendMultipleVideoStreams = Boolean(flags.sendMultipleVideoStreams);\n\n        logger.info(`Source name signaling: ${this._sourceNameSignaling},`\n            + ` Send multiple video streams: ${this._sendMultipleVideoStreams}`);\n    }\n\n    /**\n     * Checks if multiple local video streams support is enabled.\n     *\n     * @returns {boolean}\n     */\n    isMultiStreamSupportEnabled() {\n        return this._sourceNameSignaling && this._sendMultipleVideoStreams;\n    }\n\n    /**\n     * Checks if the source name signaling is enabled.\n     *\n     * @returns {boolean}\n     */\n    isSourceNameSignalingEnabled() {\n        return this._sourceNameSignaling;\n    }\n}\n\nexport default new FeatureFlags();\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $build } from 'strophe.js';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nconst logger = getLogger(__filename);\n\n/**\n * Creates a \"source\" XML element for the source described in compact JSON format in [sourceCompactJson].\n * @param {*} owner the endpoint ID of the owner of the source.\n * @param {*} sourceCompactJson the compact JSON representation of the source.\n * @returns the created \"source\" XML element.\n */\nfunction _createSourceExtension(owner, sourceCompactJson) {\n    const node = $build('source', {\n        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n        ssrc: sourceCompactJson.s,\n        name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceCompactJson.n : undefined\n    });\n\n    if (sourceCompactJson.m) {\n        node.c('parameter', {\n            name: 'msid',\n            value: sourceCompactJson.m\n        }).up();\n    }\n    node.c('ssrc-info', {\n        xmlns: 'http://jitsi.org/jitmeet',\n        owner\n    }).up();\n\n    return node.node;\n}\n\n/**\n * Creates an \"ssrc-group\" XML element for the SSRC group described in compact JSON format in [ssrcGroupCompactJson].\n * @param {*} ssrcGroupCompactJson the compact JSON representation of the SSRC group.\n * @returns the created \"ssrc-group\" element.\n */\nfunction _createSsrcGroupExtension(ssrcGroupCompactJson) {\n    const node = $build('ssrc-group', {\n        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n        semantics: _getSemantics(ssrcGroupCompactJson[0])\n    });\n\n    for (let i = 1; i < ssrcGroupCompactJson.length; i++) {\n        node.c('source', {\n            xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0',\n            ssrc: ssrcGroupCompactJson[i]\n        }).up();\n    }\n\n    return node.node;\n}\n\n/**\n * Finds in a Jingle IQ the RTP description element with the given media type. If one does not exists, create it (as\n *  well as the required  \"content\" parent element) and adds it to the IQ.\n * @param {*} iq\n * @param {*} mediaType The media type, \"audio\" or \"video\".\n * @returns the RTP description element with the given media type.\n */\nfunction _getOrCreateRtpDescription(iq, mediaType) {\n    const jingle = $(iq).find('jingle')[0];\n    let content = $(jingle).find(`content[name=\"${mediaType}\"]`);\n    let description;\n\n    if (content.length) {\n        content = content[0];\n    } else {\n        // I'm not suree if \"creator\" and \"senders\" are required.\n        content = $build('content', {\n            name: mediaType\n        }).node;\n        jingle.appendChild(content);\n    }\n\n    description = $(content).find('description');\n\n    if (description.length) {\n        description = description[0];\n    } else {\n        description = $build('description', {\n            xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n            media: mediaType\n        }).node;\n        content.appendChild(description);\n    }\n\n    return description;\n}\n\n/**\n * Converts the short string representing SSRC group semantics in compact JSON format to the standard representation\n * (i.e. convert \"f\" to \"FID\" and \"s\" to \"SIM\").\n * @param {*} str the compact JSON format representation of an SSRC group's semantics.\n * @returns the SSRC group semantics corresponding to [str].\n */\nfunction _getSemantics(str) {\n    if (str === 'f') {\n        return 'FID';\n    } else if (str === 's') {\n        return 'SIM';\n    }\n\n    return null;\n}\n\n/**\n * Reads a JSON-encoded message (from a \"json-message\" element) and extracts source descriptions. Adds the extracted\n * source descriptions to the given Jingle IQ in the standard Jingle format.\n *\n * Encoding sources in this compact JSON format instead of standard Jingle was introduced in order to reduce the\n * network traffic and load on the XMPP server. The format is described in Jicofo [TODO: insert link].\n *\n * @param {*} iq the IQ to which source descriptions will be added.\n * @param {*} jsonMessageXml The XML node for the \"json-message\" element.\n * @returns {Map<string, Array<string>} The audio and video ssrcs extracted from the JSON-encoded message with remote\n * endpoint id as the key.\n */\nexport function expandSourcesFromJson(iq, jsonMessageXml) {\n    let json;\n\n    try {\n        json = JSON.parse(jsonMessageXml.textContent);\n    } catch (error) {\n        logger.error(`json-message XML contained invalid JSON, ignoring: ${jsonMessageXml.textContent}`);\n\n        return null;\n    }\n\n    if (!json?.sources) {\n        // It might be a message of a different type, no need to log.\n        return null;\n    }\n\n    // This is where we'll add \"source\" and \"ssrc-group\" elements. Create them elements if they don't exist.\n    const audioRtpDescription = _getOrCreateRtpDescription(iq, MediaType.AUDIO);\n    const videoRtpDescription = _getOrCreateRtpDescription(iq, MediaType.VIDEO);\n    const ssrcMap = new Map();\n\n    for (const owner in json.sources) {\n        if (json.sources.hasOwnProperty(owner)) {\n            const ssrcs = [];\n            const ownerSources = json.sources[owner];\n\n            // The video sources, video ssrc-groups, audio sources and audio ssrc-groups are encoded in that order in\n            // the elements of the array.\n            const videoSources = ownerSources?.length && ownerSources[0];\n            const videoSsrcGroups = ownerSources?.length > 1 && ownerSources[1];\n            const audioSources = ownerSources?.length > 2 && ownerSources[2];\n            const audioSsrcGroups = ownerSources?.length > 3 && ownerSources[3];\n\n            if (videoSources?.length) {\n                for (let i = 0; i < videoSources.length; i++) {\n                    videoRtpDescription.appendChild(_createSourceExtension(owner, videoSources[i]));\n                    ssrcs.push(videoSources[i]?.s);\n                }\n            }\n\n            if (videoSsrcGroups?.length) {\n                for (let i = 0; i < videoSsrcGroups.length; i++) {\n                    videoRtpDescription.appendChild(_createSsrcGroupExtension(videoSsrcGroups[i]));\n                }\n            }\n            if (audioSources?.length) {\n                for (let i = 0; i < audioSources.length; i++) {\n                    audioRtpDescription.appendChild(_createSourceExtension(owner, audioSources[i]));\n                    ssrcs.push(audioSources[i]?.s);\n                }\n            }\n\n            if (audioSsrcGroups?.length) {\n                for (let i = 0; i < audioSsrcGroups.length; i++) {\n                    audioRtpDescription.appendChild(_createSsrcGroupExtension(audioSsrcGroups[i]));\n                }\n            }\n            ssrcMap.set(owner, ssrcs);\n        }\n    }\n\n    return ssrcMap;\n}\n","\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport browser from '../browser';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * The default frame rate for Screen Sharing.\n */\nexport const SS_DEFAULT_FRAME_RATE = 5;\n\n/**\n * Handles obtaining a stream from a screen capture on different browsers.\n */\nconst ScreenObtainer = {\n    /**\n     * If not <tt>null</tt> it means that the initialization process is still in\n     * progress. It is used to make desktop stream request wait and continue\n     * after it's done.\n     * {@type Promise|null}\n     */\n\n    obtainStream: null,\n\n    /**\n     * Initializes the function used to obtain a screen capture\n     * (this.obtainStream).\n     *\n     * @param {object} options\n     */\n    init(options = {}) {\n        this.options = options;\n        this.obtainStream = this._createObtainStreamMethod();\n\n        if (!this.obtainStream) {\n            logger.info('Desktop sharing disabled');\n        }\n    },\n\n    /**\n     * Returns a method which will be used to obtain the screen sharing stream\n     * (based on the browser type).\n     *\n     * @returns {Function}\n     * @private\n     */\n    _createObtainStreamMethod() {\n        if (browser.isNWJS()) {\n            return (onSuccess, onFailure) => {\n                window.JitsiMeetNW.obtainDesktopStream(\n                    onSuccess,\n                    (error, constraints) => {\n                        let jitsiError;\n\n                        // FIXME:\n                        // This is very very dirty fix for recognising that the\n                        // user have clicked the cancel button from the Desktop\n                        // sharing pick window. The proper solution would be to\n                        // detect this in the NWJS application by checking the\n                        // streamId === \"\". Even better solution would be to\n                        // stop calling GUM from the NWJS app and just pass the\n                        // streamId to lib-jitsi-meet. This way the desktop\n                        // sharing implementation for NWJS and chrome extension\n                        // will be the same and lib-jitsi-meet will be able to\n                        // control the constraints, check the streamId, etc.\n                        //\n                        // I cannot find documentation about \"InvalidStateError\"\n                        // but this is what we are receiving from GUM when the\n                        // streamId for the desktop sharing is \"\".\n\n                        if (error && error.name === 'InvalidStateError') {\n                            jitsiError = new JitsiTrackError(\n                                JitsiTrackErrors.SCREENSHARING_USER_CANCELED\n                            );\n                        } else {\n                            jitsiError = new JitsiTrackError(\n                                error, constraints, [ 'desktop' ]);\n                        }\n                        (typeof onFailure === 'function')\n                            && onFailure(jitsiError);\n                    });\n            };\n        } else if (browser.isElectron()) {\n            return this.obtainScreenOnElectron;\n        } else if (browser.isReactNative() && browser.supportsGetDisplayMedia()) {\n            return this.obtainScreenFromGetDisplayMediaRN;\n        } else if (browser.supportsGetDisplayMedia()) {\n            return this.obtainScreenFromGetDisplayMedia;\n        }\n        logger.log('Screen sharing not supported on ', browser.getName());\n\n        return null;\n    },\n\n    /**\n     * Gets the appropriate constraints for audio sharing.\n     *\n     * @returns {Object|boolean}\n     */\n    _getAudioConstraints() {\n        const { audioQuality } = this.options;\n        const audio = audioQuality?.stereo ? {\n            autoGainControl: false,\n            channelCount: 2,\n            echoCancellation: false,\n            noiseSuppression: false\n        } : true;\n\n        return audio;\n    },\n\n    /**\n     * Checks whether obtaining a screen capture is supported in the current\n     * environment.\n     * @returns {boolean}\n     */\n    isSupported() {\n        return this.obtainStream !== null;\n    },\n\n    /**\n     * Obtains a screen capture stream on Electron.\n     *\n     * @param onSuccess - Success callback.\n     * @param onFailure - Failure callback.\n     */\n    obtainScreenOnElectron(onSuccess, onFailure) {\n        if (window.JitsiMeetScreenObtainer && window.JitsiMeetScreenObtainer.openDesktopPicker) {\n            const { desktopSharingFrameRate, desktopSharingSources } = this.options;\n\n            window.JitsiMeetScreenObtainer.openDesktopPicker(\n                {\n                    desktopSharingSources: desktopSharingSources || [ 'screen', 'window' ]\n                },\n                (streamId, streamType, screenShareAudio = false) => {\n                    if (streamId) {\n                        let audioConstraints = false;\n\n                        if (screenShareAudio) {\n                            audioConstraints = {};\n                            const optionalConstraints = this._getAudioConstraints();\n\n                            if (typeof optionalConstraints !== 'boolean') {\n                                audioConstraints = {\n                                    optional: optionalConstraints\n                                };\n                            }\n\n                            // Audio screen sharing for electron only works for screen type devices.\n                            // i.e. when the user shares the whole desktop.\n                            // Note. The documentation specifies that chromeMediaSourceId should not be present\n                            // which, in the case a users has multiple monitors, leads to them being shared all\n                            // at once. However we tested with chromeMediaSourceId present and it seems to be\n                            // working properly.\n                            if (streamType === 'screen') {\n                                audioConstraints.mandatory = {\n                                    chromeMediaSource: 'desktop'\n                                };\n                            }\n                        }\n\n                        const constraints = {\n                            audio: audioConstraints,\n                            video: {\n                                mandatory: {\n                                    chromeMediaSource: 'desktop',\n                                    chromeMediaSourceId: streamId,\n                                    minFrameRate: desktopSharingFrameRate?.min ?? SS_DEFAULT_FRAME_RATE,\n                                    maxFrameRate: desktopSharingFrameRate?.max ?? SS_DEFAULT_FRAME_RATE,\n                                    maxWidth: window.screen.width,\n                                    maxHeight: window.screen.height\n                                }\n                            }\n                        };\n\n                        // We have to use the old API on Electron to get a desktop stream.\n                        navigator.mediaDevices.getUserMedia(constraints)\n                            .then(stream => onSuccess({\n                                stream,\n                                sourceId: streamId,\n                                sourceType: streamType\n                            }), onFailure);\n                    } else {\n                        // As noted in Chrome Desktop Capture API:\n                        // If user didn't select any source (i.e. canceled the prompt)\n                        // then the callback is called with an empty streamId.\n                        onFailure(new JitsiTrackError(JitsiTrackErrors.SCREENSHARING_USER_CANCELED));\n                    }\n                },\n                err => onFailure(new JitsiTrackError(\n                    JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR,\n                    err\n                ))\n            );\n        } else {\n            onFailure(new JitsiTrackError(JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_NOT_FOUND));\n        }\n    },\n\n    /**\n     * Obtains a screen capture stream using getDisplayMedia.\n     *\n     * @param callback - The success callback.\n     * @param errorCallback - The error callback.\n     */\n    obtainScreenFromGetDisplayMedia(callback, errorCallback) {\n        let getDisplayMedia;\n\n        if (navigator.getDisplayMedia) {\n            getDisplayMedia = navigator.getDisplayMedia.bind(navigator);\n        } else {\n            // eslint-disable-next-line max-len\n            getDisplayMedia = navigator.mediaDevices.getDisplayMedia.bind(navigator.mediaDevices);\n        }\n\n        const { desktopSharingFrameRate } = this.options;\n        const video = typeof desktopSharingFrameRate === 'object' ? { frameRate: desktopSharingFrameRate } : true;\n        const audio = this._getAudioConstraints();\n\n        // At the time of this writing 'min' constraint for fps is not supported by getDisplayMedia.\n        video.frameRate && delete video.frameRate.min;\n\n        const constraints = {\n            video,\n            audio,\n            cursor: 'always'\n        };\n\n        logger.info('Using getDisplayMedia for screen sharing', constraints);\n\n        getDisplayMedia(constraints)\n            .then(stream => {\n                callback({\n                    stream,\n                    sourceId: stream.id\n                });\n            })\n            .catch(error => {\n                const errorDetails = {\n                    errorName: error && error.name,\n                    errorMsg: error && error.message,\n                    errorStack: error && error.stack\n                };\n\n                logger.error('getDisplayMedia error', constraints, errorDetails);\n\n                if (errorDetails.errorMsg && errorDetails.errorMsg.indexOf('denied by system') !== -1) {\n                    // On Chrome this is the only thing different between error returned when user cancels\n                    // and when no permission was given on the OS level.\n                    errorCallback(new JitsiTrackError(JitsiTrackErrors.PERMISSION_DENIED));\n\n                    return;\n                }\n\n                errorCallback(new JitsiTrackError(JitsiTrackErrors.SCREENSHARING_USER_CANCELED));\n            });\n    },\n\n    /**\n     * Obtains a screen capture stream using getDisplayMedia.\n     *\n     * @param callback - The success callback.\n     * @param errorCallback - The error callback.\n     */\n    obtainScreenFromGetDisplayMediaRN(callback, errorCallback) {\n        logger.info('Using getDisplayMedia for screen sharing');\n\n        navigator.mediaDevices.getDisplayMedia({ video: true })\n            .then(stream => {\n                callback({\n                    stream,\n                    sourceId: stream.id });\n            })\n            .catch(() => {\n                errorCallback(new JitsiTrackError(JitsiTrackErrors\n                    .SCREENSHARING_USER_CANCELED));\n            });\n    },\n\n    /**\n     * Sets the max frame rate to be used for a desktop track capture.\n     *\n     * @param {number} maxFps capture frame rate to be used for desktop tracks.\n     * @returns {void}\n     */\n    setDesktopSharingFrameRate(maxFps) {\n        logger.info(`Setting the desktop capture rate to ${maxFps}`);\n\n        this.options.desktopSharingFrameRate = {\n            min: SS_DEFAULT_FRAME_RATE,\n            max: maxFps\n        };\n    }\n};\n\nexport default ScreenObtainer;\n","import { getLogger } from '@jitsi/logger';\nconst logger = getLogger(__filename);\n\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport browser from '../browser';\nimport RandomUtil from '../util/RandomUtil';\n\nconst SDPUtil = {\n    filterSpecialChars(text) {\n        // XXX Neither one of the falsy values (e.g. null, undefined, false,\n        // \"\", etc.) \"contain\" special chars.\n        // eslint-disable-next-line no-useless-escape\n        return text ? text.replace(/[\\\\\\/\\{,\\}\\+]/g, '') : text;\n    },\n    iceparams(mediadesc, sessiondesc) {\n        let data = null;\n        let pwd, ufrag;\n\n        if ((ufrag = SDPUtil.findLine(mediadesc, 'a=ice-ufrag:', sessiondesc))\n                && (pwd\n                    = SDPUtil.findLine(\n                        mediadesc,\n                        'a=ice-pwd:',\n                        sessiondesc))) {\n            data = {\n                ufrag: SDPUtil.parseICEUfrag(ufrag),\n                pwd: SDPUtil.parseICEPwd(pwd)\n            };\n        }\n\n        return data;\n    },\n    parseICEUfrag(line) {\n        return line.substring(12);\n    },\n    buildICEUfrag(frag) {\n        return `a=ice-ufrag:${frag}`;\n    },\n    parseICEPwd(line) {\n        return line.substring(10);\n    },\n    buildICEPwd(pwd) {\n        return `a=ice-pwd:${pwd}`;\n    },\n    parseMID(line) {\n        return line.substring(6);\n    },\n\n    /**\n     * Finds the MSID attribute in the given array of SSRC attribute lines and returns the value.\n     *\n     * @param {string[]} ssrcLines - an array of lines similar to 'a:213123 msid:stream-id track-id'.\n     * @returns {undefined|string}\n     */\n    parseMSIDAttribute(ssrcLines) {\n        const msidLine = ssrcLines.find(line => line.indexOf(' msid:') > 0);\n\n        if (!msidLine) {\n            return undefined;\n        }\n\n        const v = msidLine.substring(msidLine.indexOf(' msid:') + 6 /* the length of ' msid:' */);\n\n        return SDPUtil.filterSpecialChars(v);\n    },\n    parseMLine(line) {\n        const data = {};\n        const parts = line.substring(2).split(' ');\n\n        data.media = parts.shift();\n        data.port = parts.shift();\n        data.proto = parts.shift();\n        if (parts[parts.length - 1] === '') { // trailing whitespace\n            parts.pop();\n        }\n        data.fmt = parts;\n\n        return data;\n    },\n    buildMLine(mline) {\n        return (\n            `m=${mline.media} ${mline.port} ${mline.proto} ${\n                mline.fmt.join(' ')}`);\n    },\n    parseRTPMap(line) {\n        const data = {};\n        let parts = line.substring(9).split(' ');\n\n        data.id = parts.shift();\n        parts = parts[0].split('/');\n        data.name = parts.shift();\n        data.clockrate = parts.shift();\n        data.channels = parts.length ? parts.shift() : '1';\n\n        return data;\n    },\n\n    /**\n     * Parses SDP line \"a=sctpmap:...\" and extracts SCTP port from it.\n     * @param line eg. \"a=sctpmap:5000 webrtc-datachannel\"\n     * @returns [SCTP port number, protocol, streams]\n     */\n    parseSCTPMap(line) {\n        const parts = line.substring(10).split(' ');\n        const sctpPort = parts[0];\n        const protocol = parts[1];\n\n        // Stream count is optional\n        const streamCount = parts.length > 2 ? parts[2] : null;\n\n\n        return [ sctpPort, protocol, streamCount ];// SCTP port\n    },\n    parseSCTPPort(line) {\n        return line.substring(12);\n    },\n    buildRTPMap(el) {\n        let line\n            = `a=rtpmap:${el.getAttribute('id')} ${el.getAttribute('name')}/${\n                el.getAttribute('clockrate')}`;\n\n        if (el.getAttribute('channels')\n            && el.getAttribute('channels') !== '1') {\n            line += `/${el.getAttribute('channels')}`;\n        }\n\n        return line;\n    },\n    parseCrypto(line) {\n        const data = {};\n        const parts = line.substring(9).split(' ');\n\n        data.tag = parts.shift();\n        data['crypto-suite'] = parts.shift();\n        data['key-params'] = parts.shift();\n        if (parts.length) {\n            data['session-params'] = parts.join(' ');\n        }\n\n        return data;\n    },\n    parseFingerprint(line) { // RFC 4572\n        const data = {};\n        const parts = line.substring(14).split(' ');\n\n        data.hash = parts.shift();\n        data.fingerprint = parts.shift();\n\n        // TODO assert that fingerprint satisfies 2UHEX *(\":\" 2UHEX) ?\n        return data;\n    },\n    parseFmtp(line) {\n        const data = [];\n        let parts = line.split(' ');\n\n        parts.shift();\n        parts = parts.join(' ').split(';');\n        for (let i = 0; i < parts.length; i++) {\n            let key = parts[i].split('=')[0];\n\n            while (key.length && key[0] === ' ') {\n                key = key.substring(1);\n            }\n            const value = parts[i].split('=')[1];\n\n            if (key && value) {\n                data.push({ name: key,\n                    value });\n            } else if (key) {\n                // rfc 4733 (DTMF) style stuff\n                data.push({ name: '',\n                    value: key });\n            }\n        }\n\n        return data;\n    },\n    parseICECandidate(line) {\n        const candidate = {};\n        const elems = line.split(' ');\n\n        candidate.foundation = elems[0].substring(12);\n        candidate.component = elems[1];\n        candidate.protocol = elems[2].toLowerCase();\n        candidate.priority = elems[3];\n        candidate.ip = elems[4];\n        candidate.port = elems[5];\n\n        // elems[6] => \"typ\"\n        candidate.type = elems[7];\n        candidate.generation = 0; // default value, may be overwritten below\n        for (let i = 8; i < elems.length; i += 2) {\n            switch (elems[i]) {\n            case 'raddr':\n                candidate['rel-addr'] = elems[i + 1];\n                break;\n            case 'rport':\n                candidate['rel-port'] = elems[i + 1];\n                break;\n            case 'generation':\n                candidate.generation = elems[i + 1];\n                break;\n            case 'tcptype':\n                candidate.tcptype = elems[i + 1];\n                break;\n            default: // TODO\n                logger.debug(\n                    `parseICECandidate not translating \"${\n                        elems[i]}\" = \"${elems[i + 1]}\"`);\n            }\n        }\n        candidate.network = '1';\n\n        // not applicable to SDP -- FIXME: should be unique, not just random\n        // eslint-disable-next-line newline-per-chained-call\n        candidate.id = Math.random().toString(36).substr(2, 10);\n\n        return candidate;\n    },\n    buildICECandidate(cand) {\n        let line = [\n            `a=candidate:${cand.foundation}`,\n            cand.component,\n            cand.protocol,\n            cand.priority,\n            cand.ip,\n            cand.port,\n            'typ',\n            cand.type\n        ].join(' ');\n\n        line += ' ';\n        switch (cand.type) {\n        case 'srflx':\n        case 'prflx':\n        case 'relay':\n            if (cand.hasOwnAttribute('rel-addr')\n                    && cand.hasOwnAttribute('rel-port')) {\n                line += 'raddr';\n                line += ' ';\n                line += cand['rel-addr'];\n                line += ' ';\n                line += 'rport';\n                line += ' ';\n                line += cand['rel-port'];\n                line += ' ';\n            }\n            break;\n        }\n        if (cand.hasOwnAttribute('tcptype')) {\n            line += 'tcptype';\n            line += ' ';\n            line += cand.tcptype;\n            line += ' ';\n        }\n        line += 'generation';\n        line += ' ';\n        line += cand.hasOwnAttribute('generation') ? cand.generation : '0';\n\n        return line;\n    },\n    parseSSRC(desc) {\n        // proprietary mapping of a=ssrc lines\n        // TODO: see \"Jingle RTP Source Description\" by Juberti and P. Thatcher\n        // on google docs and parse according to that\n        const data = new Map();\n        const lines = desc.split('\\r\\n');\n\n        for (let i = 0; i < lines.length; i++) {\n            if (lines[i].substring(0, 7) === 'a=ssrc:') {\n                // FIXME: Use regex to smartly find the ssrc.\n                const ssrc = lines[i].split('a=ssrc:')[1].split(' ')[0];\n\n                if (!data.get(ssrc)) {\n                    data.set(ssrc, []);\n                }\n\n                data.get(ssrc).push(lines[i]);\n            }\n        }\n\n        return data;\n    },\n\n    /**\n     * Gets the source name out of the name attribute \"a=ssrc:254321 name:name1\".\n     *\n     * @param {string[]} ssrcLines\n     * @returns {string | undefined}\n     */\n    parseSourceNameLine(ssrcLines) {\n        const sourceNameLine = ssrcLines.find(ssrcSdpLine => ssrcSdpLine.indexOf(' name:') > 0);\n\n        // Everything past the \"name:\" part\n        return sourceNameLine?.substring(sourceNameLine.indexOf(' name:') + 6);\n    },\n    parseRTCPFB(line) {\n        const parts = line.substr(10).split(' ');\n        const data = {};\n\n        data.pt = parts.shift();\n        data.type = parts.shift();\n        data.params = parts;\n\n        return data;\n    },\n    parseExtmap(line) {\n        const parts = line.substr(9).split(' ');\n        const data = {};\n\n        data.value = parts.shift();\n        if (data.value.indexOf('/') === -1) {\n            data.direction = 'both';\n        } else {\n            data.direction = data.value.substr(data.value.indexOf('/') + 1);\n            data.value = data.value.substr(0, data.value.indexOf('/'));\n        }\n        data.uri = parts.shift();\n        data.params = parts;\n\n        return data;\n    },\n    findLine(haystack, needle, sessionpart) {\n        let lines = haystack.split('\\r\\n');\n\n        for (let i = 0; i < lines.length; i++) {\n            if (lines[i].substring(0, needle.length) === needle) {\n                return lines[i];\n            }\n        }\n        if (!sessionpart) {\n            return false;\n        }\n\n        // search session part\n        lines = sessionpart.split('\\r\\n');\n        for (let j = 0; j < lines.length; j++) {\n            if (lines[j].substring(0, needle.length) === needle) {\n                return lines[j];\n            }\n        }\n\n        return false;\n    },\n    findLines(haystack, needle, sessionpart) {\n        let lines = haystack.split('\\r\\n');\n        const needles = [];\n\n        for (let i = 0; i < lines.length; i++) {\n            if (lines[i].substring(0, needle.length) === needle) {\n                needles.push(lines[i]);\n            }\n        }\n        if (needles.length || !sessionpart) {\n            return needles;\n        }\n\n        // search session part\n        lines = sessionpart.split('\\r\\n');\n        for (let j = 0; j < lines.length; j++) {\n            if (lines[j].substring(0, needle.length) === needle) {\n                needles.push(lines[j]);\n            }\n        }\n\n        return needles;\n    },\n    candidateToJingle(line) {\n        // a=candidate:2979166662 1 udp 2113937151 192.168.2.100 57698 typ host\n        // generation 0\n        //      <candidate component=... foundation=... generation=... id=...\n        // ip=... network=... port=... priority=... protocol=... type=.../>\n        if (line.indexOf('candidate:') === 0) {\n            // eslint-disable-next-line no-param-reassign\n            line = `a=${line}`;\n        } else if (line.substring(0, 12) !== 'a=candidate:') {\n            logger.warn(\n                'parseCandidate called with a line that is not a candidate'\n                    + ' line');\n            logger.warn(line);\n\n            return null;\n        }\n        if (line.substring(line.length - 2) === '\\r\\n') { // chomp it\n            // eslint-disable-next-line no-param-reassign\n            line = line.substring(0, line.length - 2);\n        }\n        const candidate = {};\n        const elems = line.split(' ');\n\n        if (elems[6] !== 'typ') {\n            logger.warn('did not find typ in the right place');\n            logger.warn(line);\n\n            return null;\n        }\n        candidate.foundation = elems[0].substring(12);\n        candidate.component = elems[1];\n        candidate.protocol = elems[2].toLowerCase();\n        candidate.priority = elems[3];\n        candidate.ip = elems[4];\n        candidate.port = elems[5];\n\n        // elems[6] => \"typ\"\n        candidate.type = elems[7];\n\n        candidate.generation = '0'; // default, may be overwritten below\n        for (let i = 8; i < elems.length; i += 2) {\n            switch (elems[i]) {\n            case 'raddr':\n                candidate['rel-addr'] = elems[i + 1];\n                break;\n            case 'rport':\n                candidate['rel-port'] = elems[i + 1];\n                break;\n            case 'generation':\n                candidate.generation = elems[i + 1];\n                break;\n            case 'tcptype':\n                candidate.tcptype = elems[i + 1];\n                break;\n            default: // TODO\n                logger.debug(`not translating \"${elems[i]}\" = \"${elems[i + 1]}\"`);\n            }\n        }\n        candidate.network = '1';\n\n        // not applicable to SDP -- FIXME: should be unique, not just random\n        // eslint-disable-next-line newline-per-chained-call\n        candidate.id = Math.random().toString(36).substr(2, 10);\n\n        return candidate;\n    },\n    candidateFromJingle(cand) {\n        let line = 'a=candidate:';\n\n        line += cand.getAttribute('foundation');\n        line += ' ';\n        line += cand.getAttribute('component');\n        line += ' ';\n\n        let protocol = cand.getAttribute('protocol');\n\n        // use tcp candidates for FF\n\n        if (browser.isFirefox() && protocol.toLowerCase() === 'ssltcp') {\n            protocol = 'tcp';\n        }\n\n        line += protocol; // .toUpperCase(); // chrome M23 doesn't like this\n        line += ' ';\n        line += cand.getAttribute('priority');\n        line += ' ';\n        line += cand.getAttribute('ip');\n        line += ' ';\n        line += cand.getAttribute('port');\n        line += ' ';\n        line += 'typ';\n        line += ` ${cand.getAttribute('type')}`;\n        line += ' ';\n        switch (cand.getAttribute('type')) {\n        case 'srflx':\n        case 'prflx':\n        case 'relay':\n            if (cand.getAttribute('rel-addr')\n                    && cand.getAttribute('rel-port')) {\n                line += 'raddr';\n                line += ' ';\n                line += cand.getAttribute('rel-addr');\n                line += ' ';\n                line += 'rport';\n                line += ' ';\n                line += cand.getAttribute('rel-port');\n                line += ' ';\n            }\n            break;\n        }\n        if (protocol.toLowerCase() === 'tcp') {\n            line += 'tcptype';\n            line += ' ';\n            line += cand.getAttribute('tcptype');\n            line += ' ';\n        }\n        line += 'generation';\n        line += ' ';\n        line += cand.getAttribute('generation') || '0';\n\n        return `${line}\\r\\n`;\n    },\n\n    /**\n     * Parse the 'most' primary video ssrc from the given m line\n     * @param {object} mLine object as parsed from transform.parse\n     * @return {number} the primary video ssrc from the given m line\n     */\n    parsePrimaryVideoSsrc(videoMLine) {\n        const numSsrcs = videoMLine.ssrcs\n            .map(ssrcInfo => ssrcInfo.id)\n            .filter((ssrc, index, array) => array.indexOf(ssrc) === index)\n            .length;\n        const numGroups\n            = (videoMLine.ssrcGroups && videoMLine.ssrcGroups.length) || 0;\n\n        if (numSsrcs > 1 && numGroups === 0) {\n            // Ambiguous, can't figure out the primary\n            return;\n        }\n        let primarySsrc = null;\n\n        if (numSsrcs === 1) {\n            primarySsrc = videoMLine.ssrcs[0].id;\n        } else if (numSsrcs === 2) {\n            // Can figure it out if there's an FID group\n            const fidGroup\n                = videoMLine.ssrcGroups.find(\n                    group => group.semantics === 'FID');\n\n            if (fidGroup) {\n                primarySsrc = fidGroup.ssrcs.split(' ')[0];\n            }\n        } else if (numSsrcs >= 3) {\n            // Can figure it out if there's a sim group\n            const simGroup\n                = videoMLine.ssrcGroups.find(\n                    group => group.semantics === 'SIM');\n\n            if (simGroup) {\n                primarySsrc = simGroup.ssrcs.split(' ')[0];\n            }\n        }\n\n        return primarySsrc;\n    },\n\n    /**\n     * Generate an ssrc\n     * @returns {number} an ssrc\n     */\n    generateSsrc() {\n        return RandomUtil.randomInt(1, 0xffffffff);\n    },\n\n    /**\n     * Get an attribute for the given ssrc with the given attributeName\n     *  from the given mline\n     * @param {object} mLine an mLine object as parsed from transform.parse\n     * @param {number} ssrc the ssrc for which an attribute is desired\n     * @param {string} attributeName the name of the desired attribute\n     * @returns {string} the value corresponding to the given ssrc\n     *  and attributeName\n     */\n    getSsrcAttribute(mLine, ssrc, attributeName) {\n        for (let i = 0; i < mLine.ssrcs.length; ++i) {\n            const ssrcLine = mLine.ssrcs[i];\n\n            if (ssrcLine.id === ssrc\n                && ssrcLine.attribute === attributeName) {\n                return ssrcLine.value;\n            }\n        }\n    },\n\n    /**\n     * Parses the ssrcs from the group sdp line and\n     *  returns them as a list of numbers\n     * @param {object} the ssrcGroup object as parsed from\n     *  sdp-transform\n     * @returns {list<number>} a list of the ssrcs in the group\n     *  parsed as numbers\n     */\n    parseGroupSsrcs(ssrcGroup) {\n        return ssrcGroup\n            .ssrcs\n            .split(' ')\n            .map(ssrcStr => parseInt(ssrcStr, 10));\n    },\n\n    /**\n     * Get the mline of the given type from the given sdp\n     * @param {object} sdp sdp as parsed from transform.parse\n     * @param {string} type the type of the desired mline (e.g. \"video\")\n     * @returns {object} a media object\n     */\n    getMedia(sdp, type) {\n        return sdp.media.find(m => m.type === type);\n    },\n\n    /**\n     * Extracts the ICE username fragment from an SDP string.\n     * @param {string} sdp the SDP in raw text format\n     */\n    getUfrag(sdp) {\n        const ufragLines\n            = sdp.split('\\n').filter(line => line.startsWith('a=ice-ufrag:'));\n\n        if (ufragLines.length > 0) {\n            return ufragLines[0].substr('a=ice-ufrag:'.length);\n        }\n    },\n\n    /**\n     * Sets the given codecName as the preferred codec by moving it to the beginning\n     * of the payload types list (modifies the given mline in place). All instances\n     * of the codec are moved up.\n     * @param {object} mLine the mline object from an sdp as parsed by transform.parse\n     * @param {string} codecName the name of the preferred codec\n     */\n    preferCodec(mline, codecName) {\n        if (!mline || !codecName) {\n            return;\n        }\n\n        const matchingPayloadTypes = mline.rtp\n            .filter(rtp => rtp.codec && rtp.codec.toLowerCase() === codecName.toLowerCase())\n            .map(rtp => rtp.payload);\n\n        if (matchingPayloadTypes) {\n            // Call toString() on payloads to get around an issue within SDPTransform that sets\n            // payloads as a number, instead of a string, when there is only one payload.\n            const payloadTypes\n                = mline.payloads\n                .toString()\n                .split(' ')\n                .map(p => parseInt(p, 10));\n\n            for (const pt of matchingPayloadTypes.reverse()) {\n                const payloadIndex = payloadTypes.indexOf(pt);\n\n                payloadTypes.splice(payloadIndex, 1);\n                payloadTypes.unshift(pt);\n            }\n            mline.payloads = payloadTypes.join(' ');\n        }\n    },\n\n    /**\n     * Strips the given codec from the given mline. All related RTX payload\n     * types are also stripped. If the resulting mline would have no codecs,\n     * it's disabled.\n     *\n     * @param {object} mLine the mline object from an sdp as parsed by transform.parse.\n     * @param {string} codecName the name of the codec which will be stripped.\n     * @param {boolean} highProfile determines if only the high profile H264 codec needs to be\n     * stripped from the sdp when the passed codecName is H264.\n     */\n    stripCodec(mLine, codecName, highProfile = false) {\n        if (!mLine || !codecName) {\n            return;\n        }\n\n        const h264Pts = [];\n        let removePts = [];\n        const stripH264HighCodec = codecName.toLowerCase() === CodecMimeType.H264 && highProfile;\n\n        for (const rtp of mLine.rtp) {\n            if (rtp.codec\n                && rtp.codec.toLowerCase() === codecName.toLowerCase()) {\n                if (stripH264HighCodec) {\n                    h264Pts.push(rtp.payload);\n                } else {\n                    removePts.push(rtp.payload);\n                }\n            }\n        }\n\n        // high profile H264 codecs have 64 as the first two bytes of the profile-level-id.\n        if (stripH264HighCodec) {\n            removePts = mLine.fmtp\n                .filter(item => h264Pts.indexOf(item.payload) > -1 && item.config.includes('profile-level-id=64'))\n                .map(item => item.payload);\n        }\n\n        if (removePts.length > 0) {\n            // We also need to remove the payload types that are related to RTX\n            // for the codecs we want to disable.\n            const rtxApts = removePts.map(item => `apt=${item}`);\n            const rtxPts = mLine.fmtp.filter(\n                item => rtxApts.indexOf(item.config) !== -1);\n\n            removePts.push(...rtxPts.map(item => item.payload));\n\n            // Call toString() on payloads to get around an issue within\n            // SDPTransform that sets payloads as a number, instead of a string,\n            // when there is only one payload.\n            const allPts = mLine.payloads\n                .toString()\n                .split(' ')\n                .map(Number);\n            const keepPts = allPts.filter(pt => removePts.indexOf(pt) === -1);\n\n            if (keepPts.length === 0) {\n                // There are no other codecs, disable the stream.\n                mLine.port = 0;\n                mLine.direction = MediaDirection.INACTIVE;\n                mLine.payloads = '*';\n            } else {\n                mLine.payloads = keepPts.join(' ');\n            }\n\n            mLine.rtp = mLine.rtp.filter(\n                item => keepPts.indexOf(item.payload) !== -1);\n            mLine.fmtp = mLine.fmtp.filter(\n                item => keepPts.indexOf(item.payload) !== -1);\n            if (mLine.rtcpFb) {\n                mLine.rtcpFb = mLine.rtcpFb.filter(\n                    item => keepPts.indexOf(item.payload) !== -1);\n            }\n        }\n    }\n};\n\nexport default SDPUtil;\n","/* global $ */\n\nimport clonedeep from 'lodash.clonedeep';\nimport transform from 'sdp-transform';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport SDPUtil from './SDPUtil';\n\n/**\n *\n * @param sdp\n */\nexport default function SDP(sdp) {\n    const media = sdp.split('\\r\\nm=');\n\n    for (let i = 1, length = media.length; i < length; i++) {\n        let mediaI = `m=${media[i]}`;\n\n        if (i !== length - 1) {\n            mediaI += '\\r\\n';\n        }\n        media[i] = mediaI;\n    }\n    const session = `${media.shift()}\\r\\n`;\n\n    this.media = media;\n    this.raw = session + media.join('');\n    this.session = session;\n}\n\n/**\n * A flag will make {@link transportToJingle} and {@link jingle2media} replace\n * ICE candidates IPs with invalid value of '1.1.1.1' which will cause ICE\n * failure. The flag is used in the automated testing.\n * @type {boolean}\n */\nSDP.prototype.failICE = false;\n\n/**\n * Whether or not to remove TCP ice candidates when translating from/to jingle.\n * @type {boolean}\n */\nSDP.prototype.removeTcpCandidates = false;\n\n/**\n * Whether or not to remove UDP ice candidates when translating from/to jingle.\n * @type {boolean}\n */\nSDP.prototype.removeUdpCandidates = false;\n\n/**\n * Adds a new m-line to the description so that a new local source can then be attached to the transceiver that gets\n * added after a reneogtiation cycle.\n *\n * @param {Mediatype} mediaType media type of the new source that is being added.\n */\nSDP.prototype.addMlineForNewLocalSource = function(mediaType) {\n    const mid = this.media.length;\n    const sdp = transform.parse(this.raw);\n    const mline = clonedeep(sdp.media.find(m => m.type === mediaType));\n\n    // Edit media direction, mid and remove the existing ssrc lines in the m-line.\n    mline.mid = mid;\n    mline.direction = MediaDirection.RECVONLY;\n\n    // Remove the ssrcs and source groups.\n    mline.msid = undefined;\n    mline.ssrcs = undefined;\n    mline.ssrcGroups = undefined;\n\n    sdp.media = sdp.media.concat(mline);\n\n    // We regenerate the BUNDLE group (since we added a new m-line)\n    sdp.groups.forEach(group => {\n        if (group.type === 'BUNDLE') {\n            const mids = group.mids.split(' ');\n\n            mids.push(mid);\n            group.mids = mids.join(' ');\n        }\n    });\n    this.raw = transform.write(sdp);\n};\n\n/**\n * Returns map of MediaChannel mapped per channel idx.\n */\nSDP.prototype.getMediaSsrcMap = function() {\n    const mediaSSRCs = {};\n\n    for (let mediaindex = 0; mediaindex < this.media.length; mediaindex++) {\n        const mid\n            = SDPUtil.parseMID(\n                SDPUtil.findLine(this.media[mediaindex], 'a=mid:'));\n        const media = {\n            mediaindex,\n            mid,\n            ssrcs: {},\n            ssrcGroups: []\n        };\n\n        mediaSSRCs[mediaindex] = media;\n\n        SDPUtil.findLines(this.media[mediaindex], 'a=ssrc:').forEach(line => {\n            const linessrc = line.substring(7).split(' ')[0];\n\n            // allocate new ChannelSsrc\n\n            if (!media.ssrcs[linessrc]) {\n                media.ssrcs[linessrc] = {\n                    ssrc: linessrc,\n                    lines: []\n                };\n            }\n            media.ssrcs[linessrc].lines.push(line);\n        });\n        SDPUtil.findLines(this.media[mediaindex], 'a=ssrc-group:').forEach(line => {\n            const idx = line.indexOf(' ');\n            const semantics = line.substr(0, idx).substr(13);\n            const ssrcs = line.substr(14 + semantics.length).split(' ');\n\n            if (ssrcs.length) {\n                media.ssrcGroups.push({\n                    semantics,\n                    ssrcs\n                });\n            }\n        });\n    }\n\n    return mediaSSRCs;\n};\n\n/**\n * Returns <tt>true</tt> if this SDP contains given SSRC.\n * @param ssrc the ssrc to check.\n * @returns {boolean} <tt>true</tt> if this SDP contains given SSRC.\n */\nSDP.prototype.containsSSRC = function(ssrc) {\n    // FIXME this code is really strange - improve it if you can\n    const medias = this.getMediaSsrcMap();\n    let result = false;\n\n    Object.keys(medias).forEach(mediaindex => {\n        if (result) {\n            return;\n        }\n        if (medias[mediaindex].ssrcs[ssrc]) {\n            result = true;\n        }\n    });\n\n    return result;\n};\n\n// add content's to a jingle element\nSDP.prototype.toJingle = function(elem, thecreator) {\n    // https://xmpp.org/extensions/xep-0338.html\n    SDPUtil.findLines(this.session, 'a=group:').forEach(line => {\n        const parts = line.split(' ');\n        const semantics = parts.shift().substr(8);\n\n        elem.c('group', { xmlns: 'urn:xmpp:jingle:apps:grouping:0',\n            semantics });\n        for (let j = 0; j < parts.length; j++) {\n            elem.c('content', { name: parts[j] }).up();\n        }\n        elem.up();\n    });\n\n    for (let i = 0; i < this.media.length; i++) {\n        const mline = SDPUtil.parseMLine(this.media[i].split('\\r\\n')[0]);\n\n        if (!(mline.media === 'audio'\n              || mline.media === 'video'\n              || mline.media === 'application')) {\n            continue; // eslint-disable-line no-continue\n        }\n\n        let ssrc;\n        const assrcline = SDPUtil.findLine(this.media[i], 'a=ssrc:');\n\n        if (assrcline) {\n            ssrc = assrcline.substring(7).split(' ')[0]; // take the first\n        } else {\n            ssrc = false;\n        }\n\n        elem.c('content', { creator: thecreator,\n            name: mline.media });\n        const amidline = SDPUtil.findLine(this.media[i], 'a=mid:');\n\n        if (amidline) {\n            // prefer identifier from a=mid if present\n            const mid = SDPUtil.parseMID(amidline);\n\n            elem.attrs({ name: mid });\n        }\n\n        if (mline.media === 'audio' || mline.media === 'video') {\n            elem.c('description',\n                { xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n                    media: mline.media });\n            if (ssrc) {\n                elem.attrs({ ssrc });\n            }\n            for (let j = 0; j < mline.fmt.length; j++) {\n                const rtpmap\n                    = SDPUtil.findLine(\n                        this.media[i],\n                        `a=rtpmap:${mline.fmt[j]}`);\n\n                elem.c('payload-type', SDPUtil.parseRTPMap(rtpmap));\n\n                // put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo\n                // value=bar/>\n                const afmtpline\n                    = SDPUtil.findLine(\n                        this.media[i],\n                        `a=fmtp:${mline.fmt[j]}`);\n\n                if (afmtpline) {\n                    const fmtpParameters = SDPUtil.parseFmtp(afmtpline);\n\n                    // eslint-disable-next-line max-depth\n                    for (let k = 0; k < fmtpParameters.length; k++) {\n                        elem.c('parameter', fmtpParameters[k]).up();\n                    }\n                }\n\n                // XEP-0293 -- map a=rtcp-fb\n                this.rtcpFbToJingle(i, elem, mline.fmt[j]);\n\n                elem.up();\n            }\n\n            if (ssrc) {\n                const ssrcMap = SDPUtil.parseSSRC(this.media[i]);\n\n                for (const [ availableSsrc, ssrcParameters ] of ssrcMap) {\n                    const sourceName = SDPUtil.parseSourceNameLine(ssrcParameters);\n\n                    elem.c('source', {\n                        ssrc: availableSsrc,\n                        name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceName : undefined,\n                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n                    });\n\n                    const msid = SDPUtil.parseMSIDAttribute(ssrcParameters);\n\n                    // eslint-disable-next-line max-depth\n                    if (msid) {\n                        elem.c('parameter');\n                        elem.attrs({ name: 'msid' });\n                        elem.attrs({ value: msid });\n                        elem.up();\n                    }\n\n                    elem.up();\n                }\n\n                // XEP-0339 handle ssrc-group attributes\n                const ssrcGroupLines\n                    = SDPUtil.findLines(this.media[i], 'a=ssrc-group:');\n\n                ssrcGroupLines.forEach(line => {\n                    const idx = line.indexOf(' ');\n                    const semantics = line.substr(0, idx).substr(13);\n                    const ssrcs = line.substr(14 + semantics.length).split(' ');\n\n                    if (ssrcs.length) {\n                        elem.c('ssrc-group', { semantics,\n                            xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });\n                        ssrcs.forEach(s => elem.c('source', { ssrc: s }).up());\n                        elem.up();\n                    }\n                });\n            }\n\n            const ridLines = SDPUtil.findLines(this.media[i], 'a=rid:');\n\n            if (ridLines.length && browser.usesRidsForSimulcast()) {\n                // Map a line which looks like \"a=rid:2 send\" to just\n                // the rid (\"2\")\n                const rids = ridLines\n                    .map(ridLine => ridLine.split(':')[1])\n                    .map(ridInfo => ridInfo.split(' ')[0]);\n\n                rids.forEach(rid => {\n                    elem.c('source', {\n                        rid,\n                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n                    });\n                    elem.up();\n                });\n                const unifiedSimulcast\n                    = SDPUtil.findLine(this.media[i], 'a=simulcast:');\n\n                if (unifiedSimulcast) {\n                    elem.c('rid-group', {\n                        semantics: 'SIM',\n                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n                    });\n                    rids.forEach(rid => {\n                        elem.c('source', { rid }).up();\n                    });\n                    elem.up();\n                }\n            }\n\n            if (SDPUtil.findLine(this.media[i], 'a=rtcp-mux')) {\n                elem.c('rtcp-mux').up();\n            }\n\n            // XEP-0293 -- map a=rtcp-fb:*\n            this.rtcpFbToJingle(i, elem, '*');\n\n            // XEP-0294\n            const extmapLines = SDPUtil.findLines(this.media[i], 'a=extmap:');\n\n            for (let j = 0; j < extmapLines.length; j++) {\n                const extmap = SDPUtil.parseExtmap(extmapLines[j]);\n\n                elem.c('rtp-hdrext', {\n                    xmlns: 'urn:xmpp:jingle:apps:rtp:rtp-hdrext:0',\n                    uri: extmap.uri,\n                    id: extmap.value\n                });\n\n                // eslint-disable-next-line max-depth\n                if (extmap.hasOwnProperty('direction')) {\n\n                    // eslint-disable-next-line max-depth\n                    switch (extmap.direction) {\n                    case MediaDirection.SENDONLY:\n                        elem.attrs({ senders: 'responder' });\n                        break;\n                    case MediaDirection.RECVONLY:\n                        elem.attrs({ senders: 'initiator' });\n                        break;\n                    case MediaDirection.SENDRECV:\n                        elem.attrs({ senders: 'both' });\n                        break;\n                    case MediaDirection.INACTIVE:\n                        elem.attrs({ senders: 'none' });\n                        break;\n                    }\n                }\n\n                // TODO: handle params\n                elem.up();\n            }\n            elem.up(); // end of description\n        }\n\n        // map ice-ufrag/pwd, dtls fingerprint, candidates\n        this.transportToJingle(i, elem);\n\n        const m = this.media[i];\n\n        if (SDPUtil.findLine(m, `a=${MediaDirection.SENDRECV}`, this.session)) {\n            elem.attrs({ senders: 'both' });\n        } else if (SDPUtil.findLine(m, `a=${MediaDirection.SENDONLY}`, this.session)) {\n            elem.attrs({ senders: 'initiator' });\n        } else if (SDPUtil.findLine(m, `a=${MediaDirection.RECVONLY}`, this.session)) {\n            elem.attrs({ senders: 'responder' });\n        } else if (SDPUtil.findLine(m, `a=${MediaDirection.INACTIVE}`, this.session)) {\n            elem.attrs({ senders: 'none' });\n        }\n\n        // Reject an m-line only when port is 0 and a=bundle-only is not present in the section.\n        // The port is automatically set to 0 when bundle-only is used.\n        if (mline.port === '0' && !SDPUtil.findLine(m, 'a=bundle-only', this.session)) {\n            // estos hack to reject an m-line\n            elem.attrs({ senders: 'rejected' });\n        }\n        elem.up(); // end of content\n    }\n    elem.up();\n\n    return elem;\n};\n\nSDP.prototype.transportToJingle = function(mediaindex, elem) {\n    elem.c('transport');\n\n    // XEP-0343 DTLS/SCTP\n    const sctpport\n        = SDPUtil.findLine(this.media[mediaindex], 'a=sctp-port:', this.session);\n    const sctpmap\n        = SDPUtil.findLine(this.media[mediaindex], 'a=sctpmap:', this.session);\n\n    if (sctpport) {\n        const sctpAttrs = SDPUtil.parseSCTPPort(sctpport);\n\n        elem.c('sctpmap', {\n            xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',\n            number: sctpAttrs, /* SCTP port */\n            protocol: 'webrtc-datachannel' /* protocol */\n        });\n\n        // The parser currently requires streams to be present\n        elem.attrs({ streams: 0 });\n        elem.up();\n    } else if (sctpmap) {\n        const sctpAttrs = SDPUtil.parseSCTPMap(sctpmap);\n\n        elem.c('sctpmap', {\n            xmlns: 'urn:xmpp:jingle:transports:dtls-sctp:1',\n            number: sctpAttrs[0], /* SCTP port */\n            protocol: sctpAttrs[1] /* protocol */\n        });\n\n        // Optional stream count attribute\n        if (sctpAttrs.length > 2) {\n            elem.attrs({ streams: sctpAttrs[2] });\n        } else {\n            elem.attrs({ streams: 0 });\n        }\n        elem.up();\n    }\n\n    // XEP-0320\n    const fingerprints\n        = SDPUtil.findLines(\n            this.media[mediaindex],\n            'a=fingerprint:',\n            this.session);\n\n    fingerprints.forEach(line => {\n        const fingerprint = SDPUtil.parseFingerprint(line);\n\n        fingerprint.xmlns = 'urn:xmpp:jingle:apps:dtls:0';\n        elem.c('fingerprint').t(fingerprint.fingerprint);\n        delete fingerprint.fingerprint;\n\n        const setupLine\n            = SDPUtil.findLine(\n                this.media[mediaindex],\n                'a=setup:',\n                this.session);\n\n        if (setupLine) {\n            fingerprint.setup = setupLine.substr(8);\n        }\n        elem.attrs(fingerprint);\n        elem.up(); // end of fingerprint\n    });\n    const iceParameters = SDPUtil.iceparams(this.media[mediaindex], this.session);\n\n    if (iceParameters) {\n        iceParameters.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n        elem.attrs(iceParameters);\n\n        // XEP-0176\n        const candidateLines\n            = SDPUtil.findLines(\n                this.media[mediaindex],\n                'a=candidate:',\n                this.session);\n\n        candidateLines.forEach(line => { // add any a=candidate lines\n            const candidate = SDPUtil.candidateToJingle(line);\n\n            if (this.failICE) {\n                candidate.ip = '1.1.1.1';\n            }\n            const protocol\n                = candidate && typeof candidate.protocol === 'string'\n                    ? candidate.protocol.toLowerCase()\n                    : '';\n\n            if ((this.removeTcpCandidates\n                    && (protocol === 'tcp' || protocol === 'ssltcp'))\n                || (this.removeUdpCandidates && protocol === 'udp')) {\n                return;\n            }\n            elem.c('candidate', candidate).up();\n        });\n    }\n    elem.up(); // end of transport\n};\n\n// XEP-0293\nSDP.prototype.rtcpFbToJingle = function(mediaindex, elem, payloadtype) {\n    const lines\n        = SDPUtil.findLines(\n            this.media[mediaindex],\n            `a=rtcp-fb:${payloadtype}`);\n\n    lines.forEach(line => {\n        const feedback = SDPUtil.parseRTCPFB(line);\n\n        if (feedback.type === 'trr-int') {\n            elem.c('rtcp-fb-trr-int', {\n                xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0',\n                value: feedback.params[0]\n            });\n            elem.up();\n        } else {\n            elem.c('rtcp-fb', {\n                xmlns: 'urn:xmpp:jingle:apps:rtp:rtcp-fb:0',\n                type: feedback.type\n            });\n            if (feedback.params.length > 0) {\n                elem.attrs({ 'subtype': feedback.params[0] });\n            }\n            elem.up();\n        }\n    });\n};\n\nSDP.prototype.rtcpFbFromJingle = function(elem, payloadtype) { // XEP-0293\n    let sdp = '';\n    const feedbackElementTrrInt\n        = elem.find(\n            '>rtcp-fb-trr-int[xmlns=\"urn:xmpp:jingle:apps:rtp:rtcp-fb:0\"]');\n\n    if (feedbackElementTrrInt.length) {\n        sdp += 'a=rtcp-fb:* trr-int ';\n        if (feedbackElementTrrInt.attr('value')) {\n            sdp += feedbackElementTrrInt.attr('value');\n        } else {\n            sdp += '0';\n        }\n        sdp += '\\r\\n';\n    }\n\n    const feedbackElements = elem.find('>rtcp-fb[xmlns=\"urn:xmpp:jingle:apps:rtp:rtcp-fb:0\"]');\n\n    feedbackElements.each((_, fb) => {\n        sdp += `a=rtcp-fb:${payloadtype} ${fb.getAttribute('type')}`;\n        if (fb.hasAttribute('subtype')) {\n            sdp += ` ${fb.getAttribute('subtype')}`;\n        }\n        sdp += '\\r\\n';\n    });\n\n    return sdp;\n};\n\n// construct an SDP from a jingle stanza\nSDP.prototype.fromJingle = function(jingle) {\n    const sessionId = Date.now();\n\n    // Use a unique session id for every TPC.\n    this.raw = 'v=0\\r\\n'\n        + `o=- ${sessionId} 2 IN IP4 0.0.0.0\\r\\n`\n        + 's=-\\r\\n'\n        + 't=0 0\\r\\n';\n\n    // http://tools.ietf.org/html/draft-ietf-mmusic-sdp-bundle-negotiation-04\n    // #section-8\n    const groups\n        = $(jingle).find('>group[xmlns=\"urn:xmpp:jingle:apps:grouping:0\"]');\n\n    if (groups.length) {\n        groups.each((idx, group) => {\n            const contents\n                = $(group)\n                    .find('>content')\n                    .map((_, content) => content.getAttribute('name'))\n                    .get();\n\n            if (contents.length > 0) {\n                this.raw\n                    += `a=group:${\n                        group.getAttribute('semantics')\n                            || group.getAttribute('type')} ${\n                        contents.join(' ')}\\r\\n`;\n            }\n        });\n    }\n\n    this.session = this.raw;\n    jingle.find('>content').each((_, content) => {\n        const m = this.jingle2media($(content));\n\n        this.media.push(m);\n    });\n\n    // reconstruct msid-semantic -- apparently not necessary\n    /*\n     var msid = SDPUtil.parseSSRC(this.raw);\n     if (msid.hasOwnProperty('mslabel')) {\n     this.session += \"a=msid-semantic: WMS \" + msid.mslabel + \"\\r\\n\";\n     }\n     */\n\n    this.raw = this.session + this.media.join('');\n};\n\n// translate a jingle content element into an an SDP media part\nSDP.prototype.jingle2media = function(content) {\n    const desc = content.find('>description');\n    const transport = content.find('>transport[xmlns=\"urn:xmpp:jingle:transports:ice-udp:1\"]');\n    let sdp = '';\n    const sctp = transport.find(\n        '>sctpmap[xmlns=\"urn:xmpp:jingle:transports:dtls-sctp:1\"]');\n\n    const media = { media: desc.attr('media') };\n\n    media.port = '9';\n    if (content.attr('senders') === 'rejected') {\n        // estos hack to reject an m-line.\n        media.port = '0';\n    }\n    if (transport.find('>fingerprint[xmlns=\"urn:xmpp:jingle:apps:dtls:0\"]').length) {\n        media.proto = sctp.length ? 'UDP/DTLS/SCTP' : 'UDP/TLS/RTP/SAVPF';\n    } else {\n        media.proto = 'UDP/TLS/RTP/SAVPF';\n    }\n    if (sctp.length) {\n        sdp += `m=application ${media.port} UDP/DTLS/SCTP webrtc-datachannel\\r\\n`;\n        sdp += `a=sctp-port:${sctp.attr('number')}\\r\\n`;\n        sdp += 'a=max-message-size:262144\\r\\n';\n    } else {\n        media.fmt\n            = desc\n                .find('>payload-type')\n                .map((_, payloadType) => payloadType.getAttribute('id'))\n                .get();\n        sdp += `${SDPUtil.buildMLine(media)}\\r\\n`;\n    }\n\n    sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n    if (!sctp.length) {\n        sdp += 'a=rtcp:1 IN IP4 0.0.0.0\\r\\n';\n    }\n\n    // XEP-0176 ICE parameters\n    if (transport.length) {\n        if (transport.attr('ufrag')) {\n            sdp += `${SDPUtil.buildICEUfrag(transport.attr('ufrag'))}\\r\\n`;\n        }\n        if (transport.attr('pwd')) {\n            sdp += `${SDPUtil.buildICEPwd(transport.attr('pwd'))}\\r\\n`;\n        }\n        transport.find('>fingerprint[xmlns=\"urn:xmpp:jingle:apps:dtls:0\"]').each((_, fingerprint) => {\n            sdp += `a=fingerprint:${fingerprint.getAttribute('hash')}`;\n            sdp += ` ${$(fingerprint).text()}`;\n            sdp += '\\r\\n';\n            if (fingerprint.hasAttribute('setup')) {\n                sdp += `a=setup:${fingerprint.getAttribute('setup')}\\r\\n`;\n            }\n        });\n    }\n\n    // XEP-0176 ICE candidates\n    transport.find('>candidate')\n        .each((_, candidate) => {\n            let protocol = candidate.getAttribute('protocol');\n\n            protocol\n                = typeof protocol === 'string' ? protocol.toLowerCase() : '';\n\n            if ((this.removeTcpCandidates\n                    && (protocol === 'tcp' || protocol === 'ssltcp'))\n                || (this.removeUdpCandidates && protocol === 'udp')) {\n                return;\n            } else if (this.failICE) {\n                candidate.setAttribute('ip', '1.1.1.1');\n            }\n\n            sdp += SDPUtil.candidateFromJingle(candidate);\n        });\n\n    switch (content.attr('senders')) {\n    case 'initiator':\n        sdp += `a=${MediaDirection.SENDONLY}\\r\\n`;\n        break;\n    case 'responder':\n        sdp += `a=${MediaDirection.RECVONLY}\\r\\n`;\n        break;\n    case 'none':\n        sdp += `a=${MediaDirection.INACTIVE}\\r\\n`;\n        break;\n    case 'both':\n        sdp += `a=${MediaDirection.SENDRECV}\\r\\n`;\n        break;\n    }\n    sdp += `a=mid:${content.attr('name')}\\r\\n`;\n\n    // <description><rtcp-mux/></description>\n    // see http://code.google.com/p/libjingle/issues/detail?id=309 -- no spec\n    // though\n    // and http://mail.jabber.org/pipermail/jingle/2011-December/001761.html\n    if (desc.find('>rtcp-mux').length) {\n        sdp += 'a=rtcp-mux\\r\\n';\n    }\n\n    desc.find('>payload-type').each((_, payloadType) => {\n        sdp += `${SDPUtil.buildRTPMap(payloadType)}\\r\\n`;\n        if ($(payloadType).find('>parameter').length) {\n            sdp += `a=fmtp:${payloadType.getAttribute('id')} `;\n            sdp\n                += $(payloadType)\n                    .find('>parameter')\n                    .map((__, parameter) => {\n                        const name = parameter.getAttribute('name');\n\n                        return (\n                            (name ? `${name}=` : '')\n                                + parameter.getAttribute('value'));\n                    })\n                    .get()\n                    .join('; ');\n            sdp += '\\r\\n';\n        }\n\n        // xep-0293\n        sdp += this.rtcpFbFromJingle($(payloadType), payloadType.getAttribute('id'));\n    });\n\n    // xep-0293\n    sdp += this.rtcpFbFromJingle(desc, '*');\n\n    // xep-0294\n    desc\n        .find('>rtp-hdrext[xmlns=\"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0\"]')\n        .each((_, hdrExt) => {\n            sdp\n                += `a=extmap:${hdrExt.getAttribute('id')} ${\n                    hdrExt.getAttribute('uri')}\\r\\n`;\n        });\n\n    // XEP-0339 handle ssrc-group attributes\n    desc\n        .find('>ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n        .each((_, ssrcGroup) => {\n            const semantics = ssrcGroup.getAttribute('semantics');\n            const ssrcs\n                = $(ssrcGroup)\n                    .find('>source')\n                    .map((__, source) => source.getAttribute('ssrc'))\n                    .get();\n\n            if (ssrcs.length) {\n                sdp += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\\r\\n`;\n            }\n        });\n\n    // XEP-0339 handle source attributes\n    let userSources = '';\n    let nonUserSources = '';\n\n    desc\n        .find('>source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n        .each((_, source) => {\n            const ssrc = source.getAttribute('ssrc');\n            let isUserSource = true;\n            let sourceStr = '';\n\n            $(source)\n                .find('>parameter')\n                .each((__, parameter) => {\n                    const name = parameter.getAttribute('name');\n                    let value = parameter.getAttribute('value');\n\n                    value = SDPUtil.filterSpecialChars(value);\n                    sourceStr += `a=ssrc:${ssrc} ${name}`;\n\n                    if (value && value.length) {\n                        sourceStr += `:${value}`;\n                    }\n\n                    sourceStr += '\\r\\n';\n\n                    if (value?.includes('mixedmslabel')) {\n                        isUserSource = false;\n                    }\n                });\n\n            if (isUserSource) {\n                userSources += sourceStr;\n            } else {\n                nonUserSources += sourceStr;\n            }\n        });\n\n    // The sdp-interop package is relying the mixedmslabel m line to be the first one in order to set the direction\n    // to sendrecv.\n    sdp += nonUserSources + userSources;\n\n    return sdp;\n};\n","import FeatureFlags from '../flags/FeatureFlags';\n\nimport SDPUtil from './SDPUtil';\n\n// this could be useful in Array.prototype.\n/**\n *\n * @param array1\n * @param array2\n */\nfunction arrayEquals(array1, array2) {\n    // if the other array is a falsy value, return\n    if (!array2) {\n        return false;\n    }\n\n    // compare lengths - can save a lot of time\n    if (array1.length !== array2.length) {\n        return false;\n    }\n\n    for (let i = 0, l = array1.length; i < l; i++) {\n        // Check if we have nested arrays\n        if (array1[i] instanceof Array && array2[i] instanceof Array) {\n            // recurse into the nested arrays\n            if (!array1[i].equals(array2[i])) {\n                return false;\n            }\n        } else if (array1[i] !== array2[i]) {\n            // Warning - two different object instances will never be\n            // equal: {x:20} != {x:20}\n            return false;\n        }\n    }\n\n    return true;\n}\n\n/**\n *\n * @param mySDP\n * @param otherSDP\n */\nexport default function SDPDiffer(mySDP, otherSDP) {\n    this.mySDP = mySDP;\n    this.otherSDP = otherSDP;\n    if (!mySDP) {\n        throw new Error('\"mySDP\" is undefined!');\n    } else if (!otherSDP) {\n        throw new Error('\"otherSDP\" is undefined!');\n    }\n}\n\n/**\n * Returns map of MediaChannel that contains media contained in\n * 'mySDP', but not contained in 'otherSdp'. Mapped by channel idx.\n */\nSDPDiffer.prototype.getNewMedia = function() {\n\n    const myMedias = this.mySDP.getMediaSsrcMap();\n    const othersMedias = this.otherSDP.getMediaSsrcMap();\n    const newMedia = {};\n\n    Object.keys(othersMedias).forEach(othersMediaIdx => {\n        const myMedia = myMedias[othersMediaIdx];\n        const othersMedia = othersMedias[othersMediaIdx];\n\n        if (!myMedia && othersMedia) {\n            // Add whole channel\n            newMedia[othersMediaIdx] = othersMedia;\n\n            return;\n        }\n\n        // Look for new ssrcs across the channel\n        Object.keys(othersMedia.ssrcs).forEach(ssrc => {\n            if (Object.keys(myMedia.ssrcs).indexOf(ssrc) === -1) {\n                // Allocate channel if we've found ssrc that doesn't exist in\n                // our channel\n                if (!newMedia[othersMediaIdx]) {\n                    newMedia[othersMediaIdx] = {\n                        mediaindex: othersMedia.mediaindex,\n                        mid: othersMedia.mid,\n                        ssrcs: {},\n                        ssrcGroups: []\n                    };\n                }\n                newMedia[othersMediaIdx].ssrcs[ssrc] = othersMedia.ssrcs[ssrc];\n            } else if (othersMedia.ssrcs[ssrc].lines\n                        && myMedia.ssrcs[ssrc].lines) {\n                // we want to detect just changes in adding/removing msid\n                const myContainMsid = myMedia.ssrcs[ssrc].lines.find(\n                    line => line.indexOf('msid') !== -1) !== undefined;\n                const newContainMsid = othersMedia.ssrcs[ssrc].lines.find(\n                    line => line.indexOf('msid') !== -1) !== undefined;\n\n                if (myContainMsid !== newContainMsid) {\n                    if (!newMedia[othersMediaIdx]) {\n                        newMedia[othersMediaIdx] = {\n                            mediaindex: othersMedia.mediaindex,\n                            mid: othersMedia.mid,\n                            ssrcs: {},\n                            ssrcGroups: []\n                        };\n                    }\n                    newMedia[othersMediaIdx].ssrcs[ssrc]\n                        = othersMedia.ssrcs[ssrc];\n                }\n            }\n        });\n\n        // Look for new ssrc groups across the channels\n        othersMedia.ssrcGroups.forEach(otherSsrcGroup => {\n\n            // try to match the other ssrc-group with an ssrc-group of ours\n            let matched = false;\n\n            for (let i = 0; i < myMedia.ssrcGroups.length; i++) {\n                const mySsrcGroup = myMedia.ssrcGroups[i];\n\n                if (otherSsrcGroup.semantics === mySsrcGroup.semantics\n                    && arrayEquals(otherSsrcGroup.ssrcs, mySsrcGroup.ssrcs)) {\n\n                    matched = true;\n                    break;\n                }\n            }\n\n            if (!matched) {\n                // Allocate channel if we've found an ssrc-group that doesn't\n                // exist in our channel\n\n                if (!newMedia[othersMediaIdx]) {\n                    newMedia[othersMediaIdx] = {\n                        mediaindex: othersMedia.mediaindex,\n                        mid: othersMedia.mid,\n                        ssrcs: {},\n                        ssrcGroups: []\n                    };\n                }\n                newMedia[othersMediaIdx].ssrcGroups.push(otherSsrcGroup);\n            }\n        });\n    });\n\n    return newMedia;\n};\n\n/**\n * TODO: document!\n */\nSDPDiffer.prototype.toJingle = function(modify) {\n    const sdpMediaSsrcs = this.getNewMedia();\n\n    let modified = false;\n\n    Object.keys(sdpMediaSsrcs).forEach(mediaindex => {\n        modified = true;\n        const media = sdpMediaSsrcs[mediaindex];\n\n        modify.c('content', { name: media.mid });\n\n        modify.c('description',\n            { xmlns: 'urn:xmpp:jingle:apps:rtp:1',\n                media: media.mid });\n\n        // FIXME: not completely sure this operates on blocks and / or handles\n        // different ssrcs correctly\n        // generate sources from lines\n        Object.keys(media.ssrcs).forEach(ssrcNum => {\n            const mediaSsrc = media.ssrcs[ssrcNum];\n            const ssrcLines = mediaSsrc.lines;\n            const sourceName = SDPUtil.parseSourceNameLine(ssrcLines);\n\n            modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });\n            modify.attrs({\n                name: FeatureFlags.isSourceNameSignalingEnabled() ? sourceName : undefined,\n                ssrc: mediaSsrc.ssrc\n            });\n\n            // Only MSID attribute is sent\n            const msid = SDPUtil.parseMSIDAttribute(ssrcLines);\n\n            if (msid) {\n                modify.c('parameter');\n                modify.attrs({ name: 'msid' });\n                modify.attrs({ value: msid });\n                modify.up();\n            }\n\n            modify.up(); // end of source\n        });\n\n        // generate source groups from lines\n        media.ssrcGroups.forEach(ssrcGroup => {\n            if (ssrcGroup.ssrcs.length) {\n\n                modify.c('ssrc-group', {\n                    semantics: ssrcGroup.semantics,\n                    xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n                });\n\n                ssrcGroup.ssrcs.forEach(ssrc => {\n                    modify.c('source', { ssrc })\n                        .up(); // end of source\n                });\n                modify.up(); // end of ssrc-group\n            }\n        });\n\n        modify.up(); // end of description\n        modify.up(); // end of content\n    });\n\n    return modified;\n};\n","import { getLogger } from '@jitsi/logger';\nimport async from 'async';\n\nconst logger = getLogger(__filename);\n\n/**\n * A queue for async task execution.\n */\nexport default class AsyncQueue {\n    /**\n     * Creates new instance.\n     */\n    constructor() {\n        this._queue = async.queue(this._processQueueTasks.bind(this), 1);\n        this._stopped = false;\n    }\n\n    /**\n     * Removes any pending tasks from the queue.\n     */\n    clear() {\n        this._queue.kill();\n    }\n\n    /**\n     * Internal task processing implementation which makes things work.\n     */\n    _processQueueTasks(task, finishedCallback) {\n        try {\n            task(finishedCallback);\n        } catch (error) {\n            logger.error(`Task failed: ${error?.stack}`);\n            finishedCallback(error);\n        }\n    }\n\n    /**\n     * The 'task' function will be given a callback it MUST call with either:\n     *  1) No arguments if it was successful or\n     *  2) An error argument if there was an error\n     * If the task wants to process the success or failure of the task, it\n     * should pass the {@code callback} to the push function, e.g.:\n     * queue.push(task, (err) => {\n     *     if (err) {\n     *         // error handling\n     *     } else {\n     *         // success handling\n     *     }\n     * });\n     *\n     * @param {function} task - The task to be executed. See the description above.\n     * @param {function} [callback] - Optional callback to be called after the task has been executed.\n     */\n    push(task, callback) {\n        if (this._stopped) {\n            callback && callback(new Error('The queue has been stopped'));\n\n            return;\n        }\n        this._queue.push(task, callback);\n    }\n\n    /**\n     * Shutdowns the queue. All already queued tasks will execute, but no future tasks can be added. If a task is added\n     * after the queue has been shutdown then the callback will be called with an error.\n     */\n    shutdown() {\n        this._stopped = true;\n    }\n}\n","export enum JingleSessionState {\n    /**\n     * The pending Jingle session state which means the session as defined in\n     * XEP-0166(before 'session-invite/session-accept' took place).\n     */\n    PENDING = 'pending',\n\n    /**\n     * The active Jingle session state as defined in XEP-0166\n     * (after 'session-invite'/'session-accept').\n     */\n    ACTIVE = 'active',\n\n    /**\n     * The ended Jingle session state as defined in XEP-0166\n     * (after 'session-terminate').\n     */\n    ENDED = 'ended'\n};\n\n// exported for backward compatibility\nexport const PENDING = JingleSessionState.PENDING;\nexport const ACTIVE = JingleSessionState.ACTIVE;\nexport const ENDED = JingleSessionState.ENDED;\n","import { getLogger } from '@jitsi/logger';\n\nimport Listenable from '../util/Listenable';\n\nimport * as JingleSessionState from './JingleSessionState';\n\nconst logger = getLogger(__filename);\n\n/**\n * JingleSession provides an API to manage a single Jingle session. We will\n * have different implementations depending on the underlying interface used\n * (i.e. WebRTC and ORTC) and here we hold the code common to all of them.\n */\nexport default class JingleSession extends Listenable {\n\n    /* eslint-disable max-params */\n\n    /**\n     * Creates new <tt>JingleSession</tt>.\n     * @param {string} sid the Jingle session identifier\n     * @param {string} localJid our JID\n     * @param {string} remoteJid the JID of the remote peer\n     * @param {XmppConnection} connection the XMPP connection\n     * @param {Object} mediaConstraints the media constraints object passed to the PeerConnection onCreateAnswer/Offer.\n     * @param {Object} pcConfig The {@code RTCConfiguration} object passed to the PeerConnection's constructor.\n     * @param {boolean} isInitiator indicates if it will be the side which initiates the session.\n     */\n    constructor(\n            sid,\n            localJid,\n            remoteJid,\n            connection,\n            mediaConstraints,\n            pcConfig,\n            isInitiator) {\n        super();\n        this.sid = sid;\n        this.localJid = localJid;\n        this.remoteJid = remoteJid;\n        this.connection = connection;\n        this.mediaConstraints = mediaConstraints;\n        this.pcConfig = pcConfig;\n\n        /**\n         * Indicates whether this instance is an initiator or an answerer of\n         * the Jingle session.\n         * @type {boolean}\n         */\n        this.isInitiator = isInitiator;\n\n        /**\n         * Whether to use dripping or not. Dripping is sending trickle\n         * candidates not one-by-one.\n         */\n        this.usedrip = true;\n\n        /**\n         *  When dripping is used, stores ICE candidates which are to be sent.\n         */\n        this.dripContainer = [];\n\n        /**\n         * The chat room instance associated with the session.\n         * @type {ChatRoom}\n         */\n        this.room = null;\n\n        /**\n         * The signaling layer.\n         * @type {SignalingLayerImpl | null}\n         * @private\n         */\n        this._signalingLayer = null;\n\n        /**\n         * Jingle session state - uninitialized until {@link initialize} is\n         * called @type {JingleSessionState}\n         */\n        this.state = null;\n\n        /**\n         * The RTC service instance\n         * @type {RTC}\n         */\n        this.rtc = null;\n    }\n\n    /**\n     * Returns XMPP address of this session's initiator.\n     * @return {string}\n     */\n    get initiatorJid() {\n        return this.isInitiator ? this.localJid : this.remoteJid;\n    }\n\n    /**\n     * Returns XMPP address of this session's responder.\n     * @return {string}\n     */\n    get responderJid() {\n        return this.isInitiator ? this.remoteJid : this.localJid;\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Prepares this object to initiate a session.\n     * @param {ChatRoom} room the chat room for the conference associated with\n     * this session\n     * @param {RTC} rtc the RTC service instance\n     * @param {SignalingLayerImpl} signalingLayer - The signaling layer instance.\n     * @param {object} options - the options, see implementing class's\n     * {@link #doInitialize} description for more details.\n     */\n    initialize(room, rtc, signalingLayer, options) {\n        if (this.state !== null) {\n            const errmsg\n                = `attempt to initiate on session ${this.sid}\n                   in state ${this.state}`;\n\n            logger.error(errmsg);\n            throw new Error(errmsg);\n        }\n\n        // TODO decouple from room\n        this.room = room;\n        this.rtc = rtc;\n        this._signalingLayer = signalingLayer;\n        this.state = JingleSessionState.PENDING;\n        this.doInitialize(options);\n    }\n\n    /**\n     * The implementing class finishes initialization here. Called at the end of\n     * {@link initialize}.\n     * @param {Object} options - The options specific to the implementing class.\n     * @protected\n     */\n    doInitialize(options) { } // eslint-disable-line no-unused-vars, no-empty-function, max-len\n\n    /* eslint-disable no-unused-vars, no-empty-function */\n\n    /**\n     * Adds the ICE candidates found in the 'contents' array as remote\n     * candidates?\n     * Note: currently only used on transport-info\n     *\n     * @param contents\n     */\n    addIceCandidates(contents) {}\n\n    /* eslint-enable no-unused-vars, no-empty-function */\n\n    /**\n     * Returns current state of this <tt>JingleSession</tt> instance.\n     * @returns {JingleSessionState} the current state of this session instance.\n     */\n    getState() {\n        return this.state;\n    }\n\n    /* eslint-disable no-unused-vars, no-empty-function */\n\n    /**\n     * Handles an 'add-source' event.\n     *\n     * @param contents an array of Jingle 'content' elements.\n     */\n    addSources(contents) {}\n\n    /**\n     * Handles a 'remove-source' event.\n     *\n     * @param contents an array of Jingle 'content' elements.\n     */\n    removeSources(contents) {}\n\n    /**\n     * Terminates this Jingle session by sending session-terminate\n     * @param success a callback called once the 'session-terminate' packet has\n     * been acknowledged with RESULT.\n     * @param failure a callback called when either timeout occurs or ERROR\n     * response is received.\n     * @param {Object} options\n     * @param {string} [options.reason] XMPP Jingle error condition\n     * @param {string} [options.reasonDescription] some meaningful error message\n     * @param {boolean} [options.requestRestart=false] set to true to ask Jicofo to start a new session one this once is\n     * terminated.\n     * @param {boolean} [options.sendSessionTerminate=true] set to false to skip\n     * sending session-terminate. It may not make sense to send it if the XMPP\n     * connection has been closed already or if the remote peer has disconnected\n     */\n    terminate(success, failure, options) {}\n\n    /**\n     * Handles an offer from the remote peer (prepares to accept a session).\n     * @param jingle the 'jingle' XML element.\n     * @param success callback called when we the incoming session has been\n     * accepted\n     * @param failure callback called when we fail for any reason, will supply\n     * error object with details(which is meant more to be printed to the logger\n     * than analysed in the code, as the error is unrecoverable anyway)\n     */\n    acceptOffer(jingle, success, failure) {}\n\n    /**\n     * Returns the JID of the initiator of the jingle session.\n     */\n    _getInitiatorJid() {\n        return this.isInitiator ? this.localJid : this.remoteJid;\n    }\n\n    /* eslint-enable no-unused-vars, no-empty-function */\n}\n","enum MediaSessionEvents {\n    /**\n     * Event triggered when the remote party signals it's receive video max frame height.\n     */\n    REMOTE_VIDEO_CONSTRAINTS_CHANGED = 'media_session.REMOTE_VIDEO_CONSTRAINTS_CHANGED'\n};\n\nexport default MediaSessionEvents;","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport * as CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport {\n    ICE_DURATION,\n    ICE_STATE_CHANGED\n} from '../../service/statistics/AnalyticsEvents';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport { SS_DEFAULT_FRAME_RATE } from '../RTC/ScreenObtainer';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport SDP from '../sdp/SDP';\nimport SDPDiffer from '../sdp/SDPDiffer';\nimport SDPUtil from '../sdp/SDPUtil';\nimport Statistics from '../statistics/statistics';\nimport AsyncQueue from '../util/AsyncQueue';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport { integerHash } from '../util/StringUtils';\n\nimport browser from './../browser';\nimport JingleSession from './JingleSession';\nimport * as JingleSessionState from './JingleSessionState';\nimport MediaSessionEvents from './MediaSessionEvents';\nimport XmppConnection from './XmppConnection';\n\nconst logger = getLogger(__filename);\n\n/**\n * Constant tells how long we're going to wait for IQ response, before timeout\n * error is  triggered.\n * @type {number}\n */\nconst IQ_TIMEOUT = 10000;\n\n/*\n * The default number of samples (per stat) to keep when webrtc stats gathering\n * is enabled in TraceablePeerConnection.\n */\nconst DEFAULT_MAX_STATS = 300;\n\n/**\n * The time duration for which the client keeps gathering ICE candidates to be sent out in a single IQ.\n * @type {number} timeout in ms.\n */\nconst ICE_CAND_GATHERING_TIMEOUT = 150;\n\n/**\n * Reads the endpoint ID given a string which represents either the endpoint's full JID, or the endpoint ID itself.\n * @param {String} jidOrEndpointId A string which is either the full JID of a participant, or the ID of an\n * endpoint/participant.\n * @returns The endpoint ID associated with 'jidOrEndpointId'.\n */\nfunction getEndpointId(jidOrEndpointId) {\n    return Strophe.getResourceFromJid(jidOrEndpointId) || jidOrEndpointId;\n}\n\n/**\n * @typedef {Object} JingleSessionPCOptions\n * @property {Object} abTesting - A/B testing related options (ask George).\n * @property {boolean} abTesting.enableSuspendVideoTest - enables the suspend\n * video test ?(ask George).\n * @property {boolean} disableH264 - Described in the config.js[1].\n * @property {boolean} disableRtx - Described in the config.js[1].\n * @property {boolean} disableSimulcast - Described in the config.js[1].\n * @property {boolean} enableInsertableStreams - Set to true when the insertable streams constraints is to be enabled\n * on the PeerConnection.\n * @property {boolean} enableLayerSuspension - Described in the config.js[1].\n * @property {boolean} failICE - it's an option used in the tests. Set to\n * <tt>true</tt> to block any real candidates and make the ICE fail.\n * @property {boolean} gatherStats - Described in the config.js[1].\n * @property {object} p2p - Peer to peer related options (FIXME those could be\n * fetched from config.p2p on the upper level).\n * @property {boolean} preferH264 - Described in the config.js[1].\n * @property {Object} testing - Testing and/or experimental options.\n * @property {boolean} webrtcIceUdpDisable - Described in the config.js[1].\n * @property {boolean} webrtcIceTcpDisable - Described in the config.js[1].\n *\n * [1]: https://github.com/jitsi/jitsi-meet/blob/master/config.js\n */\n/**\n *\n */\nexport default class JingleSessionPC extends JingleSession {\n    /**\n     * Parses 'senders' attribute of the video content.\n     * @param {jQuery} jingleContents\n     * @return {string|null} one of the values of content \"senders\" attribute\n     * defined by Jingle. If there is no \"senders\" attribute or if the value is\n     * invalid then <tt>null</tt> will be returned.\n     * @private\n     */\n    static parseVideoSenders(jingleContents) {\n        const videoContents = jingleContents.find('>content[name=\"video\"]');\n\n        if (videoContents.length) {\n            const senders = videoContents[0].getAttribute('senders');\n\n            if (senders === 'both'\n                || senders === 'initiator'\n                || senders === 'responder'\n                || senders === 'none') {\n                return senders;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * Parses the video max frame height value out of the 'content-modify' IQ.\n     *\n     * @param {jQuery} jingleContents - A jQuery selector pointing to the '>jingle' element.\n     * @returns {Number|null}\n     */\n    static parseMaxFrameHeight(jingleContents) {\n        const maxFrameHeightSel = jingleContents.find('>content[name=\"video\"]>max-frame-height');\n\n        return maxFrameHeightSel.length ? Number(maxFrameHeightSel.text()) : null;\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     * Creates new <tt>JingleSessionPC</tt>\n     * @param {string} sid the Jingle Session ID - random string which identifies the session\n     * @param {string} localJid our JID\n     * @param {string} remoteJid remote peer JID\n     * @param {XmppConnection} connection - The XMPP connection instance.\n     * @param mediaConstraints the media constraints object passed to createOffer/Answer, as defined\n     * by the WebRTC standard\n     * @param pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n     * @param {boolean} isP2P indicates whether this instance is meant to be used in a direct, peer to\n     * peer connection or <tt>false</tt> if it's a JVB connection.\n     * @param {boolean} isInitiator indicates if it will be the side which initiates the session.\n     * @constructor\n     *\n     * @implements {SignalingLayer}\n     */\n    constructor(\n            sid,\n            localJid,\n            remoteJid,\n            connection,\n            mediaConstraints,\n            pcConfig,\n            isP2P,\n            isInitiator) {\n        super(\n            sid,\n            localJid,\n            remoteJid, connection, mediaConstraints, pcConfig, isInitiator);\n\n        /**\n         * The bridge session's identifier. One Jingle session can during\n         * it's lifetime participate in multiple bridge sessions managed by\n         * Jicofo. A new bridge session is started whenever Jicofo sends\n         * 'session-initiate' or 'transport-replace'.\n         *\n         * @type {?string}\n         * @private\n         */\n        this._bridgeSessionId = null;\n\n        /**\n         * The oldest SDP passed to {@link notifyMySSRCUpdate} while the XMPP connection was offline that will be\n         * used to update Jicofo once the XMPP connection goes back online.\n         * @type {SDP|undefined}\n         * @private\n         */\n        this._cachedOldLocalSdp = undefined;\n\n        /**\n         * The latest SDP passed to {@link notifyMySSRCUpdate} while the XMPP connection was offline that will be\n         * used to update Jicofo once the XMPP connection goes back online.\n         * @type {SDP|undefined}\n         * @private\n         */\n        this._cachedNewLocalSdp = undefined;\n\n        /**\n         * Stores result of {@link window.performance.now()} at the time when\n         * ICE enters 'checking' state.\n         * @type {number|null} null if no value has been stored yet\n         * @private\n         */\n        this._iceCheckingStartedTimestamp = null;\n\n        /**\n         * Stores result of {@link window.performance.now()} at the time when\n         * first ICE candidate is spawned by the peerconnection to mark when\n         * ICE gathering started. That's, because ICE gathering state changed\n         * events are not supported by most of the browsers, so we try something\n         * that will work everywhere. It may not be as accurate, but given that\n         * 'host' candidate usually comes first, the delay should be minimal.\n         * @type {number|null} null if no value has been stored yet\n         * @private\n         */\n        this._gatheringStartedTimestamp = null;\n\n        /**\n         * Local preference for the receive video max frame height.\n         *\n         * @type {Number|undefined}\n         */\n        this.localRecvMaxFrameHeight = undefined;\n\n        /**\n         * Indicates whether or not this session is willing to send/receive\n         * video media. When set to <tt>false</tt> the underlying peer\n         * connection will disable local video transfer and the remote peer will\n         * be will be asked to stop sending video via 'content-modify' IQ\n         * (the senders attribute of video contents will be adjusted\n         * accordingly). Note that this notification is sent only in P2P\n         * session, because Jicofo does not support it yet. Obviously when\n         * the value is changed from <tt>false</tt> to <tt>true</tt> another\n         * notification will be sent to resume video transfer on the remote\n         * side.\n         * @type {boolean}\n         * @private\n         */\n        this._localVideoActive = true;\n\n        /**\n         * Indicates whether or not the remote peer has video transfer active.\n         * When set to <tt>true</tt> it means that remote peer is neither\n         * sending nor willing to receive video. In such case we'll ask\n         * our peerconnection to stop sending video by calling\n         * {@link TraceablePeerConnection.setVideoTransferActive} with\n         * <tt>false</tt>.\n         * @type {boolean}\n         * @private\n         */\n        this._remoteVideoActive = true;\n\n        /**\n         * Marks that ICE gathering duration has been reported already. That\n         * prevents reporting it again, after eventual 'transport-replace' (JVB\n         * conference migration/ICE restart).\n         * @type {boolean}\n         * @private\n         */\n        this._gatheringReported = false;\n\n        this.lasticecandidate = false;\n        this.closed = false;\n\n        /**\n         * Indicates whether or not this <tt>JingleSessionPC</tt> is used in\n         * a peer to peer type of session.\n         * @type {boolean} <tt>true</tt> if it's a peer to peer\n         * session or <tt>false</tt> if it's a JVB session\n         */\n        this.isP2P = isP2P;\n\n        /**\n         * Remote preference for the receive video max frame height.\n         *\n         * @type {Number|undefined}\n         */\n        this.remoteRecvMaxFrameHeight = undefined;\n\n        /**\n         * The queue used to serialize operations done on the peerconnection.\n         *\n         * @type {AsyncQueue}\n         */\n        this.modificationQueue = new AsyncQueue();\n\n        /**\n         * Flag used to guarantee that the connection established event is\n         * triggered just once.\n         * @type {boolean}\n         */\n        this.wasConnected = false;\n\n        /**\n         * Keeps track of how long (in ms) it took from ICE start to ICE\n         * connect.\n         *\n         * @type {number}\n         */\n        this.establishmentDuration = undefined;\n\n        this._xmppListeners = [];\n        this._xmppListeners.push(\n            connection.addEventListener(\n                XmppConnection.Events.CONN_STATUS_CHANGED,\n                this.onXmppStatusChanged.bind(this))\n        );\n\n        this._removeSenderVideoConstraintsChangeListener = undefined;\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Checks whether or not this session instance is still operational.\n     * @private\n     * @returns {boolean} {@code true} if operation or {@code false} otherwise.\n     */\n    _assertNotEnded() {\n        return this.state !== JingleSessionState.ENDED;\n    }\n\n    /**\n     * @inheritDoc\n     * @param {JingleSessionPCOptions} options  - a set of config options.\n     */\n    doInitialize(options) {\n        this.failICE = Boolean(options.failICE);\n        this.lasticecandidate = false;\n        this.options = options;\n\n        /**\n         * {@code true} if reconnect is in progress.\n         * @type {boolean}\n         */\n        this.isReconnect = false;\n\n        /**\n         * Set to {@code true} if the connection was ever stable\n         * @type {boolean}\n         */\n        this.wasstable = false;\n        this.webrtcIceUdpDisable = Boolean(options.webrtcIceUdpDisable);\n        this.webrtcIceTcpDisable = Boolean(options.webrtcIceTcpDisable);\n\n        const pcOptions = { disableRtx: options.disableRtx };\n\n        if (options.gatherStats) {\n            pcOptions.maxstats = DEFAULT_MAX_STATS;\n        }\n        pcOptions.capScreenshareBitrate = false;\n        pcOptions.enableInsertableStreams = options.enableInsertableStreams;\n        pcOptions.videoQuality = options.videoQuality;\n        pcOptions.forceTurnRelay = options.forceTurnRelay;\n        pcOptions.audioQuality = options.audioQuality;\n        pcOptions.usesUnifiedPlan = this.usesUnifiedPlan\n            = browser.supportsUnifiedPlan()\n                && (browser.isFirefox()\n                    || browser.isWebKitBased()\n                    || (browser.isChromiumBased()\n\n                        // Provide a way to control the behavior for jvb and p2p connections independently.\n                        && this.isP2P\n                        ? options.p2p?.enableUnifiedOnChrome ?? true\n                        : options.enableUnifiedOnChrome ?? true));\n\n        if (this.isP2P) {\n            // simulcast needs to be disabled for P2P (121) calls\n            pcOptions.disableSimulcast = true;\n            const abtestSuspendVideo = this._abtestSuspendVideoEnabled(options);\n\n            if (typeof abtestSuspendVideo !== 'undefined') {\n                pcOptions.abtestSuspendVideo = abtestSuspendVideo;\n            }\n        } else {\n            // H264 does not support simulcast, so it needs to be disabled.\n            pcOptions.disableSimulcast\n                = options.disableSimulcast\n                    || (options.preferH264 && !options.disableH264)\n                    || (options.videoQuality && options.videoQuality.preferredCodec === CodecMimeType.H264);\n\n            // Disable simulcast for low fps screenshare and enable it for high fps screenshare.\n            // testing.capScreenshareBitrate config.js setting has now been deprecated.\n            pcOptions.capScreenshareBitrate = pcOptions.disableSimulcast\n                || !(typeof options.desktopSharingFrameRate?.max === 'number'\n                    && options.desktopSharingFrameRate?.max > SS_DEFAULT_FRAME_RATE);\n\n            // add the capScreenshareBitrate to the permanent properties so that it's included with every event that we\n            // send to the analytics backend.\n            Statistics.analytics.addPermanentProperties({ capScreenshareBitrate: pcOptions.capScreenshareBitrate });\n        }\n\n        if (options.startSilent) {\n            pcOptions.startSilent = true;\n        }\n\n        this.peerconnection\n            = this.rtc.createPeerConnection(\n                    this._signalingLayer,\n                    this.pcConfig,\n                    this.isP2P,\n                    pcOptions);\n\n        this.peerconnection.onicecandidate = ev => {\n            if (!ev) {\n                // There was an incomplete check for ev before which left\n                // the last line of the function unprotected from a potential\n                // throw of an exception. Consequently, it may be argued that\n                // the check is unnecessary. Anyway, I'm leaving it and making\n                // the check complete.\n                return;\n            }\n\n            // XXX this is broken, candidate is not parsed.\n            const candidate = ev.candidate;\n            const now = window.performance.now();\n\n            if (candidate) {\n                if (this._gatheringStartedTimestamp === null) {\n                    this._gatheringStartedTimestamp = now;\n                }\n\n                // Discard candidates of disabled protocols.\n                let protocol = candidate.protocol;\n\n                if (typeof protocol === 'string') {\n                    protocol = protocol.toLowerCase();\n                    if (protocol === 'tcp' || protocol === 'ssltcp') {\n                        if (this.webrtcIceTcpDisable) {\n                            return;\n                        }\n                    } else if (protocol === 'udp') {\n                        if (this.webrtcIceUdpDisable) {\n                            return;\n                        }\n                    }\n                }\n            } else if (!this._gatheringReported) {\n                // End of gathering\n                Statistics.sendAnalytics(\n                    ICE_DURATION,\n                    {\n                        phase: 'gathering',\n                        value: now - this._gatheringStartedTimestamp,\n                        p2p: this.isP2P,\n                        initiator: this.isInitiator\n                    });\n                this._gatheringReported = true;\n            }\n            this.sendIceCandidate(candidate);\n        };\n\n        // Note there is a change in the spec about closed:\n        // This value moved into the RTCPeerConnectionState enum in\n        // the May 13, 2016 draft of the specification, as it reflects the state\n        // of the RTCPeerConnection, not the signaling connection. You now\n        // detect a closed connection by checking for connectionState to be\n        // \"closed\" instead.\n        // I suppose at some point this will be moved to onconnectionstatechange\n        this.peerconnection.onsignalingstatechange = () => {\n            if (this.peerconnection.signalingState === 'stable') {\n                this.wasstable = true;\n            } else if (this.peerconnection.signalingState === 'closed'\n                || this.peerconnection.connectionState === 'closed') {\n                this.room.eventEmitter.emit(XMPPEvents.SUSPEND_DETECTED, this);\n            }\n        };\n\n        /**\n         * The oniceconnectionstatechange event handler contains the code to\n         * execute when the iceconnectionstatechange event, of type Event,\n         * is received by this RTCPeerConnection. Such an event is sent when\n         * the value of RTCPeerConnection.iceConnectionState changes.\n         */\n        this.peerconnection.oniceconnectionstatechange = () => {\n            const now = window.performance.now();\n            let isStable = false;\n\n            if (!this.isP2P) {\n                this.room.connectionTimes[\n                    `ice.state.${this.peerconnection.iceConnectionState}`]\n                    = now;\n            }\n            logger.log(`(TIME) ICE ${this.peerconnection.iceConnectionState} ${this.isP2P ? 'P2P' : 'JVB'}:\\t`, now);\n\n            Statistics.sendAnalytics(\n                ICE_STATE_CHANGED,\n                {\n                    p2p: this.isP2P,\n                    state: this.peerconnection.iceConnectionState,\n                    'signaling_state': this.peerconnection.signalingState,\n                    reconnect: this.isReconnect,\n                    value: now\n                });\n\n            this.room.eventEmitter.emit(\n                XMPPEvents.ICE_CONNECTION_STATE_CHANGED,\n                this,\n                this.peerconnection.iceConnectionState);\n            switch (this.peerconnection.iceConnectionState) {\n            case 'checking':\n                this._iceCheckingStartedTimestamp = now;\n                break;\n            case 'connected':\n                // Informs interested parties that the connection has been restored. This includes the case when\n                // media connection to the bridge has been restored after an ICE failure by using session-terminate.\n                if (this.peerconnection.signalingState === 'stable') {\n                    isStable = true;\n                    const usesTerminateForRestart = !this.options.enableIceRestart\n                        && this.room.supportsRestartByTerminate();\n\n                    if (this.isReconnect || usesTerminateForRestart) {\n                        this.room.eventEmitter.emit(\n                            XMPPEvents.CONNECTION_RESTORED, this);\n                    }\n                }\n\n                // Add a workaround for an issue on chrome in Unified plan when the local endpoint is the offerer.\n                // The 'signalingstatechange' event for 'stable' is handled after the 'iceconnectionstatechange' event\n                // for 'completed' is handled by the client. This prevents the client from firing a\n                // CONNECTION_ESTABLISHED event for the p2p session. As a result, the offerer continues to stay on the\n                // jvb connection while the remote peer switches to the p2p connection breaking the media flow between\n                // the endpoints.\n                // TODO - file a chromium bug and add the information here.\n                if (!this.wasConnected\n                    && (this.wasstable\n                        || isStable\n                        || (this.usesUnifiedPlan && this.isInitiator && browser.isChromiumBased()))) {\n\n                    Statistics.sendAnalytics(\n                        ICE_DURATION,\n                        {\n                            phase: 'checking',\n                            value: now - this._iceCheckingStartedTimestamp,\n                            p2p: this.isP2P,\n                            initiator: this.isInitiator\n                        });\n\n                    // Switch between ICE gathering and ICE checking whichever\n                    // started first (scenarios are different for initiator\n                    // vs responder)\n                    const iceStarted\n                        = Math.min(\n                            this._iceCheckingStartedTimestamp,\n                            this._gatheringStartedTimestamp);\n\n                    this.establishmentDuration = now - iceStarted;\n\n                    Statistics.sendAnalytics(\n                        ICE_DURATION,\n                        {\n                            phase: 'establishment',\n                            value: this.establishmentDuration,\n                            p2p: this.isP2P,\n                            initiator: this.isInitiator\n                        });\n\n                    this.wasConnected = true;\n                    this.room.eventEmitter.emit(\n                        XMPPEvents.CONNECTION_ESTABLISHED, this);\n                }\n                this.isReconnect = false;\n                break;\n            case 'disconnected':\n                this.isReconnect = true;\n\n                // Informs interested parties that the connection has been\n                // interrupted.\n                if (this.wasstable) {\n                    this.room.eventEmitter.emit(\n                        XMPPEvents.CONNECTION_INTERRUPTED, this);\n                }\n                break;\n            case 'failed':\n                this.room.eventEmitter.emit(\n                    XMPPEvents.CONNECTION_ICE_FAILED, this);\n                break;\n            }\n        };\n\n\n        /**\n         * The connection state event is fired whenever the aggregate of underlying\n         * transports change their state.\n         */\n        this.peerconnection.onconnectionstatechange = () => {\n            const icestate = this.peerconnection.iceConnectionState;\n\n            switch (this.peerconnection.connectionState) {\n            case 'failed':\n                // Since version 76 Chrome no longer switches ICE connection\n                // state to failed (see\n                // https://bugs.chromium.org/p/chromium/issues/detail?id=982793\n                // for details) we use this workaround to recover from lost connections\n                if (icestate === 'disconnected') {\n                    this.room.eventEmitter.emit(\n                        XMPPEvents.CONNECTION_ICE_FAILED, this);\n                }\n                break;\n            }\n        };\n\n        /**\n         * The negotiationneeded event is fired whenever we shake the media on the\n         * RTCPeerConnection object.\n         */\n        this.peerconnection.onnegotiationneeded = () => {\n            const state = this.peerconnection.signalingState;\n            const remoteDescription = this.peerconnection.remoteDescription;\n\n            if (this.usesUnifiedPlan\n                && !this.isP2P\n                && state === 'stable'\n                && remoteDescription\n                && typeof remoteDescription.sdp === 'string') {\n                logger.info(`${this} onnegotiationneeded fired on ${this.peerconnection}`);\n\n                const workFunction = finishedCallback => {\n                    const oldSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n                    this._renegotiate()\n                        .then(() => this.peerconnection.configureSenderVideoEncodings())\n                        .then(() => {\n                            const newSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n                            this.notifyMySSRCUpdate(oldSdp, newSdp);\n                        })\n                        .then(() => finishedCallback(), error => finishedCallback(error));\n                };\n\n                this.modificationQueue.push(\n                    workFunction,\n                    error => {\n                        if (error) {\n                            logger.error(`${this} onnegotiationneeded error`, error);\n                        } else {\n                            logger.debug(`${this} onnegotiationneeded executed - OK`);\n                        }\n                    });\n            }\n        };\n    }\n\n    /**\n     * Remote preference for receive video max frame height.\n     *\n     * @returns {Number|undefined}\n     */\n    getRemoteRecvMaxFrameHeight() {\n        if (this.isP2P) {\n            return this.remoteRecvMaxFrameHeight;\n        }\n\n        return undefined;\n    }\n\n    /**\n     * Sends given candidate in Jingle 'transport-info' message.\n     * @param {RTCIceCandidate} candidate the WebRTC ICE candidate instance\n     * @private\n     */\n    sendIceCandidate(candidate) {\n        const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n        if (candidate && candidate.candidate.length && !this.lasticecandidate) {\n            const ice = SDPUtil.iceparams(localSDP.media[candidate.sdpMLineIndex], localSDP.session);\n            const jcand = SDPUtil.candidateToJingle(candidate.candidate);\n\n            if (!(ice && jcand)) {\n                const errorMesssage = 'failed to get ice && jcand';\n\n                GlobalOnErrorHandler.callErrorHandler(new Error(errorMesssage));\n                logger.error(errorMesssage);\n\n                return;\n            }\n            ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n\n            if (this.usedrip) {\n                if (this.dripContainer.length === 0) {\n                    setTimeout(() => {\n                        if (this.dripContainer.length === 0) {\n                            return;\n                        }\n                        this.sendIceCandidates(this.dripContainer);\n                        this.dripContainer = [];\n                    }, ICE_CAND_GATHERING_TIMEOUT);\n                }\n                this.dripContainer.push(candidate);\n            } else {\n                this.sendIceCandidates([ candidate ]);\n            }\n        } else {\n            logger.log(`${this} sendIceCandidate: last candidate`);\n\n            // FIXME: remember to re-think in ICE-restart\n            this.lasticecandidate = true;\n        }\n    }\n\n    /**\n     * Sends given candidates in Jingle 'transport-info' message.\n     * @param {Array<RTCIceCandidate>} candidates an array of the WebRTC ICE\n     * candidate instances\n     * @private\n     */\n    sendIceCandidates(candidates) {\n        if (!this._assertNotEnded('sendIceCandidates')) {\n\n            return;\n        }\n\n        logger.log(`${this} sendIceCandidates ${JSON.stringify(candidates)}`);\n        const cand = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n                action: 'transport-info',\n                initiator: this.initiatorJid,\n                sid: this.sid });\n\n        const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n        for (let mid = 0; mid < localSDP.media.length; mid++) {\n            const cands = candidates.filter(el => el.sdpMLineIndex === mid);\n            const mline\n                = SDPUtil.parseMLine(localSDP.media[mid].split('\\r\\n')[0]);\n\n            if (cands.length > 0) {\n                const ice\n                    = SDPUtil.iceparams(localSDP.media[mid], localSDP.session);\n\n                ice.xmlns = 'urn:xmpp:jingle:transports:ice-udp:1';\n                cand.c('content', {\n                    creator: this.initiatorJid === this.localJid\n                        ? 'initiator' : 'responder',\n                    name: cands[0].sdpMid ? cands[0].sdpMid : mline.media\n                }).c('transport', ice);\n                for (let i = 0; i < cands.length; i++) {\n                    const candidate\n                        = SDPUtil.candidateToJingle(cands[i].candidate);\n\n                    // Mangle ICE candidate if 'failICE' test option is enabled\n\n                    if (this.failICE) {\n                        candidate.ip = '1.1.1.1';\n                    }\n                    cand.c('candidate', candidate).up();\n                }\n\n                // add fingerprint\n                const fingerprintLine\n                    = SDPUtil.findLine(\n                        localSDP.media[mid],\n                        'a=fingerprint:', localSDP.session);\n\n                if (fingerprintLine) {\n                    const tmp = SDPUtil.parseFingerprint(fingerprintLine);\n\n                    tmp.required = true;\n                    cand.c(\n                        'fingerprint',\n                        { xmlns: 'urn:xmpp:jingle:apps:dtls:0' })\n                        .t(tmp.fingerprint);\n                    delete tmp.fingerprint;\n                    cand.attrs(tmp);\n                    cand.up();\n                }\n                cand.up(); // transport\n                cand.up(); // content\n            }\n        }\n\n        // might merge last-candidate notification into this, but it is called\n        // a lot later. See webrtc issue #2340\n        // logger.log('was this the last candidate', this.lasticecandidate);\n        this.connection.sendIQ(\n            cand, null, this.newJingleErrorHandler(cand), IQ_TIMEOUT);\n    }\n\n    /**\n     * Sends Jingle 'session-info' message which includes custom Jitsi Meet\n     * 'ice-state' element with the text value 'failed' to let Jicofo know\n     * that the ICE connection has entered the failed state. It can then\n     * choose to re-create JVB channels and send 'transport-replace' to\n     * retry the connection.\n     */\n    sendIceFailedNotification() {\n        const sessionInfo\n            = $iq({\n                to: this.remoteJid,\n                type: 'set' })\n            .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n                action: 'session-info',\n                initiator: this.initiatorJid,\n                sid: this.sid })\n            .c('ice-state', { xmlns: 'http://jitsi.org/protocol/focus' })\n            .t('failed')\n            .up();\n\n        this._bridgeSessionId\n            && sessionInfo.c(\n                'bridge-session', {\n                    xmlns: 'http://jitsi.org/protocol/focus',\n                    id: this._bridgeSessionId\n                });\n\n        this.connection.sendIQ2(\n            sessionInfo, {\n                /*\n                 * This message will be often sent when there are connectivity\n                 * issues, so make it slightly longer than Prosody's default BOSH\n                 * inactivity timeout of 60 seconds.\n                 */\n                timeout: 65\n            })\n            .catch(this.newJingleErrorHandler(sessionInfo));\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    addIceCandidates(elem) {\n        if (this.peerconnection.signalingState === 'closed') {\n            logger.warn(`${this} Ignored add ICE candidate when in closed state`);\n\n            return;\n        }\n\n        const iceCandidates = [];\n\n        elem.find('>content>transport>candidate')\n            .each((idx, candidate) => {\n                let line = SDPUtil.candidateFromJingle(candidate);\n\n                line = line.replace('\\r\\n', '').replace('a=', '');\n\n                // FIXME this code does not care to handle\n                // non-bundle transport\n                const rtcCandidate = new RTCIceCandidate({\n                    sdpMLineIndex: 0,\n\n                    // FF comes up with more complex names like audio-23423,\n                    // Given that it works on both Chrome and FF without\n                    // providing it, let's leave it like this for the time\n                    // being...\n                    // sdpMid: 'audio',\n                    sdpMid: '',\n                    candidate: line\n                });\n\n                iceCandidates.push(rtcCandidate);\n            });\n\n        if (!iceCandidates.length) {\n            logger.error(`${this} No ICE candidates to add ?`, elem[0] && elem[0].outerHTML);\n\n            return;\n        }\n\n        // We want to have this task queued, so that we know it is executed,\n        // after the initial sRD/sLD offer/answer cycle was done (based on\n        // the assumption that candidates are spawned after the offer/answer\n        // and XMPP preserves order).\n        const workFunction = finishedCallback => {\n            for (const iceCandidate of iceCandidates) {\n                this.peerconnection.addIceCandidate(iceCandidate)\n                    .then(\n                        () => logger.debug(`${this} addIceCandidate ok!`),\n                        err => logger.error(`${this} addIceCandidate failed!`, err));\n            }\n\n            finishedCallback();\n            logger.debug(`${this} ICE candidates task finished`);\n        };\n\n        logger.debug(`${this} Queued add (${iceCandidates.length}) ICE candidates task`);\n        this.modificationQueue.push(workFunction);\n    }\n\n    /**\n     *\n     * @param contents\n     */\n    readSsrcInfo(contents) {\n        const ssrcs\n            = $(contents).find(\n                '>description>'\n                    + 'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n        ssrcs.each((i, ssrcElement) => {\n            const ssrc = Number(ssrcElement.getAttribute('ssrc'));\n\n            if (this.isP2P) {\n                // In P2P all SSRCs are owner by the remote peer\n                this._signalingLayer.setSSRCOwner(\n                    ssrc, Strophe.getResourceFromJid(this.remoteJid));\n            } else {\n                if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                    // Only set sourceName for non-P2P case\n                    if (ssrcElement.hasAttribute('name')) {\n                        const sourceName = ssrcElement.getAttribute('name');\n\n                        this._signalingLayer.setTrackSourceName(ssrc, sourceName);\n                    }\n                }\n                $(ssrcElement)\n                    .find('>ssrc-info[xmlns=\"http://jitsi.org/jitmeet\"]')\n                    .each((i3, ssrcInfoElement) => {\n                        const owner = ssrcInfoElement.getAttribute('owner');\n\n                        if (owner && owner.length) {\n                            if (isNaN(ssrc) || ssrc < 0) {\n                                logger.warn(`${this} Invalid SSRC ${ssrc} value received for ${owner}`);\n                            } else {\n                                this._signalingLayer.setSSRCOwner(\n                                    ssrc,\n                                    getEndpointId(owner));\n                            }\n                        }\n                    });\n            }\n        });\n    }\n\n    /**\n     * Makes the underlying TraceablePeerConnection generate new SSRC for\n     * the recvonly video stream.\n     * @deprecated\n     */\n    generateRecvonlySsrc() {\n        if (this.peerconnection) {\n            this.peerconnection.generateRecvonlySsrc();\n        } else {\n            logger.error(`${this} Unable to generate recvonly SSRC - no peerconnection`);\n        }\n    }\n\n    /**\n     * Returns the video codec configured as the preferred codec on the peerconnection.\n     */\n    getConfiguredVideoCodec() {\n        return this.peerconnection.getConfiguredVideoCodec();\n    }\n\n    /* eslint-disable max-params */\n    /**\n     * Accepts incoming Jingle 'session-initiate' and should send\n     * 'session-accept' in result.\n     * @param jingleOffer jQuery selector pointing to the jingle element of\n     * the offer IQ\n     * @param success callback called when we accept incoming session\n     * successfully and receive RESULT packet to 'session-accept' sent.\n     * @param failure function(error) called if for any reason we fail to accept\n     * the incoming offer. 'error' argument can be used to log some details\n     * about the error.\n     * @param {Array<JitsiLocalTrack>} [localTracks] the optional list of\n     * the local tracks that will be added, before the offer/answer cycle\n     * executes. We allow the localTracks to optionally be passed in so that\n     * the addition of the local tracks and the processing of the initial offer\n     * can all be done atomically. We want to make sure that any other\n     * operations which originate in the XMPP Jingle messages related with\n     * this session to be executed with an assumption that the initial\n     * offer/answer cycle has been executed already.\n     */\n    acceptOffer(jingleOffer, success, failure, localTracks) {\n        this.setOfferAnswerCycle(\n            jingleOffer,\n            () => {\n                // FIXME we may not care about RESULT packet for session-accept\n                // then we should either call 'success' here immediately or\n                // modify sendSessionAccept method to do that\n                this.sendSessionAccept(() => {\n                    success();\n\n                    this.room.eventEmitter.emit(XMPPEvents.SESSION_ACCEPT, this);\n                },\n                error => {\n                    failure(error);\n\n                    this.room.eventEmitter.emit(XMPPEvents.SESSION_ACCEPT_ERROR, this, error);\n                });\n            },\n            failure,\n            localTracks);\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Creates an offer and sends Jingle 'session-initiate' to the remote peer.\n     * @param {Array<JitsiLocalTrack>} localTracks the local tracks that will be\n     * added, before the offer/answer cycle executes (for the local track\n     * addition to be an atomic operation together with the offer/answer).\n     */\n    invite(localTracks = []) {\n        if (!this.isInitiator) {\n            throw new Error('Trying to invite from the responder session');\n        }\n        const workFunction = finishedCallback => {\n            const addTracks = [];\n\n            for (const localTrack of localTracks) {\n                addTracks.push(this.peerconnection.addTrack(localTrack, this.isInitiator));\n            }\n\n            Promise.all(addTracks)\n                .then(() => this.peerconnection.createOffer(this.mediaConstraints))\n                .then(offerSdp => this.peerconnection.setLocalDescription(offerSdp))\n                .then(() => {\n                    // NOTE that the offer is obtained from the localDescription getter as it needs to go though\n                    // the transformation chain.\n                    this.sendSessionInitiate(this.peerconnection.localDescription.sdp);\n                })\n                .then(() => finishedCallback(), error => finishedCallback(error));\n        };\n\n        logger.debug(`${this} Queued invite task`);\n        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error(`${this} invite error`, error);\n                } else {\n                    logger.debug(`${this} invite executed - OK`);\n                }\n            });\n    }\n\n    /**\n     * Sends 'session-initiate' to the remote peer.\n     *\n     * NOTE this method is synchronous and we're not waiting for the RESULT\n     * response which would delay the startup process.\n     *\n     * @param {string} offerSdp  - The local session description which will be\n     * used to generate an offer.\n     * @private\n     */\n    sendSessionInitiate(offerSdp) {\n        let init = $iq({\n            to: this.remoteJid,\n            type: 'set'\n        }).c('jingle', {\n            xmlns: 'urn:xmpp:jingle:1',\n            action: 'session-initiate',\n            initiator: this.initiatorJid,\n            sid: this.sid\n        });\n\n        new SDP(offerSdp).toJingle(\n            init,\n            this.isInitiator ? 'initiator' : 'responder');\n        init = init.tree();\n        logger.debug(`${this} Session-initiate: `, init);\n        this.connection.sendIQ(init,\n            () => {\n                logger.info(`${this} Got RESULT for \"session-initiate\"`);\n            },\n            error => {\n                logger.error(`${this} \"session-initiate\" error`, error);\n            },\n            IQ_TIMEOUT);\n    }\n\n    /**\n     * Sets the answer received from the remote peer.\n     * @param jingleAnswer\n     */\n    setAnswer(jingleAnswer) {\n        if (!this.isInitiator) {\n            throw new Error('Trying to set an answer on the responder session');\n        }\n        this.setOfferAnswerCycle(\n            jingleAnswer,\n            () => {\n                logger.info(`${this} setAnswer - succeeded`);\n                if (this.usesUnifiedPlan && browser.isChromiumBased()) {\n                    const workFunction = finishedCallback => {\n                        // This hack is needed for Chrome to create a decoder for the ssrcs in the remote SDP when\n                        // the local endpoint is the offerer and starts muted.\n                        const remoteSdp = this.peerconnection.remoteDescription.sdp;\n                        const remoteDescription = new RTCSessionDescription({\n                            type: 'offer',\n                            sdp: remoteSdp\n                        });\n\n                        return this._responderRenegotiate(remoteDescription)\n                        .then(() => finishedCallback(), error => finishedCallback(error));\n                    };\n\n                    logger.debug(`${this} Queued responderRenegotiate task`);\n                    this.modificationQueue.push(\n                        workFunction,\n                        error => {\n                            if (error) {\n                                logger.error(`${this} failed to renegotiate a decoder for muted endpoint ${error}`);\n                            } else {\n                                logger.debug(`${this} renegotiate a decoder for muted endpoint`);\n                            }\n                        });\n                }\n            },\n            error => {\n                logger.error(`${this} setAnswer failed: `, error);\n            });\n    }\n\n    /* eslint-disable max-params */\n    /**\n     * This is a setRemoteDescription/setLocalDescription cycle which starts at\n     * converting Strophe Jingle IQ into remote offer SDP. Once converted\n     * setRemoteDescription, createAnswer and setLocalDescription calls follow.\n     * @param jingleOfferAnswerIq jQuery selector pointing to the jingle element\n     *        of the offer (or answer) IQ\n     * @param success callback called when sRD/sLD cycle finishes successfully.\n     * @param failure callback called with an error object as an argument if we\n     *        fail at any point during setRD, createAnswer, setLD.\n     * @param {Array<JitsiLocalTrack>} [localTracks] the optional list of\n     * the local tracks that will be added, before the offer/answer cycle\n     * executes (for the local track addition to be an atomic operation together\n     * with the offer/answer).\n     */\n    setOfferAnswerCycle(jingleOfferAnswerIq, success, failure, localTracks = []) {\n        const workFunction = finishedCallback => {\n            const addTracks = [];\n\n            for (const track of localTracks) {\n                addTracks.push(this.peerconnection.addTrack(track, this.isInitiator));\n            }\n\n            const newRemoteSdp\n                = this._processNewJingleOfferIq(jingleOfferAnswerIq);\n            const oldLocalSdp\n                = this.peerconnection.localDescription.sdp;\n\n            const bridgeSession\n                = $(jingleOfferAnswerIq)\n                    .find('>bridge-session['\n                        + 'xmlns=\"http://jitsi.org/protocol/focus\"]');\n            const bridgeSessionId = bridgeSession.attr('id');\n\n            if (bridgeSessionId !== this._bridgeSessionId) {\n                this._bridgeSessionId = bridgeSessionId;\n            }\n\n            Promise.all(addTracks)\n                .then(() => this._renegotiate(newRemoteSdp.raw))\n                .then(() => {\n                    if (this.state === JingleSessionState.PENDING) {\n                        this.state = JingleSessionState.ACTIVE;\n\n                        // #1 Sync up video transfer active/inactive only after\n                        // the initial O/A cycle. We want to adjust the video\n                        // media direction only in the local SDP and the Jingle\n                        // contents direction included in the initial\n                        // offer/answer is mapped to the remote SDP. Jingle\n                        // 'content-modify' IQ is processed in a way that it\n                        // will only modify local SDP when remote peer is no\n                        // longer interested in receiving video content.\n                        // Changing media direction in the remote SDP will mess\n                        // up our SDP translation chain (simulcast, video mute,\n                        // RTX etc.)\n                        //\n                        // #2 Sends the max frame height if it was set, before the session-initiate/accept\n                        if (this.isP2P\n                            && (!this._localVideoActive || this.localRecvMaxFrameHeight)) {\n                            this.sendContentModify();\n                        }\n                    }\n\n                    // Old local SDP will be available when we're setting answer\n                    // for the first time, but not when offer and it's fine\n                    // since we're generating an answer now it will contain all\n                    // our SSRCs\n                    if (oldLocalSdp) {\n                        const newLocalSdp\n                            = new SDP(this.peerconnection.localDescription.sdp);\n\n                        this.notifyMySSRCUpdate(\n                            new SDP(oldLocalSdp), newLocalSdp);\n                    }\n                })\n                .then(() => finishedCallback(), error => finishedCallback(error));\n        };\n\n        logger.debug(`${this} Queued setOfferAnswerCycle task`);\n        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error(`${this} setOfferAnswerCycle task failed: ${error}`);\n                    failure(error);\n                } else {\n                    logger.debug(`${this} setOfferAnswerCycle task done`);\n                    success();\n                }\n            });\n    }\n\n    /**\n     * Updates the codecs on the peerconnection and initiates a renegotiation for the\n     * new codec config to take effect.\n     *\n     * @param {CodecMimeType} preferred the preferred codec.\n     * @param {CodecMimeType} disabled the codec that needs to be disabled.\n     */\n    setVideoCodecs(preferred = null, disabled = null) {\n        const current = this.peerconnection.getConfiguredVideoCodec();\n\n        if (this._assertNotEnded() && preferred !== current) {\n            logger.info(`${this} Switching video codec from ${current} to ${preferred}`);\n            this.peerconnection.setVideoCodecs(preferred, disabled);\n\n            // Initiate a renegotiate for the codec setting to take effect.\n            const workFunction = finishedCallback => {\n                this._renegotiate().then(\n                    () => {\n                        logger.debug(`${this} setVideoCodecs task is done`);\n\n                        return finishedCallback();\n                    }, error => {\n                        logger.error(`${this} setVideoCodecs task failed: ${error}`);\n\n                        return finishedCallback(error);\n                    });\n            };\n\n            logger.debug(`${this} Queued setVideoCodecs task`);\n\n            // Queue and execute\n            this.modificationQueue.push(workFunction);\n        }\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Although it states \"replace transport\" it does accept full Jingle offer\n     * which should contain new ICE transport details.\n     * @param jingleOfferElem an element Jingle IQ that contains new offer and\n     *        transport info.\n     * @param success callback called when we succeed to accept new offer.\n     * @param failure function(error) called when we fail to accept new offer.\n     */\n    replaceTransport(jingleOfferElem, success, failure) {\n        if (this.options.enableForcedReload) {\n            const sdp = new SDP(this.peerconnection.localDescription.sdp);\n\n            this.sendTransportAccept(sdp, success, failure);\n            this.room.eventEmitter.emit(XMPPEvents.CONNECTION_RESTARTED, this);\n\n            return;\n        }\n        this.room.eventEmitter.emit(XMPPEvents.ICE_RESTARTING, this);\n\n        // We need to first reject the 'data' section to have the SCTP stack\n        // cleaned up to signal the known data channel is now invalid. After\n        // that the original offer is set to have the SCTP connection\n        // established with the new bridge.\n        const originalOffer = jingleOfferElem.clone();\n\n        jingleOfferElem\n            .find('>content[name=\\'data\\']')\n            .attr('senders', 'rejected');\n\n        // Remove all remote sources in order to reset the client's state\n        // for the remote MediaStreams. When a conference is moved to\n        // another bridge it will start streaming with a sequence number\n        // that is not in sync with the most recently seen by the client.\n        // The symptoms include frozen or black video and lots of \"failed to\n        // unprotect SRTP packets\" in Chrome logs.\n        jingleOfferElem\n            .find('>content>description>source')\n            .remove();\n        jingleOfferElem\n            .find('>content>description>ssrc-group')\n            .remove();\n\n        // On the JVB it's not a real ICE restart and all layers are re-initialized from scratch as Jicofo does\n        // the restart by re-allocating new channels. Chrome (or WebRTC stack) needs to have the DTLS transport layer\n        // reset to start a new handshake with fresh DTLS transport on the bridge. Make it think that the DTLS\n        // fingerprint has changed by setting an all zeros key.\n        const newFingerprint = jingleOfferElem.find('>content>transport>fingerprint');\n\n        newFingerprint.attr('hash', 'sha-1');\n        newFingerprint.text('00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00');\n\n        // First set an offer with a rejected 'data' section\n        this.setOfferAnswerCycle(\n            jingleOfferElem,\n            () => {\n                // Now set the original offer(with the 'data' section)\n                this.setOfferAnswerCycle(\n                    originalOffer,\n                    () => {\n                        const localSDP\n                            = new SDP(this.peerconnection.localDescription.sdp);\n\n                        this.sendTransportAccept(localSDP, success, failure);\n\n                        this.room.eventEmitter.emit(\n                            XMPPEvents.ICE_RESTART_SUCCESS,\n                            this,\n                            originalOffer);\n                    },\n                    failure);\n            },\n            failure\n        );\n    }\n\n    /**\n     * Sends Jingle 'session-accept' message.\n     * @param {function()} success callback called when we receive 'RESULT'\n     *        packet for the 'session-accept'\n     * @param {function(error)} failure called when we receive an error response\n     *        or when the request has timed out.\n     * @private\n     */\n    sendSessionAccept(success, failure) {\n        // NOTE: since we're just reading from it, we don't need to be within\n        //  the modification queue to access the local description\n        const localSDP = new SDP(this.peerconnection.localDescription.sdp);\n        const accept = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', { xmlns: 'urn:xmpp:jingle:1',\n                action: 'session-accept',\n                initiator: this.initiatorJid,\n                responder: this.responderJid,\n                sid: this.sid });\n\n        if (this.webrtcIceTcpDisable) {\n            localSDP.removeTcpCandidates = true;\n        }\n        if (this.webrtcIceUdpDisable) {\n            localSDP.removeUdpCandidates = true;\n        }\n        if (this.failICE) {\n            localSDP.failICE = true;\n        }\n        localSDP.toJingle(\n            accept,\n            this.initiatorJid === this.localJid ? 'initiator' : 'responder');\n\n        logger.info(`${this} Sending session-accept`);\n        logger.debug(accept.tree());\n        this.connection.sendIQ(accept,\n            success,\n            this.newJingleErrorHandler(accept, error => {\n                failure(error);\n\n                // 'session-accept' is a critical timeout and we'll\n                // have to restart\n                this.room.eventEmitter.emit(\n                    XMPPEvents.SESSION_ACCEPT_TIMEOUT, this);\n            }),\n            IQ_TIMEOUT);\n\n        // XXX Videobridge needs WebRTC's answer (ICE ufrag and pwd, DTLS\n        // fingerprint and setup) ASAP in order to start the connection\n        // establishment.\n        //\n        // FIXME Flushing the connection at this point triggers an issue with\n        // BOSH request handling in Prosody on slow connections.\n        //\n        // The problem is that this request will be quite large and it may take\n        // time before it reaches Prosody. In the meantime Strophe may decide\n        // to send the next one. And it was observed that a small request with\n        // 'transport-info' usually follows this one. It does reach Prosody\n        // before the previous one was completely received. 'rid' on the server\n        // is increased and Prosody ignores the request with 'session-accept'.\n        // It will never reach Jicofo and everything in the request table is\n        // lost. Removing the flush does not guarantee it will never happen, but\n        // makes it much less likely('transport-info' is bundled with\n        // 'session-accept' and any immediate requests).\n        //\n        // this.connection.flush();\n    }\n\n    /**\n     * Will send 'content-modify' IQ in order to ask the remote peer to\n     * either stop or resume sending video media or to adjust sender's video constraints.\n     * @private\n     */\n    sendContentModify() {\n        const maxFrameHeight = this.localRecvMaxFrameHeight;\n        const senders = this._localVideoActive ? 'both' : 'none';\n\n        let sessionModify\n            = $iq({\n                to: this.remoteJid,\n                type: 'set'\n            })\n                .c('jingle', {\n                    xmlns: 'urn:xmpp:jingle:1',\n                    action: 'content-modify',\n                    initiator: this.initiatorJid,\n                    sid: this.sid\n                })\n                .c('content', {\n                    name: MediaType.VIDEO,\n                    senders\n                });\n\n        if (typeof maxFrameHeight !== 'undefined') {\n            sessionModify = sessionModify\n                .c('max-frame-height', { xmlns: 'http://jitsi.org/jitmeet/video' })\n                .t(maxFrameHeight);\n        }\n\n        logger.info(`${this} sending content-modify, video senders: ${senders}, max frame height: ${maxFrameHeight}`);\n        logger.debug(sessionModify.tree());\n\n        this.connection.sendIQ(\n            sessionModify,\n            null,\n            this.newJingleErrorHandler(sessionModify),\n            IQ_TIMEOUT);\n    }\n\n    /**\n     * Adjust the preference for max video frame height that the local party is willing to receive. Signals\n     * the remote party.\n     *\n     * @param {Number} maxFrameHeight - the new value to set.\n     */\n    setReceiverVideoConstraint(maxFrameHeight) {\n        logger.info(`${this} setReceiverVideoConstraint - max frame height: ${maxFrameHeight}`);\n\n        this.localRecvMaxFrameHeight = maxFrameHeight;\n\n        if (this.isP2P) {\n            // Tell the remote peer about our receive constraint. If Jingle session is not yet active the state will\n            // be synced after offer/answer.\n            if (this.state === JingleSessionState.ACTIVE) {\n                this.sendContentModify();\n            }\n        } else {\n            this.rtc.setReceiverVideoConstraint(maxFrameHeight);\n        }\n    }\n\n    /**\n     * Sends Jingle 'transport-accept' message which is a response to\n     * 'transport-replace'.\n     * @param localSDP the 'SDP' object with local session description\n     * @param success callback called when we receive 'RESULT' packet for\n     *        'transport-replace'\n     * @param failure function(error) called when we receive an error response\n     *        or when the request has timed out.\n     * @private\n     */\n    sendTransportAccept(localSDP, success, failure) {\n        const transportAccept = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', {\n                xmlns: 'urn:xmpp:jingle:1',\n                action: 'transport-accept',\n                initiator: this.initiatorJid,\n                sid: this.sid\n            });\n\n        localSDP.media.forEach((medialines, idx) => {\n            const mline = SDPUtil.parseMLine(medialines.split('\\r\\n')[0]);\n\n            transportAccept.c('content',\n                {\n                    creator:\n                        this.initiatorJid === this.localJid\n                            ? 'initiator'\n                            : 'responder',\n                    name: mline.media\n                }\n            );\n            localSDP.transportToJingle(idx, transportAccept);\n            transportAccept.up();\n        });\n\n        logger.info(`${this} Sending transport-accept`);\n        logger.debug(transportAccept.tree());\n\n        this.connection.sendIQ(transportAccept,\n            success,\n            this.newJingleErrorHandler(transportAccept, failure),\n            IQ_TIMEOUT);\n    }\n\n    /**\n     * Sends Jingle 'transport-reject' message which is a response to\n     * 'transport-replace'.\n     * @param success callback called when we receive 'RESULT' packet for\n     *        'transport-replace'\n     * @param failure function(error) called when we receive an error response\n     *        or when the request has timed out.\n     *\n     * FIXME method should be marked as private, but there's some spaghetti that\n     *       needs to be fixed prior doing that\n     */\n    sendTransportReject(success, failure) {\n        // Send 'transport-reject', so that the focus will\n        // know that we've failed\n        const transportReject = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', {\n                xmlns: 'urn:xmpp:jingle:1',\n                action: 'transport-reject',\n                initiator: this.initiatorJid,\n                sid: this.sid\n            });\n\n        logger.info(`${this} Sending 'transport-reject'`);\n        logger.debug(transportReject.tree());\n\n        this.connection.sendIQ(transportReject,\n            success,\n            this.newJingleErrorHandler(transportReject, failure),\n            IQ_TIMEOUT);\n    }\n\n    /**\n     * Sets the resolution constraint on the local camera track.\n     * @param {number} maxFrameHeight - The user preferred max frame height.\n     * @param {string} sourceName - The source name of the track.\n     * @returns {Promise} promise that will be resolved when the operation is\n     * successful and rejected otherwise.\n     */\n    setSenderVideoConstraint(maxFrameHeight, sourceName = null) {\n        if (this._assertNotEnded()) {\n            logger.info(`${this} setSenderVideoConstraint: ${maxFrameHeight}, sourceName: ${sourceName}`);\n\n            const jitsiLocalTrack = sourceName\n                ? this.rtc.getLocalVideoTracks().find(track => track.getSourceName() === sourceName)\n                : this.rtc.getLocalVideoTrack();\n\n            return this.peerconnection.setSenderVideoConstraints(maxFrameHeight, jitsiLocalTrack);\n        }\n\n        return Promise.resolve();\n    }\n\n    /**\n     * @inheritDoc\n     */\n    terminate(success, failure, options) {\n        if (this.state === JingleSessionState.ENDED) {\n            return;\n        }\n\n        if (!options || Boolean(options.sendSessionTerminate)) {\n            const sessionTerminate\n                = $iq({\n                    to: this.remoteJid,\n                    type: 'set'\n                })\n                    .c('jingle', {\n                        xmlns: 'urn:xmpp:jingle:1',\n                        action: 'session-terminate',\n                        initiator: this.initiatorJid,\n                        sid: this.sid\n                    })\n                    .c('reason')\n                    .c((options && options.reason) || 'success')\n                    .up();\n\n            if (options && options.reasonDescription) {\n                sessionTerminate\n                    .c('text')\n                    .t(options.reasonDescription)\n                    .up()\n                    .up();\n            } else {\n                sessionTerminate.up();\n            }\n\n            this._bridgeSessionId\n                && sessionTerminate.c(\n                    'bridge-session', {\n                        xmlns: 'http://jitsi.org/protocol/focus',\n                        id: this._bridgeSessionId,\n                        restart: options && options.requestRestart === true\n                    }).up();\n\n            logger.info(`${this} Sending session-terminate`);\n            logger.debug(sessionTerminate.tree());\n\n            this.connection.sendIQ(\n                sessionTerminate,\n                success,\n                this.newJingleErrorHandler(sessionTerminate, failure),\n                IQ_TIMEOUT);\n        } else {\n            logger.info(`${this} Skipped sending session-terminate`);\n        }\n\n        // this should result in 'onTerminated' being called by strope.jingle.js\n        this.connection.jingle.terminate(this.sid);\n    }\n\n    /**\n     *\n     * @param reasonCondition\n     * @param reasonText\n     */\n    onTerminated(reasonCondition, reasonText) {\n        // Do something with reason and reasonCondition when we start to care\n        // this.reasonCondition = reasonCondition;\n        // this.reasonText = reasonText;\n        logger.info(`${this} Session terminated`, reasonCondition, reasonText);\n\n        this._xmppListeners.forEach(removeListener => removeListener());\n        this._xmppListeners = [];\n\n        if (this._removeSenderVideoConstraintsChangeListener) {\n            this._removeSenderVideoConstraintsChangeListener();\n        }\n\n        this.close();\n    }\n\n    /**\n     * Handles XMPP connection state changes.\n     *\n     * @param {XmppConnection.Status} status - The new status.\n     */\n    onXmppStatusChanged(status) {\n        if (status === XmppConnection.Status.CONNECTED && this._cachedOldLocalSdp) {\n            logger.info(`${this} Sending SSRC update on reconnect`);\n            this.notifyMySSRCUpdate(\n                this._cachedOldLocalSdp,\n                this._cachedNewLocalSdp);\n        }\n    }\n\n    /**\n     * Parse the information from the xml sourceAddElem and translate it\n     *  into sdp lines\n     * @param {jquery xml element} sourceAddElem the source-add\n     *  element from jingle\n     * @param {SDP object} currentRemoteSdp the current remote\n     *  sdp (as of this new source-add)\n     * @returns {list} a list of SDP line strings that should\n     *  be added to the remote SDP\n     */\n    _parseSsrcInfoFromSourceAdd(sourceAddElem, currentRemoteSdp) {\n        const addSsrcInfo = [];\n        const self = this;\n\n        $(sourceAddElem).each((i1, content) => {\n            const name = $(content).attr('name');\n            let lines = '';\n\n            $(content)\n                .find('ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n                .each(function() {\n                    // eslint-disable-next-line no-invalid-this\n                    const semantics = this.getAttribute('semantics');\n                    const ssrcs\n                        = $(this) // eslint-disable-line no-invalid-this\n                            .find('>source')\n                            .map(function() {\n                                // eslint-disable-next-line no-invalid-this\n                                return this.getAttribute('ssrc');\n                            })\n                            .get();\n\n                    if (ssrcs.length) {\n                        lines += `a=ssrc-group:${semantics} ${ssrcs.join(' ')}\\r\\n`;\n                    }\n                });\n\n            // handles both >source and >description>source\n            const tmp\n                = $(content).find(\n                    'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n            /* eslint-disable no-invalid-this */\n            tmp.each(function() {\n                const ssrc = $(this).attr('ssrc');\n\n                if (currentRemoteSdp.containsSSRC(ssrc)) {\n\n                    // Do not print the warning for unified plan p2p case since ssrcs are never removed from the SDP.\n                    !(self.usesUnifiedPlan && self.isP2P)\n                        && logger.warn(`${self} Source-add request for existing SSRC: ${ssrc}`);\n\n                    return;\n                }\n\n                // eslint-disable-next-line newline-per-chained-call\n                $(this).find('>parameter').each(function() {\n                    lines += `a=ssrc:${ssrc} ${$(this).attr('name')}`;\n                    if ($(this).attr('value') && $(this).attr('value').length) {\n                        lines += `:${$(this).attr('value')}`;\n                    }\n                    lines += '\\r\\n';\n                });\n            });\n\n            /* eslint-enable no-invalid-this */\n            currentRemoteSdp.media.forEach((media, i2) => {\n                if (!SDPUtil.findLine(media, `a=mid:${name}`)) {\n                    return;\n                }\n                if (!addSsrcInfo[i2]) {\n                    addSsrcInfo[i2] = '';\n                }\n                addSsrcInfo[i2] += lines;\n            });\n        });\n\n        return addSsrcInfo;\n    }\n\n    /**\n     * Handles a Jingle source-add message for this Jingle session.\n     * @param elem An array of Jingle \"content\" elements.\n     */\n    addRemoteStream(elem) {\n        this._addOrRemoveRemoteStream(true /* add */, elem);\n    }\n\n    /**\n     * Handles a Jingle source-remove message for this Jingle session.\n     * @param elem An array of Jingle \"content\" elements.\n     */\n    removeRemoteStream(elem) {\n        this._addOrRemoveRemoteStream(false /* remove */, elem);\n    }\n\n    /**\n     * Handles the deletion of SSRCs associated with a remote user from the remote description when the user leaves.\n     *\n     * @param {string} id Endpoint id of the participant that has left the call.\n     * @returns {void}\n     */\n    removeRemoteStreamsOnLeave(id) {\n        const workFunction = finishCallback => {\n            const removeSsrcInfo = this.peerconnection.getRemoteSourceInfoByParticipant(id);\n\n            if (removeSsrcInfo.length) {\n                const oldLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n                const newRemoteSdp = this._processRemoteRemoveSource(removeSsrcInfo);\n\n                this._renegotiate(newRemoteSdp.raw)\n                    .then(() => {\n                        const newLocalSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n                        this.notifyMySSRCUpdate(oldLocalSdp, newLocalSDP);\n                        finishCallback();\n                    })\n                    .catch(err => finishCallback(err));\n            } else {\n                finishCallback();\n            }\n        };\n\n        logger.debug(`${this} Queued removeRemoteStreamsOnLeave task for participant ${id}`);\n\n        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error(`${this} removeRemoteStreamsOnLeave error:`, error);\n                } else {\n                    logger.info(`${this} removeRemoteStreamsOnLeave done!`);\n                }\n            });\n    }\n\n    /**\n     * Handles either Jingle 'source-add' or 'source-remove' message for this\n     * Jingle session.\n     * @param {boolean} isAdd <tt>true</tt> for 'source-add' or <tt>false</tt>\n     * otherwise.\n     * @param {Array<Element>} elem an array of Jingle \"content\" elements.\n     * @private\n     */\n    _addOrRemoveRemoteStream(isAdd, elem) {\n        const logPrefix = isAdd ? 'addRemoteStream' : 'removeRemoteStream';\n\n        if (isAdd) {\n            this.readSsrcInfo(elem);\n        }\n\n        const workFunction = finishedCallback => {\n            if (!this.peerconnection.localDescription\n                || !this.peerconnection.localDescription.sdp) {\n                const errMsg = `${logPrefix} - localDescription not ready yet`;\n\n                logger.error(errMsg);\n                finishedCallback(errMsg);\n\n                return;\n            }\n\n            logger.log(`${this} Processing ${logPrefix}`);\n\n            const oldLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n            const sdp = new SDP(this.peerconnection.remoteDescription.sdp);\n            const addOrRemoveSsrcInfo\n                = isAdd\n                    ? this._parseSsrcInfoFromSourceAdd(elem, sdp)\n                    : this._parseSsrcInfoFromSourceRemove(elem, sdp);\n            const newRemoteSdp\n                = isAdd\n                    ? this._processRemoteAddSource(addOrRemoveSsrcInfo)\n                    : this._processRemoteRemoveSource(addOrRemoveSsrcInfo);\n\n            // Add a workaround for a bug in Chrome (unified plan) for p2p connection. When the media direction on\n            // the transceiver goes from \"inactive\" (both users join muted) to \"recvonly\" (peer unmutes), the browser\n            // doesn't seem to create a decoder if the signaling state changes from \"have-local-offer\" to \"stable\".\n            // Therefore, initiate a responder renegotiate even if the endpoint is the offerer to workaround this issue.\n            // TODO - open a chrome bug and update the comments.\n            const remoteDescription = new RTCSessionDescription({\n                type: 'offer',\n                sdp: newRemoteSdp.raw\n            });\n            const promise = isAdd && this.usesUnifiedPlan && this.isP2P && browser.isChromiumBased()\n                ? this._responderRenegotiate(remoteDescription)\n                : this._renegotiate(newRemoteSdp.raw);\n\n            promise.then(() => {\n                const newLocalSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n                logger.log(`${this} ${logPrefix} - OK`);\n                this.notifyMySSRCUpdate(oldLocalSdp, newLocalSdp);\n                finishedCallback();\n            }, error => {\n                logger.error(`${this} ${logPrefix} failed:`, error);\n                finishedCallback(error);\n            });\n        };\n\n        logger.debug(`${this} Queued ${logPrefix} task`);\n\n        // Queue and execute\n        this.modificationQueue.push(workFunction);\n    }\n\n    /**\n     * Takes in a jingle offer iq, returns the new sdp offer\n     * @param {jquery xml element} offerIq the incoming offer\n     * @returns {SDP object} the jingle offer translated to SDP\n     */\n    _processNewJingleOfferIq(offerIq) {\n        const remoteSdp = new SDP('');\n\n        if (this.webrtcIceTcpDisable) {\n            remoteSdp.removeTcpCandidates = true;\n        }\n        if (this.webrtcIceUdpDisable) {\n            remoteSdp.removeUdpCandidates = true;\n        }\n        if (this.failICE) {\n            remoteSdp.failICE = true;\n        }\n\n        remoteSdp.fromJingle(offerIq);\n        this.readSsrcInfo($(offerIq).find('>content'));\n\n        return remoteSdp;\n    }\n\n    /**\n     * Remove the given ssrc lines from the current remote sdp\n     * @param {list} removeSsrcInfo a list of SDP line strings that\n     *  should be removed from the remote SDP\n     * @returns type {SDP Object} the new remote SDP (after removing the lines\n     *  in removeSsrcInfo\n     */\n    _processRemoteRemoveSource(removeSsrcInfo) {\n        const remoteSdp = this.usesUnifiedPlan\n            ? new SDP(this.peerconnection.peerconnection.remoteDescription.sdp)\n            : new SDP(this.peerconnection.remoteDescription.sdp);\n\n        removeSsrcInfo.forEach((lines, idx) => {\n            // eslint-disable-next-line no-param-reassign\n            lines = lines.split('\\r\\n');\n            lines.pop(); // remove empty last element;\n            if (this.usesUnifiedPlan) {\n                lines.forEach(line => {\n                    const mid = remoteSdp.media.findIndex(mLine => mLine.includes(line));\n\n                    if (mid > -1) {\n                        remoteSdp.media[mid] = remoteSdp.media[mid].replace(`${line}\\r\\n`, '');\n                        if (this.isP2P) {\n                            const mediaType = SDPUtil.parseMLine(remoteSdp.media[mid].split('\\r\\n')[0])?.media;\n                            const desiredDirection = this.peerconnection.getDesiredMediaDirection(mediaType, false);\n\n                            [ MediaDirection.SENDRECV, MediaDirection.SENDONLY ].forEach(direction => {\n                                remoteSdp.media[mid] = remoteSdp.media[mid]\n                                    .replace(`a=${direction}`, `a=${desiredDirection}`);\n                            });\n                        } else {\n                            // Jvb connections will have direction set to 'sendonly' for the remote sources.\n                            remoteSdp.media[mid] = remoteSdp.media[mid]\n                                .replace(`a=${MediaDirection.SENDONLY}`, `a=${MediaDirection.INACTIVE}`);\n                        }\n                    }\n                });\n            } else {\n                lines.forEach(line => {\n                    remoteSdp.media[idx] = remoteSdp.media[idx].replace(`${line}\\r\\n`, '');\n                });\n            }\n        });\n        remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');\n\n        return remoteSdp;\n    }\n\n    /**\n     * Add the given ssrc lines to the current remote sdp\n     * @param {list} addSsrcInfo a list of SDP line strings that\n     *  should be added to the remote SDP\n     * @returns type {SDP Object} the new remote SDP (after removing the lines\n     *  in removeSsrcInfo\n     */\n    _processRemoteAddSource(addSsrcInfo) {\n        const remoteSdp = new SDP(this.peerconnection.remoteDescription.sdp);\n\n        addSsrcInfo.forEach((lines, idx) => {\n            remoteSdp.media[idx] += lines;\n\n            // Make sure to change the direction to 'sendrecv/sendonly' only for p2p connections. For jvb connections,\n            // a new m-line is added for the new remote sources.\n            if (this.isP2P && this.usesUnifiedPlan) {\n                const mediaType = SDPUtil.parseMLine(remoteSdp.media[idx].split('\\r\\n')[0])?.media;\n                const desiredDirection = this.peerconnection.getDesiredMediaDirection(mediaType, true);\n\n                [ MediaDirection.RECVONLY, MediaDirection.INACTIVE ].forEach(direction => {\n                    remoteSdp.media[idx] = remoteSdp.media[idx]\n                        .replace(`a=${direction}`, `a=${desiredDirection}`);\n                });\n            }\n        });\n        remoteSdp.raw = remoteSdp.session + remoteSdp.media.join('');\n\n        return remoteSdp;\n    }\n\n    /**\n     * Do a new o/a flow using the existing remote description\n     * @param {string} [optionalRemoteSdp] optional, raw remote sdp\n     *  to use.  If not provided, the remote sdp from the\n     *  peerconnection will be used\n     * @returns {Promise} promise which resolves when the\n     *  o/a flow is complete with no arguments or\n     *  rejects with an error {string}\n     */\n    _renegotiate(optionalRemoteSdp) {\n        if (this.peerconnection.signalingState === 'closed') {\n            const error = new Error('Attempted to renegotiate in state closed');\n\n            this.room.eventEmitter.emit(XMPPEvents.RENEGOTIATION_FAILED, error, this);\n\n            return Promise.reject(error);\n        }\n\n        const remoteSdp\n            = optionalRemoteSdp || this.peerconnection.remoteDescription.sdp;\n\n        if (!remoteSdp) {\n            const error = new Error(`Can not renegotiate without remote description, current state: ${this.state}`);\n\n            this.room.eventEmitter.emit(XMPPEvents.RENEGOTIATION_FAILED, error, this);\n\n            return Promise.reject(error);\n        }\n\n        const remoteDescription = new RTCSessionDescription({\n            type: this.isInitiator ? 'answer' : 'offer',\n            sdp: remoteSdp\n        });\n\n        if (this.isInitiator) {\n            return this._initiatorRenegotiate(remoteDescription);\n        }\n\n        return this._responderRenegotiate(remoteDescription);\n    }\n\n    /**\n     * Renegotiate cycle implementation for the responder case.\n     * @param {object} remoteDescription the SDP object as defined by the WebRTC\n     * which will be used as remote description in the cycle.\n     * @private\n     */\n    _responderRenegotiate(remoteDescription) {\n        logger.debug(`${this} Renegotiate: setting remote description`);\n\n        return this.peerconnection.setRemoteDescription(remoteDescription)\n            .then(() => {\n                logger.debug(`${this} Renegotiate: creating answer`);\n\n                return this.peerconnection.createAnswer(this.mediaConstraints)\n                    .then(answer => {\n                        logger.debug(`${this} Renegotiate: setting local description`);\n\n                        return this.peerconnection.setLocalDescription(answer);\n                    });\n            });\n    }\n\n    /**\n     * Renegotiate cycle implementation for the initiator's case.\n     * @param {object} remoteDescription the SDP object as defined by the WebRTC\n     * which will be used as remote description in the cycle.\n     * @private\n     */\n    _initiatorRenegotiate(remoteDescription) {\n        logger.debug(`${this} Renegotiate: creating offer`);\n\n        return this.peerconnection.createOffer(this.mediaConstraints)\n            .then(offer => {\n                logger.debug(`${this} Renegotiate: setting local description`);\n\n                return this.peerconnection.setLocalDescription(offer)\n                    .then(() => {\n                        logger.debug(`${this} Renegotiate: setting remote description`);\n\n                        // eslint-disable-next-line max-len\n                        return this.peerconnection.setRemoteDescription(remoteDescription);\n                    });\n            });\n    }\n\n    /**\n     * Adds a new track to the peerconnection. This method needs to be called only when a secondary JitsiLocalTrack is\n     * being added to the peerconnection for the first time.\n     *\n     * @param {JitsiLocalTrack} localTrack track to be added to the peer connection.\n     * @returns {Promise<void>} that resolves when the track is successfully added to the peerconnection, rejected\n     * otherwise.\n     */\n    addTrack(localTrack) {\n        if (!FeatureFlags.isMultiStreamSupportEnabled()\n            || !this.usesUnifiedPlan\n            || localTrack.type !== MediaType.VIDEO) {\n            return Promise.reject(new Error('Multiple tracks of a given media type are not supported'));\n        }\n\n        const workFunction = finishedCallback => {\n            const remoteSdp = new SDP(this.peerconnection.peerconnection.remoteDescription.sdp);\n\n            // Add a new transceiver by adding a new mline in the remote description.\n            remoteSdp.addMlineForNewLocalSource(MediaType.VIDEO);\n            this._renegotiate(remoteSdp.raw)\n                .then(() => finishedCallback(), error => finishedCallback(error));\n        };\n\n        return new Promise((resolve, reject) => {\n            logger.debug(`${this} Queued renegotiation after addTrack`);\n\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        logger.error(`${this} renegotiation after addTrack error`, error);\n                        reject(error);\n                    } else {\n                        logger.debug(`${this} renegotiation after addTrack executed - OK`);\n\n                        // Replace the track on the newly generated transceiver.\n                        return this.replaceTrack(null, localTrack)\n                            .then(() => resolve())\n                            .catch(() => reject());\n                    }\n                });\n        });\n    }\n\n    /**\n     * Replaces <tt>oldTrack</tt> with <tt>newTrack</tt> and performs a single\n     * offer/answer cycle after both operations are done. Either\n     * <tt>oldTrack</tt> or <tt>newTrack</tt> can be null; replacing a valid\n     * <tt>oldTrack</tt> with a null <tt>newTrack</tt> effectively just removes\n     * <tt>oldTrack</tt>\n     * @param {JitsiLocalTrack|null} oldTrack the current track in use to be\n     * replaced\n     * @param {JitsiLocalTrack|null} newTrack the new track to use\n     * @returns {Promise} which resolves once the replacement is complete\n     *  with no arguments or rejects with an error {string}\n     */\n    replaceTrack(oldTrack, newTrack) {\n        const workFunction = finishedCallback => {\n            logger.debug(`${this} replaceTrack worker started. oldTrack = ${oldTrack}, newTrack = ${newTrack}`);\n\n            const oldLocalSdp = this.peerconnection.localDescription.sdp;\n\n            if (!this.usesUnifiedPlan) {\n                // NOTE the code below assumes that no more than 1 video track\n                // can be added to the peer connection.\n                // Transition from camera to desktop share\n                // or transition from one camera source to another.\n                if (this.peerconnection.options.capScreenshareBitrate\n                    && oldTrack && newTrack && newTrack.isVideoTrack()) {\n                    // Clearing current primary SSRC will make\n                    // the SdpConsistency generate a new one which will result\n                    // with:\n                    // 1. source-remove for the old video stream.\n                    // 2. source-add for the new video stream.\n                    this.peerconnection.clearRecvonlySsrc();\n                }\n\n                // Transition from no video to video (unmute).\n                if (!oldTrack && newTrack && newTrack.isVideoTrack()) {\n                    // Clearing current primary SSRC will make\n                    // the SdpConsistency generate a new one which will result\n                    // with:\n                    // 1. source-remove for the recvonly\n                    // 2. source-add for the new video stream\n                    this.peerconnection.clearRecvonlySsrc();\n\n                // Transition from video to no video\n                } else if (oldTrack && oldTrack.isVideoTrack() && !newTrack) {\n                    // Clearing current primary SSRC and generating the recvonly\n                    // will result in:\n                    // 1. source-remove for the old video stream\n                    // 2. source-add for the recvonly stream\n                    this.peerconnection.clearRecvonlySsrc();\n                    this.peerconnection.generateRecvonlySsrc();\n                }\n            }\n\n            this.peerconnection.replaceTrack(oldTrack, newTrack)\n                .then(shouldRenegotiate => {\n                    let promise = Promise.resolve();\n\n                    logger.debug(`${this} TPC.replaceTrack finished. shouldRenegotiate = ${\n                        shouldRenegotiate}, JingleSessionState = ${this.state}`);\n\n                    if (shouldRenegotiate\n                        && (oldTrack || newTrack)\n                        && this.state === JingleSessionState.ACTIVE) {\n                        promise = this._renegotiate().then(() => {\n                            const newLocalSDP = new SDP(this.peerconnection.localDescription.sdp);\n\n                            this.notifyMySSRCUpdate(new SDP(oldLocalSdp), newLocalSDP);\n                        });\n                    }\n\n                    return promise.then(() => {\n                        if (newTrack?.isVideoTrack()) {\n                            logger.debug(`${this} replaceTrack worker: configuring video stream`);\n\n                            // Configure the video encodings after the track is replaced.\n                            return this.peerconnection.configureSenderVideoEncodings(newTrack);\n                        }\n                    });\n                })\n                .then(() => finishedCallback(), error => finishedCallback(error));\n        };\n\n        return new Promise((resolve, reject) => {\n            logger.debug(`${this} Queued replaceTrack task. Old track = ${oldTrack}, new track = ${newTrack}`);\n\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        logger.error(`${this} Replace track error:`, error);\n                        reject(error);\n                    } else {\n                        logger.info(`${this}  Replace track done!`);\n                        resolve();\n                    }\n                });\n        });\n    }\n\n    /**\n     * Parse the information from the xml sourceRemoveElem and translate it\n     *  into sdp lines\n     * @param {jquery xml element} sourceRemoveElem the source-remove\n     *  element from jingle\n     * @param {SDP object} currentRemoteSdp the current remote\n     *  sdp (as of this new source-remove)\n     * @returns {list} a list of SDP line strings that should\n     *  be removed from the remote SDP\n     */\n    _parseSsrcInfoFromSourceRemove(sourceRemoveElem, currentRemoteSdp) {\n        const removeSsrcInfo = [];\n\n        $(sourceRemoveElem).each((i1, content) => {\n            const name = $(content).attr('name');\n            let lines = '';\n\n            $(content)\n                .find('ssrc-group[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n                .each(function() {\n                    /* eslint-disable no-invalid-this */\n                    const semantics = this.getAttribute('semantics');\n                    const ssrcs\n                        = $(this)\n                            .find('>source')\n                            .map(function() {\n                                return this.getAttribute('ssrc');\n                            })\n                            .get();\n\n                    if (ssrcs.length) {\n                        lines\n                            += `a=ssrc-group:${semantics} ${\n                                ssrcs.join(' ')}\\r\\n`;\n                    }\n\n                    /* eslint-enable no-invalid-this */\n                });\n            const ssrcs = [];\n\n            // handles both >source and >description>source versions\n            const tmp\n                = $(content).find(\n                    'source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]');\n\n            tmp.each(function() {\n                // eslint-disable-next-line no-invalid-this\n                const ssrc = $(this).attr('ssrc');\n\n                ssrcs.push(ssrc);\n            });\n            currentRemoteSdp.media.forEach((media, i2) => {\n                if (!SDPUtil.findLine(media, `a=mid:${name}`)) {\n                    return;\n                }\n                if (!removeSsrcInfo[i2]) {\n                    removeSsrcInfo[i2] = '';\n                }\n                ssrcs.forEach(ssrc => {\n                    const ssrcLines\n                        = SDPUtil.findLines(media, `a=ssrc:${ssrc}`);\n\n                    if (ssrcLines.length) {\n                        removeSsrcInfo[i2] += `${ssrcLines.join('\\r\\n')}\\r\\n`;\n                    }\n                });\n                removeSsrcInfo[i2] += lines;\n            });\n        });\n\n        return removeSsrcInfo;\n    }\n\n    /**\n     * Will print an error if there is any difference, between the SSRCs given\n     * in the <tt>oldSDP</tt> and the ones currently described in\n     * the peerconnection's local description.\n     * @param {string} operationName the operation's name which will be printed\n     * in the error message.\n     * @param {SDP} oldSDP the old local SDP which will be compared with\n     * the current one.\n     * @return {boolean} <tt>true</tt> if there was any change or <tt>false</tt>\n     * otherwise.\n     * @private\n     */\n    _verifyNoSSRCChanged(operationName, oldSDP) {\n        const currentLocalSDP\n            = new SDP(this.peerconnection.localDescription.sdp);\n        let sdpDiff = new SDPDiffer(oldSDP, currentLocalSDP);\n        const addedMedia = sdpDiff.getNewMedia();\n\n        if (Object.keys(addedMedia).length) {\n            logger.error(`${this} - some SSRC were added on ${operationName}`, addedMedia);\n\n            return false;\n        }\n\n        sdpDiff = new SDPDiffer(currentLocalSDP, oldSDP);\n        const removedMedia = sdpDiff.getNewMedia();\n\n        if (Object.keys(removedMedia).length) {\n            logger.error(`${this} - some SSRCs were removed on ${operationName}`, removedMedia);\n\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * Adds local track back to this session, as part of the unmute operation.\n     * @param {JitsiLocalTrack} track\n     * @return {Promise} a promise that will resolve once the local track is\n     * added back to this session and renegotiation succeeds. Will be rejected\n     * with a <tt>string</tt> that provides some error details in case something\n     * goes wrong.\n     */\n    addTrackAsUnmute(track) {\n        return this._addRemoveTrackAsMuteUnmute(\n            false /* add as unmute */, track)\n            .then(() => {\n                // Configure the video encodings after the track is unmuted. If the user joins the call muted and\n                // unmutes it the first time, all the parameters need to be configured.\n                if (track.isVideoTrack()) {\n                    return this.peerconnection.configureSenderVideoEncodings(track);\n                }\n            });\n    }\n\n    /**\n     * Remove local track as part of the mute operation.\n     * @param {JitsiLocalTrack} track the local track to be removed\n     * @return {Promise} a promise which will be resolved once the local track\n     * is removed from this session and the renegotiation is performed.\n     * The promise will be rejected with a <tt>string</tt> that the describes\n     * the error if anything goes wrong.\n     */\n    removeTrackAsMute(track) {\n        return this._addRemoveTrackAsMuteUnmute(\n            true /* remove as mute */, track);\n    }\n\n    /**\n     * See {@link addTrackAsUnmute} and {@link removeTrackAsMute}.\n     * @param {boolean} isMute <tt>true</tt> for \"remove as mute\" or\n     * <tt>false</tt> for \"add as unmute\".\n     * @param {JitsiLocalTrack} track the track that will be added/removed\n     * @private\n     */\n    _addRemoveTrackAsMuteUnmute(isMute, track) {\n        if (!track) {\n            return Promise.reject('invalid \"track\" argument value');\n        }\n        const operationName = isMute ? 'removeTrackMute' : 'addTrackUnmute';\n        const workFunction = finishedCallback => {\n            const tpc = this.peerconnection;\n\n            if (!tpc) {\n                finishedCallback(\n                    `Error:  tried ${operationName} track with no active peer`\n                        + 'connection');\n\n                return;\n            }\n            const oldLocalSDP = tpc.localDescription.sdp;\n            const operationPromise\n                = isMute\n                    ? tpc.removeTrackMute(track)\n                    : tpc.addTrackUnmute(track);\n\n            operationPromise\n                .then(shouldRenegotiate => {\n                    if (shouldRenegotiate && oldLocalSDP && tpc.remoteDescription.sdp) {\n                        this._renegotiate()\n                            .then(() => {\n                                // The results are ignored, as this check failure is not enough to fail the whole\n                                // operation. It will log an error inside for plan-b.\n                                !this.usesUnifiedPlan && this._verifyNoSSRCChanged(operationName, new SDP(oldLocalSDP));\n                                const newLocalSdp = tpc.localDescription.sdp;\n\n                                // Signal the ssrc if an unmute operation results in a new ssrc being generated.\n                                this.notifyMySSRCUpdate(new SDP(oldLocalSDP), new SDP(newLocalSdp));\n                                finishedCallback();\n                            });\n                    } else {\n                        finishedCallback();\n                    }\n                },\n                finishedCallback /* will be called with an error */);\n        };\n\n        logger.debug(`${this} Queued ${operationName} task`);\n\n        return new Promise((resolve, reject) => {\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        logger.error(`${this} ${operationName} failed`);\n                        reject(error);\n                    } else {\n                        logger.debug(`${this} ${operationName} done`);\n                        resolve();\n                    }\n                });\n        });\n    }\n\n    /**\n     * Resumes or suspends media transfer over the underlying peer connection.\n     * @param {boolean} audioActive <tt>true</tt> to enable audio media\n     * transfer or <tt>false</tt> to suspend audio media transmission.\n     * @param {boolean} videoActive <tt>true</tt> to enable video media\n     * transfer or <tt>false</tt> to suspend video media transmission.\n     * @return {Promise} a <tt>Promise</tt> which will resolve once\n     * the operation is done. It will be rejected with an error description as\n     * a string in case anything goes wrong.\n     */\n    setMediaTransferActive(audioActive, videoActive) {\n        if (!this.peerconnection) {\n            return Promise.reject(\n                'Can not modify transfer active state,'\n                    + ' before \"initialize\" is called');\n        }\n\n        const logAudioStr = audioActive ? 'audio active' : 'audio inactive';\n        const logVideoStr = videoActive ? 'video active' : 'video inactive';\n\n        logger.info(`${this} Queued make ${logVideoStr}, ${logAudioStr} task`);\n\n        const workFunction = finishedCallback => {\n            const isSessionActive = this.state === JingleSessionState.ACTIVE;\n\n            // Because the value is modified on the queue it's impossible to\n            // check it's final value reliably prior to submitting the task.\n            // The rule here is that the last submitted state counts.\n            // Check the values here to avoid unnecessary renegotiation cycle.\n            const audioActiveChanged\n                = this.peerconnection.setAudioTransferActive(audioActive);\n\n            if (this._localVideoActive !== videoActive) {\n                this._localVideoActive = videoActive;\n\n                // Do only for P2P - Jicofo will reply with 'bad-request'\n                // We don't want to send 'content-modify', before the initial\n                // O/A (state === JingleSessionState.ACTIVE), because that will\n                // mess up video media direction in the remote SDP.\n                // 'content-modify' when processed only affects the media\n                // direction in the local SDP. We're doing that, because setting\n                // 'inactive' on video media in remote SDP will mess up our SDP\n                // translation chain (simulcast, RTX, video mute etc.).\n                if (this.isP2P && isSessionActive) {\n                    this.sendContentModify();\n                }\n            }\n\n            const pcVideoActiveChanged\n                = this.peerconnection.setVideoTransferActive(\n                    this._localVideoActive && this._remoteVideoActive);\n\n            // Will do the sRD/sLD cycle to update SDPs and adjust the media\n            // direction\n            if (isSessionActive\n                    && (audioActiveChanged || pcVideoActiveChanged)) {\n                this._renegotiate()\n                    .then(\n                        finishedCallback,\n                        finishedCallback /* will be called with an error */);\n            } else {\n                finishedCallback();\n            }\n        };\n\n        return new Promise((resolve, reject) => {\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        logger.error(`${this} Make ${logVideoStr}, ${logAudioStr} task failed!`);\n                        reject(error);\n                    } else {\n                        logger.debug(`${this} Make ${logVideoStr}, ${logAudioStr} task done!`);\n                        resolve();\n                    }\n                });\n        });\n    }\n\n    /**\n     * Will put and execute on the queue a session modify task. Currently it\n     * only checks the senders attribute of the video content in order to figure\n     * out if the remote peer has video in the inactive state (stored locally\n     * in {@link _remoteVideoActive} - see field description for more info).\n     * @param {jQuery} jingleContents jQuery selector pointing to the jingle\n     * element of the session modify IQ.\n     * @see {@link _remoteVideoActive}\n     * @see {@link _localVideoActive}\n     */\n    modifyContents(jingleContents) {\n        const newVideoSenders\n            = JingleSessionPC.parseVideoSenders(jingleContents);\n        const newMaxFrameHeight\n            = JingleSessionPC.parseMaxFrameHeight(jingleContents);\n\n        // frame height is optional in our content-modify protocol\n        if (newMaxFrameHeight) {\n            logger.info(`${this} received remote max frame height: ${newMaxFrameHeight}`);\n            this.remoteRecvMaxFrameHeight = newMaxFrameHeight;\n            this.eventEmitter.emit(\n                MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED, this);\n        }\n\n        if (newVideoSenders === null) {\n            logger.error(\n                `${this} - failed to parse video \"senders\" attribute in`\n                    + '\"content-modify\" action');\n\n            return;\n        }\n\n        const workFunction = finishedCallback => {\n            if (this._assertNotEnded('content-modify')\n                    && this._modifyRemoteVideoActive(newVideoSenders)) {\n                // Will do the sRD/sLD cycle to update SDPs and adjust\n                // the media direction\n                this._renegotiate()\n                    .then(finishedCallback, finishedCallback /* (error) */);\n            } else {\n                finishedCallback();\n            }\n        };\n\n        logger.debug(`${this} queued \"content-modify\" task(video senders=\"${newVideoSenders}\")`);\n\n        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error(`${this} \"content-modify\" failed`, error);\n                } else {\n                    logger.debug(`${this} \"content-modify\" task(video senders=\"${newVideoSenders}\") done`);\n                }\n            });\n    }\n\n    /**\n     * Processes new value of remote video \"senders\" Jingle attribute and tries\n     * to apply it for {@link _remoteVideoActive}.\n     * @param {string} remoteVideoSenders the value of \"senders\" attribute of\n     * Jingle video content element advertised by remote peer.\n     * @return {boolean} <tt>true</tt> if the change affected state of\n     * the underlying peerconnection and renegotiation is required for\n     * the changes to take effect.\n     * @private\n     */\n    _modifyRemoteVideoActive(remoteVideoSenders) {\n        const isRemoteVideoActive\n            = remoteVideoSenders === 'both'\n                || (remoteVideoSenders === 'initiator' && this.isInitiator)\n                || (remoteVideoSenders === 'responder' && !this.isInitiator);\n\n        if (isRemoteVideoActive !== this._remoteVideoActive) {\n            logger.debug(`${this} new remote video active: ${isRemoteVideoActive}`);\n            this._remoteVideoActive = isRemoteVideoActive;\n        }\n\n        return this.peerconnection.setVideoTransferActive(\n            this._localVideoActive && this._remoteVideoActive);\n    }\n\n    /**\n     * Figures out added/removed ssrcs and send update IQs.\n     * @param oldSDP SDP object for old description.\n     * @param newSDP SDP object for new description.\n     */\n    notifyMySSRCUpdate(oldSDP, newSDP) {\n        if (this.state !== JingleSessionState.ACTIVE) {\n            logger.warn(`${this} Skipping SSRC update in '${this.state} ' state.`);\n\n            return;\n        }\n\n        if (!this.connection.connected) {\n            // The goal is to compare the oldest SDP with the latest one upon reconnect\n            if (!this._cachedOldLocalSdp) {\n                this._cachedOldLocalSdp = oldSDP;\n            }\n            this._cachedNewLocalSdp = newSDP;\n            logger.warn(`${this} Not sending SSRC update while the signaling is disconnected`);\n\n            return;\n        }\n\n        this._cachedOldLocalSdp = undefined;\n        this._cachedNewLocalSdp = undefined;\n\n        const getSignaledSourceInfo = sdpDiffer => {\n            const newMedia = sdpDiffer.getNewMedia();\n            let ssrcs = [];\n            let mediaType = null;\n\n            // It is assumed that sources are signaled one at a time.\n            Object.keys(newMedia).forEach(mediaIndex => {\n                const signaledSsrcs = Object.keys(newMedia[mediaIndex].ssrcs);\n\n                mediaType = newMedia[mediaIndex].mid;\n                if (signaledSsrcs?.length) {\n                    ssrcs = ssrcs.concat(signaledSsrcs);\n                }\n            });\n\n            return {\n                mediaType,\n                ssrcs\n            };\n        };\n\n        // send source-remove IQ.\n        let sdpDiffer = new SDPDiffer(newSDP, oldSDP);\n        const remove = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', {\n                xmlns: 'urn:xmpp:jingle:1',\n                action: 'source-remove',\n                initiator: this.initiatorJid,\n                sid: this.sid\n            }\n            );\n        const removedAnySSRCs = sdpDiffer.toJingle(remove);\n\n        // context a common object for one run of ssrc update (source-add and source-remove) so we can match them if we\n        // need to\n        const ctx = {};\n\n        if (removedAnySSRCs) {\n            const sourceInfo = getSignaledSourceInfo(sdpDiffer);\n\n            // Log only the SSRCs instead of the full IQ.\n            logger.info(`${this} Sending source-remove for ${sourceInfo.mediaType} ssrcs=${sourceInfo.ssrcs}`);\n            this.connection.sendIQ(\n                remove,\n                () => {\n                    this.room.eventEmitter.emit(XMPPEvents.SOURCE_REMOVE, this, ctx);\n                },\n                this.newJingleErrorHandler(remove, error => {\n                    this.room.eventEmitter.emit(XMPPEvents.SOURCE_REMOVE_ERROR, this, error, ctx);\n                }),\n                IQ_TIMEOUT);\n        }\n\n        // send source-add IQ.\n        sdpDiffer = new SDPDiffer(oldSDP, newSDP);\n        const add = $iq({ to: this.remoteJid,\n            type: 'set' })\n            .c('jingle', {\n                xmlns: 'urn:xmpp:jingle:1',\n                action: 'source-add',\n                initiator: this.initiatorJid,\n                sid: this.sid\n            }\n            );\n\n        const containsNewSSRCs = sdpDiffer.toJingle(add);\n\n        if (containsNewSSRCs) {\n            const sourceInfo = getSignaledSourceInfo(sdpDiffer);\n\n            // Log only the SSRCs instead of the full IQ.\n            logger.info(`${this} Sending source-add for ${sourceInfo.mediaType} ssrcs=${sourceInfo.ssrcs}`);\n            this.connection.sendIQ(\n                add,\n                () => {\n                    this.room.eventEmitter.emit(XMPPEvents.SOURCE_ADD, this, ctx);\n                },\n                this.newJingleErrorHandler(add, error => {\n                    this.room.eventEmitter.emit(XMPPEvents.SOURCE_ADD_ERROR, this, error, sourceInfo.mediaType, ctx);\n                }),\n                IQ_TIMEOUT);\n        }\n    }\n\n    /**\n     * Method returns function(errorResponse) which is a callback to be passed\n     * to Strophe connection.sendIQ method. An 'error' structure is created that\n     * is passed as 1st argument to given <tt>failureCb</tt>. The format of this\n     * structure is as follows:\n     * {\n     *  code: {XMPP error response code}\n     *  reason: {the name of XMPP error reason element or 'timeout' if the\n      *          request has timed out within <tt>IQ_TIMEOUT</tt> milliseconds}\n     *  source: {request.tree() that provides original request}\n     *  session: {this JingleSessionPC.toString()}\n     * }\n     * @param request Strophe IQ instance which is the request to be dumped into\n     *        the error structure\n     * @param failureCb function(error) called when error response was returned\n     *        or when a timeout has occurred.\n     * @returns {function(this:JingleSessionPC)}\n     */\n    newJingleErrorHandler(request, failureCb) {\n        return errResponse => {\n\n            const error = {};\n\n            // Get XMPP error code and condition(reason)\n            const errorElSel = $(errResponse).find('error');\n\n            if (errorElSel.length) {\n                error.code = errorElSel.attr('code');\n                const errorReasonSel = $(errResponse).find('error :first');\n\n                if (errorReasonSel.length) {\n                    error.reason = errorReasonSel[0].tagName;\n                }\n\n                const errorMsgSel = errorElSel.find('>text');\n\n                if (errorMsgSel.length) {\n                    error.msg = errorMsgSel.text();\n                }\n            }\n\n            if (!errResponse) {\n                error.reason = 'timeout';\n            }\n\n            error.session = this.toString();\n\n            if (failureCb) {\n                failureCb(error);\n            } else if (this.state === JingleSessionState.ENDED\n                        && error.reason === 'item-not-found') {\n                // When remote peer decides to terminate the session, but it\n                // still have few messages on the queue for processing,\n                // it will first send us 'session-terminate' (we enter ENDED)\n                // and then follow with 'item-not-found' for the queued requests\n                // We don't want to have that logged on error level.\n                logger.debug(`${this} Jingle error: ${JSON.stringify(error)}`);\n            } else {\n                GlobalOnErrorHandler.callErrorHandler(\n                    new Error(\n                        `Jingle error: ${JSON.stringify(error)}`));\n            }\n        };\n    }\n\n    /**\n     * Returns the ice connection state for the peer connection.\n     * @returns the ice connection state for the peer connection.\n     */\n    getIceConnectionState() {\n        return this.peerconnection.getConnectionState();\n    }\n\n    /**\n     * Closes the peerconnection.\n     */\n    close() {\n        this.state = JingleSessionState.ENDED;\n        this.establishmentDuration = undefined;\n\n        if (this.peerconnection) {\n            this.peerconnection.onicecandidate = null;\n            this.peerconnection.oniceconnectionstatechange = null;\n            this.peerconnection.onnegotiationneeded = null;\n            this.peerconnection.onsignalingstatechange = null;\n        }\n\n        logger.debug(`${this} Clearing modificationQueue`);\n\n        // Remove any pending tasks from the queue\n        this.modificationQueue.clear();\n\n        logger.debug(`${this} Queued PC close task`);\n        this.modificationQueue.push(finishCallback => {\n            // do not try to close if already closed.\n            this.peerconnection && this.peerconnection.close();\n            finishCallback();\n            logger.debug(`${this} PC close task done!`);\n        });\n\n        logger.debug(`${this} Shutdown modificationQueue!`);\n\n        // No more tasks can go in after the close task\n        this.modificationQueue.shutdown();\n    }\n\n    /**\n     * Converts to string with minor summary.\n     * @return {string}\n     */\n    toString() {\n        return `JingleSessionPC[session=${this.isP2P ? 'P2P' : 'JVB'},initiator=${this.isInitiator},sid=${this.sid}]`;\n    }\n\n    /**\n     * If the A/B test for suspend video is disabled according to the room's\n     * configuration, returns undefined. Otherwise returns a boolean which\n     * indicates whether the suspend video option should be enabled or disabled.\n     * @param {JingleSessionPCOptions} options - The config options.\n     */\n    _abtestSuspendVideoEnabled({ abTesting }) {\n        if (!abTesting || !abTesting.enableSuspendVideoTest) {\n            return;\n        }\n\n        // We want the two participants in a P2P call to agree on the value of\n        // the \"suspend\" option. We use the JID of the initiator, because it is\n        // both randomly selected and agreed upon by both participants.\n        const jid = this._getInitiatorJid();\n\n        return integerHash(jid) % 2 === 0;\n    }\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport {\n    ACTION_JINGLE_TR_RECEIVED,\n    ACTION_JINGLE_TR_SUCCESS,\n    createJingleEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport RandomUtil from '../util/RandomUtil';\n\nimport ConnectionPlugin from './ConnectionPlugin';\nimport { expandSourcesFromJson } from './JingleHelperFunctions';\nimport JingleSessionPC from './JingleSessionPC';\n\nconst logger = getLogger(__filename);\n\n// XXX Strophe is build around the idea of chaining function calls so allow long\n// function call chains.\n/* eslint-disable newline-per-chained-call */\n\n/**\n * Parses the transport XML element and returns the list of ICE candidates formatted as text.\n *\n * @param {*} transport Transport XML element extracted from the IQ.\n * @returns {Array<string>}\n */\nfunction _parseIceCandidates(transport) {\n    const candidates = $(transport).find('>candidate');\n    const parseCandidates = [];\n\n    // Extract the candidate information from the IQ.\n    candidates.each((_, candidate) => {\n        const attributes = candidate.attributes;\n        const candidateAttrs = [];\n\n        for (let i = 0; i < attributes.length; i++) {\n            const attr = attributes[i];\n\n            candidateAttrs.push(`${attr.name}: ${attr.value}`);\n        }\n        parseCandidates.push(candidateAttrs.join(' '));\n    });\n\n    return parseCandidates;\n}\n\n/**\n *\n */\nexport default class JingleConnectionPlugin extends ConnectionPlugin {\n    /**\n     * Creates new <tt>JingleConnectionPlugin</tt>\n     * @param {XMPP} xmpp\n     * @param {EventEmitter} eventEmitter\n     * @param {Object} iceConfig an object that holds the iceConfig to be passed\n     * to the p2p and the jvb <tt>PeerConnection</tt>.\n     */\n    constructor(xmpp, eventEmitter, iceConfig) {\n        super();\n        this.xmpp = xmpp;\n        this.eventEmitter = eventEmitter;\n        this.sessions = {};\n        this.jvbIceConfig = iceConfig.jvb;\n        this.p2pIceConfig = iceConfig.p2p;\n        this.mediaConstraints = {\n            offerToReceiveAudio: true,\n            offerToReceiveVideo: true\n        };\n    }\n\n    /**\n     *\n     * @param connection\n     */\n    init(connection) {\n        super.init(connection);\n        this.connection.addHandler(this.onJingle.bind(this),\n            'urn:xmpp:jingle:1', 'iq', 'set', null, null);\n    }\n\n    /**\n     *\n     * @param iq\n     */\n    onJingle(iq) {\n        const sid = $(iq).find('jingle').attr('sid');\n        const action = $(iq).find('jingle').attr('action');\n        const fromJid = iq.getAttribute('from');\n\n        // send ack first\n        const ack = $iq({ type: 'result',\n            to: fromJid,\n            id: iq.getAttribute('id')\n        });\n\n        let sess = this.sessions[sid];\n\n        if (action !== 'session-initiate') {\n            if (!sess) {\n                ack.attrs({ type: 'error' });\n                ack.c('error', { type: 'cancel' })\n                    .c('item-not-found', {\n                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n                    })\n                    .up()\n                    .c('unknown-session', {\n                        xmlns: 'urn:xmpp:jingle:errors:1'\n                    });\n                logger.warn(`invalid session id: ${sid}`);\n                logger.debug(iq);\n                this.connection.send(ack);\n\n                return true;\n            }\n\n            // local jid is not checked\n            if (fromJid !== sess.remoteJid) {\n                logger.warn(\n                    'jid mismatch for session id', sid, sess.remoteJid, iq);\n                ack.attrs({ type: 'error' });\n                ack.c('error', { type: 'cancel' })\n                    .c('item-not-found', {\n                        xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n                    })\n                    .up()\n                    .c('unknown-session', {\n                        xmlns: 'urn:xmpp:jingle:errors:1'\n                    });\n                this.connection.send(ack);\n\n                return true;\n            }\n        } else if (sess !== undefined) {\n            // Existing session with same session id. This might be out-of-order\n            // if the sess.remoteJid is the same as from.\n            ack.attrs({ type: 'error' });\n            ack.c('error', { type: 'cancel' })\n                .c('service-unavailable', {\n                    xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas'\n                })\n                .up();\n            logger.warn('duplicate session id', sid, iq);\n            this.connection.send(ack);\n\n            return true;\n        }\n        const now = window.performance.now();\n\n        // FIXME that should work most of the time, but we'd have to\n        // think how secure it is to assume that user with \"focus\"\n        // nickname is Jicofo.\n        const isP2P = Strophe.getResourceFromJid(fromJid) !== 'focus';\n\n        // see http://xmpp.org/extensions/xep-0166.html#concepts-session\n\n        const jsonMessages = $(iq).find('jingle>json-message');\n\n        if (jsonMessages?.length) {\n            let audioVideoSsrcs;\n\n            logger.info(`Found a JSON-encoded element in ${action}, translating to standard Jingle.`);\n            for (let i = 0; i < jsonMessages.length; i++) {\n                // Currently there is always a single json-message in the IQ with the source information.\n                audioVideoSsrcs = expandSourcesFromJson(iq, jsonMessages[i]);\n            }\n\n            if (audioVideoSsrcs?.size) {\n                const logMessage = [];\n\n                for (const endpoint of audioVideoSsrcs.keys()) {\n                    logMessage.push(`${endpoint}:[${audioVideoSsrcs.get(endpoint)}]`);\n                }\n                logger.debug(`Received ${action} from ${fromJid} with sources=${logMessage.join(', ')}`);\n            }\n\n            // TODO: is there a way to remove the json-message elements once we've extracted the information?\n            // removeChild doesn't seem to work.\n        }\n\n        switch (action) {\n        case 'session-initiate': {\n            logger.log('(TIME) received session-initiate:\\t', now);\n            const startMuted = $(iq).find('jingle>startmuted');\n\n            isP2P && logger.debug(`Received ${action} from ${fromJid}`);\n            if (startMuted?.length) {\n                const audioMuted = startMuted.attr(MediaType.AUDIO);\n                const videoMuted = startMuted.attr(MediaType.VIDEO);\n\n                this.eventEmitter.emit(\n                    XMPPEvents.START_MUTED_FROM_FOCUS,\n                    audioMuted === 'true',\n                    videoMuted === 'true');\n            }\n            const pcConfig = isP2P ? this.p2pIceConfig : this.jvbIceConfig;\n\n            sess\n                = new JingleSessionPC(\n                    $(iq).find('jingle').attr('sid'),\n                    $(iq).attr('to'),\n                    fromJid,\n                    this.connection,\n                    this.mediaConstraints,\n\n                    // Makes a copy in order to prevent exception thrown on RN when either this.p2pIceConfig or\n                    // this.jvbIceConfig is modified and there's a PeerConnection instance holding a reference\n                    JSON.parse(JSON.stringify(pcConfig)),\n                    isP2P,\n                    /* initiator */ false);\n\n            this.sessions[sess.sid] = sess;\n            this.eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess, $(iq).find('>jingle'), now);\n            break;\n        }\n        case 'session-accept': {\n            const ssrcs = [];\n            const contents = $(iq).find('jingle>content');\n\n            // Extract the SSRCs from the session-accept received from a p2p peer.\n            for (const content of contents) {\n                const ssrc = $(content).find('description').attr('ssrc');\n\n                ssrc && ssrcs.push(ssrc);\n            }\n            logger.debug(`Received ${action} from ${fromJid} with ssrcs=${ssrcs}`);\n            this.eventEmitter.emit(XMPPEvents.CALL_ACCEPTED, sess, $(iq).find('>jingle'));\n            break;\n        }\n        case 'content-modify': {\n            const height = $(iq).find('jingle>content[name=\"video\"]>max-frame-height');\n\n            logger.debug(`Received ${action} from ${fromJid} with a max-frame-height=${height?.text()}`);\n            sess.modifyContents($(iq).find('>jingle'));\n            break;\n        }\n        case 'transport-info': {\n            const candidates = _parseIceCandidates($(iq).find('jingle>content>transport'));\n\n            logger.debug(`Received ${action} from ${fromJid} for candidates=${candidates.join(', ')}`);\n            this.eventEmitter.emit(XMPPEvents.TRANSPORT_INFO, sess, $(iq).find('>jingle'));\n            break;\n        }\n        case 'session-terminate': {\n            logger.log('terminating...', sess.sid);\n            let reasonCondition = null;\n            let reasonText = null;\n\n            if ($(iq).find('>jingle>reason').length) {\n                reasonCondition\n                    = $(iq).find('>jingle>reason>:first')[0].tagName;\n                reasonText = $(iq).find('>jingle>reason>text').text();\n            }\n            logger.debug(`Received ${action} from ${fromJid} disconnect reason=${reasonText}`);\n            this.terminate(sess.sid, reasonCondition, reasonText);\n            this.eventEmitter.emit(XMPPEvents.CALL_ENDED, sess, reasonCondition, reasonText);\n            break;\n        }\n        case 'transport-replace': {\n            logger.info('(TIME) Start transport replace:\\t', now);\n            const transport = $(iq).find('jingle>content>transport');\n            const candidates = _parseIceCandidates(transport);\n            const iceUfrag = $(transport).attr('ufrag');\n            const icePwd = $(transport).attr('pwd');\n            const dtlsFingerprint = $(transport).find('>fingerprint')?.text();\n\n            logger.debug(`Received ${action} from ${fromJid} with iceUfrag=${iceUfrag},`\n            + ` icePwd=${icePwd}, DTLS fingerprint=${dtlsFingerprint}, candidates=${candidates.join(', ')}`);\n\n            Statistics.sendAnalytics(createJingleEvent(\n                ACTION_JINGLE_TR_RECEIVED,\n                {\n                    p2p: isP2P,\n                    value: now\n                }));\n\n            sess.replaceTransport($(iq).find('>jingle'), () => {\n                const successTime = window.performance.now();\n\n                logger.info('(TIME) Transport replace success:\\t', successTime);\n                Statistics.sendAnalytics(createJingleEvent(\n                    ACTION_JINGLE_TR_SUCCESS,\n                    {\n                        p2p: isP2P,\n                        value: successTime\n                    }));\n            }, error => {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error('Transport replace failed', error);\n                sess.sendTransportReject();\n            });\n            break;\n        }\n        case 'source-add':\n            sess.addRemoteStream($(iq).find('>jingle>content'));\n            break;\n        case 'source-remove':\n            sess.removeRemoteStream($(iq).find('>jingle>content'));\n            break;\n        default:\n            logger.warn('jingle action not implemented', action);\n            ack.attrs({ type: 'error' });\n            ack.c('error', { type: 'cancel' })\n                .c('bad-request',\n                    { xmlns: 'urn:ietf:params:xml:ns:xmpp-stanzas' })\n                .up();\n            break;\n        }\n        this.connection.send(ack);\n\n        return true;\n    }\n\n    /**\n     * Creates new <tt>JingleSessionPC</tt> meant to be used in a direct P2P\n     * connection, configured as 'initiator'.\n     * @param {string} me our JID\n     * @param {string} peer remote participant's JID\n     * @return {JingleSessionPC}\n     */\n    newP2PJingleSession(me, peer) {\n        const sess\n            = new JingleSessionPC(\n                RandomUtil.randomHexString(12),\n                me,\n                peer,\n                this.connection,\n                this.mediaConstraints,\n                this.p2pIceConfig,\n                /* P2P */ true,\n                /* initiator */ true);\n\n        this.sessions[sess.sid] = sess;\n\n        return sess;\n    }\n\n    /**\n     *\n     * @param sid\n     * @param reasonCondition\n     * @param reasonText\n     */\n    terminate(sid, reasonCondition, reasonText) {\n        if (this.sessions.hasOwnProperty(sid)) {\n            if (this.sessions[sid].state !== 'ended') {\n                this.sessions[sid].onTerminated(reasonCondition, reasonText);\n            }\n            delete this.sessions[sid];\n        }\n    }\n\n    /**\n     *\n     */\n    getStunAndTurnCredentials() {\n        // get stun and turn configuration from server via xep-0215\n        // uses time-limited credentials as described in\n        // http://tools.ietf.org/html/draft-uberti-behave-turn-rest-00\n        //\n        // See https://modules.prosody.im/mod_turncredentials.html\n        // for a prosody module which implements this.\n        // Or the new implementation https://modules.prosody.im/mod_external_services which will be in prosody 0.12\n        //\n        // Currently, this doesn't work with updateIce and therefore credentials\n        // with a long validity have to be fetched before creating the\n        // peerconnection.\n        // TODO: implement refresh via updateIce as described in\n        //      https://code.google.com/p/webrtc/issues/detail?id=1650\n        this.connection.sendIQ(\n            $iq({ type: 'get',\n                to: this.xmpp.options.hosts.domain })\n                .c('services', { xmlns: 'urn:xmpp:extdisco:2' }),\n            v2Res => this.onReceiveStunAndTurnCredentials(v2Res),\n            () => {\n                logger.warn('getting turn credentials with extdisco:2 failed, trying extdisco:1');\n                this.connection.sendIQ(\n                    $iq({ type: 'get',\n                        to: this.xmpp.options.hosts.domain })\n                        .c('services', { xmlns: 'urn:xmpp:extdisco:1' }),\n                    v1Res => this.onReceiveStunAndTurnCredentials(v1Res),\n                    () => {\n                        logger.warn('getting turn credentials failed');\n                        logger.warn('is mod_turncredentials or similar installed and configured?');\n                    }\n                );\n            });\n    }\n\n    /**\n     * Parses response when querying for services using urn:xmpp:extdisco:1 or urn:xmpp:extdisco:2.\n     * Stores results in jvbIceConfig and p2pIceConfig.\n     * @param res The response iq.\n     * @return {boolean} Whether something was processed from the supplied message.\n     */\n    onReceiveStunAndTurnCredentials(res) {\n        const iceservers = [];\n\n        $(res).find('>services>service').each((idx, el) => {\n            // eslint-disable-next-line no-param-reassign\n            el = $(el);\n            const dict = {};\n            const type = el.attr('type');\n\n            switch (type) {\n            case 'stun':\n                dict.urls = `stun:${el.attr('host')}`;\n                if (el.attr('port')) {\n                    dict.urls += `:${el.attr('port')}`;\n                }\n                iceservers.push(dict);\n                break;\n            case 'turn':\n            case 'turns': {\n                dict.urls = `${type}:`;\n                dict.username = el.attr('username');\n                dict.urls += el.attr('host');\n                const port = el.attr('port');\n\n                if (port) {\n                    dict.urls += `:${el.attr('port')}`;\n                }\n                const transport = el.attr('transport');\n\n                if (transport && transport !== 'udp') {\n                    dict.urls += `?transport=${transport}`;\n                }\n\n                dict.credential = el.attr('password')\n                        || dict.credential;\n                iceservers.push(dict);\n                break;\n            }\n            }\n        });\n\n        const options = this.xmpp.options;\n\n        // Shuffle ICEServers for loadbalancing\n        for (let i = iceservers.length - 1; i > 0; i--) {\n            const j = Math.floor(Math.random() * (i + 1));\n            const temp = iceservers[i];\n\n            iceservers[i] = iceservers[j];\n            iceservers[j] = temp;\n        }\n\n        let filter;\n\n        if (options.useTurnUdp) {\n            filter = s => s.urls.startsWith('turn');\n        } else {\n            // By default we filter out STUN and TURN/UDP and leave only TURN/TCP.\n            filter = s => s.urls.startsWith('turn') && (s.urls.indexOf('transport=tcp') >= 0);\n        }\n\n        this.jvbIceConfig.iceServers = iceservers.filter(filter);\n        this.p2pIceConfig.iceServers = iceservers;\n\n        return iceservers.length > 0;\n    }\n\n    /**\n     * Returns the data saved in 'updateLog' in a format to be logged.\n     */\n    getLog() {\n        const data = {};\n\n        Object.keys(this.sessions).forEach(sid => {\n            const session = this.sessions[sid];\n            const pc = session.peerconnection;\n\n            if (pc && pc.updateLog) {\n                // FIXME: should probably be a .dump call\n                data[`jingle_${sid}`] = {\n                    updateLog: pc.updateLog,\n                    stats: pc.stats,\n                    url: window.location.href\n                };\n            }\n        });\n\n        return data;\n    }\n}\n\n/* eslint-enable newline-per-chained-call */\n","import { Strophe } from 'strophe.js';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\n/**\n *  Logs raw stanzas and makes them available for download as JSON\n */\nclass StropheLogger extends ConnectionPlugin {\n    /**\n     *\n     */\n    constructor() {\n        super();\n        this.log = [];\n    }\n\n    /**\n     *\n     * @param connection\n     */\n    init(connection) {\n        super.init(connection);\n        this.connection.rawInput = this.logIncoming.bind(this);\n        this.connection.rawOutput = this.logOutgoing.bind(this);\n    }\n\n    /**\n     *\n     * @param stanza\n     */\n    logIncoming(stanza) {\n        this.log.push([ new Date().getTime(), 'incoming', stanza ]);\n    }\n\n    /**\n     *\n     * @param stanza\n     */\n    logOutgoing(stanza) {\n        this.log.push([ new Date().getTime(), 'outgoing', stanza ]);\n    }\n}\n\n/**\n *\n */\nexport default function() {\n    Strophe.addConnectionPlugin('logger', new StropheLogger());\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport ConnectionPlugin from './ConnectionPlugin';\n\nconst logger = getLogger(__filename);\n\nconst RAYO_XMLNS = 'urn:xmpp:rayo:1';\n\n/**\n *\n */\nexport default class RayoConnectionPlugin extends ConnectionPlugin {\n    /**\n     *\n     * @param connection\n     */\n    init(connection) {\n        super.init(connection);\n\n        this.connection.addHandler(\n            this.onRayo.bind(this), RAYO_XMLNS, 'iq', 'set', null, null);\n    }\n\n    /**\n     *\n     * @param iq\n     */\n    onRayo(iq) {\n        logger.info('Rayo IQ', iq);\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     *\n     * @param to\n     * @param from\n     * @param roomName\n     * @param roomPass\n     * @param focusMucJid\n     */\n    dial(to, from, roomName, roomPass, focusMucJid) {\n        return new Promise((resolve, reject) => {\n            if (!focusMucJid) {\n                reject(new Error('Internal error!'));\n\n                return;\n            }\n            const req = $iq({\n                type: 'set',\n                to: focusMucJid\n            });\n\n            req.c('dial', {\n                xmlns: RAYO_XMLNS,\n                to,\n                from\n            });\n            req.c('header', {\n                name: 'JvbRoomName',\n                value: roomName\n            }).up();\n\n            if (roomPass && roomPass.length) {\n                req.c('header', {\n                    name: 'JvbRoomPassword',\n                    value: roomPass\n                }).up();\n            }\n\n            this.connection.sendIQ(\n                req,\n                result => {\n                    logger.info('Dial result ', result);\n\n                    // eslint-disable-next-line newline-per-chained-call\n                    const resource = $(result).find('ref').attr('uri');\n\n                    this.callResource = resource.substr('xmpp:'.length);\n                    logger.info(`Received call resource: ${this.callResource}`);\n                    resolve();\n                },\n                error => {\n                    logger.info('Dial error ', error);\n                    reject(error);\n                });\n        });\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     *\n     */\n    hangup() {\n        return new Promise((resolve, reject) => {\n            if (!this.callResource) {\n                reject(new Error('No call in progress'));\n                logger.warn('No call in progress');\n\n                return;\n            }\n\n            const req = $iq({\n                type: 'set',\n                to: this.callResource\n            });\n\n            req.c('hangup', {\n                xmlns: RAYO_XMLNS\n            });\n\n            this.connection.sendIQ(req, result => {\n                logger.info('Hangup result ', result);\n                this.callResource = null;\n                resolve();\n            }, error => {\n                logger.info('Hangup error ', error);\n                this.callResource = null;\n                reject(new Error('Hangup error '));\n            });\n        });\n    }\n}\n","/**\n * Strophe logger implementation. Logs from level WARN and above.\n */\nimport { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = getLogger(__filename);\n\n/**\n * This is the last HTTP error status captured from Strophe debug logs.\n * The purpose of storing it is to distinguish between the network and\n * infrastructure reason for connection being dropped (see connectionHandler in\n * xmpp.js). The value will be cleared (-1) if the subsequent request succeeds\n * which means that the failure could be transient.\n *\n * FIXME in the latest Strophe (not released on npm) there is API to handle\n * particular HTTP errors, but there is no way to learn if the subsequent\n * request succeeded in order to tell if the error was one time incident or if\n * it was the reason for dropping the connection by Strophe (the connection is\n * dropped after 5 subsequent failures). Ideally Strophe should provide more\n * details about the reason on why the connection stopped.\n *\n * @type {number}\n */\nlet lastErrorStatus = -1;\n\n/**\n * A regular expression used to catch Strophe's log message indicating that the\n * last BOSH request was successful. When there is such message seen the\n * {@link lastErrorStatus} will be set back to '-1'.\n * @type {RegExp}\n */\nconst resetLastErrorStatusRegExpr = /request id \\d+.\\d+ got 200/;\n\n/**\n * A regular expression used to capture the current value of the BOSH request\n * error status (HTTP error code or '0' or something else).\n * @type {RegExp}\n */\nconst lastErrorStatusRegExpr\n    = /request errored, status: (\\d+), number of errors: \\d+/;\n\n/**\n *\n */\nexport default function() {\n\n    Strophe.log = function(level, msg) {\n        // Our global handler reports uncaught errors to the stats which may\n        // interpret those as partial call failure.\n        // Strophe log entry about secondary request timeout does not mean that\n        // it's a final failure(the request will be restarted), so we lower it's\n        // level here to a warning.\n        logger.trace('Strophe', level, msg);\n        if (typeof msg === 'string'\n                && msg.indexOf('Request ') !== -1\n                && msg.indexOf('timed out (secondary), restarting') !== -1) {\n            // eslint-disable-next-line no-param-reassign\n            level = Strophe.LogLevel.WARN;\n        }\n\n        /* eslint-disable no-case-declarations */\n        switch (level) {\n        case Strophe.LogLevel.DEBUG:\n            // The log message which reports successful status is logged on\n            // Strophe's DEBUG level.\n            if (lastErrorStatus !== -1\n                    && resetLastErrorStatusRegExpr.test(msg)) {\n                logger.debug('Reset lastErrorStatus');\n                lastErrorStatus = -1;\n            }\n            break;\n        case Strophe.LogLevel.WARN:\n            logger.warn(`Strophe: ${msg}`);\n            const errStatusCapture = lastErrorStatusRegExpr.exec(msg);\n\n            if (errStatusCapture && errStatusCapture.length === 2) {\n                lastErrorStatus = parseInt(errStatusCapture[1], 10);\n                logger.debug(`lastErrorStatus set to: ${lastErrorStatus}`);\n            }\n            break;\n        case Strophe.LogLevel.ERROR:\n        case Strophe.LogLevel.FATAL:\n            // eslint-disable-next-line no-param-reassign\n            msg = `Strophe: ${msg}`;\n            GlobalOnErrorHandler.callErrorHandler(new Error(msg));\n            logger.error(msg);\n            break;\n        }\n\n        /* eslint-enable no-case-declarations */\n    };\n\n    /**\n     * Returns error status (HTTP error code) of the last BOSH request.\n     *\n     * @return {number} HTTP error code, '0' for unknown or \"god knows what\"\n     * (this is a hack).\n     */\n    Strophe.getLastErrorStatus = function() {\n        return lastErrorStatus;\n    };\n\n    Strophe.getStatusString = function(status) {\n        switch (status) {\n        case Strophe.Status.BINDREQUIRED:\n            return 'BINDREQUIRED';\n        case Strophe.Status.ERROR:\n            return 'ERROR';\n        case Strophe.Status.CONNECTING:\n            return 'CONNECTING';\n        case Strophe.Status.CONNFAIL:\n            return 'CONNFAIL';\n        case Strophe.Status.AUTHENTICATING:\n            return 'AUTHENTICATING';\n        case Strophe.Status.AUTHFAIL:\n            return 'AUTHFAIL';\n        case Strophe.Status.CONNECTED:\n            return 'CONNECTED';\n        case Strophe.Status.DISCONNECTED:\n            return 'DISCONNECTED';\n        case Strophe.Status.DISCONNECTING:\n            return 'DISCONNECTING';\n        case Strophe.Status.ATTACHED:\n            return 'ATTACHED';\n        default:\n            return 'unknown';\n        }\n    };\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $msg, Strophe } from 'strophe.js';\nimport 'strophejs-plugin-disco';\n\nimport * as JitsiConnectionErrors from '../../JitsiConnectionErrors';\nimport * as JitsiConnectionEvents from '../../JitsiConnectionEvents';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport browser from '../browser';\nimport { E2EEncryption } from '../e2ee/E2EEncryption';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport RandomUtil from '../util/RandomUtil';\n\nimport Caps, { parseDiscoInfo } from './Caps';\nimport XmppConnection from './XmppConnection';\nimport MucConnectionPlugin from './strophe.emuc';\nimport JingleConnectionPlugin from './strophe.jingle';\nimport initStropheLogger from './strophe.logger';\nimport RayoConnectionPlugin from './strophe.rayo';\nimport initStropheUtil from './strophe.util';\n\nconst logger = getLogger(__filename);\n\n/**\n* Regex to extract exact error message on jwt error.\n*/\nconst FAILURE_REGEX = /<failure.*><not-allowed\\/><text>(.*)<\\/text><\\/failure>/gi;\n\n/**\n * Creates XMPP connection.\n *\n * @param {Object} options\n * @param {string} [options.token] - JWT token used for authentication(JWT authentication module must be enabled in\n * Prosody).\n * @param {string} options.serviceUrl - The service URL for XMPP connection.\n * @param {string} options.shard - The shard where XMPP connection initially landed.\n * @param {string} options.enableWebsocketResume - True to enable stream resumption.\n * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.\n * @param {number} [options.websocketKeepAliveUrl] - See {@link XmppConnection} constructor.\n * @param {Object} [options.xmppPing] - See {@link XmppConnection} constructor.\n * @returns {XmppConnection}\n */\nfunction createConnection({\n    enableWebsocketResume,\n    serviceUrl = '/http-bind',\n    shard,\n    token,\n    websocketKeepAlive,\n    websocketKeepAliveUrl,\n    xmppPing }) {\n\n    // Append token as URL param\n    if (token) {\n        // eslint-disable-next-line no-param-reassign\n        serviceUrl += `${serviceUrl.indexOf('?') === -1 ? '?' : '&'}token=${token}`;\n    }\n\n    return new XmppConnection({\n        enableWebsocketResume,\n        serviceUrl,\n        websocketKeepAlive,\n        websocketKeepAliveUrl,\n        xmppPing,\n        shard\n    });\n}\n\n/**\n * Initializes Strophe plugins that need to work with Strophe.Connection directly rather than the lib-jitsi-meet's\n * {@link XmppConnection} wrapper.\n *\n * @returns {void}\n */\nfunction initStropheNativePlugins() {\n    initStropheUtil();\n    initStropheLogger();\n}\n\n// FIXME: remove once we have a default config template. -saghul\n/**\n * A list of ice servers to use by default for P2P.\n */\nexport const DEFAULT_STUN_SERVERS = [\n    { urls: 'stun:meet-jit-si-turnrelay.jitsi.net:443' }\n];\n\n/**\n * The name of the field used to recognize a chat message as carrying a JSON\n * payload from another endpoint.\n * If the json-message of a chat message contains a valid JSON object, and\n * the JSON has this key, then it is a valid json-message to be sent.\n */\nexport const JITSI_MEET_MUC_TYPE = 'type';\n\n/**\n * The feature used by jigasi participants.\n * @type {string}\n */\nexport const FEATURE_JIGASI = 'http://jitsi.org/protocol/jigasi';\n\n/**\n * The feature used by the lib to mark support for e2ee. We use the feature by putting it in the presence\n * to avoid additional signaling (disco-info).\n * @type {string}\n */\nexport const FEATURE_E2EE = 'https://jitsi.org/meet/e2ee';\n\n/**\n *\n */\nexport default class XMPP extends Listenable {\n    /**\n     * FIXME describe all options\n     * @param {Object} options\n     * @param {String} options.serviceUrl - URL passed to the XMPP client which will be used to establish XMPP\n     * connection with the server.\n     * @param {String} options.bosh - Deprecated, use {@code serviceUrl}.\n     * @param {boolean} options.enableWebsocketResume - Enables XEP-0198 stream management which will make the XMPP\n     * module try to resume the session in case the Websocket connection breaks.\n     * @param {number} [options.websocketKeepAlive] - The websocket keep alive interval. See {@link XmppConnection}\n     * constructor for more details.\n     * @param {number} [options.websocketKeepAliveUrl] - The websocket keep alive url. See {@link XmppConnection}\n     * constructor for more details.\n     * @param {Object} [options.xmppPing] - The xmpp ping settings.\n     * @param {Array<Object>} options.p2pStunServers see {@link JingleConnectionPlugin} for more details.\n     * @param token\n     */\n    constructor(options, token) {\n        super();\n        this.connection = null;\n        this.disconnectInProgress = false;\n        this.connectionTimes = {};\n        this.options = options;\n        this.token = token;\n        this.authenticatedUser = false;\n\n        initStropheNativePlugins();\n\n        const xmppPing = options.xmppPing || {};\n\n        // let's ping the main domain (in case a guest one is used for the connection)\n        xmppPing.domain = options.hosts.domain;\n\n        this.connection = createConnection({\n            enableWebsocketResume: options.enableWebsocketResume,\n\n            // FIXME remove deprecated bosh option at some point\n            serviceUrl: options.serviceUrl || options.bosh,\n            token,\n            websocketKeepAlive: options.websocketKeepAlive,\n            websocketKeepAliveUrl: options.websocketKeepAliveUrl,\n            xmppPing,\n            shard: options.deploymentInfo?.shard\n        });\n\n        // forwards the shard changed event\n        this.connection.on(XmppConnection.Events.CONN_SHARD_CHANGED, () => {\n            /* eslint-disable camelcase */\n            const details = {\n                shard_changed: true,\n                suspend_time: this.connection.ping.getPingSuspendTime(),\n                time_since_last_success: this.connection.getTimeSinceLastSuccess()\n            };\n            /* eslint-enable camelcase */\n\n            this.eventEmitter.emit(\n                JitsiConnectionEvents.CONNECTION_FAILED,\n                JitsiConnectionErrors.OTHER_ERROR,\n                undefined,\n                undefined,\n                details);\n        });\n\n        this._initStrophePlugins();\n\n        this.caps = new Caps(this.connection, /* clientNode */ 'https://jitsi.org/jitsi-meet');\n\n        // Initialize features advertised in disco-info\n        this.initFeaturesList();\n\n        // Setup a disconnect on unload as a way to facilitate API consumers. It\n        // sounds like they would want that. A problem for them though may be if\n        // they wanted to utilize the connected connection in an unload handler\n        // of their own. However, it should be fairly easy for them to do that\n        // by registering their unload handler before us.\n        $(window).on(`${this.options.disableBeforeUnloadHandlers ? '' : 'beforeunload '}unload`, ev => {\n            this.disconnect(ev).catch(() => {\n                // ignore errors in order to not brake the unload.\n            });\n        });\n    }\n\n    /**\n     * Initializes the list of feature advertised through the disco-info\n     * mechanism.\n     */\n    initFeaturesList() {\n        // http://xmpp.org/extensions/xep-0167.html#support\n        // http://xmpp.org/extensions/xep-0176.html#support\n        this.caps.addFeature('urn:xmpp:jingle:1');\n        this.caps.addFeature('urn:xmpp:jingle:apps:rtp:1');\n        this.caps.addFeature('urn:xmpp:jingle:transports:ice-udp:1');\n        this.caps.addFeature('urn:xmpp:jingle:apps:dtls:0');\n        this.caps.addFeature('urn:xmpp:jingle:transports:dtls-sctp:1');\n        this.caps.addFeature('urn:xmpp:jingle:apps:rtp:audio');\n        this.caps.addFeature('urn:xmpp:jingle:apps:rtp:video');\n        this.caps.addFeature('http://jitsi.org/json-encoded-sources');\n\n        if (!(this.options.disableRtx || !browser.supportsRTX())) {\n            this.caps.addFeature('urn:ietf:rfc:4588');\n        }\n        if (this.options.enableOpusRed === true && browser.supportsAudioRed()) {\n            this.caps.addFeature('http://jitsi.org/opus-red');\n        }\n\n        if (typeof this.options.enableRemb === 'undefined' || this.options.enableRemb) {\n            this.caps.addFeature('http://jitsi.org/remb');\n        }\n\n        // Disable TCC on Firefox because of a known issue where BWE is halved on every renegotiation.\n        if (!browser.isFirefox() && (typeof this.options.enableTcc === 'undefined' || this.options.enableTcc)) {\n            this.caps.addFeature('http://jitsi.org/tcc');\n        }\n\n        // this is dealt with by SDP O/A so we don't need to announce this\n        // XEP-0293\n        // this.caps.addFeature('urn:xmpp:jingle:apps:rtp:rtcp-fb:0');\n        // XEP-0294\n        // this.caps.addFeature('urn:xmpp:jingle:apps:rtp:rtp-hdrext:0');\n\n        this.caps.addFeature('urn:ietf:rfc:5761'); // rtcp-mux\n        this.caps.addFeature('urn:ietf:rfc:5888'); // a=group, e.g. bundle\n\n        // this.caps.addFeature('urn:ietf:rfc:5576'); // a=ssrc\n\n        // Enable Lipsync ?\n        if (browser.isChromiumBased() && this.options.enableLipSync === true) {\n            logger.info('Lip-sync enabled !');\n            this.caps.addFeature('http://jitsi.org/meet/lipsync');\n        }\n\n        if (this.connection.rayo) {\n            this.caps.addFeature('urn:xmpp:rayo:client:1');\n        }\n\n        if (E2EEncryption.isSupported(this.options)) {\n            this.caps.addFeature(FEATURE_E2EE, false, true);\n        }\n    }\n\n    /**\n     *\n     */\n    getConnection() {\n        return this.connection;\n    }\n\n    /**\n     * Receive connection status changes and handles them.\n     *\n     * @param {Object} credentials\n     * @param {string} credentials.jid - The user's XMPP ID passed to the\n     * connect method. For example, 'user@xmpp.com'.\n     * @param {string} credentials.password - The password passed to the connect\n     * method.\n     * @param {string} status - One of Strophe's connection status strings.\n     * @param {string} [msg] - The connection error message provided by Strophe.\n     */\n    connectionHandler(credentials = {}, status, msg) {\n        const now = window.performance.now();\n        const statusStr = Strophe.getStatusString(status).toLowerCase();\n\n        this.connectionTimes[statusStr] = now;\n        logger.log(\n            `(TIME) Strophe ${statusStr}${msg ? `[${msg}]` : ''}:\\t`,\n            now);\n\n        this.eventEmitter.emit(XMPPEvents.CONNECTION_STATUS_CHANGED, credentials, status, msg);\n        this._maybeSendDeploymentInfoStat();\n        if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {\n            // once connected or attached we no longer need this handle, drop it if it exist\n            if (this._sysMessageHandler) {\n                this.connection._stropheConn.deleteHandler(this._sysMessageHandler);\n                this._sysMessageHandler = null;\n            }\n\n            this.sendDiscoInfo && this.connection.jingle.getStunAndTurnCredentials();\n\n            logger.info(`My Jabber ID: ${this.connection.jid}`);\n\n            // XmppConnection emits CONNECTED again on reconnect - a good opportunity to clear any \"last error\" flags\n            this._resetState();\n\n            // make sure we will send the info after the features request succeeds or fails\n            this.sendDeploymentInfo = false;\n            this.sendDiscoInfo && this.caps.getFeaturesAndIdentities(this.options.hosts.domain)\n                .then(({ features, identities }) => {\n                    if (!features.has(Strophe.NS.PING)) {\n                        logger.error(`Ping NOT supported by ${\n                            this.options.hosts.domain} - please enable ping in your XMPP server config`);\n                    }\n\n                    this._processDiscoInfoIdentities(\n                        identities, undefined /* when querying we will query for features */);\n                })\n                .catch(error => {\n                    const errmsg = 'Feature discovery error';\n\n                    GlobalOnErrorHandler.callErrorHandler(\n                        new Error(`${errmsg}: ${error}`));\n                    logger.error(errmsg, error);\n\n                    this._maybeSendDeploymentInfoStat(true);\n                });\n\n            // make sure we don't query again\n            this.sendDiscoInfo = false;\n\n            if (credentials.password) {\n                this.authenticatedUser = true;\n            }\n            if (this.connection && this.connection.connected\n                && Strophe.getResourceFromJid(this.connection.jid)) {\n                // .connected is true while connecting?\n                // this.connection.send($pres());\n                this.eventEmitter.emit(\n                    JitsiConnectionEvents.CONNECTION_ESTABLISHED,\n                    Strophe.getResourceFromJid(this.connection.jid));\n            }\n        } else if (status === Strophe.Status.CONNFAIL) {\n            if (msg === 'x-strophe-bad-non-anon-jid') {\n                this.anonymousConnectionFailed = true;\n            } else {\n                this.connectionFailed = true;\n            }\n            this.lastErrorMsg = msg;\n            if (msg === 'giving-up') {\n                this.eventEmitter.emit(\n                    JitsiConnectionEvents.CONNECTION_FAILED,\n                    JitsiConnectionErrors.OTHER_ERROR, msg);\n            }\n        } else if (status === Strophe.Status.ERROR) {\n            this.lastErrorMsg = msg;\n        } else if (status === Strophe.Status.DISCONNECTED) {\n            // Stop ping interval\n            this.connection.ping.stopInterval();\n            const wasIntentionalDisconnect = Boolean(this.disconnectInProgress);\n            const errMsg = msg || this.lastErrorMsg;\n\n            if (this.anonymousConnectionFailed) {\n                // prompt user for username and password\n                this.eventEmitter.emit(\n                    JitsiConnectionEvents.CONNECTION_FAILED,\n                    JitsiConnectionErrors.PASSWORD_REQUIRED);\n            } else if (this.connectionFailed) {\n                this.eventEmitter.emit(\n                    JitsiConnectionEvents.CONNECTION_FAILED,\n                    JitsiConnectionErrors.OTHER_ERROR,\n                    errMsg,\n                    undefined, /* credentials */\n                    this._getConnectionFailedReasonDetails());\n            } else if (wasIntentionalDisconnect) {\n                this.eventEmitter.emit(\n                    JitsiConnectionEvents.CONNECTION_DISCONNECTED, errMsg);\n            } else {\n                // XXX if Strophe drops the connection while not being asked to,\n                // it means that most likely some serious error has occurred.\n                // One currently known case is when a BOSH request fails for\n                // more than 4 times. The connection is dropped without\n                // supplying a reason(error message/event) through the API.\n                logger.error('XMPP connection dropped!');\n\n                // XXX if the last request error is within 5xx range it means it\n                // was a server failure\n                const lastErrorStatus = Strophe.getLastErrorStatus();\n\n                if (lastErrorStatus >= 500 && lastErrorStatus < 600) {\n                    this.eventEmitter.emit(\n                        JitsiConnectionEvents.CONNECTION_FAILED,\n                        JitsiConnectionErrors.SERVER_ERROR,\n                        errMsg || 'server-error',\n                        /* credentials */ undefined,\n                        this._getConnectionFailedReasonDetails());\n                } else {\n                    this.eventEmitter.emit(\n                        JitsiConnectionEvents.CONNECTION_FAILED,\n                        JitsiConnectionErrors.CONNECTION_DROPPED_ERROR,\n                        errMsg || 'connection-dropped-error',\n                        /* credentials */ undefined,\n                        this._getConnectionFailedReasonDetails());\n                }\n            }\n        } else if (status === Strophe.Status.AUTHFAIL) {\n            const lastFailedRawMessage = this.getConnection().getLastFailedMessage();\n\n            // wrong password or username, prompt user\n            this.eventEmitter.emit(\n                JitsiConnectionEvents.CONNECTION_FAILED,\n                JitsiConnectionErrors.PASSWORD_REQUIRED,\n                msg || this._parseConnectionFailedMessage(lastFailedRawMessage),\n                credentials);\n        }\n    }\n\n    /**\n     * Process received identities.\n     * @param {Set<String>} identities The identities to process.\n     * @param {Set<String>} features The features to process, optional. If missing lobby component will be queried\n     * for more features.\n     * @private\n     */\n    _processDiscoInfoIdentities(identities, features) {\n        // check for speakerstats\n        identities.forEach(identity => {\n            if (identity.type === 'av_moderation') {\n                this.avModerationComponentAddress = identity.name;\n            }\n\n            if (identity.type === 'speakerstats') {\n                this.speakerStatsComponentAddress = identity.name;\n            }\n\n            if (identity.type === 'conference_duration') {\n                this.conferenceDurationComponentAddress = identity.name;\n            }\n\n            if (identity.type === 'lobbyrooms') {\n                this.lobbySupported = true;\n                const processLobbyFeatures = f => {\n                    f.forEach(fr => {\n                        if (fr.endsWith('#displayname_required')) {\n                            this.eventEmitter.emit(JitsiConnectionEvents.DISPLAY_NAME_REQUIRED);\n                        }\n                    });\n                };\n\n                if (features) {\n                    processLobbyFeatures(features);\n                } else {\n                    identity.name && this.caps.getFeaturesAndIdentities(identity.name, identity.type)\n                        .then(({ features: f }) => processLobbyFeatures(f))\n                        .catch(e => logger.warn('Error getting features from lobby.', e && e.message));\n                }\n            }\n\n            if (identity.type === 'shard') {\n                this.options.deploymentInfo.shard = this.connection.shard = identity.name;\n            }\n\n            if (identity.type === 'region') {\n                this.options.deploymentInfo.region = this.connection.region = identity.name;\n            }\n\n            if (identity.type === 'breakout_rooms') {\n                this.breakoutRoomsComponentAddress = identity.name;\n            }\n        });\n\n        this._maybeSendDeploymentInfoStat(true);\n\n        if (this.avModerationComponentAddress\n            || this.speakerStatsComponentAddress\n            || this.conferenceDurationComponentAddress\n            || this.breakoutRoomsComponentAddress) {\n            this.connection.addHandler(this._onPrivateMessage.bind(this), null, 'message', null, null);\n        }\n    }\n\n    /**\n    * Parses a raw failure xmpp xml message received on auth failed.\n    *\n    * @param {string} msg - The raw failure message from xmpp.\n    * @returns {string|null} - The parsed message from the raw xmpp message.\n    */\n    _parseConnectionFailedMessage(msg) {\n        if (!msg) {\n            return null;\n        }\n\n        const matches = FAILURE_REGEX.exec(msg);\n\n        return matches ? matches[1] : null;\n    }\n\n    /**\n     *\n     * @param jid\n     * @param password\n     */\n    _connect(jid, password) {\n        // connection.connect() starts the connection process.\n        //\n        // As the connection process proceeds, the user supplied callback will\n        // be triggered multiple times with status updates. The callback should\n        // take two arguments - the status code and the error condition.\n        //\n        // The status code will be one of the values in the Strophe.Status\n        // constants. The error condition will be one of the conditions defined\n        // in RFC 3920 or the condition ‘strophe-parsererror’.\n        //\n        // The Parameters wait, hold and route are optional and only relevant\n        // for BOSH connections. Please see XEP 124 for a more detailed\n        // explanation of the optional parameters.\n        //\n        // Connection status constants for use by the connection handler\n        // callback.\n        //\n        //  Status.ERROR - An error has occurred (websockets specific)\n        //  Status.CONNECTING - The connection is currently being made\n        //  Status.CONNFAIL - The connection attempt failed\n        //  Status.AUTHENTICATING - The connection is authenticating\n        //  Status.AUTHFAIL - The authentication attempt failed\n        //  Status.CONNECTED - The connection has succeeded\n        //  Status.DISCONNECTED - The connection has been terminated\n        //  Status.DISCONNECTING - The connection is currently being terminated\n        //  Status.ATTACHED - The connection has been attached\n\n        this._resetState();\n\n        // we want to send this only on the initial connect\n        this.sendDiscoInfo = true;\n        this.sendDeploymentInfo = true;\n\n        if (this.connection._stropheConn && this.connection._stropheConn._addSysHandler) {\n            this._sysMessageHandler = this.connection._stropheConn._addSysHandler(\n                this._onSystemMessage.bind(this),\n                null,\n                'message'\n            );\n        } else {\n            logger.warn('Cannot attach strophe system handler, jiconop cannot operate');\n        }\n\n        this.connection.connect(\n            jid,\n            password,\n            this.connectionHandler.bind(this, {\n                jid,\n                password\n            }));\n    }\n\n    /**\n     * Receives system messages during the connect/login process and checks for services or\n     * @param msg The received message.\n     * @returns {void}\n     * @private\n     */\n    _onSystemMessage(msg) {\n        // proceed only if the message has any of the expected information\n        if ($(msg).find('>services').length === 0 && $(msg).find('>query').length === 0) {\n            return;\n        }\n\n        this.sendDiscoInfo = false;\n\n        const foundIceServers = this.connection.jingle.onReceiveStunAndTurnCredentials(msg);\n\n        const { features, identities } = parseDiscoInfo(msg);\n\n        this._processDiscoInfoIdentities(identities, features);\n\n        if (foundIceServers || identities.size > 0 || features.size > 0) {\n            this.connection._stropheConn.deleteHandler(this._sysMessageHandler);\n            this._sysMessageHandler = null;\n        }\n    }\n\n    /**\n     * Attach to existing connection. Can be used for optimizations. For\n     * example: if the connection is created on the server we can attach to it\n     * and start using it.\n     *\n     * @param options {object} connecting options - rid, sid, jid and password.\n     */\n    attach(options) {\n        this._resetState();\n\n        // we want to send this only on the initial connect\n        this.sendDiscoInfo = true;\n\n        const now = this.connectionTimes.attaching = window.performance.now();\n\n        logger.log('(TIME) Strophe Attaching:\\t', now);\n        this.connection.attach(options.jid, options.sid,\n            parseInt(options.rid, 10) + 1,\n            this.connectionHandler.bind(this, {\n                jid: options.jid,\n                password: options.password\n            }));\n    }\n\n    /**\n     * Resets any state/flag before starting a new connection.\n     * @private\n     */\n    _resetState() {\n        this.anonymousConnectionFailed = false;\n        this.connectionFailed = false;\n        this.lastErrorMsg = undefined;\n        this.disconnectInProgress = undefined;\n    }\n\n    /**\n     *\n     * @param jid\n     * @param password\n     */\n    connect(jid, password) {\n        if (!jid) {\n            const { anonymousdomain, domain } = this.options.hosts;\n            let configDomain = anonymousdomain || domain;\n\n            // Force authenticated domain if room is appended with '?login=true'\n            // or if we're joining with the token\n\n            // FIXME Do not rely on window.location because (1) React Native\n            // does not have a window.location by default and (2) here we cannot\n            // know for sure that query/search has not be stripped from\n            // window.location by the time the following executes.\n            const { location } = window;\n\n            if (anonymousdomain) {\n                const search = location && location.search;\n\n                if ((search && search.indexOf('login=true') !== -1)\n                        || this.token) {\n                    configDomain = domain;\n                }\n            }\n\n            // eslint-disable-next-line no-param-reassign\n            jid = configDomain || (location && location.hostname);\n        }\n\n        return this._connect(jid, password);\n    }\n\n    /**\n     * Joins or creates a muc with the provided jid, created from the passed\n     * in room name and muc host and onCreateResource result.\n     *\n     * @param {string} roomName - The name of the muc to join.\n     * @param {Object} options - Configuration for how to join the muc.\n     * @param {Function} [onCreateResource] - Callback to invoke when a resource\n     * is to be added to the jid.\n     * @returns {Promise} Resolves with an instance of a strophe muc.\n     */\n    createRoom(roomName, options, onCreateResource) {\n        // Support passing the domain in a String object as part of the room name.\n        const domain = roomName.domain || options.customDomain;\n\n        // There are cases (when using subdomain) where muc can hold an uppercase part\n        let roomjid = `${this.getRoomJid(roomName, domain)}/`;\n        const mucNickname = onCreateResource\n            ? onCreateResource(this.connection.jid, this.authenticatedUser)\n            : RandomUtil.randomHexString(8).toLowerCase();\n\n        logger.info(`JID ${this.connection.jid} using MUC nickname ${mucNickname}`);\n        roomjid += mucNickname;\n\n        return this.connection.emuc.createRoom(roomjid, null, options);\n    }\n\n    /**\n     * Returns the room JID based on the passed room name and domain.\n     *\n     * @param {string} roomName - The room name.\n     * @param {string} domain - The domain.\n     * @returns {string} - The room JID.\n     */\n    getRoomJid(roomName, domain) {\n        return `${roomName}@${domain ? domain : this.options.hosts.muc.toLowerCase()}`;\n    }\n\n    /**\n     * Check if a room with the passed JID is already created.\n     *\n     * @param {string} roomJid - The JID of the room.\n     * @returns {boolean}\n     */\n    isRoomCreated(roomName, domain) {\n        return this.connection.emuc.isRoomCreated(this.getRoomJid(roomName, domain));\n    }\n\n    /**\n     * Returns the jid of the participant associated with the Strophe connection.\n     *\n     * @returns {string} The jid of the participant.\n     */\n    getJid() {\n        return this.connection.jid;\n    }\n\n    /**\n     * Returns the logs from strophe.jingle.\n     * @returns {Object}\n     */\n    getJingleLog() {\n        const jingle = this.connection.jingle;\n\n\n        return jingle ? jingle.getLog() : {};\n    }\n\n    /**\n     * Returns the logs from strophe.\n     */\n    getXmppLog() {\n        return (this.connection.logger || {}).log || null;\n    }\n\n    /**\n     *\n     */\n    dial(...args) {\n        this.connection.rayo.dial(...args);\n    }\n\n    /**\n     * Pings the server.\n     * @param timeout how many ms before a timeout should occur.\n     * @returns {Promise} resolved on ping success and reject on an error or\n     * a timeout.\n     */\n    ping(timeout) {\n        return new Promise((resolve, reject) => {\n            this.connection.ping.ping(this.connection.pingDomain, resolve, reject, timeout);\n        });\n    }\n\n    /**\n     *\n     */\n    getSessions() {\n        return this.connection.jingle.sessions;\n    }\n\n    /**\n     * Disconnects this from the XMPP server (if this is connected).\n     *\n     * @param {Object} ev - Optionally, the event which triggered the necessity to\n     * disconnect from the XMPP server (e.g. beforeunload, unload).\n     * @returns {Promise} - Resolves when the disconnect process is finished or rejects with an error.\n     */\n    disconnect(ev) {\n        if (this.disconnectInProgress) {\n            return this.disconnectInProgress;\n        } else if (!this.connection) {\n            return Promise.resolve();\n        }\n\n        this.disconnectInProgress = new Promise(resolve => {\n            const disconnectListener = (credentials, status) => {\n                if (status === Strophe.Status.DISCONNECTED) {\n                    resolve();\n                    this.eventEmitter.removeListener(XMPPEvents.CONNECTION_STATUS_CHANGED, disconnectListener);\n                }\n            };\n\n            this.eventEmitter.on(XMPPEvents.CONNECTION_STATUS_CHANGED, disconnectListener);\n        });\n\n        this._cleanupXmppConnection(ev);\n\n        return this.disconnectInProgress;\n    }\n\n    /**\n     * The method is supposed to gracefully close the XMPP connection and the main goal is to make sure that the current\n     * participant will be removed from the conference XMPP MUC, so that it doesn't leave a \"ghost\" participant behind.\n     *\n     * @param {Object} ev - Optionally, the event which triggered the necessity to disconnect from the XMPP server\n     * (e.g. beforeunload, unload).\n     * @private\n     * @returns {void}\n     */\n    _cleanupXmppConnection(ev) {\n        // XXX Strophe is asynchronously sending by default. Unfortunately, that means that there may not be enough time\n        // to send an unavailable presence or disconnect at all. Switching Strophe to synchronous sending is not much of\n        // an option because it may lead to a noticeable delay in navigating away from the current location. As\n        // a compromise, we will try to increase the chances of sending an unavailable presence and/or disconnecting\n        // within the short time span that we have upon unloading by invoking flush() on the connection. We flush() once\n        // before disconnect() in order to attempt to have its unavailable presence at the top of the send queue. We\n        // flush() once more after disconnect() in order to attempt to have its unavailable presence sent as soon as\n        // possible.\n        !this.connection.isUsingWebSocket && this.connection.flush();\n\n        if (!this.connection.isUsingWebSocket && ev !== null && typeof ev !== 'undefined') {\n            const evType = ev.type;\n\n            if (evType === 'beforeunload' || evType === 'unload') {\n                // XXX Whatever we said above, synchronous sending is the best (known) way to properly disconnect from\n                // the XMPP server. Consequently, it may be fine to have the source code and comment it in or out\n                // depending on whether we want to run with it for some time.\n                this.connection.options.sync = true;\n\n                // This is needed in some browsers where sync xhr sending is disabled by default on unload.\n                if (this.connection.sendUnavailableBeacon()) {\n\n                    return;\n                }\n            }\n        }\n\n        this.connection.disconnect();\n\n        if (this.connection.options.sync !== true) {\n            this.connection.flush();\n        }\n    }\n\n    /**\n     *\n     */\n    _initStrophePlugins() {\n        const iceConfig = {\n            jvb: { iceServers: [ ] },\n            p2p: { iceServers: [ ] }\n        };\n\n        const p2pStunServers = (this.options.p2p\n            && this.options.p2p.stunServers) || DEFAULT_STUN_SERVERS;\n\n        if (Array.isArray(p2pStunServers)) {\n            logger.info('P2P STUN servers: ', p2pStunServers);\n            iceConfig.p2p.iceServers = p2pStunServers;\n        }\n\n        if (this.options.p2p && this.options.p2p.iceTransportPolicy) {\n            logger.info('P2P ICE transport policy: ',\n                this.options.p2p.iceTransportPolicy);\n\n            iceConfig.p2p.iceTransportPolicy\n                = this.options.p2p.iceTransportPolicy;\n        }\n\n        this.connection.addConnectionPlugin('emuc', new MucConnectionPlugin(this));\n        this.connection.addConnectionPlugin('jingle', new JingleConnectionPlugin(this, this.eventEmitter, iceConfig));\n        this.connection.addConnectionPlugin('rayo', new RayoConnectionPlugin());\n    }\n\n    /**\n     * Returns details about connection failure. Shard change or is it after\n     * suspend.\n     * @returns {object} contains details about a connection failure.\n     * @private\n     */\n    _getConnectionFailedReasonDetails() {\n        const details = {};\n\n        // check for moving between shard if information is available\n        if (this.options.deploymentInfo\n            && this.options.deploymentInfo.shard\n            && this.connection.lastResponseHeaders) {\n\n            // split headers by line\n            const headersArr = this.connection.lastResponseHeaders\n                .trim().split(/[\\r\\n]+/);\n            const headers = {};\n\n            headersArr.forEach(line => {\n                const parts = line.split(': ');\n                const header = parts.shift();\n                const value = parts.join(': ');\n\n                headers[header] = value;\n            });\n\n            /* eslint-disable camelcase */\n            details.shard_changed\n                = this.options.deploymentInfo.shard\n                    !== headers['x-jitsi-shard'];\n            /* eslint-enable camelcase */\n        }\n\n        /* eslint-disable camelcase */\n        // check for possible suspend\n        details.suspend_time = this.connection.ping.getPingSuspendTime();\n        details.time_since_last_success = this.connection.getTimeSinceLastSuccess();\n        /* eslint-enable camelcase */\n\n        return details;\n    }\n\n    /**\n     * Notifies speaker stats component if available that we are the new\n     * dominant speaker in the conference.\n     * @param {String} roomJid - The room jid where the speaker event occurred.\n     */\n    sendDominantSpeakerEvent(roomJid) {\n        // no speaker stats component advertised\n        if (!this.speakerStatsComponentAddress || !roomJid) {\n            return;\n        }\n\n        const msg = $msg({ to: this.speakerStatsComponentAddress });\n\n        msg.c('speakerstats', {\n            xmlns: 'http://jitsi.org/jitmeet',\n            room: roomJid })\n            .up();\n\n        this.connection.send(msg);\n    }\n\n    /**\n     * Sends facial expression to speaker stats component.\n     * @param {String} roomJid - The room jid where the speaker event occurred.\n     * @param {Object} payload - The expression to be sent to the speaker stats.\n     */\n    sendFacialExpressionEvent(roomJid, payload) {\n        // no speaker stats component advertised\n        if (!this.speakerStatsComponentAddress || !roomJid) {\n            return;\n        }\n\n        const msg = $msg({ to: this.speakerStatsComponentAddress });\n\n        msg.c('facialExpression', {\n            xmlns: 'http://jitsi.org/jitmeet',\n            room: roomJid,\n            expression: payload.facialExpression,\n            duration: payload.duration\n        }).up();\n\n        this.connection.send(msg);\n    }\n\n    /**\n     * Check if the given argument is a valid JSON ENDPOINT_MESSAGE string by\n     * parsing it and checking if it has a field called 'type'.\n     *\n     * @param {string} jsonString check if this string is a valid json string\n     * and contains the special structure.\n     * @returns {boolean, object} if given object is a valid JSON string, return\n     * the json object. Otherwise, returns false.\n     */\n    tryParseJSONAndVerify(jsonString) {\n        // ignore empty strings, like message errors\n        if (!jsonString) {\n            return false;\n        }\n\n        try {\n            const json = JSON.parse(jsonString);\n\n            // Handle non-exception-throwing cases:\n            // Neither JSON.parse(false) or JSON.parse(1234) throw errors,\n            // hence the type-checking,\n            // but... JSON.parse(null) returns null, and\n            // typeof null === \"object\",\n            // so we must check for that, too.\n            // Thankfully, null is falsey, so this suffices:\n            if (json && typeof json === 'object') {\n                const type = json[JITSI_MEET_MUC_TYPE];\n\n                if (typeof type !== 'undefined') {\n                    return json;\n                }\n\n                logger.debug('parsing valid json but does not have correct '\n                    + 'structure', 'topic: ', type);\n            }\n        } catch (e) {\n            logger.error(`Error parsing json ${jsonString}`, e);\n\n            return false;\n        }\n\n        return false;\n    }\n\n    /**\n     * A private message is received, message that is not addressed to the muc.\n     * We expect private message coming from plugins component if it is\n     * enabled and running.\n     *\n     * @param {string} msg - The message.\n     */\n    _onPrivateMessage(msg) {\n        const from = msg.getAttribute('from');\n\n        if (!(from === this.speakerStatsComponentAddress\n            || from === this.conferenceDurationComponentAddress\n            || from === this.avModerationComponentAddress\n            || from === this.breakoutRoomsComponentAddress)) {\n            return true;\n        }\n\n        const jsonMessage = $(msg).find('>json-message')\n            .text();\n        const parsedJson = this.tryParseJSONAndVerify(jsonMessage);\n\n        if (!parsedJson) {\n            return true;\n        }\n\n        if (parsedJson[JITSI_MEET_MUC_TYPE] === 'speakerstats' && parsedJson.users) {\n            this.eventEmitter.emit(XMPPEvents.SPEAKER_STATS_RECEIVED, parsedJson.users);\n        } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'conference_duration' && parsedJson.created_timestamp) {\n            this.eventEmitter.emit(XMPPEvents.CONFERENCE_TIMESTAMP_RECEIVED, parsedJson.created_timestamp);\n        } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'av_moderation') {\n            this.eventEmitter.emit(XMPPEvents.AV_MODERATION_RECEIVED, parsedJson);\n        } else if (parsedJson[JITSI_MEET_MUC_TYPE] === 'breakout_rooms') {\n            this.eventEmitter.emit(XMPPEvents.BREAKOUT_ROOMS_EVENT, parsedJson);\n        }\n\n        return true;\n    }\n\n    /**\n     * Sends deployment info to stats if not sent already.\n     * We want to try sending it on failure to connect\n     * or when we get a sys message(from jiconop2)\n     * or after success or failure of disco-info\n     * @param force Whether to force sending without checking anything.\n     * @private\n     */\n    _maybeSendDeploymentInfoStat(force) {\n        const acceptedStatuses = [\n            Strophe.Status.ERROR,\n            Strophe.Status.CONNFAIL,\n            Strophe.Status.AUTHFAIL,\n            Strophe.Status.DISCONNECTED,\n            Strophe.Status.CONNTIMEOUT\n        ];\n\n        if (!force && !(acceptedStatuses.includes(this.connection.status) && this.sendDeploymentInfo)) {\n            return;\n        }\n\n        // Log deployment-specific information, if available. Defined outside\n        // the application by individual deployments\n        const aprops = this.options.deploymentInfo;\n\n        if (aprops && Object.keys(aprops).length > 0) {\n            const logObject = {};\n\n            logObject.id = 'deployment_info';\n            for (const attr in aprops) {\n                if (aprops.hasOwnProperty(attr)) {\n                    logObject[attr] = aprops[attr];\n                }\n            }\n\n            Statistics.sendLog(JSON.stringify(logObject));\n        }\n\n        this.sendDeploymentInfo = false;\n    }\n}\n","import {\n    CONNECTION_DISCONNECTED,\n    CONNECTION_ESTABLISHED,\n    CONNECTION_FAILED\n} from './JitsiConnectionEvents';\nimport XMPP from './modules/xmpp/xmpp';\n\n/**\n * @typedef {Object} UpgradeRoleError\n *\n * @property {JitsiConnectionErrors} [connectionError] - One of\n * {@link JitsiConnectionErrors} which occurred when trying to connect to the\n * XMPP server.\n * @property {String} [authenticationError] - One of XMPP error conditions\n * returned by Jicofo on authentication attempt. See\n * {@link https://xmpp.org/rfcs/rfc3920.html#streams-error}.\n * @property {String} [message] - More details about the error.\n * @property {Object} [credentials] - The credentials that failed the\n * authentication.\n * @property {String} [credentials.jid] - The XMPP ID part of the credentials\n * that failed the authentication.\n * @property {string} [credentials.password] - The password part of the\n * credentials that failed the authentication.\n *\n * NOTE If neither one of the errors is present, then the operation has been\n * canceled.\n */\n\n/* eslint-disable no-invalid-this */\n\n/**\n * Connects to the XMPP server using the specified credentials and contacts\n * Jicofo in order to obtain a session ID (which is then stored in the local\n * storage). The user's role of the parent conference will be upgraded to\n * moderator (by Jicofo). It's also used to join the conference when starting\n * from anonymous domain and only authenticated users are allowed to create new\n * rooms.\n *\n * @param {Object} options\n * @param {string} options.id - XMPP user's ID to log in. For example,\n * user@xmpp-server.com.\n * @param {string} options.password - XMPP user's password to log in with.\n * @param {string} [options.roomPassword] - The password to join the MUC with.\n * @param {Function} [options.onLoginSuccessful] - Callback called when logging\n * into the XMPP server was successful. The next step will be to obtain a new\n * session ID from Jicofo and join the MUC using it which will effectively\n * upgrade the user's role to moderator.\n * @returns {Object} A <tt>thenable</tt> which (1) settles when the process of\n * authenticating and upgrading the role of the specified XMPP user finishes and\n * (2) has a <tt>cancel</tt> method that allows the caller to interrupt the\n * process. If the process finishes successfully, the session ID has been stored\n * in the settings and the <tt>thenable</tt> is resolved. If the process\n * finishes with failure, the <tt>thenable</tt> is rejected with reason of type\n * {@link UpgradeRoleError} which will have either <tt>connectionError</tt> or\n * <tt>authenticationError</tt> property set depending on which of the steps has\n * failed. If <tt>cancel</tt> is called before the process finishes, then the\n * thenable will be rejected with an empty object (i.e. no error property will\n * be set on the rejection reason).\n */\nexport default function authenticateAndUpgradeRole({\n    // 1. Log the specified XMPP user in.\n    id,\n    password,\n    onCreateResource,\n\n    // 2. Let the API client/consumer know as soon as the XMPP user has been\n    //    successfully logged in.\n    onLoginSuccessful,\n\n    // 3. Join the MUC.\n    roomPassword\n}) {\n    let canceled = false;\n    let rejectPromise;\n    let xmpp = new XMPP(this.connection.options);\n\n    const process = new Promise((resolve, reject) => {\n        // The process is represented by a Thenable with a cancel method. The\n        // Thenable is implemented using Promise and the cancel using the\n        // Promise's reject function.\n        rejectPromise = reject;\n\n\n        xmpp.addListener(\n            CONNECTION_DISCONNECTED,\n            () => {\n                xmpp = undefined;\n            });\n        xmpp.addListener(\n            CONNECTION_ESTABLISHED,\n            () => {\n                if (canceled) {\n                    return;\n                }\n\n                // Let the caller know that the XMPP login was successful.\n                onLoginSuccessful && onLoginSuccessful();\n\n                // Now authenticate with Jicofo and get a new session ID.\n                const room = xmpp.createRoom(\n                    this.options.name,\n                    this.options.config,\n                    onCreateResource\n                );\n\n                room.moderator.authenticate()\n                    .then(() => {\n                        xmpp && xmpp.disconnect();\n\n                        if (canceled) {\n                            return;\n                        }\n\n                        // At this point we should have the new session ID\n                        // stored in the settings. Jicofo will allow to join the\n                        // room.\n                        this.join(roomPassword);\n\n                        resolve();\n                    })\n                    .catch(({ error, message }) => {\n                        xmpp.disconnect();\n\n                        reject({\n                            authenticationError: error,\n                            message\n                        });\n                    });\n            });\n        xmpp.addListener(\n            CONNECTION_FAILED,\n            (connectionError, message, credentials) => {\n                reject({\n                    connectionError,\n                    credentials,\n                    message\n                });\n                xmpp = undefined;\n            });\n\n        canceled || xmpp.connect(id, password);\n    });\n\n    /**\n     * Cancels the process, if it's in progress, of authenticating and upgrading\n     * the role of the local participant/user.\n     *\n     * @public\n     * @returns {void}\n     */\n    process.cancel = () => {\n        canceled = true;\n        rejectPromise({});\n        xmpp && xmpp.disconnect();\n    };\n\n    return process;\n}\n\n/* eslint-enable no-invalid-this */\n","\nimport { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport browser from '../browser';\n\nconst logger = getLogger(__filename);\n\n/**\n * This class handles the codec selection mechanism for the conference based on the config.js settings.\n * The preferred codec is selected based on the settings and the list of codecs supported by the browser.\n * The preferred codec is published in presence which is then used by the other endpoints in the\n * conference to pick a supported codec at join time and when the call transitions between p2p and jvb\n * connections.\n */\nexport class CodecSelection {\n    /**\n     * Creates a new instance for a given conference.\n     *\n     * @param {JitsiConference} conference the conference instance\n     * @param {*} options\n     * @param {string} options.disabledCodec the codec that needs to be disabled.\n     * @param {boolean} options.enforcePreferredCodec whether codec preference has to be\n     * enforced even when an endpoints that doesn't support the preferred codec joins the call.\n     * Falling back to the standard codec will be skipped when this option is true, endpoints\n     * that do not support the preferred codec may not be able to encode/decode video when this happens.\n     * @param {string} options.jvbCodec the codec that is preferred on jvb connection.\n     * @param {string} options.p2pCodec the codec that is preferred on p2p connection.\n     */\n    constructor(conference, options) {\n        this.conference = conference;\n        this.options = options;\n\n        // VP8 cannot be disabled and it will be the default codec when no preference is set.\n        this.disabledCodec = options.disabledCodec === CodecMimeType.VP8\n            ? undefined\n            : this._getCodecMimeType(options.disabledCodec);\n\n        // Check if the codec values passed are valid.\n        const jvbCodec = this._getCodecMimeType(options.jvbCodec);\n        const p2pCodec = this._getCodecMimeType(options.p2pCodec);\n\n        this.jvbPreferredCodec = jvbCodec && this._isCodecSupported(jvbCodec) ? jvbCodec : CodecMimeType.VP8;\n        this.p2pPreferredCodec = p2pCodec && this._isCodecSupported(p2pCodec) ? p2pCodec : CodecMimeType.VP8;\n        logger.debug(`Codec preferences for the conference are JVB: ${this.jvbPreferredCodec},\n            P2P: ${this.p2pPreferredCodec}`);\n\n        if (this.jvbPreferredCodec === CodecMimeType.VP9 && !browser.supportsVP9()) {\n            this.jvbPreferredCodec = CodecMimeType.VP8;\n        }\n\n        this.conference.on(\n            JitsiConferenceEvents.USER_JOINED,\n            () => this._selectPreferredCodec());\n        this.conference.on(\n            JitsiConferenceEvents.USER_LEFT,\n            () => this._selectPreferredCodec());\n        this.conference.on(\n            JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n            session => this._onMediaSessionStarted(session));\n    }\n\n    /**\n     * Checks if a given string is a valid video codec mime type.\n     *\n     * @param {string} codec the codec string that needs to be validated.\n     * @returns {CodecMimeType|null} mime type if valid, null otherwise.\n     * @private\n     */\n    _getCodecMimeType(codec) {\n        if (typeof codec === 'string') {\n            return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());\n        }\n\n        return null;\n    }\n\n    /**\n     * Checks if the given codec is supported by the browser.\n     *\n     * @param {CodecMimeType} preferredCodec codec to be checked.\n     * @returns {boolean} true if the given codec is supported, false otherwise.\n     * @private\n     */\n    _isCodecSupported(preferredCodec) {\n        // Skip the check on FF and RN because they do not support the getCapabilities API.\n        // It is safe to assume both of them support all the codecs supported by Chrome.\n        if (browser.isFirefox() || browser.isReactNative()) {\n            return true;\n        }\n\n        return window.RTCRtpReceiver\n            && window.RTCRtpReceiver.getCapabilities\n            && window.RTCRtpReceiver.getCapabilities('video').codecs\n            .some(codec => codec.mimeType.toLowerCase() === `video/${preferredCodec}`);\n    }\n\n    /**\n     * Handles the {@link JitsiConferenceEvents._MEDIA_SESSION_STARTED} event. Codecs need to be\n     * configured on the media session that is newly created.\n     *\n     * @param {JingleSessionPC} mediaSession media session that started.\n     * @returns {void}\n     * @private\n     */\n    _onMediaSessionStarted(mediaSession) {\n        const preferredCodec = mediaSession.isP2P ? this.p2pPreferredCodec : this.jvbPreferredCodec;\n        const disabledCodec = this.disabledCodec && this._isCodecSupported(this.disabledCodec)\n            ? this.disabledCodec\n            : null;\n\n        this._selectPreferredCodec(mediaSession, preferredCodec, disabledCodec);\n    }\n\n    /**\n     * Sets the codec on the media session based on the preferred codec setting and the supported codecs\n     * published by the remote participants in their presence.\n     *\n     * @param {JingleSessionPC} mediaSession session for which the codec selection has to be made.\n     * @param {CodecMimeType} preferredCodec preferred codec.\n     * @param {CodecMimeType} disabledCodec codec that needs to be disabled.\n     */\n    _selectPreferredCodec(mediaSession = null, preferredCodec = null, disabledCodec = null) {\n        const session = mediaSession ? mediaSession : this.conference.jvbJingleSession;\n        const currentCodec = preferredCodec ? preferredCodec : this.jvbPreferredCodec;\n        let selectedCodec = currentCodec;\n\n        if (session && !session.isP2P && !this.options.enforcePreferredCodec) {\n            const remoteParticipants = this.conference.getParticipants().map(participant => participant.getId());\n\n            for (const remote of remoteParticipants) {\n                const peerMediaInfo = session._signalingLayer.getPeerMediaInfo(remote, MediaType.VIDEO);\n                const peerCodec = peerMediaInfo?.codecType;\n\n                if (peerCodec\n                    && peerCodec !== currentCodec\n                    && (peerCodec !== CodecMimeType.VP9 || browser.supportsVP9())) {\n                    selectedCodec = peerCodec;\n                }\n            }\n        }\n        session && session.setVideoCodecs(selectedCodec, disabledCodec);\n    }\n\n    /**\n     * Returns the preferred codec for the conference. The preferred codec for the JVB media session\n     * is the one that gets published in presence and a comparision is made whenever a participant joins\n     * or leaves the call.\n     *\n     * @returns {CodecMimeType} preferred codec.\n     */\n    getPreferredCodec() {\n        return this.jvbPreferredCodec;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createBridgeChannelClosedEvent } from '../../service/statistics/AnalyticsEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = getLogger(__filename);\n\n/**\n * Handles a WebRTC RTCPeerConnection or a WebSocket instance to communicate\n * with the videobridge.\n */\nexport default class BridgeChannel {\n    /**\n     * Binds \"ondatachannel\" event listener on the given RTCPeerConnection\n     * instance, or creates a WebSocket connection with the videobridge.\n     * At least one of both, peerconnection or wsUrl parameters, must be\n     * given.\n     * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection\n     * instance.\n     * @param {string} [wsUrl] WebSocket URL.\n     * @param {EventEmitter} emitter the EventEmitter instance to use for event emission.\n     */\n    constructor(peerconnection, wsUrl, emitter) {\n        if (!peerconnection && !wsUrl) {\n            throw new TypeError('At least peerconnection or wsUrl must be given');\n        } else if (peerconnection && wsUrl) {\n            throw new TypeError('Just one of peerconnection or wsUrl must be given');\n        }\n\n        if (peerconnection) {\n            logger.debug('constructor() with peerconnection');\n        } else {\n            logger.debug(`constructor() with wsUrl:\"${wsUrl}\"`);\n        }\n\n        // The underlying WebRTC RTCDataChannel or WebSocket instance.\n        // @type {RTCDataChannel|WebSocket}\n        this._channel = null;\n\n        // @type {EventEmitter}\n        this._eventEmitter = emitter;\n\n        // Whether a RTCDataChannel or WebSocket is internally used.\n        // @type {string} \"datachannel\" / \"websocket\"\n        this._mode = null;\n\n        // Indicates whether the connection retries are enabled or not.\n        this._areRetriesEnabled = false;\n\n        // Indicates whether the connection was closed from the client or not.\n        this._closedFromClient = false;\n\n        // If a RTCPeerConnection is given, listen for new RTCDataChannel\n        // event.\n        if (peerconnection) {\n            const datachannel\n                = peerconnection.createDataChannel(\n                    'JVB data channel', {\n                        protocol: 'http://jitsi.org/protocols/colibri'\n                    });\n\n            // Handle the RTCDataChannel.\n            this._handleChannel(datachannel);\n            this._mode = 'datachannel';\n\n        // Otherwise create a WebSocket connection.\n        } else if (wsUrl) {\n            this._areRetriesEnabled = true;\n            this._wsUrl = wsUrl;\n            this._initWebSocket();\n        }\n    }\n\n    /**\n     * Initializes the web socket channel.\n     *\n     * @returns {void}\n     */\n    _initWebSocket() {\n        // Create a WebSocket instance.\n        const ws = new WebSocket(this._wsUrl);\n\n        // Handle the WebSocket.\n        this._handleChannel(ws);\n        this._mode = 'websocket';\n    }\n\n    /**\n     * Starts the websocket connection retries.\n     *\n     * @returns {void}\n     */\n    _startConnectionRetries() {\n        let timeoutS = 1;\n\n        const reload = () => {\n            if (this.isOpen()) {\n                return;\n            }\n            this._initWebSocket(this._wsUrl);\n            timeoutS = Math.min(timeoutS * 2, 60);\n            this._retryTimeout = setTimeout(reload, timeoutS * 1000);\n        };\n\n        this._retryTimeout = setTimeout(reload, timeoutS * 1000);\n    }\n\n    /**\n     * Stops the websocket connection retries.\n     *\n     * @returns {void}\n     */\n    _stopConnectionRetries() {\n        if (this._retryTimeout) {\n            clearTimeout(this._retryTimeout);\n            this._retryTimeout = undefined;\n        }\n    }\n\n    /**\n     * Retries to establish the websocket connection after the connection was closed by the server.\n     *\n     * @param {CloseEvent} closeEvent - The close event that triggered the retries.\n     * @returns {void}\n     */\n    _retryWebSocketConnection(closeEvent) {\n        if (!this._areRetriesEnabled) {\n            return;\n        }\n        const { code, reason } = closeEvent;\n\n        Statistics.sendAnalytics(createBridgeChannelClosedEvent(code, reason));\n        this._areRetriesEnabled = false;\n        this._eventEmitter.once(RTCEvents.DATA_CHANNEL_OPEN, () => {\n            this._stopConnectionRetries();\n            this._areRetriesEnabled = true;\n        });\n        this._startConnectionRetries();\n    }\n\n    /**\n     * The channel mode.\n     * @return {string} \"datachannel\" or \"websocket\" (or null if not yet set).\n     */\n    get mode() {\n        return this._mode;\n    }\n\n    /**\n     * Closes the currently opened channel.\n     */\n    close() {\n        this._closedFromClient = true;\n        this._stopConnectionRetries();\n        this._areRetriesEnabled = false;\n        if (this._channel) {\n            try {\n                this._channel.close();\n            } catch (error) {} // eslint-disable-line no-empty\n\n            this._channel = null;\n        }\n    }\n\n    /**\n     * Whether there is an underlying RTCDataChannel or WebSocket and it's\n     * open.\n     * @return {boolean}\n     */\n    isOpen() {\n        return this._channel && (this._channel.readyState === 'open'\n            || this._channel.readyState === WebSocket.OPEN);\n    }\n\n    /**\n     * Sends local stats via the bridge channel.\n     * @param {Object} payload The payload of the message.\n     * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n     */\n    sendEndpointStatsMessage(payload) {\n        this._send({\n            colibriClass: 'EndpointStats',\n            ...payload\n        });\n    }\n\n    /**\n     * Sends message via the channel.\n     * @param {string} to The id of the endpoint that should receive the\n     * message. If \"\" the message will be sent to all participants.\n     * @param  {object} payload The payload of the message.\n     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n     * or from WebSocket#send or Error with \"No opened channel\" message.\n     */\n    sendMessage(to, payload) {\n        this._send({\n            colibriClass: 'EndpointMessage',\n            msgPayload: payload,\n            to\n        });\n    }\n\n    /**\n     * Sends a \"lastN value changed\" message via the channel.\n     * @param {number} value The new value for lastN. -1 means unlimited.\n     */\n    sendSetLastNMessage(value) {\n        logger.log(`Sending lastN=${value}.`);\n\n        this._send({\n            colibriClass: 'LastNChangedEvent',\n            lastN: value\n        });\n    }\n\n    /**\n     * Sends a \"selected endpoints changed\" message via the channel.\n     *\n     * @param {Array<string>} endpointIds - The ids of the selected endpoints.\n     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n     * or from WebSocket#send or Error with \"No opened channel\" message.\n     */\n    sendSelectedEndpointsMessage(endpointIds) {\n        logger.log(`Sending selected endpoints: ${endpointIds}.`);\n\n        this._send({\n            colibriClass: 'SelectedEndpointsChangedEvent',\n            selectedEndpoints: endpointIds\n        });\n    }\n\n    /**\n     * Sends a \"receiver video constraint\" message via the channel.\n     * @param {Number} maxFrameHeightPixels the maximum frame height,\n     * in pixels, this receiver is willing to receive\n     */\n    sendReceiverVideoConstraintMessage(maxFrameHeightPixels) {\n        logger.log(`Sending ReceiverVideoConstraint with maxFrameHeight=${maxFrameHeightPixels}px`);\n        this._send({\n            colibriClass: 'ReceiverVideoConstraint',\n            maxFrameHeight: maxFrameHeightPixels\n        });\n    }\n\n    /**\n     * Sends a 'ReceiverVideoConstraints' message via the bridge channel.\n     *\n     * @param {ReceiverVideoConstraints} constraints video constraints.\n     */\n    sendNewReceiverVideoConstraintsMessage(constraints) {\n        logger.log(`Sending ReceiverVideoConstraints with ${JSON.stringify(constraints)}`);\n        this._send({\n            colibriClass: 'ReceiverVideoConstraints',\n            ...constraints\n        });\n    }\n\n    /**\n     * Sends a 'VideoTypeMessage' message via the bridge channel.\n     *\n     * @param {string} videoType 'camera', 'desktop' or 'none'.\n     * @deprecated to be replaced with sendSourceVideoTypeMessage\n     */\n    sendVideoTypeMessage(videoType) {\n        logger.debug(`Sending VideoTypeMessage with video type as ${videoType}`);\n        this._send({\n            colibriClass: 'VideoTypeMessage',\n            videoType\n        });\n    }\n\n    /**\n     * Sends a 'VideoTypeMessage' message via the bridge channel.\n     *\n     * @param {BridgeVideoType} videoType - the video type.\n     * @param {SourceName} sourceName - the source name of the video track.\n     * @returns {void}\n     */\n    sendSourceVideoTypeMessage(sourceName, videoType) {\n        logger.info(`Sending SourceVideoTypeMessage with video type ${sourceName}: ${videoType}`);\n        this._send({\n            colibriClass: 'SourceVideoTypeMessage',\n            sourceName,\n            videoType\n        });\n    }\n\n    /**\n     * Set events on the given RTCDataChannel or WebSocket instance.\n     */\n    _handleChannel(channel) {\n        const emitter = this._eventEmitter;\n\n        channel.onopen = () => {\n            logger.info(`${this._mode} channel opened`);\n\n            // Code sample for sending string and/or binary data.\n            // Sends string message to the bridge:\n            //     channel.send(\"Hello bridge!\");\n            // Sends 12 bytes binary message to the bridge:\n            //     channel.send(new ArrayBuffer(12));\n\n            emitter.emit(RTCEvents.DATA_CHANNEL_OPEN);\n        };\n\n        channel.onerror = event => {\n            // WS error events contain no information about the failure (this is available in the onclose event) and\n            // the event references the WS object itself, which causes hangs on mobile.\n            if (this._mode !== 'websocket') {\n                logger.error(`Channel error: ${event.message}`);\n            }\n        };\n\n        channel.onmessage = ({ data }) => {\n            // JSON object.\n            let obj;\n\n            try {\n                obj = JSON.parse(data);\n            } catch (error) {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error('Failed to parse channel message as JSON: ', data, error);\n\n                return;\n            }\n\n            const colibriClass = obj.colibriClass;\n\n            switch (colibriClass) {\n            case 'DominantSpeakerEndpointChangeEvent': {\n                const { dominantSpeakerEndpoint, previousSpeakers = [] } = obj;\n\n                logger.debug(`Dominant speaker: ${dominantSpeakerEndpoint}, previous speakers: ${previousSpeakers}`);\n                emitter.emit(RTCEvents.DOMINANT_SPEAKER_CHANGED, dominantSpeakerEndpoint, previousSpeakers);\n                break;\n            }\n            case 'EndpointConnectivityStatusChangeEvent': {\n                const endpoint = obj.endpoint;\n                const isActive = obj.active === 'true';\n\n                logger.info(`Endpoint connection status changed: ${endpoint} active=${isActive}`);\n                emitter.emit(RTCEvents.ENDPOINT_CONN_STATUS_CHANGED, endpoint, isActive);\n\n                break;\n            }\n            case 'EndpointMessage': {\n                emitter.emit(RTCEvents.ENDPOINT_MESSAGE_RECEIVED, obj.from, obj.msgPayload);\n\n                break;\n            }\n            case 'EndpointStats': {\n                emitter.emit(RTCEvents.ENDPOINT_STATS_RECEIVED, obj.from, obj);\n\n                break;\n            }\n            case 'LastNEndpointsChangeEvent': {\n                if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n                    // The new/latest list of last-n endpoint IDs (i.e. endpoints for which the bridge is sending\n                    // video).\n                    const lastNEndpoints = obj.lastNEndpoints;\n\n                    logger.info(`New forwarded endpoints: ${lastNEndpoints}`);\n                    emitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED, lastNEndpoints);\n                }\n\n                break;\n            }\n            case 'ForwardedSources': {\n                if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                    // The new/latest list of forwarded sources\n                    const forwardedSources = obj.forwardedSources;\n\n                    logger.info(`New forwarded sources: ${forwardedSources}`);\n                    emitter.emit(RTCEvents.FORWARDED_SOURCES_CHANGED, forwardedSources);\n                }\n\n                break;\n            }\n            case 'SenderVideoConstraints': {\n                const videoConstraints = obj.videoConstraints;\n\n                if (videoConstraints) {\n                    logger.info(`SenderVideoConstraints: ${JSON.stringify(videoConstraints)}`);\n                    emitter.emit(RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED, videoConstraints);\n                }\n                break;\n            }\n            case 'SenderSourceConstraints': {\n                if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                    const { sourceName, maxHeight } = obj;\n\n                    if (typeof sourceName === 'string' && typeof maxHeight === 'number') {\n                        // eslint-disable-next-line object-property-newline\n                        logger.info(`SenderSourceConstraints: ${JSON.stringify({ sourceName, maxHeight })}`);\n                        emitter.emit(\n                            RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED, {\n                                sourceName,\n                                maxHeight\n                            }\n                        );\n                    } else {\n                        logger.error(`Invalid SenderSourceConstraints: ${JSON.stringify(obj)}`);\n                    }\n                }\n                break;\n            }\n            case 'ServerHello': {\n                logger.info(`Received ServerHello, version=${obj.version}.`);\n                break;\n            }\n            default: {\n                logger.debug('Channel JSON-formatted message: ', obj);\n\n                // The received message appears to be appropriately formatted\n                // (i.e. is a JSON object which assigns a value to the\n                // mandatory property colibriClass) so don't just swallow it,\n                // expose it to public consumption.\n                emitter.emit(`rtc.datachannel.${colibriClass}`, obj);\n            }\n            }\n        };\n\n        channel.onclose = event => {\n            logger.info(`Channel closed by ${this._closedFromClient ? 'client' : 'server'}`);\n\n            if (this._mode === 'websocket') {\n                if (!this._closedFromClient) {\n                    logger.error(`Channel closed: ${event.code} ${event.reason}`);\n                    this._retryWebSocketConnection(event);\n                }\n            }\n\n            // Remove the channel.\n            this._channel = null;\n        };\n\n        // Store the channel.\n        this._channel = channel;\n    }\n\n    /**\n     * Sends passed object via the channel.\n     * @param {object} jsonObject The object that will be sent.\n     * @throws NetworkError or InvalidStateError from RTCDataChannel#send (@see\n     * {@link https://developer.mozilla.org/docs/Web/API/RTCDataChannel/send})\n     * or from WebSocket#send or Error with \"No opened channel\" message.\n     */\n    _send(jsonObject) {\n        const channel = this._channel;\n\n        if (!this.isOpen()) {\n            logger.error('Bridge Channel send: no opened channel.');\n            throw new Error('No opened channel');\n        }\n\n        channel.send(JSON.stringify(jsonObject));\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\nimport clonedeep from 'lodash.clonedeep';\n\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport CameraFacingMode from '../../service/RTC/CameraFacingMode';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport Resolutions from '../../service/RTC/Resolutions';\nimport VideoType from '../../service/RTC/VideoType';\nimport { AVAILABLE_DEVICE } from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport SDPUtil from '../sdp/SDPUtil';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\n\nimport screenObtainer from './ScreenObtainer';\n\nconst logger = getLogger(__filename);\n\n// Require adapter only for certain browsers. This is being done for\n// react-native, which has its own shims, and while browsers are being migrated\n// over to use adapter's shims.\nif (browser.usesAdapter()) {\n    require('webrtc-adapter');\n}\n\nconst eventEmitter = new EventEmitter();\n\nconst AVAILABLE_DEVICES_POLL_INTERVAL_TIME = 3000; // ms\n\n/**\n * Default MediaStreamConstraints to use for calls to getUserMedia.\n *\n * @private\n */\nconst DEFAULT_CONSTRAINTS = {\n    video: {\n        height: {\n            ideal: 720,\n            max: 720,\n            min: 180\n        },\n        width: {\n            ideal: 1280,\n            max: 1280,\n            min: 320\n        }\n    }\n};\n\n// Currently audio output device change is supported only in Chrome and\n// default output always has 'default' device ID\nlet audioOutputDeviceId = 'default'; // default device\n// whether user has explicitly set a device to use\nlet audioOutputChanged = false;\n\n// Disables all audio processing\nlet disableAP = false;\n\n// Disables Acoustic Echo Cancellation\nlet disableAEC = false;\n\n// Disables Noise Suppression\nlet disableNS = false;\n\n// Disables Automatic Gain Control\nlet disableAGC = false;\n\n// Enables stereo.\nlet stereo = null;\n\nconst featureDetectionAudioEl = document.createElement('audio');\nconst isAudioOutputDeviceChangeAvailable\n    = typeof featureDetectionAudioEl.setSinkId !== 'undefined';\n\nlet availableDevices = [];\nlet availableDevicesPollTimer;\n\n/**\n * An empty function.\n */\nfunction emptyFuncton() {\n    // no-op\n}\n\n/**\n * Creates a constraints object to be passed into a call to getUserMedia.\n *\n * @param {Array} um - An array of user media types to get. The accepted types are \"video\", \"audio\", and \"desktop.\"\n * @param {Object} options - Various values to be added to the constraints.\n * @param {string} options.cameraDeviceId - The device id for the video capture device to get video from.\n * @param {Object} options.constraints - Default constraints object to use as a base for the returned constraints.\n * @param {Object} options.desktopStream - The desktop source id from which to capture a desktop sharing video.\n * @param {string} options.facingMode - Which direction the camera is pointing to (applicable on mobile)\n * @param {string} options.micDeviceId - The device id for the audio capture device to get audio from.\n * @private\n * @returns {Object}\n */\nfunction getConstraints(um = [], options = {}) {\n    // Create a deep copy of the constraints to avoid any modification of\n    // the passed in constraints object.\n    const constraints = clonedeep(options.constraints || DEFAULT_CONSTRAINTS);\n\n    if (um.indexOf('video') >= 0) {\n        // The \"resolution\" option is a shortcut and takes precendence.\n        if (Resolutions[options.resolution]) {\n            const r = Resolutions[options.resolution];\n\n            constraints.video.height = { ideal: r.height };\n            constraints.video.width = { ideal: r.width };\n        }\n\n        if (!constraints.video) {\n            constraints.video = {};\n        }\n\n        // Override the constraints on Safari because of the following webkit bug.\n        // https://bugs.webkit.org/show_bug.cgi?id=210932\n        // Camera doesn't start on older macOS versions if min/max constraints are specified.\n        // TODO: remove this hack when the bug fix is available on Mojave, Sierra and High Sierra.\n        if (browser.isWebKitBased()) {\n            if (constraints.video.height && constraints.video.height.ideal) {\n                constraints.video.height = { ideal: constraints.video.height.ideal };\n            } else {\n                logger.warn('Ideal camera height missing, camera may not start properly');\n            }\n            if (constraints.video.width && constraints.video.width.ideal) {\n                constraints.video.width = { ideal: constraints.video.width.ideal };\n            } else {\n                logger.warn('Ideal camera width missing, camera may not start properly');\n            }\n        }\n        if (options.cameraDeviceId) {\n            constraints.video.deviceId = options.cameraDeviceId;\n        } else {\n            const facingMode = options.facingMode || CameraFacingMode.USER;\n\n            constraints.video.facingMode = facingMode;\n        }\n    } else {\n        constraints.video = false;\n    }\n\n    if (um.indexOf('audio') >= 0) {\n        if (!constraints.audio || typeof constraints.audio === 'boolean') {\n            constraints.audio = {};\n        }\n\n        constraints.audio = {\n            autoGainControl: !disableAGC && !disableAP,\n            deviceId: options.micDeviceId,\n            echoCancellation: !disableAEC && !disableAP,\n            noiseSuppression: !disableNS && !disableAP\n        };\n\n        if (stereo) {\n            Object.assign(constraints.audio, { channelCount: 2 });\n        }\n    } else {\n        constraints.audio = false;\n    }\n\n    return constraints;\n}\n\n/**\n * Updates the granted permissions based on the options we requested and the\n * streams we received.\n * @param um the options we requested to getUserMedia.\n * @param stream the stream we received from calling getUserMedia.\n */\nfunction updateGrantedPermissions(um, stream) {\n    const audioTracksReceived\n        = Boolean(stream) && stream.getAudioTracks().length > 0;\n    const videoTracksReceived\n        = Boolean(stream) && stream.getVideoTracks().length > 0;\n    const grantedPermissions = {};\n\n    if (um.indexOf('video') !== -1) {\n        grantedPermissions.video = videoTracksReceived;\n    }\n    if (um.indexOf('audio') !== -1) {\n        grantedPermissions.audio = audioTracksReceived;\n    }\n\n    eventEmitter.emit(RTCEvents.PERMISSIONS_CHANGED, grantedPermissions);\n}\n\n/**\n * Checks if new list of available media devices differs from previous one.\n * @param {MediaDeviceInfo[]} newDevices - list of new devices.\n * @returns {boolean} - true if list is different, false otherwise.\n */\nfunction compareAvailableMediaDevices(newDevices) {\n    if (newDevices.length !== availableDevices.length) {\n        return true;\n    }\n\n    /* eslint-disable newline-per-chained-call */\n\n    return (\n        newDevices.map(mediaDeviceInfoToJSON).sort().join('')\n            !== availableDevices\n                .map(mediaDeviceInfoToJSON).sort().join(''));\n\n    /* eslint-enable newline-per-chained-call */\n\n    /**\n     *\n     * @param info\n     */\n    function mediaDeviceInfoToJSON(info) {\n        return JSON.stringify({\n            kind: info.kind,\n            deviceId: info.deviceId,\n            groupId: info.groupId,\n            label: info.label,\n            facing: info.facing\n        });\n    }\n}\n\n/**\n * Sends analytics event with the passed device list.\n *\n * @param {Array<MediaDeviceInfo>} deviceList - List with info about the\n * available devices.\n * @returns {void}\n */\nfunction sendDeviceListToAnalytics(deviceList) {\n    const audioInputDeviceCount\n        = deviceList.filter(d => d.kind === 'audioinput').length;\n    const audioOutputDeviceCount\n        = deviceList.filter(d => d.kind === 'audiooutput').length;\n    const videoInputDeviceCount\n        = deviceList.filter(d => d.kind === 'videoinput').length;\n    const videoOutputDeviceCount\n        = deviceList.filter(d => d.kind === 'videooutput').length;\n\n    deviceList.forEach(device => {\n        const attributes = {\n            'audio_input_device_count': audioInputDeviceCount,\n            'audio_output_device_count': audioOutputDeviceCount,\n            'video_input_device_count': videoInputDeviceCount,\n            'video_output_device_count': videoOutputDeviceCount,\n            'device_id': device.deviceId,\n            'device_group_id': device.groupId,\n            'device_kind': device.kind,\n            'device_label': device.label\n        };\n\n        Statistics.sendAnalytics(AVAILABLE_DEVICE, attributes);\n    });\n}\n\n\n/**\n * Update known devices.\n *\n * @param {Array<Object>} pds - The new devices.\n * @returns {void}\n *\n * NOTE: Use this function as a shared callback to handle both the devicechange event  and the polling implementations.\n * This prevents duplication and works around a chrome bug (verified to occur on 68) where devicechange fires twice in\n * a row, which can cause async post devicechange processing to collide.\n */\nfunction updateKnownDevices(pds) {\n    if (compareAvailableMediaDevices(pds)) {\n        onMediaDevicesListChanged(pds);\n    }\n}\n\n/**\n * Event handler for the 'devicechange' event.\n *\n * @param {MediaDeviceInfo[]} devices - list of media devices.\n * @emits RTCEvents.DEVICE_LIST_CHANGED\n */\nfunction onMediaDevicesListChanged(devicesReceived) {\n    availableDevices = devicesReceived.slice(0);\n    logger.info('list of media devices has changed:', availableDevices);\n\n    sendDeviceListToAnalytics(availableDevices);\n\n    // Used by tracks to update the real device id before the consumer of lib-jitsi-meet receives the new device list.\n    eventEmitter.emit(RTCEvents.DEVICE_LIST_WILL_CHANGE, availableDevices);\n\n    eventEmitter.emit(RTCEvents.DEVICE_LIST_CHANGED, availableDevices);\n}\n\n/**\n *\n */\nclass RTCUtils extends Listenable {\n    /**\n     *\n     */\n    constructor() {\n        super(eventEmitter);\n    }\n\n    /**\n     * Depending on the browser, sets difference instance methods for\n     * interacting with user media and adds methods to native WebRTC-related\n     * objects. Also creates an instance variable for peer connection\n     * constraints.\n     *\n     * @param {Object} options\n     * @returns {void}\n     */\n    init(options = {}) {\n        if (typeof options.disableAEC === 'boolean') {\n            disableAEC = options.disableAEC;\n            logger.info(`Disable AEC: ${disableAEC}`);\n        }\n        if (typeof options.disableNS === 'boolean') {\n            disableNS = options.disableNS;\n            logger.info(`Disable NS: ${disableNS}`);\n        }\n        if (typeof options.disableAP === 'boolean') {\n            disableAP = options.disableAP;\n            logger.info(`Disable AP: ${disableAP}`);\n        }\n        if (typeof options.disableAGC === 'boolean') {\n            disableAGC = options.disableAGC;\n            logger.info(`Disable AGC: ${disableAGC}`);\n        }\n        if (typeof options.audioQuality?.stereo === 'boolean') {\n            stereo = options.audioQuality.stereo;\n            logger.info(`Stereo: ${stereo}`);\n        }\n\n        window.clearInterval(availableDevicesPollTimer);\n        availableDevicesPollTimer = undefined;\n\n        if (browser.isReactNative()) {\n            this.RTCPeerConnectionType = RTCPeerConnection;\n\n            this.attachMediaStream = undefined; // Unused on React Native.\n\n            this.getStreamID = function({ id }) {\n                // The react-native-webrtc implementation that we use at the\n                // time of this writing returns a number for the id of\n                // MediaStream. Let's just say that a number contains no special\n                // characters.\n                return (\n                    typeof id === 'number'\n                        ? id\n                        : SDPUtil.filterSpecialChars(id));\n            };\n            this.getTrackID = ({ id }) => id;\n        } else {\n            this.RTCPeerConnectionType = RTCPeerConnection;\n\n            this.attachMediaStream\n                = wrapAttachMediaStream((element, stream) => {\n                    if (element) {\n                        element.srcObject = stream;\n                    }\n                });\n\n            this.getStreamID = ({ id }) => id;\n            this.getTrackID = ({ id }) => id;\n        }\n\n        this.pcConstraints = browser.isChromiumBased() || browser.isReactNative()\n            ? { optional: [\n                { googScreencastMinBitrate: 100 },\n                { googCpuOveruseDetection: true }\n            ] }\n            : {};\n\n        screenObtainer.init(options);\n\n        if (this.isDeviceListAvailable()) {\n            this.enumerateDevices(ds => {\n                availableDevices = ds.slice(0);\n\n                logger.debug('Available devices: ', availableDevices);\n                sendDeviceListToAnalytics(availableDevices);\n\n                eventEmitter.emit(\n                    RTCEvents.DEVICE_LIST_AVAILABLE,\n                    availableDevices);\n\n                if (browser.supportsDeviceChangeEvent()) {\n                    navigator.mediaDevices.addEventListener(\n                        'devicechange',\n                        () => this.enumerateDevices(emptyFuncton));\n                } else {\n                    // Periodically poll enumerateDevices() method to check if\n                    // list of media devices has changed.\n                    availableDevicesPollTimer = window.setInterval(\n                        () => this.enumerateDevices(emptyFuncton),\n                        AVAILABLE_DEVICES_POLL_INTERVAL_TIME);\n                }\n            });\n        }\n    }\n\n    /**\n     *\n     * @param {Function} callback\n     */\n    enumerateDevices(callback) {\n        navigator.mediaDevices.enumerateDevices()\n            .then(devices => {\n                updateKnownDevices(devices);\n                callback(devices);\n            })\n            .catch(error => {\n                logger.warn(`Failed to  enumerate devices. ${error}`);\n                updateKnownDevices([]);\n                callback([]);\n            });\n    }\n\n    /**\n     * Acquires a media stream via getUserMedia that\n     * matches the given constraints\n     *\n     * @param {array} umDevices which devices to acquire (e.g. audio, video)\n     * @param {Object} constraints - Stream specifications to use.\n     * @param {number} timeout - The timeout in ms for GUM.\n     * @returns {Promise}\n     */\n    _getUserMedia(umDevices, constraints = {}, timeout = 0) {\n        return new Promise((resolve, reject) => {\n            let gumTimeout, timeoutExpired = false;\n\n            if (typeof timeout === 'number' && !isNaN(timeout) && timeout > 0) {\n                gumTimeout = setTimeout(() => {\n                    timeoutExpired = true;\n                    gumTimeout = undefined;\n                    reject(new JitsiTrackError(JitsiTrackErrors.TIMEOUT));\n                }, timeout);\n            }\n\n            navigator.mediaDevices.getUserMedia(constraints)\n                .then(stream => {\n                    logger.log('onUserMediaSuccess');\n                    updateGrantedPermissions(umDevices, stream);\n                    if (!timeoutExpired) {\n                        if (typeof gumTimeout !== 'undefined') {\n                            clearTimeout(gumTimeout);\n                        }\n                        resolve(stream);\n                    }\n                })\n                .catch(error => {\n                    logger.warn(`Failed to get access to local media. ${error} ${JSON.stringify(constraints)}`);\n                    const jitsiError = new JitsiTrackError(error, constraints, umDevices);\n\n                    if (!timeoutExpired) {\n                        if (typeof gumTimeout !== 'undefined') {\n                            clearTimeout(gumTimeout);\n                        }\n                        reject(jitsiError);\n                    }\n\n                    if (jitsiError.name === JitsiTrackErrors.PERMISSION_DENIED) {\n                        updateGrantedPermissions(umDevices, undefined);\n                    }\n\n                    // else {\n                    // Probably the error is not caused by the lack of permissions and we don't need to update them.\n                    // }\n                });\n        });\n    }\n\n    /**\n     * Acquire a display stream via the screenObtainer. This requires extra\n     * logic compared to use screenObtainer versus normal device capture logic\n     * in RTCUtils#_getUserMedia.\n     *\n     * @returns {Promise} A promise which will be resolved with an object which\n     * contains the acquired display stream. If desktop sharing is not supported\n     * then a rejected promise will be returned.\n     */\n    _getDesktopMedia() {\n        if (!screenObtainer.isSupported()) {\n            return Promise.reject(new Error('Desktop sharing is not supported!'));\n        }\n\n        return new Promise((resolve, reject) => {\n            screenObtainer.obtainStream(\n                stream => {\n                    resolve(stream);\n                },\n                error => {\n                    reject(error);\n                });\n        });\n    }\n\n    /**\n     * Private utility for determining if the passed in MediaStream contains\n     * tracks of the type(s) specified in the requested devices.\n     *\n     * @param {string[]} requestedDevices - The track types that are expected to\n     * be includes in the stream.\n     * @param {MediaStream} stream - The MediaStream to check if it has the\n     * expected track types.\n     * @returns {string[]} An array of string with the missing track types. The\n     * array will be empty if all requestedDevices are found in the stream.\n     */\n    _getMissingTracks(requestedDevices = [], stream) {\n        const missingDevices = [];\n\n        const audioDeviceRequested = requestedDevices.includes('audio');\n        const audioTracksReceived\n            = stream && stream.getAudioTracks().length > 0;\n\n        if (audioDeviceRequested && !audioTracksReceived) {\n            missingDevices.push('audio');\n        }\n\n        const videoDeviceRequested = requestedDevices.includes('video');\n        const videoTracksReceived\n            = stream && stream.getVideoTracks().length > 0;\n\n        if (videoDeviceRequested && !videoTracksReceived) {\n            missingDevices.push('video');\n        }\n\n        return missingDevices;\n    }\n\n    /**\n     * Gets streams from specified device types. This function intentionally\n     * ignores errors for upstream to catch and handle instead.\n     *\n     * @param {Object} options - A hash describing what devices to get and\n     * relevant constraints.\n     * @param {string[]} options.devices - The types of media to capture. Valid\n     * values are \"desktop\", \"audio\", and \"video\".\n     * @param {Object} options.desktopSharingFrameRate\n     * @param {Object} options.desktopSharingFrameRate.min - Minimum fps\n     * @param {Object} options.desktopSharingFrameRate.max - Maximum fps\n     * @param {String} options.desktopSharingSourceDevice - The device id or\n     * label for a video input source that should be used for screensharing.\n     * @returns {Promise} The promise, when successful, will return an array of\n     * meta data for the requested device type, which includes the stream and\n     * track. If an error occurs, it will be deferred to the caller for\n     * handling.\n     */\n    obtainAudioAndVideoPermissions(options) {\n        const {\n            timeout,\n            ...otherOptions\n        } = options;\n\n        const mediaStreamsMetaData = [];\n\n        // Declare private functions to be used in the promise chain below.\n        // These functions are declared in the scope of this function because\n        // they are not being used anywhere else, so only this function needs to\n        // know about them.\n\n        /**\n         * Executes a request for desktop media if specified in options.\n         *\n         * @returns {Promise}\n         */\n        const maybeRequestDesktopDevice = function() {\n            const umDevices = otherOptions.devices || [];\n            const isDesktopDeviceRequested\n                = umDevices.indexOf('desktop') !== -1;\n\n            if (!isDesktopDeviceRequested) {\n                return Promise.resolve();\n            }\n\n            const {\n                desktopSharingSourceDevice\n            } = otherOptions;\n\n            // Attempt to use a video input device as a screenshare source if\n            // the option is defined.\n            if (desktopSharingSourceDevice) {\n                const matchingDevice\n                    = availableDevices && availableDevices.find(device =>\n                        device.kind === 'videoinput'\n                            && (device.deviceId === desktopSharingSourceDevice\n                            || device.label === desktopSharingSourceDevice));\n\n                if (!matchingDevice) {\n                    return Promise.reject(new JitsiTrackError(\n                        { name: 'ConstraintNotSatisfiedError' },\n                        {},\n                        [ desktopSharingSourceDevice ]\n                    ));\n                }\n\n                const requestedDevices = [ 'video' ];\n                const constraints = {\n                    video: {\n                        deviceId: matchingDevice.deviceId\n\n                        // frameRate is omited here on purpose since this is a device that we'll pretend is a screen.\n                    }\n                };\n\n                return this._getUserMedia(requestedDevices, constraints, timeout)\n                    .then(stream => {\n                        return {\n                            sourceType: 'device',\n                            stream\n                        };\n                    });\n            }\n\n            return this._getDesktopMedia();\n        }.bind(this);\n\n        /**\n         * Creates a meta data object about the passed in desktopStream and\n         * pushes the meta data to the internal array mediaStreamsMetaData to be\n         * returned later.\n         *\n         * @param {MediaStreamTrack} desktopStream - A track for a desktop\n         * capture.\n         * @returns {void}\n         */\n        const maybeCreateAndAddDesktopTrack = function(desktopStream) {\n            if (!desktopStream) {\n                return;\n            }\n\n            const { stream, sourceId, sourceType } = desktopStream;\n\n            const desktopAudioTracks = stream.getAudioTracks();\n\n            if (desktopAudioTracks.length) {\n                const desktopAudioStream = new MediaStream(desktopAudioTracks);\n\n                mediaStreamsMetaData.push({\n                    stream: desktopAudioStream,\n                    sourceId,\n                    sourceType,\n                    track: desktopAudioStream.getAudioTracks()[0]\n                });\n            }\n\n            const desktopVideoTracks = stream.getVideoTracks();\n\n            if (desktopVideoTracks.length) {\n                const desktopVideoStream = new MediaStream(desktopVideoTracks);\n\n                mediaStreamsMetaData.push({\n                    stream: desktopVideoStream,\n                    sourceId,\n                    sourceType,\n                    track: desktopVideoStream.getVideoTracks()[0],\n                    videoType: VideoType.DESKTOP\n                });\n            }\n        };\n\n        /**\n         * Executes a request for audio and/or video, as specified in options.\n         * By default both audio and video will be captured if options.devices\n         * is not defined.\n         *\n         * @returns {Promise}\n         */\n        const maybeRequestCaptureDevices = function() {\n            const umDevices = otherOptions.devices || [ 'audio', 'video' ];\n            const requestedCaptureDevices = umDevices.filter(device => device === 'audio' || device === 'video');\n\n            if (!requestedCaptureDevices.length) {\n                return Promise.resolve();\n            }\n\n            const constraints = getConstraints(requestedCaptureDevices, otherOptions);\n\n            logger.info('Got media constraints: ', JSON.stringify(constraints));\n\n            return this._getUserMedia(requestedCaptureDevices, constraints, timeout);\n        }.bind(this);\n\n        /**\n         * Splits the passed in media stream into separate audio and video\n         * streams and creates meta data objects for each and pushes them to the\n         * internal array mediaStreamsMetaData to be returned later.\n         *\n         * @param {MediaStreamTrack} avStream - A track for with audio and/or\n         * video track.\n         * @returns {void}\n         */\n        const maybeCreateAndAddAVTracks = function(avStream) {\n            if (!avStream) {\n                return;\n            }\n\n            const audioTracks = avStream.getAudioTracks();\n\n            if (audioTracks.length) {\n                const audioStream = new MediaStream(audioTracks);\n\n                mediaStreamsMetaData.push({\n                    stream: audioStream,\n                    track: audioStream.getAudioTracks()[0],\n                    effects: otherOptions.effects\n                });\n            }\n\n            const videoTracks = avStream.getVideoTracks();\n\n            if (videoTracks.length) {\n                const videoStream = new MediaStream(videoTracks);\n\n                mediaStreamsMetaData.push({\n                    stream: videoStream,\n                    track: videoStream.getVideoTracks()[0],\n                    videoType: VideoType.CAMERA,\n                    effects: otherOptions.effects\n                });\n            }\n        };\n\n        return maybeRequestDesktopDevice()\n            .then(maybeCreateAndAddDesktopTrack)\n            .then(maybeRequestCaptureDevices)\n            .then(maybeCreateAndAddAVTracks)\n            .then(() => mediaStreamsMetaData)\n            .catch(error => {\n                mediaStreamsMetaData.forEach(({ stream }) => {\n                    this.stopMediaStream(stream);\n                });\n\n                return Promise.reject(error);\n            });\n    }\n\n    /**\n     * Checks whether it is possible to enumerate available cameras/microphones.\n     *\n     * @returns {boolean} {@code true} if the device listing is available;\n     * {@code false}, otherwise.\n     */\n    isDeviceListAvailable() {\n        return Boolean(\n            navigator.mediaDevices\n                && navigator.mediaDevices.enumerateDevices);\n    }\n\n    /**\n     * Returns true if changing the input (camera / microphone) or output\n     * (audio) device is supported and false if not.\n     * @params {string} [deviceType] - type of device to change. Default is\n     *      undefined or 'input', 'output' - for audio output device change.\n     * @returns {boolean} true if available, false otherwise.\n     */\n    isDeviceChangeAvailable(deviceType) {\n        if (deviceType === 'output' || deviceType === 'audiooutput') {\n            return isAudioOutputDeviceChangeAvailable;\n        }\n\n        // Calling getUserMedia again (for preview) kills the track returned by the first getUserMedia call because of\n        // https://bugs.webkit.org/show_bug.cgi?id=179363. Therefore, do not show microphone/camera options on mobile\n        // Safari.\n        if ((deviceType === 'audioinput' || deviceType === 'input') && browser.isIosBrowser()) {\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * A method to handle stopping of the stream.\n     * One point to handle the differences in various implementations.\n     * @param mediaStream MediaStream object to stop.\n     */\n    stopMediaStream(mediaStream) {\n        if (!mediaStream) {\n            return;\n        }\n\n        mediaStream.getTracks().forEach(track => {\n            if (track.stop) {\n                track.stop();\n            }\n        });\n\n        // leave stop for implementation still using it\n        if (mediaStream.stop) {\n            mediaStream.stop();\n        }\n\n        // The MediaStream implementation of the react-native-webrtc project has\n        // an explicit release method that is to be invoked in order to release\n        // used resources such as memory.\n        if (mediaStream.release) {\n            mediaStream.release();\n        }\n    }\n\n    /**\n     * Returns whether the desktop sharing is enabled or not.\n     * @returns {boolean}\n     */\n    isDesktopSharingEnabled() {\n        return screenObtainer.isSupported();\n    }\n\n    /**\n     * Sets current audio output device.\n     * @param {string} deviceId - id of 'audiooutput' device from\n     *      navigator.mediaDevices.enumerateDevices(), 'default' for default\n     *      device\n     * @returns {Promise} - resolves when audio output is changed, is rejected\n     *      otherwise\n     */\n    setAudioOutputDevice(deviceId) {\n        if (!this.isDeviceChangeAvailable('output')) {\n            return Promise.reject(\n                new Error('Audio output device change is not supported'));\n        }\n\n        return featureDetectionAudioEl.setSinkId(deviceId)\n            .then(() => {\n                audioOutputDeviceId = deviceId;\n                audioOutputChanged = true;\n\n                logger.log(`Audio output device set to ${deviceId}`);\n\n                eventEmitter.emit(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n                    deviceId);\n            });\n    }\n\n    /**\n     * Sets the capture frame rate for desktop tracks.\n     *\n     * @param {number} maxFps - max fps to be used as the capture frame rate.\n     * @returns {void}\n     */\n    setDesktopSharingFrameRate(maxFps) {\n        screenObtainer.setDesktopSharingFrameRate(maxFps);\n    }\n\n    /**\n     * Returns currently used audio output device id, '' stands for default\n     * device\n     * @returns {string}\n     */\n    getAudioOutputDevice() {\n        return audioOutputDeviceId;\n    }\n\n    /**\n     * Returns list of available media devices if its obtained, otherwise an\n     * empty array is returned/\n     * @returns {Array} list of available media devices.\n     */\n    getCurrentlyAvailableMediaDevices() {\n        return availableDevices;\n    }\n\n    /**\n     * Returns whether available devices have permissions granted\n     * @returns {Boolean}\n     */\n    arePermissionsGrantedForAvailableDevices() {\n        return availableDevices.some(device => Boolean(device.label));\n    }\n\n    /**\n     * Returns event data for device to be reported to stats.\n     * @returns {MediaDeviceInfo} device.\n     */\n    getEventDataForActiveDevice(device) {\n        const deviceList = [];\n        const deviceData = {\n            'deviceId': device.deviceId,\n            'kind': device.kind,\n            'label': device.label,\n            'groupId': device.groupId\n        };\n\n        deviceList.push(deviceData);\n\n        return { deviceList };\n    }\n\n    /**\n     * Configures the given PeerConnection constraints to either enable or\n     * disable (according to the value of the 'enable' parameter) the\n     * 'googSuspendBelowMinBitrate' option.\n     * @param constraints the constraints on which to operate.\n     * @param enable {boolean} whether to enable or disable the suspend video\n     * option.\n     */\n    setSuspendVideo(constraints, enable) {\n        if (!constraints.optional) {\n            constraints.optional = [];\n        }\n\n        // Get rid of all \"googSuspendBelowMinBitrate\" constraints (we assume\n        // that the elements of constraints.optional contain a single property).\n        constraints.optional\n            = constraints.optional.filter(\n                c => !c.hasOwnProperty('googSuspendBelowMinBitrate'));\n\n        if (enable) {\n            constraints.optional.push({ googSuspendBelowMinBitrate: 'true' });\n        }\n    }\n}\n\nconst rtcUtils = new RTCUtils();\n\n/**\n * Wraps original attachMediaStream function to set current audio output device\n * if this is supported.\n * @param {Function} origAttachMediaStream\n * @returns {Function}\n */\nfunction wrapAttachMediaStream(origAttachMediaStream) {\n    return function(element, stream) {\n        // eslint-disable-next-line prefer-rest-params\n        const res = origAttachMediaStream.apply(rtcUtils, arguments);\n\n        if (stream\n                && rtcUtils.isDeviceChangeAvailable('output')\n                && stream.getAudioTracks\n                && stream.getAudioTracks().length\n\n                // we skip setting audio output if there was no explicit change\n                && audioOutputChanged) {\n            element.setSinkId(rtcUtils.getAudioOutputDevice())\n                .catch(function(ex) {\n                    const err\n                        = new JitsiTrackError(ex, null, [ 'audiooutput' ]);\n\n                    GlobalOnErrorHandler.callUnhandledRejectionHandler({\n                        promise: this, // eslint-disable-line no-invalid-this\n                        reason: err\n                    });\n\n                    logger.warn(\n                        'Failed to set audio output device for the element.'\n                            + ' Default audio output device will be used'\n                            + ' instead',\n                        element,\n                        err);\n                });\n        }\n\n        return res;\n    };\n}\n\nexport default rtcUtils;\n","import { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\n\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport browser from '../browser';\n\nimport RTCUtils from './RTCUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * Maps our handler types to MediaStreamTrack properties.\n */\nconst trackHandler2Prop = {\n    'track_mute': 'onmute', // Not supported on FF\n    'track_unmute': 'onunmute',\n    'track_ended': 'onended'\n};\n\n/**\n * Represents a single media track (either audio or video).\n */\nexport default class JitsiTrack extends EventEmitter {\n    /* eslint-disable max-params */\n    /**\n     * Represents a single media track (either audio or video).\n     * @constructor\n     * @param conference the rtc instance\n     * @param stream the WebRTC MediaStream instance\n     * @param track the WebRTC MediaStreamTrack instance, must be part of\n     * the given <tt>stream</tt>.\n     * @param streamInactiveHandler the function that will handle\n     *        onended/oninactive events of the stream.\n     * @param trackMediaType the media type of the JitsiTrack\n     * @param videoType the VideoType for this track if any\n     */\n    constructor(\n            conference,\n            stream,\n            track,\n            streamInactiveHandler,\n            trackMediaType,\n            videoType) {\n        super();\n\n        // aliases for addListener/removeListener\n        this.addEventListener = this.addListener;\n        this.removeEventListener = this.off = this.removeListener;\n\n        /**\n         * Array with the HTML elements that are displaying the streams.\n         * @type {Array}\n         */\n        this.containers = [];\n        this.conference = conference;\n        this.audioLevel = -1;\n        this.type = trackMediaType;\n        this.track = track;\n        this.videoType = videoType;\n        this.handlers = new Map();\n\n        /**\n         * Indicates whether this JitsiTrack has been disposed. If true, this\n         * JitsiTrack is to be considered unusable and operations involving it\n         * are to fail (e.g. {@link JitsiConference#addTrack(JitsiTrack)},\n         * {@link JitsiConference#removeTrack(JitsiTrack)}).\n         * @type {boolean}\n         */\n        this.disposed = false;\n\n        /**\n         * The inactive handler which will be triggered when the underlying\n         * <tt>MediaStream</tt> ends.\n         *\n         * @private\n         * @type {Function}\n         */\n        this._streamInactiveHandler = streamInactiveHandler;\n\n        this._setStream(stream);\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Adds onended/oninactive handler to a MediaStream or a MediaStreamTrack.\n     * Firefox doesn't fire a inactive event on the MediaStream, instead it fires\n     * a onended event on the MediaStreamTrack.\n     * @param {Function} handler the handler\n     */\n    _addMediaStreamInactiveHandler(handler) {\n        if (browser.isFirefox()) {\n            this.track.onended = handler;\n        } else {\n            this.stream.oninactive = handler;\n        }\n    }\n\n    /**\n     * Sets handler to the WebRTC MediaStream or MediaStreamTrack object\n     * depending on the passed type.\n     * @param {string} type the type of the handler that is going to be set\n     * @param {Function} handler the handler.\n     */\n    _setHandler(type, handler) {\n        if (!trackHandler2Prop.hasOwnProperty(type)) {\n            logger.error(`Invalid handler type ${type}`);\n\n            return;\n        }\n        if (handler) {\n            this.handlers.set(type, handler);\n        } else {\n            this.handlers.delete(type);\n        }\n\n        if (this.stream) {\n            for (const track of this.stream.getTracks()) {\n                track[trackHandler2Prop[type]] = handler;\n            }\n        }\n    }\n\n    /**\n     * Unregisters all event handlers bound to the underlying media stream/track\n     * @private\n     */\n    _unregisterHandlers() {\n        if (!this.stream) {\n            logger.warn(\n                `${this}: unable to unregister handlers - no stream object`);\n\n            return;\n        }\n\n        for (const type of this.handlers.keys()) {\n            // FIXME Why only video tracks?\n            for (const videoTrack of this.stream.getVideoTracks()) {\n                videoTrack[trackHandler2Prop[type]] = undefined;\n            }\n        }\n        if (this._streamInactiveHandler) {\n            this._addMediaStreamInactiveHandler(undefined);\n        }\n    }\n\n    /**\n     * Sets the stream property of JitsiTrack object and sets all stored\n     * handlers to it.\n     *\n     * @param {MediaStream} stream the new stream.\n     * @protected\n     */\n    _setStream(stream) {\n        if (this.stream === stream) {\n            return;\n        }\n\n        this.stream = stream;\n\n        // TODO Practically, that's like the opposite of _unregisterHandlers\n        // i.e. may be abstracted into a function/method called\n        // _registerHandlers for clarity and easing the maintenance of the two\n        // pieces of source code.\n        if (this.stream) {\n            for (const type of this.handlers.keys()) {\n                this._setHandler(type, this.handlers.get(type));\n            }\n            if (this._streamInactiveHandler) {\n                this._addMediaStreamInactiveHandler(this._streamInactiveHandler);\n            }\n        }\n    }\n\n    /**\n     * Returns the video type (camera or desktop) of this track.\n     */\n    getVideoType() {\n        return this.videoType;\n    }\n\n    /**\n     * Returns the type (audio or video) of this track.\n     */\n    getType() {\n        return this.type;\n    }\n\n    /**\n     * Check if this is an audio track.\n     */\n    isAudioTrack() {\n        return this.getType() === MediaType.AUDIO;\n    }\n\n    /**\n     * Checks whether the underlying WebRTC <tt>MediaStreamTrack</tt> is muted\n     * according to it's 'muted' field status.\n     * @return {boolean} <tt>true</tt> if the underlying\n     * <tt>MediaStreamTrack</tt> is muted or <tt>false</tt> otherwise.\n     */\n    isWebRTCTrackMuted() {\n        return this.track && this.track.muted;\n    }\n\n    /**\n     * Check if this is a video track.\n     */\n    isVideoTrack() {\n        return this.getType() === MediaType.VIDEO;\n    }\n\n    /**\n     * Checks whether this is a local track.\n     * @abstract\n     * @return {boolean} 'true' if it's a local track or 'false' otherwise.\n     */\n    isLocal() {\n        throw new Error('Not implemented by subclass');\n    }\n\n    /**\n     * Check whether this is a local audio track.\n     *\n     * @return {boolean} -  true if track represents a local audio track, false otherwise.\n     */\n    isLocalAudioTrack() {\n        return this.isAudioTrack() && this.isLocal();\n    }\n\n    /**\n     * Returns the WebRTC MediaStream instance.\n     */\n    getOriginalStream() {\n        return this.stream;\n    }\n\n    /**\n     * Returns the ID of the underlying WebRTC Media Stream(if any)\n     * @returns {String|null}\n     */\n    getStreamId() {\n        return this.stream ? this.stream.id : null;\n    }\n\n    /**\n     * Return the underlying WebRTC MediaStreamTrack\n     * @returns {MediaStreamTrack}\n     */\n    getTrack() {\n        return this.track;\n    }\n\n    /**\n     * Return the underlying WebRTC MediaStreamTrack label\n     * @returns {string}\n     */\n    getTrackLabel() {\n        return this.track.label;\n    }\n\n    /**\n     * Returns the ID of the underlying WebRTC MediaStreamTrack(if any)\n     * @returns {String|null}\n     */\n    getTrackId() {\n        return this.track ? this.track.id : null;\n    }\n\n    /**\n     * Return meaningful usage label for this track depending on it's media and\n     * eventual video type.\n     * @returns {string}\n     */\n    getUsageLabel() {\n        if (this.isAudioTrack()) {\n            return 'mic';\n        }\n\n        return this.videoType ? this.videoType : 'default';\n    }\n\n    /**\n     * Eventually will trigger RTCEvents.TRACK_ATTACHED event.\n     * @param container the video/audio container to which this stream is\n     *        attached and for which event will be fired.\n     * @private\n     */\n    _maybeFireTrackAttached(container) {\n        if (this.conference && container) {\n            this.conference._onTrackAttach(this, container);\n        }\n    }\n\n    /**\n     * Attaches the MediaStream of this track to an HTML container.\n     * Adds the container to the list of containers that are displaying the\n     * track.\n     *\n     * @param container the HTML container which can be 'video' or 'audio'\n     * element.\n     *\n     * @returns {void}\n     */\n    attach(container) {\n        if (this.stream) {\n            this._onTrackAttach(container);\n            RTCUtils.attachMediaStream(container, this.stream);\n        }\n        this.containers.push(container);\n        this._maybeFireTrackAttached(container);\n        this._attachTTFMTracker(container);\n    }\n\n    /**\n     * Removes this JitsiTrack from the passed HTML container.\n     *\n     * @param container the HTML container to detach from this JitsiTrack. If\n     * <tt>null</tt> or <tt>undefined</tt>, all containers are removed. A\n     * container can be a 'video', 'audio' or 'object' HTML element instance to\n     * which this JitsiTrack is currently attached.\n     */\n    detach(container) {\n        for (let cs = this.containers, i = cs.length - 1; i >= 0; --i) {\n            const c = cs[i];\n\n            if (!container) {\n                this._onTrackDetach(c);\n                RTCUtils.attachMediaStream(c, null);\n            }\n            if (!container || c === container) {\n                cs.splice(i, 1);\n            }\n        }\n\n        if (container) {\n            this._onTrackDetach(container);\n            RTCUtils.attachMediaStream(container, null);\n        }\n    }\n\n    /**\n     * Called when the track has been attached to a new container.\n     *\n     * @param {HTMLElement} container the HTML container which can be 'video' or\n     * 'audio' element.\n     * @private\n     */\n    _onTrackAttach(container) { // eslint-disable-line no-unused-vars\n        // Should be defined by the classes that are extending JitsiTrack\n    }\n\n    /**\n     * Called when the track has been detached from a container.\n     *\n     * @param {HTMLElement} container the HTML container which can be 'video' or\n     * 'audio' element.\n     * @private\n     */\n    _onTrackDetach(container) { // eslint-disable-line no-unused-vars\n        // Should be defined by the classes that are extending JitsiTrack\n    }\n\n    /**\n     * Attach time to first media tracker only if there is conference and only\n     * for the first element.\n     *\n     * @param {HTMLElement} container the HTML container which can be 'video' or\n     * 'audio' element.\n     * @private\n     */\n    _attachTTFMTracker(container) { // eslint-disable-line no-unused-vars\n        // Should be defined by the classes that are extending JitsiTrack\n    }\n\n    /**\n     * Removes attached event listeners.\n     *\n     * @returns {Promise}\n     */\n    dispose() {\n        this.removeAllListeners();\n\n        this.disposed = true;\n\n        return Promise.resolve();\n    }\n\n    /**\n     * Returns true if this is a video track and the source of the video is a\n     * screen capture as opposed to a camera.\n     */\n    isScreenSharing() {\n        // FIXME: Should be fixed or removed.\n    }\n\n    /**\n     * Returns id of the track.\n     * @returns {string|null} id of the track or null if this is fake track.\n     */\n    getId() {\n        if (this.stream) {\n            return RTCUtils.getStreamID(this.stream);\n        }\n\n        return null;\n    }\n\n    /**\n     * Checks whether the MediaStream is active/not ended.\n     * When there is no check for active we don't have information and so\n     * will return that stream is active (in case of FF).\n     * @returns {boolean} whether MediaStream is active.\n     */\n    isActive() {\n        if (typeof this.stream.active !== 'undefined') {\n            return this.stream.active;\n        }\n\n        return true;\n    }\n\n    /**\n     * Sets the audio level for the stream\n     * @param {number} audioLevel value between 0 and 1\n     * @param {TraceablePeerConnection} [tpc] the peerconnection instance which\n     * is source for the audio level. It can be <tt>undefined</tt> for\n     * a local track if the audio level was measured outside of the\n     * peerconnection (see /modules/statistics/LocalStatsCollector.js).\n     */\n    setAudioLevel(audioLevel, tpc) {\n        let newAudioLevel = audioLevel;\n\n        // When using getSynchornizationSources on the audio receiver to gather audio levels for\n        // remote tracks, browser reports last known audio levels even when the remote user is\n        // audio muted, we need to reset the value to zero here so that the audio levels are cleared.\n        // Remote tracks have the tpc info present while local tracks do not.\n        if (browser.supportsReceiverStats() && typeof tpc !== 'undefined' && this.isMuted()) {\n            newAudioLevel = 0;\n        }\n\n        if (this.audioLevel !== newAudioLevel) {\n            this.audioLevel = newAudioLevel;\n            this.emit(\n                JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n                newAudioLevel,\n                tpc);\n\n        // LocalStatsCollector reports a value of 0.008 for muted mics\n        // and a value of 0 when there is no audio input.\n        } else if (this.audioLevel === 0\n            && newAudioLevel === 0\n            && this.isLocal()\n            && !this.isWebRTCTrackMuted()) {\n            this.emit(\n                JitsiTrackEvents.NO_AUDIO_INPUT,\n                newAudioLevel);\n        }\n    }\n\n    /**\n     * Returns the msid of the stream attached to the JitsiTrack object or null\n     * if no stream is attached.\n     */\n    getMSID() {\n        const streamId = this.getStreamId();\n        const trackId = this.getTrackId();\n\n        return streamId && trackId ? `${streamId} ${trackId}` : null;\n    }\n\n    /**\n     * Sets new audio output device for track's DOM elements. Video tracks are\n     * ignored.\n     * @param {string} audioOutputDeviceId - id of 'audiooutput' device from\n     *      navigator.mediaDevices.enumerateDevices(), '' for default device\n     * @emits JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED\n     * @returns {Promise}\n     */\n    setAudioOutput(audioOutputDeviceId) {\n        if (!RTCUtils.isDeviceChangeAvailable('output')) {\n            return Promise.reject(\n                new Error('Audio output device change is not supported'));\n        }\n\n        // All audio communication is done through audio tracks, so ignore\n        // changing audio output for video tracks at all.\n        if (this.isVideoTrack()) {\n            return Promise.resolve();\n        }\n\n        return (\n            Promise.all(\n                this.containers.map(\n                    element =>\n                        element.setSinkId(audioOutputDeviceId)\n                            .catch(error => {\n                                logger.warn(\n                                    'Failed to change audio output device on'\n                                        + ' element. Default or previously set'\n                                        + ' audio output device will be used.',\n                                    element,\n                                    error);\n                                throw error;\n                            }))\n            )\n                .then(() => {\n                    this.emit(\n                        JitsiTrackEvents.TRACK_AUDIO_OUTPUT_CHANGED,\n                        audioOutputDeviceId);\n                }));\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport JitsiTrackError from '../../JitsiTrackError';\nimport {\n    TRACK_IS_DISPOSED,\n    TRACK_NO_STREAM_FOUND\n} from '../../JitsiTrackErrors';\nimport {\n    LOCAL_TRACK_STOPPED,\n    NO_DATA_FROM_SOURCE,\n    TRACK_MUTE_CHANGED\n} from '../../JitsiTrackEvents';\nimport CameraFacingMode from '../../service/RTC/CameraFacingMode';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport VideoType from '../../service/RTC/VideoType';\nimport {\n    NO_BYTES_SENT,\n    TRACK_UNMUTED,\n    createNoDataFromSourceEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\nimport JitsiTrack from './JitsiTrack';\nimport RTCUtils from './RTCUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * Represents a single media track(either audio or video).\n * One <tt>JitsiLocalTrack</tt> corresponds to one WebRTC MediaStreamTrack.\n */\nexport default class JitsiLocalTrack extends JitsiTrack {\n    /**\n     * Constructs a new JitsiLocalTrack instance.\n     *\n     * @constructor\n     * @param {Object} trackInfo\n     * @param {number} trackInfo.rtcId - The ID assigned by the RTC module.\n     * @param {Object} trackInfo.stream - The WebRTC MediaStream, parent of the track.\n     * @param {Object} trackInfo.track - The underlying WebRTC MediaStreamTrack for new JitsiLocalTrack.\n     * @param {string} trackInfo.mediaType - The MediaType of the JitsiLocalTrack.\n     * @param {string} trackInfo.videoType - The VideoType of the JitsiLocalTrack.\n     * @param {Array<Object>} trackInfo.effects - The effects to be applied to the JitsiLocalTrack.\n     * @param {number} trackInfo.resolution - The the video resolution if it's a video track\n     * @param {string} trackInfo.deviceId - The ID of the local device for this track.\n     * @param {string} trackInfo.facingMode - Thehe camera facing mode used in getUserMedia call (for mobile only).\n     * @param {sourceId} trackInfo.sourceId - The id of the desktop sharing source. NOTE: defined for desktop sharing\n     * tracks only.\n     */\n    constructor({\n        deviceId,\n        facingMode,\n        mediaType,\n        resolution,\n        rtcId,\n        sourceId,\n        sourceType,\n        stream,\n        track,\n        videoType,\n        effects = []\n    }) {\n        super(\n            /* conference */ null,\n            stream,\n            track,\n            /* streamInactiveHandler */ () => this.emit(LOCAL_TRACK_STOPPED),\n            mediaType,\n            videoType);\n\n        this._setEffectInProgress = false;\n        const effect = effects.find(e => e.isEnabled(this));\n\n        if (effect) {\n            this._startStreamEffect(effect);\n        }\n\n        const displaySurface = videoType === VideoType.DESKTOP\n            ? track.getSettings().displaySurface\n            : null;\n\n        /**\n         * Track metadata.\n         */\n        this.metadata = {\n            timestamp: Date.now(),\n            ...displaySurface ? { displaySurface } : {}\n        };\n\n\n        /**\n         * The ID assigned by the RTC module on instance creation.\n         *\n         * @type {number}\n         */\n        this.rtcId = rtcId;\n        this.sourceId = sourceId;\n        this.sourceType = sourceType;\n\n        // Get the resolution from the track itself because it cannot be\n        // certain which resolution webrtc has fallen back to using.\n        this.resolution = track.getSettings().height;\n        this.maxEnabledResolution = resolution;\n\n        // Cache the constraints of the track in case of any this track\n        // model needs to call getUserMedia again, such as when unmuting.\n        this._constraints = track.getConstraints();\n\n        // Safari returns an empty constraints object, construct the constraints using getSettings.\n        if (!Object.keys(this._constraints).length && videoType === VideoType.CAMERA) {\n            this._constraints = {\n                height: track.getSettings().height,\n                width: track.getSettings().width\n            };\n        }\n\n        this.deviceId = deviceId;\n\n        /**\n         * The <tt>Promise</tt> which represents the progress of a previously\n         * queued/scheduled {@link _setMuted} (from the point of view of\n         * {@link _queueSetMuted}).\n         *\n         * @private\n         * @type {Promise}\n         */\n        this._prevSetMuted = Promise.resolve();\n\n        /**\n         * The facing mode of the camera from which this JitsiLocalTrack\n         * instance was obtained.\n         *\n         * @private\n         * @type {CameraFacingMode|undefined}\n         */\n        this._facingMode = facingMode;\n\n        // Currently there is no way to know the MediaStreamTrack ended due to\n        // to device disconnect in Firefox through e.g. \"readyState\" property.\n        // Instead we will compare current track's label with device labels from\n        // enumerateDevices() list.\n        this._trackEnded = false;\n\n        /**\n         * Indicates whether data has been sent or not.\n         */\n        this._hasSentData = false;\n\n        /**\n         * Used only for detection of audio problems. We want to check only once\n         * whether the track is sending data ot not. This flag is set to false\n         * after the check.\n         */\n        this._testDataSent = true;\n\n        // Currently there is no way to determine with what device track was\n        // created (until getConstraints() support), however we can associate\n        // tracks with real devices obtained from enumerateDevices() call as\n        // soon as it's called.\n        // NOTE: this.deviceId corresponds to the device id specified in GUM constraints and this._realDeviceId seems to\n        // correspond to the id of a matching device from the available device list.\n        this._realDeviceId = this.deviceId === '' ? undefined : this.deviceId;\n\n        // The source name that will be signaled for this track.\n        this._sourceName = null;\n\n        this._trackMutedTS = 0;\n\n        this._onDeviceListWillChange = devices => {\n            const oldRealDeviceId = this._realDeviceId;\n\n            this._setRealDeviceIdFromDeviceList(devices);\n\n            if (\n                // Mark track as ended for those browsers that do not support\n                // \"readyState\" property. We do not touch tracks created with\n                // default device ID \"\".\n                (typeof this.getTrack().readyState === 'undefined'\n                    && typeof this._realDeviceId !== 'undefined'\n                    && !devices.find(d => d.deviceId === this._realDeviceId))\n\n                // If there was an associated realDeviceID and after the device change the realDeviceId is undefined\n                // then the associated device has been disconnected and the _trackEnded flag needs to be set. In\n                // addition on some Chrome versions the readyState property is set after the device change event is\n                // triggered which causes issues in jitsi-meet with the selection of a new device because we don't\n                // detect that the old one was removed.\n                || (typeof oldRealDeviceId !== 'undefined' && typeof this._realDeviceId === 'undefined')\n            ) {\n                this._trackEnded = true;\n            }\n        };\n\n        // Subscribe each created local audio track to\n        // RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED event. This is different from\n        // handling this event for remote tracks (which are handled in RTC.js),\n        // because there might be local tracks not attached to a conference.\n        if (this.isAudioTrack() && RTCUtils.isDeviceChangeAvailable('output')) {\n            this._onAudioOutputDeviceChanged = this.setAudioOutput.bind(this);\n            RTCUtils.addListener(\n                RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n                this._onAudioOutputDeviceChanged);\n        }\n\n        RTCUtils.addListener(RTCEvents.DEVICE_LIST_WILL_CHANGE, this._onDeviceListWillChange);\n\n        this._initNoDataFromSourceHandlers();\n    }\n\n    /**\n     * Adds stream to conference and marks it as \"unmute\" operation.\n     *\n     * @private\n     * @returns {Promise}\n     */\n    _addStreamToConferenceAsUnmute() {\n        if (!this.conference) {\n            return Promise.resolve();\n        }\n\n        // FIXME it would be good to not included conference as part of this process. Only TraceablePeerConnections to\n        // which the track is attached should care about this action. The TPCs to which the track is not attached can\n        // sync up when track is re-attached. A problem with that is that the \"modify sources\" queue is part of the\n        // JingleSessionPC and it would be excluded from the process. One solution would be to extract class between\n        // TPC and JingleSessionPC which would contain the queue and would notify the signaling layer when local SSRCs\n        // are changed. This would help to separate XMPP from the RTC module.\n        return new Promise((resolve, reject) => {\n            this.conference._addLocalTrackAsUnmute(this)\n                .then(resolve, error => reject(new Error(error)));\n        });\n    }\n\n    /**\n     * Fires NO_DATA_FROM_SOURCE event and logs it to analytics and callstats.\n     *\n     * @private\n     * @returns {void}\n     */\n    _fireNoDataFromSourceEvent() {\n        const value = !this.isReceivingData();\n\n        this.emit(NO_DATA_FROM_SOURCE, value);\n\n        // FIXME: Should we report all of those events\n        Statistics.sendAnalytics(createNoDataFromSourceEvent(this.getType(), value));\n        Statistics.sendLog(JSON.stringify({\n            name: NO_DATA_FROM_SOURCE,\n            log: value\n        }));\n    }\n\n    /**\n     * Sets handlers to the MediaStreamTrack object that will detect camera issues.\n     *\n     * @private\n     * @returns {void}\n     */\n    _initNoDataFromSourceHandlers() {\n        if (!this._isNoDataFromSourceEventsEnabled()) {\n            return;\n        }\n\n        this._setHandler('track_mute', () => {\n            this._trackMutedTS = window.performance.now();\n            this._fireNoDataFromSourceEvent();\n        });\n\n        this._setHandler('track_unmute', () => {\n            this._fireNoDataFromSourceEvent();\n            Statistics.sendAnalyticsAndLog(\n                TRACK_UNMUTED,\n                {\n                    'media_type': this.getType(),\n                    'track_type': 'local',\n                    value: window.performance.now() - this._trackMutedTS\n                });\n        });\n\n        if (this.isVideoTrack() && this.videoType === VideoType.CAMERA) {\n            this._setHandler('track_ended', () => {\n                if (!this.isReceivingData()) {\n                    this._fireNoDataFromSourceEvent();\n                }\n            });\n        }\n    }\n\n    /**\n     * Returns true if no data from source events are enabled for this JitsiLocalTrack and false otherwise.\n     *\n     * @private\n     * @returns {boolean} - True if no data from source events are enabled for this JitsiLocalTrack and false otherwise.\n     */\n    _isNoDataFromSourceEventsEnabled() {\n        // Disable the events for screen sharing.\n        return !this.isVideoTrack() || this.videoType !== VideoType.DESKTOP;\n    }\n\n    /**\n     * Initializes a new Promise to execute {@link #_setMuted}. May be called multiple times in a row and the\n     * invocations of {@link #_setMuted} and, consequently, {@link #mute} and/or {@link #unmute} will be resolved in a\n     * serialized fashion.\n     *\n     * @param {boolean} muted - The value to invoke <tt>_setMuted</tt> with.\n     * @private\n     * @returns {Promise}\n     */\n    _queueSetMuted(muted) {\n        const setMuted = this._setMuted.bind(this, muted);\n\n        this._prevSetMuted = this._prevSetMuted.then(setMuted, setMuted);\n\n        return this._prevSetMuted;\n    }\n\n    /**\n     * Removes stream from conference and marks it as \"mute\" operation.\n     *\n     * @param {Function} successCallback - Callback that will be called when the operation is successful.\n     * @param {Function} errorCallback - Callback that will be called when the operation fails.\n     * @private\n     * @returns {Promise}\n     */\n    _removeStreamFromConferenceAsMute(successCallback, errorCallback) {\n        if (!this.conference) {\n            successCallback();\n\n            return;\n        }\n        this.conference._removeLocalTrackAsMute(this).then(\n            successCallback,\n            error => errorCallback(new Error(error)));\n    }\n\n    /**\n     * Sends mute status for a track to conference if any.\n     *\n     * @param {boolean} mute - If track is muted.\n     * @private\n     * @returns {void}\n     */\n    _sendMuteStatus(mute) {\n        if (this.conference) {\n            this.conference._setTrackMuteStatus(this.getType(), this, mute) && this.conference.room.sendPresence();\n        }\n    }\n\n    /**\n     * Mutes / unmutes this track.\n     *\n     * @param {boolean} muted - If <tt>true</tt>, this track will be muted; otherwise, this track will be unmuted.\n     * @private\n     * @returns {Promise}\n     */\n    _setMuted(muted) {\n        if (this.isMuted() === muted) {\n            return Promise.resolve();\n        }\n\n        if (this.disposed) {\n            return Promise.reject(new JitsiTrackError(TRACK_IS_DISPOSED));\n        }\n\n        let promise = Promise.resolve();\n\n        // A function that will print info about muted status transition\n        const logMuteInfo = () => logger.info(`Mute ${this}: ${muted}`);\n\n        if (this.isAudioTrack()\n                || this.videoType === VideoType.DESKTOP\n                || !browser.doesVideoMuteByStreamRemove()) {\n            logMuteInfo();\n\n            // If we have a stream effect that implements its own mute functionality, prioritize it before\n            // normal mute e.g. the stream effect that implements system audio sharing has a custom\n            // mute state in which if the user mutes, system audio still has to go through.\n            if (this._streamEffect && this._streamEffect.setMuted) {\n                this._streamEffect.setMuted(muted);\n            } else if (this.track) {\n                this.track.enabled = !muted;\n            }\n        } else if (muted) {\n            promise = new Promise((resolve, reject) => {\n                logMuteInfo();\n                this._removeStreamFromConferenceAsMute(\n                    () => {\n                        if (this._streamEffect) {\n                            this._stopStreamEffect();\n                        }\n\n                        // FIXME: Maybe here we should set the SRC for the\n                        // containers to something\n                        // We don't want any events to be fired on this stream\n                        this._unregisterHandlers();\n                        this.stopStream();\n                        this._setStream(null);\n\n                        resolve();\n                    },\n                    reject);\n            });\n        } else {\n            logMuteInfo();\n\n            // This path is only for camera.\n            const streamOptions = {\n                cameraDeviceId: this.getDeviceId(),\n                devices: [ MediaType.VIDEO ],\n                effects: this._streamEffect ? [ this._streamEffect ] : [],\n                facingMode: this.getCameraFacingMode()\n            };\n\n            promise\n                = RTCUtils.obtainAudioAndVideoPermissions(Object.assign(\n                    {},\n                    streamOptions,\n                    { constraints: { video: this._constraints } }));\n\n            promise = promise.then(streamsInfo => {\n                // The track kind for presenter track is video as well.\n                const mediaType = this.getType() === MediaType.PRESENTER ? MediaType.VIDEO : this.getType();\n                const streamInfo = streamsInfo.find(info => info.track.kind === mediaType);\n\n                if (streamInfo) {\n                    this._setStream(streamInfo.stream);\n                    this.track = streamInfo.track;\n\n                    // This is not good when video type changes after\n                    // unmute, but let's not crash here\n                    if (this.videoType !== streamInfo.videoType) {\n                        logger.warn(\n                            `${this}: video type has changed after unmute!`,\n                            this.videoType, streamInfo.videoType);\n                        this.videoType = streamInfo.videoType;\n                    }\n                } else {\n                    throw new JitsiTrackError(TRACK_NO_STREAM_FOUND);\n                }\n\n                if (this._streamEffect) {\n                    this._startStreamEffect(this._streamEffect);\n                }\n\n                this.containers.map(\n                    cont => RTCUtils.attachMediaStream(cont, this.stream));\n\n                return this._addStreamToConferenceAsUnmute();\n            });\n        }\n\n        return promise\n            .then(() => {\n                this._sendMuteStatus(muted);\n\n                // Send the videoType message to the bridge.\n                this.isVideoTrack() && this.conference && this.conference._sendBridgeVideoTypeMessage(this);\n                this.emit(TRACK_MUTE_CHANGED, this);\n            });\n    }\n\n    /**\n     * Sets real device ID by comparing track information with device information. This is temporary solution until\n     * getConstraints() method will be implemented in browsers.\n     *\n     * @param {MediaDeviceInfo[]} devices - The list of devices obtained from enumerateDevices() call.\n     * @private\n     * @returns {void}\n     */\n    _setRealDeviceIdFromDeviceList(devices) {\n        const track = this.getTrack();\n        const kind = `${track.kind}input`;\n\n        // We need to match by deviceId as well, in case of multiple devices with the same label.\n        let device = devices.find(d => d.kind === kind && d.label === track.label && d.deviceId === this.deviceId);\n\n        if (!device && this._realDeviceId === 'default') { // the default device has been changed.\n            // If the default device was 'A' and the default device is changed to 'B' the label for the track will\n            // remain 'Default - A' but the label for the device in the device list will be updated to 'A'. That's\n            // why in order to match it we need to remove the 'Default - ' part.\n            const label = (track.label || '').replace('Default - ', '');\n\n            device = devices.find(d => d.kind === kind && d.label === label);\n        }\n\n        if (device) {\n            this._realDeviceId = device.deviceId;\n        } else {\n            this._realDeviceId = undefined;\n        }\n    }\n\n    /**\n     * Sets the stream property of JitsiLocalTrack object and sets all stored handlers to it.\n     *\n     * @param {MediaStream} stream - The new MediaStream.\n     * @private\n     * @returns {void}\n     */\n    _setStream(stream) {\n        super._setStream(stream);\n\n        if (stream) {\n            // Store the MSID for video mute/unmute purposes.\n            this.storedMSID = this.getMSID();\n            logger.debug(`Setting new MSID: ${this.storedMSID} on ${this}`);\n        } else {\n            logger.debug(`Setting 'null' stream on ${this}`);\n        }\n    }\n\n    /**\n     * Starts the effect process and returns the modified stream.\n     *\n     * @param {Object} effect - Represents effect instance\n     * @private\n     * @returns {void}\n     */\n    _startStreamEffect(effect) {\n        this._streamEffect = effect;\n        this._originalStream = this.stream;\n        this._setStream(this._streamEffect.startEffect(this._originalStream));\n        this.track = this.stream.getTracks()[0];\n    }\n\n    /**\n     * Stops the effect process and returns the original stream.\n     *\n     * @private\n     * @returns {void}\n     */\n    _stopStreamEffect() {\n        if (this._streamEffect) {\n            this._streamEffect.stopEffect();\n            this._setStream(this._originalStream);\n            this._originalStream = null;\n            this.track = this.stream ? this.stream.getTracks()[0] : null;\n        }\n    }\n\n    /**\n     * Switches the camera facing mode if the WebRTC implementation supports the custom MediaStreamTrack._switchCamera\n     * method. Currently, the method in question is implemented in react-native-webrtc only. When such a WebRTC\n     * implementation is executing, the method is the preferred way to switch between the front/user-facing and the\n     * back/environment-facing cameras because it will likely be (as is the case of react-native-webrtc) noticeably\n     * faster that creating a new MediaStreamTrack via a new getUserMedia call with the switched facingMode constraint\n     * value. Moreover, the approach with a new getUserMedia call may not even work: WebRTC on Android and iOS is\n     * either very slow to open the camera a second time or plainly freezes attempting to do that.\n     *\n     * @returns {void}\n     */\n    _switchCamera() {\n        if (this.isVideoTrack()\n                && this.videoType === VideoType.CAMERA\n                && typeof this.track._switchCamera === 'function') {\n            this.track._switchCamera();\n\n            this._facingMode\n                = this._facingMode === CameraFacingMode.ENVIRONMENT\n                    ? CameraFacingMode.USER\n                    : CameraFacingMode.ENVIRONMENT;\n        }\n    }\n\n    /**\n     * Stops the currently used effect (if there is one) and starts the passed effect (if there is one).\n     *\n     * @param {Object|undefined} effect - The new effect to be set.\n     * @private\n     * @returns {void}\n     */\n    _switchStreamEffect(effect) {\n        if (this._streamEffect) {\n            this._stopStreamEffect();\n            this._streamEffect = undefined;\n        }\n        if (effect) {\n            this._startStreamEffect(effect);\n        }\n    }\n\n    /**\n     * @inheritdoc\n     *\n     * Stops sending the media track. And removes it from the HTML. NOTE: Works for local tracks only.\n     *\n     * @extends JitsiTrack#dispose\n     * @returns {Promise}\n     */\n    dispose() {\n        let promise = Promise.resolve();\n\n        // Remove the effect instead of stopping it so that the original stream is restored\n        // on both the local track and on the peerconnection.\n        if (this._streamEffect) {\n            promise = this.setEffect();\n        }\n\n        if (this.conference) {\n            promise = promise.then(() => this.conference.removeTrack(this));\n        }\n\n        if (this.stream) {\n            this.stopStream();\n            this.detach();\n        }\n\n        RTCUtils.removeListener(RTCEvents.DEVICE_LIST_WILL_CHANGE, this._onDeviceListWillChange);\n\n        if (this._onAudioOutputDeviceChanged) {\n            RTCUtils.removeListener(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n                this._onAudioOutputDeviceChanged);\n        }\n\n        return promise.then(() => super.dispose());\n    }\n\n    /**\n     * Returns facing mode for video track from camera. For other cases (e.g. audio track or 'desktop' video track)\n     * returns undefined.\n     *\n     * @returns {CameraFacingMode|undefined}\n     */\n    getCameraFacingMode() {\n        if (this.isVideoTrack() && this.videoType === VideoType.CAMERA) {\n            // MediaStreamTrack#getSettings() is not implemented in many\n            // browsers, so we need feature checking here. Progress on the\n            // respective browser's implementation can be tracked at\n            // https://bugs.chromium.org/p/webrtc/issues/detail?id=2481 for\n            // Chromium and https://bugzilla.mozilla.org/show_bug.cgi?id=1213517\n            // for Firefox. Even if a browser implements getSettings() already,\n            // it might still not return anything for 'facingMode'.\n            const trackSettings = this.track.getSettings?.();\n\n            if (trackSettings && 'facingMode' in trackSettings) {\n                return trackSettings.facingMode;\n            }\n\n            if (typeof this._facingMode !== 'undefined') {\n                return this._facingMode;\n            }\n\n            // In most cases we are showing a webcam. So if we've gotten here,\n            // it should be relatively safe to assume that we are probably\n            // showing the user-facing camera.\n            return CameraFacingMode.USER;\n        }\n\n        return undefined;\n    }\n\n    /**\n     * Returns device id associated with track.\n     *\n     * @returns {string}\n     */\n    getDeviceId() {\n        return this._realDeviceId || this.deviceId;\n    }\n\n    /**\n     * Get the duration of the track.\n     *\n     * @returns {Number} the duration of the track in seconds\n     */\n    getDuration() {\n        return (Date.now() / 1000) - (this.metadata.timestamp / 1000);\n    }\n\n    /**\n     * Returns the participant id which owns the track.\n     *\n     * @returns {string} the id of the participants. It corresponds to the\n     * Colibri endpoint id/MUC nickname in case of Jitsi-meet.\n     */\n    getParticipantId() {\n        return this.conference && this.conference.myUserId();\n    }\n\n    /**\n     * Returns the source name associated with the jitsi track.\n     *\n     * @returns {string | null} source name\n     */\n    getSourceName() {\n        return this._sourceName;\n    }\n\n    /**\n     * Returns if associated MediaStreamTrack is in the 'ended' state\n     *\n     * @returns {boolean}\n     */\n    isEnded() {\n        if (this.isVideoTrack() && this.isMuted()) {\n            // If a video track is muted the readyState will be ended, that's why we need to rely only on the\n            // _trackEnded flag.\n            return this._trackEnded;\n        }\n\n        return this.getTrack().readyState === 'ended' || this._trackEnded;\n    }\n\n    /**\n     * Returns <tt>true</tt>.\n     *\n     * @returns {boolean} <tt>true</tt>\n     */\n    isLocal() {\n        return true;\n    }\n\n    /**\n     * Returns <tt>true</tt> - if the stream is muted and <tt>false</tt> otherwise.\n     *\n     * @returns {boolean} <tt>true</tt> - if the stream is muted and <tt>false</tt> otherwise.\n     */\n    isMuted() {\n        // this.stream will be null when we mute local video on Chrome\n        if (!this.stream) {\n            return true;\n        }\n        if (this.isVideoTrack() && !this.isActive()) {\n            return true;\n        }\n\n        // If currently used stream effect has its own muted state, use that.\n        if (this._streamEffect && this._streamEffect.isMuted) {\n            return this._streamEffect.isMuted();\n        }\n\n        return !this.track || !this.track.enabled;\n    }\n\n    /**\n     * Checks whether the attached MediaStream is receiving data from source or not. If the stream property is null\n     * (because of mute or another reason) this method will return false.\n     * NOTE: This method doesn't indicate problem with the streams directly. For example in case of video mute the\n     * method will return false or if the user has disposed the track.\n     *\n     * @returns {boolean} true if the stream is receiving data and false this otherwise.\n     */\n    isReceivingData() {\n        if (this.isVideoTrack()\n            && (this.isMuted() || this._stopStreamInProgress || this.videoType === VideoType.DESKTOP)) {\n            return true;\n        }\n\n        if (!this.stream) {\n            return false;\n        }\n\n        // In older version of the spec there is no muted property and readyState can have value muted. In the latest\n        // versions readyState can have values \"live\" and \"ended\" and there is muted boolean property. If the stream is\n        // muted that means that we aren't receiving any data from the source. We want to notify the users for error if\n        // the stream is muted or ended on it's creation.\n\n        // For video blur enabled use the original video stream\n        const stream = this._effectEnabled ? this._originalStream : this.stream;\n\n        return stream.getTracks().some(track =>\n            (!('readyState' in track) || track.readyState === 'live')\n                && (!('muted' in track) || track.muted !== true));\n    }\n\n    /**\n     * Asynchronously mutes this track.\n     *\n     * @returns {Promise}\n     */\n    mute() {\n        return this._queueSetMuted(true);\n    }\n\n    /**\n     * Handles bytes sent statistics. NOTE: used only for audio tracks to detect audio issues.\n     *\n     * @param {TraceablePeerConnection} tpc - The peerconnection that is reporting the bytes sent stat.\n     * @param {number} bytesSent - The new value.\n     * @returns {void}\n     */\n    onByteSentStatsReceived(tpc, bytesSent) {\n        if (bytesSent > 0) {\n            this._hasSentData = true;\n        }\n        const iceConnectionState = tpc.getConnectionState();\n\n        if (this._testDataSent && iceConnectionState === 'connected') {\n            setTimeout(() => {\n                if (!this._hasSentData) {\n                    logger.warn(`${this} 'bytes sent' <= 0: \\\n                        ${bytesSent}`);\n\n                    Statistics.analytics.sendEvent(NO_BYTES_SENT, { 'media_type': this.getType() });\n                }\n            }, 3000);\n            this._testDataSent = false;\n        }\n    }\n\n    /**\n     * Sets the JitsiConference object associated with the track. This is temp solution.\n     *\n     * @param conference - JitsiConference object.\n     * @returns {void}\n     */\n    setConference(conference) {\n        this.conference = conference;\n\n        // We want to keep up with postponed events which should have been fired\n        // on \"attach\" call, but for local track we not always have the\n        // conference before attaching. However this may result in duplicated\n        // events if they have been triggered on \"attach\" already.\n        for (let i = 0; i < this.containers.length; i++) {\n            this._maybeFireTrackAttached(this.containers[i]);\n        }\n    }\n\n    /**\n     * Sets the effect and switches between the modified stream and original one.\n     *\n     * @param {Object} effect - Represents the effect instance to be used.\n     * @returns {Promise}\n     */\n    setEffect(effect) {\n        if (typeof this._streamEffect === 'undefined' && typeof effect === 'undefined') {\n            return Promise.resolve();\n        }\n\n        if (typeof effect !== 'undefined' && !effect.isEnabled(this)) {\n            return Promise.reject(new Error('Incompatible effect instance!'));\n        }\n\n        if (this._setEffectInProgress === true) {\n            return Promise.reject(new Error('setEffect already in progress!'));\n        }\n\n        // In case we have an audio track that is being enhanced with an effect, we still want it to be applied,\n        // even if the track is muted. Where as for video the actual track doesn't exists if it's muted.\n        if (this.isMuted() && !this.isAudioTrack()) {\n            this._streamEffect = effect;\n\n            return Promise.resolve();\n        }\n\n        const conference = this.conference;\n\n        if (!conference) {\n            this._switchStreamEffect(effect);\n            if (this.isVideoTrack()) {\n                this.containers.forEach(cont => RTCUtils.attachMediaStream(cont, this.stream));\n            }\n\n            return Promise.resolve();\n        }\n\n        this._setEffectInProgress = true;\n\n        // TODO: Create new JingleSessionPC method for replacing a stream in JitsiLocalTrack without offer answer.\n        return conference.removeTrack(this)\n            .then(() => {\n                this._switchStreamEffect(effect);\n                if (this.isVideoTrack()) {\n                    this.containers.forEach(cont => RTCUtils.attachMediaStream(cont, this.stream));\n                }\n\n                return conference.addTrack(this);\n            })\n            .then(() => {\n                this._setEffectInProgress = false;\n            })\n            .catch(error => {\n                // Any error will be not recovarable and will trigger CONFERENCE_FAILED event. But let's try to cleanup\n                // everyhting related to the effect functionality.\n                this._setEffectInProgress = false;\n                this._switchStreamEffect();\n                logger.error('Failed to switch to the new stream!', error);\n                throw error;\n            });\n    }\n\n    /**\n     * Sets the source name to be used for signaling the jitsi track.\n     *\n     * @param {string} name The source name.\n     */\n    setSourceName(name) {\n        this._sourceName = name;\n    }\n\n    /**\n     * Stops the associated MediaStream.\n     *\n     * @returns {void}\n     */\n    stopStream() {\n        /**\n         * Indicates that we are executing {@link #stopStream} i.e.\n         * {@link RTCUtils#stopMediaStream} for the <tt>MediaStream</tt>\n         * associated with this <tt>JitsiTrack</tt> instance.\n         *\n         * @private\n         * @type {boolean}\n         */\n        this._stopStreamInProgress = true;\n\n        try {\n            RTCUtils.stopMediaStream(this.stream);\n        } finally {\n            this._stopStreamInProgress = false;\n        }\n    }\n\n    /**\n     * Creates a text representation of this local track instance.\n     *\n     * @return {string}\n     */\n    toString() {\n        return `LocalTrack[${this.rtcId},${this.getType()}]`;\n    }\n\n    /**\n     * Asynchronously unmutes this track.\n     *\n     * @returns {Promise}\n     */\n    unmute() {\n        return this._queueSetMuted(false);\n    }\n}\n","/* Copyright @ 2015 - Present, 8x8 Inc\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport transform from 'sdp-transform';\n\n/**\n * Rewrites the source information in the way sdp-transform expects.\n * Source information is split into multiple ssrc objects each containing\n * an id, attribute and value.\n * @param {Object} media - media description to be modified.\n * @returns {void}\n */\nconst write = function(session, opts) {\n    if (typeof session !== 'undefined' && typeof session.media !== 'undefined' && Array.isArray(session.media)) {\n        session.media.forEach(mLine => {\n            if (mLine.sources && mLine.sources.length) {\n                mLine.ssrcs = [];\n                mLine.sources.forEach(source => {\n                    Object.keys(source).forEach(attribute => {\n                        if (attribute === 'id') {\n                            return;\n                        }\n                        mLine.ssrcs.push({\n                            id: source.id,\n                            attribute,\n                            value: source[attribute]\n                        });\n                    });\n                });\n                delete mLine.sources;\n            }\n\n            // join ssrcs in ssrc groups\n            if (mLine.ssrcGroups && mLine.ssrcGroups.length) {\n                mLine.ssrcGroups.forEach(ssrcGroup => {\n                    if (typeof ssrcGroup.ssrcs !== 'undefined'\n                    && Array.isArray(ssrcGroup.ssrcs)) {\n                        ssrcGroup.ssrcs = ssrcGroup.ssrcs.join(' ');\n                    }\n                });\n            }\n        });\n    }\n\n    return transform.write(session, opts);\n};\n\n/**\n * Rewrites the source information that we get from sdp-transform.\n * All the ssrc lines with different attributes that belong to the\n * same ssrc are grouped into a single soure object with multiple key value pairs.\n * @param {Object} media - media description to be modified.\n * @returns {void}\n */\nconst parse = function(sdp) {\n    const session = transform.parse(sdp);\n\n    if (typeof session !== 'undefined' && typeof session.media !== 'undefined' && Array.isArray(session.media)) {\n        session.media.forEach(mLine => {\n            // group sources attributes by ssrc\n            if (typeof mLine.ssrcs !== 'undefined' && Array.isArray(mLine.ssrcs)) {\n                mLine.sources = [];\n                mLine.ssrcs.forEach(ssrc => {\n                    const found = mLine.sources.findIndex(source => source.id === ssrc.id);\n\n                    if (found > -1) {\n                        mLine.sources[found][ssrc.attribute] = ssrc.value;\n                    } else {\n                        const src = { id: ssrc.id };\n\n                        src[ssrc.attribute] = ssrc.value;\n                        mLine.sources.push(src);\n                    }\n                });\n                delete mLine.ssrcs;\n            }\n\n            // split ssrcs in ssrc groups\n            if (typeof mLine.ssrcGroups !== 'undefined' && Array.isArray(mLine.ssrcGroups)) {\n                mLine.ssrcGroups.forEach(ssrcGroup => {\n                    if (typeof ssrcGroup.ssrcs === 'string') {\n                        ssrcGroup.ssrcs = ssrcGroup.ssrcs.split(' ');\n                    }\n                });\n            }\n        });\n    }\n\n    return session;\n};\n\nexport default {\n    write,\n    parse\n};\n","/* Copyright @ 2015 - Present, 8x8 Inc\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport clonedeep from 'lodash.clonedeep';\n\nimport transform from './transform.js';\n\nconst PLAN_B_MIDS = [ 'audio', 'video', 'data' ];\nconst findSimGroup = ssrcGroup => ssrcGroup.find(grp => grp.semantics === 'SIM');\nconst findFidGroup = ssrcGroup => ssrcGroup.find(grp => grp.semantics === 'FID');\n\n/**\n * Add the ssrcs of the SIM group and their corresponding FID group ssrcs\n * to the m-line.\n * @param {Object} mLine - The m-line to which ssrcs have to be added.\n * @param {Object} simGroup - The SIM group whose ssrcs have to be added to\n * the m-line.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array<Object>} sourceList - array containing all the sources.\n */\nfunction addSimGroupSources(mLine, simGroup, sourceGroups, sourceList) {\n    if (!mLine || !simGroup) {\n        return;\n    }\n    const findSourcebyId = src => sourceList.find(source => source.id.toString() === src);\n\n    simGroup.ssrcs.forEach(src => {\n        mLine.sources.push(findSourcebyId(src));\n\n        // find the related FID group member for this ssrc.\n        const relatedFidGroup = sourceGroups[parseInt(src, 10)].find(grp => grp.semantics === 'FID');\n\n        if (relatedFidGroup) {\n            const relatedSsrc = relatedFidGroup.ssrcs.find(s => s !== src);\n\n            mLine.sources.push(findSourcebyId(relatedSsrc));\n            mLine.ssrcGroups.push(relatedFidGroup);\n        }\n    });\n\n    // Add the SIM group last.\n    mLine.ssrcGroups.push(simGroup);\n}\n\n/**\n * Add ssrcs and ssrc-groups to the m-line. When a primary ssrc, i.e., the\n * first ssrc in a SIM group is passed, all the other ssrcs from the SIM\n * group and the other ssrcs from the related FID groups are added to the same\n * m-line since they all belong to the same remote source. Since the ssrcs are\n * not guaranteed to be in the correct order, try to find if a SIM group exists,\n * if not, just add the FID group.\n * @param {Object} mLine - The m-line to which ssrcs have to be added.\n * @param {Object} ssrc - the primary ssrc.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array<Object>} sourceList - array containing all the sources.\n * @returns {void}\n */\nfunction addSourcesToMline(mLine, ssrc, sourceGroups, sourceList) {\n    if (!mLine || !ssrc) {\n        return;\n    }\n    mLine.sources = [];\n    mLine.ssrcGroups = [];\n\n    // If there are no associated ssrc-groups, just add the ssrc and msid.\n    if (!sourceGroups[ssrc.id]) {\n        mLine.sources.push(ssrc);\n        mLine.msid = ssrc.msid;\n\n        return;\n    }\n    const findSourcebyId = src => sourceList.find(source => source.id.toString() === src);\n\n    // Find the SIM and FID groups that this ssrc belongs to.\n    const simGroup = findSimGroup(sourceGroups[ssrc.id]);\n    const fidGroup = findFidGroup(sourceGroups[ssrc.id]);\n\n    // Add the ssrcs for the SIM group and their corresponding FID groups.\n    if (simGroup) {\n        addSimGroupSources(mLine, simGroup, sourceGroups, sourceList);\n    } else if (fidGroup) {\n        // check if the other ssrc from this FID group is part of a SIM group\n        const otherSsrc = fidGroup.ssrcs.find(s => s !== ssrc);\n        const simGroup2 = findSimGroup(sourceGroups[otherSsrc]);\n\n        if (simGroup2) {\n            addSimGroupSources(mLine, simGroup2, sourceGroups, sourceList);\n        } else {\n            // Add the FID group ssrcs.\n            fidGroup.ssrcs.forEach(src => {\n                mLine.sources.push(findSourcebyId(src));\n            });\n            mLine.ssrcGroups.push(fidGroup);\n        }\n    }\n\n    // Set the msid for the media description using the msid attribute of the ssrcs.\n    mLine.msid = mLine.sources[0].msid;\n}\n\n/**\n * Check if a new SDP requests an ICE restart.\n * @param {Object} - the parsed new SDP\n * @param {Object} - the parsed previous SDP\n * @returns {Boolean} - Returns true if an ICE restart is requested otherwise false.\n */\nfunction checkForIceRestart(newDesc, oldDesc) {\n    if (!newDesc || !oldDesc || newDesc.media.length === 0 || oldDesc.media.length === 0) {\n        return false;\n    }\n\n    const newMLine = newDesc.media[0];\n    const oldMLine = oldDesc.media[0];\n\n    return newMLine.iceUfrag !== oldMLine.iceUfrag || newMLine.icePwd !== oldMLine.icePwd;\n}\n\n/**\n * Returns a rejected m-line (with port 0) if its exists in the provided description.\n * @param {Object} desc - the current remote description.\n * @param {String} mediaType - media type.\n * @returns {Object}\n */\nfunction checkForRejectedMlines(desc, mediaType) {\n    if (!desc || !mediaType) {\n        return null;\n    }\n\n    return desc.media.find(m => m.port === 0 && m.type === mediaType);\n}\n\n/**\n * Checks if there is a mline for the given ssrc or its related primary ssrc.\n * We always implode the SIM group to the first ssrc in the SIM group before sRD,\n * so we also check if mline for that ssrc exists.\n * For example:\n * If the following ssrcs are in a SIM group,\n * <ssrc-group xmlns=\\\"urn:xmpp:jingle:apps:rtp:ssma:0\\\" semantics=\\\"SIM\\\">\n *        <source ssrc=\\\"1806330949\\\"/>\n *        <source ssrc=\\\"4173145196\\\"/>\n *        <source ssrc=\\\"2002632207\\\"/>\n * </ssrc-group>\n * This method returns true for any one of the 3 ssrcs if there is a mline for 1806330949.\n * @param {Object} ssrc - ssrc to check.\n * @param {Object} sourceGroups - inverted source-group map.\n * @param {Array<Object>} mlines - mlines in the description\n\n * @returns {Boolean} - Returns true if mline for the given ssrc or the related primary ssrc\n * exists, returns false otherwise.\n */\nfunction checkIfMlineForSsrcExists(ssrc, sourceGroups, mlines) {\n    const findMatchingMline = mline => {\n        if (mline.sources) {\n            return mline.sources.some(source => source.id === ssrc.id);\n        }\n\n        return false;\n    };\n\n    if (!mlines.find(findMatchingMline)) {\n        // check if this ssrc is member of a SIM group. If so, check if there\n        // is a matching m-line for the primary ssrc of the SIM group.\n        if (!sourceGroups[ssrc.id]) {\n            return false;\n        }\n        const simGroup = findSimGroup(sourceGroups[ssrc.id]);\n        const fidGroup = findFidGroup(sourceGroups[ssrc.id]);\n\n        if (simGroup) {\n            return mlines.some(mline => mline.sources\n                && mline.sources.some(src => src.id.toString() === simGroup.ssrcs[0]));\n        } else if (fidGroup && ssrc.id.toString() !== fidGroup.ssrcs[0]) {\n            const otherSsrc = { id: fidGroup.ssrcs[0] };\n\n            return checkIfMlineForSsrcExists(otherSsrc, sourceGroups, mlines);\n\n        }\n\n        return false;\n    }\n\n    return true;\n}\n\n/**\n * Create an inverted sourceGroup map to put all the grouped ssrcs\n * in the same m-line.\n * @param {Array<Object>} sourceGroups\n * @returns {Object} - An inverted sourceGroup map.\n */\nfunction createSourceGroupMap(sourceGroups) {\n    const ssrc2group = {};\n\n    if (!sourceGroups || !Array.isArray(sourceGroups)) {\n        return ssrc2group;\n    }\n    sourceGroups.forEach(group => {\n        if (group.ssrcs && Array.isArray(group.ssrcs)) {\n            group.ssrcs.forEach(ssrc => {\n                if (typeof ssrc2group[ssrc] === 'undefined') {\n                    ssrc2group[ssrc] = [];\n                }\n                ssrc2group[ssrc].push(group);\n            });\n        }\n    });\n\n    return ssrc2group;\n}\n\n/**\n * Interop provides an API for tranforming a Plan B SDP to a Unified Plan SDP and\n * vice versa.\n */\nexport class Interop {\n    /**\n     * This method transforms a Unified Plan SDP to an equivalent Plan B SDP.\n     * @param {RTCSessionDescription} description - The description in Unified plan format.\n     * @returns RTCSessionDescription - The transformed session description.\n     */\n    toPlanB(description) {\n        if (!description || typeof description.sdp !== 'string') {\n            console.warn('An empty description was passed as an argument.');\n\n            return description;\n        }\n\n        // Objectify the SDP for easier manipulation.\n        const session = transform.parse(description.sdp);\n\n        // If the SDP contains no media, there's nothing to transform.\n        if (!session.media || !session.media.length) {\n            console.warn('The description has no media.');\n\n            return description;\n        }\n\n        // Make sure this is a unified plan sdp\n        if (session.media.every(m => PLAN_B_MIDS.indexOf(m.mid) !== -1)) {\n            console.warn('The description does not look like unified plan sdp');\n\n            return description;\n        }\n\n        const media = {};\n        const sessionMedia = session.media;\n\n        session.media = [];\n        sessionMedia.forEach(mLine => {\n            const type = mLine.type;\n\n            if (type === 'application') {\n                mLine.mid = 'data';\n                media[mLine.mid] = mLine;\n\n                return;\n            }\n            if (typeof media[type] === 'undefined') {\n                const bLine = clonedeep(mLine);\n\n                // Copy the msid attribute to all the ssrcs if they belong to the same source group\n                if (bLine.sources && Array.isArray(bLine.sources)) {\n                    bLine.sources.forEach(source => {\n                        mLine.msid ? source.msid = mLine.msid : delete source.msid;\n                    });\n                }\n\n                // Do not signal the FID groups if there is no msid attribute present\n                // on the sources as sesison-accept with this source info will fail strophe\n                // validation and the session will not be established. This behavior is seen\n                // on Firefox (with RTX enabled) when no video source is added at the join time.\n                // FF generates two recvonly ssrcs with no msid and a corresponding FID group in\n                // this case.\n                if (!bLine.ssrcGroups || !mLine.msid) {\n                    bLine.ssrcGroups = [];\n                }\n                delete bLine.msid;\n                bLine.mid = type;\n                media[type] = bLine;\n            } else if (mLine.msid) {\n                // Add sources and source-groups to the existing m-line of the same media type.\n                if (mLine.sources && Array.isArray(mLine.sources)) {\n                    media[type].sources = (media[type].sources || []).concat(mLine.sources);\n                }\n                if (typeof mLine.ssrcGroups !== 'undefined' && Array.isArray(mLine.ssrcGroups)) {\n                    media[type].ssrcGroups = media[type].ssrcGroups.concat(mLine.ssrcGroups);\n                }\n            }\n        });\n        session.media = Object.values(media);\n\n        // Bundle the media only if it is active.\n        const bundle = [];\n\n        Object.values(media).forEach(mline => {\n            if (mline.direction !== 'inactive') {\n                bundle.push(mline.mid);\n            }\n        });\n\n        // We regenerate the BUNDLE group with the new mids.\n        session.groups.forEach(group => {\n            if (group.type === 'BUNDLE') {\n                group.mids = bundle.join(' ');\n            }\n        });\n\n        // msid semantic\n        session.msidSemantic = {\n            semantic: 'WMS',\n            token: '*'\n        };\n        const resStr = transform.write(session);\n\n        return new RTCSessionDescription({\n            type: description.type,\n            sdp: resStr\n        });\n    }\n\n    /**\n     * This method transforms a Plan B SDP to an equivalent Unified Plan SDP.\n     * @param {RTCSessionDescription} description - The description in plan-b format.\n     * @param {RTCSessionDescription} current - The current description set on\n     * the peerconnection in Unified-plan format, i.e., the readonly attribute\n     * remoteDescription on the RTCPeerConnection object.\n     * @returns RTCSessionDescription - The transformed session description.\n     */\n    toUnifiedPlan(description, current = null) {\n        if (!description || typeof description.sdp !== 'string') {\n            console.warn('An empty description was passed as an argument.');\n\n            return description;\n        }\n\n        // Objectify the SDP for easier manipulation.\n        const session = transform.parse(description.sdp);\n\n        // If the SDP contains no media, there's nothing to transform.\n        if (!session.media || !session.media.length) {\n            console.warn('The description has no media.');\n\n            return description;\n        }\n\n        // Make sure this is a plan-b sdp.\n        if (session.media.length > 3 || session.media.every(m => PLAN_B_MIDS.indexOf(m.mid) === -1)) {\n            console.warn('The description does not look like plan-b');\n\n            return description;\n        }\n        const currentDesc = current ? transform.parse(current.sdp) : null;\n        const iceRestart = checkForIceRestart(session, currentDesc);\n        const newIceUfrag = session.media[0].iceUfrag;\n        const newIcePwd = session.media[0].icePwd;\n        const newFingerprint = session.media[0].fingerprint;\n        const media = {};\n\n        session.media.forEach(mLine => {\n            const type = mLine.type;\n\n            if (type === 'application') {\n                if (!currentDesc || !currentDesc.media) {\n                    const newMline = clonedeep(mLine);\n\n                    newMline.mid = Object.keys(media).length.toString();\n                    media[mLine.mid] = newMline;\n\n                    return;\n                }\n                const mLineForData = currentDesc.media.findIndex(m => m.type === type);\n\n                if (mLineForData) {\n                    currentDesc.media[mLineForData] = mLine;\n                    currentDesc.media[mLineForData].mid = mLineForData;\n                }\n\n                return;\n            }\n\n            // Create an inverted sourceGroup map here to put all the grouped SSRCs in the same m-line.\n            const ssrc2group = createSourceGroupMap(mLine.ssrcGroups);\n\n            // If there are no sources advertised for a media type, add the description if this is the first\n            // remote offer, i.e., no current description was passed. Chrome in Unified plan does not produce\n            // recvonly ssrcs unlike Firefox and Safari.\n            if (!mLine.sources) {\n                if (!currentDesc) {\n                    const newMline = clonedeep(mLine);\n\n                    newMline.mid = Object.keys(media).length.toString();\n                    media[mLine.mid] = newMline;\n                }\n\n                return;\n            }\n            mLine.sources.forEach((ssrc, idx) => {\n                // Do not add the receive-only ssrcs that Jicofo sends in the source-add.\n                // These ssrcs do not have the \"msid\" attribute set.\n                if (!ssrc.msid) {\n                    return;\n                }\n\n                // If there is no description set on the peerconnection, create new m-lines.\n                if (!currentDesc || !currentDesc.media) {\n                    if (checkIfMlineForSsrcExists(ssrc, ssrc2group, Object.values(media))) {\n                        return;\n                    }\n                    const newMline = clonedeep(mLine);\n\n                    newMline.mid = Object.keys(media).length.toString();\n                    newMline.direction = idx\n                        ? 'sendonly'\n                        : mLine.direction === 'sendonly' ? 'sendonly' : 'sendrecv';\n                    newMline.bundleOnly = undefined;\n                    addSourcesToMline(newMline, ssrc, ssrc2group, mLine.sources);\n                    media[newMline.mid] = newMline;\n\n                    return;\n                }\n\n                // Create if the m-line for the ssrcs already exists.\n                if (checkIfMlineForSsrcExists(ssrc, ssrc2group, currentDesc.media)) {\n                    return;\n                }\n\n                // Check if a rejected m-line with port 0 exists and recycle it if so.\n                const existingMline = checkForRejectedMlines(currentDesc, type);\n\n                if (existingMline) {\n                    existingMline.port = mLine.port;\n                    existingMline.direction = 'sendonly';\n                    addSourcesToMline(existingMline, ssrc, ssrc2group, mLine.sources);\n\n                    return;\n                }\n\n                // Append a new m-line.\n                const newMline = clonedeep(mLine);\n\n                newMline.mid = currentDesc.media.length.toString();\n                newMline.direction = 'sendonly';\n                addSourcesToMline(newMline, ssrc, ssrc2group, mLine.sources);\n                currentDesc.media.push(newMline);\n            });\n        });\n        session.media = currentDesc ? currentDesc.media : Object.values(media);\n        const mids = [];\n\n        session.media.forEach(mLine => {\n            mids.push(mLine.mid);\n            if (iceRestart) {\n                mLine.iceUfrag = newIceUfrag;\n                mLine.icePwd = newIcePwd;\n                mLine.fingerprint = newFingerprint;\n            }\n        });\n\n        // We regenerate the BUNDLE group (since we regenerated the mids)\n        session.groups.forEach(group => {\n            if (group.type === 'BUNDLE') {\n                group.mids = mids.join(' ');\n            }\n        });\n\n        // msid semantic\n        session.msidSemantic = {\n            semantic: 'WMS',\n            token: '*'\n        };\n\n        // Increment the session version every time.\n        session.origin.sessionVersion++;\n        const resultSdp = transform.write(session);\n\n        return new RTCSessionDescription({\n            type: description.type,\n            sdp: resultSdp\n        });\n    }\n}\n","export enum SignalingEvents {\n    /**\n     * Event triggered when participant's muted status changes.\n     * @param {string} endpointId the track owner's identifier (MUC nickname)\n     * @param {MediaType} mediaType \"audio\" or \"video\"\n     * @param {boolean} isMuted the new muted state\n     */\n    PEER_MUTED_CHANGED = 'signaling.peerMuted',\n\n    /**\n     * Event triggered when participant's video type changes.\n     * @param {string} endpointId the video owner's ID (MUC nickname)\n     * @param {VideoType} videoType the new value\n     */\n    PEER_VIDEO_TYPE_CHANGED = 'signaling.peerVideoType'\n}\n\n// exported for backward compatibility\nexport const PEER_MUTED_CHANGED = SignalingEvents.PEER_MUTED_CHANGED;\nexport const PEER_VIDEO_TYPE_CHANGED = SignalingEvents.PEER_VIDEO_TYPE_CHANGED;\n","\nimport Listenable from '../../modules/util/Listenable';\nimport { MediaType } from '../../service/RTC/MediaType';\n\n/**\n * @typedef {string} EndpointId\n */\n/**\n * @typedef {string} SourceName\n */\n/**\n * @typedef {Object} SourceInfo\n *\n * @property {SourceName} sourceName - Name of the media source.\n * @property {boolean} [muted=false] - Tells if the source is muted (paused?).\n * @property {string} [videoType] - Type of the video for video type.\n */\n\n/**\n * Generates a source name.\n *\n * @param {EndpointId} endpointId - Jitsi Endpoint Id.\n * @param {MediaType} mediaType - the media type string.\n * @param {number} trackIdx - Track index (or sender idx? - to be figured out) starting from 0.\n * @returns {SourceName} eg. endpointA-v0\n */\nexport function getSourceNameForJitsiTrack(endpointId, mediaType, trackIdx) {\n    const firstLetterOfMediaType = mediaType.substring(0, 1);\n\n    return `${endpointId}-${firstLetterOfMediaType}${trackIdx}`;\n}\n\n/**\n * Extracts MediaType from give source name (must be in the correct format as generated by\n * {@link getSourceNameForJitsiTrack}).\n *\n * @param {SourceName} sourceName - the source name.\n * @returns {MediaType}\n */\nexport function getMediaTypeFromSourceName(sourceName) {\n    const firstLetterOfMediaTypeIdx = sourceName.indexOf('-') + 1;\n\n    if (firstLetterOfMediaTypeIdx <= 0) {\n        throw new Error(`Invalid source name: ${sourceName}`);\n    }\n\n    const firstLetterOfMediaType = sourceName.substr(firstLetterOfMediaTypeIdx, 1);\n\n    for (const type of Object.values(MediaType)) {\n        if (type.substr(0, 1) === firstLetterOfMediaType) {\n            return type;\n        }\n    }\n\n    throw new Error(`Invalid source name: ${sourceName}`);\n}\n\n/**\n * An object that carries the info about specific media type advertised by\n * participant in the signaling channel.\n * @typedef {Object} PeerMediaInfo\n * @property {boolean} muted indicates if the media is currently muted\n * @property {VideoType|undefined} videoType the type of the video if applicable\n */\n\n/**\n * Interface used to expose the information carried over the signaling channel\n * which is not available to the RTC module in the media SDP.\n *\n * @interface SignalingLayer\n */\nexport default class SignalingLayer extends Listenable {\n\n    /**\n     * Obtains the endpoint ID for given SSRC.\n     * @param {number} ssrc the SSRC number.\n     * @return {string|null} the endpoint ID for given media SSRC.\n     */\n    getSSRCOwner(ssrc) { // eslint-disable-line no-unused-vars\n        throw new Error('not implemented');\n    }\n\n    /**\n     * Obtains the info about given media advertised in the MUC presence of\n     * the participant identified by the given MUC JID.\n     * @param {string} owner the MUC jid of the participant for whom\n     * {@link PeerMediaInfo} will be obtained.\n     * @param {MediaType} mediaType the type of the media for which presence\n     * info will be obtained.\n     * @return {PeerMediaInfo|null} presenceInfo an object with media presence\n     * info or <tt>null</tt> either if there is no presence available for given\n     * JID or if the media type given is invalid.\n     *\n     * @deprecated This method is to be replaced with getPeerSourceInfo.\n     */\n    getPeerMediaInfo(owner, mediaType) { // eslint-disable-line no-unused-vars\n        throw new Error('not implemented');\n    }\n\n    /**\n     * Obtains the info about a source for given name and endpoint ID.\n     * @param {EndpointId} owner - The owner's endpoint ID.\n     * @param {SourceName} sourceName - The name of the source for which the info is to be obtained.\n     * @returns {SourceInfo | undefined}\n     */\n    getPeerSourceInfo(owner, sourceName) { // eslint-disable-line no-unused-vars\n        throw new Error('not implemented');\n    }\n\n    /**\n     * Obtains the source name for given SSRC.\n     * @param {number} ssrc the track's SSRC identifier.\n     * @returns {SourceName | undefined} the track's source name.\n     */\n    getTrackSourceName(ssrc) { // eslint-disable-line no-unused-vars\n        throw new Error('not implemented');\n    }\n}\n","import * as transform from 'sdp-transform';\n\n/**\n * Parses the primary SSRC of given SSRC group.\n * @param {object} group the SSRC group object as defined by the 'sdp-transform'\n * @return {Number} the primary SSRC number\n */\nexport function parsePrimarySSRC(group) {\n    return parseInt(group.ssrcs.split(' ')[0], 10);\n}\n\n/**\n * Parses the secondary SSRC of given SSRC group.\n * @param {object} group the SSRC group object as defined by the 'sdp-transform'\n * @return {Number} the secondary SSRC number\n */\nexport function parseSecondarySSRC(group) {\n    return parseInt(group.ssrcs.split(' ')[1], 10);\n}\n\n/**\n * Tells how many distinct SSRCs are contained in given media line.\n * @param {Object} mLine the media line object as defined by 'sdp-transform' lib\n * @return {number}\n */\nfunction _getSSRCCount(mLine) {\n    if (!mLine.ssrcs) {\n        return 0;\n    }\n\n    return mLine.ssrcs\n        .map(ssrcInfo => ssrcInfo.id)\n        .filter((ssrc, index, array) => array.indexOf(ssrc) === index)\n        .length;\n}\n\n/**\n * A wrapper around 'sdp-transform' media description object which provides\n * utility methods for common SDP/SSRC related operations.\n */\nclass MLineWrap {\n\n    /**\n     * Creates new <tt>MLineWrap</t>>\n     * @param {Object} mLine the media line object as defined by 'sdp-transform'\n     * lib.\n     */\n    constructor(mLine) {\n        if (!mLine) {\n            throw new Error('mLine is undefined');\n        }\n\n        this.mLine = mLine;\n    }\n\n    /**\n     * Getter for the mLine's \"ssrcs\" array. If the array was undefined an empty\n     * one will be preassigned.\n     *\n     * @return {Array<Object>} an array of 'sdp-transform' SSRC attributes\n     * objects.\n     */\n    get ssrcs() {\n        if (!this.mLine.ssrcs) {\n            this.mLine.ssrcs = [];\n        }\n\n        return this.mLine.ssrcs;\n    }\n\n    /**\n     * Setter for the mLine's \"ssrcs\" array.\n     *\n     * @param {Array<Object>} ssrcs an array of 'sdp-transform' SSRC attributes\n     * objects.\n     */\n    set ssrcs(ssrcs) {\n        this.mLine.ssrcs = ssrcs;\n    }\n\n    /**\n     * Returns the direction of the underlying media description.\n     * @return {string} the media direction name as defined in the SDP.\n     */\n    get direction() {\n        return this.mLine.direction;\n    }\n\n    /**\n     * Modifies the direction of the underlying media description.\n     * @param {string} direction the new direction to be set\n     */\n    set direction(direction) {\n        this.mLine.direction = direction;\n    }\n\n    /**\n     * Exposes the SSRC group array of the underlying media description object.\n     * @return {Array.<Object>}\n     */\n    get ssrcGroups() {\n        if (!this.mLine.ssrcGroups) {\n            this.mLine.ssrcGroups = [];\n        }\n\n        return this.mLine.ssrcGroups;\n    }\n\n    /**\n     * Modifies the SSRC groups array of the underlying media description\n     * object.\n     * @param {Array.<Object>} ssrcGroups\n     */\n    set ssrcGroups(ssrcGroups) {\n        this.mLine.ssrcGroups = ssrcGroups;\n    }\n\n    /**\n     * Obtains value from SSRC attribute.\n     * @param {number} ssrcNumber the SSRC number for which attribute is to be\n     * found\n     * @param {string} attrName the name of the SSRC attribute to be found.\n     * @return {string|undefined} the value of SSRC attribute or\n     * <tt>undefined</tt> if no such attribute exists.\n     */\n    getSSRCAttrValue(ssrcNumber, attrName) {\n        const attribute = this.ssrcs.find(\n            ssrcObj => ssrcObj.id === ssrcNumber\n            && ssrcObj.attribute === attrName);\n\n\n        return attribute && attribute.value;\n    }\n\n    /**\n     * Removes all attributes for given SSRC number.\n     * @param {number} ssrcNum the SSRC number for which all attributes will be\n     * removed.\n     */\n    removeSSRC(ssrcNum) {\n        if (!this.mLine.ssrcs || !this.mLine.ssrcs.length) {\n            return;\n        }\n\n        this.mLine.ssrcs\n            = this.mLine.ssrcs.filter(ssrcObj => ssrcObj.id !== ssrcNum);\n    }\n\n    /**\n     * Adds SSRC attribute\n     * @param {object} ssrcObj the SSRC attribute object as defined in\n     * the 'sdp-transform' lib.\n     */\n    addSSRCAttribute(ssrcObj) {\n        this.ssrcs.push(ssrcObj);\n    }\n\n    /**\n     * Finds a SSRC group matching both semantics and SSRCs in order.\n     * @param {string} semantics the name of the semantics\n     * @param {string} [ssrcs] group SSRCs as a string (like it's defined in\n     * SSRC group object of the 'sdp-transform' lib) e.g. \"1232546 342344 25434\"\n     * @return {object|undefined} the SSRC group object or <tt>undefined</tt> if\n     * not found.\n     */\n    findGroup(semantics, ssrcs) {\n        return this.ssrcGroups.find(\n            group =>\n                group.semantics === semantics\n                    && (!ssrcs || ssrcs === group.ssrcs));\n    }\n\n    /**\n     * Finds all groups matching given semantic's name.\n     * @param {string} semantics the name of the semantics\n     * @return {Array.<object>} an array of SSRC group objects as defined by\n     * the 'sdp-transform' lib.\n     */\n    findGroups(semantics) {\n        return this.ssrcGroups.filter(\n            group => group.semantics === semantics);\n    }\n\n    /**\n     * Finds all groups matching given semantic's name and group's primary SSRC.\n     * @param {string} semantics the name of the semantics\n     * @param {number} primarySSRC the primary SSRC number to be matched\n     * @return {Object} SSRC group object as defined by the 'sdp-transform' lib.\n     */\n    findGroupByPrimarySSRC(semantics, primarySSRC) {\n        return this.ssrcGroups.find(\n            group => group.semantics === semantics\n                && parsePrimarySSRC(group) === primarySSRC);\n    }\n\n    /**\n     * @param {string|null} msid the media stream id or <tt>null</tt> to match\n     * the first SSRC object with any 'msid' value.\n     * @return {Object|undefined} the SSRC object as defined by 'sdp-transform'\n     * lib.\n     */\n    findSSRCByMSID(msid) {\n        return this.ssrcs.find(\n            ssrcObj => ssrcObj.attribute === 'msid'\n                && (msid === null || ssrcObj.value === msid));\n    }\n\n    /**\n     * Gets the SSRC count for the underlying media description.\n     * @return {number}\n     */\n    getSSRCCount() {\n        return _getSSRCCount(this.mLine);\n    }\n\n    /**\n     * Checks whether the underlying media description contains any SSRC groups.\n     * @return {boolean} <tt>true</tt> if there are any SSRC groups or\n     * <tt>false</tt> otherwise.\n     */\n    containsAnySSRCGroups() {\n        return this.mLine.ssrcGroups !== undefined;\n    }\n\n    /**\n     * Finds the primary video SSRC.\n     * @returns {number|undefined} the primary video ssrc\n     * @throws Error if the underlying media description is not a video\n     */\n    getPrimaryVideoSsrc() {\n        const mediaType = this.mLine.type;\n\n        if (mediaType !== 'video') {\n            throw new Error(\n                `getPrimarySsrc doesn't work with '${mediaType}'`);\n        }\n\n        const numSsrcs = _getSSRCCount(this.mLine);\n\n        if (numSsrcs === 1) {\n            // Not using \"ssrcs\" getter on purpose here\n            return this.mLine.ssrcs[0].id;\n        }\n\n        // Look for a SIM, FID, or FEC-FR group\n        if (this.mLine.ssrcGroups) {\n            const simGroup = this.findGroup('SIM');\n\n            if (simGroup) {\n                return parsePrimarySSRC(simGroup);\n            }\n            const fidGroup = this.findGroup('FID');\n\n            if (fidGroup) {\n                return parsePrimarySSRC(fidGroup);\n            }\n            const fecGroup = this.findGroup('FEC-FR');\n\n            if (fecGroup) {\n                return parsePrimarySSRC(fecGroup);\n            }\n        }\n\n    }\n\n    /**\n     * Obtains RTX SSRC from the underlying video description (the\n     * secondary SSRC of the first \"FID\" group found)\n     * @param {number} primarySsrc the video ssrc for which to find the\n     * corresponding rtx ssrc\n     * @returns {number|undefined} the rtx ssrc (or undefined if there isn't\n     * one)\n     */\n    getRtxSSRC(primarySsrc) {\n        const fidGroup = this.findGroupByPrimarySSRC('FID', primarySsrc);\n\n\n        return fidGroup && parseSecondarySSRC(fidGroup);\n    }\n\n    /**\n     * Obtains all SSRCs contained in the underlying media description.\n     * @return {Array.<number>} an array with all SSRC as numbers.\n     */\n    getSSRCs() {\n        return this.ssrcs\n            .map(ssrcInfo => ssrcInfo.id)\n            .filter((ssrc, index, array) => array.indexOf(ssrc) === index);\n    }\n\n    /**\n     * Obtains primary video SSRCs.\n     * @return {Array.<number>} an array of all primary video SSRCs as numbers.\n     * @throws Error if the wrapped media description is not a video.\n     */\n    getPrimaryVideoSSRCs() {\n        const mediaType = this.mLine.type;\n\n        if (mediaType !== 'video') {\n            throw new Error(\n                `getPrimaryVideoSSRCs doesn't work with ${mediaType}`);\n        }\n\n        const videoSSRCs = this.getSSRCs();\n\n        for (const ssrcGroupInfo of this.ssrcGroups) {\n            // Right now, FID and FEC-FR groups are the only ones we parse to\n            // disqualify streams.  If/when others arise we'll\n            // need to add support for them here\n            if (ssrcGroupInfo.semantics === 'FID'\n                    || ssrcGroupInfo.semantics === 'FEC-FR') {\n                // secondary streams should be filtered out\n                const secondarySsrc = parseSecondarySSRC(ssrcGroupInfo);\n\n                videoSSRCs.splice(\n                    videoSSRCs.indexOf(secondarySsrc), 1);\n            }\n        }\n\n        return videoSSRCs;\n    }\n\n    /**\n     * Dumps all SSRC groups of this media description to JSON.\n     */\n    dumpSSRCGroups() {\n        return JSON.stringify(this.mLine.ssrcGroups);\n    }\n\n    /**\n     * Removes all SSRC groups which contain given SSRC number at any position.\n     * @param {number} ssrc the SSRC for which all matching groups are to be\n     * removed.\n     */\n    removeGroupsWithSSRC(ssrc) {\n        if (!this.mLine.ssrcGroups) {\n            return;\n        }\n\n        this.mLine.ssrcGroups = this.mLine.ssrcGroups\n            .filter(groupInfo => groupInfo.ssrcs.indexOf(`${ssrc}`) === -1);\n    }\n\n    /**\n     * Removes groups that match given semantics.\n     * @param {string} semantics e.g. \"SIM\" or \"FID\"\n     */\n    removeGroupsBySemantics(semantics) {\n        if (!this.mLine.ssrcGroups) {\n            return;\n        }\n\n        this.mLine.ssrcGroups\n            = this.mLine.ssrcGroups\n                .filter(groupInfo => groupInfo.semantics !== semantics);\n    }\n\n    /**\n     * Replaces SSRC (does not affect SSRC groups, but only attributes).\n     * @param {number} oldSSRC the old SSRC number\n     * @param {number} newSSRC the new SSRC number\n     */\n    replaceSSRC(oldSSRC, newSSRC) {\n        if (this.mLine.ssrcs) {\n            this.mLine.ssrcs.forEach(ssrcInfo => {\n                if (ssrcInfo.id === oldSSRC) {\n                    ssrcInfo.id = newSSRC;\n                }\n            });\n        }\n    }\n\n    /**\n     * Adds given SSRC group to this media description.\n     * @param {object} group the SSRC group object as defined by\n     * the 'sdp-transform' lib.\n     */\n    addSSRCGroup(group) {\n        this.ssrcGroups.push(group);\n    }\n}\n\n/**\n * Utility class for SDP manipulation using the 'sdp-transform' library.\n *\n * Typical use usage scenario:\n *\n * const transformer = new SdpTransformWrap(rawSdp);\n * const videoMLine = transformer.selectMedia('video);\n * if (videoMLine) {\n *     videoMLiner.addSSRCAttribute({\n *         id: 2342343,\n *         attribute: \"cname\",\n *         value: \"someCname\"\n *     });\n *     rawSdp = transformer.toRawSdp();\n * }\n */\nexport class SdpTransformWrap {\n\n    /**\n     * Creates new instance and parses the raw SDP into objects using\n     * 'sdp-transform' lib.\n     * @param {string} rawSDP the SDP in raw text format.\n     */\n    constructor(rawSDP) {\n        this.parsedSDP = transform.parse(rawSDP);\n    }\n\n    /**\n     * Selects all the m-lines from the SDP for a given media type.\n     *\n     * @param {string} mediaType the name of the media e.g. 'audio', 'video', 'data'.\n     * @return {MLineWrap|null} return {@link MLineWrap} instance for the media line or <tt>null</tt> if not found. The\n     * object returned references the underlying SDP state held by this <tt>SdpTransformWrap</tt> instance (it's not a\n     * copy).\n     */\n    selectMedia(mediaType) {\n        const selectedMLines = this.parsedSDP.media\n            .filter(mLine => mLine.type === mediaType)\n            .map(mLine => new MLineWrap(mLine));\n\n        return selectedMLines ?? null;\n    }\n\n    /**\n     * Converts the currently stored SDP state in this instance to raw text SDP\n     * format.\n     * @return {string}\n     */\n    toRawSDP() {\n        return transform.write(this.parsedSDP);\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport { getSourceNameForJitsiTrack } from '../../service/RTC/SignalingLayer';\nimport VideoType from '../../service/RTC/VideoType';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport { SdpTransformWrap } from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Fakes local SDP exposed to {@link JingleSessionPC} through the local\n * description getter. Modifies the SDP, so that it will contain muted local\n * video tracks description, even though their underlying {MediaStreamTrack}s\n * are no longer in the WebRTC peerconnection. That prevents from SSRC updates\n * being sent to Jicofo/remote peer and prevents sRD/sLD cycle on the remote\n * side.\n */\nexport default class LocalSdpMunger {\n\n    /**\n     * Creates new <tt>LocalSdpMunger</tt> instance.\n     *\n     * @param {TraceablePeerConnection} tpc\n     * @param {string} localEndpointId - The endpoint id of the local user.\n     */\n    constructor(tpc, localEndpointId) {\n        this.tpc = tpc;\n        this.localEndpointId = localEndpointId;\n    }\n\n    /**\n     * Makes sure that muted local video tracks associated with the parent\n     * {@link TraceablePeerConnection} are described in the local SDP. It's done\n     * in order to prevent from sending 'source-remove'/'source-add' Jingle\n     * notifications when local video track is muted (<tt>MediaStream</tt> is\n     * removed from the peerconnection).\n     *\n     * NOTE 1 video track is assumed\n     *\n     * @param {SdpTransformWrap} transformer the transformer instance which will\n     * be used to process the SDP.\n     * @return {boolean} <tt>true</tt> if there were any modifications to\n     * the SDP wrapped by <tt>transformer</tt>.\n     * @private\n     */\n    _addMutedLocalVideoTracksToSDP(transformer) {\n        // Go over each video tracks and check if the SDP has to be changed\n        const localVideos = this.tpc.getLocalTracks(MediaType.VIDEO);\n\n        if (!localVideos.length) {\n            return false;\n        } else if (localVideos.length !== 1) {\n            logger.error(\n                `${this.tpc} there is more than 1 video track ! `\n                    + 'Strange things may happen !', localVideos);\n        }\n\n        const videoMLine = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n        if (!videoMLine) {\n            logger.debug(\n                `${this.tpc} unable to hack local video track SDP`\n                    + '- no \"video\" media');\n\n            return false;\n        }\n\n        let modified = false;\n\n        for (const videoTrack of localVideos) {\n            const muted = videoTrack.isMuted();\n            const mediaStream = videoTrack.getOriginalStream();\n            const isCamera = videoTrack.videoType === VideoType.CAMERA;\n\n            // During the mute/unmute operation there are periods of time when\n            // the track's underlying MediaStream is not added yet to\n            // the PeerConnection. The SDP needs to be munged in such case.\n            const isInPeerConnection\n                = mediaStream && this.tpc.isMediaStreamInPc(mediaStream);\n            const shouldFakeSdp = isCamera && (muted || !isInPeerConnection);\n\n            if (!shouldFakeSdp) {\n                continue; // eslint-disable-line no-continue\n            }\n\n            // Inject removed SSRCs\n            const requiredSSRCs\n                = this.tpc.isSimulcastOn()\n                    ? this.tpc.simulcast.ssrcCache\n                    : [ this.tpc.sdpConsistency.cachedPrimarySsrc ];\n\n            if (!requiredSSRCs.length) {\n                logger.error(`No SSRCs stored for: ${videoTrack} in ${this.tpc}`);\n\n                continue; // eslint-disable-line no-continue\n            }\n\n            modified = true;\n\n            // We need to fake sendrecv.\n            // NOTE the SDP produced here goes only to Jicofo and is never set\n            // as localDescription. That's why\n            // TraceablePeerConnection.mediaTransferActive is ignored here.\n            videoMLine.direction = MediaDirection.SENDRECV;\n\n            // Check if the recvonly has MSID\n            const primarySSRC = requiredSSRCs[0];\n\n            // FIXME The cname could come from the stream, but may turn out to\n            // be too complex. It is fine to come up with any value, as long as\n            // we only care about the actual SSRC values when deciding whether\n            // or not an update should be sent.\n            const primaryCname = `injected-${primarySSRC}`;\n\n            for (const ssrcNum of requiredSSRCs) {\n                // Remove old attributes\n                videoMLine.removeSSRC(ssrcNum);\n\n                // Inject\n                videoMLine.addSSRCAttribute({\n                    id: ssrcNum,\n                    attribute: 'cname',\n                    value: primaryCname\n                });\n                videoMLine.addSSRCAttribute({\n                    id: ssrcNum,\n                    attribute: 'msid',\n                    value: videoTrack.storedMSID\n                });\n            }\n            if (requiredSSRCs.length > 1) {\n                const group = {\n                    ssrcs: requiredSSRCs.join(' '),\n                    semantics: 'SIM'\n                };\n\n                if (!videoMLine.findGroup(group.semantics, group.ssrcs)) {\n                    // Inject the group\n                    videoMLine.addSSRCGroup(group);\n                }\n            }\n\n            // Insert RTX\n            // FIXME in P2P RTX is used by Chrome regardless of config option\n            // status. Because of that 'source-remove'/'source-add'\n            // notifications are still sent to remove/add RTX SSRC and FID group\n            if (!this.tpc.options.disableRtx) {\n                this.tpc.rtxModifier.modifyRtxSsrcs2(videoMLine);\n            }\n        }\n\n        return modified;\n    }\n\n    /**\n     * Returns a string that can be set as the MSID attribute for a source.\n     *\n     * @param {string} mediaType - Media type of the source.\n     * @param {string} trackId - Id of the MediaStreamTrack associated with the source.\n     * @param {string} streamId - Id of the MediaStream associated with the source.\n     * @returns {string|null}\n     */\n    _generateMsidAttribute(mediaType, trackId, streamId = null) {\n        if (!(mediaType && trackId)) {\n            logger.warn(`Unable to munge local MSID - track id=${trackId} or media type=${mediaType} is missing`);\n\n            return null;\n        }\n        const pcId = this.tpc.id;\n\n        // Handle a case on Firefox when the browser doesn't produce a 'a:ssrc' line with the 'msid' attribute or has\n        // '-' for the stream id part of the msid line. Jicofo needs an unique identifier to be associated with a ssrc\n        // and uses the msid for that.\n        if (streamId === '-' || !streamId) {\n            return `${this.localEndpointId}-${mediaType}-${pcId} ${trackId}-${pcId}`;\n        }\n\n        return `${streamId}-${pcId} ${trackId}-${pcId}`;\n    }\n\n    /**\n     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending the id of {@link LocalSdpMunger#tpc} at the end,\n     * preceding by a dash sign.\n     *\n     * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be\n     * modified in place.\n     * @returns {void}\n     * @private\n     */\n    _transformMediaIdentifiers(mediaSection) {\n        const mediaType = mediaSection.mLine?.type;\n        const pcId = this.tpc.id;\n        const sourceToMsidMap = new Map();\n\n        for (const ssrcLine of mediaSection.ssrcs) {\n            switch (ssrcLine.attribute) {\n            case 'cname':\n            case 'label':\n            case 'mslabel':\n                ssrcLine.value = ssrcLine.value && `${ssrcLine.value}-${pcId}`;\n                break;\n            case 'msid': {\n                if (ssrcLine.value) {\n                    const streamAndTrackIDs = ssrcLine.value.split(' ');\n\n                    let streamId = streamAndTrackIDs[0];\n                    const trackId = streamAndTrackIDs[1];\n\n                    // eslint-disable-next-line max-depth\n                    if (FeatureFlags.isMultiStreamSupportEnabled()\n                        && this.tpc.usesUnifiedPlan()\n                        && mediaType === MediaType.VIDEO) {\n\n                        // eslint-disable-next-line max-depth\n                        if (streamId === '-' || !streamId) {\n                            streamId = `${this.localEndpointId}-${mediaType}`;\n                        }\n\n                        // eslint-disable-next-line max-depth\n                        if (!sourceToMsidMap.has(trackId)) {\n                            streamId = `${streamId}-${sourceToMsidMap.size}`;\n                            sourceToMsidMap.set(trackId, streamId);\n                        }\n                    }\n\n                    ssrcLine.value = this._generateMsidAttribute(mediaType, trackId, sourceToMsidMap.get(trackId));\n                } else {\n                    logger.warn(`Unable to munge local MSID - weird format detected: ${ssrcLine.value}`);\n                }\n                break;\n            }\n            }\n        }\n\n        // Additional transformations related to MSID are applicable to Unified-plan implementation only.\n        if (!this.tpc.usesUnifiedPlan()) {\n            return;\n        }\n\n        // If the msid attribute is missing, then remove the ssrc from the transformed description so that a\n        // source-remove is signaled to Jicofo. This happens when the direction of the transceiver (or m-line)\n        // is set to 'inactive' or 'recvonly' on Firefox, Chrome (unified) and Safari.\n        const mediaDirection = mediaSection.mLine?.direction;\n\n        if (mediaDirection === MediaDirection.RECVONLY || mediaDirection === MediaDirection.INACTIVE) {\n            mediaSection.ssrcs = undefined;\n            mediaSection.ssrcGroups = undefined;\n\n        // Add the msid attribute if it is missing when the direction is sendrecv/sendonly. Firefox doesn't produce a\n        // a=ssrc line with msid attribute for p2p connection.\n        } else {\n            const msidLine = mediaSection.mLine?.msid;\n            const trackId = msidLine && msidLine.split(' ')[1];\n            const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ];\n\n            for (const source of sources) {\n                const msidExists = mediaSection.ssrcs\n                    .find(ssrc => ssrc.id === source && ssrc.attribute === 'msid');\n\n                if (!msidExists) {\n                    const generatedMsid = this._generateMsidAttribute(mediaType, trackId);\n\n                    mediaSection.ssrcs.push({\n                        id: source,\n                        attribute: 'msid',\n                        value: generatedMsid\n                    });\n                }\n            }\n        }\n    }\n\n    /**\n     * Maybe modifies local description to fake local video tracks SDP when\n     * those are muted.\n     *\n     * @param {object} desc the WebRTC SDP object instance for the local\n     * description.\n     * @returns {RTCSessionDescription}\n     */\n    maybeAddMutedLocalVideoTracksToSDP(desc) {\n        if (!desc) {\n            throw new Error('No local description passed in.');\n        }\n\n        const transformer = new SdpTransformWrap(desc.sdp);\n\n        if (this._addMutedLocalVideoTracksToSDP(transformer)) {\n            return new RTCSessionDescription({\n                type: desc.type,\n                sdp: transformer.toRawSDP()\n            });\n        }\n\n        return desc;\n    }\n\n    /**\n     * This transformation will make sure that stream identifiers are unique\n     * across all of the local PeerConnections even if the same stream is used\n     * by multiple instances at the same time.\n     * Each PeerConnection assigns different SSRCs to the same local\n     * MediaStream, but the MSID remains the same as it's used to identify\n     * the stream by the WebRTC backend. The transformation will append\n     * {@link TraceablePeerConnection#id} at the end of each stream's identifier\n     * (\"cname\", \"msid\", \"label\" and \"mslabel\").\n     *\n     * @param {RTCSessionDescription} sessionDesc - The local session\n     * description (this instance remains unchanged).\n     * @return {RTCSessionDescription} - Transformed local session description\n     * (a modified copy of the one given as the input).\n     */\n    transformStreamIdentifiers(sessionDesc) {\n        // FIXME similar check is probably duplicated in all other transformers\n        if (!sessionDesc || !sessionDesc.sdp || !sessionDesc.type) {\n            return sessionDesc;\n        }\n\n        const transformer = new SdpTransformWrap(sessionDesc.sdp);\n        const audioMLine = transformer.selectMedia(MediaType.AUDIO)?.[0];\n\n        if (audioMLine) {\n            this._transformMediaIdentifiers(audioMLine);\n            this._injectSourceNames(audioMLine);\n        }\n\n        const videoMLine = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n        if (videoMLine) {\n            this._transformMediaIdentifiers(videoMLine);\n            this._injectSourceNames(videoMLine);\n        }\n\n        return new RTCSessionDescription({\n            type: sessionDesc.type,\n            sdp: transformer.toRawSDP()\n        });\n    }\n\n    /**\n     * Injects source names. Source names are need to for multiple streams per endpoint support. The final plan is to\n     * use the \"mid\" attribute for source names, but because the SDP to Jingle conversion still operates in the Plan-B\n     * semantics (one source name per media), a custom \"name\" attribute is injected into SSRC lines..\n     *\n     * @param {MLineWrap} mediaSection - The media part (audio or video) of the session description which will be\n     * modified in place.\n     * @returns {void}\n     * @private\n     */\n    _injectSourceNames(mediaSection) {\n        if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n            return;\n        }\n\n        const sources = [ ...new Set(mediaSection.mLine?.ssrcs?.map(s => s.id)) ];\n        const mediaType = mediaSection.mLine?.type;\n\n        if (!mediaType) {\n            throw new Error('_transformMediaIdentifiers - no media type in mediaSection');\n        }\n\n        for (const source of sources) {\n            const nameExists = mediaSection.ssrcs.find(ssrc => ssrc.id === source && ssrc.attribute === 'name');\n\n            if (!nameExists) {\n                // Inject source names as a=ssrc:3124985624 name:endpointA-v0\n                mediaSection.ssrcs.push({\n                    id: source,\n                    attribute: 'name',\n                    value: getSourceNameForJitsiTrack(this.localEndpointId, mediaType, 0)\n                });\n            }\n        }\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\n\nimport SDPUtil from './SDPUtil';\nimport { parseSecondarySSRC, SdpTransformWrap } from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Begin helper functions\n */\n/**\n * Updates or inserts the appropriate rtx information for primarySsrc with\n *  the given rtxSsrc.  If no rtx ssrc for primarySsrc currently exists, it will\n *  add the appropriate ssrc and ssrc group lines.  If primarySsrc already has\n *  an rtx ssrc, the appropriate ssrc and group lines will be updated\n * @param {MLineWrap} mLine\n * @param {object} primarySsrcInfo the info (ssrc, msid & cname) for the\n *  primary ssrc\n * @param {number} rtxSsrc the rtx ssrc to associate with the primary ssrc\n */\nfunction updateAssociatedRtxStream(mLine, primarySsrcInfo, rtxSsrc) {\n    const primarySsrc = primarySsrcInfo.id;\n    const primarySsrcMsid = primarySsrcInfo.msid;\n    const primarySsrcCname = primarySsrcInfo.cname;\n\n    const previousRtxSSRC = mLine.getRtxSSRC(primarySsrc);\n\n    if (previousRtxSSRC === rtxSsrc) {\n        return;\n    }\n    if (previousRtxSSRC) {\n        // Stream already had an rtx ssrc that is different than the one given,\n        //  remove all trace of the old one\n        mLine.removeSSRC(previousRtxSSRC);\n        mLine.removeGroupsWithSSRC(previousRtxSSRC);\n    }\n    mLine.addSSRCAttribute({\n        id: rtxSsrc,\n        attribute: 'cname',\n        value: primarySsrcCname\n    });\n    mLine.addSSRCAttribute({\n        id: rtxSsrc,\n        attribute: 'msid',\n        value: primarySsrcMsid\n    });\n    mLine.addSSRCGroup({\n        semantics: 'FID',\n        ssrcs: `${primarySsrc} ${rtxSsrc}`\n    });\n}\n\n/**\n * End helper functions\n */\n\n/**\n * Adds any missing RTX streams for video streams\n *  and makes sure that they remain consistent\n */\nexport default class RtxModifier {\n    /**\n     * Constructor\n     */\n    constructor() {\n        /**\n         * Map of video ssrc to corresponding RTX\n         *  ssrc\n         */\n        this.correspondingRtxSsrcs = new Map();\n    }\n\n    /**\n     * Clear the cached map of primary video ssrcs to\n     *  their corresponding rtx ssrcs so that they will\n     *  not be used for the next call to modifyRtxSsrcs\n     */\n    clearSsrcCache() {\n        this.correspondingRtxSsrcs.clear();\n    }\n\n    /**\n     * Explicitly set the primary video ssrc -> rtx ssrc\n     *  mapping to be used in modifyRtxSsrcs\n     * @param {Map} ssrcMapping a mapping of primary video\n     *  ssrcs to their corresponding rtx ssrcs\n     */\n    setSsrcCache(ssrcMapping) {\n        logger.debug('Setting ssrc cache to ', ssrcMapping);\n        this.correspondingRtxSsrcs = ssrcMapping;\n    }\n\n    /**\n     * Adds RTX ssrcs for any video ssrcs that don't already have them.  If the video ssrc has been seen before, and\n     * already had an RTX ssrc generated, the same RTX ssrc will be used again.\n     *\n     * @param {string} sdpStr sdp in raw string format\n     * @returns {string} The modified sdp in raw string format.\n     */\n    modifyRtxSsrcs(sdpStr) {\n        let modified = false;\n        const sdpTransformer = new SdpTransformWrap(sdpStr);\n        const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);\n\n        if (!videoMLines?.length) {\n            logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n            return sdpStr;\n        }\n\n        for (const videoMLine of videoMLines) {\n            if (this.modifyRtxSsrcs2(videoMLine)) {\n                modified = true;\n            }\n        }\n\n        return modified ? sdpTransformer.toRawSDP() : sdpStr;\n    }\n\n    /**\n     * Does the same thing as {@link modifyRtxSsrcs}, but takes the {@link MLineWrap} instance wrapping video media as\n     * an argument.\n     * @param {MLineWrap} videoMLine\n     * @return {boolean} <tt>true</tt> if the SDP wrapped by {@link SdpTransformWrap} has been modified or\n     * <tt>false</tt> otherwise.\n     */\n    modifyRtxSsrcs2(videoMLine) {\n        if (videoMLine.direction === MediaDirection.RECVONLY) {\n            return false;\n        }\n        if (videoMLine.getSSRCCount() < 1) {\n            return false;\n        }\n        const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();\n\n        for (const ssrc of primaryVideoSsrcs) {\n            const msid = videoMLine.getSSRCAttrValue(ssrc, 'msid');\n            const cname = videoMLine.getSSRCAttrValue(ssrc, 'cname');\n            let correspondingRtxSsrc = this.correspondingRtxSsrcs.get(ssrc);\n\n            if (!correspondingRtxSsrc) {\n                // If there's one in the sdp already for it, we'll just set\n                //  that as the corresponding one\n                const previousAssociatedRtxStream = videoMLine.getRtxSSRC(ssrc);\n\n                if (previousAssociatedRtxStream) {\n                    correspondingRtxSsrc = previousAssociatedRtxStream;\n                } else {\n                    correspondingRtxSsrc = SDPUtil.generateSsrc();\n                }\n                this.correspondingRtxSsrcs.set(ssrc, correspondingRtxSsrc);\n            }\n            updateAssociatedRtxStream(\n                videoMLine,\n                {\n                    id: ssrc,\n                    cname,\n                    msid\n                },\n                correspondingRtxSsrc);\n        }\n\n        // FIXME we're not looking into much details whether the SDP has been\n        // modified or not once the precondition requirements are met.\n        return true;\n    }\n\n    /**\n     * Strip all rtx streams from the given sdp.\n     *\n     * @param {string} sdpStr sdp in raw string format\n     * @returns {string} sdp string with all rtx streams stripped\n     */\n    stripRtx(sdpStr) {\n        const sdpTransformer = new SdpTransformWrap(sdpStr);\n        const videoMLines = sdpTransformer.selectMedia(MediaType.VIDEO);\n\n        if (!videoMLines?.length) {\n            logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n            return sdpStr;\n        }\n\n        for (const videoMLine of videoMLines) {\n            if (videoMLine.direction !== MediaDirection.RECVONLY\n                && videoMLine.getSSRCCount()\n                && videoMLine.containsAnySSRCGroups()) {\n                const fidGroups = videoMLine.findGroups('FID');\n\n                // Remove the fid groups from the mline\n                videoMLine.removeGroupsBySemantics('FID');\n\n                // Get the rtx ssrcs and remove them from the mline\n                for (const fidGroup of fidGroups) {\n                    const rtxSsrc = parseSecondarySSRC(fidGroup);\n\n                    videoMLine.removeSSRC(rtxSsrc);\n                }\n            }\n        }\n\n        return sdpTransformer.toRawSDP();\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport { MediaType } from '../../service/RTC/MediaType';\n\nimport {\n    parsePrimarySSRC,\n    parseSecondarySSRC,\n    SdpTransformWrap\n} from './SdpTransformUtil';\n\nconst logger = getLogger(__filename);\n\n/**\n * Handles the work of keeping video ssrcs consistent across multiple\n * o/a cycles, making it such that all stream operations can be\n * kept local and do not need to be signaled.\n * NOTE: This only keeps the 'primary' video ssrc consistent: meaning\n * the primary video stream\n */\nexport default class SdpConsistency {\n    /**\n     * Constructor\n     * @param {string} logPrefix the log prefix appended to every logged\n     * message, currently used to distinguish for which\n     * <tt>TraceablePeerConnection</tt> the instance works.\n     */\n    constructor(logPrefix) {\n        this.clearVideoSsrcCache();\n        this.logPrefix = logPrefix;\n    }\n\n    /**\n     * Clear the cached video primary and primary rtx ssrcs so that\n     *  they will not be used for the next call to\n     *  makeVideoPrimarySsrcsConsistent\n     */\n    clearVideoSsrcCache() {\n        this.cachedPrimarySsrc = null;\n        this.injectRecvOnly = false;\n    }\n\n    /**\n     * Explicitly set the primary ssrc to be used in\n     *  makeVideoPrimarySsrcsConsistent\n     * @param {number} primarySsrc the primarySsrc to be used\n     *  in future calls to makeVideoPrimarySsrcsConsistent\n     * @throws Error if <tt>primarySsrc</tt> is not a number\n     */\n    setPrimarySsrc(primarySsrc) {\n        if (typeof primarySsrc !== 'number') {\n            throw new Error('Primary SSRC must be a number!');\n        }\n        this.cachedPrimarySsrc = primarySsrc;\n    }\n\n    /**\n     * Checks whether or not there is a primary video SSRC cached already.\n     * @return {boolean}\n     */\n    hasPrimarySsrcCached() {\n        return Boolean(this.cachedPrimarySsrc);\n    }\n\n    /**\n     * Given an sdp string, either:\n     *  1) record the primary video and primary rtx ssrcs to be\n     *   used in future calls to makeVideoPrimarySsrcsConsistent or\n     *  2) change the primary and primary rtx ssrcs in the given sdp\n     *   to match the ones previously cached\n     * @param {string} sdpStr the sdp string to (potentially)\n     *  change to make the video ssrcs consistent\n     * @returns {string} a (potentially) modified sdp string\n     *  with ssrcs consistent with this class' cache\n     */\n    makeVideoPrimarySsrcsConsistent(sdpStr) {\n        const sdpTransformer = new SdpTransformWrap(sdpStr);\n        const videoMLine = sdpTransformer.selectMedia(MediaType.VIDEO)?.[0];\n\n        if (!videoMLine) {\n            logger.debug(`${this.logPrefix} no 'video' media found in the sdp: ${sdpStr}`);\n\n            return sdpStr;\n        }\n\n        if (videoMLine.direction === 'recvonly') {\n            // If the mline is recvonly, we'll add the primary\n            //  ssrc as a recvonly ssrc\n            if (this.cachedPrimarySsrc && this.injectRecvOnly) {\n                videoMLine.addSSRCAttribute({\n                    id: this.cachedPrimarySsrc,\n                    attribute: 'cname',\n                    value: `recvonly-${this.cachedPrimarySsrc}`\n                });\n            } else {\n                logger.info(`${this.logPrefix} no SSRC found for the recvonly video stream!`);\n            }\n        } else {\n            const newPrimarySsrc = videoMLine.getPrimaryVideoSsrc();\n\n            if (!newPrimarySsrc) {\n                logger.info(`${this.logPrefix} sdp-consistency couldn't parse new primary ssrc`);\n\n                return sdpStr;\n            }\n            if (this.cachedPrimarySsrc) {\n                videoMLine.replaceSSRC(newPrimarySsrc, this.cachedPrimarySsrc);\n                for (const group of videoMLine.ssrcGroups) {\n                    if (group.semantics === 'FID') {\n                        const primarySsrc = parsePrimarySSRC(group);\n                        const rtxSsrc = parseSecondarySSRC(group);\n\n                        // eslint-disable-next-line max-depth\n                        if (primarySsrc === newPrimarySsrc) {\n                            group.ssrcs\n                                = `${this.cachedPrimarySsrc} ${rtxSsrc}`;\n                        }\n                    }\n                }\n            } else {\n                this.cachedPrimarySsrc = newPrimarySsrc;\n            }\n            this.injectRecvOnly = true;\n        }\n\n        return sdpTransformer.toRawSDP();\n    }\n}\n","import MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\n\nimport * as transform from 'sdp-transform';\n\nconst DEFAULT_NUM_OF_LAYERS = 3;\n\ninterface Description {\n    type: RTCSdpType;\n    sdp: string;\n}\n\ninterface Options {\n    numOfLayers?: number\n}\n\n/**\n * This class handles SDP munging for enabling simulcast for local video streams in Unified plan. A set of random SSRCs\n * are generated for the higher layer streams and they are cached for a given mid. The cached SSRCs are then reused on\n * the subsequent iterations while munging the local description. This class also handles imploding of the simulcast\n * SSRCs for remote endpoints into the primary FID group in remote description since Jicofo signals all SSRCs relevant\n * to a given endpoint.\n */\nexport default class SdpSimulcast {\n    private _options: Options;\n    private _ssrcCache: Map<string, Array<number>>;\n\n    /**\n     * Creates a new instance.\n     *\n     * @param options\n     */\n    constructor(options: Options) {\n        this._options = options;\n        this._ssrcCache = new Map();\n\n        if (!this._options.numOfLayers) {\n            this._options.numOfLayers = DEFAULT_NUM_OF_LAYERS;\n        }\n    }\n\n    /**\n     * Updates the given media description using the SSRCs that were cached for the mid associated\n     * with the media description and returns the modified media description.\n     *\n     * @param mLine\n     * @returns\n     */\n     _fillSsrcsFromCache(mLine: transform.MediaDescription) : any {\n        const mid = mLine.mid;\n        const cachedSsrcs = this._ssrcCache.get(mid);\n        const newSsrcs = this._parseSimLayers(mLine);\n        const newMsid = this._getSsrcAttribute(mLine, newSsrcs[0], 'msid');\n        const newCname = this._getSsrcAttribute(mLine, newSsrcs[0], 'cname');\n\n        mLine.ssrcs = [];\n        mLine.ssrcGroups = [];\n\n        for (const ssrc of cachedSsrcs) {\n            mLine.ssrcs.push({\n                id: ssrc,\n                attribute: 'msid',\n                value: newMsid\n            });\n            mLine.ssrcs.push({\n                id: ssrc,\n                attribute: 'cname',\n                value: newCname\n            });\n        }\n\n        mLine.ssrcGroups.push({\n            semantics: 'SIM',\n            ssrcs: cachedSsrcs.join(' ')\n        });\n\n        return mLine;\n    }\n\n    /**\n     * Generates a new set of SSRCs for the higher simulcast layers/streams and adds the attributes and SIM group to\n     * the given media description and returns the modified media description.\n     *\n     * @param mLine\n     * @param primarySsrc\n     * @returns\n     */\n    _generateNewSsrcsForSimulcast(mLine: transform.MediaDescription, primarySsrc: number) : any {\n        const cname = this._getSsrcAttribute(mLine, primarySsrc, 'cname');\n        let msid = this._getSsrcAttribute(mLine, primarySsrc, 'msid');\n        const addAssociatedAttributes = (mLine: transform.MediaDescription, ssrc: number) => {\n            mLine.ssrcs.push({\n                id: ssrc,\n                attribute: 'cname',\n                value: cname\n            });\n            mLine.ssrcs.push({\n                id: ssrc,\n                attribute: 'msid',\n                value: msid\n            });\n        }\n\n        // In Unified-plan mode, the a=ssrc lines with the msid attribute are not present (only cname attributes are\n        // present) in the answers that Chrome and Safari generate for an offer received from Jicofo. Generate these\n        // a=ssrc lines using the msid values from the a=msid line.\n        if (!msid) {\n            msid = mLine.msid;\n            const primarySsrcs = mLine.ssrcs;\n\n            primarySsrcs.forEach(ssrc => {\n                mLine.ssrcs.push({\n                    id: ssrc.id,\n                    attribute: 'msid',\n                    value: msid\n                });\n            })\n        }\n\n        // Generate SIM layers.\n        const simSsrcs = [];\n\n        for (let i = 0; i < this._options.numOfLayers - 1; ++i) {\n            const simSsrc = this._generateSsrc();\n\n            addAssociatedAttributes(mLine, simSsrc);\n            simSsrcs.push(simSsrc);\n        }\n\n        mLine.ssrcGroups = mLine.ssrcGroups || [];\n        mLine.ssrcGroups.push({\n            semantics: 'SIM',\n            ssrcs: primarySsrc + ' ' + simSsrcs.join(' ')\n        });\n\n        return mLine;\n    }\n\n    /**\n     * Returns a random number to be used for the SSRC.\n     *\n     * @returns\n     */\n    _generateSsrc() : number {\n        const max = 0xffffffff;\n\n        return Math.floor(Math.random() * max);\n    }\n\n    /**\n     * Returns the requested attribute value for a SSRC from a given media description.\n     *\n     * @param mLine\n     * @param ssrc\n     * @param attributeName\n     * @returns\n     */\n    _getSsrcAttribute(mLine: transform.MediaDescription, ssrc: number, attributeName: string) : string | undefined {\n        return mLine.ssrcs?.find(\n            ssrcInfo => Number(ssrcInfo.id) === ssrc\n            && ssrcInfo.attribute === attributeName)?.value;\n    }\n\n    /**\n     * Returns an array of all the primary SSRCs in the SIM group for a given media description.\n     *\n     * @param mLine\n     * @returns\n     */\n    _parseSimLayers(mLine: transform.MediaDescription) : Array<number> | null {\n        const simGroup = mLine.ssrcGroups?.find(group => group.semantics === 'SIM');\n\n        if (simGroup) {\n            return simGroup.ssrcs.split(' ').map(ssrc => Number(ssrc));\n        }\n\n        if (mLine.ssrcs?.length) {\n            return [ Number(mLine.ssrcs[0].id) ];\n        }\n\n        return null;\n    }\n\n    /**\n     * Munges the given media description to enable simulcast for the video media sections that are in either have\n     * SENDRECV or SENDONLY as the media direction thereby ignoring all the RECVONLY transceivers created for remote\n     * endpoints.\n     * NOTE: This needs to be called only when simulcast is enabled.\n     *\n     * @param description\n     * @returns\n     */\n    mungeLocalDescription(description: Description) : Description {\n        if (!description || !description.sdp) {\n            return description;\n        }\n        const session = transform.parse(description.sdp);\n\n        for (let media of session.media) {\n            // Ignore recvonly and inactive transceivers created for remote sources.\n            if (media.direction === MediaDirection.RECVONLY || media.direction === MediaDirection.INACTIVE) {\n                continue;\n            }\n\n            // Ignore audio m-lines.\n            if (media.type !== MediaType.VIDEO) {\n                continue;\n            }\n            const mid = media.mid;\n            const numSsrcs = new Set(media.ssrcs?.map(ssrcInfo => ssrcInfo.id));\n            const numGroups = media.ssrcGroups?.length ?? 0;\n            let primarySsrc: number;\n\n            // Do not munge if the description has no ssrcs or if simulcast is already enabled.\n            if (numSsrcs.size === 0 || numSsrcs.size > 2 || (numSsrcs.size === 2 && numGroups === 0)) {\n                continue;\n            }\n            if (numSsrcs.size === 1) {\n                primarySsrc = Number(media.ssrcs[0]?.id);\n            } else {\n                const fidGroup = media.ssrcGroups.find(group => group.semantics === 'FID');\n\n                if (fidGroup) {\n                    primarySsrc = Number(fidGroup.ssrcs.split(' ')[0]);\n                }\n            }\n\n            if (this._ssrcCache.has(mid)) {\n                media = this._fillSsrcsFromCache(media);\n            } else {\n                media = this._generateNewSsrcsForSimulcast(media, primarySsrc);\n                const simulcastSsrcs = this._parseSimLayers(media);\n\n                // Update the SSRCs in the cache so that they can re-used for the same mid again.\n                this._ssrcCache.set(mid, simulcastSsrcs);\n            }\n        }\n\n        return new RTCSessionDescription({\n            type: description.type,\n            sdp: transform.write(session)\n        });\n    }\n\n    /**\n     * Munges the given media description by removing the SSRCs and related FID groups for the higher layer streams.\n     *\n     * @param description\n     * @returns\n     */\n    mungeRemoteDescription(description: Description) : Description {\n        if (!description || !description.sdp) {\n            return description;\n        }\n\n        const session = transform.parse(description.sdp);\n\n        for (const media of session.media) {\n            if (media.type !== MediaType.VIDEO) {\n                continue;\n            }\n\n            if (media.direction !== MediaDirection.SENDONLY) {\n                continue;\n            }\n\n            // Ignore m-lines that do not have any SSRCs or SSRC groups. These are the ones associated with remote\n            // sources that have left the call. These will be recycled when a new remote source joins the call.\n            if (!media.ssrcGroups?.length || !media?.ssrcs.length) {\n                continue;\n            }\n\n            // Cache the SSRCs and the source groups.\n            const mungedSsrcs = new Set(media.ssrcs.slice());\n            const mungedSsrcGroups = new Set(media.ssrcGroups.slice());\n            const fidGroups = media.ssrcGroups.filter(group => group.semantics === 'FID');\n            const simGroup = media.ssrcGroups.find(group => group.semantics === 'SIM');\n            const primarySsrc = simGroup?.ssrcs.split(' ')[0];;\n\n            // When simulcast and RTX are both enabled.\n            if (fidGroups.length && simGroup) {\n                const fidGroup = fidGroups.find(group => group.ssrcs.includes(primarySsrc));\n                const secondarySsrc = fidGroup.ssrcs.split(' ')[1];\n\n                for (const ssrcGroup of media.ssrcGroups) {\n                    if (ssrcGroup !== fidGroup) {\n                        mungedSsrcGroups.delete(ssrcGroup);\n                    }\n                }\n                for (const ssrc of media.ssrcs) {\n                    if (ssrc.id.toString() !== primarySsrc\n                        && ssrc.id.toString() !== secondarySsrc) {\n                        mungedSsrcs.delete(ssrc);\n                    }\n                }\n\n            // When simulcast is enabled but RTX is disabled.\n            } else if (simGroup) {\n                mungedSsrcGroups.delete(simGroup);\n\n                for (const ssrc of media.ssrcs) {\n                    if (ssrc.id.toString() !== primarySsrc) {\n                        mungedSsrcs.delete(ssrc);\n                    }\n                }\n            }\n\n            media.ssrcs = Array.from(mungedSsrcs);\n            media.ssrcGroups = Array.from(mungedSsrcGroups);\n        }\n\n        return new RTCSessionDescription ({\n            type: description.type,\n            sdp: transform.write(session)\n        });\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport { JitsiConferenceEvents } from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createTrackStreamingStatusEvent } from '../../service/statistics/AnalyticsEvents';\nimport JitsiConference from '../../types/hand-crafted/JitsiConference';\nimport JitsiRemoteTrack from '../../types/hand-crafted/modules/RTC/JitsiRemoteTrack';\nimport RTC from '../../types/hand-crafted/modules/RTC/RTC';\nimport { VideoType } from '../../types/hand-crafted/service/RTC/VideoType';\nimport browser from '../browser';\nimport Statistics from '../statistics/statistics';\n\n/** Track streaming statuses. */\nexport enum TrackStreamingStatus {\n\n    /**\n     * Status indicating that streaming is currently active.\n     */\n    ACTIVE = 'active',\n\n    /**\n     * Status indicating that streaming is currently inactive.\n     * Inactive means the streaming was stopped on purpose from the bridge, like exiting forwarded sources or\n     * adaptivity decided to drop video because of not enough bandwidth.\n     */\n    INACTIVE = 'inactive',\n\n    /**\n     * Status indicating that streaming is currently interrupted.\n     */\n    INTERRUPTED = 'interrupted',\n\n    /**\n     * Status indicating that streaming is currently restoring.\n     */\n    RESTORING = 'restoring',\n  }\n\ntype StreamingStatusMap = {\n    // TODO: Replace this hand crafted VideoType when we convert VideoType.js to Typescript.\n    videoType?: VideoType, \n    startedMs?: number,\n    p2p?: boolean,\n    streamingStatus?: string,\n    value?: number\n};\n\nconst logger = getLogger(__filename);\n\n/**\n * Default value of 500 milliseconds for {@link TrackStreamingStatusImpl.outOfForwardedSourcesTimeout}.\n */\nconst DEFAULT_NOT_IN_FORWARDED_SOURCES_TIMEOUT = 500;\n\n/**\n * Default value of 2500 milliseconds for {@link TrackStreamingStatusImpl.p2pRtcMuteTimeout}.\n */\nconst DEFAULT_P2P_RTC_MUTE_TIMEOUT = 2500;\n\n/**\n * Default value of 10000 milliseconds for {@link TrackStreamingStatusImpl.rtcMuteTimeout}.\n */\nconst DEFAULT_RTC_MUTE_TIMEOUT = 10000;\n\n/**\n * The time to wait a track to be restored. Track which was out of forwarded sources should be inactive and when\n * entering forwarded sources it becomes restoring and when data is received from bridge it will become active, but if\n * no data is received for some time we set status of that track streaming to interrupted.\n */\nconst DEFAULT_RESTORING_TIMEOUT = 10000;\n\n/**\n * Class is responsible for emitting JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED events.\n */\nexport class TrackStreamingStatusImpl {\n    rtc: RTC;\n    conference: JitsiConference;\n    track: JitsiRemoteTrack;\n\n    /**  This holds the timeout callback ID scheduled using window.setTimeout. */\n    trackTimer: number | null;\n\n    /**\n     * If video track frozen detection through RTC mute event is supported, we wait some time until video track is\n     * considered frozen. But because when the track falls out of forwarded sources it is expected for the video to\n     * freeze this timeout must be significantly reduced in \"out of forwarded sources\" case.\n     *\n     * Basically this value is used instead of {@link rtcMuteTimeout} when track is not in forwarded sources.\n     */\n    outOfForwardedSourcesTimeout: number;\n\n    /**\n     * How long we are going to wait for the corresponding signaling mute event after the RTC video track muted\n     * event is fired on the Media stream, before the connection interrupted is fired. The default value is\n     * {@link DEFAULT_P2P_RTC_MUTE_TIMEOUT}.\n     */\n    p2pRtcMuteTimeout: number;\n\n    /**\n     * How long we're going to wait after the RTC video track muted event for the corresponding signalling mute\n     * event, before the connection interrupted is fired. The default value is {@link DEFAULT_RTC_MUTE_TIMEOUT}.\n     *\n     * @returns amount of time in milliseconds\n     */\n    rtcMuteTimeout: number;\n\n    /**\n     * This holds a timestamp indicating  when remote video track was RTC muted. The purpose of storing the\n     * timestamp is to avoid the transition to disconnected status in case of legitimate video mute operation where\n     * the signalling video muted event can arrive shortly after RTC muted event.\n     *\n     * The timestamp is measured in milliseconds obtained with <tt>Date.now()</tt>.\n     *\n     * FIXME merge this logic with NO_DATA_FROM_SOURCE event implemented in JitsiLocalTrack by extending the event\n     * to the remote track and allowing to set different timeout for local and remote tracks.\n     */\n    rtcMutedTimestamp: number | null;\n\n    /** This holds the restoring timeout callback ID scheduled using window.setTimeout. */\n    restoringTimer: ReturnType<typeof setTimeout> | null;\n\n    /**\n     * This holds the current streaming status (along with all the internal events that happen while in that\n     * state).\n     *\n     * The goal is to send this information to the analytics backend for post-mortem analysis.\n     */\n    streamingStatusMap: StreamingStatusMap;\n\n    _onP2PStatus: () => void;\n    _onUserLeft: () => void;\n    _onTrackRtcMuted: () => void;\n    _onTrackRtcUnmuted: () => void;\n    _onSignallingMuteChanged: () => void;\n    _onTrackVideoTypeChanged: () => void;\n    _onLastNValueChanged: () => void;\n    _onForwardedSourcesChanged: () => void;\n\n    /* eslint-disable max-params*/\n    /**\n     * Calculates the new {@link TrackStreamingStatus} based on the values given for some specific remote track. It is\n     * assumed that the conference is currently in the JVB mode (in contrary to the P2P mode)\n     * @param isInForwardedSources - indicates whether the track is in the forwarded sources set. When set to\n     * false it means that JVB is not sending any video for the track.\n     * @param isRestoringTimedout - if true it means that the track has been outside of forwarded sources too\n     * long to be considered {@link TrackStreamingStatus.RESTORING}.\n     * @param isVideoMuted - true if the track is video muted and we should not expect to receive any video.\n     * @param isVideoTrackFrozen - if the current browser support video frozen detection then it will be set to\n     * true when the video track is frozen. If the current browser does not support frozen detection the it's always\n     * false.\n     * @return {TrackStreamingStatus} the new streaming status for the track for whom the values above were provided.\n     * @private\n     */\n    static _getNewStateForJvbMode(\n            isInForwardedSources: boolean,\n            isRestoringTimedout: boolean,\n            isVideoMuted: boolean,\n            isVideoTrackFrozen: boolean): TrackStreamingStatus {\n\n        // We are currently not checking the endpoint connection status received from the JVB.\n        if (isVideoMuted) {\n            // If the connection is active according to JVB and the track is video muted there is no way for the\n            // connection to be inactive, because the detection logic below only makes sense for video.\n            return TrackStreamingStatus.ACTIVE;\n        }\n\n        // Logic when isVideoTrackFrozen is supported\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n            if (!isVideoTrackFrozen) {\n                // If the video is playing we're good\n                return TrackStreamingStatus.ACTIVE;\n            } else if (isInForwardedSources) {\n                return isRestoringTimedout ? TrackStreamingStatus.INTERRUPTED : TrackStreamingStatus.RESTORING;\n            }\n\n            return TrackStreamingStatus.INACTIVE;\n        }\n\n        // Because this browser is incapable of detecting frozen video we must rely on the forwarded sources value\n        return isInForwardedSources ? TrackStreamingStatus.ACTIVE : TrackStreamingStatus.INACTIVE;\n    }\n\n    /* eslint-enable max-params*/\n\n    /**\n     * In P2P mode we don't care about any values coming from the JVB and the streaming status can be only active or\n     * interrupted.\n     * @param isVideoMuted - true if video muted\n     * @param isVideoTrackFrozen - true if the video track for the remote track is currently frozen. If the\n     * current browser does not support video frozen detection then it's always false.\n     * @return {TrackStreamingStatus}\n     * @private\n     */\n    static _getNewStateForP2PMode(isVideoMuted: boolean, isVideoTrackFrozen: boolean): TrackStreamingStatus {\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            // There's no way to detect problems in P2P when there's no video track frozen detection...\n            return TrackStreamingStatus.ACTIVE;\n        }\n\n        return isVideoMuted || !isVideoTrackFrozen\n            ? TrackStreamingStatus.ACTIVE : TrackStreamingStatus.INTERRUPTED;\n    }\n\n    /**\n     * Creates new instance of <tt>TrackStreamingStatus</tt>.\n     *\n     * @constructor\n     * @param rtc - the RTC service instance\n     * @param conference - parent conference instance\n     * @param {Object} options\n     * @param {number} [options.p2pRtcMuteTimeout=2500] custom value for\n     * {@link TrackStreamingStatusImpl.p2pRtcMuteTimeout}.\n     * @param {number} [options.rtcMuteTimeout=2000] custom value for\n     * {@link TrackStreamingStatusImpl.rtcMuteTimeout}.\n     * @param {number} [options.outOfForwardedSourcesTimeout=500] custom value for\n     * {@link TrackStreamingStatusImpl.outOfForwardedSourcesTimeout}.\n     */\n    constructor(rtc: RTC, conference: JitsiConference, track: JitsiRemoteTrack, options: {\n        outOfForwardedSourcesTimeout: number,\n        p2pRtcMuteTimeout: number,\n        rtcMuteTimeout: number\n    }) {\n        this.rtc = rtc;\n        this.conference = conference;\n        this.track = track;\n\n        this.restoringTimer = null;\n        this.rtcMutedTimestamp = null;\n        this.streamingStatusMap = {};\n        this.trackTimer = null;\n\n        this.outOfForwardedSourcesTimeout = typeof options.outOfForwardedSourcesTimeout === 'number'\n            ? options.outOfForwardedSourcesTimeout : DEFAULT_NOT_IN_FORWARDED_SOURCES_TIMEOUT;\n\n        this.p2pRtcMuteTimeout = typeof options.p2pRtcMuteTimeout === 'number'\n            ? options.p2pRtcMuteTimeout : DEFAULT_P2P_RTC_MUTE_TIMEOUT;\n\n        this.rtcMuteTimeout = typeof options.rtcMuteTimeout === 'number'\n            ? options.rtcMuteTimeout : DEFAULT_RTC_MUTE_TIMEOUT;\n        logger.info(`RtcMuteTimeout set to: ${this.rtcMuteTimeout}`);\n    }\n\n    /**\n     * Gets the video frozen timeout for given source name.\n     * @return how long are we going to wait since RTC video muted even, before a video track is considered\n     * frozen.\n     * @private\n     */\n    _getVideoFrozenTimeout(): number {\n        const sourceName = this.track.getSourceName();\n\n        return this.rtc.isInForwardedSources(sourceName)\n            ? this.rtcMuteTimeout\n            : this.conference.isP2PActive() ? this.p2pRtcMuteTimeout : this.outOfForwardedSourcesTimeout;\n    }\n\n    /**\n     * Initializes <tt>TrackStreamingStatus</tt> and bind required event listeners.\n     */\n    init(): void {\n        // Handles P2P status changes\n        this._onP2PStatus = this.figureOutStreamingStatus.bind(this);\n        this.conference.on(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n\n        // Used to send analytics events for the participant that left the call.\n        this._onUserLeft = this.onUserLeft.bind(this);\n        this.conference.on(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n\n        // On some browsers MediaStreamTrack trigger \"onmute\"/\"onunmute\" events for video type tracks when they stop\n        // receiving data which is often a sign that remote user is having connectivity issues.\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n\n            this._onTrackRtcMuted = this.onTrackRtcMuted.bind(this);\n            this.rtc.addListener(RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n\n            this._onTrackRtcUnmuted = this.onTrackRtcUnmuted.bind(this);\n            this.rtc.addListener(RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n            // Listened which will be bound to JitsiRemoteTrack to listen for signalling mute/unmute events.\n            this._onSignallingMuteChanged = this.onSignallingMuteChanged.bind(this);\n            this.track.on(JitsiTrackEvents.TRACK_MUTE_CHANGED, this._onSignallingMuteChanged);\n\n            // Used to send an analytics event when the video type changes.\n            this._onTrackVideoTypeChanged = this.onTrackVideoTypeChanged.bind(this);\n            this.track.on(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED, this._onTrackVideoTypeChanged);\n        }\n\n        this._onForwardedSourcesChanged = this.onForwardedSourcesChanged.bind(this);\n        this.conference.on(JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED, this._onForwardedSourcesChanged);\n\n        this._onLastNValueChanged = this.figureOutStreamingStatus.bind(this);\n        this.rtc.on(RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n    }\n\n    /**\n     * Removes all event listeners and disposes of all resources held by this instance.\n     */\n    dispose(): void {\n        if (browser.supportsVideoMuteOnConnInterrupted()) {\n            this.rtc.removeListener(RTCEvents.REMOTE_TRACK_MUTE, this._onTrackRtcMuted);\n            this.rtc.removeListener(RTCEvents.REMOTE_TRACK_UNMUTE, this._onTrackRtcUnmuted);\n\n            this.track.off(JitsiTrackEvents.TRACK_MUTE_CHANGED, this._onSignallingMuteChanged);\n        }\n\n        this.conference.off(JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED, this._onForwardedSourcesChanged);\n        this.conference.off(JitsiConferenceEvents.P2P_STATUS, this._onP2PStatus);\n        this.conference.off(JitsiConferenceEvents.USER_LEFT, this._onUserLeft);\n        this.rtc.removeListener(RTCEvents.LASTN_VALUE_CHANGED, this._onLastNValueChanged);\n\n        this.clearTimeout();\n        this.clearRtcMutedTimestamp();\n        this.maybeSendTrackStreamingStatusEvent(Date.now());\n        this.figureOutStreamingStatus();\n    }\n\n    /**\n     * Changes streaming status.\n     * @param newStatus\n     */\n    _changeStreamingStatus(newStatus: TrackStreamingStatus): void {\n        if (this.track.getTrackStreamingStatus() !== newStatus) {\n\n            const sourceName = this.track.getSourceName();\n\n            this.track._setTrackStreamingStatus(newStatus);\n\n            logger.debug(`Emit track streaming status(${Date.now()}) ${sourceName}: ${newStatus}`);\n\n            // Log the event on CallStats\n            Statistics.sendLog(\n                JSON.stringify({\n                    id: 'track.streaming.status',\n                    track: sourceName,\n                    status: newStatus\n                }));\n\n            this.track.emit(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED, newStatus);\n        }\n    }\n\n    /**\n     * Reset the postponed \"streaming interrupted\" event which was previously scheduled as a timeout on RTC 'onmute'\n     * event.\n     */\n    clearTimeout(): void {\n        if (this.trackTimer) {\n            window.clearTimeout(this.trackTimer);\n            this.trackTimer = null;\n        }\n    }\n\n    /**\n     * Clears the timestamp of the RTC muted event for remote video track.\n     */\n    clearRtcMutedTimestamp(): void {\n        this.rtcMutedTimestamp = null;\n    }\n\n    /**\n     * Checks if track is considered frozen.\n     * @return <tt>true</tt> if the video has frozen or <tt>false</tt> when it's either not considered frozen\n     * (yet) or if freeze detection is not supported by the current browser.\n     *\n     * FIXME merge this logic with NO_DATA_FROM_SOURCE event implemented in JitsiLocalTrack by extending the event to\n     *       the remote track and allowing to set different timeout for local and remote tracks.\n     */\n    isVideoTrackFrozen(): boolean {\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            return false;\n        }\n\n        const isVideoRTCMuted = this.track.isWebRTCTrackMuted();\n        const rtcMutedTimestamp = this.rtcMutedTimestamp;\n        const timeout = this._getVideoFrozenTimeout();\n\n        return isVideoRTCMuted && typeof rtcMutedTimestamp === 'number' && (Date.now() - rtcMutedTimestamp) >= timeout;\n    }\n\n    /**\n     * Figures out (and updates) the current streaming status for the track identified by the source name.\n     */\n    figureOutStreamingStatus(): void {\n        const sourceName = this.track.getSourceName();\n        const inP2PMode = this.conference.isP2PActive();\n        const isRestoringTimedOut = this._isRestoringTimedout();\n        const audioOnlyMode = this.conference.getLastN() === 0;\n\n        // NOTE Overriding videoMuted to true for audioOnlyMode should disable any detection based on video playback or\n        // forwarded sources.\n        const isVideoMuted = this.track.isMuted() || audioOnlyMode;\n        const isVideoTrackFrozen = this.isVideoTrackFrozen();\n        const isInForwardedSources = this.rtc.isInForwardedSources(sourceName);\n\n        const newState\n            = inP2PMode\n                ? TrackStreamingStatusImpl._getNewStateForP2PMode(\n                    isVideoMuted,\n                    isVideoTrackFrozen)\n                : TrackStreamingStatusImpl._getNewStateForJvbMode(\n                    isInForwardedSources,\n                    isRestoringTimedOut,\n                    isVideoMuted,\n                    isVideoTrackFrozen);\n\n        // if the new state is not restoring clear timers and timestamps that we use to track the restoring state\n        if (newState !== TrackStreamingStatus.RESTORING) {\n            this._clearRestoringTimer();\n        }\n\n        logger.debug(\n            `Figure out conn status for ${sourceName}, is video muted: ${\n                isVideoMuted} video track frozen: ${\n                isVideoTrackFrozen} p2p mode: ${\n                inP2PMode} is in forwarded sources: ${\n                isInForwardedSources} currentStatus => newStatus: ${\n                this.track.getTrackStreamingStatus()} => ${newState}`);\n\n        const oldStreamingStatus = this.streamingStatusMap || {};\n\n        // Send an analytics event (guard on either the p2p flag or the streaming status has changed since the last\n        // time this code block run).\n        if (!('p2p' in oldStreamingStatus)\n            || !('streamingStatus' in oldStreamingStatus)\n            || oldStreamingStatus.p2p !== inP2PMode\n            || oldStreamingStatus.streamingStatus !== newState) {\n\n            const nowMs = Date.now();\n\n            this.maybeSendTrackStreamingStatusEvent(nowMs);\n\n            this.streamingStatusMap = {\n                ...oldStreamingStatus,\n                streamingStatus: newState,\n                p2p: inP2PMode,\n                startedMs: nowMs\n            };\n\n            // sometimes (always?) we're late to hook the TRACK_VIDEOTYPE_CHANGED event and the video type is not in\n            // oldStreamingStatus.\n            if (!('videoType' in this.streamingStatusMap)) {\n                this.streamingStatusMap.videoType = this.track.getVideoType();\n            }\n        }\n        this._changeStreamingStatus(newState);\n    }\n\n    /**\n     * Computes the duration of the current streaming status for the track (i.e. 15 seconds in the INTERRUPTED state)\n     * and sends a track streaming status event.\n     * @param nowMs - The current time (in millis).\n     */\n    maybeSendTrackStreamingStatusEvent(nowMs: number): void {\n        const trackStreamingStatus = this.streamingStatusMap;\n\n        if (trackStreamingStatus\n            && 'startedMs' in trackStreamingStatus\n            && 'videoType' in trackStreamingStatus\n            && 'streamingStatus' in trackStreamingStatus\n            && 'p2p' in trackStreamingStatus) {\n            trackStreamingStatus.value = nowMs - trackStreamingStatus.startedMs;\n            Statistics.sendAnalytics(createTrackStreamingStatusEvent(trackStreamingStatus));\n        }\n    }\n\n    /**\n     * On change in forwarded sources set check all leaving and entering track to change their corresponding statuses.\n     *\n     * @param leavingForwardedSources - The array of sourceName leaving forwarded sources.\n     * @param enteringForwardedSources - The array of sourceName entering forwarded sources.\n     * @param timestamp - The time in millis\n     * @private\n     */\n    onForwardedSourcesChanged(\n            leavingForwardedSources: string[] = [],\n            enteringForwardedSources: string[] = [],\n            timestamp: number): void {\n\n        const sourceName = this.track.getSourceName();\n\n        logger.debug(`Fowarded sources changed leaving=${leavingForwardedSources}, entering=${\n            enteringForwardedSources} at ${timestamp}`);\n\n        // If the browser doesn't fire the mute/onmute events when the remote peer stops/starts sending media,\n        // calculate the streaming status for all the tracks since it won't get triggered automatically on the track\n        // that has started/stopped receiving media.\n        if (!browser.supportsVideoMuteOnConnInterrupted()) {\n            this.figureOutStreamingStatus();\n        }\n\n        if (leavingForwardedSources.includes(sourceName)) {\n            this.track._clearEnteredForwardedSourcesTimestamp();\n            this._clearRestoringTimer();\n            browser.supportsVideoMuteOnConnInterrupted() && this.figureOutStreamingStatus();\n        }\n\n        if (enteringForwardedSources.includes(sourceName)) {\n            // store the timestamp this track is entering forwarded sources\n            this.track._setEnteredForwardedSourcesTimestamp(timestamp);\n            browser.supportsVideoMuteOnConnInterrupted() && this.figureOutStreamingStatus();\n        }\n    }\n\n    /**\n     * Clears the restoring timer for video track and the timestamp for entering forwarded sources.\n     */\n    _clearRestoringTimer(): void {\n        const rTimer = this.restoringTimer;\n\n        if (rTimer) {\n            clearTimeout(rTimer);\n            this.restoringTimer = null;\n        }\n    }\n\n    /**\n     * Checks whether a track had stayed enough in restoring state, compares current time and the time the track\n     * entered in forwarded sources. If it hasn't timedout and there is no timer added, add new timer in order to give\n     * it more time to become active or mark it as interrupted on next check.\n     *\n     * @returns <tt>true</tt> if the track was in restoring state more than the timeout\n     * ({@link DEFAULT_RESTORING_TIMEOUT}.) in order to set its status to interrupted.\n     * @private\n     */\n    _isRestoringTimedout(): boolean {\n        const enteredForwardedSourcesTimestamp = this.track._getEnteredForwardedSourcesTimestamp();\n\n        if (enteredForwardedSourcesTimestamp\n            && (Date.now() - enteredForwardedSourcesTimestamp) >= DEFAULT_RESTORING_TIMEOUT) {\n            return true;\n        }\n\n        // still haven't reached timeout, if there is no timer scheduled, schedule one so we can track the restoring\n        // state and change it after reaching the timeout\n        const rTimer = this.restoringTimer;\n\n        if (!rTimer) {\n            this.restoringTimer = setTimeout(() => this.figureOutStreamingStatus(), DEFAULT_RESTORING_TIMEOUT);\n        }\n\n        return false;\n    }\n\n    /** Checks whether a track is the current track. */\n    _isCurrentTrack(track: JitsiRemoteTrack): boolean {\n        return track.getSourceName() === this.track.getSourceName();\n    }\n\n    /**\n     * Sends a last/final track streaming status event for the track of the user that left the conference.\n     * @param id - The id of the participant that left the conference.\n     */\n    onUserLeft(id: string): void {\n        if (this.track.getParticipantId() === id) {\n            this.maybeSendTrackStreamingStatusEvent(Date.now());\n            this.streamingStatusMap = {};\n        }\n    }\n\n    /**\n     * Handles RTC 'onmute' event for the video track.\n     *\n     * @param track - The video track for which 'onmute' event will be processed.\n     */\n    onTrackRtcMuted(track: JitsiRemoteTrack): void {\n        if (!this._isCurrentTrack(track)) {\n            return;\n        }\n\n        const sourceName = track.getSourceName();\n\n        logger.debug(`Detector track RTC muted: ${sourceName}`, Date.now());\n\n        this.rtcMutedTimestamp = Date.now();\n        if (!track.isMuted()) {\n            // If the user is not muted according to the signalling we'll give it some time, before the streaming\n            // interrupted event is triggered.\n            this.clearTimeout();\n\n            // The timeout is reduced when track is not in the forwarded sources\n            const timeout = this._getVideoFrozenTimeout();\n\n            this.trackTimer = window.setTimeout(() => {\n                logger.debug(`Set RTC mute timeout for: ${sourceName} of ${timeout} ms`);\n                this.clearTimeout();\n                this.figureOutStreamingStatus();\n            }, timeout);\n        }\n    }\n\n    /**\n     * Handles RTC 'onunmute' event for the video track.\n     *\n     * @param track - The video track for which 'onunmute' event will be processed.\n     */\n    onTrackRtcUnmuted(track: JitsiRemoteTrack): void {\n        if (!this._isCurrentTrack(track)) {\n            return;\n        }\n\n        const sourceName = this.track.getSourceName();\n\n        logger.debug(`Detector track RTC unmuted: ${sourceName}`, Date.now());\n\n        this.clearTimeout();\n        this.clearRtcMutedTimestamp();\n\n        this.figureOutStreamingStatus();\n    }\n\n    /**\n     * Here the signalling \"mute\"/\"unmute\" events are processed.\n     *\n     * @param track - The remote video track for which the signalling mute/unmute event will be\n     * processed.\n     */\n    onSignallingMuteChanged(track: JitsiRemoteTrack): void {\n        if (!this._isCurrentTrack(track)) {\n            return;\n        }\n\n        const sourceName = this.track.getSourceName();\n\n        logger.debug(`Detector on track signalling mute changed: ${sourceName}`, track.isMuted());\n\n        this.figureOutStreamingStatus();\n    }\n\n    /**\n     * Sends a track streaming status event as a result of the video type changing.\n     * @deprecated this will go away with full multiple streams support\n     * @param type - The video type.\n     */\n    onTrackVideoTypeChanged(type: VideoType): void {\n        const nowMs = Date.now();\n\n        this.maybeSendTrackStreamingStatusEvent(nowMs);\n\n        this.streamingStatusMap = {\n            ...this.streamingStatusMap || {},\n            videoType: type,\n            startedMs: nowMs\n        };\n    }\n}\n\nexport default TrackStreamingStatusImpl;\n","import * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport { createTtfmEvent } from '../../service/statistics/AnalyticsEvents';\nimport TrackStreamingStatusImpl, { TrackStreamingStatus } from '../connectivity/TrackStreamingStatus';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\n\nimport JitsiTrack from './JitsiTrack';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\nconst RTCEvents = require('../../service/RTC/RTCEvents');\n\nlet ttfmTrackerAudioAttached = false;\nlet ttfmTrackerVideoAttached = false;\n\n/**\n * List of container events that we are going to process. _onContainerEventHandler will be added as listener to the\n * container for every event in the list.\n */\nconst containerEvents = [ 'abort', 'canplaythrough', 'ended', 'error' ];\n\n/* eslint-disable max-params */\n\n/**\n * Represents a single media track (either audio or video).\n */\nexport default class JitsiRemoteTrack extends JitsiTrack {\n    /**\n     * Creates new JitsiRemoteTrack instance.\n     * @param {RTC} rtc the RTC service instance.\n     * @param {JitsiConference} conference the conference to which this track\n     *        belongs to\n     * @param {string} ownerEndpointId the endpoint ID of the track owner\n     * @param {MediaStream} stream WebRTC MediaStream, parent of the track\n     * @param {MediaStreamTrack} track underlying WebRTC MediaStreamTrack for\n     *        the new JitsiRemoteTrack\n     * @param {MediaType} mediaType the type of the media\n     * @param {VideoType} videoType the type of the video if applicable\n     * @param {number} ssrc the SSRC number of the Media Stream\n     * @param {boolean} muted the initial muted state\n     * @param {boolean} isP2P indicates whether or not this track belongs to a\n     * P2P session\n     * @param {String} sourceName the source name signaled for the track\n     * @throws {TypeError} if <tt>ssrc</tt> is not a number.\n     * @constructor\n     */\n    constructor(\n            rtc,\n            conference,\n            ownerEndpointId,\n            stream,\n            track,\n            mediaType,\n            videoType,\n            ssrc,\n            muted,\n            isP2P,\n            sourceName) {\n        super(\n            conference,\n            stream,\n            track,\n            () => {\n                // Nothing to do if the track is inactive.\n            },\n            mediaType,\n            videoType);\n        this.rtc = rtc;\n\n        // Prevent from mixing up type of SSRC which should be a number\n        if (typeof ssrc !== 'number') {\n            throw new TypeError(`SSRC ${ssrc} is not a number`);\n        }\n        this.ssrc = ssrc;\n        this.ownerEndpointId = ownerEndpointId;\n        this.muted = muted;\n        this.isP2P = isP2P;\n        this._sourceName = sourceName;\n        this._trackStreamingStatus = null;\n        this._trackStreamingStatusImpl = null;\n\n        /**\n         * This holds the timestamp indicating when remote video track entered forwarded sources set. Track entering\n         * forwardedSources will have streaming status restoring and when we start receiving video will become active,\n         * but if video is not received for certain time {@link DEFAULT_RESTORING_TIMEOUT} that track streaming status\n         * will become interrupted.\n         */\n        this._enteredForwardedSourcesTimestamp = null;\n\n        this.addEventListener = this.on = this._addEventListener.bind(this);\n        this.removeEventListener = this.off = this._removeEventListener.bind(this);\n\n        logger.debug(`New remote track added: ${this}`);\n\n        // we want to mark whether the track has been ever muted\n        // to detect ttfm events for startmuted conferences, as it can\n        // significantly increase ttfm values\n        this.hasBeenMuted = muted;\n\n        // Bind 'onmute' and 'onunmute' event handlers\n        if (this.rtc && this.track) {\n            this._bindTrackHandlers();\n        }\n        this._containerHandlers = {};\n        containerEvents.forEach(event => {\n            this._containerHandlers[event] = this._containerEventHandler.bind(this, event);\n        });\n    }\n\n    /* eslint-enable max-params */\n    /**\n     * Attaches the track handlers.\n     *\n     * @returns {void}\n     */\n    _bindTrackHandlers() {\n        this.track.addEventListener('mute', () => this._onTrackMute());\n        this.track.addEventListener('unmute', () => this._onTrackUnmute());\n        this.track.addEventListener('ended', () => {\n            logger.debug(`\"onended\" event(${Date.now()}): ${this}`);\n        });\n    }\n\n    /**\n     * Overrides addEventListener method to init TrackStreamingStatus instance when there are listeners for the\n     * {@link JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED} event.\n     *\n     * @param {string} event - event name\n     * @param {function} handler - event handler\n     */\n    _addEventListener(event, handler) {\n        super.addListener(event, handler);\n\n        if (FeatureFlags.isSourceNameSignalingEnabled()\n            && event === JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED\n            && this.listenerCount(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED)\n            && !this._trackStreamingStatusImpl\n        ) {\n            this._initTrackStreamingStatus();\n            logger.debug(`Initializing track streaming status: ${this._sourceName}`);\n        }\n    }\n\n    /**\n     * Overrides removeEventListener method to dispose TrackStreamingStatus instance.\n     *\n     * @param {string} event - event name\n     * @param {function} handler - event handler\n     */\n    _removeEventListener(event, handler) {\n        super.removeListener(event, handler);\n\n        if (FeatureFlags.isSourceNameSignalingEnabled()\n            && event === JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED\n            && !this.listenerCount(JitsiTrackEvents.TRACK_STREAMING_STATUS_CHANGED)\n        ) {\n            this._disposeTrackStreamingStatus();\n            logger.debug(`Disposing track streaming status: ${this._sourceName}`);\n        }\n    }\n\n    /**\n     * Callback invoked when the track is muted. Emits an event notifying\n     * listeners of the mute event.\n     *\n     * @private\n     * @returns {void}\n     */\n    _onTrackMute() {\n        logger.debug(`\"onmute\" event(${Date.now()}): ${this}`);\n\n        this.rtc.eventEmitter.emit(RTCEvents.REMOTE_TRACK_MUTE, this);\n    }\n\n    /**\n     * Callback invoked when the track is unmuted. Emits an event notifying\n     * listeners of the mute event.\n     *\n     * @private\n     * @returns {void}\n     */\n    _onTrackUnmute() {\n        logger.debug(`\"onunmute\" event(${Date.now()}): ${this}`);\n\n        this.rtc.eventEmitter.emit(RTCEvents.REMOTE_TRACK_UNMUTE, this);\n    }\n\n    /**\n     * Removes attached event listeners and dispose TrackStreamingStatus .\n     *\n     * @returns {Promise}\n     */\n    dispose() {\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            this._disposeTrackStreamingStatus();\n        }\n\n        return super.dispose();\n    }\n\n    /**\n     * Sets current muted status and fires an events for the change.\n     * @param value the muted status.\n     */\n    setMute(value) {\n        if (this.muted === value) {\n            return;\n        }\n\n        if (value) {\n            this.hasBeenMuted = true;\n        }\n\n        // we can have a fake video stream\n        if (this.stream) {\n            this.stream.muted = value;\n        }\n\n        this.muted = value;\n        this.emit(JitsiTrackEvents.TRACK_MUTE_CHANGED, this);\n    }\n\n    /**\n     * Returns the current muted status of the track.\n     * @returns {boolean|*|JitsiRemoteTrack.muted} <tt>true</tt> if the track is\n     * muted and <tt>false</tt> otherwise.\n     */\n    isMuted() {\n        return this.muted;\n    }\n\n    /**\n     * Returns the participant id which owns the track.\n     *\n     * @returns {string} the id of the participants. It corresponds to the\n     * Colibri endpoint id/MUC nickname in case of Jitsi-meet.\n     */\n    getParticipantId() {\n        return this.ownerEndpointId;\n    }\n\n    /**\n     * Return false;\n     */\n    isLocal() {\n        return false;\n    }\n\n    /**\n     * Returns the synchronization source identifier (SSRC) of this remote\n     * track.\n     *\n     * @returns {number} the SSRC of this remote track.\n     */\n    getSSRC() {\n        return this.ssrc;\n    }\n\n\n    /**\n     * Returns the tracks source name\n     *\n     * @returns {string} the track's source name\n     */\n    getSourceName() {\n        return this._sourceName;\n    }\n\n    /**\n     * Changes the video type of the track.\n     *\n     * @param {string} type - The new video type(\"camera\", \"desktop\").\n     */\n    _setVideoType(type) {\n        if (this.videoType === type) {\n            return;\n        }\n        this.videoType = type;\n        this.emit(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED, type);\n    }\n\n    /**\n     * Handles track play events.\n     */\n    _playCallback() {\n        if (!this.conference.room) {\n            return;\n        }\n\n        const type = this.isVideoTrack() ? 'video' : 'audio';\n\n        const now = window.performance.now();\n\n        console.log(`(TIME) Render ${type}:\\t`, now);\n        this.conference.getConnectionTimes()[`${type}.render`] = now;\n\n        // The conference can be started without calling GUM\n        // FIXME if there would be a module for connection times this kind\n        // of logic (gumDuration or ttfm) should end up there\n        const gumStart = window.connectionTimes['obtainPermissions.start'];\n        const gumEnd = window.connectionTimes['obtainPermissions.end'];\n        const gumDuration\n            = !isNaN(gumEnd) && !isNaN(gumStart) ? gumEnd - gumStart : 0;\n\n        // Subtract the muc.joined-to-session-initiate duration because jicofo\n        // waits until there are 2 participants to start Jingle sessions.\n        const ttfm = now\n            - (this.conference.getConnectionTimes()['session.initiate']\n                - this.conference.getConnectionTimes()['muc.joined'])\n            - gumDuration;\n\n        this.conference.getConnectionTimes()[`${type}.ttfm`] = ttfm;\n        console.log(`(TIME) TTFM ${type}:\\t`, ttfm);\n\n        Statistics.sendAnalytics(createTtfmEvent(\n            {\n                'media_type': type,\n                muted: this.hasBeenMuted,\n                value: ttfm\n            }));\n\n    }\n\n    /**\n     * Attach time to first media tracker only if there is conference and only\n     * for the first element.\n     * @param container the HTML container which can be 'video' or 'audio'\n     * element.\n     * @private\n     */\n    _attachTTFMTracker(container) {\n        if ((ttfmTrackerAudioAttached && this.isAudioTrack())\n            || (ttfmTrackerVideoAttached && this.isVideoTrack())) {\n            return;\n        }\n\n        if (this.isAudioTrack()) {\n            ttfmTrackerAudioAttached = true;\n        }\n        if (this.isVideoTrack()) {\n            ttfmTrackerVideoAttached = true;\n        }\n\n        container.addEventListener('canplay', this._playCallback.bind(this));\n    }\n\n    /**\n     * Called when the track has been attached to a new container.\n     *\n     * @param {HTMLElement} container the HTML container which can be 'video' or 'audio' element.\n     * @private\n     */\n    _onTrackAttach(container) {\n        containerEvents.forEach(event => {\n            container.addEventListener(event, this._containerHandlers[event]);\n        });\n    }\n\n    /**\n     * Called when the track has been detached from a container.\n     *\n     * @param {HTMLElement} container the HTML container which can be 'video' or 'audio' element.\n     * @private\n     */\n    _onTrackDetach(container) {\n        containerEvents.forEach(event => {\n            container.removeEventListener(event, this._containerHandlers[event]);\n        });\n    }\n\n    /**\n     * An event handler for events triggered by the attached container.\n     *\n     * @param {string} type - The type of the event.\n     */\n    _containerEventHandler(type) {\n        logger.debug(`${type} handler was called for a container with attached ${this}`);\n    }\n\n    /**\n     * Returns a string with a description of the current status of the track.\n     *\n     * @returns {string}\n     */\n    _getStatus() {\n        const { enabled, muted, readyState } = this.track;\n\n        return `readyState: ${readyState}, muted: ${muted}, enabled: ${enabled}`;\n    }\n\n    /**\n     * Initializes trackStreamingStatusImpl.\n     */\n    _initTrackStreamingStatus() {\n        const config = this.conference.options.config;\n\n        this._trackStreamingStatus = TrackStreamingStatus.ACTIVE;\n\n        this._trackStreamingStatusImpl = new TrackStreamingStatusImpl(\n            this.rtc,\n            this.conference,\n            this,\n            {\n                // These options are not public API, leaving it here only as an entry point through config for\n                // tuning up purposes. Default values should be adjusted as soon as optimal values are discovered.\n                p2pRtcMuteTimeout: config._p2pConnStatusRtcMuteTimeout,\n                rtcMuteTimeout: config._peerConnStatusRtcMuteTimeout,\n                outOfForwardedSourcesTimeout: config._peerConnStatusOutOfLastNTimeout\n            });\n\n        this._trackStreamingStatusImpl.init();\n    }\n\n    /**\n     * Disposes trackStreamingStatusImpl and clears trackStreamingStatus.\n     */\n    _disposeTrackStreamingStatus() {\n        if (this._trackStreamingStatusImpl) {\n            this._trackStreamingStatusImpl.dispose();\n            this._trackStreamingStatusImpl = null;\n            this._trackStreamingStatus = null;\n        }\n    }\n\n    /**\n     * Updates track's streaming status.\n     *\n     * @param {string} state the current track streaming state. {@link TrackStreamingStatus}.\n     */\n    _setTrackStreamingStatus(status) {\n        this._trackStreamingStatus = status;\n    }\n\n    /**\n     * Returns track's streaming status.\n     *\n     * @returns {string} the streaming status <tt>TrackStreamingStatus</tt> of the track. Returns null\n     * if trackStreamingStatusImpl hasn't been initialized.\n     *\n     * {@link TrackStreamingStatus}.\n     */\n    getTrackStreamingStatus() {\n        return this._trackStreamingStatus;\n    }\n\n    /**\n     * Clears the timestamp of when the track entered forwarded sources.\n     */\n    _clearEnteredForwardedSourcesTimestamp() {\n        this._enteredForwardedSourcesTimestamp = null;\n    }\n\n    /**\n     * Updates the timestamp of when the track entered forwarded sources.\n     *\n     * @param {number} timestamp the time in millis\n     */\n    _setEnteredForwardedSourcesTimestamp(timestamp) {\n        this._enteredForwardedSourcesTimestamp = timestamp;\n    }\n\n    /**\n     * Returns the timestamp of when the track entered forwarded sources.\n     *\n     * @returns {number} the time in millis\n     */\n    _getEnteredForwardedSourcesTimestamp() {\n        return this._enteredForwardedSourcesTimestamp;\n    }\n\n    /**\n     * Creates a text representation of this remote track instance.\n     * @return {string}\n     */\n    toString() {\n        return `RemoteTrack[userID: ${this.getParticipantId()}, type: ${this.getType()}, ssrc: ${\n            this.getSSRC()}, p2p: ${this.isP2P}, sourceName: ${this._sourceName}, status: ${this._getStatus()}]`;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport transform from 'sdp-transform';\n\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nconst logger = getLogger(__filename);\nconst DESKTOP_SHARE_RATE = 500000;\nconst LD_BITRATE = 200000;\nconst SD_BITRATE = 700000;\nconst SIM_LAYER_1_RID = '1';\nconst SIM_LAYER_2_RID = '2';\nconst SIM_LAYER_3_RID = '3';\n\nexport const HD_BITRATE = 2500000;\nexport const HD_SCALE_FACTOR = 1;\nexport const LD_SCALE_FACTOR = 4;\nexport const SD_SCALE_FACTOR = 2;\nexport const SIM_LAYER_RIDS = [ SIM_LAYER_1_RID, SIM_LAYER_2_RID, SIM_LAYER_3_RID ];\n\n/**\n * Handles track related operations on TraceablePeerConnection when browser is\n * running in unified plan mode.\n */\nexport class TPCUtils {\n    /**\n     * Creates a new instance for a given TraceablePeerConnection\n     *\n     * @param peerconnection - the tpc instance for which we have utility functions.\n     */\n    constructor(peerconnection) {\n        this.pc = peerconnection;\n        const bitrateSettings = this.pc.options?.videoQuality?.maxBitratesVideo;\n        const standardBitrates = {\n            low: LD_BITRATE,\n            standard: SD_BITRATE,\n            high: HD_BITRATE\n        };\n\n        // Check if the max. bitrates for video are specified through config.js videoQuality settings.\n        // Right now only VP8 bitrates are configured on the simulcast encodings, VP9 bitrates have to be\n        // configured on the SDP using b:AS line.\n        this.videoBitrates = bitrateSettings ?? standardBitrates;\n        const encodingBitrates = this.videoBitrates.VP8 ?? this.videoBitrates;\n\n        /**\n         * The startup configuration for the stream encodings that are applicable to\n         * the video stream when a new sender is created on the peerconnection. The initial\n         * config takes into account the differences in browser's simulcast implementation.\n         *\n         * Encoding parameters:\n         * active - determine the on/off state of a particular encoding.\n         * maxBitrate - max. bitrate value to be applied to that particular encoding\n         *  based on the encoding's resolution and config.js videoQuality settings if applicable.\n         * rid - Rtp Stream ID that is configured for a particular simulcast stream.\n         * scaleResolutionDownBy - the factor by which the encoding is scaled down from the\n         *  original resolution of the captured video.\n         */\n        this.localStreamEncodingsConfig = [\n            {\n                active: true,\n                maxBitrate: browser.isFirefox() ? encodingBitrates.high : encodingBitrates.low,\n                rid: SIM_LAYER_1_RID,\n                scaleResolutionDownBy: browser.isFirefox() ? HD_SCALE_FACTOR : LD_SCALE_FACTOR\n            },\n            {\n                active: true,\n                maxBitrate: encodingBitrates.standard,\n                rid: SIM_LAYER_2_RID,\n                scaleResolutionDownBy: SD_SCALE_FACTOR\n            },\n            {\n                active: true,\n                maxBitrate: browser.isFirefox() ? encodingBitrates.low : encodingBitrates.high,\n                rid: SIM_LAYER_3_RID,\n                scaleResolutionDownBy: browser.isFirefox() ? LD_SCALE_FACTOR : HD_SCALE_FACTOR\n            }\n        ];\n    }\n\n    /**\n     * Obtains stream encodings that need to be configured on the given track based\n     * on the track media type and the simulcast setting.\n     * @param {JitsiLocalTrack} localTrack\n     */\n    _getStreamEncodings(localTrack) {\n        if (this.pc.isSimulcastOn() && localTrack.isVideoTrack()) {\n            return this.localStreamEncodingsConfig;\n        }\n\n        return localTrack.isVideoTrack()\n            ? [ {\n                active: true,\n                maxBitrate: this.videoBitrates.high\n            } ]\n            : [ { active: true } ];\n    }\n\n    /**\n     * Ensures that the ssrcs associated with a FID ssrc-group appear in the correct order, i.e.,\n     * the primary ssrc first and the secondary rtx ssrc later. This is important for unified\n     * plan since we have only one FID group per media description.\n     * @param {Object} description the webRTC session description instance for the remote\n     * description.\n     * @private\n     */\n    ensureCorrectOrderOfSsrcs(description) {\n        const parsedSdp = transform.parse(description.sdp);\n\n        parsedSdp.media.forEach(mLine => {\n            if (mLine.type === MediaType.AUDIO) {\n                return;\n            }\n            if (!mLine.ssrcGroups || !mLine.ssrcGroups.length) {\n                return;\n            }\n            let reorderedSsrcs = [];\n\n            const ssrcs = new Set();\n\n            mLine.ssrcGroups.map(group =>\n                group.ssrcs\n                    .split(' ')\n                    .filter(Boolean)\n                    .forEach(ssrc => ssrcs.add(ssrc))\n            );\n\n            ssrcs.forEach(ssrc => {\n                const sources = mLine.ssrcs.filter(source => source.id.toString() === ssrc);\n\n                reorderedSsrcs = reorderedSsrcs.concat(sources);\n            });\n            mLine.ssrcs = reorderedSsrcs;\n        });\n\n        return new RTCSessionDescription({\n            type: description.type,\n            sdp: transform.write(parsedSdp)\n        });\n    }\n\n    /**\n     * Returns the transceiver associated with a given RTCRtpSender/RTCRtpReceiver.\n     *\n     * @param {string} mediaType - type of track associated with the transceiver 'audio' or 'video'.\n     * @param {JitsiLocalTrack} localTrack - local track to be used for lookup.\n     * @returns {RTCRtpTransceiver}\n     */\n    findTransceiver(mediaType, localTrack = null) {\n        const transceiver = localTrack?.track && localTrack.getOriginalStream()\n            ? this.pc.peerconnection.getTransceivers().find(t => t.sender?.track?.id === localTrack.getTrackId())\n            : this.pc.peerconnection.getTransceivers().find(t => t.receiver?.track?.kind === mediaType);\n\n        return transceiver;\n    }\n\n    /**\n     * Takes in a *unified plan* offer and inserts the appropriate\n     * parameters for adding simulcast receive support.\n     * @param {Object} desc - A session description object\n     * @param {String} desc.type - the type (offer/answer)\n     * @param {String} desc.sdp - the sdp content\n     *\n     * @return {Object} A session description (same format as above) object\n     * with its sdp field modified to advertise simulcast receive support\n     */\n    insertUnifiedPlanSimulcastReceive(desc) {\n        // a=simulcast line is not needed on browsers where we SDP munging is used for enabling on simulcast.\n        // Remove this check when the client switches to RID/MID based simulcast on all browsers.\n        if (browser.usesSdpMungingForSimulcast()) {\n            return desc;\n        }\n        const sdp = transform.parse(desc.sdp);\n        const idx = sdp.media.findIndex(mline => mline.type === MediaType.VIDEO);\n\n        if (sdp.media[idx].rids && (sdp.media[idx].simulcast_03 || sdp.media[idx].simulcast)) {\n            // Make sure we don't have the simulcast recv line on video descriptions other than\n            // the first video description.\n            sdp.media.forEach((mline, i) => {\n                if (mline.type === MediaType.VIDEO && i !== idx) {\n                    sdp.media[i].rids = undefined;\n                    sdp.media[i].simulcast = undefined;\n\n                    // eslint-disable-next-line camelcase\n                    sdp.media[i].simulcast_03 = undefined;\n                }\n            });\n\n            return new RTCSessionDescription({\n                type: desc.type,\n                sdp: transform.write(sdp)\n            });\n        }\n\n        // In order of highest to lowest spatial quality\n        sdp.media[idx].rids = [\n            {\n                id: SIM_LAYER_1_RID,\n                direction: 'recv'\n            },\n            {\n                id: SIM_LAYER_2_RID,\n                direction: 'recv'\n            },\n            {\n                id: SIM_LAYER_3_RID,\n                direction: 'recv'\n            }\n        ];\n\n        // Firefox 72 has stopped parsing the legacy rid= parameters in simulcast attributes.\n        // eslint-disable-next-line max-len\n        // https://www.fxsitecompat.dev/en-CA/docs/2019/pt-and-rid-in-webrtc-simulcast-attributes-are-no-longer-supported/\n        const simulcastLine = browser.isFirefox() && browser.isVersionGreaterThan(71)\n            ? `recv ${SIM_LAYER_RIDS.join(';')}`\n            : `recv rid=${SIM_LAYER_RIDS.join(';')}`;\n\n        // eslint-disable-next-line camelcase\n        sdp.media[idx].simulcast_03 = {\n            value: simulcastLine\n        };\n\n        return new RTCSessionDescription({\n            type: desc.type,\n            sdp: transform.write(sdp)\n        });\n    }\n\n    /**\n    * Adds {@link JitsiLocalTrack} to the WebRTC peerconnection for the first time.\n    * @param {JitsiLocalTrack} track - track to be added to the peerconnection.\n    * @param {boolean} isInitiator - boolean that indicates if the endpoint is offerer in a p2p connection.\n    * @returns {void}\n    */\n    addTrack(localTrack, isInitiator) {\n        const track = localTrack.getTrack();\n\n        if (isInitiator) {\n            const streams = [];\n\n            if (localTrack.getOriginalStream()) {\n                streams.push(localTrack.getOriginalStream());\n            }\n\n            // Use pc.addTransceiver() for the initiator case when local tracks are getting added\n            // to the peerconnection before a session-initiate is sent over to the peer.\n            const transceiverInit = {\n                direction: MediaDirection.SENDRECV,\n                streams,\n                sendEncodings: []\n            };\n\n            if (!browser.isFirefox()) {\n                transceiverInit.sendEncodings = this._getStreamEncodings(localTrack);\n            }\n            this.pc.peerconnection.addTransceiver(track, transceiverInit);\n        } else {\n            // Use pc.addTrack() for responder case so that we can re-use the m-lines that were created\n            // when setRemoteDescription was called. pc.addTrack() automatically  attaches to any existing\n            // unused \"recv-only\" transceiver.\n            this.pc.peerconnection.addTrack(track);\n        }\n    }\n\n    /**\n     * Returns the calculated active state of the simulcast encodings based on the frame height requested for the send\n     * stream. All the encodings that have a resolution lower than the frame height requested will be enabled.\n     *\n     * @param {JitsiLocalTrack} localVideoTrack The local video track.\n     * @param {number} newHeight The resolution requested for the video track.\n     * @returns {Array<boolean>}\n     */\n    calculateEncodingsActiveState(localVideoTrack, newHeight) {\n        const localTrack = localVideoTrack.getTrack();\n        const { height } = localTrack.getSettings();\n        const encodingsState = this.localStreamEncodingsConfig\n        .map(encoding => height / encoding.scaleResolutionDownBy)\n        .map((frameHeight, idx) => {\n            let active = localVideoTrack.getVideoType() === VideoType.CAMERA\n\n                // Keep the LD stream enabled even when the LD stream's resolution is higher than of the requested\n                // resolution. This can happen when camera is captured at resolutions higher than 720p but the\n                // requested resolution is 180. Since getParameters doesn't give us information about the resolutions\n                // of the simulcast encodings, we have to rely on our initial config for the simulcast streams.\n                ? newHeight > 0 && this.localStreamEncodingsConfig[idx]?.scaleResolutionDownBy === LD_SCALE_FACTOR\n                    ? true\n                    : frameHeight <= newHeight\n\n                // Keep all the encodings for desktop track active.\n                : true;\n\n            // Disable the lower spatial layers for screensharing in Unified plan when low fps screensharing is in\n            // progress. Sending all three streams often results in the browser suspending the high resolution in low\n            // b/w and cpu cases, especially on the low end machines. Suspending the low resolution streams ensures\n            // that the highest resolution stream is available always. Safari is an exception here since it does not\n            // send the desktop stream at all if only the high resolution stream is enabled.\n            if (this.pc.isSharingLowFpsScreen()\n                && this.pc.usesUnifiedPlan()\n                && !browser.isWebKitBased()\n                && this.localStreamEncodingsConfig[idx].scaleResolutionDownBy !== HD_SCALE_FACTOR) {\n                active = false;\n            }\n\n            return active;\n        });\n\n        return encodingsState;\n    }\n\n    /**\n     * Returns the calculates max bitrates that need to be configured on the simulcast encodings based on the video\n     * type and other considerations associated with screenshare.\n     *\n     * @param {JitsiLocalTrack} localVideoTrack The local video track.\n     * @returns {Array<number>}\n     */\n    calculateEncodingsBitrates(localVideoTrack) {\n        const videoType = localVideoTrack.getVideoType();\n        const desktopShareBitrate = this.pc.options?.videoQuality?.desktopBitrate || DESKTOP_SHARE_RATE;\n        const presenterEnabled = localVideoTrack._originalStream\n            && localVideoTrack._originalStream.id !== localVideoTrack.getStreamId();\n\n        const encodingsBitrates = this.localStreamEncodingsConfig\n        .map(encoding => {\n            const bitrate = this.pc.isSharingLowFpsScreen() && !browser.isWebKitBased()\n\n                // For low fps screensharing, set a max bitrate of 500 Kbps when presenter is not turned on, 2500 Kbps\n                // otherwise.\n                ? presenterEnabled ? HD_BITRATE : desktopShareBitrate\n\n                // For high fps screenshare, 'maxBitrate' setting must be cleared on Chrome in plan-b, because\n                // if simulcast is enabled for screen and maxBitrates are set then Chrome will not send the\n                // desktop stream.\n                : videoType === VideoType.DESKTOP && browser.isChromiumBased() && !this.pc.usesUnifiedPlan()\n                    ? undefined\n                    : encoding.maxBitrate;\n\n            return bitrate;\n        });\n\n        return encodingsBitrates;\n    }\n\n    /**\n     * Replaces the existing track on a RTCRtpSender with the given track.\n     *\n     * @param {JitsiLocalTrack} oldTrack - existing track on the sender that needs to be removed.\n     * @param {JitsiLocalTrack} newTrack - new track that needs to be added to the sender.\n     * @returns {Promise<RTCRtpTransceiver>} - resolved with the associated transceiver when done, rejected otherwise.\n     */\n    replaceTrack(oldTrack, newTrack) {\n        const mediaType = newTrack?.getType() ?? oldTrack?.getType();\n        const track = newTrack?.getTrack() ?? null;\n        let transceiver;\n\n        // If old track exists, replace the track on the corresponding sender.\n        if (oldTrack && !oldTrack.isMuted()) {\n            transceiver = this.pc.peerconnection.getTransceivers().find(t => t.sender.track === oldTrack.getTrack());\n\n        // Find the first recvonly transceiver when more than one track of the same media type is being added to the pc.\n        // As part of the track addition, a new m-line was added to the remote description with direction set to\n        // recvonly.\n        } else if (FeatureFlags.isMultiStreamSupportEnabled()\n            && this.pc.getLocalTracks(mediaType)?.length\n            && !newTrack.conference) {\n            transceiver = this.pc.peerconnection.getTransceivers().find(\n                t => t.receiver.track.kind === mediaType\n                && t.direction === MediaDirection.RECVONLY\n                && t.currentDirection === MediaDirection.INACTIVE);\n\n        // For unmute operations, find the transceiver based on the track index in the source name if present, otherwise\n        // it is assumed to be the first local track that was added to the peerconnection.\n        } else {\n            transceiver = this.pc.peerconnection.getTransceivers().find(t => t.receiver.track.kind === mediaType);\n\n            const sourceName = newTrack?.getSourceName();\n\n            if (sourceName) {\n                const trackIndex = Number(sourceName.split('-')[1].substring(1));\n\n                if (trackIndex) {\n                    transceiver = this.pc.peerconnection.getTransceivers()\n                        .filter(t => t.receiver.track.kind === mediaType\n                            && t.direction !== MediaDirection.RECVONLY)[trackIndex];\n                }\n            }\n        }\n\n        if (!transceiver) {\n            return Promise.reject(new Error('replace track failed'));\n        }\n        logger.debug(`${this.pc} Replacing ${oldTrack} with ${newTrack}`);\n\n        return transceiver.sender.replaceTrack(track)\n            .then(() => Promise.resolve(transceiver));\n    }\n\n    /**\n    * Enables/disables audio transmission on the peer connection. When\n    * disabled the audio transceiver direction will be set to 'inactive'\n    * which means that no data will be sent nor accepted, but\n    * the connection should be kept alive.\n    * @param {boolean} active - true to enable audio media transmission or\n    * false to disable.\n    * @returns {void}\n    */\n    setAudioTransferActive(active) {\n        this.setMediaTransferActive(MediaType.AUDIO, active);\n    }\n\n    /**\n     * Set the simulcast stream encoding properties on the RTCRtpSender.\n     * @param {JitsiLocalTrack} track - the current track in use for which\n     * the encodings are to be set.\n     * @returns {Promise<void>} - resolved when done.\n     */\n    setEncodings(track) {\n        const mediaType = track.getType();\n        const transceiver = this.findTransceiver(mediaType, track);\n        const parameters = transceiver?.sender?.getParameters();\n\n        // Resolve if the encodings are not available yet. This happens immediately after the track is added to the\n        // peerconnection on chrome in unified-plan. It is ok to ignore and not report the error here since the\n        // action that triggers 'addTrack' (like unmute) will also configure the encodings and set bitrates after that.\n        if (!parameters?.encodings?.length) {\n            return Promise.resolve();\n        }\n        parameters.encodings = this._getStreamEncodings(track);\n\n        return transceiver.sender.setParameters(parameters);\n    }\n\n    /**\n     * Enables/disables media transmission on the peerconnection by changing the direction\n     * on the transceiver for the specified media type.\n     * @param {String} mediaType - 'audio' or 'video'\n     * @param {boolean} active - true to enable media transmission or false\n     * to disable.\n     * @returns {void}\n     */\n    setMediaTransferActive(mediaType, active) {\n        const transceivers = this.pc.peerconnection.getTransceivers()\n            .filter(t => t.receiver && t.receiver.track && t.receiver.track.kind === mediaType);\n        const localTracks = this.pc.getLocalTracks(mediaType);\n\n        logger.info(`${this.pc} ${active ? 'Enabling' : 'Suspending'} ${mediaType} media transfer.`);\n        transceivers.forEach((transceiver, idx) => {\n            if (active) {\n                // The first transceiver is for the local track and only this one can be set to 'sendrecv'\n                if (idx === 0 && localTracks.length) {\n                    transceiver.direction = MediaDirection.SENDRECV;\n                } else {\n                    transceiver.direction = MediaDirection.RECVONLY;\n                }\n            } else {\n                transceiver.direction = MediaDirection.INACTIVE;\n            }\n        });\n    }\n\n    /**\n    * Enables/disables video media transmission on the peer connection. When\n    * disabled the SDP video media direction in the local SDP will be adjusted to\n    * 'inactive' which means that no data will be sent nor accepted, but\n    * the connection should be kept alive.\n    * @param {boolean} active - true to enable video media transmission or\n    * false to disable.\n    * @returns {void}\n    */\n    setVideoTransferActive(active) {\n        this.setMediaTransferActive(MediaType.VIDEO, active);\n    }\n\n    /**\n     * Ensures that the resolution of the stream encodings are consistent with the values\n     * that were configured on the RTCRtpSender when the source was added to the peerconnection.\n     * This should prevent us from overriding the default values if the browser returns\n     * erroneous values when RTCRtpSender.getParameters is used for getting the encodings info.\n     * @param {Object} parameters - the RTCRtpEncodingParameters obtained from the browser.\n     * @returns {void}\n     */\n    updateEncodingsResolution(parameters) {\n        if (!(browser.isWebKitBased() && parameters.encodings && Array.isArray(parameters.encodings))) {\n            return;\n        }\n        const allEqualEncodings\n            = encodings => encodings.every(encoding => typeof encoding.scaleResolutionDownBy !== 'undefined'\n                && encoding.scaleResolutionDownBy === encodings[0].scaleResolutionDownBy);\n\n        // Implement the workaround only when all the encodings report the same resolution.\n        if (allEqualEncodings(parameters.encodings)) {\n            parameters.encodings.forEach((encoding, idx) => {\n                encoding.scaleResolutionDownBy = this.localStreamEncodingsConfig[idx].scaleResolutionDownBy;\n            });\n        }\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { Interop } from '@jitsi/sdp-interop';\nimport transform from 'sdp-transform';\n\nimport * as CodecMimeType from '../../service/RTC/CodecMimeType';\nimport MediaDirection from '../../service/RTC/MediaDirection';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport { getSourceNameForJitsiTrack } from '../../service/RTC/SignalingLayer';\nimport * as VideoType from '../../service/RTC/VideoType';\nimport { SS_DEFAULT_FRAME_RATE } from '../RTC/ScreenObtainer';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport LocalSdpMunger from '../sdp/LocalSdpMunger';\nimport RtxModifier from '../sdp/RtxModifier';\nimport SDP from '../sdp/SDP';\nimport SDPUtil from '../sdp/SDPUtil';\nimport SdpConsistency from '../sdp/SdpConsistency';\nimport SdpSimulcast from '../sdp/SdpSimulcast.ts';\nimport { SdpTransformWrap } from '../sdp/SdpTransformUtil';\nimport * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nimport JitsiRemoteTrack from './JitsiRemoteTrack';\nimport RTC from './RTC';\nimport RTCUtils from './RTCUtils';\nimport {\n    HD_BITRATE,\n    HD_SCALE_FACTOR,\n    SIM_LAYER_RIDS,\n    TPCUtils\n} from './TPCUtils';\n\n// FIXME SDP tools should end up in some kind of util module\n\nconst logger = getLogger(__filename);\nconst DEGRADATION_PREFERENCE_CAMERA = 'maintain-framerate';\nconst DEGRADATION_PREFERENCE_DESKTOP = 'maintain-resolution';\n\n/* eslint-disable max-params */\n\n/**\n * Creates new instance of 'TraceablePeerConnection'.\n *\n * @param {RTC} rtc the instance of <tt>RTC</tt> service\n * @param {number} id the peer connection id assigned by the parent RTC module.\n * @param {SignalingLayer} signalingLayer the signaling layer instance\n * @param {object} pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n * @param {object} constraints WebRTC 'PeerConnection' constraints\n * @param {boolean} isP2P indicates whether or not the new instance will be used in a peer to peer connection.\n * @param {object} options <tt>TracablePeerConnection</tt> config options.\n * @param {boolean} options.disableSimulcast if set to 'true' will disable the simulcast.\n * @param {boolean} options.disableRtx if set to 'true' will disable the RTX.\n * @param {string} options.disabledCodec the mime type of the code that should not be negotiated on the peerconnection.\n * @param {string} options.preferredCodec the mime type of the codec that needs to be made the preferred codec for the\n * peerconnection.\n * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\n * @param {boolean} options.usesUnifiedPlan Indicates if the  browser is running in unified plan mode.\n *\n * FIXME: initially the purpose of TraceablePeerConnection was to be able to\n * debug the peer connection. Since many other responsibilities have been added\n * it would make sense to extract a separate class from it and come up with\n * a more suitable name.\n *\n * @constructor\n */\nexport default function TraceablePeerConnection(\n        rtc,\n        id,\n        signalingLayer,\n        pcConfig,\n        constraints,\n        isP2P,\n        options) {\n\n    /**\n     * Indicates whether or not this peer connection instance is actively\n     * sending/receiving audio media. When set to <tt>false</tt> the SDP audio\n     * media direction will be adjusted to 'inactive' in order to suspend\n     * the transmission.\n     * @type {boolean}\n     * @private\n     */\n    this.audioTransferActive = !(options.startSilent === true);\n\n    /**\n     * The DTMF sender instance used to send DTMF tones.\n     *\n     * @type {RTCDTMFSender|undefined}\n     * @private\n     */\n    this._dtmfSender = undefined;\n\n    /**\n     * @typedef {Object} TouchToneRequest\n     * @property {string} tones - The DTMF tones string as defined by\n     * {@code RTCDTMFSender.insertDTMF}, 'tones' argument.\n     * @property {number} duration - The amount of time in milliseconds that\n     * each DTMF should last.\n     * @property {string} interToneGap - The length of time in miliseconds to\n     * wait between tones.\n     */\n    /**\n     * TouchToneRequests which are waiting to be played. This queue is filled\n     * if there are touch tones currently being played.\n     *\n     * @type {Array<TouchToneRequest>}\n     * @private\n     */\n    this._dtmfTonesQueue = [];\n\n    /**\n     * Indicates whether or not this peer connection instance is actively\n     * sending/receiving video media. When set to <tt>false</tt> the SDP video\n     * media direction will be adjusted to 'inactive' in order to suspend\n     * the transmission.\n     * @type {boolean}\n     * @private\n     */\n    this.videoTransferActive = true;\n\n    /**\n     * The parent instance of RTC service which created this\n     * <tt>TracablePeerConnection</tt>.\n     * @type {RTC}\n     */\n    this.rtc = rtc;\n\n    /**\n     * The peer connection identifier assigned by the RTC module.\n     * @type {number}\n     */\n    this.id = id;\n\n    /**\n     * Indicates whether or not this instance is used in a peer to peer\n     * connection.\n     * @type {boolean}\n     */\n    this.isP2P = isP2P;\n\n    /**\n     * The map holds remote tracks associated with this peer connection. It maps user's JID to media type and a set of\n     * remote tracks.\n     * @type {Map<string, Map<MediaType, Set<JitsiRemoteTrack>>>}\n     */\n    this.remoteTracks = new Map();\n\n    /**\n     * A map which stores local tracks mapped by {@link JitsiLocalTrack.rtcId}\n     * @type {Map<number, JitsiLocalTrack>}\n     */\n    this.localTracks = new Map();\n\n    /**\n     * Keeps tracks of the WebRTC <tt>MediaStream</tt>s that have been added to\n     * the underlying WebRTC PeerConnection.\n     * @type {Array}\n     * @private\n     */\n    this._addedStreams = [];\n\n    /**\n     * @typedef {Object} TPCGroupInfo\n     * @property {string} semantics the SSRC groups semantics\n     * @property {Array<number>} ssrcs group's SSRCs in order where the first\n     * one is group's primary SSRC, the second one is secondary (RTX) and so\n     * on...\n     */\n    /**\n     * @typedef {Object} TPCSSRCInfo\n     * @property {Array<number>} ssrcs an array which holds all track's SSRCs\n     * @property {Array<TPCGroupInfo>} groups an array stores all track's SSRC\n     * groups\n     */\n    /**\n     * Holds the info about local track's SSRCs mapped per their\n     * {@link JitsiLocalTrack.rtcId}\n     * @type {Map<number, TPCSSRCInfo>}\n     */\n    this.localSSRCs = new Map();\n\n    /**\n     * The local ICE username fragment for this session.\n     */\n    this.localUfrag = null;\n\n    /**\n     * The remote ICE username fragment for this session.\n     */\n    this.remoteUfrag = null;\n\n    /**\n     * The DTLS transport object for the PeerConnection.\n     * Note: this assume only one shared transport exists because we bundled\n     *       all streams on the same underlying transport.\n     */\n    this._dtlsTransport = null;\n\n    /**\n     * The signaling layer which operates this peer connection.\n     * @type {SignalingLayer}\n     */\n    this.signalingLayer = signalingLayer;\n\n    // SignalingLayer listeners\n    this._peerVideoTypeChanged = this._peerVideoTypeChanged.bind(this);\n    this.signalingLayer.on(\n        SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n        this._peerVideoTypeChanged);\n\n    this._peerMutedChanged = this._peerMutedChanged.bind(this);\n    this.signalingLayer.on(\n        SignalingEvents.PEER_MUTED_CHANGED,\n        this._peerMutedChanged);\n    this.options = options;\n\n    // Make sure constraints is properly formatted in order to provide information about whether or not this\n    // connection is P2P to rtcstats.\n    const safeConstraints = constraints || {};\n\n    safeConstraints.optional = safeConstraints.optional || [];\n\n    // The `optional` parameter needs to be of type array, otherwise chrome will throw an error.\n    // Firefox and Safari just ignore it.\n    if (Array.isArray(safeConstraints.optional)) {\n        safeConstraints.optional.push({ rtcStatsSFUP2P: this.isP2P });\n    } else {\n        logger.warn('Optional param is not an array, rtcstats p2p data is omitted.');\n    }\n\n    this.peerconnection = new RTCUtils.RTCPeerConnectionType(pcConfig, safeConstraints);\n\n    this.tpcUtils = new TPCUtils(this);\n    this.updateLog = [];\n    this.stats = {};\n    this.statsinterval = null;\n\n    /**\n     * Flag used to indicate if simulcast is turned off and a cap of 500 Kbps is applied on screensharing.\n     */\n    this._capScreenshareBitrate = this.options.capScreenshareBitrate;\n\n    /**\n    * Flag used to indicate if the browser is running in unified  plan mode.\n    */\n    this._usesUnifiedPlan = options.usesUnifiedPlan;\n\n    /**\n     * Flag used to indicate if RTCRtpTransceiver#setCodecPreferences is to be used instead of SDP\n     * munging for codec selection.\n     */\n    this._usesTransceiverCodecPreferences = browser.supportsCodecPreferences() && this._usesUnifiedPlan;\n    this._usesTransceiverCodecPreferences\n        && logger.info('Using RTCRtpTransceiver#setCodecPreferences for codec selection');\n\n    /**\n     * @type {number} The max number of stats to keep in this.stats. Limit to\n     * 300 values, i.e. 5 minutes; set to 0 to disable\n     */\n    this.maxstats = options.maxstats;\n\n    this.interop = new Interop();\n\n    if (this._usesUnifiedPlan) {\n        this.simulcast = new SdpSimulcast({ numOfLayers: SIM_LAYER_RIDS.length });\n    } else {\n        const Simulcast = require('@jitsi/sdp-simulcast');\n\n        this.simulcast = new Simulcast(\n            {\n                numOfLayers: SIM_LAYER_RIDS.length,\n                explodeRemoteSimulcast: false,\n                usesUnifiedPlan: false\n            });\n    }\n\n    this.sdpConsistency = new SdpConsistency(this.toString());\n\n    /**\n     * Munges local SDP provided to the Jingle Session in order to prevent from\n     * sending SSRC updates on attach/detach and mute/unmute (for video).\n     * @type {LocalSdpMunger}\n     */\n    this.localSdpMunger = new LocalSdpMunger(this, this.rtc.getLocalEndpointId());\n\n    /**\n     * TracablePeerConnection uses RTC's eventEmitter\n     * @type {EventEmitter}\n     */\n    this.eventEmitter = rtc.eventEmitter;\n    this.rtxModifier = new RtxModifier();\n\n    /**\n     * The height constraint applied on the video sender. The default value is 2160 (4K) when layer suspension is\n     * explicitly disabled.\n     */\n    this._senderVideoMaxHeight = 2160;\n\n    /**\n     * The height constraints to be applied on the sender per local video source (source name as the key).\n     * @type {Map<string, number>}\n     */\n    this._senderMaxHeights = new Map();\n\n    // override as desired\n    this.trace = (what, info) => {\n        logger.debug(what, info);\n\n        this.updateLog.push({\n            time: new Date(),\n            type: what,\n            value: info || ''\n        });\n    };\n    this.onicecandidate = null;\n    this.peerconnection.onicecandidate = event => {\n        this.trace(\n            'onicecandidate',\n            JSON.stringify(event.candidate, null, ' '));\n\n        if (this.onicecandidate !== null) {\n            this.onicecandidate(event);\n        }\n    };\n\n    // Use track events when browser is running in unified plan mode and stream events in plan-b mode.\n    if (this._usesUnifiedPlan) {\n        this.onTrack = evt => {\n            const stream = evt.streams[0];\n\n            this._remoteTrackAdded(stream, evt.track, evt.transceiver);\n            stream.addEventListener('removetrack', e => {\n                this._remoteTrackRemoved(stream, e.track);\n            });\n        };\n        this.peerconnection.addEventListener('track', this.onTrack);\n    } else {\n        this.peerconnection.onaddstream = event => this._remoteStreamAdded(event.stream);\n        this.peerconnection.onremovestream = event => this._remoteStreamRemoved(event.stream);\n    }\n    this.onsignalingstatechange = null;\n    this.peerconnection.onsignalingstatechange = event => {\n        this.trace('onsignalingstatechange', this.signalingState);\n        if (this.onsignalingstatechange !== null) {\n            this.onsignalingstatechange(event);\n        }\n    };\n    this.oniceconnectionstatechange = null;\n    this.peerconnection.oniceconnectionstatechange = event => {\n        this.trace('oniceconnectionstatechange', this.iceConnectionState);\n        if (this.oniceconnectionstatechange !== null) {\n            this.oniceconnectionstatechange(event);\n        }\n    };\n    this.onnegotiationneeded = null;\n    this.peerconnection.onnegotiationneeded = event => {\n        this.trace('onnegotiationneeded');\n        if (this.onnegotiationneeded !== null) {\n            this.onnegotiationneeded(event);\n        }\n    };\n    this.onconnectionstatechange = null;\n    this.peerconnection.onconnectionstatechange = event => {\n        this.trace('onconnectionstatechange', this.connectionState);\n        if (this.onconnectionstatechange !== null) {\n            this.onconnectionstatechange(event);\n        }\n    };\n    this.ondatachannel = null;\n    this.peerconnection.ondatachannel = event => {\n        this.trace('ondatachannel');\n        if (this.ondatachannel !== null) {\n            this.ondatachannel(event);\n        }\n    };\n\n    if (this.maxstats) {\n        this.statsinterval = window.setInterval(() => {\n            this.getStats().then(stats => {\n                if (typeof stats?.result === 'function') {\n                    const results = stats.result();\n\n                    for (let i = 0; i < results.length; ++i) {\n                        const res = results[i];\n\n                        res.names().forEach(name => {\n                            this._processStat(res, name, res.stat(name));\n                        });\n                    }\n                } else {\n                    stats.forEach(r => this._processStat(r, '', r));\n                }\n            });\n        }, 1000);\n    }\n\n    logger.info(`Create new ${this}`);\n}\n\n/* eslint-enable max-params */\n\n/**\n * Process stat and adds it to the array of stats we store.\n * @param report the current stats report.\n * @param name the name of the report, if available\n * @param statValue the value to add.\n * @private\n */\nTraceablePeerConnection.prototype._processStat\n    = function(report, name, statValue) {\n        const id = `${report.id}-${name}`;\n        let s = this.stats[id];\n        const now = new Date();\n\n        if (!s) {\n            this.stats[id] = s = {\n                startTime: now,\n                endTime: now,\n                values: [],\n                times: []\n            };\n        }\n        s.values.push(statValue);\n        s.times.push(now.getTime());\n        if (s.values.length > this.maxstats) {\n            s.values.shift();\n            s.times.shift();\n        }\n        s.endTime = now;\n    };\n\n/**\n * Returns a string representation of a SessionDescription object.\n */\nconst dumpSDP = function(description) {\n    if (typeof description === 'undefined' || description === null) {\n        return '';\n    }\n\n    return `type: ${description.type}\\r\\n${description.sdp}`;\n};\n\n\n/**\n * Forwards the {@link peerconnection.iceConnectionState} state except that it\n * will convert \"completed\" into \"connected\" where both mean that the ICE has\n * succeeded and is up and running. We never see \"completed\" state for\n * the JVB connection, but it started appearing for the P2P one. This method\n * allows to adapt old logic to this new situation.\n * @return {string}\n */\nTraceablePeerConnection.prototype.getConnectionState = function() {\n    const state = this.peerconnection.iceConnectionState;\n\n    if (state === 'completed') {\n        return 'connected';\n    }\n\n    return state;\n};\n\n/**\n * Obtains the media direction for given {@link MediaType}. The method takes\n * into account whether or not there are any local tracks for media and\n * the {@link audioTransferActive} and {@link videoTransferActive} flags.\n * @param {MediaType} mediaType\n * @param {boolean} isAddOperation whether the direction is to be calculated after a source-add action.\n * @return {string} one of the SDP direction constants ('sendrecv, 'recvonly'\n * etc.) which should be used when setting local description on the peer\n * connection.\n * @private\n */\nTraceablePeerConnection.prototype.getDesiredMediaDirection = function(mediaType, isAddOperation = false) {\n    const hasLocalSource = this.hasAnyTracksOfType(mediaType);\n\n    if (this._usesUnifiedPlan) {\n        return isAddOperation\n            ? hasLocalSource ? MediaDirection.SENDRECV : MediaDirection.SENDONLY\n            : hasLocalSource ? MediaDirection.RECVONLY : MediaDirection.INACTIVE;\n    }\n\n    const mediaTransferActive = mediaType === MediaType.AUDIO ? this.audioTransferActive : this.videoTransferActive;\n\n    if (mediaTransferActive) {\n        return hasLocalSource ? MediaDirection.SENDRECV : MediaDirection.RECVONLY;\n    }\n\n    return MediaDirection.INACTIVE;\n};\n\n/**\n * Returns the list of RTCRtpReceivers created for the source of the given media type associated with\n * the set of remote endpoints specified.\n * @param {Array<string>} endpoints list of the endpoints\n * @param {string} mediaType 'audio' or 'video'\n * @returns {Array<RTCRtpReceiver>} list of receivers created by the peerconnection.\n */\nTraceablePeerConnection.prototype._getReceiversByEndpointIds = function(endpoints, mediaType) {\n    let remoteTracks = [];\n    let receivers = [];\n\n    for (const endpoint of endpoints) {\n        remoteTracks = remoteTracks.concat(this.getRemoteTracks(endpoint, mediaType));\n    }\n\n    // Get the ids of the MediaStreamTracks associated with each of these remote tracks.\n    const remoteTrackIds = remoteTracks.map(remote => remote.track?.id);\n\n    receivers = this.peerconnection.getReceivers()\n        .filter(receiver => receiver.track\n            && receiver.track.kind === mediaType\n            && remoteTrackIds.find(trackId => trackId === receiver.track.id));\n\n    return receivers;\n};\n\n/**\n * Tells whether or not this TPC instance is using Simulcast.\n * @return {boolean} <tt>true</tt> if simulcast is enabled and active or\n * <tt>false</tt> if it's turned off.\n */\nTraceablePeerConnection.prototype.isSimulcastOn = function() {\n    return !this.options.disableSimulcast;\n};\n\n/**\n * Handles {@link SignalingEvents.PEER_VIDEO_TYPE_CHANGED}\n * @param {string} endpointId the video owner's ID (MUC nickname)\n * @param {VideoType} videoType the new value\n * @private\n */\nTraceablePeerConnection.prototype._peerVideoTypeChanged = function(\n        endpointId,\n        videoType) {\n    // Check if endpointId has a value to avoid action on random track\n    if (!endpointId) {\n        logger.error(`${this} No endpointID on peerVideoTypeChanged`);\n\n        return;\n    }\n    const videoTrack = this.getRemoteTracks(endpointId, MediaType.VIDEO);\n\n    if (videoTrack.length) {\n        // NOTE 1 track per media type is assumed\n        videoTrack[0]._setVideoType(videoType);\n    }\n};\n\n/**\n * Handles remote track mute / unmute events.\n * @param {string} endpointId the track owner's identifier (MUC nickname)\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param {boolean} isMuted the new mute state\n * @private\n */\nTraceablePeerConnection.prototype._peerMutedChanged = function(\n        endpointId,\n        mediaType,\n        isMuted) {\n    // Check if endpointId is a value to avoid doing action on all remote tracks\n    if (!endpointId) {\n        logger.error(`${this} On peerMuteChanged - no endpoint ID`);\n\n        return;\n    }\n    const track = this.getRemoteTracks(endpointId, mediaType);\n\n    if (track.length) {\n        // NOTE 1 track per media type is assumed\n        track[0].setMute(isMuted);\n    }\n};\n\n/**\n * Obtains audio levels of the remote audio tracks by getting the source information on the RTCRtpReceivers.\n * The information relevant to the ssrc is updated each time a RTP packet constaining the ssrc is received.\n * @param {Array<string>} speakerList list of endpoint ids for which audio levels are to be gathered.\n * @returns {Object} containing ssrc and audio level information as a key-value pair.\n */\nTraceablePeerConnection.prototype.getAudioLevels = function(speakerList = []) {\n    const audioLevels = {};\n    const audioReceivers = speakerList.length\n        ? this._getReceiversByEndpointIds(speakerList, MediaType.AUDIO)\n        : this.peerconnection.getReceivers()\n            .filter(receiver => receiver.track && receiver.track.kind === MediaType.AUDIO && receiver.track.enabled);\n\n    audioReceivers.forEach(remote => {\n        const ssrc = remote.getSynchronizationSources();\n\n        if (ssrc && ssrc.length) {\n            // As per spec, this audiolevel is a value between 0..1 (linear), where 1.0\n            // represents 0 dBov, 0 represents silence, and 0.5 represents approximately\n            // 6 dBSPL change in the sound pressure level from 0 dBov.\n            // https://www.w3.org/TR/webrtc/#dom-rtcrtpcontributingsource-audiolevel\n            audioLevels[ssrc[0].source] = ssrc[0].audioLevel;\n        }\n    });\n\n    return audioLevels;\n};\n\n/**\n * Obtains local tracks for given {@link MediaType}. If the <tt>mediaType</tt>\n * argument is omitted the list of all local tracks will be returned.\n * @param {MediaType} [mediaType]\n * @return {Array<JitsiLocalTrack>}\n */\nTraceablePeerConnection.prototype.getLocalTracks = function(mediaType) {\n    let tracks = Array.from(this.localTracks.values());\n\n    if (mediaType !== undefined) {\n        tracks = tracks.filter(track => track.getType() === mediaType);\n    }\n\n    return tracks;\n};\n\n/**\n * Retrieves the local video tracks.\n *\n * @returns {JitsiLocalTrack|undefined} - local video tracks.\n */\nTraceablePeerConnection.prototype.getLocalVideoTracks = function() {\n    return this.getLocalTracks(MediaType.VIDEO);\n};\n\n/**\n * Checks whether or not this {@link TraceablePeerConnection} instance contains any local tracks for given\n * <tt>mediaType</tt>.\n *\n * @param {MediaType} mediaType - The media type.\n * @return {boolean}\n */\nTraceablePeerConnection.prototype.hasAnyTracksOfType = function(mediaType) {\n    if (!mediaType) {\n        throw new Error('\"mediaType\" is required');\n    }\n\n    return this.getLocalTracks(mediaType).length > 0;\n};\n\n/**\n * Obtains all remote tracks currently known to this PeerConnection instance.\n *\n * @param {string} [endpointId] - The track owner's identifier (MUC nickname)\n * @param {MediaType} [mediaType] - The remote tracks will be filtered by their media type if this argument is\n * specified.\n * @return {Array<JitsiRemoteTrack>}\n */\nTraceablePeerConnection.prototype.getRemoteTracks = function(endpointId, mediaType) {\n    let remoteTracks = [];\n    const endpoints = endpointId ? [ endpointId ] : this.remoteTracks.keys();\n\n    for (const endpoint of endpoints) {\n        const endpointTracksByMediaType = this.remoteTracks.get(endpoint);\n\n        if (endpointTracksByMediaType) {\n            for (const trackMediaType of endpointTracksByMediaType.keys()) {\n                // per media type filtering\n                if (!mediaType || mediaType === trackMediaType) {\n                    remoteTracks = remoteTracks.concat(Array.from(endpointTracksByMediaType.get(trackMediaType)));\n                }\n            }\n        }\n    }\n\n    return remoteTracks;\n};\n\n/**\n * Parses the remote description and returns the sdp lines of the sources associated with a remote participant.\n *\n * @param {string} id Endpoint id of the remote participant.\n * @returns {Array<string>} The sdp lines that have the ssrc information.\n */\nTraceablePeerConnection.prototype.getRemoteSourceInfoByParticipant = function(id) {\n    const removeSsrcInfo = [];\n    const remoteTracks = this.getRemoteTracks(id);\n\n    if (!remoteTracks?.length) {\n        return removeSsrcInfo;\n    }\n    const primarySsrcs = remoteTracks.map(track => track.getSSRC());\n    const sdp = new SDP(this.remoteDescription.sdp);\n\n    primarySsrcs.forEach((ssrc, idx) => {\n        for (const media of sdp.media) {\n            let lines = '';\n            let ssrcLines = SDPUtil.findLines(media, `a=ssrc:${ssrc}`);\n\n            if (ssrcLines.length) {\n                if (!removeSsrcInfo[idx]) {\n                    removeSsrcInfo[idx] = '';\n                }\n\n                // Check if there are any FID groups present for the primary ssrc.\n                const fidLines = SDPUtil.findLines(media, `a=ssrc-group:FID ${ssrc}`);\n\n                if (fidLines.length) {\n                    const secondarySsrc = fidLines[0].split(' ')[2];\n\n                    lines += `${fidLines[0]}\\r\\n`;\n                    ssrcLines = ssrcLines.concat(SDPUtil.findLines(media, `a=ssrc:${secondarySsrc}`));\n                }\n                removeSsrcInfo[idx] += `${ssrcLines.join('\\r\\n')}\\r\\n`;\n                removeSsrcInfo[idx] += lines;\n            }\n        }\n    });\n\n    return removeSsrcInfo;\n};\n\n/**\n * Returns the target bitrates configured for the local video source.\n *\n * @returns {Object}\n */\nTraceablePeerConnection.prototype.getTargetVideoBitrates = function() {\n    const currentCodec = this.getConfiguredVideoCodec();\n\n    return this.tpcUtils.videoBitrates[currentCodec.toUpperCase()] || this.tpcUtils.videoBitrates;\n};\n\n/**\n * Tries to find {@link JitsiTrack} for given SSRC number. It will search both\n * local and remote tracks bound to this instance.\n * @param {number} ssrc\n * @return {JitsiTrack|null}\n */\nTraceablePeerConnection.prototype.getTrackBySSRC = function(ssrc) {\n    if (typeof ssrc !== 'number') {\n        throw new Error(`SSRC ${ssrc} is not a number`);\n    }\n    for (const localTrack of this.localTracks.values()) {\n        if (this.getLocalSSRC(localTrack) === ssrc) {\n            return localTrack;\n        }\n    }\n    for (const remoteTrack of this.getRemoteTracks()) {\n        if (remoteTrack.getSSRC() === ssrc) {\n            return remoteTrack;\n        }\n    }\n\n    return null;\n};\n\n/**\n * Tries to find SSRC number for given {@link JitsiTrack} id. It will search\n * both local and remote tracks bound to this instance.\n * @param {string} id\n * @return {number|null}\n */\nTraceablePeerConnection.prototype.getSsrcByTrackId = function(id) {\n\n    const findTrackById = track => track.getTrack().id === id;\n    const localTrack = this.getLocalTracks().find(findTrackById);\n\n    if (localTrack) {\n        return this.getLocalSSRC(localTrack);\n    }\n\n    const remoteTrack = this.getRemoteTracks().find(findTrackById);\n\n    if (remoteTrack) {\n        return remoteTrack.getSSRC();\n    }\n\n    return null;\n};\n\n/**\n * Called when new remote MediaStream is added to the PeerConnection.\n * @param {MediaStream} stream the WebRTC MediaStream for remote participant\n */\nTraceablePeerConnection.prototype._remoteStreamAdded = function(stream) {\n    const streamId = RTC.getStreamID(stream);\n\n    if (!RTC.isUserStreamById(streamId)) {\n        logger.info(`${this} ignored remote 'stream added' event for non-user stream[id=${streamId}]`);\n\n        return;\n    }\n\n    // Bind 'addtrack'/'removetrack' event handlers\n    if (browser.isChromiumBased()) {\n        stream.onaddtrack = event => {\n            this._remoteTrackAdded(stream, event.track);\n        };\n        stream.onremovetrack = event => {\n            this._remoteTrackRemoved(stream, event.track);\n        };\n    }\n\n    // Call remoteTrackAdded for each track in the stream\n    const streamAudioTracks = stream.getAudioTracks();\n\n    for (const audioTrack of streamAudioTracks) {\n        this._remoteTrackAdded(stream, audioTrack);\n    }\n    const streamVideoTracks = stream.getVideoTracks();\n\n    for (const videoTrack of streamVideoTracks) {\n        this._remoteTrackAdded(stream, videoTrack);\n    }\n};\n\n\n/**\n * Called on \"track added\" and \"stream added\" PeerConnection events (because we\n * handle streams on per track basis). Finds the owner and the SSRC for\n * the track and passes that to ChatRoom for further processing.\n * @param {MediaStream} stream the WebRTC MediaStream instance which is\n * the parent of the track\n * @param {MediaStreamTrack} track the WebRTC MediaStreamTrack added for remote\n * participant.\n * @param {RTCRtpTransceiver} transceiver the WebRTC transceiver that is created\n * for the remote participant in unified plan.\n */\nTraceablePeerConnection.prototype._remoteTrackAdded = function(stream, track, transceiver = null) {\n    const streamId = RTC.getStreamID(stream);\n    const mediaType = track.kind;\n\n    if (!this.isP2P && !RTC.isUserStreamById(streamId)) {\n        logger.info(`${this} ignored remote 'stream added' event for non-user stream[id=${streamId}]`);\n\n        return;\n    }\n    logger.info(`${this} adding remote track for stream[id=${streamId},type=${mediaType}]`);\n\n    // look up an associated JID for a stream id\n    if (!mediaType) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(\n                `MediaType undefined for remote track, stream id: ${streamId}`\n            ));\n\n        // Abort\n        return;\n    }\n\n    const remoteSDP = this._usesUnifiedPlan\n        ? new SDP(this.peerconnection.remoteDescription.sdp)\n        : new SDP(this.remoteDescription.sdp);\n    let mediaLines;\n\n    // In unified plan mode, find the matching mline using 'mid' if its availble, otherwise use the\n    // 'msid' attribute of the stream.\n    if (this._usesUnifiedPlan) {\n        if (transceiver && transceiver.mid) {\n            const mid = transceiver.mid;\n\n            mediaLines = remoteSDP.media.filter(mls => SDPUtil.findLine(mls, `a=mid:${mid}`));\n        } else {\n            mediaLines = remoteSDP.media.filter(mls => {\n                const msid = SDPUtil.findLine(mls, 'a=msid:');\n\n                return typeof msid !== 'undefined' && streamId === msid.substring(7).split(' ')[0];\n            });\n        }\n    } else {\n        mediaLines = remoteSDP.media.filter(mls => mls.startsWith(`m=${mediaType}`));\n    }\n\n    if (!mediaLines.length) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(`No media lines found in remote SDP for remote stream[id=${streamId},type=${mediaType}]`));\n\n        // Abort\n        return;\n    }\n\n    let ssrcLines = SDPUtil.findLines(mediaLines[0], 'a=ssrc:');\n\n    ssrcLines\n        = ssrcLines.filter(line => line.indexOf(`msid:${streamId}`) !== -1);\n    if (!ssrcLines.length) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(`No SSRC lines found in remote SDP for remote stream[msid=${streamId},type=${mediaType}]`));\n\n        // Abort\n        return;\n    }\n\n    // FIXME the length of ssrcLines[0] not verified, but it will fail\n    // with global error handler anyway\n    const ssrcStr = ssrcLines[0].substring(7).split(' ')[0];\n    const trackSsrc = Number(ssrcStr);\n    const ownerEndpointId = this.signalingLayer.getSSRCOwner(trackSsrc);\n\n    if (isNaN(trackSsrc) || trackSsrc < 0) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(\n                `Invalid SSRC for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`));\n\n        // Abort\n        return;\n    } else if (!ownerEndpointId) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(\n                `No SSRC owner known for remote stream[ssrc=${trackSsrc},id=${streamId},type=${mediaType}]`));\n\n        // Abort\n        return;\n    }\n\n\n    let sourceName;\n\n    if (FeatureFlags.isSourceNameSignalingEnabled()) {\n        sourceName = this.signalingLayer.getTrackSourceName(trackSsrc);\n\n        // If source name was not signaled, we'll generate one which allows testing signaling\n        // when mixing legacy(mobile) with new clients.\n        if (!sourceName) {\n            sourceName = getSourceNameForJitsiTrack(ownerEndpointId, mediaType, 0);\n        }\n    }\n\n    // eslint-disable-next-line no-undef\n    logger.info(`${this} creating remote track[endpoint=${ownerEndpointId},ssrc=${trackSsrc},`\n        + `type=${mediaType},sourceName=${sourceName}]`);\n\n    const peerMediaInfo\n        = this.signalingLayer.getPeerMediaInfo(ownerEndpointId, mediaType);\n\n    if (!peerMediaInfo) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(`${this}: no peer media info available for ${ownerEndpointId}`));\n\n        return;\n    }\n\n    const muted = peerMediaInfo.muted;\n    const videoType = peerMediaInfo.videoType; // can be undefined\n\n    // eslint-disable-next-line no-undef\n    this._createRemoteTrack(\n        ownerEndpointId, stream, track, mediaType, videoType, trackSsrc, muted, sourceName);\n};\n\n// FIXME cleanup params\n/* eslint-disable max-params */\n\n/**\n * Initializes a new JitsiRemoteTrack instance with the data provided by\n * the signaling layer and SDP.\n *\n * @param {string} ownerEndpointId the owner's endpoint ID (MUC nickname)\n * @param {MediaStream} stream the WebRTC stream instance\n * @param {MediaStreamTrack} track the WebRTC track instance\n * @param {MediaType} mediaType the track's type of the media\n * @param {VideoType} [videoType] the track's type of the video (if applicable)\n * @param {number} ssrc the track's main SSRC number\n * @param {boolean} muted the initial muted status\n * @param {String} sourceName the track's source name\n */\nTraceablePeerConnection.prototype._createRemoteTrack = function(\n        ownerEndpointId,\n        stream,\n        track,\n        mediaType,\n        videoType,\n        ssrc,\n        muted,\n        sourceName) {\n    let remoteTracksMap = this.remoteTracks.get(ownerEndpointId);\n\n    if (!remoteTracksMap) {\n        remoteTracksMap = new Map();\n        remoteTracksMap.set(MediaType.AUDIO, new Set());\n        remoteTracksMap.set(MediaType.VIDEO, new Set());\n        this.remoteTracks.set(ownerEndpointId, remoteTracksMap);\n    }\n\n    const userTracksByMediaType = remoteTracksMap.get(mediaType);\n\n    if (userTracksByMediaType?.size\n        && Array.from(userTracksByMediaType).find(jitsiTrack => jitsiTrack.getTrack() === track)) {\n        // Ignore duplicated event which can originate either from 'onStreamAdded' or 'onTrackAdded'.\n        logger.info(`${this} ignored duplicated track event for track[endpoint=${ownerEndpointId},type=${mediaType}]`);\n\n        return;\n    } else if (userTracksByMediaType?.size && !FeatureFlags.isSourceNameSignalingEnabled()) {\n        logger.error(`${this} received a second remote track for track[endpoint=${ownerEndpointId},type=${mediaType}]`\n            + 'deleting the existing track');\n        const existingTrack = Array.from(userTracksByMediaType)[0];\n\n        // The exisiting track needs to be removed here. This happens on Safari sometimes when a SSRC is removed from\n        // the remote description and the browser doesn't fire a 'removetrack' event on the associated MediaStream.\n        this._remoteTrackRemoved(existingTrack.getOriginalStream(), existingTrack.getTrack());\n    }\n\n    const remoteTrack\n        = new JitsiRemoteTrack(\n                this.rtc,\n                this.rtc.conference,\n                ownerEndpointId,\n                stream,\n                track,\n                mediaType,\n                videoType,\n                ssrc,\n                muted,\n                this.isP2P,\n                sourceName);\n\n    userTracksByMediaType.add(remoteTrack);\n    this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_ADDED, remoteTrack, this);\n};\n\n/* eslint-enable max-params */\n\n/**\n * Handles remote stream removal.\n * @param stream the WebRTC MediaStream object which is being removed from the\n * PeerConnection\n */\nTraceablePeerConnection.prototype._remoteStreamRemoved = function(stream) {\n    if (!RTC.isUserStream(stream)) {\n        const id = RTC.getStreamID(stream);\n\n        logger.info(`Ignored remote 'stream removed' event for stream[id=${id}]`);\n\n        return;\n    }\n\n    // Call remoteTrackRemoved for each track in the stream\n    const streamVideoTracks = stream.getVideoTracks();\n\n    for (const videoTrack of streamVideoTracks) {\n        this._remoteTrackRemoved(stream, videoTrack);\n    }\n    const streamAudioTracks = stream.getAudioTracks();\n\n    for (const audioTrack of streamAudioTracks) {\n        this._remoteTrackRemoved(stream, audioTrack);\n    }\n};\n\n/**\n * Handles remote media track removal.\n *\n * @param {MediaStream} stream - WebRTC MediaStream instance which is the parent of the track.\n * @param {MediaStreamTrack} track - WebRTC MediaStreamTrack which has been removed from the PeerConnection.\n * @returns {void}\n */\nTraceablePeerConnection.prototype._remoteTrackRemoved = function(stream, track) {\n    const streamId = RTC.getStreamID(stream);\n    const trackId = track && RTC.getTrackID(track);\n\n    if (!RTC.isUserStreamById(streamId)) {\n        logger.info(`${this} ignored remote 'stream removed' event for non-user stream[id=${streamId}]`);\n\n        return;\n    }\n\n    if (!streamId) {\n        GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - no stream ID`));\n\n        return;\n    }\n\n    if (!trackId) {\n        GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - no track ID`));\n\n        return;\n    }\n\n    const toBeRemoved = this.getRemoteTracks().find(\n        remoteTrack => remoteTrack.getStreamId() === streamId\n        && remoteTrack.getTrackId() === trackId);\n\n    if (!toBeRemoved) {\n        GlobalOnErrorHandler.callErrorHandler(new Error(`${this} remote track removal failed - track not found`));\n\n        return;\n    }\n\n    logger.info(`${this} remote track removed stream[id=${streamId},trackId=${trackId}]`);\n    this._removeRemoteTrack(toBeRemoved);\n};\n\n/**\n * Removes all JitsiRemoteTracks associated with given MUC nickname (resource part of the JID).\n *\n * @param {string} owner - The resource part of the MUC JID.\n * @returns {JitsiRemoteTrack[]} - The array of removed tracks.\n */\nTraceablePeerConnection.prototype.removeRemoteTracks = function(owner) {\n    let removedTracks = [];\n    const remoteTracksByMedia = this.remoteTracks.get(owner);\n\n    if (remoteTracksByMedia) {\n        removedTracks = removedTracks.concat(Array.from(remoteTracksByMedia.get(MediaType.AUDIO)));\n        removedTracks = removedTracks.concat(Array.from(remoteTracksByMedia.get(MediaType.VIDEO)));\n        this.remoteTracks.delete(owner);\n    }\n    logger.debug(`${this} removed remote tracks[endpoint=${owner},count=${removedTracks.length}`);\n\n    return removedTracks;\n};\n\n/**\n * Removes and disposes given <tt>JitsiRemoteTrack</tt> instance. Emits {@link RTCEvents.REMOTE_TRACK_REMOVED}.\n *\n * @param {JitsiRemoteTrack} toBeRemoved - The remote track to be removed.\n * @returns {void}\n */\nTraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {\n    toBeRemoved.dispose();\n    const participantId = toBeRemoved.getParticipantId();\n    const userTracksByMediaType = this.remoteTracks.get(participantId);\n\n    if (!userTracksByMediaType) {\n        logger.error(`${this} removeRemoteTrack: no remote tracks map for endpoint=${participantId}`);\n    } else if (!userTracksByMediaType.get(toBeRemoved.getType())?.delete(toBeRemoved)) {\n        logger.error(`${this} Failed to remove ${toBeRemoved} - type mapping messed up ?`);\n    }\n    this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_REMOVED, toBeRemoved);\n};\n\n/**\n * Returns a map with keys msid/mediaType and <tt>TrackSSRCInfo</tt> values.\n * @param {RTCSessionDescription} desc the local description.\n * @return {Map<string,TrackSSRCInfo>}\n */\nTraceablePeerConnection.prototype._extractSSRCMap = function(desc) {\n    /**\n     * Track SSRC infos mapped by stream ID (msid) or mediaType (unfied-plan)\n     * @type {Map<string,TrackSSRCInfo>}\n     */\n    const ssrcMap = new Map();\n\n    /**\n     * Groups mapped by primary SSRC number\n     * @type {Map<number,Array<SSRCGroupInfo>>}\n     */\n    const groupsMap = new Map();\n\n    if (typeof desc !== 'object' || desc === null\n        || typeof desc.sdp !== 'string') {\n        logger.warn('An empty description was passed as an argument');\n\n        return ssrcMap;\n    }\n\n    const session = transform.parse(desc.sdp);\n\n    if (!Array.isArray(session.media)) {\n        return ssrcMap;\n    }\n\n    let media = session.media;\n\n    // For unified plan clients, only the first audio and video mlines will have ssrcs for the local sources.\n    // The rest of the m-lines are for the recv-only sources, one for each remote source.\n    if (this._usesUnifiedPlan) {\n        media = [];\n        [ MediaType.AUDIO, MediaType.VIDEO ].forEach(mediaType => {\n            const mLine = session.media.find(m => m.type === mediaType);\n\n            mLine && media.push(mLine);\n        });\n    }\n\n    for (const mLine of media) {\n        if (!Array.isArray(mLine.ssrcs)) {\n            continue; // eslint-disable-line no-continue\n        }\n\n        if (Array.isArray(mLine.ssrcGroups)) {\n            for (const group of mLine.ssrcGroups) {\n                if (typeof group.semantics !== 'undefined'\n                    && typeof group.ssrcs !== 'undefined') {\n                    // Parse SSRCs and store as numbers\n                    const groupSSRCs = group.ssrcs.split(' ').map(ssrcStr => parseInt(ssrcStr, 10));\n                    const primarySSRC = groupSSRCs[0];\n\n                    // Note that group.semantics is already present\n                    group.ssrcs = groupSSRCs;\n\n                    // eslint-disable-next-line max-depth\n                    if (!groupsMap.has(primarySSRC)) {\n                        groupsMap.set(primarySSRC, []);\n                    }\n                    groupsMap.get(primarySSRC).push(group);\n                }\n            }\n        }\n\n        let ssrcs = mLine.ssrcs;\n\n        // Filter the ssrcs with 'msid' attribute for plan-b clients and 'cname' for unified-plan clients.\n        ssrcs = this._usesUnifiedPlan\n            ? ssrcs.filter(s => s.attribute === 'cname')\n            : ssrcs.filter(s => s.attribute === 'msid');\n\n        for (const ssrc of ssrcs) {\n            // Use the mediaType as key for the source map for unified plan clients since msids are not part of\n            // the standard and the unified plan SDPs do not have a proper msid attribute for the sources.\n            // Also the ssrcs for sources do not change for Unified plan clients since RTCRtpSender#replaceTrack is\n            // used for switching the tracks so it is safe to use the mediaType as the key for the TrackSSRCInfo map.\n            const key = this._usesUnifiedPlan ? mLine.type : ssrc.value;\n            const ssrcNumber = ssrc.id;\n            let ssrcInfo = ssrcMap.get(key);\n\n            if (!ssrcInfo) {\n                ssrcInfo = {\n                    ssrcs: [],\n                    groups: [],\n                    msid: key\n                };\n                ssrcMap.set(key, ssrcInfo);\n            }\n            ssrcInfo.ssrcs.push(ssrcNumber);\n\n            if (groupsMap.has(ssrcNumber)) {\n                const ssrcGroups = groupsMap.get(ssrcNumber);\n\n                for (const group of ssrcGroups) {\n                    ssrcInfo.groups.push(group);\n                }\n            }\n        }\n    }\n\n    return ssrcMap;\n};\n\n/**\n * Takes a SessionDescription object and returns a \"normalized\" version.\n * Currently it takes care of ordering the a=ssrc lines and denoting receive\n * only SSRCs.\n */\nconst normalizePlanB = function(desc) {\n    if (typeof desc !== 'object' || desc === null\n        || typeof desc.sdp !== 'string') {\n        logger.warn('An empty description was passed as an argument');\n\n        return desc;\n    }\n\n    // eslint-disable-next-line no-shadow\n    const transform = require('sdp-transform');\n    const session = transform.parse(desc.sdp);\n\n    if (typeof session !== 'undefined'\n            && typeof session.media !== 'undefined'\n            && Array.isArray(session.media)) {\n        session.media.forEach(mLine => {\n\n            // Chrome appears to be picky about the order in which a=ssrc lines\n            // are listed in an m-line when rtx is enabled (and thus there are\n            // a=ssrc-group lines with FID semantics). Specifically if we have\n            // \"a=ssrc-group:FID S1 S2\" and the \"a=ssrc:S2\" lines appear before\n            // the \"a=ssrc:S1\" lines, SRD fails.\n            // So, put SSRC which appear as the first SSRC in an FID ssrc-group\n            // first.\n            const firstSsrcs = [];\n            const newSsrcLines = [];\n\n            if (typeof mLine.ssrcGroups !== 'undefined'\n                && Array.isArray(mLine.ssrcGroups)) {\n                mLine.ssrcGroups.forEach(group => {\n                    if (typeof group.semantics !== 'undefined'\n                        && group.semantics === 'FID') {\n                        if (typeof group.ssrcs !== 'undefined') {\n                            firstSsrcs.push(Number(group.ssrcs.split(' ')[0]));\n                        }\n                    }\n                });\n            }\n\n            if (Array.isArray(mLine.ssrcs)) {\n                let i;\n\n                for (i = 0; i < mLine.ssrcs.length; i++) {\n                    if (typeof mLine.ssrcs[i] === 'object'\n                        && typeof mLine.ssrcs[i].id !== 'undefined'\n                        && firstSsrcs.indexOf(mLine.ssrcs[i].id) >= 0) {\n                        newSsrcLines.push(mLine.ssrcs[i]);\n                        delete mLine.ssrcs[i];\n                    }\n                }\n\n                for (i = 0; i < mLine.ssrcs.length; i++) {\n                    if (typeof mLine.ssrcs[i] !== 'undefined') {\n                        newSsrcLines.push(mLine.ssrcs[i]);\n                    }\n                }\n\n                mLine.ssrcs = replaceDefaultUnifiedPlanMsid(newSsrcLines);\n            }\n        });\n    }\n\n    const resStr = transform.write(session);\n\n\n    return new RTCSessionDescription({\n        type: desc.type,\n        sdp: resStr\n    });\n};\n\n/**\n * Unified plan differentiates a remote track not associated with a stream using\n * the msid \"-\", which can incorrectly trigger an onaddstream event in plan-b.\n * For jitsi, these tracks are actually receive-only ssrcs. To prevent\n * onaddstream from firing, remove the ssrcs with msid \"-\" except the cname\n * line. Normally the ssrcs are not used by the client, as the bridge controls\n * media flow, but keep one reference to the ssrc for the p2p case.\n *\n * @param {Array<Object>} ssrcLines - The ssrc lines from a remote description.\n * @private\n * @returns {Array<Object>} ssrcLines with removed lines referencing msid \"-\".\n */\nfunction replaceDefaultUnifiedPlanMsid(ssrcLines = []) {\n    if (!browser.isChrome() || !browser.isVersionGreaterThan(70)) {\n        return ssrcLines;\n    }\n\n    let filteredLines = [ ...ssrcLines ];\n\n    const problematicSsrcIds = ssrcLines.filter(ssrcLine =>\n        ssrcLine.attribute === 'mslabel' && ssrcLine.value === '-')\n        .map(ssrcLine => ssrcLine.id);\n\n    problematicSsrcIds.forEach(ssrcId => {\n        // Find the cname which is to be modified and left in.\n        const cnameLine = filteredLines.find(line =>\n            line.id === ssrcId && line.attribute === 'cname');\n\n        cnameLine.value = `${MediaDirection.RECVONLY}-${ssrcId}`;\n\n        // Remove all of lines for the ssrc.\n        filteredLines\n            = filteredLines.filter(line => line.id !== ssrcId);\n\n        // But re-add the cname line so there is a reference kept to the ssrc\n        // in the SDP.\n        filteredLines.push(cnameLine);\n    });\n\n    return filteredLines;\n}\n\n/**\n * Makes sure that both audio and video directions are configured as 'sendrecv'.\n * @param {Object} localDescription the SDP object as defined by WebRTC.\n * @param {object} options <tt>TracablePeerConnection</tt> config options.\n */\nconst enforceSendRecv = function(localDescription, options) {\n    if (!localDescription) {\n        throw new Error('No local description passed in.');\n    }\n\n    const transformer = new SdpTransformWrap(localDescription.sdp);\n    const audioMedia = transformer.selectMedia(MediaType.AUDIO)?.[0];\n    let changed = false;\n\n    if (audioMedia && audioMedia.direction !== MediaDirection.SENDRECV) {\n        if (options.startSilent) {\n            audioMedia.direction = MediaDirection.INACTIVE;\n        } else {\n            audioMedia.direction = MediaDirection.SENDRECV;\n        }\n\n        changed = true;\n    }\n\n    const videoMedia = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n    if (videoMedia && videoMedia.direction !== MediaDirection.SENDRECV) {\n        videoMedia.direction = MediaDirection.SENDRECV;\n        changed = true;\n    }\n\n    if (changed) {\n        return new RTCSessionDescription({\n            type: localDescription.type,\n            sdp: transformer.toRawSDP()\n        });\n    }\n\n    return localDescription;\n};\n\n/**\n *\n * @param {JitsiLocalTrack} localTrack\n */\nTraceablePeerConnection.prototype.getLocalSSRC = function(localTrack) {\n    const ssrcInfo = this._getSSRC(localTrack.rtcId);\n\n    return ssrcInfo && ssrcInfo.ssrcs[0];\n};\n\n/**\n * When doing unified plan simulcast, we'll have a set of ssrcs with the\n * same msid but no ssrc-group, since unified plan signals the simulcast\n * group via the a=simulcast line.  Unfortunately, Jicofo will complain\n * if it sees ssrcs with matching msids but no ssrc-group, so we'll inject\n * an ssrc-group line to make Jicofo happy.\n * @param desc A session description object (with 'type' and 'sdp' fields)\n * @return A session description object with its sdp field modified to\n * contain an inject ssrc-group for simulcast\n */\nTraceablePeerConnection.prototype._injectSsrcGroupForUnifiedSimulcast\n    = function(desc) {\n        const sdp = transform.parse(desc.sdp);\n        const video = sdp.media.find(mline => mline.type === 'video');\n\n        // Check if the browser supports RTX, add only the primary ssrcs to the SIM group if that is the case.\n        video.ssrcGroups = video.ssrcGroups || [];\n        const fidGroups = video.ssrcGroups.filter(group => group.semantics === 'FID');\n\n        if (video.simulcast || video.simulcast_03) {\n            const ssrcs = [];\n\n            if (fidGroups && fidGroups.length) {\n                fidGroups.forEach(group => {\n                    ssrcs.push(group.ssrcs.split(' ')[0]);\n                });\n            } else {\n                video.ssrcs.forEach(ssrc => {\n                    if (ssrc.attribute === 'msid') {\n                        ssrcs.push(ssrc.id);\n                    }\n                });\n            }\n            if (video.ssrcGroups.find(group => group.semantics === 'SIM')) {\n                // Group already exists, no need to do anything\n                return desc;\n            }\n\n            video.ssrcGroups.push({\n                semantics: 'SIM',\n                ssrcs: ssrcs.join(' ')\n            });\n        }\n\n        return new RTCSessionDescription({\n            type: desc.type,\n            sdp: transform.write(sdp)\n        });\n    };\n\n/* eslint-disable-next-line vars-on-top */\nconst getters = {\n    signalingState() {\n        return this.peerconnection.signalingState;\n    },\n    iceConnectionState() {\n        return this.peerconnection.iceConnectionState;\n    },\n    connectionState() {\n        return this.peerconnection.connectionState;\n    },\n    localDescription() {\n        let desc = this.peerconnection.localDescription;\n\n        if (!desc) {\n            logger.debug(`${this} getLocalDescription no localDescription found`);\n\n            return {};\n        }\n\n        this.trace('getLocalDescription::preTransform', dumpSDP(desc));\n\n        // If the browser is running in unified plan mode and this is a jvb connection,\n        // transform the SDP to Plan B first.\n        if (this._usesUnifiedPlan && !this.isP2P) {\n            desc = this.interop.toPlanB(desc);\n            this.trace('getLocalDescription::postTransform (Plan B)',\n                dumpSDP(desc));\n\n            desc = this._injectSsrcGroupForUnifiedSimulcast(desc);\n            this.trace('getLocalDescription::postTransform (inject ssrc group)',\n                dumpSDP(desc));\n        } else if (!this._usesUnifiedPlan) {\n            if (browser.doesVideoMuteByStreamRemove()) {\n                desc = this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(desc);\n                logger.debug(\n                    'getLocalDescription::postTransform (munge local SDP)', desc);\n            }\n\n            // What comes out of this getter will be signalled over Jingle to\n            // the other peer, so we need to make sure the media direction is\n            // 'sendrecv' because we won't change the direction later and don't want\n            // the other peer to think we can't send or receive.\n            //\n            // Note that the description we set in chrome does have the accurate\n            // direction (e.g. 'recvonly'), since that is technically what is\n            // happening (check setLocalDescription impl).\n            desc = enforceSendRecv(desc, this.options);\n        }\n\n        // See the method's doc for more info about this transformation.\n        desc = this.localSdpMunger.transformStreamIdentifiers(desc);\n\n        return desc;\n    },\n    remoteDescription() {\n        let desc = this.peerconnection.remoteDescription;\n\n        if (!desc) {\n            logger.debug(`${this} getRemoteDescription no remoteDescription found`);\n\n            return {};\n        }\n        this.trace('getRemoteDescription::preTransform', dumpSDP(desc));\n\n        if (this._usesUnifiedPlan) {\n            if (this.isP2P) {\n                // Adjust the media direction for p2p based on whether a local source has been added.\n                desc = this._adjustRemoteMediaDirection(desc);\n            } else {\n                // If this is a jvb connection, transform the SDP to Plan B first.\n                desc = this.interop.toPlanB(desc);\n                this.trace('getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));\n            }\n        }\n\n        return desc;\n    }\n};\n\nObject.keys(getters).forEach(prop => {\n    Object.defineProperty(\n        TraceablePeerConnection.prototype,\n        prop, {\n            get: getters[prop]\n        }\n    );\n});\n\nTraceablePeerConnection.prototype._getSSRC = function(rtcId) {\n    return this.localSSRCs.get(rtcId);\n};\n\n/**\n * Checks if low fps screensharing is in progress.\n *\n * @private\n * @returns {boolean} Returns true if 5 fps screensharing is in progress, false otherwise.\n */\nTraceablePeerConnection.prototype.isSharingLowFpsScreen = function() {\n    return this._isSharingScreen() && this._capScreenshareBitrate;\n};\n\n/**\n * Checks if screensharing is in progress.\n *\n * @returns {boolean}  Returns true if a desktop track has been added to the peerconnection, false otherwise.\n */\nTraceablePeerConnection.prototype._isSharingScreen = function() {\n    const tracks = this.getLocalVideoTracks();\n\n    return Boolean(tracks.find(track => track.videoType === VideoType.DESKTOP));\n};\n\n/**\n * Munges the order of the codecs in the SDP passed based on the preference\n * set through config.js settings. All instances of the specified codec are\n * moved up to the top of the list when it is preferred. The specified codec\n * is deleted from the list if the configuration specifies that the codec be\n * disabled.\n * @param {RTCSessionDescription} description that needs to be munged.\n * @returns {RTCSessionDescription} the munged description.\n */\nTraceablePeerConnection.prototype._mungeCodecOrder = function(description) {\n    if (!this.codecPreference) {\n        return description;\n    }\n\n    const parsedSdp = transform.parse(description.sdp);\n\n    // Only the m-line that defines the source the browser will be sending should need to change.\n    // This is typically the first m-line with the matching media type.\n    const mLine = parsedSdp.media.find(m => m.type === this.codecPreference.mediaType);\n\n    if (!mLine) {\n        return description;\n    }\n\n    if (this.codecPreference.enable) {\n        SDPUtil.preferCodec(mLine, this.codecPreference.mimeType);\n\n        // Strip the high profile H264 codecs on mobile clients for p2p connection.\n        // High profile codecs give better quality at the expense of higher load which\n        // we do not want on mobile clients.\n        // Jicofo offers only the baseline code for the jvb connection.\n        // TODO - add check for mobile browsers once js-utils provides that check.\n        if (this.codecPreference.mimeType === CodecMimeType.H264 && browser.isReactNative() && this.isP2P) {\n            SDPUtil.stripCodec(mLine, this.codecPreference.mimeType, true /* high profile */);\n        }\n\n        // Set the max bitrate here on the SDP so that the configured max. bitrate is effective\n        // as soon as the browser switches to VP9.\n        if (this.codecPreference.mimeType === CodecMimeType.VP9\n            && this.getConfiguredVideoCodec() === CodecMimeType.VP9) {\n            const bitrates = this.tpcUtils.videoBitrates.VP9 || this.tpcUtils.videoBitrates;\n            const hdBitrate = bitrates.high ? bitrates.high : HD_BITRATE;\n            const limit = Math.floor((this._isSharingScreen() ? HD_BITRATE : hdBitrate) / 1000);\n\n            // Use only the HD bitrate for now as there is no API available yet for configuring\n            // the bitrates on the individual SVC layers.\n            mLine.bandwidth = [ {\n                type: 'AS',\n                limit\n            } ];\n        } else {\n            // Clear the bandwidth limit in SDP when VP9 is no longer the preferred codec.\n            // This is needed on react native clients as react-native-webrtc returns the\n            // SDP that the application passed instead of returning the SDP off the native side.\n            // This line automatically gets cleared on web on every renegotiation.\n            mLine.bandwidth = undefined;\n        }\n    } else {\n        SDPUtil.stripCodec(mLine, this.codecPreference.mimeType);\n    }\n\n    return new RTCSessionDescription({\n        type: description.type,\n        sdp: transform.write(parsedSdp)\n    });\n};\n\n/**\n * Add {@link JitsiLocalTrack} to this TPC.\n * @param {JitsiLocalTrack} track\n * @param {boolean} isInitiator indicates if the endpoint is the offerer.\n * @returns {Promise<void>} - resolved when done.\n */\nTraceablePeerConnection.prototype.addTrack = function(track, isInitiator = false) {\n    const rtcId = track.rtcId;\n\n    logger.info(`${this} adding ${track}`);\n\n    if (this.localTracks.has(rtcId)) {\n\n        return Promise.reject(new Error(`${track} is already in ${this}`));\n    }\n\n    this.localTracks.set(rtcId, track);\n    const webrtcStream = track.getOriginalStream();\n\n    if (this._usesUnifiedPlan) {\n        logger.debug(`${this} TPC.addTrack using unified plan`);\n        try {\n            this.tpcUtils.addTrack(track, isInitiator);\n        } catch (error) {\n            logger.error(`${this} Adding track=${track} failed: ${error?.message}`);\n\n            return Promise.reject(error);\n        }\n    } else {\n        // Use addStream API for the plan-b case.\n        if (webrtcStream) {\n            this._addStream(webrtcStream);\n\n        // It's not ok for a track to not have a WebRTC stream if:\n        } else if (!browser.doesVideoMuteByStreamRemove()\n                    || track.isAudioTrack()\n                    || (track.isVideoTrack() && !track.isMuted())) {\n            return Promise.reject(new Error(`${this} no WebRTC stream for track=${track}`));\n        }\n\n        // Muted video tracks do not have WebRTC stream\n        if (browser.doesVideoMuteByStreamRemove() && track.isVideoTrack() && track.isMuted()) {\n            const ssrcInfo = this.generateNewStreamSSRCInfo(track);\n\n            this.sdpConsistency.setPrimarySsrc(ssrcInfo.ssrcs[0]);\n            const simGroup\n                = ssrcInfo.groups.find(groupInfo => groupInfo.semantics === 'SIM');\n\n            if (simGroup) {\n                this.simulcast.setSsrcCache(simGroup.ssrcs);\n            }\n            const fidGroups\n                = ssrcInfo.groups.filter(\n                    groupInfo => groupInfo.semantics === 'FID');\n\n            if (fidGroups) {\n                const rtxSsrcMapping = new Map();\n\n                fidGroups.forEach(fidGroup => {\n                    const primarySsrc = fidGroup.ssrcs[0];\n                    const rtxSsrc = fidGroup.ssrcs[1];\n\n                    rtxSsrcMapping.set(primarySsrc, rtxSsrc);\n                });\n                this.rtxModifier.setSsrcCache(rtxSsrcMapping);\n            }\n        }\n    }\n    let promiseChain = Promise.resolve();\n\n    // On Firefox, the encodings have to be configured on the sender only after the transceiver is created.\n    if (browser.isFirefox()) {\n        promiseChain = promiseChain.then(() => webrtcStream && this.tpcUtils.setEncodings(track));\n    }\n\n    return promiseChain;\n};\n\n/**\n * Adds local track as part of the unmute operation.\n * @param {JitsiLocalTrack} track the track to be added as part of the unmute operation.\n *\n * @return {Promise<boolean>} Promise that resolves to true if the underlying PeerConnection's\n * state has changed and renegotiation is required, false if no renegotiation is needed or\n * Promise is rejected when something goes wrong.\n */\nTraceablePeerConnection.prototype.addTrackUnmute = function(track) {\n    logger.info(`${this} Adding track=${track} as unmute`);\n\n    if (!this._assertTrackBelongs('addTrackUnmute', track)) {\n        // Abort\n\n        return Promise.reject('Track not found on the peerconnection');\n    }\n\n    const webRtcStream = track.getOriginalStream();\n\n    if (!webRtcStream) {\n        logger.error(`${this} Unable to add track=${track} as unmute - no WebRTC stream`);\n\n        return Promise.reject('Stream not found');\n    }\n\n    if (this._usesUnifiedPlan) {\n        return this.tpcUtils.replaceTrack(null, track).then(() => false);\n    }\n\n    this._addStream(webRtcStream);\n\n    return Promise.resolve(true);\n};\n\n/**\n * Adds WebRTC media stream to the underlying PeerConnection\n * @param {MediaStream} mediaStream\n * @private\n */\nTraceablePeerConnection.prototype._addStream = function(mediaStream) {\n    this.peerconnection.addStream(mediaStream);\n    this._addedStreams.push(mediaStream);\n};\n\n/**\n * Removes WebRTC media stream from the underlying PeerConection\n * @param {MediaStream} mediaStream\n */\nTraceablePeerConnection.prototype._removeStream = function(mediaStream) {\n    this.peerconnection.removeStream(mediaStream);\n    this._addedStreams\n        = this._addedStreams.filter(stream => stream !== mediaStream);\n};\n\n/**\n * This method when called will check if given <tt>localTrack</tt> belongs to\n * this TPC (that it has been previously added using {@link addTrack}). If the\n * track does not belong an error message will be logged.\n * @param {string} methodName the method name that will be logged in an error\n * message\n * @param {JitsiLocalTrack} localTrack\n * @return {boolean} <tt>true</tt> if given local track belongs to this TPC or\n * <tt>false</tt> otherwise.\n * @private\n */\nTraceablePeerConnection.prototype._assertTrackBelongs = function(\n        methodName,\n        localTrack) {\n    const doesBelong = this.localTracks.has(localTrack?.rtcId);\n\n    if (!doesBelong) {\n        logger.error(`${this} ${methodName}: track=${localTrack} does not belong to pc`);\n    }\n\n    return doesBelong;\n};\n\n/**\n * Returns the codec that is configured on the client as the preferred video codec.\n * This takes into account the current order of codecs in the local description sdp.\n *\n * @returns {CodecMimeType} The codec that is set as the preferred codec to receive\n * video in the local SDP.\n */\nTraceablePeerConnection.prototype.getConfiguredVideoCodec = function() {\n    const sdp = this.peerconnection.localDescription?.sdp;\n    const defaultCodec = CodecMimeType.VP8;\n\n    if (!sdp) {\n        return defaultCodec;\n    }\n    const parsedSdp = transform.parse(sdp);\n    const mLine = parsedSdp.media.find(m => m.type === MediaType.VIDEO);\n    const codec = mLine.rtp[0].codec;\n\n    if (codec) {\n        return Object.values(CodecMimeType).find(value => value === codec.toLowerCase());\n    }\n\n    return defaultCodec;\n};\n\n/**\n * Enables or disables simulcast for screenshare based on the frame rate requested for desktop track capture.\n *\n * @param {number} maxFps framerate to be used for desktop track capture.\n */\nTraceablePeerConnection.prototype.setDesktopSharingFrameRate = function(maxFps) {\n    const lowFps = maxFps <= SS_DEFAULT_FRAME_RATE;\n\n    this._capScreenshareBitrate = this.isSimulcastOn() && lowFps;\n};\n\n/**\n * Sets the codec preference on the peerconnection. The codec preference goes into effect when\n * the next renegotiation happens.\n *\n * @param {CodecMimeType} preferredCodec the preferred codec.\n * @param {CodecMimeType} disabledCodec the codec that needs to be disabled.\n * @returns {void}\n */\nTraceablePeerConnection.prototype.setVideoCodecs = function(preferredCodec = null, disabledCodec = null) {\n    // If both enable and disable are set, disable settings will prevail.\n    const enable = disabledCodec === null;\n    const mimeType = disabledCodec ? disabledCodec : preferredCodec;\n\n    if (this.codecPreference && (preferredCodec || disabledCodec)) {\n        this.codecPreference.enable = enable;\n        this.codecPreference.mimeType = mimeType;\n    } else if (preferredCodec || disabledCodec) {\n        this.codecPreference = {\n            enable,\n            mediaType: MediaType.VIDEO,\n            mimeType\n        };\n    } else {\n        logger.warn(`${this} Invalid codec settings[preferred=${preferredCodec},disabled=${disabledCodec}],\n            atleast one value is needed`);\n    }\n};\n\n/**\n * Tells if the given WebRTC <tt>MediaStream</tt> has been added to\n * the underlying WebRTC PeerConnection.\n * @param {MediaStream} mediaStream\n * @returns {boolean}\n */\nTraceablePeerConnection.prototype.isMediaStreamInPc = function(mediaStream) {\n    return this._addedStreams.indexOf(mediaStream) > -1;\n};\n\n/**\n * Remove local track from this TPC.\n * @param {JitsiLocalTrack} localTrack the track to be removed from this TPC.\n *\n * FIXME It should probably remove a boolean just like {@link removeTrackMute}\n *       The same applies to addTrack.\n */\nTraceablePeerConnection.prototype.removeTrack = function(localTrack) {\n    const webRtcStream = localTrack.getOriginalStream();\n\n    this.trace(\n        'removeStream',\n        localTrack.rtcId, webRtcStream ? webRtcStream.id : undefined);\n\n    if (!this._assertTrackBelongs('removeStream', localTrack)) {\n        // Abort - nothing to be done here\n        return;\n    }\n    this.localTracks.delete(localTrack.rtcId);\n    this.localSSRCs.delete(localTrack.rtcId);\n\n    if (webRtcStream) {\n        this.peerconnection.removeStream(webRtcStream);\n    }\n};\n\n/**\n * Returns the sender corresponding to the given media type.\n * @param {MEDIA_TYPE} mediaType - The media type 'audio' or 'video' to be used for the search.\n * @returns {RTPSender|undefined} - The found sender or undefined if no sender\n * was found.\n */\nTraceablePeerConnection.prototype.findSenderByKind = function(mediaType) {\n    if (this.peerconnection.getSenders) {\n        return this.peerconnection.getSenders().find(s => s.track && s.track.kind === mediaType);\n    }\n};\n\n/**\n * Returns the receiver corresponding to the given MediaStreamTrack.\n *\n * @param {MediaSreamTrack} track - The media stream track used for the search.\n * @returns {RTCRtpReceiver|undefined} - The found receiver or undefined if no receiver\n * was found.\n */\nTraceablePeerConnection.prototype.findReceiverForTrack = function(track) {\n    return this.peerconnection.getReceivers().find(r => r.track === track);\n};\n\n/**\n * Returns the sender corresponding to the given MediaStreamTrack.\n *\n * @param {MediaSreamTrack} track - The media stream track used for the search.\n * @returns {RTCRtpSender|undefined} - The found sender or undefined if no sender\n * was found.\n */\nTraceablePeerConnection.prototype.findSenderForTrack = function(track) {\n    if (this.peerconnection.getSenders) {\n        return this.peerconnection.getSenders().find(s => s.track === track);\n    }\n};\n\n/**\n * Replaces <tt>oldTrack</tt> with <tt>newTrack</tt> from the peer connection.\n * Either <tt>oldTrack</tt> or <tt>newTrack</tt> can be null; replacing a valid\n * <tt>oldTrack</tt> with a null <tt>newTrack</tt> effectively just removes\n * <tt>oldTrack</tt>\n *\n * @param {JitsiLocalTrack|null} oldTrack - The current track in use to be replaced on the pc.\n * @param {JitsiLocalTrack|null} newTrack - The new track to be used.\n *\n * @returns {Promise<boolean>} - If the promise resolves with true, renegotiation will be needed.\n * Otherwise no renegotiation is needed.\n */\nTraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {\n    if (!(oldTrack || newTrack)) {\n        logger.info(`${this} replaceTrack called with no new track and no old track`);\n\n        return Promise.resolve();\n    }\n\n    // If a track is being added to the peerconnection for the first time, we want the source signaling to be sent to\n    // Jicofo before the mute state is sent over presence. Therefore, trigger a renegotiation in this case. If we\n    // rely on \"negotiationneeded\" fired by the browser to signal new ssrcs, the mute state in presence will be sent\n    // before the source signaling which is undesirable.\n    const negotiationNeeded = Boolean(!oldTrack || !this.localTracks.has(oldTrack?.rtcId));\n\n    if (this._usesUnifiedPlan) {\n        logger.debug(`${this} TPC.replaceTrack using unified plan`);\n        const mediaType = newTrack?.getType() ?? oldTrack?.getType();\n        const stream = newTrack?.getOriginalStream();\n        const promise = newTrack && !stream\n\n            // Ignore cases when the track is replaced while the device is in a muted state.\n            // The track will be replaced again on the peerconnection when the user unmutes.\n            ? Promise.resolve()\n            : this.tpcUtils.replaceTrack(oldTrack, newTrack);\n\n        return promise\n            .then(transceiver => {\n                oldTrack && this.localTracks.delete(oldTrack.rtcId);\n                newTrack && this.localTracks.set(newTrack.rtcId, newTrack);\n                const mediaActive = mediaType === MediaType.AUDIO\n                    ? this.audioTransferActive\n                    : this.videoTransferActive;\n\n                // Set the transceiver direction only if media is not suspended on the connection. This happens when\n                // the client is using the p2p connection. Transceiver direction is updated when media is resumed on\n                // this connection again.\n                if (transceiver && mediaActive) {\n                    transceiver.direction = newTrack ? MediaDirection.SENDRECV : MediaDirection.RECVONLY;\n                }\n\n                // Avoid configuring the encodings on Chromium/Safari until simulcast is configured\n                // for the newly added track using SDP munging which happens during the renegotiation.\n                const configureEncodingsPromise = browser.usesSdpMungingForSimulcast() || !newTrack\n                    ? Promise.resolve()\n                    : this.tpcUtils.setEncodings(newTrack);\n\n                // Force renegotiation only when the source is added for the first time.\n                return configureEncodingsPromise.then(() => negotiationNeeded);\n            });\n    }\n\n    logger.debug(`${this} TPC.replaceTrack using plan B`);\n\n    let promiseChain = Promise.resolve();\n\n    if (oldTrack) {\n        this.removeTrack(oldTrack);\n    }\n    if (newTrack) {\n        promiseChain = this.addTrack(newTrack);\n    }\n\n    return promiseChain.then(() => true);\n};\n\n/**\n * Removes local track as part of the mute operation.\n * @param {JitsiLocalTrack} localTrack the local track to be remove as part of\n * the mute operation.\n * @return {Promise<boolean>} Promise that resolves to true if the underlying PeerConnection's\n * state has changed and renegotiation is required, false if no renegotiation is needed or\n * Promise is rejected when something goes wrong.\n */\nTraceablePeerConnection.prototype.removeTrackMute = function(localTrack) {\n    const webRtcStream = localTrack.getOriginalStream();\n\n    this.trace(\n        'removeStreamMute',\n        localTrack.rtcId, webRtcStream ? webRtcStream.id : null);\n\n    if (!this._assertTrackBelongs('removeStreamMute', localTrack)) {\n        // Abort - nothing to be done here\n        return Promise.reject('Track not found in the peerconnection');\n    }\n\n    if (this._usesUnifiedPlan) {\n        return this.tpcUtils.replaceTrack(localTrack, null).then(() => false);\n    }\n\n    if (webRtcStream) {\n        logger.info(`${this} Removing track=${localTrack} as mute`);\n        this._removeStream(webRtcStream);\n\n        return Promise.resolve(true);\n    }\n\n    logger.error(`${this} removeStreamMute - no WebRTC stream for track=${localTrack}`);\n\n    return Promise.reject('Stream not found');\n};\n\nTraceablePeerConnection.prototype.createDataChannel = function(label, opts) {\n    this.trace('createDataChannel', label, opts);\n\n    return this.peerconnection.createDataChannel(label, opts);\n};\n\n/**\n * Ensures that the simulcast ssrc-group appears after any other ssrc-groups\n * in the SDP so that simulcast is properly activated.\n *\n * @param {Object} localSdp the WebRTC session description instance for\n * the local description.\n * @private\n */\nTraceablePeerConnection.prototype._ensureSimulcastGroupIsLast = function(localSdp) {\n    let sdpStr = localSdp.sdp;\n\n    const videoStartIndex = sdpStr.indexOf('m=video');\n    const simStartIndex = sdpStr.indexOf('a=ssrc-group:SIM', videoStartIndex);\n    let otherStartIndex = sdpStr.lastIndexOf('a=ssrc-group');\n\n    if (simStartIndex === -1\n        || otherStartIndex === -1\n        || otherStartIndex === simStartIndex) {\n        return localSdp;\n    }\n\n    const simEndIndex = sdpStr.indexOf('\\r\\n', simStartIndex);\n    const simStr = sdpStr.substring(simStartIndex, simEndIndex + 2);\n\n    sdpStr = sdpStr.replace(simStr, '');\n    otherStartIndex = sdpStr.lastIndexOf('a=ssrc-group');\n    const otherEndIndex = sdpStr.indexOf('\\r\\n', otherStartIndex);\n    const sdpHead = sdpStr.slice(0, otherEndIndex);\n    const simStrTrimmed = simStr.trim();\n    const sdpTail = sdpStr.slice(otherEndIndex);\n\n    sdpStr = `${sdpHead}\\r\\n${simStrTrimmed}${sdpTail}`;\n\n    return new RTCSessionDescription({\n        type: localSdp.type,\n        sdp: sdpStr\n    });\n};\n\n/**\n * Will adjust audio and video media direction in the given SDP object to\n * reflect the current status of the {@link audioTransferActive} and\n * {@link videoTransferActive} flags.\n * @param {RTCSessionDescription} localDescription the WebRTC session description instance for\n * the local description.\n * @private\n */\nTraceablePeerConnection.prototype._adjustLocalMediaDirection = function(localDescription) {\n    const transformer = new SdpTransformWrap(localDescription.sdp);\n    let modifiedDirection = false;\n    const audioMedia = transformer.selectMedia(MediaType.AUDIO)?.[0];\n\n    if (audioMedia) {\n        const desiredAudioDirection = this.getDesiredMediaDirection(MediaType.AUDIO);\n\n        if (audioMedia.direction !== desiredAudioDirection) {\n            audioMedia.direction = desiredAudioDirection;\n            logger.info(`${this} Adjusted local audio direction to ${desiredAudioDirection}`);\n            modifiedDirection = true;\n        }\n    } else {\n        logger.warn(`${this} No \"audio\" media found in the local description`);\n    }\n\n    const videoMedia = transformer.selectMedia(MediaType.VIDEO)?.[0];\n\n    if (videoMedia) {\n        const desiredVideoDirection = this.getDesiredMediaDirection(MediaType.VIDEO);\n\n        if (videoMedia.direction !== desiredVideoDirection) {\n            videoMedia.direction = desiredVideoDirection;\n            logger.info(`${this} Adjusted local video direction to ${desiredVideoDirection}`);\n            modifiedDirection = true;\n        }\n    } else {\n        logger.warn(`${this} No \"video\" media found in the local description`);\n    }\n\n    if (modifiedDirection) {\n        return new RTCSessionDescription({\n            type: localDescription.type,\n            sdp: transformer.toRawSDP()\n        });\n    }\n\n    return localDescription;\n};\n\n/**\n * Adjusts the media direction on the remote description based on availability of local and remote sources in a p2p\n * media connection.\n *\n * @param {RTCSessionDescription} remoteDescription the WebRTC session description instance for the remote description.\n * @returns the transformed remoteDescription.\n * @private\n */\nTraceablePeerConnection.prototype._adjustRemoteMediaDirection = function(remoteDescription) {\n    const transformer = new SdpTransformWrap(remoteDescription.sdp);\n\n    [ MediaType.AUDIO, MediaType.VIDEO ].forEach(mediaType => {\n        const media = transformer.selectMedia(mediaType)?.[0];\n        const hasLocalSource = this.hasAnyTracksOfType(mediaType);\n        const hasRemoteSource = this.getRemoteTracks(null, mediaType).length > 0;\n\n        media.direction = hasLocalSource && hasRemoteSource\n            ? MediaDirection.SENDRECV\n            : hasLocalSource\n                ? MediaDirection.RECVONLY\n                : hasRemoteSource ? MediaDirection.SENDONLY : MediaDirection.INACTIVE;\n    });\n\n    return new RTCSessionDescription({\n        type: remoteDescription.type,\n        sdp: transformer.toRawSDP()\n    });\n};\n\n/**\n * Munges the stereo flag as well as the opusMaxAverageBitrate in the SDP, based\n * on values set through config.js, if present.\n *\n * @param {RTCSessionDescription} description that needs to be munged.\n * @returns {RTCSessionDescription} the munged description.\n */\nTraceablePeerConnection.prototype._mungeOpus = function(description) {\n    const { audioQuality } = this.options;\n\n    if (!audioQuality?.stereo && !audioQuality?.opusMaxAverageBitrate) {\n        return description;\n    }\n\n    const parsedSdp = transform.parse(description.sdp);\n    const mLines = parsedSdp.media;\n\n    for (const mLine of mLines) {\n        if (mLine.type === 'audio') {\n            const { payload } = mLine.rtp.find(protocol => protocol.codec === CodecMimeType.OPUS);\n\n            if (!payload) {\n                // eslint-disable-next-line no-continue\n                continue;\n            }\n\n            let fmtpOpus = mLine.fmtp.find(protocol => protocol.payload === payload);\n\n            if (!fmtpOpus) {\n                fmtpOpus = {\n                    payload,\n                    config: ''\n                };\n            }\n\n            const fmtpConfig = transform.parseParams(fmtpOpus.config);\n            let sdpChanged = false;\n\n            if (audioQuality?.stereo) {\n                fmtpConfig.stereo = 1;\n                sdpChanged = true;\n            }\n\n            if (audioQuality?.opusMaxAverageBitrate) {\n                fmtpConfig.maxaveragebitrate = audioQuality.opusMaxAverageBitrate;\n                sdpChanged = true;\n            }\n\n            if (!sdpChanged) {\n                // eslint-disable-next-line no-continue\n                continue;\n            }\n\n            let mungedConfig = '';\n\n            for (const key of Object.keys(fmtpConfig)) {\n                mungedConfig += `${key}=${fmtpConfig[key]}; `;\n            }\n\n            fmtpOpus.config = mungedConfig.trim();\n        }\n    }\n\n    return new RTCSessionDescription({\n        type: description.type,\n        sdp: transform.write(parsedSdp)\n    });\n};\n\n/**\n * Sets up the _dtlsTransport object and initializes callbacks for it.\n */\nTraceablePeerConnection.prototype._initializeDtlsTransport = function() {\n    // We are assuming here that we only have one bundled transport here\n    if (!this.peerconnection.getSenders || this._dtlsTransport) {\n        return;\n    }\n\n    const senders = this.peerconnection.getSenders();\n\n    if (senders.length !== 0 && senders[0].transport) {\n        this._dtlsTransport = senders[0].transport;\n\n        this._dtlsTransport.onerror = error => {\n            logger.error(`${this} DtlsTransport error: ${error}`);\n        };\n\n        this._dtlsTransport.onstatechange = () => {\n            this.trace('dtlsTransport.onstatechange', this._dtlsTransport.state);\n        };\n    }\n};\n\n\n/**\n * Configures the stream encodings depending on the video type and the bitrates configured.\n *\n * @param {JitsiLocalTrack} - The local track for which the sender encodings have to configured.\n * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.\n */\nTraceablePeerConnection.prototype.configureSenderVideoEncodings = function(localVideoTrack = null) {\n    if (FeatureFlags.isSourceNameSignalingEnabled()) {\n        if (localVideoTrack) {\n            return this.setSenderVideoConstraints(\n                this._senderMaxHeights.get(localVideoTrack.getSourceName()),\n                localVideoTrack);\n        }\n        const promises = [];\n\n        for (const track of this.getLocalVideoTracks()) {\n            promises.push(this.setSenderVideoConstraints(this._senderMaxHeights.get(track.getSourceName()), track));\n        }\n\n        return Promise.allSettled(promises);\n    }\n\n    let localTrack = localVideoTrack;\n\n    if (!localTrack) {\n        localTrack = this.getLocalVideoTracks()[0];\n    }\n\n    return this.setSenderVideoConstraints(this._senderVideoMaxHeight, localTrack);\n};\n\nTraceablePeerConnection.prototype.setLocalDescription = function(description) {\n    let localDescription = description;\n\n    this.trace('setLocalDescription::preTransform', dumpSDP(localDescription));\n\n    // Munge stereo flag and opusMaxAverageBitrate based on config.js\n    localDescription = this._mungeOpus(localDescription);\n\n    if (!this._usesUnifiedPlan) {\n        localDescription = this._adjustLocalMediaDirection(localDescription);\n        localDescription = this._ensureSimulcastGroupIsLast(localDescription);\n    }\n\n    // Munge the order of the codecs based on the preferences set through config.js if we are using SDP munging.\n    if (!this._usesTransceiverCodecPreferences) {\n        localDescription = this._mungeCodecOrder(localDescription);\n    }\n\n    this.trace('setLocalDescription::postTransform', dumpSDP(localDescription));\n\n    return new Promise((resolve, reject) => {\n        this.peerconnection.setLocalDescription(localDescription)\n            .then(() => {\n                this.trace('setLocalDescriptionOnSuccess');\n                const localUfrag = SDPUtil.getUfrag(localDescription.sdp);\n\n                if (localUfrag !== this.localUfrag) {\n                    this.localUfrag = localUfrag;\n                    this.eventEmitter.emit(RTCEvents.LOCAL_UFRAG_CHANGED, this, localUfrag);\n                }\n\n                this._initializeDtlsTransport();\n\n                resolve();\n            }, err => {\n                this.trace('setLocalDescriptionOnFailure', err);\n                this.eventEmitter.emit(RTCEvents.SET_LOCAL_DESCRIPTION_FAILED, err, this);\n                reject(err);\n            });\n    });\n};\n\n/**\n * Enables/disables audio media transmission on this peer connection. When\n * disabled the SDP audio media direction in the local SDP will be adjusted to\n * 'inactive' which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active <tt>true</tt> to enable audio media transmission or\n * <tt>false</tt> to disable. If the value is not a boolean the call will have\n * no effect.\n * @return {boolean} <tt>true</tt> if the value has changed and sRD/sLD cycle\n * needs to be executed in order for the changes to take effect or\n * <tt>false</tt> if the given value was the same as the previous one.\n * @public\n */\nTraceablePeerConnection.prototype.setAudioTransferActive = function(active) {\n    logger.debug(`${this} audio transfer active: ${active}`);\n    const changed = this.audioTransferActive !== active;\n\n    this.audioTransferActive = active;\n\n    if (this._usesUnifiedPlan) {\n        this.tpcUtils.setAudioTransferActive(active);\n\n        // false means no renegotiation up the chain which is not needed in the Unified mode\n        return false;\n    }\n\n    return changed;\n};\n\nTraceablePeerConnection.prototype.setRemoteDescription = function(description) {\n    let remoteDescription = description;\n\n    this.trace('setRemoteDescription::preTransform', dumpSDP(description));\n\n    // Munge stereo flag and opusMaxAverageBitrate based on config.js\n    remoteDescription = this._mungeOpus(remoteDescription);\n\n    if (this._usesUnifiedPlan) {\n        // Translate the SDP to Unified plan format first for the jvb case, p2p case will only have 2 m-lines.\n        if (!this.isP2P) {\n            const currentDescription = this.peerconnection.remoteDescription;\n\n            remoteDescription = this.interop.toUnifiedPlan(remoteDescription, currentDescription);\n            this.trace('setRemoteDescription::postTransform (Unified)', dumpSDP(remoteDescription));\n        }\n        if (this.isSimulcastOn()) {\n            // Implode the simulcast ssrcs so that the remote sdp has only the first ssrc in the SIM group.\n            remoteDescription = this.simulcast.mungeRemoteDescription(remoteDescription);\n            this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(remoteDescription));\n\n            remoteDescription = this.tpcUtils.insertUnifiedPlanSimulcastReceive(remoteDescription);\n            this.trace('setRemoteDescription::postTransform (sim receive)', dumpSDP(remoteDescription));\n        }\n        remoteDescription = this.tpcUtils.ensureCorrectOrderOfSsrcs(remoteDescription);\n        this.trace('setRemoteDescription::postTransform (correct ssrc order)', dumpSDP(remoteDescription));\n    } else {\n        if (this.isSimulcastOn()) {\n            // Implode the simulcast ssrcs so that the remote sdp has only the first ssrc in the SIM group.\n            remoteDescription = this.simulcast.mungeRemoteDescription(\n                remoteDescription,\n                true /* add x-google-conference flag */);\n            this.trace('setRemoteDescription::postTransform (simulcast)', dumpSDP(remoteDescription));\n        }\n        remoteDescription = normalizePlanB(remoteDescription);\n    }\n\n    // Munge the order of the codecs based on the preferences set through config.js.\n    remoteDescription = this._mungeCodecOrder(remoteDescription);\n    this.trace('setRemoteDescription::postTransform (munge codec order)', dumpSDP(remoteDescription));\n\n    return new Promise((resolve, reject) => {\n        this.peerconnection.setRemoteDescription(remoteDescription)\n            .then(() => {\n                this.trace('setRemoteDescriptionOnSuccess');\n                const remoteUfrag = SDPUtil.getUfrag(remoteDescription.sdp);\n\n                if (remoteUfrag !== this.remoteUfrag) {\n                    this.remoteUfrag = remoteUfrag;\n                    this.eventEmitter.emit(RTCEvents.REMOTE_UFRAG_CHANGED, this, remoteUfrag);\n                }\n\n                this._initializeDtlsTransport();\n\n                resolve();\n            }, err => {\n                this.trace('setRemoteDescriptionOnFailure', err);\n                this.eventEmitter.emit(RTCEvents.SET_REMOTE_DESCRIPTION_FAILED, err, this);\n                reject(err);\n            });\n    });\n};\n\n/**\n * Changes the resolution of the video stream that is sent to the peer based on the resolution requested by the peer\n * and user preference, sets the degradation preference on the sender based on the video type, configures the maximum\n * bitrates on the send stream.\n *\n * @param {number} frameHeight - The max frame height to be imposed on the outgoing video stream.\n * @param {JitsiLocalTrack} - The local track for which the sender constraints have to be applied.\n * @returns {Promise} promise that will be resolved when the operation is successful and rejected otherwise.\n */\nTraceablePeerConnection.prototype.setSenderVideoConstraints = function(frameHeight, localVideoTrack) {\n    if (frameHeight < 0) {\n        throw new Error(`Invalid frameHeight: ${frameHeight}`);\n    }\n\n    // XXX: This is not yet supported on mobile.\n    if (browser.isReactNative()) {\n        return Promise.resolve();\n    }\n\n    if (FeatureFlags.isSourceNameSignalingEnabled()) {\n        this._senderMaxHeights.set(localVideoTrack.getSourceName(), frameHeight);\n    } else {\n        this._senderVideoMaxHeight = frameHeight;\n    }\n\n    if (!localVideoTrack || localVideoTrack.isMuted()) {\n        return Promise.resolve();\n    }\n    const videoSender = this.findSenderForTrack(localVideoTrack.getTrack());\n\n    if (!videoSender) {\n        return Promise.resolve();\n    }\n    const parameters = videoSender.getParameters();\n\n    if (!parameters?.encodings?.length) {\n        return Promise.resolve();\n    }\n\n    // Set the degradation preference.\n    const preference = this.isSharingLowFpsScreen()\n        ? DEGRADATION_PREFERENCE_DESKTOP // Prefer resolution for low fps share.\n        : DEGRADATION_PREFERENCE_CAMERA; // Prefer frame-rate for high fps share and camera.\n\n    parameters.degradationPreference = preference;\n    logger.info(`${this} Setting degradation preference [preference=${preference},track=${localVideoTrack}`);\n\n    // Calculate the encodings active state based on the resolution requested by the bridge.\n    this.encodingsEnabledState = this.tpcUtils.calculateEncodingsActiveState(localVideoTrack, frameHeight);\n    const maxBitrates = this.tpcUtils.calculateEncodingsBitrates(localVideoTrack);\n    const videoType = localVideoTrack.getVideoType();\n\n    if (this.isSimulcastOn()) {\n        for (const encoding in parameters.encodings) {\n            if (parameters.encodings.hasOwnProperty(encoding)) {\n                parameters.encodings[encoding].active = this.encodingsEnabledState[encoding];\n\n                // Firefox doesn't follow the spec and lets application specify the degradation preference on the\n                // encodings.\n                browser.isFirefox() && (parameters.encodings[encoding].degradationPreference = preference);\n\n                // Max bitrates are configured on the encodings only for VP8.\n                if (this.getConfiguredVideoCodec() === CodecMimeType.VP8\n                    && (this.options?.videoQuality?.maxBitratesVideo\n                        || this.isSharingLowFpsScreen()\n                        || this._usesUnifiedPlan)) {\n                    parameters.encodings[encoding].maxBitrate = maxBitrates[encoding];\n                }\n            }\n        }\n        this.tpcUtils.updateEncodingsResolution(parameters);\n\n    // For p2p and cases and where simulcast is explicitly disabled.\n    } else if (frameHeight > 0) {\n        let scaleFactor = HD_SCALE_FACTOR;\n\n        // Do not scale down encodings for desktop tracks for non-simulcast case.\n        if (videoType === VideoType.CAMERA && localVideoTrack.resolution > frameHeight) {\n            scaleFactor = Math.floor(localVideoTrack.resolution / frameHeight);\n        }\n\n        parameters.encodings[0].active = true;\n        parameters.encodings[0].scaleResolutionDownBy = scaleFactor;\n\n        // Firefox doesn't follow the spec and lets application specify the degradation preference on the encodings.\n        browser.isFirefox() && (parameters.encodings[0].degradationPreference = preference);\n\n        // Configure the bitrate.\n        if (this.getConfiguredVideoCodec() === CodecMimeType.VP8 && this.options?.videoQuality?.maxBitratesVideo) {\n            let bitrate = this.getTargetVideoBitrates()?.high;\n\n            if (videoType === VideoType.CAMERA) {\n                bitrate = this.tpcUtils.localStreamEncodingsConfig\n                    .find(layer => layer.scaleResolutionDownBy === scaleFactor)?.maxBitrate ?? bitrate;\n            }\n            parameters.encodings[0].maxBitrate = bitrate;\n        }\n    } else {\n        parameters.encodings[0].active = false;\n    }\n\n    logger.info(`${this} setting max height=${frameHeight},encodings=${JSON.stringify(parameters.encodings)}`);\n\n    return videoSender.setParameters(parameters).then(() => {\n        localVideoTrack.maxEnabledResolution = frameHeight;\n        this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED, localVideoTrack);\n    });\n};\n\n/**\n * Enables/disables video media transmission on this peer connection. When\n * disabled the SDP video media direction in the local SDP will be adjusted to\n * 'inactive' which means that no data will be sent nor accepted, but\n * the connection should be kept alive.\n * @param {boolean} active <tt>true</tt> to enable video media transmission or\n * <tt>false</tt> to disable. If the value is not a boolean the call will have\n * no effect.\n * @return {boolean} <tt>true</tt> if the value has changed and sRD/sLD cycle\n * needs to be executed in order for the changes to take effect or\n * <tt>false</tt> if the given value was the same as the previous one.\n * @public\n */\nTraceablePeerConnection.prototype.setVideoTransferActive = function(active) {\n    logger.debug(`${this} video transfer active: ${active}`);\n    const changed = this.videoTransferActive !== active;\n\n    this.videoTransferActive = active;\n\n    if (this._usesUnifiedPlan) {\n        this.tpcUtils.setVideoTransferActive(active);\n\n        // false means no renegotiation up the chain which is not needed in the Unified mode\n        return false;\n    }\n\n    return changed;\n};\n\n/**\n * Sends DTMF tones if possible.\n *\n * @param {string} tones - The DTMF tones string as defined by {@code RTCDTMFSender.insertDTMF}, 'tones' argument.\n * @param {number} duration - The amount of time in milliseconds that each DTMF should last. It's 200ms by default.\n * @param {number} interToneGap - The length of time in miliseconds to wait between tones. It's 200ms by default.\n *\n * @returns {void}\n */\nTraceablePeerConnection.prototype.sendTones = function(tones, duration = 200, interToneGap = 200) {\n    if (!this._dtmfSender) {\n        if (this.peerconnection.getSenders) {\n            const rtpSender = this.peerconnection.getSenders().find(s => s.dtmf);\n\n            this._dtmfSender = rtpSender && rtpSender.dtmf;\n            this._dtmfSender && logger.info(`${this} initialized DTMFSender using getSenders`);\n        }\n\n        if (!this._dtmfSender) {\n            const localAudioTrack = Array.from(this.localTracks.values()).find(t => t.isAudioTrack());\n\n            if (this.peerconnection.createDTMFSender && localAudioTrack) {\n                this._dtmfSender = this.peerconnection.createDTMFSender(localAudioTrack.getTrack());\n            }\n            this._dtmfSender && logger.info(`${this} initialized DTMFSender using deprecated createDTMFSender`);\n        }\n\n        if (this._dtmfSender) {\n            this._dtmfSender.ontonechange = this._onToneChange.bind(this);\n        }\n    }\n\n    if (this._dtmfSender) {\n        if (this._dtmfSender.toneBuffer) {\n            this._dtmfTonesQueue.push({\n                tones,\n                duration,\n                interToneGap\n            });\n\n            return;\n        }\n\n        this._dtmfSender.insertDTMF(tones, duration, interToneGap);\n    } else {\n        logger.warn(`${this} sendTones - failed to select DTMFSender`);\n    }\n};\n\n/**\n * Callback ivoked by {@code this._dtmfSender} when it has finished playing\n * a single tone.\n *\n * @param {Object} event - The tonechange event which indicates what characters\n * are left to be played for the current tone.\n * @private\n * @returns {void}\n */\nTraceablePeerConnection.prototype._onToneChange = function(event) {\n    // An empty event.tone indicates the current tones have finished playing.\n    // Automatically start playing any queued tones on finish.\n    if (this._dtmfSender && event.tone === '' && this._dtmfTonesQueue.length) {\n        const { tones, duration, interToneGap } = this._dtmfTonesQueue.shift();\n\n        this._dtmfSender.insertDTMF(tones, duration, interToneGap);\n    }\n};\n\n/**\n * Makes the underlying TraceablePeerConnection generate new SSRC for\n * the recvonly video stream.\n */\nTraceablePeerConnection.prototype.generateRecvonlySsrc = function() {\n    const newSSRC = SDPUtil.generateSsrc();\n\n    logger.info(`${this} generated new recvonly SSRC=${newSSRC}`);\n    this.sdpConsistency.setPrimarySsrc(newSSRC);\n};\n\n/**\n * Makes the underlying TraceablePeerConnection forget the current primary video\n * SSRC.\n */\nTraceablePeerConnection.prototype.clearRecvonlySsrc = function() {\n    logger.info(`${this} Clearing primary video SSRC!`);\n    this.sdpConsistency.clearVideoSsrcCache();\n};\n\n/**\n * Closes underlying WebRTC PeerConnection instance and removes all remote\n * tracks by emitting {@link RTCEvents.REMOTE_TRACK_REMOVED} for each one of\n * them.\n */\nTraceablePeerConnection.prototype.close = function() {\n    this.trace('stop');\n\n    // Off SignalingEvents\n    this.signalingLayer.off(SignalingEvents.PEER_MUTED_CHANGED, this._peerMutedChanged);\n    this.signalingLayer.off(SignalingEvents.PEER_VIDEO_TYPE_CHANGED, this._peerVideoTypeChanged);\n    this._usesUnifiedPlan && this.peerconnection.removeEventListener('track', this.onTrack);\n\n    for (const peerTracks of this.remoteTracks.values()) {\n        for (const remoteTracks of peerTracks.values()) {\n            for (const remoteTrack of remoteTracks) {\n                this._removeRemoteTrack(remoteTrack);\n            }\n        }\n    }\n    this.remoteTracks.clear();\n\n    this._addedStreams = [];\n\n    this._dtmfSender = null;\n    this._dtmfTonesQueue = [];\n\n    if (!this.rtc._removePeerConnection(this)) {\n        logger.error(`${this} RTC._removePeerConnection returned false`);\n    }\n    if (this.statsinterval !== null) {\n        window.clearInterval(this.statsinterval);\n        this.statsinterval = null;\n    }\n    logger.info(`${this} Closing peerconnection`);\n    this.peerconnection.close();\n};\n\nTraceablePeerConnection.prototype.createAnswer = function(constraints) {\n    return this._createOfferOrAnswer(false /* answer */, constraints);\n};\n\nTraceablePeerConnection.prototype.createOffer = function(constraints) {\n    return this._createOfferOrAnswer(true /* offer */, constraints);\n};\n\nTraceablePeerConnection.prototype._createOfferOrAnswer = function(\n        isOffer,\n        constraints) {\n    const logName = isOffer ? 'Offer' : 'Answer';\n\n    this.trace(`create${logName}`, JSON.stringify(constraints, null, ' '));\n\n    const handleSuccess = (resultSdp, resolveFn, rejectFn) => {\n        try {\n            this.trace(\n                `create${logName}OnSuccess::preTransform`, dumpSDP(resultSdp));\n\n            if (!this._usesUnifiedPlan) {\n                // If there are no local video tracks, then a \"recvonly\"\n                // SSRC needs to be generated\n                if (!this.hasAnyTracksOfType(MediaType.VIDEO)\n                    && !this.sdpConsistency.hasPrimarySsrcCached()) {\n                    this.generateRecvonlySsrc();\n                }\n\n                // eslint-disable-next-line no-param-reassign\n                resultSdp = new RTCSessionDescription({\n                    type: resultSdp.type,\n                    sdp: this.sdpConsistency.makeVideoPrimarySsrcsConsistent(\n                        resultSdp.sdp)\n                });\n\n                this.trace(\n                    `create${logName}OnSuccess::postTransform `\n                         + '(make primary audio/video ssrcs consistent)',\n                    dumpSDP(resultSdp));\n            }\n\n            const localVideoTrack = this.getLocalVideoTracks()[0];\n\n            // Configure simulcast for camera tracks and for desktop tracks that need simulcast.\n            if (this.isSimulcastOn() && browser.usesSdpMungingForSimulcast()\n                && (localVideoTrack?.getVideoType() === VideoType.CAMERA\n                || this._usesUnifiedPlan\n                || !this.isSharingLowFpsScreen())) {\n                // eslint-disable-next-line no-param-reassign\n                resultSdp = this.simulcast.mungeLocalDescription(resultSdp);\n                this.trace(`create${logName} OnSuccess::postTransform (simulcast)`, dumpSDP(resultSdp));\n            }\n\n            if (!this.options.disableRtx && browser.usesSdpMungingForSimulcast()) {\n                // eslint-disable-next-line no-param-reassign\n                resultSdp = new RTCSessionDescription({\n                    type: resultSdp.type,\n                    sdp: this.rtxModifier.modifyRtxSsrcs(resultSdp.sdp)\n                });\n\n                this.trace(\n                    `create${logName}`\n                         + 'OnSuccess::postTransform (rtx modifier)',\n                    dumpSDP(resultSdp));\n            }\n\n            const ssrcMap = this._extractSSRCMap(resultSdp);\n\n            this._processLocalSSRCsMap(ssrcMap);\n\n            resolveFn(resultSdp);\n        } catch (e) {\n            this.trace(`create${logName}OnError`, e);\n            this.trace(`create${logName}OnError`, dumpSDP(resultSdp));\n            logger.error(`${this} create${logName}OnError`, e, dumpSDP(resultSdp));\n\n            rejectFn(e);\n        }\n    };\n\n    const handleFailure = (err, rejectFn) => {\n        this.trace(`create${logName}OnFailure`, err);\n        const eventType\n            = isOffer\n                ? RTCEvents.CREATE_OFFER_FAILED\n                : RTCEvents.CREATE_ANSWER_FAILED;\n\n        this.eventEmitter.emit(eventType, err, this);\n\n        rejectFn(err);\n    };\n\n    // Set the codec preference before creating an offer or answer so that the generated SDP will have\n    // the correct preference order.\n    if (this._usesTransceiverCodecPreferences) {\n        const transceiver = this.peerconnection.getTransceivers()\n            .find(t => t.receiver && t.receiver?.track?.kind === MediaType.VIDEO);\n\n        if (transceiver) {\n            let capabilities = RTCRtpReceiver.getCapabilities(MediaType.VIDEO)?.codecs;\n            const mimeType = this.codecPreference?.mimeType;\n            const enable = this.codecPreference?.enable;\n\n            if (capabilities && mimeType && enable) {\n                // Move the desired codec (all variations of it as well) to the beginning of the list.\n                /* eslint-disable-next-line arrow-body-style */\n                capabilities.sort(caps => {\n                    return caps.mimeType.toLowerCase() === `${MediaType.VIDEO}/${mimeType}` ? -1 : 1;\n                });\n            } else if (capabilities && mimeType) {\n                capabilities = capabilities\n                    .filter(caps => caps.mimeType.toLowerCase() !== `${MediaType.VIDEO}/${mimeType}`);\n            }\n\n            // Disable ulpfec on Google Chrome and derivatives because\n            // https://bugs.chromium.org/p/chromium/issues/detail?id=1276427\n            if (browser.isChromiumBased()) {\n                capabilities = capabilities\n                    .filter(caps => caps.mimeType.toLowerCase() !== `${MediaType.VIDEO}/${CodecMimeType.ULPFEC}`);\n            }\n\n            try {\n                transceiver.setCodecPreferences(capabilities);\n            } catch (err) {\n                logger.warn(`${this} Setting codec[preference=${mimeType},enable=${enable}] failed`, err);\n            }\n        }\n    }\n\n    return new Promise((resolve, reject) => {\n        let oaPromise;\n\n        if (isOffer) {\n            oaPromise = this.peerconnection.createOffer(constraints);\n        } else {\n            oaPromise = this.peerconnection.createAnswer(constraints);\n        }\n\n        oaPromise\n            .then(\n                sdp => handleSuccess(sdp, resolve, reject),\n                error => handleFailure(error, reject));\n    });\n};\n\n/**\n * Extract primary SSRC from given {@link TrackSSRCInfo} object.\n * @param {TrackSSRCInfo} ssrcObj\n * @return {number|null} the primary SSRC or <tt>null</tt>\n */\nTraceablePeerConnection.prototype._extractPrimarySSRC = function(ssrcObj) {\n    if (ssrcObj && ssrcObj.groups && ssrcObj.groups.length) {\n        return ssrcObj.groups[0].ssrcs[0];\n    } else if (ssrcObj && ssrcObj.ssrcs && ssrcObj.ssrcs.length) {\n        return ssrcObj.ssrcs[0];\n    }\n\n    return null;\n};\n\n/**\n * Goes over the SSRC map extracted from the latest local description and tries\n * to match them with the local tracks (by MSID). Will update the values\n * currently stored in the {@link TraceablePeerConnection.localSSRCs} map.\n * @param {Map<string,TrackSSRCInfo>} ssrcMap\n * @private\n */\nTraceablePeerConnection.prototype._processLocalSSRCsMap = function(ssrcMap) {\n    for (const track of this.localTracks.values()) {\n        const sourceIdentifier = this._usesUnifiedPlan ? track.getType() : track.storedMSID;\n\n        if (ssrcMap.has(sourceIdentifier)) {\n            const newSSRC = ssrcMap.get(sourceIdentifier);\n\n            if (!newSSRC) {\n                logger.error(`${this} No SSRC found for stream=${sourceIdentifier}`);\n\n                return;\n            }\n            const oldSSRC = this.localSSRCs.get(track.rtcId);\n            const newSSRCNum = this._extractPrimarySSRC(newSSRC);\n            const oldSSRCNum = this._extractPrimarySSRC(oldSSRC);\n\n            // eslint-disable-next-line no-negated-condition\n            if (newSSRCNum !== oldSSRCNum) {\n                oldSSRCNum && logger.error(`${this} Overwriting SSRC for track=${track}] with ssrc=${newSSRC}`);\n                this.localSSRCs.set(track.rtcId, newSSRC);\n                this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_SSRC_UPDATED, track, newSSRCNum);\n            }\n        } else if (!track.isVideoTrack() && !track.isMuted()) {\n            // It is normal to find no SSRCs for a muted video track in\n            // the local SDP as the recv-only SSRC is no longer munged in.\n            // So log the warning only if it's not a muted video track.\n            logger.warn(`${this} No SSRCs found in the local SDP for track=${track}, stream=${sourceIdentifier}`);\n        }\n    }\n};\n\nTraceablePeerConnection.prototype.addIceCandidate = function(candidate) {\n    this.trace('addIceCandidate', JSON.stringify({\n        candidate: candidate.candidate,\n        sdpMid: candidate.sdpMid,\n        sdpMLineIndex: candidate.sdpMLineIndex,\n        usernameFragment: candidate.usernameFragment\n    }, null, ' '));\n\n    return this.peerconnection.addIceCandidate(candidate);\n};\n\n/**\n * Returns the number of simulcast streams that are currently enabled on the peerconnection.\n *\n * @returns {number} The number of simulcast streams currently enabled or 1 when simulcast is disabled.\n */\nTraceablePeerConnection.prototype.getActiveSimulcastStreams = function() {\n    let activeStreams = 1;\n\n    if (this.isSimulcastOn() && this.encodingsEnabledState) {\n        activeStreams = this.encodingsEnabledState.filter(stream => Boolean(stream))?.length;\n    } else if (this.isSimulcastOn()) {\n        activeStreams = SIM_LAYER_RIDS.length;\n    }\n\n    return activeStreams;\n};\n\n/**\n * Obtains call-related stats from the peer connection.\n *\n * @returns {Promise<Object>} Promise which resolves with data providing statistics about\n * the peerconnection.\n */\nTraceablePeerConnection.prototype.getStats = function() {\n    return this.peerconnection.getStats();\n};\n\n/**\n * Generates and stores new SSRC info object for given local track.\n * The method should be called only for a video track being added to this TPC\n * in the muted state (given that the current browser uses this strategy).\n * @param {JitsiLocalTrack} track\n * @return {TPCSSRCInfo}\n */\nTraceablePeerConnection.prototype.generateNewStreamSSRCInfo = function(track) {\n    const rtcId = track.rtcId;\n    let ssrcInfo = this._getSSRC(rtcId);\n\n    if (ssrcInfo) {\n        logger.error(`${this} Overwriting local SSRCs for track id=${rtcId}`);\n    }\n\n    // Configure simulcast for camera tracks and desktop tracks that need simulcast.\n    if (this.isSimulcastOn()\n        && (track.getVideoType() === VideoType.CAMERA || !this.isSharingLowFpsScreen())) {\n        ssrcInfo = {\n            ssrcs: [],\n            groups: []\n        };\n        for (let i = 0; i < SIM_LAYER_RIDS.length; i++) {\n            ssrcInfo.ssrcs.push(SDPUtil.generateSsrc());\n        }\n        ssrcInfo.groups.push({\n            ssrcs: ssrcInfo.ssrcs.slice(),\n            semantics: 'SIM'\n        });\n    } else {\n        ssrcInfo = {\n            ssrcs: [ SDPUtil.generateSsrc() ],\n            groups: []\n        };\n    }\n    if (!this.options.disableRtx) {\n        // Specifically use a for loop here because we'll\n        //  be adding to the list we're iterating over, so we\n        //  only want to iterate through the items originally\n        //  on the list\n        const currNumSsrcs = ssrcInfo.ssrcs.length;\n\n        for (let i = 0; i < currNumSsrcs; ++i) {\n            const primarySsrc = ssrcInfo.ssrcs[i];\n            const rtxSsrc = SDPUtil.generateSsrc();\n\n            ssrcInfo.ssrcs.push(rtxSsrc);\n            ssrcInfo.groups.push({\n                ssrcs: [ primarySsrc, rtxSsrc ],\n                semantics: 'FID'\n            });\n        }\n    }\n    ssrcInfo.msid = track.storedMSID;\n    this.localSSRCs.set(rtcId, ssrcInfo);\n\n    return ssrcInfo;\n};\n\n/**\n * Returns if the peer connection uses Unified plan implementation.\n *\n * @returns {boolean} True if the pc uses Unified plan, false otherwise.\n */\nTraceablePeerConnection.prototype.usesUnifiedPlan = function() {\n    return this._usesUnifiedPlan;\n};\n\n/**\n * Creates a text representation of this <tt>TraceablePeerConnection</tt>\n * instance.\n * @return {string}\n */\nTraceablePeerConnection.prototype.toString = function() {\n    return `TPC[id=${this.id},type=${this.isP2P ? 'P2P' : 'JVB'}]`;\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport BridgeVideoType from '../../service/RTC/BridgeVideoType';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport { safeCounterIncrement } from '../util/MathUtil';\n\nimport BridgeChannel from './BridgeChannel';\nimport JitsiLocalTrack from './JitsiLocalTrack';\nimport RTCUtils from './RTCUtils';\nimport TraceablePeerConnection from './TraceablePeerConnection';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * The counter used to generated id numbers assigned to peer connections\n * @type {number}\n */\nlet peerConnectionIdCounter = 0;\n\n/**\n * The counter used to generate id number for the local\n * <code>MediaStreamTrack</code>s.\n * @type {number}\n */\nlet rtcTrackIdCounter = 0;\n\n/**\n * Creates {@code JitsiLocalTrack} instances from the passed in meta information\n * about MedieaTracks.\n *\n * @param {Object[]} mediaStreamMetaData - An array of meta information with\n * MediaTrack instances. Each can look like:\n * {{\n *     stream: MediaStream instance that holds a track with audio or video,\n *     track: MediaTrack within the MediaStream,\n *     videoType: \"camera\" or \"desktop\" or falsy,\n *     sourceId: ID of the desktopsharing source,\n *     sourceType: The desktopsharing source type,\n *     effects: Array of effect types\n * }}\n */\nfunction _createLocalTracks(mediaStreamMetaData = []) {\n    return mediaStreamMetaData.map(metaData => {\n        const {\n            sourceId,\n            sourceType,\n            stream,\n            track,\n            videoType,\n            effects\n        } = metaData;\n\n        const { deviceId, facingMode } = track.getSettings();\n\n        // FIXME Move rtcTrackIdCounter to a static method in JitsiLocalTrack\n        // so RTC does not need to handle ID management. This move would be\n        // safer to do once the old createLocalTracks is removed.\n        rtcTrackIdCounter = safeCounterIncrement(rtcTrackIdCounter);\n\n        return new JitsiLocalTrack({\n            deviceId,\n            facingMode,\n            mediaType: track.kind,\n            rtcId: rtcTrackIdCounter,\n            sourceId,\n            sourceType,\n            stream,\n            track,\n            videoType: videoType || null,\n            effects\n        });\n    });\n}\n\n/**\n *\n */\nexport default class RTC extends Listenable {\n    /**\n     *\n     * @param conference\n     * @param options\n     */\n    constructor(conference, options = {}) {\n        super();\n        this.conference = conference;\n\n        /**\n         * A map of active <tt>TraceablePeerConnection</tt>.\n         * @type {Map.<number, TraceablePeerConnection>}\n         */\n        this.peerConnections = new Map();\n\n        this.localTracks = [];\n\n        this.options = options;\n\n        // BridgeChannel instance.\n        // @private\n        // @type {BridgeChannel}\n        this._channel = null;\n\n        /**\n         * The value specified to the last invocation of setLastN before the\n         * channel completed opening. If non-null, the value will be sent\n         * through a channel (once) as soon as it opens and will then be\n         * discarded.\n         * @private\n         * @type {number}\n         */\n        this._lastN = undefined;\n\n        /**\n         * Defines the last N endpoints list. It can be null or an array once\n         * initialised with a channel last N event.\n         * @type {Array<string>|null}\n         * @private\n         */\n        this._lastNEndpoints = null;\n\n        /**\n         * Defines the forwarded sources list. It can be null or an array once initialised with a channel forwarded\n         * sources event.\n         *\n         * @type {Array<string>|null}\n         * @private\n         */\n        this._forwardedSources = null;\n\n        /**\n         * The number representing the maximum video height the local client\n         * should receive from the bridge.\n         *\n         * @type {number|undefined}\n         * @private\n         */\n        this._maxFrameHeight = undefined;\n\n        /**\n         * The endpoint IDs of currently selected participants.\n         *\n         * @type {Array}\n         * @private\n         */\n        this._selectedEndpoints = null;\n\n        // The last N change listener.\n        this._lastNChangeListener = this._onLastNChanged.bind(this);\n\n        // The forwarded sources change listener.\n        this._forwardedSourcesChangeListener = this._onForwardedSourcesChanged.bind(this);\n\n        this._onDeviceListChanged = this._onDeviceListChanged.bind(this);\n        this._updateAudioOutputForAudioTracks\n            = this._updateAudioOutputForAudioTracks.bind(this);\n\n        /**\n         * The default video type assumed by the bridge.\n         * @deprecated this will go away with multiple streams support\n         * @type {BridgeVideoType}\n         * @private\n         */\n        this._videoType = BridgeVideoType.NONE;\n\n        // Switch audio output device on all remote audio tracks. Local audio\n        // tracks handle this event by themselves.\n        if (RTCUtils.isDeviceChangeAvailable('output')) {\n            RTCUtils.addListener(\n                RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n                this._updateAudioOutputForAudioTracks\n            );\n\n            RTCUtils.addListener(\n                RTCEvents.DEVICE_LIST_CHANGED,\n                this._onDeviceListChanged\n            );\n        }\n    }\n\n    /**\n     * Removes any listeners and stored state from this {@code RTC} instance.\n     *\n     * @returns {void}\n     */\n    destroy() {\n        RTCUtils.removeListener(RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED, this._updateAudioOutputForAudioTracks);\n        RTCUtils.removeListener(RTCEvents.DEVICE_LIST_CHANGED, this._onDeviceListChanged);\n\n        if (this._channelOpenListener) {\n            this.removeListener(\n                RTCEvents.DATA_CHANNEL_OPEN,\n                this._channelOpenListener\n            );\n        }\n    }\n\n    /**\n     * Exposes the private helper for converting a WebRTC MediaStream to a\n     * JitsiLocalTrack.\n     *\n     * @param {Array<Object>} tracksInfo\n     * @returns {Array<JitsiLocalTrack>}\n     */\n    static createLocalTracks(tracksInfo) {\n        return _createLocalTracks(tracksInfo);\n    }\n\n    /**\n     * Creates the local MediaStreams.\n     * @param {object} [options] Optional parameters.\n     * @param {array} options.devices The devices that will be requested.\n     * @param {string} options.resolution Resolution constraints.\n     * @param {string} options.cameraDeviceId\n     * @param {string} options.micDeviceId\n     * @returns {*} Promise object that will receive the new JitsiTracks\n     */\n    static obtainAudioAndVideoPermissions(options) {\n        return RTCUtils.obtainAudioAndVideoPermissions(options)\n            .then(tracksInfo => _createLocalTracks(tracksInfo));\n    }\n\n    /**\n     * Initializes the bridge channel of this instance.\n     * At least one of both, peerconnection or wsUrl parameters, must be\n     * given.\n     * @param {RTCPeerConnection} [peerconnection] WebRTC peer connection\n     * instance.\n     * @param {string} [wsUrl] WebSocket URL.\n     */\n    initializeBridgeChannel(peerconnection, wsUrl) {\n        this._channel = new BridgeChannel(peerconnection, wsUrl, this.eventEmitter);\n\n        this._channelOpenListener = () => {\n            const logError = (error, msgType, value) => {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error(`Cannot send ${msgType}(${JSON.stringify(value)}) endpoint message`, error);\n            };\n\n            // When the channel becomes available, tell the bridge about video selections so that it can do adaptive\n            // simulcast, we want the notification to trigger even if userJid is undefined, or null.\n            if (this._receiverVideoConstraints) {\n                try {\n                    this._channel.sendNewReceiverVideoConstraintsMessage(this._receiverVideoConstraints);\n                } catch (error) {\n                    logError(error, 'ReceiverVideoConstraints', this._receiverVideoConstraints);\n                }\n            }\n            if (this._selectedEndpoints) {\n                try {\n                    this._channel.sendSelectedEndpointsMessage(this._selectedEndpoints);\n                } catch (error) {\n                    logError(error, 'SelectedEndpointsChangedEvent', this._selectedEndpoints);\n                }\n            }\n            if (typeof this._maxFrameHeight !== 'undefined') {\n                try {\n                    this._channel.sendReceiverVideoConstraintMessage(this._maxFrameHeight);\n                } catch (error) {\n                    logError(error, 'ReceiverVideoConstraint', this._maxFrameHeight);\n                }\n            }\n            if (typeof this._lastN !== 'undefined' && this._lastN !== -1) {\n                try {\n                    this._channel.sendSetLastNMessage(this._lastN);\n                } catch (error) {\n                    logError(error, 'LastNChangedEvent', this._lastN);\n                }\n            }\n            if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n                try {\n                    this._channel.sendVideoTypeMessage(this._videoType);\n                } catch (error) {\n                    logError(error, 'VideoTypeMessage', this._videoType);\n                }\n            }\n\n            this.removeListener(RTCEvents.DATA_CHANNEL_OPEN, this._channelOpenListener);\n            this._channelOpenListener = null;\n        };\n        this.addListener(RTCEvents.DATA_CHANNEL_OPEN, this._channelOpenListener);\n\n        // Add Last N change listener.\n        this.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED, this._lastNChangeListener);\n\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            // Add forwarded sources change listener.\n            this.addListener(RTCEvents.FORWARDED_SOURCES_CHANGED, this._forwardedSourcesChangeListener);\n        }\n    }\n\n    /**\n     * Callback invoked when the list of known audio and video devices has\n     * been updated. Attempts to update the known available audio output\n     * devices.\n     *\n     * @private\n     * @returns {void}\n     */\n    _onDeviceListChanged() {\n        this._updateAudioOutputForAudioTracks(RTCUtils.getAudioOutputDevice());\n    }\n\n    /**\n     * Receives events when Last N had changed.\n     * @param {array} lastNEndpoints The new Last N endpoints.\n     * @private\n     */\n    _onLastNChanged(lastNEndpoints = []) {\n        const oldLastNEndpoints = this._lastNEndpoints || [];\n        let leavingLastNEndpoints = [];\n        let enteringLastNEndpoints = [];\n\n        this._lastNEndpoints = lastNEndpoints;\n\n        leavingLastNEndpoints = oldLastNEndpoints.filter(\n            id => !this.isInLastN(id));\n\n        enteringLastNEndpoints = lastNEndpoints.filter(\n            id => oldLastNEndpoints.indexOf(id) === -1);\n\n        this.conference.eventEmitter.emit(\n            JitsiConferenceEvents.LAST_N_ENDPOINTS_CHANGED,\n            leavingLastNEndpoints,\n            enteringLastNEndpoints);\n    }\n\n    /**\n     * Receives events when forwarded sources had changed.\n     *\n     * @param {array} forwardedSources The new forwarded sources.\n     * @private\n     */\n    _onForwardedSourcesChanged(forwardedSources = []) {\n        const oldForwardedSources = this._forwardedSources || [];\n        let leavingForwardedSources = [];\n        let enteringForwardedSources = [];\n\n        this._forwardedSources = forwardedSources;\n\n        leavingForwardedSources = oldForwardedSources.filter(sourceName => !this.isInForwardedSources(sourceName));\n\n        enteringForwardedSources = forwardedSources.filter(\n            sourceName => oldForwardedSources.indexOf(sourceName) === -1);\n\n        this.conference.eventEmitter.emit(\n            JitsiConferenceEvents.FORWARDED_SOURCES_CHANGED,\n            leavingForwardedSources,\n            enteringForwardedSources,\n            Date.now());\n    }\n\n    /**\n     * Should be called when current media session ends and after the\n     * PeerConnection has been closed using PeerConnection.close() method.\n     */\n    onCallEnded() {\n        if (this._channel) {\n            // The BridgeChannel is not explicitly closed as the PeerConnection\n            // is closed on call ended which triggers datachannel onclose\n            // events. If using a WebSocket, the channel must be closed since\n            // it is not managed by the PeerConnection.\n            // The reference is cleared to disable any logic related to the\n            // channel.\n            if (this._channel && this._channel.mode === 'websocket') {\n                this._channel.close();\n            }\n\n            this._channel = null;\n        }\n    }\n\n    /**\n     * Sets the capture frame rate to be used for desktop tracks.\n     *\n     * @param {number} maxFps framerate to be used for desktop track capture.\n     */\n    setDesktopSharingFrameRate(maxFps) {\n        RTCUtils.setDesktopSharingFrameRate(maxFps);\n    }\n\n    /**\n     * Sets the receiver video constraints that determine how bitrate is allocated to each of the video streams\n     * requested from the bridge. The constraints are cached and sent through the bridge channel once the channel\n     * is established.\n     * @param {*} constraints\n     */\n    setNewReceiverVideoConstraints(constraints) {\n        this._receiverVideoConstraints = constraints;\n\n        if (this._channel && this._channel.isOpen()) {\n            this._channel.sendNewReceiverVideoConstraintsMessage(constraints);\n        }\n    }\n\n    /**\n     * Sets the maximum video size the local participant should receive from\n     * remote participants. Will cache the value and send it through the channel\n     * once it is created.\n     *\n     * @param {number} maxFrameHeightPixels the maximum frame height, in pixels,\n     * this receiver is willing to receive.\n     * @returns {void}\n     */\n    setReceiverVideoConstraint(maxFrameHeight) {\n        this._maxFrameHeight = maxFrameHeight;\n\n        if (this._channel && this._channel.isOpen()) {\n            this._channel.sendReceiverVideoConstraintMessage(maxFrameHeight);\n        }\n    }\n\n    /**\n     * Sets the video type and availability for the local video source.\n     *\n     * @param {string} videoType 'camera' for camera, 'desktop' for screenshare and\n     * 'none' for when local video source is muted or removed from the peerconnection.\n     * @returns {void}\n     */\n    setVideoType(videoType) {\n        if (this._videoType !== videoType) {\n            this._videoType = videoType;\n\n            if (this._channel && this._channel.isOpen()) {\n                this._channel.sendVideoTypeMessage(videoType);\n            }\n        }\n    }\n\n    /**\n     * Sends the track's  video type to the JVB.\n     * @param {SourceName} sourceName - the track's source name.\n     * @param {BridgeVideoType} videoType - the track's video type.\n     */\n    sendSourceVideoType(sourceName, videoType) {\n        if (this._channel && this._channel.isOpen()) {\n            this._channel.sendSourceVideoTypeMessage(sourceName, videoType);\n        }\n    }\n\n    /**\n     * Elects the participants with the given ids to be the selected\n     * participants in order to always receive video for this participant (even\n     * when last n is enabled). If there is no channel we store it and send it\n     * through the channel once it is created.\n     *\n     * @param {Array<string>} ids - The user ids.\n     * @throws NetworkError or InvalidStateError or Error if the operation\n     * fails.\n     * @returns {void}\n     */\n    selectEndpoints(ids) {\n        this._selectedEndpoints = ids;\n\n        if (this._channel && this._channel.isOpen()) {\n            this._channel.sendSelectedEndpointsMessage(ids);\n        }\n    }\n\n    /**\n     *\n     * @param eventType\n     * @param listener\n     */\n    static addListener(eventType, listener) {\n        RTCUtils.addListener(eventType, listener);\n    }\n\n    /**\n     *\n     * @param eventType\n     * @param listener\n     */\n    static removeListener(eventType, listener) {\n        RTCUtils.removeListener(eventType, listener);\n    }\n\n    /**\n     *\n     * @param options\n     */\n    static init(options = {}) {\n        this.options = options;\n\n        return RTCUtils.init(this.options);\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n     * Creates new <tt>TraceablePeerConnection</tt>\n     * @param {SignalingLayer} signaling The signaling layer that will provide information about the media or\n     * participants which is not carried over SDP.\n     * @param {object} pcConfig The {@code RTCConfiguration} to use for the WebRTC peer connection.\n     * @param {boolean} isP2P Indicates whether or not the new TPC will be used in a peer to peer type of session.\n     * @param {object} options The config options.\n     * @param {boolean} options.enableInsertableStreams - Set to true when the insertable streams constraints is to be\n     * enabled on the PeerConnection.\n     * @param {boolean} options.disableSimulcast If set to 'true' will disable the simulcast.\n     * @param {boolean} options.disableRtx If set to 'true' will disable the RTX.\n     * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\n     * @return {TraceablePeerConnection}\n     */\n    createPeerConnection(signaling, pcConfig, isP2P, options) {\n        const pcConstraints = JSON.parse(JSON.stringify(RTCUtils.pcConstraints));\n\n        if (typeof options.abtestSuspendVideo !== 'undefined') {\n            RTCUtils.setSuspendVideo(pcConstraints, options.abtestSuspendVideo);\n\n            Statistics.analytics.addPermanentProperties(\n                { abtestSuspendVideo: options.abtestSuspendVideo });\n        }\n\n        if (options.enableInsertableStreams) {\n            logger.debug('E2EE - setting insertable streams constraints');\n            pcConfig.encodedInsertableStreams = true;\n        }\n\n        const supportsSdpSemantics = browser.isReactNative()\n            || (browser.isChromiumBased() && !options.usesUnifiedPlan);\n\n        if (supportsSdpSemantics) {\n            logger.debug('WebRTC application is running in plan-b mode');\n            pcConfig.sdpSemantics = 'plan-b';\n        }\n\n        if (options.forceTurnRelay) {\n            pcConfig.iceTransportPolicy = 'relay';\n        }\n\n        // Set the RTCBundlePolicy to max-bundle so that only one set of ice candidates is generated.\n        // The default policy generates separate ice candidates for audio and video connections.\n        // This change is necessary for Unified plan to work properly on Chrome and Safari.\n        pcConfig.bundlePolicy = 'max-bundle';\n\n        peerConnectionIdCounter = safeCounterIncrement(peerConnectionIdCounter);\n\n        const newConnection\n            = new TraceablePeerConnection(\n                this,\n                peerConnectionIdCounter,\n                signaling,\n                pcConfig, pcConstraints,\n                isP2P, options);\n\n        this.peerConnections.set(newConnection.id, newConnection);\n\n        return newConnection;\n    }\n\n    /* eslint-enable max-params */\n\n    /**\n     * Removed given peer connection from this RTC module instance.\n     * @param {TraceablePeerConnection} traceablePeerConnection\n     * @return {boolean} <tt>true</tt> if the given peer connection was removed\n     * successfully or <tt>false</tt> if there was no peer connection mapped in\n     * this RTC instance.\n     */\n    _removePeerConnection(traceablePeerConnection) {\n        const id = traceablePeerConnection.id;\n\n        if (this.peerConnections.has(id)) {\n            // NOTE Remote tracks are not removed here.\n            this.peerConnections.delete(id);\n\n            return true;\n        }\n\n        return false;\n\n    }\n\n    /**\n     *\n     * @param track\n     */\n    addLocalTrack(track) {\n        if (!track) {\n            throw new Error('track must not be null nor undefined');\n        }\n\n        this.localTracks.push(track);\n\n        track.conference = this.conference;\n    }\n\n    /**\n     * Get forwarded sources list.\n     * @returns {Array<string>|null}\n     */\n    getForwardedSources() {\n        return this._forwardedSources;\n    }\n\n    /**\n     * Get local video track.\n     * @returns {JitsiLocalTrack|undefined}\n     */\n    getLocalVideoTrack() {\n        const localVideo = this.getLocalTracks(MediaType.VIDEO);\n\n\n        return localVideo.length ? localVideo[0] : undefined;\n    }\n\n    /**\n     * Returns all the local video tracks.\n     * @returns {Array<JitsiLocalTrack>}\n     */\n    getLocalVideoTracks() {\n        return this.getLocalTracks(MediaType.VIDEO);\n    }\n\n    /**\n     * Get local audio track.\n     * @returns {JitsiLocalTrack|undefined}\n     */\n    getLocalAudioTrack() {\n        const localAudio = this.getLocalTracks(MediaType.AUDIO);\n\n\n        return localAudio.length ? localAudio[0] : undefined;\n    }\n\n    /**\n     * Returns the endpoint id for the local user.\n     * @returns {string}\n     */\n    getLocalEndpointId() {\n        return this.conference.myUserId();\n    }\n\n    /**\n     * Returns the local tracks of the given media type, or all local tracks if\n     * no specific type is given.\n     * @param {MediaType} [mediaType] Optional media type filter.\n     * (audio or video).\n     */\n    getLocalTracks(mediaType) {\n        let tracks = this.localTracks.slice();\n\n        if (mediaType !== undefined) {\n            tracks = tracks.filter(\n                track => track.getType() === mediaType);\n        }\n\n        return tracks;\n    }\n\n    /**\n     * Obtains all remote tracks currently known to this RTC module instance.\n     * @param {MediaType} [mediaType] The remote tracks will be filtered\n     *      by their media type if this argument is specified.\n     * @return {Array<JitsiRemoteTrack>}\n     */\n    getRemoteTracks(mediaType) {\n        let remoteTracks = [];\n\n        for (const tpc of this.peerConnections.values()) {\n            const pcRemoteTracks = tpc.getRemoteTracks(undefined, mediaType);\n\n            if (pcRemoteTracks) {\n                remoteTracks = remoteTracks.concat(pcRemoteTracks);\n            }\n        }\n\n        return remoteTracks;\n    }\n\n    /**\n     * Set mute for all local audio streams attached to the conference.\n     * @param value The mute value.\n     * @returns {Promise}\n     */\n    setAudioMute(value) {\n        const mutePromises = [];\n\n        this.getLocalTracks(MediaType.AUDIO).forEach(audioTrack => {\n            // this is a Promise\n            mutePromises.push(value ? audioTrack.mute() : audioTrack.unmute());\n        });\n\n        // We return a Promise from all Promises so we can wait for their\n        // execution.\n        return Promise.all(mutePromises);\n    }\n\n    /**\n    * Set mute for all local video streams attached to the conference.\n    * @param value The mute value.\n    * @returns {Promise}\n    */\n    setVideoMute(value) {\n        const mutePromises = [];\n\n        this.getLocalTracks(MediaType.VIDEO).concat(this.getLocalTracks(MediaType.PRESENTER))\n            .forEach(videoTrack => {\n                // this is a Promise\n                mutePromises.push(value ? videoTrack.mute() : videoTrack.unmute());\n            });\n\n        // We return a Promise from all Promises so we can wait for their\n        // execution.\n        return Promise.all(mutePromises);\n    }\n\n    /**\n     *\n     * @param track\n     */\n    removeLocalTrack(track) {\n        const pos = this.localTracks.indexOf(track);\n\n        if (pos === -1) {\n            return;\n        }\n\n        this.localTracks.splice(pos, 1);\n    }\n\n    /**\n     *\n     * @param elSelector\n     * @param stream\n     */\n    static attachMediaStream(elSelector, stream) {\n        return RTCUtils.attachMediaStream(elSelector, stream);\n    }\n\n    /**\n     * Returns the id of the given stream.\n     * @param {MediaStream} stream\n     */\n    static getStreamID(stream) {\n        return RTCUtils.getStreamID(stream);\n    }\n\n    /**\n     * Returns the id of the given track.\n     * @param {MediaStreamTrack} track\n     */\n    static getTrackID(track) {\n        return RTCUtils.getTrackID(track);\n    }\n\n    /**\n     * Returns true if retrieving the list of input devices is supported\n     * and false if not.\n     */\n    static isDeviceListAvailable() {\n        return RTCUtils.isDeviceListAvailable();\n    }\n\n    /**\n     * Returns true if changing the input (camera / microphone) or output\n     * (audio) device is supported and false if not.\n     * @param {string} [deviceType] Type of device to change. Default is\n     *      undefined or 'input', 'output' - for audio output device change.\n     * @returns {boolean} true if available, false otherwise.\n     */\n    static isDeviceChangeAvailable(deviceType) {\n        return RTCUtils.isDeviceChangeAvailable(deviceType);\n    }\n\n    /**\n     * Returns whether the current execution environment supports WebRTC (for\n     * use within this library).\n     *\n     * @returns {boolean} {@code true} if WebRTC is supported in the current\n     * execution environment (for use within this library); {@code false},\n     * otherwise.\n     */\n    static isWebRtcSupported() {\n        return browser.isSupported();\n    }\n\n    /**\n     * Returns currently used audio output device id, '' stands for default\n     * device\n     * @returns {string}\n     */\n    static getAudioOutputDevice() {\n        return RTCUtils.getAudioOutputDevice();\n    }\n\n    /**\n     * Returns list of available media devices if its obtained, otherwise an\n     * empty array is returned/\n     * @returns {array} list of available media devices.\n     */\n    static getCurrentlyAvailableMediaDevices() {\n        return RTCUtils.getCurrentlyAvailableMediaDevices();\n    }\n\n    /**\n     * Returns whether available devices have permissions granted\n     * @returns {Boolean}\n     */\n    static arePermissionsGrantedForAvailableDevices() {\n        return RTCUtils.arePermissionsGrantedForAvailableDevices();\n    }\n\n    /**\n     * Returns event data for device to be reported to stats.\n     * @returns {MediaDeviceInfo} device.\n     */\n    static getEventDataForActiveDevice(device) {\n        return RTCUtils.getEventDataForActiveDevice(device);\n    }\n\n    /**\n     * Sets current audio output device.\n     * @param {string} deviceId Id of 'audiooutput' device from\n     *      navigator.mediaDevices.enumerateDevices().\n     * @returns {Promise} resolves when audio output is changed, is rejected\n     *      otherwise\n     */\n    static setAudioOutputDevice(deviceId) {\n        return RTCUtils.setAudioOutputDevice(deviceId);\n    }\n\n    /**\n     * Returns <tt>true<tt/> if given WebRTC MediaStream is considered a valid\n     * \"user\" stream which means that it's not a \"receive only\" stream nor a\n     * \"mixed\" JVB stream.\n     *\n     * Clients that implement Unified Plan, such as Firefox use recvonly\n     * \"streams/channels/tracks\" for receiving remote stream/tracks, as opposed\n     * to Plan B where there are only 3 channels: audio, video and data.\n     *\n     * @param {MediaStream} stream The WebRTC MediaStream instance.\n     * @returns {boolean}\n     */\n    static isUserStream(stream) {\n        return RTC.isUserStreamById(RTCUtils.getStreamID(stream));\n    }\n\n    /**\n     * Returns <tt>true<tt/> if a WebRTC MediaStream identified by given stream\n     * ID is considered a valid \"user\" stream which means that it's not a\n     * \"receive only\" stream nor a \"mixed\" JVB stream.\n     *\n     * Clients that implement Unified Plan, such as Firefox use recvonly\n     * \"streams/channels/tracks\" for receiving remote stream/tracks, as opposed\n     * to Plan B where there are only 3 channels: audio, video and data.\n     *\n     * @param {string} streamId The id of WebRTC MediaStream.\n     * @returns {boolean}\n     */\n    static isUserStreamById(streamId) {\n        return streamId && streamId !== 'mixedmslabel'\n            && streamId !== 'default';\n    }\n\n    /**\n     * Allows to receive list of available cameras/microphones.\n     * @param {function} callback Would receive array of devices as an\n     *      argument.\n     */\n    static enumerateDevices(callback) {\n        RTCUtils.enumerateDevices(callback);\n    }\n\n    /**\n     * A method to handle stopping of the stream.\n     * One point to handle the differences in various implementations.\n     * @param {MediaStream} mediaStream MediaStream object to stop.\n     */\n    static stopMediaStream(mediaStream) {\n        RTCUtils.stopMediaStream(mediaStream);\n    }\n\n    /**\n     * Returns whether the desktop sharing is enabled or not.\n     * @returns {boolean}\n     */\n    static isDesktopSharingEnabled() {\n        return RTCUtils.isDesktopSharingEnabled();\n    }\n\n    /**\n     * Closes the currently opened bridge channel.\n     */\n    closeBridgeChannel() {\n        if (this._channel) {\n            this._channel.close();\n            this._channel = null;\n\n            this.removeListener(RTCEvents.LASTN_ENDPOINT_CHANGED, this._lastNChangeListener);\n        }\n    }\n\n    /* eslint-disable max-params */\n    /**\n     *\n     * @param {TraceablePeerConnection} tpc\n     * @param {number} ssrc\n     * @param {number} audioLevel\n     * @param {boolean} isLocal\n     */\n    setAudioLevel(tpc, ssrc, audioLevel, isLocal) {\n        const track = tpc.getTrackBySSRC(ssrc);\n\n        if (!track) {\n            return;\n        } else if (!track.isAudioTrack()) {\n            logger.warn(`Received audio level for non-audio track: ${ssrc}`);\n\n            return;\n        } else if (track.isLocal() !== isLocal) {\n            logger.error(\n                `${track} was expected to ${isLocal ? 'be' : 'not be'} local`);\n        }\n\n        track.setAudioLevel(audioLevel, tpc);\n    }\n\n    /**\n     * Sends message via the bridge channel.\n     * @param {string} to The id of the endpoint that should receive the\n     *      message. If \"\" the message will be sent to all participants.\n     * @param {object} payload The payload of the message.\n     * @throws NetworkError or InvalidStateError or Error if the operation\n     * fails or there is no data channel created.\n     */\n    sendChannelMessage(to, payload) {\n        if (this._channel) {\n            this._channel.sendMessage(to, payload);\n        } else {\n            throw new Error('Channel support is disabled!');\n        }\n    }\n\n    /**\n     * Sends the local stats via the bridge channel.\n     * @param {Object} payload The payload of the message.\n     * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n     */\n    sendEndpointStatsMessage(payload) {\n        if (this._channel && this._channel.isOpen()) {\n            this._channel.sendEndpointStatsMessage(payload);\n        }\n    }\n\n    /**\n     * Selects a new value for \"lastN\". The requested amount of videos are going\n     * to be delivered after the value is in effect. Set to -1 for unlimited or\n     * all available videos.\n     * @param {number} value the new value for lastN.\n     */\n    setLastN(value) {\n        if (this._lastN !== value) {\n            this._lastN = value;\n            if (this._channel && this._channel.isOpen()) {\n                this._channel.sendSetLastNMessage(value);\n            }\n            this.eventEmitter.emit(RTCEvents.LASTN_VALUE_CHANGED, value);\n        }\n    }\n\n    /**\n     * Indicates if the endpoint id is currently included in the last N.\n     * @param {string} id The endpoint id that we check for last N.\n     * @returns {boolean} true if the endpoint id is in the last N or if we\n     * don't have bridge channel support, otherwise we return false.\n     */\n    isInLastN(id) {\n        return !this._lastNEndpoints // lastNEndpoints not initialised yet.\n            || this._lastNEndpoints.indexOf(id) > -1;\n    }\n\n    /**\n     * Indicates if the source name is currently included in the forwarded sources.\n     *\n     * @param {string} sourceName The source name that we check for forwarded sources.\n     * @returns {boolean} true if the source name is in the forwarded sources or if we don't have bridge channel\n     * support, otherwise we return false.\n     */\n    isInForwardedSources(sourceName) {\n        return !this._forwardedSources // forwardedSources not initialised yet.\n            || this._forwardedSources.indexOf(sourceName) > -1;\n    }\n\n    /**\n     * Updates the target audio output device for all remote audio tracks.\n     *\n     * @param {string} deviceId - The device id of the audio ouput device to\n     * use for all remote tracks.\n     * @private\n     * @returns {void}\n     */\n    _updateAudioOutputForAudioTracks(deviceId) {\n        const remoteAudioTracks = this.getRemoteTracks(MediaType.AUDIO);\n\n        for (const track of remoteAudioTracks) {\n            track.setAudioOutput(deviceId);\n        }\n    }\n}\n","export enum ConnectionQualityEvents {\n    /**\n     * Indicates that the local connection statistics were updated.\n     */\n    LOCAL_STATS_UPDATED = 'cq.local_stats_updated',\n\n    /**\n     * Indicates that the connection statistics for a particular remote participant\n     * were updated.\n     */\n    REMOTE_STATS_UPDATED = 'cq.remote_stats_updated'\n};\n\n// exported for backward compatibility\nexport const LOCAL_STATS_UPDATED = ConnectionQualityEvents.LOCAL_STATS_UPDATED;\nexport const REMOTE_STATS_UPDATED = ConnectionQualityEvents.REMOTE_STATS_UPDATED;\n\n","import { getLogger } from '@jitsi/logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport * as RTCEvents from '../../service/RTC/RTCEvents';\nimport * as ConnectionQualityEvents from '../../service/connectivity/ConnectionQualityEvents';\nimport browser from '../browser';\n\nconst Resolutions = require('../../service/RTC/Resolutions');\nconst VideoType = require('../../service/RTC/VideoType');\nconst { XMPPEvents } = require('../../service/xmpp/XMPPEvents');\n\nconst logger = getLogger(__filename);\n\n/**\n * The value to use for the \"type\" field for messages sent by ConnectionQuality\n * over the data channel.\n */\nconst STATS_MESSAGE_TYPE = 'stats';\n\n/**\n * The value to use for the \"type\" field for messages sent\n * over the data channel that contain facial expression.\n */\nconst FACIAL_EXPRESSION_MESSAGE_TYPE = 'facial_expression';\n\nconst kSimulcastFormats = [\n    { width: 1920,\n        height: 1080,\n        layers: 3,\n        target: 'high',\n        targetRN: 4000000 },\n    { width: 1280,\n        height: 720,\n        layers: 3,\n        target: 'high',\n        targetRN: 2500000 },\n    { width: 960,\n        height: 540,\n        layers: 3,\n        target: 'standard',\n        targetRN: 900000 },\n    { width: 640,\n        height: 360,\n        layers: 2,\n        target: 'standard',\n        targetRN: 500000 },\n    { width: 480,\n        height: 270,\n        layers: 2,\n        target: 'low',\n        targetRN: 350000 },\n    { width: 320,\n        height: 180,\n        layers: 1,\n        target: 'low',\n        targetRN: 150000 }\n];\n\n/**\n * The maximum bitrate to use as a measurement against the participant's current\n * bitrate. This cap helps in the cases where the participant's bitrate is high\n * but not enough to fulfill high targets, such as with 1080p.\n */\nconst MAX_TARGET_BITRATE = 2500;\n\n/**\n * The initial bitrate for video in kbps.\n */\nlet startBitrate = 800;\n\n/**\n * Gets the expected bitrate (in kbps) in perfect network conditions.\n * @param simulcast {boolean} whether simulcast is enabled or not.\n * @param resolution {Resolution} the resolution.\n * @param millisSinceStart {number} the number of milliseconds since sending video started.\n * @param videoQualitySettings {Object} the bitrate and codec settings for the local video source.\n */\nfunction getTarget(simulcast, resolution, millisSinceStart, videoQualitySettings) {\n    let target = 0;\n    let height = Math.min(resolution.height, resolution.width);\n\n    // Find the first format with height no bigger than ours.\n    let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);\n\n    if (simulcastFormat && simulcast && videoQualitySettings.codec === CodecMimeType.VP8) {\n        // Sum the target fields from all simulcast layers for the given\n        // resolution (e.g. 720p + 360p + 180p) for VP8 simulcast.\n        for (height = simulcastFormat.height; height >= 180; height /= 2) {\n            const targetHeight = height;\n\n            simulcastFormat = kSimulcastFormats.find(f => f.height === targetHeight);\n            if (simulcastFormat) {\n                target += browser.isReactNative()\n                    ? simulcastFormat.targetRN\n                    : videoQualitySettings[simulcastFormat.target];\n            } else {\n                break;\n            }\n        }\n    } else if (simulcastFormat) {\n        // For VP9 SVC, H.264 (simulcast automatically disabled) and p2p, target bitrate will be\n        // same as that of the individual stream bitrate.\n        target = browser.isReactNative()\n            ? simulcastFormat.targetRN\n            : videoQualitySettings[simulcastFormat.target];\n    }\n\n    // Allow for an additional 1 second for ramp up -- delay any initial drop\n    // of connection quality by 1 second. Convert target from bps to kbps.\n    return Math.min(target / 1000, rampUp(Math.max(0, millisSinceStart - 1000)));\n}\n\n/**\n * Gets the bitrate to which GCC would have ramped up in perfect network\n * conditions after millisSinceStart milliseconds.\n * @param millisSinceStart {number} the number of milliseconds since sending\n * video was enabled.\n */\nfunction rampUp(millisSinceStart) {\n    if (millisSinceStart > 60000) {\n        return Number.MAX_SAFE_INTEGER;\n    }\n\n    // According to GCC the send side bandwidth estimation grows with at most\n    // 8% per second.\n    // https://tools.ietf.org/html/draft-ietf-rmcat-gcc-02#section-5.5\n    return startBitrate * Math.pow(1.08, millisSinceStart / 1000);\n}\n\n/**\n * A class which monitors the local statistics coming from the RTC modules, and\n * calculates a \"connection quality\" value, in percent, for the media\n * connection. A value of 100% indicates a very good network connection, and a\n * value of 0% indicates a poor connection.\n */\nexport default class ConnectionQuality {\n    /**\n     *\n     * @param conference\n     * @param eventEmitter\n     * @param options\n     */\n    constructor(conference, eventEmitter, options) {\n        this.eventEmitter = eventEmitter;\n\n        /**\n         * The owning JitsiConference.\n         */\n        this._conference = conference;\n\n        /**\n         * Holds statistics about the local connection quality.\n         */\n        this._localStats = {\n            connectionQuality: 100,\n            jvbRTT: undefined\n        };\n\n        /**\n         * The time this._localStats.connectionQuality was last updated.\n         */\n        this._lastConnectionQualityUpdate = -1;\n\n        /**\n         * Conference options.\n         */\n        this._options = options;\n\n        /**\n         * Maps a participant ID to an object holding connection quality\n         * statistics received from this participant.\n         */\n        this._remoteStats = {};\n\n        /**\n         * The time that the ICE state last changed to CONNECTED. We use this\n         * to calculate how much time we as a sender have had to ramp-up.\n         */\n        this._timeIceConnected = -1;\n\n        /**\n         * The time that local video was unmuted. We use this to calculate how\n         * much time we as a sender have had to ramp-up.\n         */\n        this._timeVideoUnmuted = -1;\n\n        // We assume a global startBitrate value for the sake of simplicity.\n        if (this._options.config?.startBitrate > 0) {\n            startBitrate = this._options.config.startBitrate;\n        }\n\n        // TODO: consider ignoring these events and letting the user of\n        // lib-jitsi-meet handle these separately.\n        conference.on(\n            ConferenceEvents.CONNECTION_INTERRUPTED,\n            () => {\n                this._updateLocalConnectionQuality(0);\n                this.eventEmitter.emit(\n                    ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n                    this._localStats);\n                this._broadcastLocalStats();\n            });\n\n        conference.room.addListener(\n            XMPPEvents.ICE_CONNECTION_STATE_CHANGED,\n            (jingleSession, newState) => {\n                if (!jingleSession.isP2P && newState === 'connected') {\n                    this._timeIceConnected = window.performance.now();\n                }\n            });\n\n        // Listen to DataChannel message from other participants in the\n        // conference, and update the _remoteStats field accordingly.\n        // TODO - Delete this when all the mobile endpoints switch to using the new Colibri\n        // message format for sending the endpoint stats.\n        conference.on(\n            ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n            (participant, payload) => {\n                if (payload.type === STATS_MESSAGE_TYPE) {\n                    this._updateRemoteStats(\n                        participant.getId(), payload.values);\n                }\n            });\n\n        conference.on(\n            ConferenceEvents.ENDPOINT_STATS_RECEIVED,\n            (participant, payload) => {\n                this._updateRemoteStats(participant.getId(), payload);\n            });\n\n        conference.on(\n            ConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n            (participant, payload) => {\n                if (payload.type === FACIAL_EXPRESSION_MESSAGE_TYPE) {\n                    this.eventEmitter.emit(\n                        ConferenceEvents.FACIAL_EXPRESSION_ADDED,\n                        participant.getId(),\n                        payload);\n                }\n            });\n\n        // Listen to local statistics events originating from the RTC module and update the _localStats field.\n        conference.statistics.addConnectionStatsListener(this._updateLocalStats.bind(this));\n\n        // Save the last time we were unmuted.\n        conference.on(\n            ConferenceEvents.TRACK_MUTE_CHANGED,\n            track => {\n                if (track.isVideoTrack()) {\n                    if (track.isMuted()) {\n                        this._timeVideoUnmuted = -1;\n                    } else {\n                        this._maybeUpdateUnmuteTime();\n                    }\n                }\n            });\n        conference.on(\n            ConferenceEvents.TRACK_ADDED,\n            track => {\n                if (track.isVideoTrack() && !track.isMuted()) {\n                    this._maybeUpdateUnmuteTime();\n                }\n            });\n        conference.rtc.on(\n            RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED,\n            track => {\n                this._localStats.maxEnabledResolution = track.maxEnabledResolution;\n            });\n\n        conference.on(\n            ConferenceEvents.SERVER_REGION_CHANGED,\n            serverRegion => {\n                this._localStats.serverRegion = serverRegion;\n            });\n\n        conference.on(\n            ConferenceEvents.PROPERTIES_CHANGED,\n            properties => {\n                this._localStats.bridgeCount\n                    = Number((properties || {})['bridge-count']);\n            }\n        );\n    }\n\n    /**\n     * Sets _timeVideoUnmuted if it was previously unset. If it was already set,\n     * doesn't change it.\n     */\n    _maybeUpdateUnmuteTime() {\n        if (this._timeVideoUnmuted < 0) {\n            this._timeVideoUnmuted = window.performance.now();\n        }\n    }\n\n    /**\n     * Calculates a new \"connection quality\" value.\n     * @param videoType {VideoType} the type of the video source (camera or a screen capture).\n     * @param isMuted {boolean} whether the local video is muted.\n     * @param resolutionName {Resolution} the input resolution used by the camera.\n     * @returns {*} the newly calculated connection quality.\n     */\n    _calculateConnectionQuality(videoType, isMuted, resolutionName) {\n\n        // resolutionName is an index into Resolutions (where \"720\" is\n        // \"1280x720\" and \"960\" is \"960x720\" ...).\n        const resolution = Resolutions[resolutionName];\n\n        let quality = 100;\n        let packetLoss;\n\n        // TODO: take into account packet loss for received streams\n\n        if (this._localStats.packetLoss) {\n            packetLoss = this._localStats.packetLoss.upload;\n\n            // Ugly Hack Alert (UHA):\n            // The packet loss for the upload direction is calculated based on\n            // incoming RTCP Receiver Reports. Since we don't have RTCP\n            // termination for audio, these reports come from the actual\n            // receivers in the conference and therefore the reported packet\n            // loss includes loss from the bridge to the receiver.\n            // When we are sending video this effect is small, because the\n            // number of video packets is much larger than the number of audio\n            // packets (and our calculation is based on the total number of\n            // received and lost packets).\n            // When video is muted, however, the effect might be significant,\n            // but we don't know what it is. We do know that it is positive, so\n            // as a temporary solution, until RTCP termination is implemented\n            // for the audio streams, we relax the packet loss checks here.\n            if (isMuted) {\n                packetLoss *= 0.5;\n            }\n        }\n\n        if (isMuted || !resolution || videoType === VideoType.DESKTOP\n            || this._timeIceConnected < 0\n            || this._timeVideoUnmuted < 0) {\n\n            // Calculate a value based on packet loss only.\n            if (packetLoss === undefined) {\n                logger.error('Cannot calculate connection quality, unknown '\n                    + 'packet loss.');\n                quality = 100;\n            } else if (packetLoss <= 2) {\n                quality = 100; // Full 5 bars.\n            } else if (packetLoss <= 4) {\n                quality = 70; // 4 bars\n            } else if (packetLoss <= 6) {\n                quality = 50; // 3 bars\n            } else if (packetLoss <= 8) {\n                quality = 30; // 2 bars\n            } else if (packetLoss <= 12) {\n                quality = 10; // 1 bars\n            } else {\n                quality = 0; // Still 1 bar, but slower climb-up.\n            }\n        } else {\n            // Calculate a value based on the send video bitrate on the active TPC.\n            const activeTPC = this._conference.getActivePeerConnection();\n\n            if (activeTPC) {\n                const isSimulcastOn = activeTPC.isSimulcastOn();\n                const videoQualitySettings = activeTPC.getTargetVideoBitrates();\n\n                // Add the codec info as well.\n                videoQualitySettings.codec = activeTPC.getConfiguredVideoCodec();\n\n                // Time since sending of video was enabled.\n                const millisSinceStart = window.performance.now()\n                    - Math.max(this._timeVideoUnmuted, this._timeIceConnected);\n                const statsInterval = this._options.config?.pcStatsInterval ?? 10000;\n\n                // Expected sending bitrate in perfect conditions.\n                let target = getTarget(isSimulcastOn, resolution, millisSinceStart, videoQualitySettings);\n\n                target = Math.min(target, MAX_TARGET_BITRATE);\n\n                // Calculate the quality only after the stats are available (after video was enabled).\n                if (millisSinceStart > statsInterval) {\n                    quality = 100 * this._localStats.bitrate.upload / target;\n                }\n            }\n\n            // Whatever the bitrate, drop early if there is significant loss\n            if (packetLoss && packetLoss >= 10) {\n                quality = Math.min(quality, 30);\n            }\n        }\n\n        // Make sure that the quality doesn't climb quickly\n        if (this._lastConnectionQualityUpdate > 0) {\n            const maxIncreasePerSecond = 2;\n            const prevConnectionQuality = this._localStats.connectionQuality;\n            const diffSeconds = (window.performance.now() - this._lastConnectionQualityUpdate) / 1000;\n\n            quality = Math.min(quality, prevConnectionQuality + (diffSeconds * maxIncreasePerSecond));\n        }\n\n        return Math.min(100, quality);\n    }\n\n    /**\n     * Updates the localConnectionQuality value\n     * @param values {number} the new value. Should be in [0, 100].\n     */\n    _updateLocalConnectionQuality(value) {\n        this._localStats.connectionQuality = value;\n        this._lastConnectionQualityUpdate = window.performance.now();\n    }\n\n    /**\n     * Broadcasts the local statistics to all other participants in the\n     * conference.\n     */\n    _broadcastLocalStats() {\n        // Send only the data that remote participants care about.\n        const data = {\n            bitrate: this._localStats.bitrate,\n            packetLoss: this._localStats.packetLoss,\n            connectionQuality: this._localStats.connectionQuality,\n            jvbRTT: this._localStats.jvbRTT,\n            serverRegion: this._localStats.serverRegion,\n            maxEnabledResolution: this._localStats.maxEnabledResolution,\n            avgAudioLevels: this._localStats.localAvgAudioLevels\n        };\n\n        try {\n            this._conference.sendEndpointStatsMessage(data);\n        } catch (err) {\n            // Ignore the error as we might hit it in the beginning of the call before the channel is ready.\n            // The statistics will be sent again after few seconds and error is logged elseware as well.\n        }\n    }\n\n    /**\n     * Updates the local statistics\n     * @param {TraceablePeerConnection} tpc the peerconnection which emitted\n     * the stats\n     * @param data new statistics\n     */\n    _updateLocalStats(tpc, data) {\n        // Update jvbRTT\n        if (!tpc.isP2P) {\n            const jvbRTT\n                = data.transport\n                    && data.transport.length && data.transport[0].rtt;\n\n            this._localStats.jvbRTT = jvbRTT ? jvbRTT : undefined;\n        }\n\n        // Do not continue with processing of other stats if they do not\n        // originate from the active peerconnection\n        if (tpc !== this._conference.getActivePeerConnection()) {\n            return;\n        }\n\n        let key;\n        const updateLocalConnectionQuality\n            = !this._conference.isConnectionInterrupted();\n        const localVideoTrack\n            = this._conference.getLocalVideoTrack();\n        const videoType\n            = localVideoTrack ? localVideoTrack.videoType : undefined;\n        const isMuted = localVideoTrack ? localVideoTrack.isMuted() : true;\n        const resolution = localVideoTrack\n            ? Math.min(localVideoTrack.resolution, localVideoTrack.maxEnabledResolution) : null;\n\n        if (!isMuted) {\n            this._maybeUpdateUnmuteTime();\n        }\n\n        // Copy the fields already in 'data'.\n        for (key in data) {\n            if (data.hasOwnProperty(key)) {\n                this._localStats[key] = data[key];\n            }\n        }\n\n        // And re-calculate the connectionQuality field.\n        if (updateLocalConnectionQuality) {\n            this._updateLocalConnectionQuality(\n                this._calculateConnectionQuality(\n                    videoType,\n                    isMuted,\n                    resolution));\n        }\n\n        this.eventEmitter.emit(\n            ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n            this._localStats);\n        this._broadcastLocalStats();\n    }\n\n    /**\n     * Updates remote statistics\n     * @param id the id of the remote participant\n     * @param data the statistics received\n     */\n    _updateRemoteStats(id, data) {\n        // Use only the fields we need\n        this._remoteStats[id] = {\n            bitrate: data.bitrate,\n            packetLoss: data.packetLoss,\n            connectionQuality: data.connectionQuality,\n            jvbRTT: data.jvbRTT,\n            serverRegion: data.serverRegion,\n            maxEnabledResolution: data.maxEnabledResolution,\n            avgAudioLevels: data.avgAudioLevels\n        };\n\n        this.eventEmitter.emit(\n            ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n            id,\n            this._remoteStats[id]);\n    }\n\n    /**\n     * Returns the local statistics.\n     * Exported only for use in jitsi-meet-torture.\n     */\n    getStats() {\n        return this._localStats;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceErrors from '../../JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * This class deals with shenanigans around JVB media session's ICE failed status handling.\n *\n * If ICE restarts are NOT explicitly enabled by the {@code enableIceRestart} config option, then the conference will\n * delay emitting the {@JitsiConferenceErrors.ICE_FAILED} event by 15 seconds. If the network info module reports\n * the internet offline status then the time will start counting after the internet comes back online.\n *\n * If ICE restart are enabled, then a delayed ICE failed notification to Jicofo will be sent, only if the ICE connection\n * does not recover soon after or before the XMPP connection is restored (if it was ever broken). If ICE fails while\n * the XMPP connection is not broken then the notifications will be sent after 2 seconds delay.\n */\nexport default class IceFailedHandling {\n    /**\n     * Creates new {@code DelayedIceFailed} task.\n     * @param {JitsiConference} conference\n     */\n    constructor(conference) {\n        this._conference = conference;\n    }\n\n    /**\n     * After making sure there's no way for the ICE connection to recover this method either sends ICE failed\n     * notification to Jicofo or emits the ice failed conference event.\n     * @private\n     * @returns {void}\n     */\n    _actOnIceFailed() {\n        if (!this._conference.room) {\n            return;\n        }\n\n        const { enableForcedReload, enableIceRestart } = this._conference.options.config;\n        const explicitlyDisabled = typeof enableIceRestart !== 'undefined' && !enableIceRestart;\n        const supportsRestartByTerminate = this._conference.room.supportsRestartByTerminate();\n        const useTerminateForRestart = supportsRestartByTerminate && !enableIceRestart;\n\n        logger.info('ICE failed,'\n            + ` enableForcedReload: ${enableForcedReload},`\n            + ` enableIceRestart: ${enableIceRestart},`\n            + ` supports restart by terminate: ${supportsRestartByTerminate}`);\n\n        if (explicitlyDisabled || (!enableIceRestart && !supportsRestartByTerminate) || enableForcedReload) {\n            logger.info('ICE failed, but ICE restarts are disabled');\n            const reason = enableForcedReload\n                ? JitsiConferenceErrors.CONFERENCE_RESTARTED\n                : JitsiConferenceErrors.ICE_FAILED;\n\n            this._conference.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, reason);\n\n            return;\n        }\n\n        const jvbConnection = this._conference.jvbJingleSession;\n        const jvbConnIceState = jvbConnection && jvbConnection.getIceConnectionState();\n\n        if (!jvbConnection) {\n            logger.warn('Not sending ICE failed - no JVB connection');\n        } else if (jvbConnIceState === 'connected') {\n            logger.info('ICE connection restored - not sending ICE failed');\n        } else {\n            logger.info('Sending ICE failed - the connection did not recover, '\n                + `ICE state: ${jvbConnIceState}, `\n                + `use 'session-terminate': ${useTerminateForRestart}`);\n            if (useTerminateForRestart) {\n                this._conference.jvbJingleSession.terminate(\n                    () => {\n                        logger.info('session-terminate for ice restart - done');\n                    },\n                    error => {\n                        logger.error(`session-terminate for ice restart - error: ${error.message}`);\n                    }, {\n                        reason: 'connectivity-error',\n                        reasonDescription: 'ICE FAILED',\n                        requestRestart: true,\n                        sendSessionTerminate: true\n                    });\n            } else {\n                this._conference.jvbJingleSession.sendIceFailedNotification();\n            }\n        }\n    }\n\n    /**\n     * Starts the task.\n     */\n    start() {\n        //  Using xmpp.ping allows to handle both XMPP being disconnected and internet offline cases. The ping function\n        // uses sendIQ2 method which is resilient to XMPP connection disconnected state and will patiently wait until it\n        // gets reconnected.\n        //  This also handles the case about waiting for the internet to come back online, because ping\n        // will only succeed when the internet is online and then there's a chance for the ICE to recover from FAILED to\n        // CONNECTED which is the extra 2 second timeout after ping.\n        //  The 65 second timeout is given on purpose as there's no chance for XMPP to recover after 65 seconds of no\n        // communication with the server. Such resume attempt will result in unrecoverable conference failed event due\n        // to 'item-not-found' error returned by the server.\n        this._conference.xmpp.ping(65000).then(\n            () => {\n                if (!this._canceled) {\n                    this._iceFailedTimeout = window.setTimeout(() => {\n                        this._iceFailedTimeout = undefined;\n                        this._actOnIceFailed();\n                    }, 2000);\n                }\n            },\n            error => {\n                logger.error('PING error/timeout - not sending ICE failed', error);\n            });\n    }\n\n    /**\n     * Cancels the task.\n     */\n    cancel() {\n        this._canceled = true;\n        window.clearTimeout(this._iceFailedTimeout);\n    }\n}\n","export enum DetectionEvents {\n    /**\n     * Event triggered by a audio detector indicating that its active state has changed from active to inactive or vice\n     * versa.\n     * @event\n     * @type {boolean} - true when service has changed to active false otherwise.\n     */\n    DETECTOR_STATE_CHANGE = 'detector_state_change',\n\n    /** Event triggered by {@link NoAudioSignalDetector} when the local audio device associated with a JitsiConference\n     * starts receiving audio levels with the value of 0 meaning no audio is being captured on that device, or when\n     * it starts receiving audio levels !== 0 after being in a state of no audio.\n     * @event\n     * @type {boolean} - true when the current conference audio track has audio input false otherwise.\n     */\n    AUDIO_INPUT_STATE_CHANGE = 'audio_input_state_changed',\n\n    /** Event triggered by NoAudioSignalDetector when the local audio device associated with a JitsiConference goes silent\n     * for a period of time, meaning that the device is either broken or hardware/software muted.\n     * @event\n     * @type {void}\n     */\n    NO_AUDIO_INPUT = 'no_audio_input_detected',\n\n    /**\n     *  Event generated by {@link VADNoiseDetection} when the tracked device is considered noisy.\n     *  @event\n     *  @type {Object}\n     */\n    VAD_NOISY_DEVICE = 'detection.vad_noise_device',\n\n    /**\n     * Event generated by VADReportingService when if finishes creating a VAD report for the monitored devices.\n     * The generated objects are of type Array<Object>, one score for each monitored device.\n     * @event VAD_REPORT_PUBLISHED\n     * @type Array<Object> with the following structure:\n     * @property {Date} timestamp - Timestamp at which the compute took place.\n     * @property {number} avgVAD - Average VAD score over monitored period of time.\n     * @property {string} deviceId - Associate local audio device ID.\n     */\n    VAD_REPORT_PUBLISHED = 'vad-report-published',\n\n    /**\n     * Event generated by {@link TrackVADEmitter} when PCM sample VAD score is available.\n     *\n     * @event\n     * @type {Object}\n     * @property {Date}   timestamp - Exact time at which processed PCM sample was generated.\n     * @property {number} score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n     * @property {Float32Array} pcmData - Raw PCM data with which the VAD score was calculated.\n     * @property {string} deviceId - Device id of the associated track.\n     */\n    VAD_SCORE_PUBLISHED = 'detection.vad_score_published',\n\n    /**\n     *  Event generated by {@link VADTalkMutedDetection} when a user is talking while the mic is muted.\n     *\n     *  @event\n     *  @type {Object}\n     */\n    VAD_TALK_WHILE_MUTED = 'detection.vad_talk_while_muted'\n};\n\n// exported for backward compatibility\nexport const DETECTOR_STATE_CHANGE = DetectionEvents.DETECTOR_STATE_CHANGE;\nexport const AUDIO_INPUT_STATE_CHANGE = DetectionEvents.AUDIO_INPUT_STATE_CHANGE;\nexport const NO_AUDIO_INPUT = DetectionEvents.NO_AUDIO_INPUT;\nexport const VAD_NOISY_DEVICE = DetectionEvents.VAD_NOISY_DEVICE;\nexport const VAD_REPORT_PUBLISHED = DetectionEvents.VAD_REPORT_PUBLISHED;\nexport const VAD_SCORE_PUBLISHED = DetectionEvents.VAD_SCORE_PUBLISHED;\nexport const VAD_TALK_WHILE_MUTED = DetectionEvents.VAD_TALK_WHILE_MUTED;\n","import EventEmitter from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport browser from '../browser';\n\nimport * as DetectionEvents from './DetectionEvents';\n\n// We wait a certain time interval for constant silence input from the current device to account for\n// potential abnormalities and for a better use experience i.e. don't generate event the instant\n// an audio track is added to the tcr.\n// Potential improvement - add this as a configurable parameter.\nconst SILENCE_PERIOD_MS = 4000;\n\n/**\n * Detect if there is no audio input on the current TraceAblePeerConnection selected track. The no audio\n * state must be constant for a configured amount of time in order for the event to be triggered.\n * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE\n * @fires DetectionEvents.NO_AUDIO_INPUT\n */\nexport default class NoAudioSignalDetection extends EventEmitter {\n    /**\n     * Creates new NoAudioSignalDetection.\n     *\n     * @param conference the JitsiConference instance that created us.\n     * @constructor\n     */\n    constructor(conference) {\n        super();\n\n        this._conference = conference;\n        this._timeoutTrigger = null;\n        this._hasAudioInput = null;\n\n        if (!browser.supportsReceiverStats()) {\n            conference.statistics.addAudioLevelListener(this._audioLevel.bind(this));\n        }\n        conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));\n    }\n\n    /**\n     * Clear the timeout state.\n     */\n    _clearTriggerTimeout() {\n        clearTimeout(this._timeoutTrigger);\n        this._timeoutTrigger = null;\n    }\n\n\n    /**\n     * Generated event triggered by a change in the current conference audio input state.\n     *\n     * @param {*} audioLevel - The audio level of the ssrc.\n     * @fires DetectionEvents.AUDIO_INPUT_STATE_CHANGE\n     */\n    _handleAudioInputStateChange(audioLevel) {\n        // Current audio input state of the active local track in the conference, true for audio input false for no\n        // audio input.\n        const status = audioLevel !== 0;\n\n        // If this is the first audio event picked up or the current status is different from the previous trigger\n        // the event.\n        if (this._hasAudioInput === null || this._hasAudioInput !== status) {\n            this._hasAudioInput = status;\n            this.emit(DetectionEvents.AUDIO_INPUT_STATE_CHANGE, this._hasAudioInput);\n        }\n    }\n\n    /**\n     * Generate event triggered by a prolonged period of no audio input.\n     *\n     * @param {number} audioLevel - The audio level of the ssrc.\n     * @fires DetectionEvents.NO_AUDIO_INPUT\n     */\n    _handleNoAudioInputDetection(audioLevel) {\n        if (this._eventFired) {\n            return;\n        }\n\n        if (audioLevel === 0 && !this._timeoutTrigger) {\n            this._timeoutTrigger = setTimeout(() => {\n                this._eventFired = true;\n\n                this.emit(DetectionEvents.NO_AUDIO_INPUT);\n            }, SILENCE_PERIOD_MS);\n        } else if (audioLevel !== 0 && this._timeoutTrigger) {\n            this._clearTriggerTimeout();\n        }\n    }\n\n    /**\n     * Receives audio level events for all send and receive streams on the current TraceablePeerConnection.\n     *\n     * @param {TraceablePeerConnection} tpc - TraceablePeerConnection of the owning conference.\n     * @param {number} ssrc - The synchronization source identifier (SSRC) of the endpoint/participant/stream\n     * being reported.\n     * @param {number} audioLevel - The audio level of the ssrc.\n     * @param {boolean} isLocal - true for local/send streams or false for remote/receive streams.\n     */\n    _audioLevel(tpc, ssrc, audioLevel, isLocal) {\n        // We are interested in the local audio streams\n        if (!isLocal || !this._audioTrack) {\n            return;\n        }\n\n        // Get currently active local tracks from the TraceablePeerConnection\n        const localSSRCs = tpc.localSSRCs.get(this._audioTrack.rtcId);\n\n        // Only target the current active track in the tpc. For some reason audio levels for previous\n        // devices are also picked up from the PeerConnection so we filter them out.\n        if (!localSSRCs || !localSSRCs.ssrcs.includes(ssrc)) {\n            return;\n        }\n\n        // First handle audio input state change. In case the state changed to no input the no audio input event\n        // can try to fire again.\n        this._handleAudioInputStateChange(audioLevel);\n        this._handleNoAudioInputDetection(audioLevel);\n    }\n\n    /**\n     * Notifies NoAudioSignalDetection that a JitsiTrack was added to the associated JitsiConference.\n     * Only take into account local audio tracks.\n     *\n     * @param {JitsiTrack} track - The added JitsiTrack.\n     */\n    _trackAdded(track) {\n        if (track.isLocalAudioTrack()) {\n            // Reset state for the new track.\n            this._audioTrack = track;\n            this._eventFired = false;\n            this._clearTriggerTimeout();\n\n            // Listen for the audio levels on the newly added audio track\n            if (browser.supportsReceiverStats()) {\n                track.on(\n                    JitsiTrackEvents.NO_AUDIO_INPUT,\n                    audioLevel => {\n                        this._handleNoAudioInputDetection(audioLevel);\n                    }\n                );\n                track.on(\n                    JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n                    audioLevel => {\n                        this._handleNoAudioInputDetection(audioLevel);\n                        this._handleAudioInputStateChange(audioLevel);\n                    }\n                );\n            }\n        }\n    }\n}\n","import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\n\n/**\n * The value which we use to say, every sound over this threshold\n * is talking on the mic.\n * @type {number}\n */\nconst SPEECH_DETECT_THRESHOLD = 0.6;\n\n/**\n * The <tt>P2PDominantSpeakerDetection</tt> is activated only when p2p is\n * currently used.\n * Listens for changes in the audio level changes of the local p2p audio track\n * or remote p2p one and fires dominant speaker events to be able to use\n * features depending on those events (speaker stats), to make them work without\n * the video bridge.\n */\nexport default class P2PDominantSpeakerDetection {\n    /**\n     * Creates P2PDominantSpeakerDetection\n     * @param conference the JitsiConference instance that created us.\n     * @constructor\n     */\n    constructor(conference) {\n        this.conference = conference;\n\n        conference.addEventListener(\n            JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED,\n            this._audioLevel.bind(this));\n\n        this.myUserID = this.conference.myUserId();\n    }\n\n    /**\n     * Receives audio level events for all streams in the conference.\n     *\n     * @param {String} id - The participant id\n     * @param {number} audioLevel - The audio level.\n     */\n    _audioLevel(id, audioLevel) {\n\n        // we do not process if p2p is not active\n        // or audio level is under certain threshold\n        // or if the audio level is for local audio track which is muted\n        if (!this.conference.isP2PActive()\n            || audioLevel <= SPEECH_DETECT_THRESHOLD\n            || (id === this.myUserID\n                    && this.conference.getLocalAudioTrack().isMuted())) {\n            return;\n        }\n\n        this.conference.rtc.eventEmitter.emit(\n            RTCEvents.DOMINANT_SPEAKER_CHANGED,\n            id);\n    }\n}\n","/**\n * Adapter that creates AudioContext objects depending on the browser.\n *\n * @returns {AudioContext} - Return a new AudioContext or undefined if the browser does not support it.\n */\nexport function createAudioContext(options) {\n    const AudioContextImpl = window.AudioContext || window.webkitAudioContext;\n\n    if (!AudioContextImpl) {\n        return undefined;\n    }\n\n    return new AudioContextImpl(options);\n}\n","import EventEmitter from 'events';\n\nimport RTC from '../RTC/RTC';\nimport { createAudioContext } from '../webaudio/WebAudioUtils';\n\nimport { VAD_SCORE_PUBLISHED } from './DetectionEvents';\n\n/**\n * Connects an audio JitsiLocalTrack to a vadProcessor using WebAudio ScriptProcessorNode.\n * Once an object is created audio from the local track flows through the ScriptProcessorNode as raw PCM.\n * The PCM is processed by the injected vad module and a voice activity detection score is obtained, the\n * score is published to consumers via an EventEmitter.\n * After work is done with this service the destroy method needs to be called for a proper cleanup.\n *\n * @fires VAD_SCORE_PUBLISHED\n */\nexport default class TrackVADEmitter extends EventEmitter {\n    /**\n     * Constructor.\n     *\n     * @param {number} procNodeSampleRate - Sample rate of the ScriptProcessorNode. Possible values  256, 512, 1024,\n     *  2048, 4096, 8192, 16384. Passing other values will default to closes neighbor.\n     * @param {Object} vadProcessor - VAD processor that allows us to calculate VAD score for PCM samples.\n     * @param {JitsiLocalTrack} jitsiLocalTrack - JitsiLocalTrack corresponding to micDeviceId.\n     */\n    constructor(procNodeSampleRate, vadProcessor, jitsiLocalTrack) {\n        super();\n\n        /**\n         * Sample rate of the ScriptProcessorNode.\n         */\n        this._procNodeSampleRate = procNodeSampleRate;\n\n        /**\n         * VAD Processor that allows us to calculate VAD score for PCM samples\n         */\n        this._vadProcessor = vadProcessor;\n\n        /**\n         * The JitsiLocalTrack instance.\n         */\n        this._localTrack = jitsiLocalTrack;\n\n        /**\n         * Buffer to hold residue PCM resulting after a ScriptProcessorNode callback\n         */\n        this._bufferResidue = new Float32Array([]);\n\n        /**\n         * The AudioContext instance with the preferred sample frequency.\n         */\n        this._audioContext = createAudioContext({ sampleRate: vadProcessor.getRequiredPCMFrequency() });\n\n        /**\n         * PCM Sample size expected by the VAD Processor instance. We cache it here as this value is used extensively,\n         * saves a couple of function calls.\n         */\n        this._vadSampleSize = vadProcessor.getSampleLength();\n\n        /**\n         * Event listener function that will be called by the ScriptProcessNode with raw PCM data, depending on the set\n         * sample rate.\n         */\n        this._onAudioProcess = this._onAudioProcess.bind(this);\n\n        this._initializeAudioContext();\n    }\n\n    /**\n     * Factory method that sets up all the necessary components for the creation of the TrackVADEmitter.\n     *\n     * @param {string} micDeviceId - Target microphone device id.\n     * @param {number} procNodeSampleRate - Sample rate of the proc node.\n     * @param {Object} vadProcessor -Module that calculates the voice activity score for a certain audio PCM sample.\n     * The processor needs to implement the following functions:\n     * - <tt>getSampleLength()</tt> - Returns the sample size accepted by getSampleLength.\n     * - <tt>getRequiredPCMFrequency()</tt> - Returns the PCM frequency at which the processor operates.\n     * - <tt>calculateAudioFrameVAD(pcmSample)</tt> - Process a 32 float pcm sample of getSampleLength size.\n     * @returns {Promise<TrackVADEmitter>} - Promise resolving in a new instance of TrackVADEmitter.\n     */\n    static create(micDeviceId, procNodeSampleRate, vadProcessor) {\n        return RTC.obtainAudioAndVideoPermissions({\n            devices: [ 'audio' ],\n            micDeviceId\n        }).then(localTrack => {\n            // We only expect one audio track when specifying a device id.\n            if (!localTrack[0]) {\n                throw new Error(`Failed to create jitsi local track for device id: ${micDeviceId}`);\n            }\n\n            return new TrackVADEmitter(procNodeSampleRate, vadProcessor, localTrack[0]);\n\n            // We have no exception handling at this point as there is nothing to clean up, the vadProcessor\n            // life cycle is handled by whoever created this instance.\n        });\n    }\n\n    /**\n     * Sets up the audio graph in the AudioContext.\n     *\n     * @returns {void}\n     */\n    _initializeAudioContext() {\n        this._audioSource = this._audioContext.createMediaStreamSource(this._localTrack.stream);\n\n        // TODO AudioProcessingNode is deprecated in the web audio specifications and the recommended replacement\n        // is audio worklet, however at the point of implementation AudioProcessingNode was still de de facto way\n        // of achieving this functionality and supported in all major browsers as opposed to audio worklet which\n        // was only available in Chrome. This todo is just a reminder that we should replace AudioProcessingNode\n        // with audio worklet when it's mature enough and has more browser support.\n        // We don't need stereo for determining the VAD score so we create a single channel processing node.\n        this._audioProcessingNode = this._audioContext.createScriptProcessor(this._procNodeSampleRate, 1, 1);\n    }\n\n    /**\n     * ScriptProcessorNode callback, the input parameters contains the PCM audio that is then sent to rnnoise.\n     * Rnnoise only accepts PCM samples of 480 bytes whereas the webaudio processor node can't sample at a multiple\n     * of 480 thus after each _onAudioProcess callback there will remain and PCM buffer residue equal\n     * to _procNodeSampleRate / 480 which will be added to the next sample buffer and so on.\\\n     *\n     *\n     * @param {AudioProcessingEvent} audioEvent - Audio event.\n     * @returns {void}\n     * @fires VAD_SCORE_PUBLISHED\n     */\n    _onAudioProcess(audioEvent) {\n        // Prepend the residue PCM buffer from the previous process callback.\n        const inData = audioEvent.inputBuffer.getChannelData(0);\n        const completeInData = [ ...this._bufferResidue, ...inData ];\n        const sampleTimestamp = Date.now();\n\n        let i = 0;\n\n        for (; i + this._vadSampleSize < completeInData.length; i += this._vadSampleSize) {\n            const pcmSample = completeInData.slice(i, i + this._vadSampleSize);\n\n            // The VAD processor might change the values inside the array so we make a copy.\n            const vadScore = this._vadProcessor.calculateAudioFrameVAD(pcmSample.slice());\n\n            this.emit(VAD_SCORE_PUBLISHED, {\n                timestamp: sampleTimestamp,\n                score: vadScore,\n                pcmData: pcmSample,\n                deviceId: this._localTrack.getDeviceId()\n            });\n        }\n\n        this._bufferResidue = completeInData.slice(i, completeInData.length);\n    }\n\n    /**\n     * Connects the nodes in the AudioContext to start the flow of audio data.\n     *\n     * @returns {void}\n     */\n    _connectAudioGraph() {\n        this._audioProcessingNode.onaudioprocess = this._onAudioProcess;\n        this._audioSource.connect(this._audioProcessingNode);\n        this._audioProcessingNode.connect(this._audioContext.destination);\n    }\n\n    /**\n     * Disconnects the nodes in the AudioContext.\n     *\n     * @returns {void}\n     */\n    _disconnectAudioGraph() {\n        // Even thought we disconnect the processing node it seems that some callbacks remain queued,\n        // resulting in calls with and uninitialized context.\n        // eslint-disable-next-line no-empty-function\n        this._audioProcessingNode.onaudioprocess = () => {};\n        this._audioProcessingNode.disconnect();\n        this._audioSource.disconnect();\n    }\n\n    /**\n     * Cleanup potentially acquired resources.\n     *\n     * @returns {void}\n     */\n    _cleanupResources() {\n        this._disconnectAudioGraph();\n        this._localTrack.stopStream();\n    }\n\n    /**\n     * Get the associated track device ID.\n     *\n     * @returns {string}\n     */\n    getDeviceId() {\n        return this._localTrack.getDeviceId();\n    }\n\n\n    /**\n     * Get the associated track label.\n     *\n     * @returns {string}\n     */\n    getTrackLabel() {\n        return this._localTrack.getDeviceLabel();\n    }\n\n    /**\n     * Start the emitter by connecting the audio graph.\n     *\n     * @returns {void}\n     */\n    start() {\n        this._connectAudioGraph();\n    }\n\n    /**\n     * Stops the emitter by disconnecting the audio graph.\n     *\n     * @returns {void}\n     */\n    stop() {\n        this._disconnectAudioGraph();\n        this._bufferResidue = [];\n    }\n\n    /**\n     * Destroy TrackVADEmitter instance (release resources and stop callbacks).\n     *\n     * @returns {void}\n     */\n    destroy() {\n        if (this._destroyed) {\n            return;\n        }\n\n        this._cleanupResources();\n        this._destroyed = true;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { EventEmitter } from 'events';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nimport { VAD_SCORE_PUBLISHED, DETECTOR_STATE_CHANGE } from './DetectionEvents';\nimport TrackVADEmitter from './TrackVADEmitter';\n\nconst logger = getLogger(__filename);\n\n/**\n * Sample rate of TrackVADEmitter, it defines how many audio samples are processed at a time.\n * @type {number}\n */\nconst VAD_EMITTER_SAMPLE_RATE = 4096;\n\n/**\n * Connects a TrackVADEmitter to the target conference local audio track and manages various services that use\n * the data to produce audio analytics (VADTalkMutedDetection and VADNoiseDetection).\n */\nexport default class VADAudioAnalyser extends EventEmitter {\n    /**\n     * Creates <tt>VADAudioAnalyser</tt>\n     * @param {JitsiConference} conference - JitsiConference instance that created us.\n     * @param {Object} createVADProcessor - Function that creates a Voice activity detection processor. The processor\n     * needs to implement the following functions:\n     * - <tt>getSampleLength()</tt> - Returns the sample size accepted by getSampleLength.\n     * - <tt>getRequiredPCMFrequency()</tt> - Returns the PCM frequency at which the processor operates.\n     * - <tt>calculateAudioFrameVAD(pcmSample)</tt> - Process a 32 float pcm sample of getSampleLength size.\n     * @constructor\n     */\n    constructor(conference, createVADProcessor) {\n        super();\n\n        /**\n         * Member function that instantiates a VAD processor.\n         */\n        this._createVADProcessor = createVADProcessor;\n\n        /**\n         * Current {@link TrackVADEmitter}. VAD Emitter uses a {@link JitsiLocalTrack} and VAD processor to generate\n         * period voice probability scores.\n         */\n        this._vadEmitter = null;\n\n        /**\n         * Current state of the _vadEmitter\n         */\n        this._isVADEmitterRunning = false;\n\n        /**\n         * Array of currently attached VAD processing services.\n         */\n        this._detectionServices = [];\n\n        /**\n         * Promise used to chain create and destroy operations associated with TRACK_ADDED and TRACK_REMOVED events\n         * coming from the conference.\n         * Because we have an async created component (VAD Processor) we need to make sure that it's initialized before\n         * we destroy it ( when changing the device for instance), or when we use it from an external point of entry\n         * i.e. (TRACK_MUTE_CHANGED event callback).\n         */\n        this._vadInitTracker = Promise.resolve();\n\n        /**\n         * Listens for {@link TrackVADEmitter} events and processes them.\n         */\n        this._processVADScore = this._processVADScore.bind(this);\n\n        conference.on(JitsiConferenceEvents.TRACK_ADDED, this._trackAdded.bind(this));\n        conference.on(JitsiConferenceEvents.TRACK_REMOVED, this._trackRemoved.bind(this));\n        conference.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, this._trackMuteChanged.bind(this));\n    }\n\n    /**\n     * Attach a VAD detector service to the analyser and handle it's state changes.\n     *\n     * @param {Object} vadTMDetector\n     */\n    addVADDetectionService(vadService) {\n        this._detectionServices.push(vadService);\n        vadService.on(DETECTOR_STATE_CHANGE, () => {\n            // When the state of a detector changes check if there are any active detectors attached so that\n            // the _vadEmitter doesn't run needlessly.\n            const activeDetector = this._detectionServices.filter(detector => detector.isActive() === true);\n\n            // If there are no active detectors running and the vadEmitter is running then stop the emitter as it is\n            // uses a considerable amount of CPU. Otherwise start the service if it's stopped and there is a detector\n            // that needs it.\n            if (!activeDetector.length && this._isVADEmitterRunning) {\n                this._stopVADEmitter();\n            } else if (!this._isVADEmitterRunning) {\n                this._startVADEmitter();\n            }\n        });\n    }\n\n    /**\n     * Start the {@link TrackVADEmitter} and attach the event listener.\n     * @returns {void}\n     */\n    _startVADEmitter() {\n        if (this._vadEmitter) {\n            this._vadEmitter.on(VAD_SCORE_PUBLISHED, this._processVADScore);\n            this._vadEmitter.start();\n            this._isVADEmitterRunning = true;\n        }\n    }\n\n    /**\n     * Stop the {@link TrackVADEmitter} and detach the event listener.\n     * @returns {void}\n     */\n    _stopVADEmitter() {\n        if (this._vadEmitter) {\n            this._vadEmitter.removeListener(VAD_SCORE_PUBLISHED, this._processVADScore);\n            this._vadEmitter.stop();\n        }\n        this._isVADEmitterRunning = false;\n    }\n\n    /**\n     * Listens for {@link TrackVADEmitter} events and directs them to attached services as needed.\n     *\n     * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n     * @param {Date}   vadScore.timestamp - Exact time at which processed PCM sample was generated.\n     * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n     * @param {Float32Array} pcmData - Raw PCM data with which the VAD score was calculated.\n     * @param {string} vadScore.deviceId - Device id of the associated track.\n     * @listens VAD_SCORE_PUBLISHED\n     */\n    _processVADScore(vadScore) {\n        for (const detector of this._detectionServices) {\n            detector.processVADScore(vadScore);\n        }\n    }\n\n    /**\n     * Change the isMuted state of all attached detection services.\n     *\n     * @param {boolean} isMuted\n     */\n    _changeDetectorsMuteState(isMuted) {\n        for (const detector of this._detectionServices) {\n            detector.changeMuteState(isMuted);\n        }\n    }\n\n    /**\n     * Notifies the detector that a track was added to the associated {@link JitsiConference}.\n     * Only take into account local audio tracks.\n     * @param {JitsiTrack} track - The added track.\n     * @returns {void}\n     * @listens TRACK_ADDED\n     */\n    _trackAdded(track) {\n        if (track.isLocalAudioTrack()) {\n            // Keep a track promise so we take into account successive TRACK_ADD events being generated so that we\n            // destroy/create the processing context in the proper order.\n            this._vadInitTracker = this._vadInitTracker.then(() => this._createVADProcessor())\n                .then(vadProcessor =>\n                    TrackVADEmitter.create(track.getDeviceId(), VAD_EMITTER_SAMPLE_RATE, vadProcessor)\n                )\n                .then(vadEmitter => {\n                    logger.debug('Created VAD emitter for track: ', track.getTrackLabel());\n\n                    this._vadEmitter = vadEmitter;\n\n                    // Iterate through the detection services and set their appropriate mute state, depending on\n                    // service this will trigger a DETECTOR_STATE_CHANGE which in turn might start the _vadEmitter.\n                    this._changeDetectorsMuteState(track.isMuted());\n                })\n                .catch(error => {\n                    logger.warn('Failed to start VADAudioAnalyser', error);\n                });\n        }\n    }\n\n    /**\n     * Notifies the detector that the mute state of a {@link JitsiConference} track has changed. Only takes into account\n     * local audio tracks.\n     * @param {JitsiTrack} track - The track whose mute state has changed.\n     * @returns {void}\n     * @listens TRACK_MUTE_CHANGED\n     */\n    _trackMuteChanged(track) {\n        if (track.isLocalAudioTrack()) {\n            // On a mute toggle reset the state.\n            this._vadInitTracker = this._vadInitTracker.then(() => {\n                // Set mute status for the attached detection services.\n                this._changeDetectorsMuteState(track.isMuted());\n            });\n        }\n    }\n\n    /**\n     * Notifies the detector that a track associated with the {@link JitsiConference} was removed. Only takes into\n     * account local audio tracks. Cleans up resources associated with the track and resets the processing context.\n     *\n     * @param {JitsiTrack} track - The removed track.\n     * @returns {void}\n     * @listens TRACK_REMOVED\n     */\n    _trackRemoved(track) {\n        if (track.isLocalAudioTrack()) {\n            // Use the promise to make sure operations are in sequence.\n            this._vadInitTracker = this._vadInitTracker.then(() => {\n                logger.debug('Removing track from VAD detection - ', track.getTrackLabel());\n\n                // Track was removed, clean up and set appropriate states.\n                if (this._vadEmitter) {\n                    this._stopVADEmitter();\n                    this._vadEmitter.destroy();\n                    this._vadEmitter = null;\n                }\n\n                // Reset state of detectors when active track is removed.\n                for (const detector of this._detectionServices) {\n                    detector.reset();\n                }\n            });\n        }\n    }\n\n\n}\n","import { EventEmitter } from 'events';\n\nimport { calculateAverage, filterPositiveValues } from '../util/MathUtil';\n\nimport { VAD_NOISY_DEVICE, DETECTOR_STATE_CHANGE } from './DetectionEvents';\n\n/**\n * The average value VAD needs to be under over a period of time to be considered noise.\n * @type {number}\n */\nconst VAD_NOISE_AVG_THRESHOLD = 0.2;\n\n/**\n * The average values that audio input need to be over to be considered loud.\n * @type {number}\n */\nconst NOISY_AUDIO_LEVEL_THRESHOLD = 0.040;\n\n/**\n * The value that a VAD score needs to be under in order for processing to begin.\n * @type {number}\n */\nconst VAD_SCORE_TRIGGER = 0.2;\n\n/**\n * The value that a VAD score needs to be under in order for processing to begin.\n * @type {number}\n */\nconst AUDIO_LEVEL_SCORE_TRIGGER = 0.020;\n\n/**\n * Time span over which we calculate an average score used to determine if we trigger the event.\n * @type {number}\n */\nconst PROCESS_TIME_FRAME_SPAN_MS = 1500;\n\n/**\n * Detect if provided VAD score and PCM data is considered noise.\n */\nexport default class VADNoiseDetection extends EventEmitter {\n    /**\n     * Creates <tt>VADNoiseDetection</tt>\n     *\n     * @constructor\n     */\n    constructor() {\n        super();\n\n        /**\n         * Flag which denotes the current state of the detection service i.e.if there is already a processing operation\n         * ongoing.\n         */\n        this._processing = false;\n\n        /**\n         * Buffer that keeps the VAD scores for a period of time.\n         */\n        this._scoreArray = [];\n\n        /**\n         * Buffer that keeps audio level samples for a period of time.\n         */\n        this._audioLvlArray = [];\n\n        /**\n         * Current state of the service, if it's not active no processing will occur.\n         */\n        this._active = false;\n\n        this._calculateNoisyScore = this._calculateNoisyScore.bind(this);\n    }\n\n    /**\n     * Compute cumulative VAD score and PCM audio levels once the PROCESS_TIME_FRAME_SPAN_MS timeout has elapsed.\n     * If the score is above the set threshold fire the event.\n     * @returns {void}\n     * @fires VAD_NOISY_DEVICE\n     */\n    _calculateNoisyScore() {\n        const scoreAvg = calculateAverage(this._scoreArray);\n        const audioLevelAvg = calculateAverage(this._audioLvlArray);\n\n        if (scoreAvg < VAD_NOISE_AVG_THRESHOLD && audioLevelAvg > NOISY_AUDIO_LEVEL_THRESHOLD) {\n            this.emit(VAD_NOISY_DEVICE);\n\n            this._setActiveState(false);\n        }\n\n        // We reset the context in case a new process phase needs to be triggered.\n        this.reset();\n    }\n\n    /**\n     * Record the vad score and average volume in the appropriate buffers.\n     *\n     * @param {number} vadScore\n     * @param {number} avgAudioLvl - average audio level of the PCM sample associated with the VAD score.s\n     */\n    _recordValues(vadScore, avgAudioLvl) {\n        this._scoreArray.push(vadScore);\n        this._audioLvlArray.push(avgAudioLvl);\n    }\n\n    /**\n     * Set the active state of the detection service and notify any listeners.\n     *\n     * @param {boolean} active\n     * @fires DETECTOR_STATE_CHANGE\n     */\n    _setActiveState(active) {\n        this._active = active;\n        this.emit(DETECTOR_STATE_CHANGE, this._active);\n    }\n\n    /**\n     * Change the state according to the muted status of the tracked device.\n     *\n     * @param {boolean} isMuted - Is the device muted or not.\n     */\n    changeMuteState(isMuted) {\n        // This service only needs to run when the microphone is not muted.\n        this._setActiveState(!isMuted);\n        this.reset();\n    }\n\n    /**\n     * Check whether or not the service is active or not.\n     *\n     * @returns {boolean}\n     */\n    isActive() {\n        return this._active;\n    }\n\n    /**\n     * Reset the processing context, clear buffers, cancel the timeout trigger.\n     *\n     * @returns {void}\n     */\n    reset() {\n        this._processing = false;\n        this._scoreArray = [];\n        this._audioLvlArray = [];\n        clearTimeout(this._processTimeout);\n    }\n\n    /**\n     * Listens for {@link TrackVADEmitter} events and processes them.\n     *\n     * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n     * @param {Date}   vadScore.timestamp - Exact time at which processed PCM sample was generated.\n     * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n     * @param {Float32Array} vadScore.pcmData - Raw PCM Data associated with the VAD score.\n     * @param {string} vadScore.deviceId - Device id of the associated track.\n     * @listens VAD_SCORE_PUBLISHED\n     */\n    processVADScore(vadScore) {\n        if (!this._active) {\n            return;\n        }\n\n        // There is a processing phase on going, add score to buffer array.\n        if (this._processing) {\n            // Filter and calculate sample average so we don't have to process one large array at a time.\n            const posAudioLevels = filterPositiveValues(vadScore.pcmData);\n\n            this._recordValues(vadScore.score, calculateAverage(posAudioLevels));\n\n            return;\n        }\n\n        // If the VAD score for the sample is low and audio level has a high enough level we can start listening for\n        // noise\n        if (vadScore.score < VAD_SCORE_TRIGGER) {\n            const posAudioLevels = filterPositiveValues(vadScore.pcmData);\n            const avgAudioLvl = calculateAverage(posAudioLevels);\n\n            if (avgAudioLvl > AUDIO_LEVEL_SCORE_TRIGGER) {\n                this._processing = true;\n                this._recordValues(vadScore.score, avgAudioLvl);\n\n                // Once the preset timeout executes the final score will be calculated.\n                this._processTimeout = setTimeout(this._calculateNoisyScore, PROCESS_TIME_FRAME_SPAN_MS);\n            }\n        }\n    }\n}\n","import { EventEmitter } from 'events';\n\nimport { calculateAverage } from '../util/MathUtil';\n\nimport { VAD_TALK_WHILE_MUTED, DETECTOR_STATE_CHANGE } from './DetectionEvents';\n\n\n/**\n * The threshold which the average VAD values for a span of time needs to exceed to trigger an event.\n * @type {number}\n */\nconst VAD_AVG_THRESHOLD = 0.6;\n\n/**\n * The VAD score needed to trigger the processing algorithm, i.e. if a sample has the VAD score >= VAD_VOICE_LEVEL\n * we start processing all scores for a time span defined by const PROCESS_TIME_FRAME_SPAN_MS.\n * @type {number}\n */\nconst VAD_VOICE_LEVEL = 0.9;\n\n/**\n * Sample rate of TrackVADEmitter, it defines how many audio samples are processed at a time.\n * @type {number}\n */\n\n/**\n * Time span over which we calculate an average score used to determine if we trigger the event.\n * @type {number}\n */\nconst PROCESS_TIME_FRAME_SPAN_MS = 700;\n\n/**\n * Detect if provided VAD score which is generated on a muted device is voice and fires an event.\n */\nexport default class VADTalkMutedDetection extends EventEmitter {\n    /**\n     * Creates <tt>VADTalkMutedDetection</tt>\n     * @constructor\n     */\n    constructor() {\n        super();\n\n        /**\n         * Flag which denotes the current state of the detection service i.e.if there is already a processing operation\n         * ongoing.\n         */\n        this._processing = false;\n\n        /**\n         * Buffer that keeps the VAD scores for a period of time.\n         */\n        this._scoreArray = [];\n\n        /**\n         * Current mute state of the audio track being monitored.\n         */\n        this._active = false;\n\n        this._calculateVADScore = this._calculateVADScore.bind(this);\n    }\n\n    /**\n     * Compute cumulative VAD score function called once the PROCESS_TIME_FRAME_SPAN_MS timeout has elapsed.\n     * @returns {void}\n     * @fires VAD_TALK_WHILE_MUTED\n     */\n    _calculateVADScore() {\n        const score = calculateAverage(this._scoreArray);\n\n        if (score > VAD_AVG_THRESHOLD) {\n            this.emit(VAD_TALK_WHILE_MUTED);\n\n            // Event was fired. Stop event emitter and remove listeners so no residue events kick off after this point\n            // and a single VAD_TALK_WHILE_MUTED is generated per mic muted state.\n            this._setActiveState(false);\n        }\n\n        // We reset the context in case a new process phase needs to be triggered.\n        this.reset();\n    }\n\n    /**\n     * Set the active state of the detection service and notify any listeners.\n     *\n     * @param {boolean} active\n     * @fires DETECTOR_STATE_CHANGE\n     */\n    _setActiveState(active) {\n        this._active = active;\n        this.emit(DETECTOR_STATE_CHANGE, this._active);\n    }\n\n    /**\n     * Change the state according to the muted status of the tracked device.\n     *\n     * @param {boolean} isMuted - Is the device muted or not.\n     */\n    changeMuteState(isMuted) {\n        // This service only needs to run when the microphone is muted.\n        this._setActiveState(isMuted);\n        this.reset();\n    }\n\n    /**\n     * Check whether or not the service is active or not.\n     *\n     * @returns {boolean}\n     */\n    isActive() {\n        return this._active;\n    }\n\n    /**\n     * Listens for {@link TrackVADEmitter} events and processes them.\n     *\n     * @param {Object} vadScore -VAD score emitted by {@link TrackVADEmitter}\n     * @param {Date}   vadScore.timestamp - Exact time at which processed PCM sample was generated.\n     * @param {number} vadScore.score - VAD score on a scale from 0 to 1 (i.e. 0.7)\n     * @param {string} vadScore.deviceId - Device id of the associated track.\n     * @listens VAD_SCORE_PUBLISHED\n     */\n    processVADScore(vadScore) {\n        if (!this._active) {\n            return;\n        }\n\n        // There is a processing phase on going, add score to buffer array.\n        if (this._processing) {\n            this._scoreArray.push(vadScore.score);\n\n            return;\n        }\n\n        // Because we remove all listeners on the vadEmitter once the main event is triggered,\n        // there is no need to check for rogue events.\n        if (vadScore.score > VAD_VOICE_LEVEL) {\n            this._processing = true;\n            this._scoreArray.push(vadScore.score);\n\n            // Start gathering VAD scores for the configured period of time.\n            this._processTimeout = setTimeout(this._calculateVADScore, PROCESS_TIME_FRAME_SPAN_MS);\n        }\n    }\n\n    /**\n     * Reset the processing context, clear buffer, cancel the timeout trigger.\n     *\n     * @returns {void}\n     */\n    reset() {\n        this._processing = false;\n        this._scoreArray = [];\n        clearTimeout(this._processTimeout);\n    }\n}\n","export enum E2ePingEvents {\n    /**\n     * Indicates that the end-to-end round-trip-time for a participant has changed.\n     */\n    E2E_RTT_CHANGED = 'e2eping.e2e_rtt_changed'\n};\n\n// exported for backward compatibility\nexport const E2E_RTT_CHANGED = E2ePingEvents.E2E_RTT_CHANGED;\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as E2ePingEvents\n    from '../../service/e2eping/E2ePingEvents';\nimport { createE2eRttEvent } from '../../service/statistics/AnalyticsEvents';\nimport Statistics from '../statistics/statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * The 'type' of a message which designates an e2e ping request.\n * @type {string}\n */\nconst E2E_PING_REQUEST = 'e2e-ping-request';\n\n/**\n * The 'type' of a message which designates an e2e ping response.\n * @type {string}\n */\nconst E2E_PING_RESPONSE = 'e2e-ping-response';\n\n/**\n * Saves e2e ping related state for a single JitsiParticipant.\n */\nclass ParticipantWrapper {\n    /**\n     * Creates a ParticipantWrapper\n     * @param {JitsiParticipant} participant - The remote participant that this\n     * object wraps.\n     * @param {E2ePing} e2eping\n     */\n    constructor(participant, e2eping) {\n        // The JitsiParticipant\n        this.participant = participant;\n\n        // The E2ePing\n        this.e2eping = e2eping;\n\n        // Caches the ID\n        this.id = participant.getId();\n\n        // Recently sent requests\n        this.requests = {};\n\n        // The ID of the last sent request. We just increment it for each new\n        // request. Start at 1 so we can consider only thruthy values valid.\n        this.lastRequestId = 1;\n\n        this.clearIntervals = this.clearIntervals.bind(this);\n        this.sendRequest = this.sendRequest.bind(this);\n        this.handleResponse = this.handleResponse.bind(this);\n        this.maybeSendAnalytics = this.maybeSendAnalytics.bind(this);\n        this.sendAnalytics = this.sendAnalytics.bind(this);\n\n        // If the data channel was already open (this is likely a participant\n        // joining an existing conference) send a request immediately.\n        if (e2eping.isDataChannelOpen) {\n            this.sendRequest();\n        }\n\n        this.pingInterval = window.setInterval(\n            this.sendRequest, e2eping.pingIntervalMs);\n        this.analyticsInterval = window.setTimeout(\n            this.maybeSendAnalytics, this.e2eping.analyticsIntervalMs);\n    }\n\n    /**\n     * Clears the interval which sends pings.\n     * @type {*}\n     */\n    clearIntervals() {\n        if (this.pingInterval) {\n            window.clearInterval(this.pingInterval);\n        }\n        if (this.analyticsInterval) {\n            window.clearInterval(this.analyticsInterval);\n        }\n    }\n\n    /**\n     * Sends the next ping request.\n     * @type {*}\n     */\n    sendRequest() {\n        const requestId = this.lastRequestId++;\n        const requestMessage = {\n            type: E2E_PING_REQUEST,\n            id: requestId\n        };\n\n        this.e2eping.sendMessage(requestMessage, this.id);\n        this.requests[requestId] = {\n            id: requestId,\n            timeSent: window.performance.now()\n        };\n    }\n\n    /**\n     * Handles a response from this participant.\n     * @type {*}\n     */\n    handleResponse(response) {\n        const request = this.requests[response.id];\n\n        if (request) {\n            request.rtt = window.performance.now() - request.timeSent;\n            this.e2eping.eventEmitter.emit(\n                E2ePingEvents.E2E_RTT_CHANGED,\n                this.participant,\n                request.rtt);\n        }\n\n        this.maybeSendAnalytics();\n    }\n\n    /**\n     * Goes over the requests, clearing ones which we don't need anymore, and\n     * if it finds at least one request with a valid RTT in the last\n     * 'analyticsIntervalMs' then sends an analytics event.\n     * @type {*}\n     */\n    maybeSendAnalytics() {\n        const now = window.performance.now();\n\n        // The RTT we'll report is the minimum RTT measured in the last\n        // analyticsInterval\n        let rtt = Infinity;\n        let request, requestId;\n\n        // It's time to send analytics. Clean up all requests and find the\n        for (requestId in this.requests) {\n            if (this.requests.hasOwnProperty(requestId)) {\n                request = this.requests[requestId];\n\n                if (request.timeSent < now - this.e2eping.analyticsIntervalMs) {\n                    // An old request. We don't care about it anymore.\n                    delete this.requests[requestId];\n                } else if (request.rtt) {\n                    rtt = Math.min(rtt, request.rtt);\n                }\n            }\n        }\n\n        if (rtt < Infinity) {\n            this.sendAnalytics(rtt);\n        }\n    }\n\n    /**\n     * Sends an analytics event for this participant with the given RTT.\n     * @type {*}\n     */\n    sendAnalytics(rtt) {\n        Statistics.sendAnalytics(createE2eRttEvent(\n            this.id,\n            this.participant.getProperty('region'),\n            rtt));\n    }\n}\n\n/**\n * Implements end-to-end ping (from one conference participant to another) via\n * the jitsi-videobridge channel (either WebRTC data channel or web socket).\n *\n * TODO: use a broadcast message instead of individual pings to each remote\n * participant.\n *\n * This class:\n * 1. Sends periodic ping requests to all other participants in the\n * conference.\n * 2. Responds to ping requests from other participants.\n * 3. Fires events with the end-to-end RTT to each participant whenever a\n * response is received.\n * 4. Fires analytics events with the end-to-end RTT periodically.\n */\nexport default class E2ePing {\n    /**\n     * @param {JitsiConference} conference - The conference.\n     * @param {Function} sendMessage - The function to use to send a message.\n     * @param {Object} options\n     */\n    constructor(conference, options, sendMessage) {\n        this.conference = conference;\n        this.eventEmitter = conference.eventEmitter;\n        this.sendMessage = sendMessage;\n\n        // The interval at which pings will be sent (<= 0 disables sending).\n        this.pingIntervalMs = 10000;\n\n        // The interval at which analytics events will be sent.\n        this.analyticsIntervalMs = 60000;\n\n        // Maps a participant ID to its ParticipantWrapper\n        this.participants = {};\n\n        // Whether the WebRTC channel has been opened or not.\n        this.isDataChannelOpen = false;\n\n        if (options && options.e2eping) {\n            if (typeof options.e2eping.pingInterval === 'number') {\n                this.pingIntervalMs = options.e2eping.pingInterval;\n            }\n            if (typeof options.e2eping.analyticsInterval === 'number') {\n                this.analyticsIntervalMs = options.e2eping.analyticsInterval;\n            }\n\n            // We want to report at most once a ping interval.\n            if (this.analyticsIntervalMs > 0 && this.analyticsIntervalMs\n                < this.pingIntervalMs) {\n                this.analyticsIntervalMs = this.pingIntervalMs;\n            }\n        }\n        logger.info(\n            `Initializing e2e ping; pingInterval=${\n                this.pingIntervalMs}, analyticsInterval=${\n                this.analyticsIntervalMs}.`);\n\n        this.participantJoined = this.participantJoined.bind(this);\n        conference.on(\n            JitsiConferenceEvents.USER_JOINED,\n            this.participantJoined);\n\n        this.participantLeft = this.participantLeft.bind(this);\n        conference.on(\n            JitsiConferenceEvents.USER_LEFT,\n            this.participantLeft);\n\n        this.messageReceived = this.messageReceived.bind(this);\n        conference.on(\n            JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n            this.messageReceived);\n\n        this.dataChannelOpened = this.dataChannelOpened.bind(this);\n        conference.on(\n            JitsiConferenceEvents.DATA_CHANNEL_OPENED,\n            this.dataChannelOpened);\n    }\n\n    /**\n     * Notifies this instance that the communications channel has been opened\n     * and it can now send messages via sendMessage.\n     */\n    dataChannelOpened() {\n        this.isDataChannelOpen = true;\n\n        // We don't want to wait the whole interval before sending the first\n        // request, but we can't send it immediately after the participant joins\n        // either, because our data channel might not have initialized.\n        // So once the data channel initializes, send requests to everyone.\n        // Wait an additional 200ms to give a chance to the remote side (if it\n        // also just connected as is the case for the first 2 participants in a\n        // conference) to open its data channel.\n        for (const id in this.participants) {\n            if (this.participants.hasOwnProperty(id)) {\n                const participantWrapper = this.participants[id];\n\n                window.setTimeout(participantWrapper.sendRequest, 200);\n            }\n        }\n    }\n\n    /**\n     * Handles a message that was received.\n     *\n     * @param participant - The message sender.\n     * @param payload - The payload of the message.\n     */\n    messageReceived(participant, payload) {\n        // Listen to E2E PING requests and responses from other participants\n        // in the conference.\n        if (payload.type === E2E_PING_REQUEST) {\n            this.handleRequest(participant.getId(), payload);\n        } else if (payload.type === E2E_PING_RESPONSE) {\n            this.handleResponse(participant.getId(), payload);\n        }\n    }\n\n    /**\n     * Handles a participant joining the conference. Starts to send ping\n     * requests to the participant.\n     *\n     * @param {String} id - The ID of the participant.\n     * @param {JitsiParticipant} participant - The participant that joined.\n     */\n    participantJoined(id, participant) {\n        if (this.pingIntervalMs <= 0) {\n            return;\n        }\n\n        if (this.participants[id]) {\n            logger.info(\n                `Participant wrapper already exists for ${id}. Clearing.`);\n            this.participants[id].clearIntervals();\n            delete this.participants[id];\n        }\n\n        this.participants[id] = new ParticipantWrapper(participant, this);\n    }\n\n    /**\n     * Handles a participant leaving the conference. Stops sending requests.\n     *\n     * @param {String} id - The ID of the participant.\n     */\n    participantLeft(id) {\n        if (this.pingIntervalMs <= 0) {\n            return;\n        }\n\n        if (this.participants[id]) {\n            this.participants[id].clearIntervals();\n            delete this.participants[id];\n        }\n    }\n\n    /**\n     * Handles a ping request coming from another participant.\n     *\n     * @param {string} participantId - The ID of the participant who sent the\n     * request.\n     * @param {Object} request - The request.\n     */\n    handleRequest(participantId, request) {\n        // If it's a valid request, just send a response.\n        if (request && request.id) {\n            const response = {\n                type: E2E_PING_RESPONSE,\n                id: request.id\n            };\n\n            this.sendMessage(response, participantId);\n        } else {\n            logger.info(\n                `Received an invalid e2e ping request from ${participantId}.`);\n        }\n    }\n\n    /**\n     * Handles a ping response coming from another participant\n     * @param {string} participantId - The ID of the participant who sent the\n     * response.\n     * @param {Object} response - The response.\n     */\n    handleResponse(participantId, response) {\n        const participantWrapper = this.participants[participantId];\n\n        if (participantWrapper) {\n            participantWrapper.handleResponse(response);\n        }\n    }\n\n    /**\n     * Stops this E2ePing (i.e. stop sending requests).\n     */\n    stop() {\n        logger.info('Stopping e2eping');\n\n        this.conference.off(\n            JitsiConferenceEvents.USER_JOINED,\n            this.participantJoined);\n        this.conference.off(\n            JitsiConferenceEvents.USER_LEFT,\n            this.participantLeft);\n        this.conference.off(\n            JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,\n            this.messageReceived);\n        this.conference.off(\n            JitsiConferenceEvents.DATA_CHANNEL_OPENED,\n            this.dataChannelOpened);\n\n        for (const id in this.participants) {\n            if (this.participants.hasOwnProperty(id)) {\n                this.participants[id].clearIntervals();\n            }\n        }\n\n        this.participants = {};\n    }\n}\n\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * Emits {@link JitsiConferenceEvents.JVB121_STATUS} events based on the current\n * P2P status and the conference participants count. See the event description\n * for more info.\n */\nexport default class Jvb121EventGenerator {\n    /**\n     * Creates new <tt>Jvb121EventGenerator</tt> for the given conference.\n     * @param {JitsiConference} conference\n     */\n    constructor(conference) {\n        this._conference = conference;\n\n        /**\n         * Indicates whether it's a one to one JVB conference (<tt>true</tt>)\n         * or a multiparty (<tt>false</tt>). Will be also <tt>false</tt> if\n         * the conference is currently in the P2P mode.\n         * @type {boolean}\n         * @private\n         */\n        this._jvb121 = true;\n\n        this._conference.addEventListener(\n            JitsiConferenceEvents.USER_JOINED, () => this.evaluateStatus());\n        this._conference.addEventListener(\n            JitsiConferenceEvents.USER_LEFT, () => this.evaluateStatus());\n        this._conference.addEventListener(\n            JitsiConferenceEvents.P2P_STATUS, () => this.evaluateStatus());\n    }\n\n    /**\n     * Checks whether the JVB121 value should be updated and a new event\n     * emitted.\n     */\n    evaluateStatus() {\n        const oldStatus = this._jvb121;\n        const newStatus\n            = !this._conference.isP2PActive()\n                && this._conference.getParticipantCount() <= 2;\n\n        if (oldStatus !== newStatus) {\n            this._jvb121 = newStatus;\n            logger.debug(`JVB121 status ${oldStatus} => ${newStatus}`);\n            this._conference.eventEmitter.emit(\n                JitsiConferenceEvents.JVB121_STATUS, oldStatus, newStatus);\n        }\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nconst logger = getLogger(__filename);\nconst MAX_HEIGHT_ONSTAGE = 2160;\nconst MAX_HEIGHT_THUMBNAIL = 180;\nconst LASTN_UNLIMITED = -1;\n\n/**\n * This class translates the legacy signaling format between the client and the bridge (that affects bandwidth\n * allocation) to the new format described here https://github.com/jitsi/jitsi-videobridge/blob/master/doc/allocation.md\n */\nclass ReceiverVideoConstraints {\n    /**\n     * Creates a new instance.\n     */\n    constructor() {\n        // Default constraints used for endpoints that are not explicitly included in constraints.\n        // These constraints are used for endpoints that are thumbnails in the stage view.\n        this._defaultConstraints = { 'maxHeight': MAX_HEIGHT_THUMBNAIL };\n\n        // The number of videos requested from the bridge.\n        this._lastN = LASTN_UNLIMITED;\n\n        // The number representing the maximum video height the local client should receive from the bridge.\n        this._maxFrameHeight = MAX_HEIGHT_ONSTAGE;\n\n        // The endpoint IDs of the participants that are currently selected.\n        this._selectedEndpoints = [];\n\n        this._receiverVideoConstraints = {\n            constraints: {},\n            defaultConstraints: this.defaultConstraints,\n            lastN: this._lastN,\n            onStageEndpoints: [],\n            selectedEndpoints: this._selectedEndpoints\n        };\n    }\n\n    /**\n     * Returns the receiver video constraints that need to be sent on the bridge channel.\n     */\n    get constraints() {\n        this._receiverVideoConstraints.lastN = this._lastN;\n\n        if (!this._selectedEndpoints.length) {\n            return this._receiverVideoConstraints;\n        }\n\n        // The client is assumed to be in TileView if it has selected more than one endpoint, otherwise it is\n        // assumed to be in StageView.\n        this._receiverVideoConstraints.constraints = {};\n        if (this._selectedEndpoints.length > 1) {\n            /**\n             * Tile view.\n             * Only the default constraints are specified here along with lastN (if it is set).\n             * {\n             *  'colibriClass': 'ReceiverVideoConstraints',\n             *  'defaultConstraints': { 'maxHeight': 360 }\n             * }\n             */\n            this._receiverVideoConstraints.defaultConstraints = { 'maxHeight': this._maxFrameHeight };\n            this._receiverVideoConstraints.onStageEndpoints = [];\n            this._receiverVideoConstraints.selectedEndpoints = [];\n        } else {\n            /**\n             * Stage view.\n             * The participant on stage is specified in onStageEndpoints and a higher maxHeight is specified\n             * for that endpoint while a default maxHeight of 180 is applied to all the other endpoints.\n             * {\n             *  'colibriClass': 'ReceiverVideoConstraints',\n             *  'onStageEndpoints': ['A'],\n             *  'defaultConstraints': { 'maxHeight':  180 },\n             *  'constraints': {\n             *      'A': { 'maxHeight': 720 }\n             *   }\n             * }\n             */\n            this._receiverVideoConstraints.constraints[this._selectedEndpoints[0]] = {\n                'maxHeight': this._maxFrameHeight\n            };\n            this._receiverVideoConstraints.defaultConstraints = this._defaultConstraints;\n            this._receiverVideoConstraints.onStageEndpoints = this._selectedEndpoints;\n            this._receiverVideoConstraints.selectedEndpoints = [];\n        }\n\n        return this._receiverVideoConstraints;\n    }\n\n    /**\n     * Updates the lastN field of the ReceiverVideoConstraints sent to the bridge.\n     *\n     * @param {number} value\n     * @returns {boolean} Returns true if the the value has been updated, false otherwise.\n     */\n    updateLastN(value) {\n        const changed = this._lastN !== value;\n\n        if (changed) {\n            this._lastN = value;\n            logger.debug(`Updating ReceiverVideoConstraints lastN(${value})`);\n        }\n\n        return changed;\n    }\n\n    /**\n     * Updates the resolution (height requested) in the contraints field of the ReceiverVideoConstraints\n     * sent to the bridge.\n     *\n     * @param {number} maxFrameHeight\n     * @requires {boolean} Returns true if the the value has been updated, false otherwise.\n     */\n    updateReceiveResolution(maxFrameHeight) {\n        const changed = this._maxFrameHeight !== maxFrameHeight;\n\n        if (changed) {\n            this._maxFrameHeight = maxFrameHeight;\n            logger.debug(`Updating receive maxFrameHeight: ${maxFrameHeight}`);\n        }\n\n        return changed;\n    }\n\n    /**\n     * Updates the receiver constraints sent to the bridge.\n     *\n     * @param {Object} videoConstraints\n     * @returns {boolean} Returns true if the the value has been updated, false otherwise.\n     */\n    updateReceiverVideoConstraints(videoConstraints) {\n        const changed = !isEqual(this._receiverVideoConstraints, videoConstraints);\n\n        if (changed) {\n            this._receiverVideoConstraints = videoConstraints;\n            logger.debug(`Updating ReceiverVideoConstraints ${JSON.stringify(videoConstraints)}`);\n        }\n\n        return changed;\n    }\n\n    /**\n     * Updates the list of selected endpoints.\n     *\n     * @param {Array<string>} ids\n     * @returns {void}\n     */\n    updateSelectedEndpoints(ids) {\n        logger.debug(`Updating selected endpoints: ${JSON.stringify(ids)}`);\n        this._selectedEndpoints = ids;\n    }\n}\n\n/**\n * This class manages the receive video contraints for a given {@link JitsiConference}. These constraints are\n * determined by the application based on how the remote video streams need to be displayed. This class is responsible\n * for communicating these constraints to the bridge over the bridge channel.\n */\nexport default class ReceiveVideoController {\n    /**\n     * Creates a new instance for a given conference.\n     *\n     * @param {JitsiConference} conference the conference instance for which the new instance will be managing\n     * the receive video quality constraints.\n     * @param {RTC} rtc the rtc instance which is responsible for initializing the bridge channel.\n     */\n    constructor(conference, rtc) {\n        this._conference = conference;\n        this._rtc = rtc;\n\n        const { config } = conference.options;\n\n        // The number of videos requested from the bridge, -1 represents unlimited or all available videos.\n        this._lastN = config?.startLastN ?? (config?.channelLastN || LASTN_UNLIMITED);\n\n        // The number representing the maximum video height the local client should receive from the bridge.\n        this._maxFrameHeight = MAX_HEIGHT_ONSTAGE;\n\n        // Enable new receiver constraints by default unless it is explicitly disabled through config.js.\n        const useNewReceiverConstraints = config?.useNewBandwidthAllocationStrategy ?? true;\n\n        if (useNewReceiverConstraints) {\n            this._receiverVideoConstraints = new ReceiverVideoConstraints();\n            const lastNUpdated = this._receiverVideoConstraints.updateLastN(this._lastN);\n\n            lastNUpdated && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n        } else {\n            this._rtc.setLastN(this._lastN);\n        }\n\n        // The endpoint IDs of the participants that are currently selected.\n        this._selectedEndpoints = [];\n\n        this._conference.on(\n            JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n            session => this._onMediaSessionStarted(session));\n    }\n\n    /**\n     * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media\n     * session. The preferred receive frameHeight is applied on the media session.\n     *\n     * @param {JingleSessionPC} mediaSession - the started media session.\n     * @returns {void}\n     * @private\n     */\n    _onMediaSessionStarted(mediaSession) {\n        if (mediaSession.isP2P || !this._receiverVideoConstraints) {\n            mediaSession.setReceiverVideoConstraint(this._maxFrameHeight);\n        } else {\n            this._receiverVideoConstraints.updateReceiveResolution(this._maxFrameHeight);\n            this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n        }\n    }\n\n    /**\n     * Returns the lastN value for the conference.\n     *\n     * @returns {number}\n     */\n    getLastN() {\n        return this._lastN;\n    }\n\n    /**\n     * Elects the participants with the given ids to be the selected participants in order to always receive video\n     * for this participant (even when last n is enabled).\n     *\n     * @param {Array<string>} ids - The user ids.\n     * @returns {void}\n     */\n    selectEndpoints(ids) {\n        this._selectedEndpoints = ids;\n\n        if (this._receiverVideoConstraints) {\n            // Filter out the local endpointId from the list of selected endpoints.\n            const remoteEndpointIds = ids.filter(id => id !== this._conference.myUserId());\n            const oldConstraints = JSON.parse(JSON.stringify(this._receiverVideoConstraints.constraints));\n\n            remoteEndpointIds.length && this._receiverVideoConstraints.updateSelectedEndpoints(remoteEndpointIds);\n            const newConstraints = this._receiverVideoConstraints.constraints;\n\n            // Send bridge message only when the constraints change.\n            if (!isEqual(newConstraints, oldConstraints)) {\n                this._rtc.setNewReceiverVideoConstraints(newConstraints);\n            }\n\n            return;\n        }\n        this._rtc.selectEndpoints(ids);\n    }\n\n    /**\n     * Selects a new value for \"lastN\". The requested amount of videos are going to be delivered after the value is\n     * in effect. Set to -1 for unlimited or all available videos.\n     *\n     * @param {number} value the new value for lastN.\n     * @returns {void}\n     */\n    setLastN(value) {\n        if (this._lastN !== value) {\n            this._lastN = value;\n\n            if (this._receiverVideoConstraints) {\n                const lastNUpdated = this._receiverVideoConstraints.updateLastN(value);\n\n                // Send out the message on the bridge channel if lastN was updated.\n                lastNUpdated && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n\n                return;\n            }\n            this._rtc.setLastN(value);\n        }\n    }\n\n    /**\n     * Sets the maximum video resolution the local participant should receive from remote participants.\n     *\n     * @param {number|undefined} maxFrameHeight - the new value.\n     * @returns {void}\n     */\n    setPreferredReceiveMaxFrameHeight(maxFrameHeight) {\n        this._maxFrameHeight = maxFrameHeight;\n\n        for (const session of this._conference.getMediaSessions()) {\n            if (session.isP2P || !this._receiverVideoConstraints) {\n                maxFrameHeight && session.setReceiverVideoConstraint(maxFrameHeight);\n            } else {\n                const resolutionUpdated = this._receiverVideoConstraints.updateReceiveResolution(maxFrameHeight);\n\n                resolutionUpdated\n                    && this._rtc.setNewReceiverVideoConstraints(this._receiverVideoConstraints.constraints);\n            }\n        }\n    }\n\n    /**\n     * Sets the receiver constraints for the conference.\n     *\n     * @param {Object} constraints The video constraints.\n     */\n    setReceiverConstraints(constraints) {\n        if (!this._receiverVideoConstraints) {\n            this._receiverVideoConstraints = new ReceiverVideoConstraints();\n        }\n\n        const isEndpointsFormat = Object.keys(constraints).includes('onStageEndpoints', 'selectedEndpoints');\n        const isSourcesFormat = Object.keys(constraints).includes('onStageSources', 'selectedSources');\n\n        if (!FeatureFlags.isSourceNameSignalingEnabled() && isSourcesFormat) {\n            throw new Error(\n                '\"onStageSources\" and \"selectedSources\" are not supported when sourceNameSignaling is disabled.'\n            );\n        }\n\n        if (FeatureFlags.isSourceNameSignalingEnabled() && isEndpointsFormat) {\n            throw new Error(\n                '\"onStageEndpoints\" and \"selectedEndpoints\" are not supported when sourceNameSignaling is enabled.'\n            );\n        }\n        const constraintsChanged = this._receiverVideoConstraints.updateReceiverVideoConstraints(constraints);\n\n        if (constraintsChanged) {\n            this._lastN = constraints.lastN ?? this._lastN;\n            this._selectedEndpoints = constraints.selectedEndpoints ?? this._selectedEndpoints;\n            this._rtc.setNewReceiverVideoConstraints(constraints);\n\n            const p2pSession = this._conference.getMediaSessions().find(session => session.isP2P);\n\n            if (p2pSession) {\n                let maxFrameHeight = Object.values(constraints.constraints)[0]?.maxHeight;\n\n                if (!maxFrameHeight) {\n                    maxFrameHeight = constraints.defaultConstraints?.maxHeight;\n                }\n                maxFrameHeight && p2pSession.setReceiverVideoConstraint(maxFrameHeight);\n            }\n        }\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\nimport MediaSessionEvents from '../xmpp/MediaSessionEvents';\n\nconst logger = getLogger(__filename);\nconst MAX_LOCAL_RESOLUTION = 2160;\n\n/**\n * The class manages send video constraints across media sessions({@link JingleSessionPC}) which belong to\n * {@link JitsiConference}. It finds the lowest common value, between the local user's send preference and\n * the remote party's receive preference. Also this module will consider only the active session's receive value,\n * because local tracks are shared and while JVB may have no preference, the remote p2p may have and they may be totally\n * different.\n */\nexport default class SendVideoController {\n    /**\n     * Creates new instance for a given conference.\n     *\n     * @param {JitsiConference} conference - the conference instance for which the new instance will be managing\n     * the send video quality constraints.\n     * @param {RTC} rtc - the rtc instance that is responsible for sending the messages on the bridge channel.\n     */\n    constructor(conference, rtc) {\n        this._conference = conference;\n        this._preferredSendMaxFrameHeight = MAX_LOCAL_RESOLUTION;\n        this._rtc = rtc;\n\n        /**\n         * Source name based sender constraints.\n         * @type {Map<string, number>};\n         */\n\n        this._sourceSenderConstraints = new Map();\n        this._conference.on(\n            JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n            session => this._onMediaSessionStarted(session));\n        this._conference.on(\n            JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED,\n            () => this._configureConstraintsForLocalSources());\n        this._rtc.on(\n            RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED,\n            videoConstraints => this._onSenderConstraintsReceived(videoConstraints));\n    }\n\n    /**\n     * Configures the video encodings on the local sources when a media connection is established or becomes active.\n     *\n     * @returns {Promise<void[]>}\n     * @private\n     */\n    _configureConstraintsForLocalSources() {\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            for (const track of this._rtc.getLocalVideoTracks()) {\n                const sourceName = track.getSourceName();\n\n                sourceName && this._propagateSendMaxFrameHeight(sourceName);\n            }\n        } else {\n            this._propagateSendMaxFrameHeight();\n        }\n    }\n\n    /**\n     * Handles the {@link JitsiConferenceEvents.MEDIA_SESSION_STARTED}, that is when the conference creates new media\n     * session. It doesn't mean it's already active though. For example the JVB connection may be created after\n     * the conference has entered the p2p mode already.\n     *\n     * @param {JingleSessionPC} mediaSession - the started media session.\n     * @private\n     */\n    _onMediaSessionStarted(mediaSession) {\n        mediaSession.addListener(\n            MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED,\n            session => {\n                if (session === this._conference.getActiveMediaSession()) {\n                    this._configureConstraintsForLocalSources();\n                }\n            });\n    }\n\n    /**\n     * Propagates the video constraints if they have changed.\n     *\n     * @param {Object} videoConstraints - The sender video constraints received from the bridge.\n     * @returns {Promise<void[]>}\n     * @private\n     */\n    _onSenderConstraintsReceived(videoConstraints) {\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            const { maxHeight, sourceName } = videoConstraints;\n            const localVideoTracks = this._conference.getLocalVideoTracks() ?? [];\n\n            for (const track of localVideoTracks) {\n                // Propagate the sender constraint only if it has changed.\n                if (track.getSourceName() === sourceName\n                    && (!this._sourceSenderConstraints.has(sourceName)\n                    || this._sourceSenderConstraints.get(sourceName) !== maxHeight)) {\n                    this._sourceSenderConstraints.set(sourceName, maxHeight);\n                    logger.debug(`Sender constraints for source:${sourceName} changed to maxHeight:${maxHeight}`);\n                    this._propagateSendMaxFrameHeight(sourceName);\n                }\n            }\n        } else if (this._senderVideoConstraints?.idealHeight !== videoConstraints.idealHeight) {\n            this._senderVideoConstraints = videoConstraints;\n            this._propagateSendMaxFrameHeight();\n        }\n    }\n\n    /**\n     * Figures out the send video constraint as specified by {@link _selectSendMaxFrameHeight} and sets it on all media\n     * sessions for the reasons mentioned in this class description.\n     *\n     * @param {string} sourceName - The source for which sender constraints have changed.\n     * @returns {Promise<void[]>}\n     * @private\n     */\n    _propagateSendMaxFrameHeight(sourceName = null) {\n        if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {\n            throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');\n        }\n        const sendMaxFrameHeight = this._selectSendMaxFrameHeight(sourceName);\n        const promises = [];\n\n        if (sendMaxFrameHeight >= 0) {\n            for (const session of this._conference.getMediaSessions()) {\n                promises.push(session.setSenderVideoConstraint(sendMaxFrameHeight, sourceName));\n            }\n        }\n\n        return Promise.all(promises);\n    }\n\n    /**\n     * Selects the lowest common value for the local video send constraint by looking at local user's preference and\n     * the active media session's receive preference set by the remote party.\n     *\n     * @param {string} sourceName - The source for which sender constraints have changed.\n     * @returns {number|undefined}\n     * @private\n     */\n    _selectSendMaxFrameHeight(sourceName = null) {\n        if (FeatureFlags.isSourceNameSignalingEnabled() && !sourceName) {\n            throw new Error('sourceName missing for calculating the sendMaxHeight for video tracks');\n        }\n        const activeMediaSession = this._conference.getActiveMediaSession();\n        const remoteRecvMaxFrameHeight = activeMediaSession\n            ? activeMediaSession.isP2P\n                ? activeMediaSession.getRemoteRecvMaxFrameHeight()\n                : sourceName ? this._sourceSenderConstraints.get(sourceName) : this._senderVideoConstraints?.idealHeight\n            : undefined;\n\n        if (this._preferredSendMaxFrameHeight >= 0 && remoteRecvMaxFrameHeight >= 0) {\n            return Math.min(this._preferredSendMaxFrameHeight, remoteRecvMaxFrameHeight);\n        } else if (remoteRecvMaxFrameHeight >= 0) {\n            return remoteRecvMaxFrameHeight;\n        }\n\n        return this._preferredSendMaxFrameHeight;\n    }\n\n    /**\n     * Sets local preference for max send video frame height.\n     *\n     * @param {number} maxFrameHeight - the new value to set.\n     * @returns {Promise<void[]>} - resolved when the operation is complete.\n     */\n    setPreferredSendMaxFrameHeight(maxFrameHeight) {\n        this._preferredSendMaxFrameHeight = maxFrameHeight;\n\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            const promises = [];\n\n            for (const sourceName of this._sourceSenderConstraints.keys()) {\n                promises.push(this._propagateSendMaxFrameHeight(sourceName));\n            }\n\n            return Promise.allSettled(promises);\n        }\n\n        return this._propagateSendMaxFrameHeight();\n    }\n}\n","/**\n * A collection of utility functions for taking in XML and parsing it to return\n * certain values.\n */\nexport default {\n    /**\n     * Parses the presence update of the focus and returns an object with the\n     * statuses related to recording.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {Object} The current presence values related to recording.\n     */\n    getFocusRecordingUpdate(presence) {\n        const jibriStatus = presence\n            && presence.getElementsByTagName('jibri-recording-status')[0];\n\n        if (!jibriStatus) {\n            return;\n        }\n\n        return {\n            error: jibriStatus.getAttribute('failure_reason'),\n            initiator: jibriStatus.getAttribute('initiator'),\n            recordingMode: jibriStatus.getAttribute('recording_mode'),\n            sessionID: jibriStatus.getAttribute('session_id'),\n            status: jibriStatus.getAttribute('status')\n        };\n    },\n\n    /**\n     * Parses the presence update from a hidden domain participant and returns\n     * an object with the statuses related to recording.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {Object} The current presence values related to recording.\n     */\n    getHiddenDomainUpdate(presence) {\n        const liveStreamViewURLContainer\n            = presence.getElementsByTagName('live-stream-view-url')[0];\n        const liveStreamViewURL = liveStreamViewURLContainer\n            && liveStreamViewURLContainer.textContent;\n        const modeContainer\n            = presence.getElementsByTagName('mode')[0];\n        const mode = modeContainer\n            && modeContainer.textContent\n            && modeContainer.textContent.toLowerCase();\n        const sessionIDContainer\n            = presence.getElementsByTagName('session_id')[0];\n        const sessionID\n            = sessionIDContainer && sessionIDContainer.textContent;\n\n        return {\n            liveStreamViewURL,\n            mode,\n            sessionID\n        };\n    },\n\n    /**\n     * Returns the recording session ID from a successful IQ.\n     *\n     * @param {Node} response - The response from the IQ.\n     * @returns {string} The session ID of the recording session.\n     */\n    getSessionIdFromIq(response) {\n        const jibri = response && response.getElementsByTagName('jibri')[0];\n\n        return jibri && jibri.getAttribute('session_id');\n    },\n\n    /**\n     * Returns the recording session ID from a presence, if it exists.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {string|undefined} The session ID of the recording session.\n     */\n    getSessionId(presence) {\n        const sessionIdContainer\n            = presence.getElementsByTagName('session_id')[0];\n        const sessionId = sessionIdContainer && sessionIdContainer.textContent;\n\n        return sessionId;\n    },\n\n    /**\n     * Returns whether or not a presence is from the focus.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {boolean} True if the presence is from the focus.\n     */\n    isFromFocus(presence) {\n        return presence.getAttribute('from').includes('focus');\n    }\n};\n","import { $iq } from 'strophe.js';\n\nimport recordingXMLUtils from './recordingXMLUtils';\n\n/**\n * Represents a recording session.\n */\nexport default class JibriSession {\n    /**\n     * Initializes a new JibriSession instance.\n     *\n     * @constructor\n     */\n    constructor(options = {}) {\n        this._connection = options.connection;\n        this._mode = options.mode;\n\n        this._setSessionID(options.sessionID);\n        this.setStatus(options.status);\n    }\n\n    /**\n     * Returns the error related to the session instance, if any.\n     *\n     * @returns {string|undefined}\n     */\n    getError() {\n        return this._error;\n    }\n\n    /**\n     * Returns the session ID of the session instance.\n     *\n     * @returns {string|undefined}\n     */\n    getID() {\n        return this._sessionID;\n    }\n\n    /**\n     * Returns the initiator of the session instance.\n     *\n     * @returns {JitsiParticipant|string} The participant that started the session.\n     */\n    getInitiator() {\n        return this._initiator;\n    }\n\n    /**\n     * Returns the streaming URL of the session.\n     *\n     * @returns {string|undefined}\n     */\n    getLiveStreamViewURL() {\n        return this._liveStreamViewURL;\n    }\n\n    /**\n     * Returns the current status of the session.\n     *\n     * @returns {string|undefined}\n     */\n    getStatus() {\n        return this._status;\n    }\n\n    /**\n     * Returns the jid of the participant that stopped the session.\n     *\n     * @returns {JitsiParticipant|string} The participant that stopped the session.\n     */\n    getTerminator() {\n        return this._terminator;\n    }\n\n    /**\n     * Returns the current recording mode of the session, such as \"file\".\n     *\n     * @returns {string}\n     */\n    getMode() {\n        return this._mode;\n    }\n\n    /**\n     * Sets the last known error message related to the session.\n     *\n     * @param {string} error - The error string explaining why the session\n     * entered an error state.\n     * @returns {void}\n     */\n    setError(error) {\n        this._error = error;\n    }\n\n    /**\n     * Sets the last live stream URL for the session instance. Usually this is\n     * a YouTube URL and usually this is only set for \"stream\" sessions.\n     *\n     * @param {string} url - The live stream URL associated with the session.\n     * @returns {void}\n     */\n    setLiveStreamViewURL(url) {\n        this._liveStreamViewURL = url;\n    }\n\n    /**\n     * Sets the last known status for this recording session.\n     *\n     * @param {string} status - The new status to set.\n     * @returns {void}\n     */\n    setStatus(status) {\n        this._status = status;\n    }\n\n    /**\n     * Sets the participant that started the session.\n     * @param {JitsiParticipant | string} participant - The participant or resource id\n     * if local participant.\n     */\n    setInitiator(participant) {\n        this._initiator = participant;\n    }\n\n    /**\n     * Sets the participant that stopped the session.\n     * @param {JitsiParticipant | string} participant - The participant or the resource id\n     * if local participant.\n     */\n    setTerminator(participant) {\n        this._terminator = participant;\n    }\n\n    /**\n     * Sends a message to start the actual recording.\n     *\n     * @param {Object} options - Additional arguments for starting the\n     * recording.\n     * @param {string} [options.appData] - Data specific to the app/service that\n     * the result file will be uploaded.\n     * @param {string} [options.broadcastId] - The broadcast ID of an\n     * associated YouTube stream, used for knowing the URL from which the stream\n     * can be viewed.\n     * @param {string} options.focusMucJid - The JID of the focus participant\n     * that controls recording.\n     * @param {streamId} options.streamId - Necessary for live streaming, this\n     * is the stream key needed to start a live streaming session with the\n     * streaming service provider.\n     * @returns Promise\n     */\n    start({ appData, broadcastId, focusMucJid, streamId }) {\n        return new Promise((resolve, reject) => {\n            this._connection.sendIQ(\n                this._createIQ({\n                    action: 'start',\n                    appData,\n                    focusMucJid,\n                    broadcastId,\n                    streamId\n                }),\n                result => {\n                    // All users will eventually receive the 'pending' status\n                    // from the backend, but for the user initiating the session\n                    // it's better to give some instant feedback that recording\n                    // is starting so fire 'pending' here manually.\n                    this.setStatus('pending');\n                    this._setSessionID(\n                        recordingXMLUtils.getSessionIdFromIq(result));\n\n                    resolve();\n                },\n                error => {\n                    this._setErrorFromIq(error);\n\n                    reject(error);\n                });\n        });\n    }\n\n    /**\n     * Sends a message to actually stop the recording session.\n     *\n     * @param {Object} options - Additional arguments for stopping the\n     * recording.\n     * @param {Object} options.focusMucJid - The JID of the focus participant\n     * that controls recording.\n     * @returns Promise\n     */\n    stop({ focusMucJid }) {\n        return new Promise((resolve, reject) => {\n            this._connection.sendIQ(\n                this._createIQ({\n                    action: 'stop',\n                    focusMucJid\n                }),\n                resolve,\n                reject);\n        });\n    }\n\n    /**\n     * Generates the message to change the status of the recording session.\n     *\n     * @param {string} status - The new status to which the recording session\n     * should transition.\n     * @param {string} [options.appData] - Data specific to the app/service that\n     * the result file will be uploaded.\n     * @param {string} [options.broadcastId] - The broadcast ID of an\n     * associated YouTube stream, used for knowing the URL from which the stream\n     * can be viewed.\n     * @param {string} options.focusMucJid - The JID of the focus participant\n     * that controls recording.\n     * @param {streamId} options.streamId - Necessary for live streaming, this\n     * is the stream key needed to start a live streaming session with the\n     * streaming service provider.\n     * @returns Object - The XMPP IQ message.\n     */\n    _createIQ({ action, appData, broadcastId, focusMucJid, streamId }) {\n        return $iq({\n            to: focusMucJid,\n            type: 'set'\n        })\n        .c('jibri', {\n            'xmlns': 'http://jitsi.org/protocol/jibri',\n            'action': action,\n            'app_data': appData,\n            'recording_mode': this._mode,\n            'streamid': streamId,\n            'you_tube_broadcast_id': broadcastId\n        })\n        .up();\n    }\n\n    /**\n     * Handles the error from an iq and stores the error.\n     *\n     * @param {Node} errorIq - The error response from an Iq.\n     * @private\n     * @returns {void}\n     */\n    _setErrorFromIq(errorIq) {\n        const error = errorIq.getElementsByTagName('error')[0];\n\n        this.setError(error.children[0].tagName);\n    }\n\n    /**\n     * Sets the known session ID for this recording session.\n     *\n     * @param {string} sessionID\n     * @private\n     * @returns {void}\n     */\n    _setSessionID(sessionID) {\n        this._sessionID = sessionID;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nimport JibriSession from './JibriSession';\nimport recordingXMLUtils from './recordingXMLUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * A class responsible for starting and stopping recording sessions and emitting\n * state updates for them.\n */\nclass RecordingManager {\n    /**\n     * Initialize {@code RecordingManager} with other objects that are necessary\n     * for starting a recording.\n     *\n     * @param {ChatRoom} chatRoom - The chat room to handle.\n     * @returns {void}\n     */\n    constructor(chatRoom) {\n        /**\n         * All known recording sessions from the current conference.\n         */\n        this._sessions = {};\n\n        this._chatRoom = chatRoom;\n\n        this.onPresence = this.onPresence.bind(this);\n\n        this._chatRoom.eventEmitter.addListener(\n            XMPPEvents.PRESENCE_RECEIVED, this.onPresence);\n    }\n\n    /**\n     * Finds an existing recording session by session ID.\n     *\n     * @param {string} sessionID - The session ID associated with the recording.\n     * @returns {JibriSession|undefined}\n     */\n    getSession(sessionID) {\n        return this._sessions[sessionID];\n    }\n\n    /**\n     * Callback to invoke to parse through a presence update to find recording\n     * related updates (from Jibri participant doing the recording and the\n     * focus which controls recording).\n     *\n     * @param {Object} event - The presence data from the pubsub event.\n     * @param {Node} event.presence - An XMPP presence update.\n     * @param {boolean} event.fromHiddenDomain - Whether or not the update comes\n     * from a participant that is trusted but not visible, as would be the case\n     * with the Jibri recorder participant.\n     * @returns {void}\n     */\n    onPresence({ fromHiddenDomain, presence }) {\n        if (recordingXMLUtils.isFromFocus(presence)) {\n            this._handleFocusPresence(presence);\n        } else if (fromHiddenDomain) {\n            this._handleJibriPresence(presence);\n        }\n    }\n\n    /**\n     * Start a recording session.\n     *\n     * @param {Object} options - Configuration for the recording.\n     * @param {string} [options.appData] - Data specific to the app/service that\n     * the result file will be uploaded.\n     * @param {string} [optional] options.broadcastId - The channel on which a\n     * live stream will occur.\n     * @param {string} options.mode - The mode in which recording should be\n     * started. Recognized values are \"file\" and \"stream\".\n     * @param {string} [optional] options.streamId - The stream key to be used\n     * for live stream broadcasting. Required for live streaming.\n     * @returns {Promise} A promise for starting a recording, which will pass\n     * back the session on success. The promise resolves after receiving an\n     * acknowledgment of the start request success or fail.\n     */\n    startRecording(options) {\n        const session = new JibriSession({\n            ...options,\n            connection: this._chatRoom.connection\n        });\n\n        return session.start({\n            appData: options.appData,\n            broadcastId: options.broadcastId,\n            focusMucJid: this._chatRoom.focusMucJid,\n            streamId: options.streamId\n        })\n            .then(() => {\n                // Only store the session and emit if the session has not been\n                // added already. This is a workaround for the session getting\n                // created due to a presence update to announce a \"pending\"\n                // recording being received before JibriSession#start finishes.\n                if (!this.getSession(session.getID())) {\n                    this._addSession(session);\n                    this._emitSessionUpdate(session);\n                }\n\n                return session;\n            })\n            .catch(error => {\n                this._emitSessionUpdate(session);\n\n                return Promise.reject(error);\n            });\n    }\n\n    /**\n     * Stop a recording session.\n     *\n     * @param {string} sessionID - The ID associated with the recording session\n     * to be stopped.\n     * @returns {Promise} The promise resolves after receiving an\n     * acknowledgment of the stop request success or fail.\n     */\n    stopRecording(sessionID) {\n        const session = this.getSession(sessionID);\n\n        if (session) {\n            return session.stop({ focusMucJid: this._chatRoom.focusMucJid });\n        }\n\n        return Promise.reject(new Error('Could not find session'));\n    }\n\n    /**\n     * Stores a reference to the passed in JibriSession.\n     *\n     * @param {string} session - The JibriSession instance to store.\n     * @returns {void}\n     */\n    _addSession(session) {\n        this._sessions[session.getID()] = session;\n    }\n\n    /**\n     * Create a new instance of a recording session and stores a reference to\n     * it.\n     *\n     * @param {string} sessionID - The session ID of the recording in progress.\n     * @param {string} status - The current status of the recording session.\n     * @param {string} mode - The recording mode of the session.\n     * @returns {JibriSession}\n     */\n    _createSession(sessionID, status, mode) {\n        const session = new JibriSession({\n            connection: this._chatRoom.connection,\n            focusMucJid: this._chatRoom.focusMucJid,\n            mode,\n            sessionID,\n            status\n        });\n\n        this._addSession(session);\n\n        return session;\n    }\n\n    /**\n     * Notifies listeners of an update to a recording session.\n     *\n     * @param {JibriSession} session - The session that has been updated.\n     * @param {string|undefined} initiator - The jid of the initiator of the update.\n     */\n    _emitSessionUpdate(session, initiator) {\n        this._chatRoom.eventEmitter.emit(\n            XMPPEvents.RECORDER_STATE_CHANGED, session, initiator);\n    }\n\n    /**\n     * Parses presence to update an existing JibriSession or to create a new\n     * JibriSession.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {void}\n     */\n    _handleFocusPresence(presence) {\n        const jibriStatus = recordingXMLUtils.getFocusRecordingUpdate(presence);\n\n        if (!jibriStatus) {\n            return;\n        }\n\n        const { error, initiator, recordingMode, sessionID, status } = jibriStatus;\n\n        // We'll look for an existing session or create one (in case we're a\n        // participant joining a call with an existing recording going on).\n        let session = this.getSession(sessionID);\n\n        // Handle the case where a status update is received in presence but\n        // the local participant has joined while the JibriSession has already\n        // ended.\n        if (!session && status === 'off') {\n            logger.warn(\n                'Ignoring recording presence update',\n                'Received a new session with status off.');\n\n            return;\n        }\n\n        // Jicofo sends updates via presence, and any extension in presence\n        // is sent until it is explicitly removed.  It's difficult for\n        // Jicofo to know when a presence has been sent once, so it won't\n        // remove jibri status extension.  This means we may receive the same\n        // status update more than once, so check for that here\n        if (session\n            && session.getStatus() === status\n            && session.getError() === error) {\n            logger.warn('Ignoring duplicate presence update: ',\n                JSON.stringify(jibriStatus));\n\n            return;\n        }\n\n        if (!session) {\n            session = this._createSession(sessionID, status, recordingMode);\n        }\n\n        session.setStatus(status);\n\n        if (error) {\n            session.setError(error);\n        }\n\n        this._emitSessionUpdate(session, initiator);\n    }\n\n    /**\n     * Handles updates from the Jibri which can broadcast a YouTube URL that\n     * needs to be updated in a JibriSession.\n     *\n     * @param {Node} presence - An XMPP presence update.\n     * @returns {void}\n     */\n    _handleJibriPresence(presence) {\n        const { liveStreamViewURL, mode, sessionID }\n            = recordingXMLUtils.getHiddenDomainUpdate(presence);\n\n        if (!sessionID) {\n            logger.warn(\n                'Ignoring potential jibri presence due to no session id.');\n\n            return;\n        }\n\n        let session = this.getSession(sessionID);\n\n        if (!session) {\n            session = this._createSession(sessionID, '', mode);\n        }\n\n        session.setLiveStreamViewURL(liveStreamViewURL);\n\n        this._emitSessionUpdate(session);\n    }\n}\n\nexport default RecordingManager;\n","import { getLogger } from '@jitsi/logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport * as ConnectionQualityEvents from '../../service/connectivity/ConnectionQualityEvents';\nimport { createAudioOutputProblemEvent } from '../../service/statistics/AnalyticsEvents';\n\nimport Statistics from './statistics';\n\nconst logger = getLogger(__filename);\n\n/**\n * Number of local samples that will be used for comparison before and after the remote sample is received.\n */\nconst NUMBER_OF_LOCAL_SAMPLES = 2;\n\n/**\n * Collects the average audio levels per participant from the local stats and the stats received by every remote\n * participant and compares them to detect potential audio problem for a participant.\n */\nexport default class AudioOutputProblemDetector {\n\n    /**\n     * Creates new <tt>AudioOutputProblemDetector</tt> instance.\n     *\n     * @param {JitsiCofnerence} conference - The conference instance to be monitored.\n     */\n    constructor(conference) {\n        this._conference = conference;\n        this._localAudioLevelCache = {};\n        this._reportedParticipants = [];\n        this._audioProblemCandidates = {};\n        this._numberOfRemoteAudioLevelsReceived = {};\n        this._onLocalAudioLevelsReport = this._onLocalAudioLevelsReport.bind(this);\n        this._onRemoteAudioLevelReceived = this._onRemoteAudioLevelReceived.bind(this);\n        this._clearUserData = this._clearUserData.bind(this);\n        this._conference.on(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);\n        this._conference.statistics.addConnectionStatsListener(this._onLocalAudioLevelsReport);\n        this._conference.on(ConferenceEvents.USER_LEFT, this._clearUserData);\n    }\n\n    /**\n     * A listener for audio level data received by a remote participant.\n     *\n     * @param {string} userID - The user id of the participant that sent the data.\n     * @param {number} audioLevel - The average audio level value.\n     * @returns {void}\n     */\n    _onRemoteAudioLevelReceived(userID, { avgAudioLevels }) {\n        const numberOfReports = (this._numberOfRemoteAudioLevelsReceived[userID] + 1) || 0;\n\n        this._numberOfRemoteAudioLevelsReceived[userID] = numberOfReports;\n\n        if (this._reportedParticipants.indexOf(userID) !== -1 || (userID in this._audioProblemCandidates)\n                || avgAudioLevels <= 0 || numberOfReports < 3) {\n            return;\n        }\n\n        const participant = this._conference.getParticipantById(userID);\n\n        if (participant) {\n            const tracks = participant.getTracksByMediaType(MediaType.AUDIO);\n\n            if (tracks.length > 0 && participant.isAudioMuted()) {\n                // We don't need to report an error if everything seems fine with the participant and its tracks but\n                // the participant is audio muted. Since those are average audio levels we potentially can receive non\n                // zero values for muted track.\n                return;\n            }\n        }\n\n        const localAudioLevels = this._localAudioLevelCache[userID];\n\n        if (!Array.isArray(localAudioLevels) || localAudioLevels.every(audioLevel => audioLevel === 0)) {\n            this._audioProblemCandidates[userID] = {\n                remoteAudioLevels: avgAudioLevels,\n                localAudioLevels: []\n            };\n        }\n    }\n\n    /**\n     * A listener for audio level data retrieved by the local stats.\n     *\n     * @param {TraceablePeerConnection} tpc - The <tt>TraceablePeerConnection</tt> instance used to gather the data.\n     * @param {Object} avgAudioLevels - The average audio levels per participant.\n     * @returns {void}\n     */\n    _onLocalAudioLevelsReport(tpc, { avgAudioLevels }) {\n        if (tpc !== this._conference.getActivePeerConnection()) {\n            return;\n        }\n\n        Object.keys(avgAudioLevels).forEach(userID => {\n            if (this._reportedParticipants.indexOf(userID) !== -1) {\n                return;\n            }\n\n            const localAudioLevels = this._localAudioLevelCache[userID];\n\n            if (!Array.isArray(localAudioLevels)) {\n                this._localAudioLevelCache[userID] = [ ];\n            } else if (localAudioLevels.length >= NUMBER_OF_LOCAL_SAMPLES) {\n                localAudioLevels.shift();\n            }\n\n            this._localAudioLevelCache[userID].push(avgAudioLevels[userID]);\n        });\n\n\n        Object.keys(this._audioProblemCandidates).forEach(userID => {\n            const { localAudioLevels, remoteAudioLevels } = this._audioProblemCandidates[userID];\n\n            localAudioLevels.push(avgAudioLevels[userID]);\n\n            if (localAudioLevels.length === NUMBER_OF_LOCAL_SAMPLES) {\n                if (localAudioLevels.every(audioLevel => typeof audioLevel === 'undefined' || audioLevel === 0)) {\n                    const localAudioLevelsString = JSON.stringify(localAudioLevels);\n\n                    Statistics.sendAnalytics(\n                        createAudioOutputProblemEvent(userID, localAudioLevelsString, remoteAudioLevels));\n                    logger.warn(`A potential problem is detected with the audio output for participant ${\n                        userID}, local audio levels: ${localAudioLevelsString}, remote audio levels: ${\n                        remoteAudioLevels}`);\n                    this._reportedParticipants.push(userID);\n                    this._clearUserData(userID);\n                }\n\n                delete this._audioProblemCandidates[userID];\n            }\n        });\n    }\n\n    /**\n     * Clears the data stored for a participant.\n     *\n     * @param {string} userID - The id of the participant.\n     * @returns {void}\n     */\n    _clearUserData(userID) {\n        delete this._localAudioLevelCache[userID];\n    }\n\n    /**\n     * Disposes the allocated resources.\n     *\n     * @returns {void}\n     */\n    dispose() {\n        this._conference.off(ConnectionQualityEvents.REMOTE_STATS_UPDATED, this._onRemoteAudioLevelReceived);\n        this._conference.off(ConferenceEvents.USER_LEFT, this._clearUserData);\n        this._conference.statistics.removeConnectionStatsListener(this._onLocalAudioLevelsReport);\n        this._localAudioLevelCache = undefined;\n        this._audioProblemCandidates = undefined;\n        this._reportedParticipants = undefined;\n        this._numberOfRemoteAudioLevelsReceived = undefined;\n        this._conference = undefined;\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport isEqual from 'lodash.isequal';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport { MediaType } from '../../service/RTC/MediaType';\nimport * as VideoType from '../../service/RTC/VideoType';\nimport * as ConnectionQualityEvents\n    from '../../service/connectivity/ConnectionQualityEvents';\nimport {\n    createRtpStatsEvent,\n    createTransportStatsEvent\n} from '../../service/statistics/AnalyticsEvents';\nimport browser from '../browser';\n\nimport Statistics from './statistics';\n\n\nconst logger = getLogger(__filename);\n\n/**\n * This will calculate an average for one, named stat and submit it to\n * the analytics module when requested. It automatically counts the samples.\n */\nclass AverageStatReport {\n    /**\n     * Creates new <tt>AverageStatReport</tt> for given name.\n     * @param {string} name that's the name of the event that will be reported\n     * to the analytics module.\n     */\n    constructor(name) {\n        this.name = name;\n        this.count = 0;\n        this.sum = 0;\n        this.samples = [];\n    }\n\n    /**\n     * Adds the next value that will be included in the average when\n     * {@link calculate} is called.\n     * @param {number} nextValue\n     */\n    addNext(nextValue) {\n        if (typeof nextValue !== 'number') {\n            logger.error(\n                `${this.name} - invalid value for idx: ${this.count}`,\n                nextValue);\n        } else if (!isNaN(nextValue)) {\n            this.sum += nextValue;\n            this.samples.push(nextValue);\n            this.count += 1;\n        }\n    }\n\n    /**\n     * Calculates an average for the samples collected using {@link addNext}.\n     * @return {number|NaN} an average of all collected samples or <tt>NaN</tt>\n     * if no samples were collected.\n     */\n    calculate() {\n        return this.sum / this.count;\n    }\n\n    /**\n     * Appends the report to the analytics \"data\" object. The object will be\n     * set under <tt>prefix</tt> + {@link this.name} key.\n     * @param {Object} report the analytics \"data\" object\n     */\n    appendReport(report) {\n        report[`${this.name}_avg`] = this.calculate();\n        report[`${this.name}_samples`] = JSON.stringify(this.samples);\n    }\n\n    /**\n     * Clears all memory of any samples collected, so that new average can be\n     * calculated using this instance.\n     */\n    reset() {\n        this.samples = [];\n        this.sum = 0;\n        this.count = 0;\n    }\n}\n\n/**\n * Class gathers the stats that are calculated and reported for a\n * {@link TraceablePeerConnection} even if it's not currently active. For\n * example we want to monitor RTT for the JVB connection while in P2P mode.\n */\nclass ConnectionAvgStats {\n    /**\n     * Creates new <tt>ConnectionAvgStats</tt>\n     * @param {AvgRTPStatsReporter} avgRtpStatsReporter\n     * @param {boolean} isP2P\n     * @param {number} n the number of samples, before arithmetic mean is to be\n     * calculated and values submitted to the analytics module.\n     */\n    constructor(avgRtpStatsReporter, isP2P, n) {\n        /**\n         * Is this instance for JVB or P2P connection ?\n         * @type {boolean}\n         */\n        this.isP2P = isP2P;\n\n        /**\n         * How many samples are to be included in arithmetic mean calculation.\n         * @type {number}\n         * @private\n         */\n        this._n = n;\n\n        /**\n         * The current sample index. Starts from 0 and goes up to {@link _n})\n         * when analytics report will be submitted.\n         * @type {number}\n         * @private\n         */\n        this._sampleIdx = 0;\n\n        /**\n         * Average round trip time reported by the ICE candidate pair.\n         * @type {AverageStatReport}\n         */\n        this._avgRTT = new AverageStatReport('rtt');\n\n        /**\n         * Map stores average RTT to the JVB reported by remote participants.\n         * Mapped per participant id {@link JitsiParticipant.getId}.\n         *\n         * This is used only when {@link ConnectionAvgStats.isP2P} equals to\n         * <tt>false</tt>.\n         *\n         * @type {Map<string,AverageStatReport>}\n         * @private\n         */\n        this._avgRemoteRTTMap = new Map();\n\n        /**\n         * The conference for which stats will be collected and reported.\n         * @type {JitsiConference}\n         * @private\n         */\n        this._avgRtpStatsReporter = avgRtpStatsReporter;\n\n        /**\n         * The latest average E2E RTT for the JVB connection only.\n         *\n         * This is used only when {@link ConnectionAvgStats.isP2P} equals to\n         * <tt>false</tt>.\n         *\n         * @type {number}\n         */\n        this._avgEnd2EndRTT = undefined;\n\n        this._onConnectionStats = (tpc, stats) => {\n            if (this.isP2P === tpc.isP2P) {\n                this._calculateAvgStats(stats);\n            }\n        };\n\n        const conference = avgRtpStatsReporter._conference;\n\n        conference.statistics.addConnectionStatsListener(\n            this._onConnectionStats);\n\n        if (!this.isP2P) {\n            this._onUserLeft = id => this._avgRemoteRTTMap.delete(id);\n            conference.on(ConferenceEvents.USER_LEFT, this._onUserLeft);\n\n            this._onRemoteStatsUpdated\n                = (id, data) => this._processRemoteStats(id, data);\n            conference.on(\n                ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n                this._onRemoteStatsUpdated);\n        }\n    }\n\n    /**\n     * Processes next batch of stats.\n     * @param {go figure} data\n     * @private\n     */\n    _calculateAvgStats(data) {\n        if (!data) {\n            logger.error('No stats');\n\n            return;\n        }\n\n        if (browser.supportsRTTStatistics()) {\n            if (data.transport && data.transport.length) {\n                this._avgRTT.addNext(data.transport[0].rtt);\n            }\n        }\n\n        this._sampleIdx += 1;\n\n        if (this._sampleIdx >= this._n) {\n            if (browser.supportsRTTStatistics()) {\n                const conference = this._avgRtpStatsReporter._conference;\n\n                const batchReport = {\n                    p2p: this.isP2P,\n                    'conference_size': conference.getParticipantCount()\n                };\n\n                if (data.transport && data.transport.length) {\n                    Object.assign(batchReport, {\n                        'local_candidate_type':\n                            data.transport[0].localCandidateType,\n                        'remote_candidate_type':\n                            data.transport[0].remoteCandidateType,\n                        'transport_type': data.transport[0].type\n                    });\n                }\n\n                this._avgRTT.appendReport(batchReport);\n\n                if (this.isP2P) {\n                    // Report RTT diff only for P2P.\n                    const jvbEnd2EndRTT = this\n                        ._avgRtpStatsReporter.jvbStatsMonitor._avgEnd2EndRTT;\n\n                    if (!isNaN(jvbEnd2EndRTT)) {\n                        // eslint-disable-next-line dot-notation\n                        batchReport['rtt_diff']\n                            = this._avgRTT.calculate() - jvbEnd2EndRTT;\n                    }\n                } else {\n                    // Report end to end RTT only for JVB.\n                    const avgRemoteRTT = this._calculateAvgRemoteRTT();\n                    const avgLocalRTT = this._avgRTT.calculate();\n\n                    this._avgEnd2EndRTT = avgLocalRTT + avgRemoteRTT;\n\n                    if (!isNaN(avgLocalRTT) && !isNaN(avgRemoteRTT)) {\n                        // eslint-disable-next-line dot-notation\n                        batchReport['end2end_rtt_avg'] = this._avgEnd2EndRTT;\n                    }\n                }\n\n                Statistics.sendAnalytics(createRtpStatsEvent(batchReport));\n            }\n\n            this._resetAvgStats();\n        }\n    }\n\n    /**\n     * Calculates arithmetic mean of all RTTs towards the JVB reported by\n     * participants.\n     * @return {number|NaN} NaN if not available (not enough data)\n     * @private\n     */\n    _calculateAvgRemoteRTT() {\n        let count = 0, sum = 0;\n\n        // FIXME should we ignore RTT for participant\n        // who \"is having connectivity issues\" ?\n        for (const remoteAvg of this._avgRemoteRTTMap.values()) {\n            const avg = remoteAvg.calculate();\n\n            if (!isNaN(avg)) {\n                sum += avg;\n                count += 1;\n                remoteAvg.reset();\n            }\n        }\n\n        return sum / count;\n    }\n\n    /**\n     * Processes {@link ConnectionQualityEvents.REMOTE_STATS_UPDATED} to analyse\n     * RTT towards the JVB reported by each participant.\n     * @param {string} id {@link JitsiParticipant.getId}\n     * @param {go figure in ConnectionQuality.js} data\n     * @private\n     */\n    _processRemoteStats(id, data) {\n        const validData = typeof data.jvbRTT === 'number';\n        let rttAvg = this._avgRemoteRTTMap.get(id);\n\n        if (!rttAvg && validData) {\n            rttAvg = new AverageStatReport(`${id}_stat_rtt`);\n            this._avgRemoteRTTMap.set(id, rttAvg);\n        }\n\n        if (validData) {\n            rttAvg.addNext(data.jvbRTT);\n        } else if (rttAvg) {\n            this._avgRemoteRTTMap.delete(id);\n        }\n    }\n\n    /**\n     * Reset cache of all averages and {@link _sampleIdx}.\n     * @private\n     */\n    _resetAvgStats() {\n        this._avgRTT.reset();\n        if (this._avgRemoteRTTMap) {\n            this._avgRemoteRTTMap.clear();\n        }\n        this._sampleIdx = 0;\n    }\n\n    /**\n     *\n     */\n    dispose() {\n\n        const conference = this._avgRtpStatsReporter._conference;\n\n        conference.statistics.removeConnectionStatsListener(\n            this._onConnectionStats);\n        if (!this.isP2P) {\n            conference.off(\n                ConnectionQualityEvents.REMOTE_STATS_UPDATED,\n                this._onRemoteStatsUpdated);\n            conference.off(\n                ConferenceEvents.USER_LEFT,\n                this._onUserLeft);\n        }\n    }\n}\n\n/**\n * Reports average RTP statistics values (arithmetic mean) to the analytics\n * module for things like bit rate, bandwidth, packet loss etc. It keeps track\n * of the P2P vs JVB conference modes and submits the values under different\n * namespaces (the events for P2P mode have 'p2p.' prefix). Every switch between\n * P2P mode resets the data collected so far and averages are calculated from\n * scratch.\n */\nexport default class AvgRTPStatsReporter {\n    /**\n     * Creates new instance of <tt>AvgRTPStatsReporter</tt>\n     * @param {JitsiConference} conference\n     * @param {number} n the number of samples, before arithmetic mean is to be\n     * calculated and values submitted to the analytics module.\n     */\n    constructor(conference, n) {\n        /**\n         * How many {@link ConnectionQualityEvents.LOCAL_STATS_UPDATED} samples\n         * are to be included in arithmetic mean calculation.\n         * @type {number}\n         * @private\n         */\n        this._n = n;\n\n        if (n > 0) {\n            logger.info(`Avg RTP stats will be calculated every ${n} samples`);\n        } else {\n            logger.info('Avg RTP stats reports are disabled.');\n\n            // Do not initialize\n            return;\n        }\n\n        /**\n         * The current sample index. Starts from 0 and goes up to {@link _n})\n         * when analytics report will be submitted.\n         * @type {number}\n         * @private\n         */\n        this._sampleIdx = 0;\n\n        /**\n         * The conference for which stats will be collected and reported.\n         * @type {JitsiConference}\n         * @private\n         */\n        this._conference = conference;\n\n        /**\n         * Average audio upload bitrate\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgAudioBitrateUp\n            = new AverageStatReport('bitrate_audio_upload');\n\n        /**\n         * Average audio download bitrate\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgAudioBitrateDown\n            = new AverageStatReport('bitrate_audio_download');\n\n        /**\n         * Average video upload bitrate\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgVideoBitrateUp\n            = new AverageStatReport('bitrate_video_upload');\n\n        /**\n         * Average video download bitrate\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgVideoBitrateDown\n            = new AverageStatReport('bitrate_video_download');\n\n        /**\n         * Average upload bandwidth\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgBandwidthUp\n            = new AverageStatReport('bandwidth_upload');\n\n        /**\n         * Average download bandwidth\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgBandwidthDown\n            = new AverageStatReport('bandwidth_download');\n\n        /**\n         * Average total packet loss\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgPacketLossTotal\n            = new AverageStatReport('packet_loss_total');\n\n        /**\n         * Average upload packet loss\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgPacketLossUp\n            = new AverageStatReport('packet_loss_upload');\n\n        /**\n         * Average download packet loss\n         * XXX What are the units?\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgPacketLossDown\n            = new AverageStatReport('packet_loss_download');\n\n        /**\n         * Average FPS for remote videos\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgRemoteFPS = new AverageStatReport('framerate_remote');\n\n        /**\n         * Average FPS for remote screen streaming videos (reported only if not\n         * a <tt>NaN</tt>).\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgRemoteScreenFPS\n            = new AverageStatReport('framerate_screen_remote');\n\n        /**\n         * Average FPS for local video (camera)\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgLocalFPS = new AverageStatReport('framerate_local');\n\n        /**\n         * Average FPS for local screen streaming video (reported only if not\n         * a <tt>NaN</tt>).\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgLocalScreenFPS\n            = new AverageStatReport('framerate_screen_local');\n\n        /**\n         * Average pixels for remote screen streaming videos (reported only if\n         * not a <tt>NaN</tt>).\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgRemoteCameraPixels\n            = new AverageStatReport('pixels_remote');\n\n        /**\n         * Average pixels for remote screen streaming videos (reported only if\n         * not a <tt>NaN</tt>).\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgRemoteScreenPixels\n            = new AverageStatReport('pixels_screen_remote');\n\n        /**\n         * Average pixels for local video (camera)\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgLocalCameraPixels\n            = new AverageStatReport('pixels_local');\n\n        /**\n         * Average pixels for local screen streaming video (reported only if not\n         * a <tt>NaN</tt>).\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgLocalScreenPixels\n            = new AverageStatReport('pixels_screen_local');\n\n        /**\n         * Average connection quality as defined by\n         * the {@link ConnectionQuality} module.\n         * @type {AverageStatReport}\n         * @private\n         */\n        this._avgCQ = new AverageStatReport('connection_quality');\n\n        this._cachedTransportStats = undefined;\n\n        this._onLocalStatsUpdated = data => {\n            this._calculateAvgStats(data);\n            this._maybeSendTransportAnalyticsEvent(data);\n        };\n        conference.on(\n            ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n            this._onLocalStatsUpdated);\n\n        this._onP2PStatusChanged = () => {\n            logger.debug('Resetting average stats calculation');\n            this._resetAvgStats();\n            this.jvbStatsMonitor._resetAvgStats();\n            this.p2pStatsMonitor._resetAvgStats();\n        };\n        conference.on(\n            ConferenceEvents.P2P_STATUS,\n            this._onP2PStatusChanged);\n\n        this._onJvb121StatusChanged = (oldStatus, newStatus) => {\n            // We want to reset only on the transition from false => true,\n            // because otherwise those stats are resetted on JVB <=> P2P\n            // transition.\n            if (newStatus === true) {\n                logger.info('Resetting JVB avg RTP stats');\n                this._resetAvgJvbStats();\n            }\n        };\n        conference.on(\n            ConferenceEvents.JVB121_STATUS,\n            this._onJvb121StatusChanged);\n\n        this.jvbStatsMonitor\n            = new ConnectionAvgStats(this, false /* JVB */, n);\n\n        this.p2pStatsMonitor\n            = new ConnectionAvgStats(this, true /* P2P */, n);\n    }\n\n    /**\n     * Processes next batch of stats reported on\n     * {@link ConnectionQualityEvents.LOCAL_STATS_UPDATED}.\n     * @param {go figure} data\n     * @private\n     */\n    _calculateAvgStats(data) {\n\n        if (!data) {\n            logger.error('No stats');\n\n            return;\n        }\n\n        const isP2P = this._conference.isP2PActive();\n        const confSize = this._conference.getParticipantCount();\n\n        if (!isP2P && confSize < 2) {\n\n            // There's no point in collecting stats for a JVB conference of 1.\n            // That happens for short period of time after everyone leaves\n            // the room, until Jicofo terminates the session.\n            return;\n        }\n\n        /* Uncomment to figure out stats structure\n        for (const key in data) {\n            if (data.hasOwnProperty(key)) {\n                logger.info(`local stat ${key}: `, data[key]);\n            }\n        } */\n\n        const bitrate = data.bitrate;\n        const bandwidth = data.bandwidth;\n        const packetLoss = data.packetLoss;\n        const frameRate = data.framerate;\n        const resolution = data.resolution;\n\n        if (!bitrate) {\n            logger.error('No \"bitrate\"');\n\n            return;\n        } else if (!bandwidth) {\n            logger.error('No \"bandwidth\"');\n\n            return;\n        } else if (!packetLoss) {\n            logger.error('No \"packetloss\"');\n\n            return;\n        } else if (!frameRate) {\n            logger.error('No \"framerate\"');\n\n            return;\n        } else if (!resolution) {\n            logger.error('No resolution');\n\n            return;\n        }\n\n        this._avgAudioBitrateUp.addNext(bitrate.audio.upload);\n        this._avgAudioBitrateDown.addNext(bitrate.audio.download);\n\n        this._avgVideoBitrateUp.addNext(bitrate.video.upload);\n        this._avgVideoBitrateDown.addNext(bitrate.video.download);\n\n        if (browser.supportsBandwidthStatistics()) {\n            this._avgBandwidthUp.addNext(bandwidth.upload);\n            this._avgBandwidthDown.addNext(bandwidth.download);\n        }\n\n        this._avgPacketLossUp.addNext(packetLoss.upload);\n        this._avgPacketLossDown.addNext(packetLoss.download);\n        this._avgPacketLossTotal.addNext(packetLoss.total);\n\n        this._avgCQ.addNext(data.connectionQuality);\n\n        if (frameRate) {\n            this._avgRemoteFPS.addNext(\n                this._calculateAvgVideoFps(\n                    frameRate, false /* remote */, VideoType.CAMERA));\n            this._avgRemoteScreenFPS.addNext(\n                this._calculateAvgVideoFps(\n                    frameRate, false /* remote */, VideoType.DESKTOP));\n\n            this._avgLocalFPS.addNext(\n                this._calculateAvgVideoFps(\n                    frameRate, true /* local */, VideoType.CAMERA));\n            this._avgLocalScreenFPS.addNext(\n                this._calculateAvgVideoFps(\n                    frameRate, true /* local */, VideoType.DESKTOP));\n        }\n\n        if (resolution) {\n            this._avgRemoteCameraPixels.addNext(\n                this._calculateAvgVideoPixels(\n                    resolution, false /* remote */, VideoType.CAMERA));\n\n            this._avgRemoteScreenPixels.addNext(\n                this._calculateAvgVideoPixels(\n                    resolution, false /* remote */, VideoType.DESKTOP));\n\n            this._avgLocalCameraPixels.addNext(\n                this._calculateAvgVideoPixels(\n                    resolution, true /* local */, VideoType.CAMERA));\n\n            this._avgLocalScreenPixels.addNext(\n                this._calculateAvgVideoPixels(\n                    resolution, true /* local */, VideoType.DESKTOP));\n        }\n\n        this._sampleIdx += 1;\n\n        if (this._sampleIdx >= this._n) {\n\n            const batchReport = {\n                p2p: isP2P,\n                'conference_size': confSize\n            };\n\n            if (data.transport && data.transport.length) {\n                Object.assign(batchReport, {\n                    'local_candidate_type':\n                        data.transport[0].localCandidateType,\n                    'remote_candidate_type':\n                        data.transport[0].remoteCandidateType,\n                    'transport_type': data.transport[0].type\n                });\n            }\n\n            this._avgAudioBitrateUp.appendReport(batchReport);\n            this._avgAudioBitrateDown.appendReport(batchReport);\n\n            this._avgVideoBitrateUp.appendReport(batchReport);\n            this._avgVideoBitrateDown.appendReport(batchReport);\n\n            if (browser.supportsBandwidthStatistics()) {\n                this._avgBandwidthUp.appendReport(batchReport);\n                this._avgBandwidthDown.appendReport(batchReport);\n            }\n            this._avgPacketLossUp.appendReport(batchReport);\n            this._avgPacketLossDown.appendReport(batchReport);\n            this._avgPacketLossTotal.appendReport(batchReport);\n\n            this._avgRemoteFPS.appendReport(batchReport);\n            if (!isNaN(this._avgRemoteScreenFPS.calculate())) {\n                this._avgRemoteScreenFPS.appendReport(batchReport);\n            }\n            this._avgLocalFPS.appendReport(batchReport);\n            if (!isNaN(this._avgLocalScreenFPS.calculate())) {\n                this._avgLocalScreenFPS.appendReport(batchReport);\n            }\n\n            this._avgRemoteCameraPixels.appendReport(batchReport);\n            if (!isNaN(this._avgRemoteScreenPixels.calculate())) {\n                this._avgRemoteScreenPixels.appendReport(batchReport);\n            }\n            this._avgLocalCameraPixels.appendReport(batchReport);\n            if (!isNaN(this._avgLocalScreenPixels.calculate())) {\n                this._avgLocalScreenPixels.appendReport(batchReport);\n            }\n\n            this._avgCQ.appendReport(batchReport);\n\n            Statistics.sendAnalytics(createRtpStatsEvent(batchReport));\n\n            this._resetAvgStats();\n        }\n    }\n\n    /**\n     * Calculates average number of pixels for the report\n     *\n     * @param {map} peerResolutions a map of peer resolutions\n     * @param {boolean} isLocal if the average is to be calculated for the local\n     * video or <tt>false</tt> if for remote videos.\n     * @param {VideoType} videoType\n     * @return {number|NaN} average number of pixels or <tt>NaN</tt> if there\n     * are no samples.\n     * @private\n     */\n    _calculateAvgVideoPixels(peerResolutions, isLocal, videoType) {\n        let peerPixelsSum = 0;\n        let peerCount = 0;\n        const myID = this._conference.myUserId();\n\n        for (const peerID of Object.keys(peerResolutions)) {\n            if (isLocal ? peerID === myID : peerID !== myID) {\n                const participant\n                    = isLocal\n                        ? null\n                        : this._conference.getParticipantById(peerID);\n                const videosResolution = peerResolutions[peerID];\n\n                // Do not continue without participant for non local peerID\n                if ((isLocal || participant) && videosResolution) {\n                    const peerAvgPixels = this._calculatePeerAvgVideoPixels(\n                        videosResolution, participant, videoType);\n\n                    if (!isNaN(peerAvgPixels)) {\n                        peerPixelsSum += peerAvgPixels;\n                        peerCount += 1;\n                    }\n                }\n            }\n        }\n\n        return peerPixelsSum / peerCount;\n    }\n\n    /**\n     * Calculate average pixels for either remote or local participant\n     * @param {object} videos maps resolution per video SSRC\n     * @param {JitsiParticipant|null} participant remote participant or\n     * <tt>null</tt> for local video pixels calculation.\n     * @param {VideoType} videoType the type of the video for which an average\n     * will be calculated.\n     * @return {number|NaN} average video pixels of all participant's videos or\n     * <tt>NaN</tt> if currently not available\n     * @private\n     */\n    _calculatePeerAvgVideoPixels(videos, participant, videoType) {\n        let ssrcs = Object.keys(videos).map(ssrc => Number(ssrc));\n        let videoTracks = null;\n\n        // NOTE that this method is supposed to be called for the stats\n        // received from the current peerconnection.\n        const tpc = this._conference.getActivePeerConnection();\n\n        if (participant) {\n            videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n            if (videoTracks) {\n                ssrcs\n                    = ssrcs.filter(\n                        ssrc => videoTracks.find(\n                            track =>\n                                !track.isMuted()\n                                    && track.getSSRC() === ssrc\n                                    && track.videoType === videoType));\n            }\n        } else {\n            videoTracks = this._conference.getLocalTracks(MediaType.VIDEO);\n            ssrcs\n                = ssrcs.filter(\n                    ssrc => videoTracks.find(\n                        track =>\n                            !track.isMuted()\n                                && tpc.getLocalSSRC(track) === ssrc\n                                && track.videoType === videoType));\n        }\n\n        let peerPixelsSum = 0;\n        let peerSsrcCount = 0;\n\n        for (const ssrc of ssrcs) {\n            const peerSsrcPixels\n                = Number(videos[ssrc].height) * Number(videos[ssrc].width);\n\n            // FPS is reported as 0 for users with no video\n            if (!isNaN(peerSsrcPixels) && peerSsrcPixels > 0) {\n                peerPixelsSum += peerSsrcPixels;\n                peerSsrcCount += 1;\n            }\n        }\n\n        return peerPixelsSum / peerSsrcCount;\n    }\n\n\n    /**\n     * Calculates average FPS for the report\n     * @param {go figure} frameRate\n     * @param {boolean} isLocal if the average is to be calculated for the local\n     * video or <tt>false</tt> if for remote videos.\n     * @param {VideoType} videoType\n     * @return {number|NaN} average FPS or <tt>NaN</tt> if there are no samples.\n     * @private\n     */\n    _calculateAvgVideoFps(frameRate, isLocal, videoType) {\n        let peerFpsSum = 0;\n        let peerCount = 0;\n        const myID = this._conference.myUserId();\n\n        for (const peerID of Object.keys(frameRate)) {\n            if (isLocal ? peerID === myID : peerID !== myID) {\n                const participant\n                    = isLocal\n                        ? null : this._conference.getParticipantById(peerID);\n                const videosFps = frameRate[peerID];\n\n                // Do not continue without participant for non local peerID\n                if ((isLocal || participant) && videosFps) {\n                    const peerAvgFPS\n                        = this._calculatePeerAvgVideoFps(\n                            videosFps, participant, videoType);\n\n                    if (!isNaN(peerAvgFPS)) {\n                        peerFpsSum += peerAvgFPS;\n                        peerCount += 1;\n                    }\n                }\n            }\n        }\n\n        return peerFpsSum / peerCount;\n    }\n\n    /**\n     * Calculate average FPS for either remote or local participant\n     * @param {object} videos maps FPS per video SSRC\n     * @param {JitsiParticipant|null} participant remote participant or\n     * <tt>null</tt> for local FPS calculation.\n     * @param {VideoType} videoType the type of the video for which an average\n     * will be calculated.\n     * @return {number|NaN} average FPS of all participant's videos or\n     * <tt>NaN</tt> if currently not available\n     * @private\n     */\n    _calculatePeerAvgVideoFps(videos, participant, videoType) {\n        let ssrcs = Object.keys(videos).map(ssrc => Number(ssrc));\n        let videoTracks = null;\n\n        // NOTE that this method is supposed to be called for the stats\n        // received from the current peerconnection.\n        const tpc = this._conference.getActivePeerConnection();\n\n        if (participant) {\n            videoTracks = participant.getTracksByMediaType(MediaType.VIDEO);\n            if (videoTracks) {\n                ssrcs\n                    = ssrcs.filter(\n                        ssrc => videoTracks.find(\n                            track => !track.isMuted()\n                                && track.getSSRC() === ssrc\n                                && track.videoType === videoType));\n            }\n        } else {\n            videoTracks = this._conference.getLocalTracks(MediaType.VIDEO);\n            ssrcs\n                = ssrcs.filter(\n                    ssrc => videoTracks.find(\n                        track => !track.isMuted()\n                            && tpc.getLocalSSRC(track) === ssrc\n                            && track.videoType === videoType));\n        }\n\n        let peerFpsSum = 0;\n        let peerSsrcCount = 0;\n\n        for (const ssrc of ssrcs) {\n            const peerSsrcFps = Number(videos[ssrc]);\n\n            // FPS is reported as 0 for users with no video\n            if (!isNaN(peerSsrcFps) && peerSsrcFps > 0) {\n                peerFpsSum += peerSsrcFps;\n                peerSsrcCount += 1;\n            }\n        }\n\n        return peerFpsSum / peerSsrcCount;\n    }\n\n    /**\n     * Sends the 'transport.stats' analytics event whenever we detect that\n     * there is a change in the local or remote candidate type on the transport\n     * that is currently selected.\n     * @param {*} data\n     * @private\n     */\n    _maybeSendTransportAnalyticsEvent(data) {\n        if (!data || !data.transport || !data.transport.length) {\n            return;\n        }\n        const transportStats = {\n            p2p: data.transport[0].p2p,\n            'local_candidate_type': data.transport[0].localCandidateType,\n            'remote_candidate_type': data.transport[0].remoteCandidateType,\n            'transport_type': data.transport[0].type\n        };\n\n        if (!this._cachedTransportStats || !isEqual(transportStats, this._cachedTransportStats)) {\n            this._cachedTransportStats = transportStats;\n            Statistics.sendAnalytics(createTransportStatsEvent(transportStats));\n        }\n    }\n\n    /**\n     * Resets the stats related to JVB connection. Must not be called when in\n     * P2P mode, because then the {@link AverageStatReport} instances are\n     * tracking P2P stats. Note that this should never happen unless something\n     * is wrong with the P2P and JVB121 events.\n     * @private\n     */\n    _resetAvgJvbStats() {\n        this._resetAvgStats();\n        this.jvbStatsMonitor._resetAvgStats();\n    }\n\n    /**\n     * Reset cache of all averages and {@link _sampleIdx}.\n     * @private\n     */\n    _resetAvgStats() {\n        this._avgAudioBitrateUp.reset();\n        this._avgAudioBitrateDown.reset();\n\n        this._avgVideoBitrateUp.reset();\n        this._avgVideoBitrateDown.reset();\n\n        this._avgBandwidthUp.reset();\n        this._avgBandwidthDown.reset();\n\n        this._avgPacketLossUp.reset();\n        this._avgPacketLossDown.reset();\n        this._avgPacketLossTotal.reset();\n\n        this._avgRemoteFPS.reset();\n        this._avgRemoteScreenFPS.reset();\n        this._avgLocalFPS.reset();\n        this._avgLocalScreenFPS.reset();\n\n        this._avgRemoteCameraPixels.reset();\n        this._avgRemoteScreenPixels.reset();\n        this._avgLocalCameraPixels.reset();\n        this._avgLocalScreenPixels.reset();\n\n        this._avgCQ.reset();\n\n        this._sampleIdx = 0;\n    }\n\n    /**\n     * Unregisters all event listeners and stops working.\n     */\n    dispose() {\n        this._conference.off(\n            ConferenceEvents.P2P_STATUS,\n            this._onP2PStatusChanged);\n        this._conference.off(\n            ConnectionQualityEvents.LOCAL_STATS_UPDATED,\n            this._onLocalStatsUpdated);\n        this._conference.off(\n            ConferenceEvents.JVB121_STATUS,\n            this._onJvb121StatusChanged);\n        this.jvbStatsMonitor.dispose();\n        this.p2pStatsMonitor.dispose();\n    }\n}\n","import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nimport SpeakerStats from './SpeakerStats';\n\n/**\n * A collection for tracking speaker stats. Attaches listeners\n * to the conference to automatically update on tracked events.\n */\nexport default class SpeakerStatsCollector {\n    /**\n     * Initializes a new SpeakerStatsCollector instance.\n     *\n     * @constructor\n     * @param {JitsiConference} conference - The conference to track.\n     * @returns {void}\n     */\n    constructor(conference) {\n        this.stats = {\n            users: {\n\n                // userId: SpeakerStats\n            },\n            dominantSpeakerId: null\n        };\n\n        const userId = conference.myUserId();\n\n        this.stats.users[userId] = new SpeakerStats(userId, null, true);\n        this.conference = conference;\n\n        conference.addEventListener(\n            JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED,\n            this._onDominantSpeaker.bind(this));\n        conference.addEventListener(\n            JitsiConferenceEvents.USER_JOINED,\n            this._onUserJoin.bind(this));\n        conference.addEventListener(\n            JitsiConferenceEvents.USER_LEFT,\n            this._onUserLeave.bind(this));\n        conference.addEventListener(\n            JitsiConferenceEvents.DISPLAY_NAME_CHANGED,\n            this._onDisplayNameChange.bind(this));\n        conference.addEventListener(\n            JitsiConferenceEvents.FACIAL_EXPRESSION_ADDED,\n            this._onFacialExpressionAdd.bind(this));\n        if (conference.xmpp) {\n            conference.xmpp.addListener(\n                XMPPEvents.SPEAKER_STATS_RECEIVED,\n                this._updateStats.bind(this));\n        }\n    }\n\n    /**\n     * Reacts to dominant speaker change events by changing its speaker stats\n     * models to reflect the current dominant speaker.\n     *\n     * @param {string} dominantSpeakerId - The user id of the new\n     * dominant speaker.\n     * @returns {void}\n     * @private\n     */\n    _onDominantSpeaker(dominantSpeakerId) {\n        const oldDominantSpeaker\n            = this.stats.users[this.stats.dominantSpeakerId];\n        const newDominantSpeaker = this.stats.users[dominantSpeakerId];\n\n        oldDominantSpeaker && oldDominantSpeaker.setDominantSpeaker(false);\n        newDominantSpeaker && newDominantSpeaker.setDominantSpeaker(true);\n        this.stats.dominantSpeakerId = dominantSpeakerId;\n    }\n\n    /**\n     * Reacts to user join events by creating a new SpeakerStats model.\n     *\n     * @param {string} userId - The user id of the new user.\n     * @param {JitsiParticipant} - The JitsiParticipant model for the new user.\n     * @returns {void}\n     * @private\n     */\n    _onUserJoin(userId, participant) {\n        if (participant.isHidden()) {\n            return;\n        }\n\n        if (!this.stats.users[userId]) {\n            this.stats.users[userId] = new SpeakerStats(userId, participant.getDisplayName());\n        }\n    }\n\n    /**\n     * Reacts to user leave events by updating the associated user's\n     * SpeakerStats model.\n     *\n     * @param {string} userId - The user id of the user that left.\n     * @returns {void}\n     * @private\n     */\n    _onUserLeave(userId) {\n        const savedUser = this.stats.users[userId];\n\n        if (savedUser) {\n            savedUser.markAsHasLeft();\n        }\n    }\n\n    /**\n     * Reacts to user name change events by updating the last known name\n     * tracked in the associated SpeakerStats model.\n     *\n     * @param {string} userId - The user id of the user that left.\n     * @returns {void}\n     * @private\n     */\n    _onDisplayNameChange(userId, newName) {\n        const savedUser = this.stats.users[userId];\n\n        if (savedUser) {\n            savedUser.setDisplayName(newName);\n        }\n    }\n\n    /**\n     * Adds a new facial expression with its duration of a remote user.\n     *\n     * @param {string} userId - The user id of the user that left.\n     * @param {Object} data - The facial expression with its duration.\n     * @returns {void}\n     * @private\n     */\n    _onFacialExpressionAdd(userId, data) {\n        const savedUser = this.stats.users[userId];\n\n        if (savedUser) {\n            savedUser.addFacialExpression(data.facialExpression, data.duration);\n        }\n    }\n\n    /**\n     * Return a copy of the tracked SpeakerStats models.\n     *\n     * @returns {Object} The keys are the user ids and the values are the\n     * associated user's SpeakerStats model.\n     */\n    getStats() {\n        return this.stats.users;\n    }\n\n    /**\n     * Updates of the current stats is requested, passing the new values.\n     *\n     * @param {Object} newStats - The new values used to update current one.\n     * @private\n     */\n    _updateStats(newStats) {\n        for (const userId in newStats) { // eslint-disable-line guard-for-in\n            let speakerStatsToUpdate;\n            const newParticipant = this.conference.getParticipantById(userId);\n\n            // we want to ignore hidden participants\n            if (!newParticipant || !newParticipant.isHidden()) {\n                if (this.stats.users[userId]) {\n                    speakerStatsToUpdate = this.stats.users[userId];\n\n                    if (!speakerStatsToUpdate.getDisplayName()) {\n                        speakerStatsToUpdate\n                            .setDisplayName(newStats[userId].displayName);\n                    }\n                } else {\n                    speakerStatsToUpdate = new SpeakerStats(\n                        userId, newStats[userId].displayName);\n                    this.stats.users[userId] = speakerStatsToUpdate;\n                    speakerStatsToUpdate.markAsHasLeft();\n                }\n            }\n\n            speakerStatsToUpdate.totalDominantSpeakerTime\n                = newStats[userId].totalDominantSpeakerTime;\n\n            speakerStatsToUpdate.setFacialExpressions(newStats[userId].facialExpressions);\n        }\n    }\n}\n","/* eslint-disable max-params */\n\n/**\n * This object stores variables needed around the recording of an audio stream\n * and passing this recording along with additional information along to\n * different processes\n */\nexport default class RecordingResult {\n    /**\n     * @param blob the recording audio stream as a single blob\n     * @param name the name of the person of the audio stream\n     * @param startTime the time in UTC when recording of the audiostream started\n     * @param wordArray the recorder audio stream transcribed as an array of Word objects\n     */\n    constructor(blob, name, startTime, wordArray) {\n        this.blob = blob;\n        this.name = name;\n        this.startTime = startTime;\n        this.wordArray = wordArray;\n    }\n}\n","/**\n * A TrackRecorder object holds all the information needed for recording a\n * single JitsiTrack (either remote or local)\n * @param track The JitsiTrack the object is going to hold\n */\nexport default class TrackRecorder {\n    /**\n     * @param track The JitsiTrack the object is going to hold\n     */\n    constructor(track) {\n        // The JitsiTrack holding the stream\n        this.track = track;\n\n        // The MediaRecorder recording the stream\n        this.recorder = null;\n\n        // The array of data chunks recorded from the stream\n        // acts as a buffer until the data is stored on disk\n        this.data = null;\n\n        // the name of the person of the JitsiTrack. This can be undefined and/or\n        // not unique\n        this.name = null;\n\n        // the time of the start of the recording\n        this.startTime = null;\n    }\n}\n","import RecordingResult from './recordingResult';\nimport TrackRecorder from './trackRecorder';\n\n/**\n * Possible audio formats MIME types\n */\nconst AUDIO_WEBM = 'audio/webm'; // Supported in chrome\nconst AUDIO_OGG = 'audio/ogg'; // Supported in firefox\n\n/**\n * Starts the recording of a JitsiTrack in a TrackRecorder object.\n * This will also define the timestamp and try to update the name\n * @param trackRecorder the TrackRecorder to start\n */\nfunction startRecorder(trackRecorder) {\n    if (trackRecorder.recorder === undefined) {\n        throw new Error('Passed an object to startRecorder which is not a '\n            + 'TrackRecorder object');\n    }\n    trackRecorder.recorder.start();\n    trackRecorder.startTime = new Date();\n}\n\n/**\n * Stops the recording of a JitsiTrack in a TrackRecorder object.\n * This will also try to update the name\n * @param trackRecorder the TrackRecorder to stop\n */\nfunction stopRecorder(trackRecorder) {\n    if (trackRecorder.recorder === undefined) {\n        throw new Error('Passed an object to stopRecorder which is not a '\n            + 'TrackRecorder object');\n    }\n    trackRecorder.recorder.stop();\n}\n\n/**\n * Determines which kind of audio recording the browser supports\n * chrome supports \"audio/webm\" and firefox supports \"audio/ogg\"\n */\nfunction determineCorrectFileType() {\n    if (MediaRecorder.isTypeSupported(AUDIO_WEBM)) {\n        return AUDIO_WEBM;\n    } else if (MediaRecorder.isTypeSupported(AUDIO_OGG)) {\n        return AUDIO_OGG;\n    }\n    throw new Error(\n        'unable to create a MediaRecorder with the right mimetype!');\n}\n\n/**\n * main exported object of the file, holding all\n * relevant functions and variables for the outside world\n * @param jitsiConference the jitsiConference which this object\n * is going to record\n */\nfunction AudioRecorder(jitsiConference) {\n    // array of TrackRecorders, where each trackRecorder\n    // holds the JitsiTrack, MediaRecorder and recorder data\n    this.recorders = [];\n\n    // get which file type is supported by the current browser\n    this.fileType = determineCorrectFileType();\n\n    // boolean flag for active recording\n    this.isRecording = false;\n\n    // the jitsiconference the object is recording\n    this.jitsiConference = jitsiConference;\n}\n\n/**\n * Add the exported module so that it can be accessed by other files\n */\nAudioRecorder.determineCorrectFileType = determineCorrectFileType;\n\n/**\n * Adds a new TrackRecorder object to the array.\n *\n * @param track the track potentially holding an audio stream\n */\nAudioRecorder.prototype.addTrack = function(track) {\n    if (track.isAudioTrack()) {\n        // create the track recorder\n        const trackRecorder = this.instantiateTrackRecorder(track);\n\n        // push it to the local array of all recorders\n\n        this.recorders.push(trackRecorder);\n\n        // update the name of the trackRecorders\n        this.updateNames();\n\n        // If we're already recording, immediately start recording this new\n        // track.\n        if (this.isRecording) {\n            startRecorder(trackRecorder);\n        }\n    }\n};\n\n/**\n * Creates a TrackRecorder object. Also creates the MediaRecorder and\n * data array for the trackRecorder.\n * @param track the JitsiTrack holding the audio MediaStream(s)\n */\nAudioRecorder.prototype.instantiateTrackRecorder = function(track) {\n    const trackRecorder = new TrackRecorder(track);\n\n    // Create a new stream which only holds the audio track\n    const originalStream = trackRecorder.track.getOriginalStream();\n    const stream = new MediaStream();\n\n    originalStream.getAudioTracks().forEach(t => stream.addTrack(t));\n\n    // Create the MediaRecorder\n    trackRecorder.recorder = new MediaRecorder(stream,\n        { mimeType: this.fileType });\n\n    // array for holding the recorder data. Resets it when\n    // audio already has been recorder once\n    trackRecorder.data = [];\n\n    // function handling a dataEvent, e.g the stream gets new data\n    trackRecorder.recorder.ondataavailable = function(dataEvent) {\n        if (dataEvent.data.size > 0) {\n            trackRecorder.data.push(dataEvent.data);\n        }\n    };\n\n    return trackRecorder;\n};\n\n/**\n * Notifies the module that a specific track has stopped, e.g participant left\n * the conference.\n * if the recording has not started yet, the TrackRecorder will be removed from\n * the array. If the recording has started, the recorder will stop recording\n * but not removed from the array so that the recorded stream can still be\n * accessed\n *\n * @param {JitsiTrack} track the JitsiTrack to remove from the recording session\n */\nAudioRecorder.prototype.removeTrack = function(track) {\n    if (track.isVideoTrack()) {\n        return;\n    }\n\n    const array = this.recorders;\n    let i;\n\n    for (i = 0; i < array.length; i++) {\n        if (array[i].track.getParticipantId() === track.getParticipantId()) {\n            const recorderToRemove = array[i];\n\n            if (this.isRecording) {\n                stopRecorder(recorderToRemove);\n            } else {\n                // remove the TrackRecorder from the array\n                array.splice(i, 1);\n            }\n        }\n    }\n\n    // make sure the names are up to date\n    this.updateNames();\n};\n\n/**\n * Tries to update the name value of all TrackRecorder in the array.\n * If it hasn't changed,it will keep the exiting name. If it changes to a\n * undefined value, the old value will also be kept.\n */\nAudioRecorder.prototype.updateNames = function() {\n    const conference = this.jitsiConference;\n\n    this.recorders.forEach(trackRecorder => {\n        if (trackRecorder.track.isLocal()) {\n            trackRecorder.name = 'the transcriber';\n        } else {\n            const id = trackRecorder.track.getParticipantId();\n            const participant = conference.getParticipantById(id);\n            const newName = participant.getDisplayName();\n\n            if (newName !== 'undefined') {\n                trackRecorder.name = newName;\n            }\n        }\n    });\n};\n\n/**\n * Starts the audio recording of every local and remote track\n */\nAudioRecorder.prototype.start = function() {\n    if (this.isRecording) {\n        throw new Error('audiorecorder is already recording');\n    }\n\n    // set boolean isRecording flag to true so if new participants join the\n    // conference, that track can instantly start recording as well\n    this.isRecording = true;\n\n    // start all the mediaRecorders\n    this.recorders.forEach(trackRecorder => startRecorder(trackRecorder));\n\n    // log that recording has started\n    console.log(\n        `Started the recording of the audio. There are currently ${\n            this.recorders.length} recorders active.`);\n};\n\n/**\n * Stops the audio recording of every local and remote track\n */\nAudioRecorder.prototype.stop = function() {\n    // set the boolean flag to false\n    this.isRecording = false;\n\n    // stop all recorders\n    this.recorders.forEach(trackRecorder => stopRecorder(trackRecorder));\n    console.log('stopped recording');\n};\n\n/**\n * link hacking to download all recorded audio streams\n */\nAudioRecorder.prototype.download = function() {\n    this.recorders.forEach(trackRecorder => {\n        const blob = new Blob(trackRecorder.data, { type: this.fileType });\n        const url = URL.createObjectURL(blob);\n        const a = document.createElement('a');\n\n        document.body.appendChild(a);\n        a.style = 'display: none';\n        a.href = url;\n        a.download = `test.${this.fileType.split('/')[1]}`;\n        a.click();\n        window.URL.revokeObjectURL(url);\n    });\n};\n\n/**\n * returns the audio files of all recorders as an array of objects,\n * which include the name of the owner of the track and the starting time stamp\n * @returns {Array} an array of RecordingResult objects\n */\nAudioRecorder.prototype.getRecordingResults = function() {\n    if (this.isRecording) {\n        throw new Error(\n            'cannot get blobs because the AudioRecorder is still recording!');\n    }\n\n    // make sure the names are up to date before sending them off\n    this.updateNames();\n\n    const array = [];\n\n    this.recorders.forEach(\n        recorder =>\n            array.push(\n                new RecordingResult(\n                    new Blob(recorder.data, { type: this.fileType }),\n                    recorder.name,\n                    recorder.startTime)));\n\n    return array;\n};\n\n/**\n * Gets the mime type of the recorder audio\n * @returns {String} the mime type of the recorder audio\n */\nAudioRecorder.prototype.getFileType = function() {\n    return this.fileType;\n};\n\n/**\n * export the main object AudioRecorder\n */\nexport default AudioRecorder;\n","/**\n * An object representing a transcribed word, with some additional information\n * @param word the word\n * @param begin the time the word was started being uttered\n * @param end the time the word stopped being uttered\n */\nexport default class Word {\n    /**\n     * @param word the word\n     * @param begin the time the word was started being uttered\n     * @param end the time the word stopped being uttered\n     */\n    constructor(word, begin, end) {\n        this.word = word;\n        this.begin = begin;\n        this.end = end;\n    }\n\n    /**\n     * Get the string representation of the word\n     * @returns {*} the word as a string\n     */\n    getWord() {\n        return this.word;\n    }\n\n    /**\n     * Get the time the word started being uttered\n     * @returns {*} the start time as an integer\n     */\n    getBeginTime() {\n        return this.begin;\n    }\n\n    /**\n     * Get the time the word stopped being uttered\n     * @returns {*} the end time as an integer\n     */\n    getEndTime() {\n        return this.end;\n    }\n}\n","/* global config */\n\nimport Word from '../word';\n\nimport audioRecorder from './../audioRecorder';\nimport AbstractTranscriptionService from './AbstractTranscriptionService';\n\n/**\n * Implements a TranscriptionService for a Sphinx4 http server\n */\nexport default class SphinxService extends AbstractTranscriptionService {\n    /**\n     * Implements a TranscriptionService for a Sphinx4 http server\n     */\n    constructor() {\n        super();\n\n        // set the correct url\n        this.url = getURL();\n    }\n\n    /**\n     * Overrides the sendRequest method from AbstractTranscriptionService\n     * it will send the audio stream the a Sphinx4 server to get the transcription\n     *\n     * @param audioFileBlob the recorder audio stream an a single Blob\n     * @param callback the callback function retrieving the server response\n     */\n    sendRequest(audioFileBlob, callback) {\n        console.log(`sending an audio file  to ${this.url}`);\n        console.log(`the audio file being sent: ${audioFileBlob}`);\n        const request = new XMLHttpRequest();\n\n        request.onreadystatechange = function() {\n            if (request.readyState === XMLHttpRequest.DONE\n                && request.status === 200) {\n                callback(request.responseText);\n            } else if (request.readyState === XMLHttpRequest.DONE) {\n                throw new Error(\n                    `unable to accept response from sphinx server. status: ${request.status}`);\n            }\n\n            // if not ready no point to throw an error\n        };\n        request.open('POST', this.url);\n        request.setRequestHeader('Content-Type',\n            audioRecorder.determineCorrectFileType());\n        request.send(audioFileBlob);\n        console.log(`send ${audioFileBlob}`);\n    }\n\n    /**\n     * Overrides the formatResponse method from AbstractTranscriptionService\n     * It will parse the answer from the server in the expected format\n     *\n     * @param response the JSON body retrieved from the Sphinx4 server\n     */\n    formatResponse(response) {\n        const result = JSON.parse(response).objects;\n\n        // make sure to delete the session id object, which is always\n        // the first value in the JSON array\n        result.shift();\n        const array = [];\n\n        result.forEach(\n            word => word.filler\n                || array.push(new Word(word.word, word.start, word.end)));\n\n        return array;\n    }\n\n    /**\n     * checks wether the reply is empty, or doesn't contain a correct JSON object\n     * @param response the server response\n     * @return {boolean} whether the response is valid\n     */\n    verify(response) {\n        console.log(`response from server:${response.toString()}`);\n\n        // test if server responded with a string object\n        if (typeof response !== 'string') {\n            return false;\n        }\n\n        // test if the string can be parsed into valid JSON\n        let json;\n\n        try {\n            json = JSON.parse(response);\n        } catch (error) {\n            console.log(error);\n\n            return false;\n        }\n\n        // check if the JSON has a \"objects\" value\n        if (json.objects === undefined) {\n            return false;\n        }\n\n        // get the \"objects\" value and check for a session ID\n        const array = json.objects;\n\n        if (!(array[0] && array[0]['session-id'])) {\n            return false;\n        }\n\n        // everything seems to be in order\n        return true;\n    }\n}\n\n/**\n * Gets the URL to the Sphinx4 server from the config file. If it's not there,\n * it will throw an error\n *\n * @returns {string} the URL to the sphinx4 server\n */\nfunction getURL() {\n    const message = 'config does not contain an url to a Sphinx4 https server';\n\n    if (config.sphinxURL === undefined) {\n        console.log(message);\n    } else {\n        const toReturn = config.sphinxURL;\n\n        if (toReturn.includes !== undefined && toReturn.includes('https://')) {\n            return toReturn;\n        }\n        console.log(message);\n\n    }\n}\n","/**\n * Abstract class representing an interface to implement a speech-to-text\n * service on.\n */\nexport default class TranscriptionService {\n    /**\n     * Abstract class representing an interface to implement a speech-to-text\n     * service on.\n     */\n    constructor() {\n        throw new Error('TranscriptionService is abstract and cannot be created');\n    }\n\n    /**\n     * This method can be used to send the recorder audio stream and\n     * retrieve the answer from the transcription service from the callback\n     *\n     * @param {RecordingResult} recordingResult a recordingResult object which\n     * includes the recorded audio stream as a blob\n     * @param {Function} callback  which will retrieve the a RecordingResult with\n     *        the answer as a WordArray\n     */\n    send(recordingResult, callback) {\n        this.sendRequest(recordingResult.blob, response => {\n            if (this.verify(response)) {\n                recordingResult.wordArray = this.formatResponse(response);\n            } else {\n                console.log('the retrieved response from the server is not valid!');\n                recordingResult.wordArray = [];\n            }\n            callback(recordingResult);\n        });\n    }\n\n    /**\n     * Abstract method which will rend the recorder audio stream to the implemented\n     * transcription service and will retrieve an answer, which will be\n     * called on the given callback method\n     *\n     * @param {Blob} audioBlob the recorded audio stream as a single Blob\n     * @param {function} callback function which will retrieve the answer\n     *                            from the service\n     */\n    sendRequest(audioBlob, callback) { // eslint-disable-line no-unused-vars\n        throw new Error('TranscriptionService.sendRequest is abstract');\n    }\n\n    /**\n     * Abstract method which will parse the output from the implemented\n     * transcription service to the expected format\n     *\n     * The transcriber class expect an array of word objects, where each word\n     * object is one transcribed word by the service.\n     *\n     * The expected output of this method is an array of word objects, in\n     * the correct order. That is, the first object in the array is the first word\n     * being said, and the last word in the array is the last word being said\n     *\n     * @param response the answer from the speech-to-text server which needs to be\n     *                 formatted\n     * @return {Array<Word>} an array of Word objects\n     */\n    formatResponse(response) { // eslint-disable-line no-unused-vars\n        throw new Error('TranscriptionService.format is abstract');\n    }\n\n    /**\n     * Abstract method which will verify that the response from the server is valid\n     *\n     * @param response the response from the server\n     * @return {boolean} true if response is valid, false otherwise\n     */\n    verify(response) { // eslint-disable-line no-unused-vars\n        throw new Error('TranscriptionService.verify is abstract');\n    }\n}\n","import AudioRecorder from './audioRecorder';\nimport SphinxService from './transcriptionServices/SphinxTranscriptionService';\n\nconst BEFORE_STATE = 'before';\nconst RECORDING_STATE = 'recording';\nconst TRANSCRIBING_STATE = 'transcribing';\nconst FINISHED_STATE = 'finished';\n\n// the amount of characters each line in the transcription will have\nconst MAXIMUM_SENTENCE_LENGTH = 80;\n\n/**\n * This is the main object for handing the Transcription. It interacts with\n * the audioRecorder to record every person in a conference and sends the\n * recorder audio to a transcriptionService. The returned speech-to-text result\n * will be merged to create a transcript\n * @param {AudioRecorder} audioRecorder An audioRecorder recording a conference\n */\nfunction Transcriber() {\n    // the object which can record all audio in the conference\n    this.audioRecorder = new AudioRecorder();\n\n    // this object can send the recorder audio to a speech-to-text service\n    this.transcriptionService = new SphinxService();\n\n    // holds a counter to keep track if merging can start\n    this.counter = null;\n\n    // holds the date when transcription started which makes it possible\n    // to calculate the offset between recordings\n    this.startTime = null;\n\n    // will hold the transcription once it is completed\n    this.transcription = null;\n\n    // this will be a method which will be called once the transcription is done\n    // with the transcription as parameter\n    this.callback = null;\n\n    // stores all the retrieved speech-to-text results to merge together\n    // this value will store an Array<Word> object\n    this.results = [];\n\n    // Stores the current state of the transcription process\n    this.state = BEFORE_STATE;\n\n    // Used in the updateTranscription method to add a new line when the\n    // sentence becomes to long\n    this.lineLength = 0;\n}\n\n/**\n * Method to start the transcription process. It will tell the audioRecorder\n * to start storing all audio streams and record the start time for merging\n * purposes\n */\nTranscriber.prototype.start = function start() {\n    if (this.state !== BEFORE_STATE) {\n        throw new Error(\n            `The transcription can only start when it's in the \"${\n                BEFORE_STATE}\" state. It's currently in the \"${\n                this.state}\" state`);\n    }\n    this.state = RECORDING_STATE;\n    this.audioRecorder.start();\n    this.startTime = new Date();\n};\n\n/**\n * Method to stop the transcription process. It will tell the audioRecorder to\n * stop, and get all the recorded audio to send it to the transcription service\n\n * @param callback a callback which will receive the transcription\n */\nTranscriber.prototype.stop = function stop(callback) {\n    if (this.state !== RECORDING_STATE) {\n        throw new Error(\n            `The transcription can only stop when it's in the \"${\n                RECORDING_STATE}\" state. It's currently in the \"${\n                this.state}\" state`);\n    }\n\n    // stop the recording\n    console.log('stopping recording and sending audio files');\n    this.audioRecorder.stop();\n\n    // and send all recorded audio to the transcription service\n    const callBack = blobCallBack.bind(null, this);\n\n    this.audioRecorder.getRecordingResults().forEach(recordingResult => {\n        this.transcriptionService.send(recordingResult, callBack);\n        this.counter++;\n    });\n\n    // set the state to \"transcribing\" so that maybeMerge() functions correctly\n    this.state = TRANSCRIBING_STATE;\n\n    // and store the callback for later\n    this.callback = callback;\n};\n\n/**\n * This method gets the answer from the transcription service, calculates the\n * offset and adds is to every Word object. It will also start the merging\n * when every send request has been received\n *\n * note: Make sure to bind this as a Transcription object\n * @param {Transcriber} transcriber the transcriber instance\n * @param {RecordingResult} answer a RecordingResult object with a defined\n * WordArray\n */\nfunction blobCallBack(transcriber, answer) {\n    console.log(\n        'retrieved an answer from the transcription service. The answer has an'\n            + ` array of length: ${answer.wordArray.length}`);\n\n    // first add the offset between the start of the transcription and\n    // the start of the recording to all start and end times\n    if (answer.wordArray.length > 0) {\n        let offset = answer.startTime.getUTCMilliseconds()\n            - transcriber.startTime.getUTCMilliseconds();\n\n        // transcriber time will always be earlier\n\n        if (offset < 0) {\n            offset = 0; // presume 0 if it somehow not earlier\n        }\n\n        let array = '[';\n\n        answer.wordArray.forEach(wordObject => {\n            wordObject.begin += offset;\n            wordObject.end += offset;\n            array += `${wordObject.word},`;\n        });\n        array += ']';\n        console.log(array);\n\n        // give a name value to the Array object so that the merging can access\n        // the name value without having to use the whole recordingResult object\n        // in the algorithm\n        answer.wordArray.name = answer.name;\n    }\n\n    // then store the array and decrease the counter\n    transcriber.results.push(answer.wordArray);\n    transcriber.counter--;\n    console.log(`current counter: ${transcriber.counter}`);\n\n    // and check if all results have been received.\n    transcriber.maybeMerge();\n}\n\n/**\n * this method will check if the counter is zero. If it is, it will call\n * the merging method\n */\nTranscriber.prototype.maybeMerge = function() {\n    if (this.state === TRANSCRIBING_STATE && this.counter === 0) {\n        // make sure to include the events in the result arrays before\n        // merging starts\n        this.merge();\n    }\n};\n\n/**\n * This method will merge all speech-to-text arrays together in one\n * readable transcription string\n */\nTranscriber.prototype.merge = function() {\n    console.log(\n        `starting merge process!\\n The length of the array: ${\n            this.results.length}`);\n    this.transcription = '';\n\n    // the merging algorithm will look over all Word objects who are at pos 0 in\n    // every array. It will then select the one closest in time to the\n    // previously placed word, while removing the selected word from its array\n    // note: words can be skipped the skipped word's begin and end time somehow\n    // end up between the closest word start and end time\n    const arrays = this.results;\n\n    // arrays of Word objects\n    const potentialWords = []; // array of the first Word objects\n    // check if any arrays are already empty and remove them\n\n    hasPopulatedArrays(arrays);\n\n    // populate all the potential Words for a first time\n    arrays.forEach(array => pushWordToSortedArray(potentialWords, array));\n\n    // keep adding words to transcription until all arrays are exhausted\n    while (hasPopulatedArrays(arrays)) {\n        // first select the lowest array;\n        let lowestWordArray = arrays[0];\n\n        arrays.forEach(wordArray => {\n            if (wordArray[0].begin < lowestWordArray[0].begin) {\n                lowestWordArray = wordArray;\n            }\n        });\n\n        // put the word in the transcription\n        let wordToAdd = lowestWordArray.shift();\n\n        this.updateTranscription(wordToAdd, lowestWordArray.name);\n\n        // keep going until a word in another array has a smaller time\n        // or the array is empty\n        while (lowestWordArray.length > 0) {\n            let foundSmaller = false;\n            const wordToCompare = lowestWordArray[0].begin;\n\n            arrays.forEach(wordArray => {\n                if (wordArray[0].begin < wordToCompare) {\n                    foundSmaller = true;\n                }\n            });\n\n            // add next word if no smaller time has been found\n            if (foundSmaller) {\n                break;\n            }\n\n            wordToAdd = lowestWordArray.shift();\n            this.updateTranscription(wordToAdd, null);\n        }\n\n    }\n\n    // set the state to finished and do the necessary left-over tasks\n    this.state = FINISHED_STATE;\n    if (this.callback) {\n        this.callback(this.transcription);\n    }\n};\n\n/**\n * Appends a word object to the transcription. It will make a new line with a\n * name if a name is specified\n * @param {Word} word the Word object holding the word to append\n * @param {String|null} name the name of a new speaker. Null if not applicable\n */\nTranscriber.prototype.updateTranscription = function(word, name) {\n    if (name !== undefined && name !== null) {\n        this.transcription += `\\n${name}:`;\n        this.lineLength = name.length + 1; // +1 for the semi-colon\n    }\n    if (this.lineLength + word.word.length > MAXIMUM_SENTENCE_LENGTH) {\n        this.transcription += '\\n    ';\n        this.lineLength = 4; // because of the 4 spaces after the new line\n    }\n    this.transcription += ` ${word.word}`;\n    this.lineLength += word.word.length + 1; // +1 for the space\n};\n\n/**\n * Check if the given 2 dimensional array has any non-zero Word-arrays in them.\n * All zero-element arrays inside will be removed\n * If any non-zero-element arrays are found, the method will return true.\n * otherwise it will return false\n * @param {Array<Array>} twoDimensionalArray the array to check\n * @returns {boolean} true if any non-zero arrays inside, otherwise false\n */\nfunction hasPopulatedArrays(twoDimensionalArray) {\n    for (let i = 0; i < twoDimensionalArray.length; i++) {\n        if (twoDimensionalArray[i].length === 0) {\n            twoDimensionalArray.splice(i, 1);\n        }\n    }\n\n    return twoDimensionalArray.length > 0;\n}\n\n/**\n * Push a word to the right location in a sorted array. The array is sorted\n * from lowest to highest start time. Every word is stored in an object which\n * includes the name of the person saying the word.\n *\n * @param {Array<Word>} array the sorted array to push to\n * @param {Word} word the word to push into the array\n */\nfunction pushWordToSortedArray(array, word) {\n    if (array.length === 0) {\n        array.push(word);\n    } else {\n        if (array[array.length - 1].begin <= word.begin) {\n            array.push(word);\n\n            return;\n        }\n\n        for (let i = 0; i < array.length; i++) {\n            if (word.begin < array[i].begin) {\n                array.splice(i, 0, word);\n\n                return;\n            }\n        }\n        array.push(word); // fail safe\n    }\n}\n\n/**\n * Gives the transcriber a JitsiTrack holding an audioStream to transcribe.\n * The JitsiTrack is given to the audioRecorder. If it doesn't hold an\n * audiostream, it will not be added by the audioRecorder\n * @param {JitsiTrack} track the track to give to the audioRecorder\n */\nTranscriber.prototype.addTrack = function(track) {\n    this.audioRecorder.addTrack(track);\n};\n\n/**\n * Remove the given track from the auioRecorder\n * @param track\n */\nTranscriber.prototype.removeTrack = function(track) {\n    this.audioRecorder.removeTrack(track);\n};\n\n/**\n * Will return the created transcription if it's avialable or throw an error\n * when it's not done yet\n * @returns {String} the transcription as a String\n */\nTranscriber.prototype.getTranscription = function() {\n    if (this.state !== FINISHED_STATE) {\n        throw new Error(\n            `The transcription can only be retrieved when it's in the \"${\n                FINISHED_STATE}\" state. It's currently in the \"${\n                this.state}\" state`);\n    }\n\n    return this.transcription;\n};\n\n/**\n * Returns the current state of the transcription process\n */\nTranscriber.prototype.getState = function() {\n    return this.state;\n};\n\n/**\n * Resets the state to the \"before\" state, such that it's again possible to\n * call the start method\n */\nTranscriber.prototype.reset = function() {\n    this.state = BEFORE_STATE;\n    this.counter = null;\n    this.transcription = null;\n    this.startTime = null;\n    this.callback = null;\n    this.results = [];\n    this.lineLength = 0;\n};\n\nexport default Transcriber;\n","import Statistics from '../statistics/statistics';\n\nconst logger = require('@jitsi/logger').getLogger(__filename);\n\n/**\n * Creates new instance of <tt>ComponentsVersions</tt> which will be discovering\n * the versions of conferencing system components in given\n * <tt>JitsiConference</tt>.\n * @param conference <tt>JitsiConference</tt> instance which will be used to\n *        listen for focus presence updates.\n * @constructor\n */\nexport default function ComponentsVersions(conference) {\n\n    this.versions = {};\n\n    this.conference = conference;\n    this.conference.addCommandListener(\n        'versions', this.processVersions.bind(this));\n}\n\nComponentsVersions.prototype.processVersions\n    = function(versions, mucResource, mucJid) {\n        if (!this.conference.isFocus(mucJid)) {\n            logger.warn(\n                `Received versions not from the focus user: ${versions}`,\n                mucJid);\n\n            return;\n        }\n\n        const log = [];\n\n        versions.children.forEach(component => {\n\n            const name = component.attributes.name;\n            const version = component.value;\n\n            if (this.versions[name] !== version) {\n                this.versions[name] = version;\n                logger.info(`Got ${name} version: ${version}`);\n\n                log.push({\n                    id: 'component_version',\n                    component: name,\n                    version\n                });\n            }\n        });\n\n        // logs versions to stats\n        if (log.length > 0) {\n            Statistics.sendLog(JSON.stringify(log));\n        }\n    };\n\n/**\n * Obtains the version of conferencing system component.\n * @param componentName the name of the component for which we want to obtain\n *        the version.\n * @returns {String} which describes the version of the component identified by\n *          given <tt>componentName</tt> or <tt>undefined</tt> if not found.\n */\nComponentsVersions.prototype.getComponentVersion = function(componentName) {\n    return this.versions[componentName];\n};\n","export enum VideoSIPGWStatusConstants {\n    /**\n     * Status that video SIP GW service is available.\n     */\n    STATUS_AVAILABLE = 'available',\n\n    /**\n     * Status that video SIP GW service is not available.\n     */\n    STATUS_UNDEFINED = 'undefined',\n\n    /**\n     * Status that video SIP GW service is available but there are no free nodes\n     * at the moment to serve new requests.\n     */\n    STATUS_BUSY = 'busy'\n};\n\nexport enum VideoSIPGWStateConstants {\n    /**\n     * Video SIP GW session state, currently running.\n     */\n    STATE_ON = 'on',\n\n    /**\n     * Video SIP GW session state, currently stopped and not running.\n     */\n    STATE_OFF = 'off',\n\n    /**\n     * Video SIP GW session state, currently is starting.\n     */\n    STATE_PENDING = 'pending',\n\n    /**\n     * Video SIP GW session state, has observed some issues and is retrying at the\n     * moment.\n     */\n    STATE_RETRYING = 'retrying',\n\n    /**\n     * Video SIP GW session state, tried to start but it failed.\n     */\n    STATE_FAILED = 'failed'\n};\n\nexport enum VideoSIPGWErrorConstants {\n    /**\n     * Error on trying to create video SIP GW session in conference where\n     * there is no room connection (hasn't joined or has left the room).\n     */\n    ERROR_NO_CONNECTION = 'error_no_connection',\n\n    /**\n     * Error on trying to create video SIP GW session with address for which\n     * there is an already created session.\n     */\n    ERROR_SESSION_EXISTS = 'error_session_already_exists'\n};\n\n// exported for backward compatibility\nexport const STATUS_AVAILABLE = VideoSIPGWStatusConstants.STATUS_AVAILABLE;\nexport const STATUS_UNDEFINED = VideoSIPGWStatusConstants.STATUS_UNDEFINED;\nexport const STATUS_BUSY = VideoSIPGWStatusConstants.STATUS_BUSY;\n\nexport const STATE_ON = VideoSIPGWStateConstants.STATE_ON;\nexport const STATE_OFF = VideoSIPGWStateConstants.STATE_OFF;\nexport const STATE_PENDING = VideoSIPGWStateConstants.STATE_PENDING;\nexport const STATE_RETRYING = VideoSIPGWStateConstants.STATE_RETRYING;\nexport const STATE_FAILED = VideoSIPGWStateConstants.STATE_FAILED;\n\nexport const ERROR_NO_CONNECTION = VideoSIPGWErrorConstants.ERROR_NO_CONNECTION;\nexport const ERROR_SESSION_EXISTS = VideoSIPGWErrorConstants.ERROR_SESSION_EXISTS;\n","import { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport Listenable from '../util/Listenable';\n\nimport * as VideoSIPGWConstants from './VideoSIPGWConstants';\n\nconst logger = getLogger(__filename);\n\n/**\n * The event name for current sip video session state changed.\n * @type {string} event name for sip video session state changed.\n */\nconst STATE_CHANGED = 'STATE_CHANGED';\n\n/**\n * Jitsi video SIP GW session. Holding its state and able to start/stop it.\n * When session is in OFF or FAILED stated it cannot be used anymore.\n */\nexport default class JitsiVideoSIPGWSession extends Listenable {\n\n    /**\n     * Creates new session with the desired sip address and display name.\n     *\n     * @param {string} sipAddress - The sip address to use when\n     * starting the session.\n     * @param {string} displayName - The display name to use for\n     * that participant.\n     * @param {ChatRoom} chatRoom - The chat room this session is bound to.\n     */\n    constructor(sipAddress, displayName, chatRoom) {\n        super();\n\n        this.sipAddress = sipAddress;\n        this.displayName = displayName;\n        this.chatRoom = chatRoom;\n\n        /*\n         * The initial state is undefined. Initial state cannot be STATE_OFF,\n         * the session enters this state when it was in STATE_ON and was stopped\n         * and such session cannot be used anymore.\n         *\n         * @type {VideoSIPGWConstants|undefined}\n         */\n        this.state = undefined;\n    }\n\n    /**\n     * Stops the current session.\n     */\n    stop() {\n        if (this.state === VideoSIPGWConstants.STATE_OFF\n            || this.state === VideoSIPGWConstants.STATE_FAILED) {\n            logger.warn('Video SIP GW session already stopped or failed!');\n\n            return;\n        }\n\n        this._sendJibriIQ('stop');\n    }\n\n    /**\n     * Starts a new session. Sends an iq to the focus.\n     */\n    start() {\n        // if state is off, this session was active for some reason\n        // and we should create new one, rather than reusing it\n        if (this.state === VideoSIPGWConstants.STATE_ON\n            || this.state === VideoSIPGWConstants.STATE_OFF\n            || this.state === VideoSIPGWConstants.STATE_PENDING\n            || this.state === VideoSIPGWConstants.STATE_RETRYING) {\n            logger.warn('Video SIP GW session already started!');\n\n            return;\n        }\n\n        this._sendJibriIQ('start');\n    }\n\n    /**\n     * Changes the state of this session.\n     *\n     * @param {string} newState - The new {VideoSIPGWConstants} state to set.\n     * @param {string} [optional] failureReason - The reason why a failure state\n     * was entered.\n     * @returns {void}\n     */\n    setState(newState, failureReason) {\n        if (newState === this.state) {\n            return;\n        }\n\n        const oldState = this.state;\n\n        this.state = newState;\n        this.eventEmitter.emit(STATE_CHANGED,\n            {\n                address: this.sipAddress,\n                failureReason,\n                oldState,\n                newState: this.state,\n                displayName: this.displayName\n            }\n        );\n    }\n\n    /**\n     * Subscribes the passed listener to the event for state change of this\n     * session.\n     *\n     * @param {Function} listener - The function that will receive the event.\n     */\n    addStateListener(listener) {\n        this.addListener(STATE_CHANGED, listener);\n    }\n\n    /**\n     * Unsubscribes the passed handler.\n     *\n     * @param {Function} listener - The function to be removed.\n     */\n    removeStateListener(listener) {\n        this.removeListener(STATE_CHANGED, listener);\n    }\n\n    /**\n     * Sends a jibri command using an iq.\n     *\n     * @private\n     * @param {string} action - The action to send ('start' or 'stop').\n     */\n    _sendJibriIQ(action) {\n        const attributes = {\n            'xmlns': 'http://jitsi.org/protocol/jibri',\n            'action': action,\n            sipaddress: this.sipAddress\n        };\n\n        attributes.displayname = this.displayName;\n\n        const iq = $iq({\n            to: this.chatRoom.focusMucJid,\n            type: 'set' })\n            .c('jibri', attributes)\n            .up();\n\n        logger.debug(`${action} video SIP GW session`, iq.nodeTree);\n        this.chatRoom.connection.sendIQ(\n            iq,\n            () => {}, // eslint-disable-line no-empty-function\n            error => {\n                logger.error(\n                    `Failed to ${action} video SIP GW session, error: `, error);\n                this.setState(VideoSIPGWConstants.STATE_FAILED);\n            });\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nconst logger = getLogger(__filename);\n\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\n\nimport JitsiVideoSIPGWSession from './JitsiVideoSIPGWSession';\nimport * as Constants from './VideoSIPGWConstants';\n\n/**\n * Main video SIP GW handler. Stores references of all created sessions.\n */\nexport default class VideoSIPGW {\n\n    /**\n     * Creates new handler.\n     *\n     * @param {ChatRoom} chatRoom - Tha chat room to handle.\n     */\n    constructor(chatRoom) {\n        this.chatRoom = chatRoom;\n        this.eventEmitter = chatRoom.eventEmitter;\n        logger.debug('creating VideoSIPGW');\n        this.sessions = {};\n\n        this.sessionStateChangeListener = this.sessionStateChanged.bind(this);\n\n        // VideoSIPGW, JitsiConference and ChatRoom are not reusable and no\n        // more than one VideoSIPGW can be created per JitsiConference,\n        // so we don't bother to cleanup\n        chatRoom.addPresenceListener('jibri-sip-call-state',\n            this.handleJibriSIPState.bind(this));\n    }\n\n    /**\n     * Handles presence nodes with name: jibri-sip-call-state.\n     *\n     * @param {Object} node the presence node Object to handle.\n     * Object representing part of the presence received over xmpp.\n     */\n    handleJibriSIPState(node) {\n        const attributes = node.attributes;\n\n        if (!attributes) {\n            return;\n        }\n\n        logger.debug('Handle video sip gw state : ', attributes);\n\n        const newState = attributes.state;\n\n        if (newState === this.state) {\n            return;\n        }\n\n        switch (newState) {\n        case Constants.STATE_ON:\n        case Constants.STATE_OFF:\n        case Constants.STATE_PENDING:\n        case Constants.STATE_RETRYING:\n        case Constants.STATE_FAILED: {\n            const address = attributes.sipaddress;\n\n            if (!address) {\n                return;\n            }\n\n            // find the corresponding session and set its state\n            const session = this.sessions[address];\n\n            if (session) {\n                session.setState(newState, attributes.failure_reason);\n            } else {\n                logger.warn('Video SIP GW session not found:', address);\n            }\n        }\n        }\n    }\n\n    /**\n     * Creates new session and stores its reference if it does not exist or\n     * returns an error otherwise.\n     *\n     * @param {string} sipAddress - The sip address to use.\n     * @param {string} displayName - The display name to use.\n     * @returns {JitsiVideoSIPGWSession|Error}\n     */\n    createVideoSIPGWSession(sipAddress, displayName) {\n        if (this.sessions[sipAddress]) {\n            logger.warn('There was already a Video SIP GW session for address',\n                sipAddress);\n\n            return new Error(Constants.ERROR_SESSION_EXISTS);\n        }\n\n        const session = new JitsiVideoSIPGWSession(\n            sipAddress, displayName, this.chatRoom);\n\n        session.addStateListener(this.sessionStateChangeListener);\n\n        this.sessions[sipAddress] = session;\n\n        return session;\n    }\n\n    /**\n     * Listener for session state changed. When a session goes to off or failed\n     * we delete its reference.\n     *\n     * @param {options} event - { address, oldState, newState, displayName }\n     */\n    sessionStateChanged(event) {\n        const address = event.address;\n\n        if (event.newState === Constants.STATE_OFF\n            || event.newState === Constants.STATE_FAILED) {\n            const session = this.sessions[address];\n\n            if (!session) {\n                logger.error('Missing Video SIP GW session with address:',\n                    address);\n\n                return;\n            }\n\n            session.removeStateListener(this.sessionStateChangeListener);\n            delete this.sessions[address];\n        }\n\n        this.eventEmitter.emit(\n            XMPPEvents.VIDEO_SIP_GW_SESSION_STATE_CHANGED,\n            event);\n    }\n}\n","import { getLogger } from '@jitsi/logger';\nimport { Strophe } from 'strophe.js';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport SignalingLayer, { getMediaTypeFromSourceName } from '../../service/RTC/SignalingLayer';\nimport VideoType from '../../service/RTC/VideoType';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport FeatureFlags from '../flags/FeatureFlags';\n\nimport { filterNodeFromPresenceJSON } from './ChatRoom';\n\nconst logger = getLogger(__filename);\n\nexport const SOURCE_INFO_PRESENCE_ELEMENT = 'SourceInfo';\n\n/**\n * Default XMPP implementation of the {@link SignalingLayer} interface. Obtains\n * the data from the MUC presence.\n */\nexport default class SignalingLayerImpl extends SignalingLayer {\n    /**\n     * Creates new instance.\n     */\n    constructor() {\n        super();\n\n        /**\n         * A map that stores SSRCs of remote streams. And is used only locally\n         * We store the mapping when jingle is received, and later is used\n         * onaddstream webrtc event where we have only the ssrc\n         * FIXME: This map got filled and never cleaned and can grow during long\n         * conference\n         * @type {Map<number, string>} maps SSRC number to jid\n         */\n        this.ssrcOwners = new Map();\n\n        /**\n         *\n         * @type {ChatRoom|null}\n         */\n        this.chatRoom = null;\n\n        /**\n         * @type {Map<SourceName, SourceInfo>}\n         * @private\n         */\n        this._localSourceState = { };\n\n        /**\n         * @type {Map<EndpointId, Map<SourceName, SourceInfo>>}\n         * @private\n         */\n        this._remoteSourceState = { };\n\n        /**\n         * A map that stores the source name of a track identified by it's ssrc.\n         * We store the mapping when jingle is received, and later is used\n         * onaddstream webrtc event where we have only the ssrc\n         * FIXME: This map got filled and never cleaned and can grow during long\n         * conference\n         * @type {Map<number, string>} maps SSRC number to source name\n         */\n        this._sourceNames = new Map();\n    }\n\n    /**\n     * Adds <SourceInfo> element to the local presence.\n     *\n     * @returns {void}\n     * @private\n     */\n    _addLocalSourceInfoToPresence() {\n        if (this.chatRoom) {\n            return this.chatRoom.addOrReplaceInPresence(\n                SOURCE_INFO_PRESENCE_ELEMENT,\n                { value: JSON.stringify(this._localSourceState) });\n        }\n\n        return false;\n    }\n\n    /**\n     * Check is given endpoint has advertised <SourceInfo/> in it's presence which means that the source name signaling\n     * is used by this endpoint.\n     *\n     * @param {EndpointId} endpointId\n     * @returns {boolean}\n     */\n    _doesEndpointSendNewSourceInfo(endpointId) {\n        const presence = this.chatRoom?.getLastPresence(endpointId);\n\n        return Boolean(presence && presence.find(node => node.tagName === SOURCE_INFO_PRESENCE_ELEMENT));\n    }\n\n    /**\n     * Sets the <tt>ChatRoom</tt> instance used and binds presence listeners.\n     * @param {ChatRoom} room\n     */\n    setChatRoom(room) {\n        const oldChatRoom = this.chatRoom;\n\n        this.chatRoom = room;\n        if (oldChatRoom) {\n            oldChatRoom.removePresenceListener(\n                'audiomuted', this._audioMuteHandler);\n            oldChatRoom.removePresenceListener(\n                'videomuted', this._videoMuteHandler);\n            oldChatRoom.removePresenceListener(\n                'videoType', this._videoTypeHandler);\n            if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                this._sourceInfoHandler\n                    && oldChatRoom.removePresenceListener(\n                        SOURCE_INFO_PRESENCE_ELEMENT, this._sourceInfoHandler);\n                this._memberLeftHandler\n                    && oldChatRoom.removeEventListener(\n                        XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);\n            }\n        }\n        if (room) {\n            if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                this._bindChatRoomEventHandlers(room);\n                this._addLocalSourceInfoToPresence();\n            } else {\n                // TODO the logic below has been duplicated in _bindChatRoomEventHandlers, clean this up once\n                //  the new impl has been tested well enough\n                // SignalingEvents\n                this._audioMuteHandler = (node, from) => {\n                    this.eventEmitter.emit(\n                        SignalingEvents.PEER_MUTED_CHANGED,\n                        from, MediaType.AUDIO, node.value === 'true');\n                };\n                room.addPresenceListener('audiomuted', this._audioMuteHandler);\n\n                this._videoMuteHandler = (node, from) => {\n                    this.eventEmitter.emit(\n                        SignalingEvents.PEER_MUTED_CHANGED,\n                        from, MediaType.VIDEO, node.value === 'true');\n                };\n                room.addPresenceListener('videomuted', this._videoMuteHandler);\n\n                this._videoTypeHandler = (node, from) => {\n                    this.eventEmitter.emit(\n                        SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n                        from, node.value);\n                };\n                room.addPresenceListener('videoType', this._videoTypeHandler);\n            }\n        }\n    }\n\n    /**\n     * Binds event listeners to the chat room instance.\n     * @param {ChatRoom} room\n     * @private\n     * @returns {void}\n     */\n    _bindChatRoomEventHandlers(room) {\n        const emitAudioMutedEvent = (endpointId, muted) => {\n            this.eventEmitter.emit(\n                SignalingEvents.PEER_MUTED_CHANGED,\n                endpointId,\n                MediaType.AUDIO,\n                muted);\n        };\n        const emitVideoMutedEvent = (endpointId, muted) => {\n            this.eventEmitter.emit(\n                SignalingEvents.PEER_MUTED_CHANGED,\n                endpointId,\n                MediaType.VIDEO,\n                muted);\n        };\n\n        // SignalingEvents\n        this._audioMuteHandler = (node, from) => {\n            if (!this._doesEndpointSendNewSourceInfo(from)) {\n                emitAudioMutedEvent(from, node.value === 'true');\n            }\n        };\n        room.addPresenceListener('audiomuted', this._audioMuteHandler);\n\n        this._videoMuteHandler = (node, from) => {\n            if (!this._doesEndpointSendNewSourceInfo(from)) {\n                emitVideoMutedEvent(from, node.value === 'true');\n            }\n        };\n        room.addPresenceListener('videomuted', this._videoMuteHandler);\n\n        const emitVideoTypeEvent = (endpointId, videoType) => {\n            this.eventEmitter.emit(\n                SignalingEvents.PEER_VIDEO_TYPE_CHANGED,\n                endpointId, videoType);\n        };\n\n        this._videoTypeHandler = (node, from) => {\n            if (!this._doesEndpointSendNewSourceInfo(from)) {\n                emitVideoTypeEvent(from, node.value);\n            }\n        };\n\n        if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n            room.addPresenceListener('videoType', this._videoTypeHandler);\n        }\n\n        this._sourceInfoHandler = (node, mucNick) => {\n            const endpointId = mucNick;\n            const { value } = node;\n            const sourceInfoJSON = JSON.parse(value);\n            const emitEventsFromHere = this._doesEndpointSendNewSourceInfo(endpointId);\n            const endpointSourceState\n                = this._remoteSourceState[endpointId] || (this._remoteSourceState[endpointId] = {});\n\n            for (const sourceName of Object.keys(sourceInfoJSON)) {\n                const mediaType = getMediaTypeFromSourceName(sourceName);\n                const newMutedState = Boolean(sourceInfoJSON[sourceName].muted);\n                const oldSourceState = endpointSourceState[sourceName]\n                    || (endpointSourceState[sourceName] = { sourceName });\n\n                if (oldSourceState.muted !== newMutedState) {\n                    oldSourceState.muted = newMutedState;\n                    if (emitEventsFromHere && mediaType === MediaType.AUDIO) {\n                        emitAudioMutedEvent(endpointId, newMutedState);\n                    } else {\n                        emitVideoMutedEvent(endpointId, newMutedState);\n                    }\n                }\n\n                // Assume a default videoType of 'camera' for video sources.\n                const newVideoType = mediaType === MediaType.VIDEO\n                    ? sourceInfoJSON[sourceName].videoType ?? VideoType.CAMERA\n                    : undefined;\n\n                if (oldSourceState.videoType !== newVideoType) {\n                    oldSourceState.videoType = newVideoType;\n\n                    // videoType is not allowed to change on a given JitsiLocalTrack when multi stream support is\n                    // enabled.\n                    emitEventsFromHere\n                        && !FeatureFlags.isMultiStreamSupportEnabled()\n                        && emitVideoTypeEvent(endpointId, newVideoType);\n                }\n            }\n\n            // Cleanup removed source names\n            const newSourceNames = Object.keys(sourceInfoJSON);\n\n            for (const sourceName of Object.keys(endpointSourceState)) {\n                if (newSourceNames.indexOf(sourceName) === -1) {\n                    delete endpointSourceState[sourceName];\n                }\n            }\n        };\n        room.addPresenceListener('SourceInfo', this._sourceInfoHandler);\n\n        // Cleanup when participant leaves\n        this._memberLeftHandler = jid => {\n            const endpointId = Strophe.getResourceFromJid(jid);\n\n            delete this._remoteSourceState[endpointId];\n\n            if (FeatureFlags.isSourceNameSignalingEnabled()) {\n                for (const [ key, value ] of this.ssrcOwners.entries()) {\n                    if (value === endpointId) {\n                        delete this._sourceNames[key];\n                    }\n                }\n            }\n        };\n\n        room.addEventListener(XMPPEvents.MUC_MEMBER_LEFT, this._memberLeftHandler);\n    }\n\n    /**\n     * Finds the first source of given media type for the given endpoint.\n     * @param endpointId\n     * @param mediaType\n     * @returns {SourceInfo|null}\n     * @private\n     */\n    _findEndpointSourceInfoForMediaType(endpointId, mediaType) {\n        const remoteSourceState = this._remoteSourceState[endpointId];\n\n        if (!remoteSourceState) {\n            return null;\n        }\n\n        for (const sourceInfo of Object.values(remoteSourceState)) {\n            const _mediaType = getMediaTypeFromSourceName(sourceInfo.sourceName);\n\n            if (_mediaType === mediaType) {\n                return sourceInfo;\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * @inheritDoc\n     */\n    getPeerMediaInfo(owner, mediaType) {\n        const legacyGetPeerMediaInfo = () => {\n            if (this.chatRoom) {\n                return this.chatRoom.getMediaPresenceInfo(owner, mediaType);\n            }\n            logger.error('Requested peer media info, before room was set');\n        };\n\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            const lastPresence = this.chatRoom.getLastPresence(owner);\n\n            if (!lastPresence) {\n                throw new Error(`getPeerMediaInfo - no presence stored for: ${owner}`);\n            }\n\n            if (!this._doesEndpointSendNewSourceInfo(owner)) {\n                return legacyGetPeerMediaInfo();\n            }\n\n            /**\n             * @type {PeerMediaInfo}\n             */\n            const mediaInfo = {};\n            const endpointMediaSource = this._findEndpointSourceInfoForMediaType(owner, mediaType);\n\n            // The defaults are provided only, because getPeerMediaInfo is a legacy method. This will be eventually\n            // changed into a getSourceInfo method which returns undefined if there's no source. Also there will be\n            // no mediaType argument there.\n            if (mediaType === MediaType.AUDIO) {\n                mediaInfo.muted = endpointMediaSource ? endpointMediaSource.muted : true;\n            } else if (mediaType === MediaType.VIDEO) {\n                mediaInfo.muted = endpointMediaSource ? endpointMediaSource.muted : true;\n                mediaInfo.videoType = endpointMediaSource ? endpointMediaSource.videoType : undefined;\n\n                const codecTypeNode = filterNodeFromPresenceJSON(lastPresence, 'jitsi_participant_codecType');\n\n                if (codecTypeNode.length > 0) {\n                    mediaInfo.codecType = codecTypeNode[0].value;\n                }\n            } else {\n                throw new Error(`Unsupported media type: ${mediaType}`);\n            }\n\n            return mediaInfo;\n        }\n\n        return legacyGetPeerMediaInfo();\n    }\n\n    /**\n     * @inheritDoc\n     */\n    getPeerSourceInfo(owner, sourceName) {\n        return this._remoteSourceState[owner] ? this._remoteSourceState[owner][sourceName] : undefined;\n    }\n\n    /**\n     * @inheritDoc\n     */\n    getSSRCOwner(ssrc) {\n        return this.ssrcOwners.get(ssrc);\n    }\n\n    /**\n     * Set an SSRC owner.\n     * @param {number} ssrc an SSRC to be owned\n     * @param {string} endpointId owner's ID (MUC nickname)\n     * @throws TypeError if <tt>ssrc</tt> is not a number\n     */\n    setSSRCOwner(ssrc, endpointId) {\n        if (typeof ssrc !== 'number') {\n            throw new TypeError(`SSRC(${ssrc}) must be a number`);\n        }\n\n        // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely\n        // an SSRC conflict could potentially occur. Log a message to make debugging easier.\n        const existingOwner = this.ssrcOwners.get(ssrc);\n\n        if (existingOwner && existingOwner !== endpointId) {\n            logger.error(`SSRC owner re-assigned from ${existingOwner} to ${endpointId}`);\n        }\n        this.ssrcOwners.set(ssrc, endpointId);\n    }\n\n    /**\n     * Adjusts muted status of given track.\n     *\n     * @param {SourceName} sourceName - the name of the track's source.\n     * @param {boolean} muted - the new muted status.\n     * @returns {boolean}\n     */\n    setTrackMuteStatus(sourceName, muted) {\n        if (!this._localSourceState[sourceName]) {\n            this._localSourceState[sourceName] = {};\n        }\n\n        this._localSourceState[sourceName].muted = muted;\n\n        if (this.chatRoom) {\n            // FIXME This only adjusts the presence, but doesn't actually send it. Here we temporarily rely on\n            // the legacy signaling part to send the presence. Remember to add \"send presence\" here when the legacy\n            // signaling is removed.\n            return this._addLocalSourceInfoToPresence();\n        }\n\n        return false;\n    }\n\n    /**\n     * Sets track's video type.\n     * @param {SourceName} sourceName - the track's source name.\n     * @param {VideoType} videoType - the new video type.\n     * @returns {boolean}\n     */\n    setTrackVideoType(sourceName, videoType) {\n        if (!this._localSourceState[sourceName]) {\n            this._localSourceState[sourceName] = {};\n        }\n\n        if (this._localSourceState[sourceName].videoType !== videoType) {\n            // Include only if not a camera (default)\n            this._localSourceState[sourceName].videoType = videoType === VideoType.CAMERA ? undefined : videoType;\n\n            // NOTE this doesn't send the actual presence, because is called from the same place where the legacy video\n            // type is emitted which does the actual sending. A send presence statement needs to be added when\n            // the legacy part is removed.\n            return this._addLocalSourceInfoToPresence();\n        }\n\n        return false;\n    }\n\n    /**\n     * @inheritDoc\n     */\n    getTrackSourceName(ssrc) {\n        return this._sourceNames.get(ssrc);\n    }\n\n    /**\n     * Saves the source name for a track identified by it's ssrc.\n     * @param {number} ssrc the ssrc of the target track.\n     * @param {SourceName} sourceName the track's source name to save.\n     * @throws TypeError if <tt>ssrc</tt> is not a number\n     */\n    setTrackSourceName(ssrc, sourceName) {\n        if (typeof ssrc !== 'number') {\n            throw new TypeError(`SSRC(${ssrc}) must be a number`);\n        }\n\n        // Now signaling layer instance is shared between different JingleSessionPC instances, so although very unlikely\n        // an SSRC conflict could potentially occur. Log a message to make debugging easier.\n        const existingName = this._sourceNames.get(ssrc);\n\n        if (existingName && existingName !== sourceName) {\n            logger.error(`SSRC(${ssrc}) sourceName re-assigned from ${existingName} to ${sourceName}`);\n        }\n\n        this._sourceNames.set(ssrc, sourceName);\n    }\n\n}\n","/* global $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport EventEmitter from 'events';\nimport isEqual from 'lodash.isequal';\nimport { Strophe } from 'strophe.js';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport JitsiConferenceEventManager from './JitsiConferenceEventManager';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport JitsiParticipant from './JitsiParticipant';\nimport JitsiTrackError from './JitsiTrackError';\nimport * as JitsiTrackErrors from './JitsiTrackErrors';\nimport * as JitsiTrackEvents from './JitsiTrackEvents';\nimport authenticateAndUpgradeRole from './authenticateAndUpgradeRole';\nimport { CodecSelection } from './modules/RTC/CodecSelection';\nimport RTC from './modules/RTC/RTC';\nimport { SS_DEFAULT_FRAME_RATE } from './modules/RTC/ScreenObtainer';\nimport browser from './modules/browser';\nimport ConnectionQuality from './modules/connectivity/ConnectionQuality';\nimport IceFailedHandling\n    from './modules/connectivity/IceFailedHandling';\nimport ParticipantConnectionStatusHandler\n    from './modules/connectivity/ParticipantConnectionStatus';\nimport * as DetectionEvents from './modules/detection/DetectionEvents';\nimport NoAudioSignalDetection from './modules/detection/NoAudioSignalDetection';\nimport P2PDominantSpeakerDetection from './modules/detection/P2PDominantSpeakerDetection';\nimport VADAudioAnalyser from './modules/detection/VADAudioAnalyser';\nimport VADNoiseDetection from './modules/detection/VADNoiseDetection';\nimport VADTalkMutedDetection from './modules/detection/VADTalkMutedDetection';\nimport { E2EEncryption } from './modules/e2ee/E2EEncryption';\nimport E2ePing from './modules/e2eping/e2eping';\nimport Jvb121EventGenerator from './modules/event/Jvb121EventGenerator';\nimport FeatureFlags from './modules/flags/FeatureFlags';\nimport ReceiveVideoController from './modules/qualitycontrol/ReceiveVideoController';\nimport SendVideoController from './modules/qualitycontrol/SendVideoController';\nimport RecordingManager from './modules/recording/RecordingManager';\nimport Settings from './modules/settings/Settings';\nimport AudioOutputProblemDetector from './modules/statistics/AudioOutputProblemDetector';\nimport AvgRTPStatsReporter from './modules/statistics/AvgRTPStatsReporter';\nimport SpeakerStatsCollector from './modules/statistics/SpeakerStatsCollector';\nimport Statistics from './modules/statistics/statistics';\nimport Transcriber from './modules/transcription/transcriber';\nimport GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';\nimport RandomUtil from './modules/util/RandomUtil';\nimport ComponentsVersions from './modules/version/ComponentsVersions';\nimport VideoSIPGW from './modules/videosipgw/VideoSIPGW';\nimport * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';\nimport SignalingLayerImpl from './modules/xmpp/SignalingLayerImpl';\nimport {\n    FEATURE_E2EE,\n    FEATURE_JIGASI,\n    JITSI_MEET_MUC_TYPE\n} from './modules/xmpp/xmpp';\nimport BridgeVideoType from './service/RTC/BridgeVideoType';\nimport CodecMimeType from './service/RTC/CodecMimeType';\nimport { MediaType } from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\nimport { getSourceNameForJitsiTrack } from './service/RTC/SignalingLayer';\nimport VideoType from './service/RTC/VideoType';\nimport {\n    ACTION_JINGLE_RESTART,\n    ACTION_JINGLE_SI_RECEIVED,\n    ACTION_JINGLE_SI_TIMEOUT,\n    ACTION_JINGLE_TERMINATE,\n    ACTION_P2P_DECLINED,\n    ACTION_P2P_ESTABLISHED,\n    ACTION_P2P_FAILED,\n    ACTION_P2P_SWITCH_TO_JVB,\n    ICE_ESTABLISHMENT_DURATION_DIFF,\n    createConferenceEvent,\n    createJingleEvent,\n    createP2PEvent\n} from './service/statistics/AnalyticsEvents';\nimport { XMPPEvents } from './service/xmpp/XMPPEvents';\n\nconst logger = getLogger(__filename);\n\n/**\n * How long since Jicofo is supposed to send a session-initiate, before\n * {@link ACTION_JINGLE_SI_TIMEOUT} analytics event is sent (in ms).\n * @type {number}\n */\nconst JINGLE_SI_TIMEOUT = 5000;\n\n/**\n * Creates a JitsiConference object with the given name and properties.\n * Note: this constructor is not a part of the public API (objects should be\n * created using JitsiConnection.createConference).\n * @param options.config properties / settings related to the conference that\n * will be created.\n * @param options.name the name of the conference\n * @param options.connection the JitsiConnection object for this\n * JitsiConference.\n * @param {number} [options.config.avgRtpStatsN=15] how many samples are to be\n * collected by {@link AvgRTPStatsReporter}, before arithmetic mean is\n * calculated and submitted to the analytics module.\n * @param {boolean} [options.config.enableIceRestart=false] - enables the ICE\n * restart logic.\n * @param {boolean} [options.config.p2p.enabled] when set to <tt>true</tt>\n * the peer to peer mode will be enabled. It means that when there are only 2\n * participants in the conference an attempt to make direct connection will be\n * made. If the connection succeeds the conference will stop sending data\n * through the JVB connection and will use the direct one instead.\n * @param {number} [options.config.p2p.backToP2PDelay=5] a delay given in\n * seconds, before the conference switches back to P2P, after the 3rd\n * participant has left the room.\n * @param {number} [options.config.channelLastN=-1] The requested amount of\n * videos are going to be delivered after the value is in effect. Set to -1 for\n * unlimited or all available videos.\n * @param {number} [options.config.forceJVB121Ratio]\n * \"Math.random() < forceJVB121Ratio\" will determine whether a 2 people\n * conference should be moved to the JVB instead of P2P. The decision is made on\n * the responder side, after ICE succeeds on the P2P connection.\n * @constructor\n *\n * FIXME Make all methods which are called from lib-internal classes\n *       to non-public (use _). To name a few:\n *       {@link JitsiConference.onLocalRoleChanged}\n *       {@link JitsiConference.onUserRoleChanged}\n *       {@link JitsiConference.onMemberLeft}\n *       and so on...\n */\nexport default function JitsiConference(options) {\n    if (!options.name || options.name.toLowerCase() !== options.name.toString()) {\n        const errmsg\n            = 'Invalid conference name (no conference name passed or it '\n                + 'contains invalid characters like capital letters)!';\n\n        logger.error(errmsg);\n        throw new Error(errmsg);\n    }\n    this.connection = options.connection;\n    this.xmpp = this.connection?.xmpp;\n\n    if (this.xmpp.isRoomCreated(options.name, options.customDomain)) {\n        const errmsg = 'A coference with the same name has already been created!';\n\n        delete this.connection;\n        delete this.xmpp;\n        logger.error(errmsg);\n        throw new Error(errmsg);\n    }\n    this.eventEmitter = new EventEmitter();\n    this.options = options;\n    this.eventManager = new JitsiConferenceEventManager(this);\n    this.participants = {};\n\n    /**\n     * The signaling layer instance.\n     * @type {SignalingLayerImpl}\n     * @private\n     */\n    this._signalingLayer = new SignalingLayerImpl();\n\n    this._init(options);\n    this.componentsVersions = new ComponentsVersions(this);\n\n    /**\n     * Jingle session instance for the JVB connection.\n     * @type {JingleSessionPC}\n     */\n    this.jvbJingleSession = null;\n    this.lastDominantSpeaker = null;\n    this.dtmfManager = null;\n    this.somebodySupportsDTMF = false;\n    this.authEnabled = false;\n    this.startAudioMuted = false;\n    this.startVideoMuted = false;\n    this.startMutedPolicy = {\n        audio: false,\n        video: false\n    };\n    this.isMutedByFocus = false;\n\n    // when muted by focus we receive the jid of the initiator of the mute\n    this.mutedByFocusActor = null;\n\n    this.isVideoMutedByFocus = false;\n\n    // when video muted by focus we receive the jid of the initiator of the mute\n    this.mutedVideoByFocusActor = null;\n\n    // Flag indicates if the 'onCallEnded' method was ever called on this\n    // instance. Used to log extra analytics event for debugging purpose.\n    // We need to know if the potential issue happened before or after\n    // the restart.\n    this.wasStopped = false;\n\n    // Conference properties, maintained by jicofo.\n    this.properties = {};\n\n    /**\n     * The object which monitors local and remote connection statistics (e.g.\n     * sending bitrate) and calculates a number which represents the connection\n     * quality.\n     */\n    this.connectionQuality\n        = new ConnectionQuality(this, this.eventEmitter, options);\n\n    /**\n     * Reports average RTP statistics to the analytics module.\n     * @type {AvgRTPStatsReporter}\n     */\n    this.avgRtpStatsReporter\n        = new AvgRTPStatsReporter(this, options.config.avgRtpStatsN || 15);\n\n    /**\n     * Detects issues with the audio of remote participants.\n     * @type {AudioOutputProblemDetector}\n     */\n    if (!options.config.disableAudioLevels) {\n        this._audioOutputProblemDetector = new AudioOutputProblemDetector(this);\n    }\n\n    /**\n     * Indicates whether the connection is interrupted or not.\n     */\n    this.isJvbConnectionInterrupted = false;\n\n    /**\n     * The object which tracks active speaker times\n     */\n    this.speakerStatsCollector = new SpeakerStatsCollector(this);\n\n    /* P2P related fields below: */\n\n    /**\n     * Stores reference to deferred start P2P task. It's created when 3rd\n     * participant leaves the room in order to avoid ping pong effect (it\n     * could be just a page reload).\n     * @type {number|null}\n     */\n    this.deferredStartP2PTask = null;\n\n    const delay\n        = parseInt(options.config.p2p && options.config.p2p.backToP2PDelay, 10);\n\n    /**\n     * A delay given in seconds, before the conference switches back to P2P\n     * after the 3rd participant has left.\n     * @type {number}\n     */\n    this.backToP2PDelay = isNaN(delay) ? 5 : delay;\n    logger.info(`backToP2PDelay: ${this.backToP2PDelay}`);\n\n    /**\n     * If set to <tt>true</tt> it means the P2P ICE is no longer connected.\n     * When <tt>false</tt> it means that P2P ICE (media) connection is up\n     * and running.\n     * @type {boolean}\n     */\n    this.isP2PConnectionInterrupted = false;\n\n    /**\n     * Flag set to <tt>true</tt> when P2P session has been established\n     * (ICE has been connected) and this conference is currently in the peer to\n     * peer mode (P2P connection is the active one).\n     * @type {boolean}\n     */\n    this.p2p = false;\n\n    /**\n     * A JingleSession for the direct peer to peer connection.\n     * @type {JingleSessionPC}\n     */\n    this.p2pJingleSession = null;\n\n    this.videoSIPGWHandler = new VideoSIPGW(this.room);\n    this.recordingManager = new RecordingManager(this.room);\n\n    /**\n     * If the conference.joined event has been sent this will store the timestamp when it happened.\n     *\n     * @type {undefined|number}\n     * @private\n     */\n    this._conferenceJoinAnalyticsEventSent = undefined;\n\n    /**\n     * End-to-End Encryption. Make it available if supported.\n     */\n    if (this.isE2EESupported()) {\n        logger.info('End-to-End Encryption is supported');\n\n        this._e2eEncryption = new E2EEncryption(this);\n    }\n\n    /**\n     * Flag set to <tt>true</tt> when Jicofo sends a presence message indicating that the max audio sender limit has\n     * been reached for the call. Once this is set, unmuting audio will be disabled from the client until it gets reset\n     * again by Jicofo.\n     */\n    this._audioSenderLimitReached = undefined;\n\n    /**\n     * Flag set to <tt>true</tt> when Jicofo sends a presence message indicating that the max video sender limit has\n     * been reached for the call. Once this is set, unmuting video will be disabled from the client until it gets reset\n     * again by Jicofo.\n     */\n    this._videoSenderLimitReached = undefined;\n}\n\n// FIXME convert JitsiConference to ES6 - ASAP !\nJitsiConference.prototype.constructor = JitsiConference;\n\n/**\n * Create a resource for the a jid. We use the room nickname (the resource part\n * of the occupant JID, see XEP-0045) as the endpoint ID in colibri. We require\n * endpoint IDs to be 8 hex digits because in some cases they get serialized\n * into a 32bit field.\n *\n * @param {string} jid - The id set onto the XMPP connection.\n * @param {boolean} isAuthenticatedUser - Whether or not the user has connected\n * to the XMPP service with a password.\n * @returns {string}\n * @static\n */\nJitsiConference.resourceCreator = function(jid, isAuthenticatedUser) {\n    let mucNickname;\n\n    if (isAuthenticatedUser) {\n        // For authenticated users generate a random ID.\n        mucNickname = RandomUtil.randomHexString(8).toLowerCase();\n    } else {\n        // We try to use the first part of the node (which for anonymous users\n        // on prosody is a UUID) to match the previous behavior (and maybe make\n        // debugging easier).\n        mucNickname = Strophe.getNodeFromJid(jid)?.substr(0, 8)\n            .toLowerCase();\n\n        // But if this doesn't have the required format we just generate a new\n        // random nickname.\n        const re = /[0-9a-f]{8}/g;\n\n        if (!mucNickname || !re.test(mucNickname)) {\n            mucNickname = RandomUtil.randomHexString(8).toLowerCase();\n        }\n    }\n\n    return mucNickname;\n};\n\n/**\n * Initializes the conference object properties\n * @param options {object}\n * @param options.connection {JitsiConnection} overrides this.connection\n */\nJitsiConference.prototype._init = function(options = {}) {\n    this.eventManager.setupXMPPListeners();\n\n    const { config } = this.options;\n\n    // Get the codec preference settings from config.js.\n    // 'preferH264' and 'disableH264' settings have been deprecated for a while,\n    // 'preferredCodec' and 'disabledCodec' will have precedence over them.\n    const codecSettings = {\n        disabledCodec: config.videoQuality\n            ? config.videoQuality.disabledCodec\n            : config.p2p && config.p2p.disableH264 && CodecMimeType.H264,\n        enforcePreferredCodec: config.videoQuality && config.videoQuality.enforcePreferredCodec,\n        jvbCodec: (config.videoQuality && config.videoQuality.preferredCodec)\n            || (config.preferH264 && CodecMimeType.H264),\n        p2pCodec: config.p2p\n            ? config.p2p.preferredCodec || (config.p2p.preferH264 && CodecMimeType.H264)\n            : CodecMimeType.VP8\n    };\n\n    this.codecSelection = new CodecSelection(this, codecSettings);\n    this._statsCurrentId = config.statisticsId ? config.statisticsId : Settings.callStatsUserName;\n    this.room = this.xmpp.createRoom(\n        this.options.name, {\n            ...config,\n            statsId: this._statsCurrentId\n        },\n        JitsiConference.resourceCreator\n    );\n\n    this._signalingLayer.setChatRoom(this.room);\n\n    // Connection interrupted/restored listeners\n    this._onIceConnectionInterrupted\n        = this._onIceConnectionInterrupted.bind(this);\n    this.room.addListener(\n        XMPPEvents.CONNECTION_INTERRUPTED, this._onIceConnectionInterrupted);\n\n    this._onIceConnectionRestored = this._onIceConnectionRestored.bind(this);\n    this.room.addListener(\n        XMPPEvents.CONNECTION_RESTORED, this._onIceConnectionRestored);\n\n    this._onIceConnectionEstablished\n        = this._onIceConnectionEstablished.bind(this);\n    this.room.addListener(\n        XMPPEvents.CONNECTION_ESTABLISHED, this._onIceConnectionEstablished);\n\n    this._updateProperties = this._updateProperties.bind(this);\n    this.room.addListener(XMPPEvents.CONFERENCE_PROPERTIES_CHANGED,\n        this._updateProperties);\n\n    this._sendConferenceJoinAnalyticsEvent = this._sendConferenceJoinAnalyticsEvent.bind(this);\n    this.room.addListener(XMPPEvents.MEETING_ID_SET, this._sendConferenceJoinAnalyticsEvent);\n\n    this._removeLocalSourceOnReject = this._removeLocalSourceOnReject.bind(this);\n    this._updateRoomPresence = this._updateRoomPresence.bind(this);\n    this.room.addListener(XMPPEvents.SESSION_ACCEPT, this._updateRoomPresence);\n    this.room.addListener(XMPPEvents.SOURCE_ADD, this._updateRoomPresence);\n    this.room.addListener(XMPPEvents.SOURCE_ADD_ERROR, this._removeLocalSourceOnReject);\n    this.room.addListener(XMPPEvents.SOURCE_REMOVE, this._updateRoomPresence);\n\n    this.e2eping = new E2ePing(\n        this,\n        config,\n        (message, to) => {\n            try {\n                this.sendMessage(\n                    message, to, true /* sendThroughVideobridge */);\n            } catch (error) {\n                logger.warn('Failed to send E2E ping request or response.', error && error.msg);\n            }\n        });\n\n    if (!this.rtc) {\n        this.rtc = new RTC(this, options);\n        this.eventManager.setupRTCListeners();\n        if (FeatureFlags.isSourceNameSignalingEnabled()) {\n            this._registerRtcListeners(this.rtc);\n        }\n    }\n\n    this.receiveVideoController = new ReceiveVideoController(this, this.rtc);\n    this.sendVideoController = new SendVideoController(this, this.rtc);\n\n    this.participantConnectionStatus\n        = new ParticipantConnectionStatusHandler(\n            this.rtc,\n            this,\n            {\n                // These options are not public API, leaving it here only as an entry point through config for tuning\n                // up purposes. Default values should be adjusted as soon as optimal values are discovered.\n                p2pRtcMuteTimeout: config._p2pConnStatusRtcMuteTimeout,\n                rtcMuteTimeout: config._peerConnStatusRtcMuteTimeout,\n                outOfLastNTimeout: config._peerConnStatusOutOfLastNTimeout\n            });\n    this.participantConnectionStatus.init();\n\n    // Add the ability to enable callStats only on a percentage of users based on config.js settings.\n    let enableCallStats = true;\n\n    if (config.testing && config.testing.callStatsThreshold) {\n        enableCallStats = (Math.random() * 100) <= config.testing.callStatsThreshold;\n    }\n\n    if (!this.statistics) {\n        this.statistics = new Statistics(this.xmpp, {\n            aliasName: this._statsCurrentId,\n            userName: config.statisticsDisplayName ? config.statisticsDisplayName : this.myUserId(),\n            confID: config.confID || `${this.connection.options.hosts.domain}/${this.options.name}`,\n            siteID: config.siteID,\n            customScriptUrl: config.callStatsCustomScriptUrl,\n            callStatsID: config.callStatsID,\n            callStatsSecret: config.callStatsSecret,\n            callStatsApplicationLogsDisabled: config.callStatsApplicationLogsDisabled,\n            enableCallStats,\n            roomName: this.options.name,\n            applicationName: config.applicationName,\n            getWiFiStatsMethod: config.getWiFiStatsMethod,\n            configParams: config.callStatsConfigParams\n        });\n        Statistics.analytics.addPermanentProperties({\n            'callstats_name': this._statsCurrentId\n        });\n\n        // Start performance observer for monitoring long tasks\n        if (config.longTasksStatsInterval) {\n            this.statistics.attachLongTasksStats(this);\n        }\n    }\n\n    this.eventManager.setupChatRoomListeners();\n\n    // Always add listeners because on reload we are executing leave and the\n    // listeners are removed from statistics module.\n    this.eventManager.setupStatisticsListeners();\n\n    // Disable VAD processing on Safari since it causes audio input to\n    // fail on some of the mobile devices.\n    if (config.enableTalkWhileMuted && browser.supportsVADDetection()) {\n        // If VAD processor factory method is provided uses VAD based detection, otherwise fallback to audio level\n        // based detection.\n        if (config.createVADProcessor) {\n            logger.info('Using VAD detection for generating talk while muted events');\n\n            if (!this._audioAnalyser) {\n                this._audioAnalyser = new VADAudioAnalyser(this, config.createVADProcessor);\n            }\n\n            const vadTalkMutedDetection = new VADTalkMutedDetection();\n\n            vadTalkMutedDetection.on(DetectionEvents.VAD_TALK_WHILE_MUTED, () =>\n                this.eventEmitter.emit(JitsiConferenceEvents.TALK_WHILE_MUTED));\n\n            this._audioAnalyser.addVADDetectionService(vadTalkMutedDetection);\n        } else {\n            logger.warn('No VAD Processor was provided. Talk while muted detection service was not initialized!');\n        }\n    }\n\n    // Disable noisy mic detection on safari since it causes the audio input to\n    // fail on Safari on iPadOS.\n    if (config.enableNoisyMicDetection && browser.supportsVADDetection()) {\n        if (config.createVADProcessor) {\n            if (!this._audioAnalyser) {\n                this._audioAnalyser = new VADAudioAnalyser(this, config.createVADProcessor);\n            }\n\n            const vadNoiseDetection = new VADNoiseDetection();\n\n            vadNoiseDetection.on(DetectionEvents.VAD_NOISY_DEVICE, () =>\n                this.eventEmitter.emit(JitsiConferenceEvents.NOISY_MIC));\n\n            this._audioAnalyser.addVADDetectionService(vadNoiseDetection);\n        } else {\n            logger.warn('No VAD Processor was provided. Noisy microphone detection service was not initialized!');\n        }\n    }\n\n    // Generates events based on no audio input detector.\n    if (config.enableNoAudioDetection) {\n        this._noAudioSignalDetection = new NoAudioSignalDetection(this);\n        this._noAudioSignalDetection.on(DetectionEvents.NO_AUDIO_INPUT, () => {\n            this.eventEmitter.emit(JitsiConferenceEvents.NO_AUDIO_INPUT);\n        });\n        this._noAudioSignalDetection.on(DetectionEvents.AUDIO_INPUT_STATE_CHANGE, hasAudioSignal => {\n            this.eventEmitter.emit(JitsiConferenceEvents.AUDIO_INPUT_STATE_CHANGE, hasAudioSignal);\n        });\n    }\n\n\n    if ('channelLastN' in config) {\n        this.setLastN(config.channelLastN);\n    }\n\n    /**\n     * Emits {@link JitsiConferenceEvents.JVB121_STATUS}.\n     * @type {Jvb121EventGenerator}\n     */\n    this.jvb121Status = new Jvb121EventGenerator(this);\n\n    // creates dominant speaker detection that works only in p2p mode\n    this.p2pDominantSpeakerDetection = new P2PDominantSpeakerDetection(this);\n\n    if (config && config.deploymentInfo && config.deploymentInfo.userRegion) {\n        this.setLocalParticipantProperty(\n            'region', config.deploymentInfo.userRegion);\n    }\n\n    // Publish the codec type to presence.\n    this.setLocalParticipantProperty('codecType', this.codecSelection.getPreferredCodec());\n\n    // Set transcription language presence extension.\n    // In case the language config is undefined or has the default value that the transcriber uses\n    // (in our case Jigasi uses 'en-US'), don't set the participant property in order to avoid\n    // needlessly polluting the presence stanza.\n    if (config && config.transcriptionLanguage && config.transcriptionLanguage !== 'en-US') {\n        this.setLocalParticipantProperty('transcription_language', config.transcriptionLanguage);\n    }\n};\n\n/**\n * Joins the conference.\n * @param password {string} the password\n * @param replaceParticipant {boolean} whether the current join replaces\n * an existing participant with same jwt from the meeting.\n */\nJitsiConference.prototype.join = function(password, replaceParticipant = false) {\n    if (this.room) {\n        this.room.join(password, replaceParticipant).then(() => this._maybeSetSITimeout());\n    }\n};\n\n/**\n * Authenticates and upgrades the role of the local participant/user.\n *\n * @returns {Object} A <tt>thenable</tt> which (1) settles when the process of\n * authenticating and upgrading the role of the local participant/user finishes\n * and (2) has a <tt>cancel</tt> method that allows the caller to interrupt the\n * process.\n */\nJitsiConference.prototype.authenticateAndUpgradeRole = function(options) {\n    return authenticateAndUpgradeRole.call(this, {\n        ...options,\n        onCreateResource: JitsiConference.resourceCreator\n    });\n};\n\n/**\n * Check if joined to the conference.\n */\nJitsiConference.prototype.isJoined = function() {\n    return this.room && this.room.joined;\n};\n\n/**\n * Tells whether or not the P2P mode is enabled in the configuration.\n * @return {boolean}\n */\nJitsiConference.prototype.isP2PEnabled = function() {\n    return Boolean(this.options.config.p2p && this.options.config.p2p.enabled)\n\n        // FIXME: remove once we have a default config template. -saghul\n        || typeof this.options.config.p2p === 'undefined';\n};\n\n/**\n * When in P2P test mode, the conference will not automatically switch to P2P\n * when there 2 participants.\n * @return {boolean}\n */\nJitsiConference.prototype.isP2PTestModeEnabled = function() {\n    return Boolean(this.options.config.testing\n        && this.options.config.testing.p2pTestMode);\n};\n\n/**\n * Leaves the conference.\n * @returns {Promise}\n */\nJitsiConference.prototype.leave = async function() {\n    if (this.participantConnectionStatus) {\n        this.participantConnectionStatus.dispose();\n        this.participantConnectionStatus = null;\n    }\n    if (this.avgRtpStatsReporter) {\n        this.avgRtpStatsReporter.dispose();\n        this.avgRtpStatsReporter = null;\n    }\n\n    if (this._audioOutputProblemDetector) {\n        this._audioOutputProblemDetector.dispose();\n        this._audioOutputProblemDetector = null;\n    }\n\n    if (this.e2eping) {\n        this.e2eping.stop();\n        this.e2eping = null;\n    }\n\n    this.getLocalTracks().forEach(track => this.onLocalTrackRemoved(track));\n\n    this.rtc.closeBridgeChannel();\n\n    this._sendConferenceLeftAnalyticsEvent();\n\n    if (this.statistics) {\n        this.statistics.dispose();\n    }\n\n    this._delayedIceFailed && this._delayedIceFailed.cancel();\n\n    this._maybeClearSITimeout();\n\n    // Close both JVb and P2P JingleSessions\n    if (this.jvbJingleSession) {\n        this.jvbJingleSession.close();\n        this.jvbJingleSession = null;\n    }\n    if (this.p2pJingleSession) {\n        this.p2pJingleSession.close();\n        this.p2pJingleSession = null;\n    }\n\n    // Leave the conference. If this.room == null we are calling second time leave().\n    if (!this.room) {\n        throw new Error('The conference is has been already left');\n    }\n\n    const room = this.room;\n\n    // Unregister connection state listeners\n    room.removeListener(\n        XMPPEvents.CONNECTION_INTERRUPTED,\n        this._onIceConnectionInterrupted);\n    room.removeListener(\n        XMPPEvents.CONNECTION_RESTORED,\n        this._onIceConnectionRestored);\n    room.removeListener(\n        XMPPEvents.CONNECTION_ESTABLISHED,\n        this._onIceConnectionEstablished);\n\n    room.removeListener(\n        XMPPEvents.CONFERENCE_PROPERTIES_CHANGED,\n        this._updateProperties);\n\n    room.removeListener(XMPPEvents.MEETING_ID_SET, this._sendConferenceJoinAnalyticsEvent);\n    room.removeListener(XMPPEvents.SESSION_ACCEPT, this._updateRoomPresence);\n    room.removeListener(XMPPEvents.SOURCE_ADD, this._updateRoomPresence);\n    room.removeListener(XMPPEvents.SOURCE_ADD_ERROR, this._removeLocalSourceOnReject);\n    room.removeListener(XMPPEvents.SOURCE_REMOVE, this._updateRoomPresence);\n\n    this.eventManager.removeXMPPListeners();\n\n    this._signalingLayer.setChatRoom(null);\n\n    this.room = null;\n\n    let leaveError;\n\n    try {\n        await room.leave();\n    } catch (err) {\n        leaveError = err;\n\n        // Remove all participants because currently the conference\n        // won't be usable anyway. This is done on success automatically\n        // by the ChatRoom instance.\n        this.getParticipants().forEach(\n            participant => this.onMemberLeft(participant.getJid()));\n    }\n\n    if (this.rtc) {\n        this.rtc.destroy();\n    }\n\n    if (leaveError) {\n        throw leaveError;\n    }\n};\n\n/**\n * Returns the currently active media session if any.\n *\n * @returns {JingleSessionPC|undefined}\n */\nJitsiConference.prototype.getActiveMediaSession = function() {\n    return this.isP2PActive() ? this.p2pJingleSession : this.jvbJingleSession;\n};\n\n/**\n * Returns an array containing all media sessions existing in this conference.\n *\n * @returns {Array<JingleSessionPC>}\n */\nJitsiConference.prototype.getMediaSessions = function() {\n    const sessions = [];\n\n    this.jvbJingleSession && sessions.push(this.jvbJingleSession);\n    this.p2pJingleSession && sessions.push(this.p2pJingleSession);\n\n    return sessions;\n};\n\n/**\n * Registers event listeners on the RTC instance.\n * @param {RTC} rtc - the RTC module instance used by this conference.\n * @private\n * @returns {void}\n */\nJitsiConference.prototype._registerRtcListeners = function(rtc) {\n    rtc.addListener(RTCEvents.DATA_CHANNEL_OPEN, () => {\n        for (const localTrack of this.rtc.localTracks) {\n            localTrack.isVideoTrack() && this._sendBridgeVideoTypeMessage(localTrack);\n        }\n    });\n};\n\n/**\n * Sends the 'VideoTypeMessage' to the bridge on the bridge channel so that the bridge can make bitrate allocation\n * decisions based on the video type of the local source.\n *\n * @param {JitsiLocalTrack} localtrack - The track associated with the local source signaled to the bridge.\n * @returns {void}\n * @private\n */\nJitsiConference.prototype._sendBridgeVideoTypeMessage = function(localtrack) {\n    let videoType = !localtrack || localtrack.isMuted() ? BridgeVideoType.NONE : localtrack.getVideoType();\n\n    if (videoType === BridgeVideoType.DESKTOP && this._desktopSharingFrameRate > SS_DEFAULT_FRAME_RATE) {\n        videoType = BridgeVideoType.DESKTOP_HIGH_FPS;\n    }\n\n    if (FeatureFlags.isSourceNameSignalingEnabled() && localtrack) {\n        this.rtc.sendSourceVideoType(localtrack.getSourceName(), videoType);\n    } else if (!FeatureFlags.isSourceNameSignalingEnabled()) {\n        this.rtc.setVideoType(videoType);\n    }\n};\n\n/**\n * Returns name of this conference.\n */\nJitsiConference.prototype.getName = function() {\n    return this.options.name.toString();\n};\n\n/**\n * Returns the {@link JitsiConnection} used by this this conference.\n */\nJitsiConference.prototype.getConnection = function() {\n    return this.connection;\n};\n\n/**\n * Check if authentication is enabled for this conference.\n */\nJitsiConference.prototype.isAuthEnabled = function() {\n    return this.authEnabled;\n};\n\n/**\n * Check if user is logged in.\n */\nJitsiConference.prototype.isLoggedIn = function() {\n    return Boolean(this.authIdentity);\n};\n\n/**\n * Get authorized login.\n */\nJitsiConference.prototype.getAuthLogin = function() {\n    return this.authIdentity;\n};\n\n/**\n * Check if external authentication is enabled for this conference.\n */\nJitsiConference.prototype.isExternalAuthEnabled = function() {\n    return this.room && this.room.moderator.isExternalAuthEnabled();\n};\n\n/**\n * Get url for external authentication.\n * @param {boolean} [urlForPopup] if true then return url for login popup,\n *                                else url of login page.\n * @returns {Promise}\n */\nJitsiConference.prototype.getExternalAuthUrl = function(urlForPopup) {\n    return new Promise((resolve, reject) => {\n        if (!this.isExternalAuthEnabled()) {\n            reject();\n\n            return;\n        }\n        if (urlForPopup) {\n            this.room.moderator.getPopupLoginUrl(resolve, reject);\n        } else {\n            this.room.moderator.getLoginUrl(resolve, reject);\n        }\n    });\n};\n\n/**\n * Returns the local tracks of the given media type, or all local tracks if no\n * specific type is given.\n * @param {MediaType} [mediaType] Optional media type (audio or video).\n */\nJitsiConference.prototype.getLocalTracks = function(mediaType) {\n    let tracks = [];\n\n    if (this.rtc) {\n        tracks = this.rtc.getLocalTracks(mediaType);\n    }\n\n    return tracks;\n};\n\n/**\n * Obtains local audio track.\n * @return {JitsiLocalTrack|null}\n */\nJitsiConference.prototype.getLocalAudioTrack = function() {\n    return this.rtc ? this.rtc.getLocalAudioTrack() : null;\n};\n\n/**\n * Obtains local video track.\n * @return {JitsiLocalTrack|null}\n */\nJitsiConference.prototype.getLocalVideoTrack = function() {\n    return this.rtc ? this.rtc.getLocalVideoTrack() : null;\n};\n\n/**\n * Returns all the local video tracks.\n * @returns {Array<JitsiLocalTrack>}\n */\nJitsiConference.prototype.getLocalVideoTracks = function() {\n    return this.rtc ? this.rtc.getLocalVideoTracks() : null;\n};\n\n/**\n * Obtains the performance statistics.\n * @returns {Object|null}\n */\nJitsiConference.prototype.getPerformanceStats = function() {\n    return {\n        longTasksStats: this.statistics.getLongTasksStats()\n    };\n};\n\n/**\n * Attaches a handler for events(For example - \"participant joined\".) in the\n * conference. All possible event are defined in JitsiConferenceEvents.\n * @param eventId the event ID.\n * @param handler handler for the event.\n *\n * Note: consider adding eventing functionality by extending an EventEmitter\n * impl, instead of rolling ourselves\n */\nJitsiConference.prototype.on = function(eventId, handler) {\n    if (this.eventEmitter) {\n        this.eventEmitter.on(eventId, handler);\n    }\n};\n\n/**\n * Removes event listener\n * @param eventId the event ID.\n * @param [handler] optional, the specific handler to unbind\n *\n * Note: consider adding eventing functionality by extending an EventEmitter\n * impl, instead of rolling ourselves\n */\nJitsiConference.prototype.off = function(eventId, handler) {\n    if (this.eventEmitter) {\n        this.eventEmitter.removeListener(eventId, handler);\n    }\n};\n\n// Common aliases for event emitter\nJitsiConference.prototype.addEventListener = JitsiConference.prototype.on;\nJitsiConference.prototype.removeEventListener = JitsiConference.prototype.off;\n\n/**\n * Receives notifications from other participants about commands / custom events\n * (sent by sendCommand or sendCommandOnce methods).\n * @param command {String} the name of the command\n * @param handler {Function} handler for the command\n */\nJitsiConference.prototype.addCommandListener = function(command, handler) {\n    if (this.room) {\n        this.room.addPresenceListener(command, handler);\n    }\n};\n\n/**\n  * Removes command  listener\n  * @param command {String} the name of the command\n  * @param handler {Function} handler to remove for the command\n  */\nJitsiConference.prototype.removeCommandListener = function(command, handler) {\n    if (this.room) {\n        this.room.removePresenceListener(command, handler);\n    }\n};\n\n/**\n * Sends text message to the other participants in the conference\n * @param message the text message.\n * @param elementName the element name to encapsulate the message.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendTextMessage = function(message, elementName = 'body') {\n    if (this.room) {\n        this.room.sendMessage(message, elementName);\n    }\n};\n\n/**\n * Send private text message to another participant of the conference\n * @param id the id of the participant to send a private message.\n * @param message the text message.\n * @param elementName the element name to encapsulate the message.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendPrivateTextMessage = function(id, message, elementName = 'body') {\n    if (this.room) {\n        this.room.sendPrivateMessage(id, message, elementName);\n    }\n};\n\n/**\n * Send presence command.\n * @param name {String} the name of the command.\n * @param values {Object} with keys and values that will be sent.\n **/\nJitsiConference.prototype.sendCommand = function(name, values) {\n    if (this.room) {\n        this.room.addOrReplaceInPresence(name, values) && this.room.sendPresence();\n    } else {\n        logger.warn('Not sending a command, room not initialized.');\n    }\n\n};\n\n/**\n * Send presence command one time.\n * @param name {String} the name of the command.\n * @param values {Object} with keys and values that will be sent.\n **/\nJitsiConference.prototype.sendCommandOnce = function(name, values) {\n    this.sendCommand(name, values);\n    this.removeCommand(name);\n};\n\n/**\n * Removes presence command.\n * @param name {String} the name of the command.\n **/\nJitsiConference.prototype.removeCommand = function(name) {\n    if (this.room) {\n        this.room.removeFromPresence(name);\n    }\n};\n\n/**\n * Sets the display name for this conference.\n * @param name the display name to set\n */\nJitsiConference.prototype.setDisplayName = function(name) {\n    if (this.room) {\n        const nickKey = 'nick';\n\n        // if there is no display name already set, avoid setting an empty one\n        if (!name && !this.room.getFromPresence(nickKey)) {\n            return;\n        }\n\n        this.room.addOrReplaceInPresence(nickKey, {\n            attributes: { xmlns: 'http://jabber.org/protocol/nick' },\n            value: name\n        }) && this.room.sendPresence();\n    }\n};\n\n/**\n * Set new subject for this conference. (available only for moderator)\n * @param {string} subject new subject\n */\nJitsiConference.prototype.setSubject = function(subject) {\n    if (this.room && this.isModerator()) {\n        this.room.setSubject(subject);\n    } else {\n        logger.warn(`Failed to set subject, ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator'}`);\n    }\n};\n\n/**\n * Get a transcriber object for all current participants in this conference\n * @return {Transcriber} the transcriber object\n */\nJitsiConference.prototype.getTranscriber = function() {\n    if (this.transcriber === undefined) {\n        this.transcriber = new Transcriber();\n\n        // add all existing local audio tracks to the transcriber\n        const localAudioTracks = this.getLocalTracks(MediaType.AUDIO);\n\n        for (const localAudio of localAudioTracks) {\n            this.transcriber.addTrack(localAudio);\n        }\n\n        // and all remote audio tracks\n        const remoteAudioTracks = this.rtc.getRemoteTracks(MediaType.AUDIO);\n\n        for (const remoteTrack of remoteAudioTracks) {\n            this.transcriber.addTrack(remoteTrack);\n        }\n    }\n\n    return this.transcriber;\n};\n\n/**\n * Returns the transcription status.\n *\n * @returns {String} \"on\" or \"off\".\n */\nJitsiConference.prototype.getTranscriptionStatus = function() {\n    return this.room.transcriptionStatus;\n};\n\n/**\n * Adds JitsiLocalTrack object to the conference.\n * @param {JitsiLocalTrack} track the JitsiLocalTrack object.\n * @returns {Promise<JitsiLocalTrack>}\n * @throws {Error} if the specified track is a video track and there is already\n * another video track in the conference.\n */\nJitsiConference.prototype.addTrack = function(track) {\n    const mediaType = track.getType();\n    const localTracks = this.rtc.getLocalTracks(mediaType);\n\n    // Ensure there's exactly 1 local track of each media type in the conference.\n    if (localTracks.length > 0) {\n        // Don't be excessively harsh and severe if the API client happens to attempt to add the same local track twice.\n        if (track === localTracks[0]) {\n            return Promise.resolve(track);\n        }\n\n        if (FeatureFlags.isMultiStreamSupportEnabled() && mediaType === MediaType.VIDEO) {\n            const addTrackPromises = [];\n\n            this.p2pJingleSession && addTrackPromises.push(this.p2pJingleSession.addTrack(track));\n            this.jvbJingleSession && addTrackPromises.push(this.jvbJingleSession.addTrack(track));\n\n            return Promise.all(addTrackPromises)\n                .then(() => {\n                    this._setupNewTrack(track);\n\n                    // TODO Update presence and sent videoType message.\n                    if (this.isMutedByFocus || this.isVideoMutedByFocus) {\n                        this._fireMuteChangeEvent(track);\n                    }\n                });\n        }\n\n        return Promise.reject(new Error(`Cannot add second ${mediaType} track to the conference`));\n    }\n\n    return this.replaceTrack(null, track);\n};\n\n/**\n * Fires TRACK_AUDIO_LEVEL_CHANGED change conference event (for local tracks).\n * @param {number} audioLevel the audio level\n * @param {TraceablePeerConnection} [tpc]\n */\nJitsiConference.prototype._fireAudioLevelChangeEvent = function(audioLevel, tpc) {\n    const activeTpc = this.getActivePeerConnection();\n\n    // There will be no TraceablePeerConnection if audio levels do not come from\n    // a peerconnection. LocalStatsCollector.js measures audio levels using Web\n    // Audio Analyser API and emits local audio levels events through\n    // JitsiTrack.setAudioLevel, but does not provide TPC instance which is\n    // optional.\n    if (!tpc || activeTpc === tpc) {\n        this.eventEmitter.emit(\n            JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED,\n            this.myUserId(), audioLevel);\n    }\n};\n\n/**\n * Fires TRACK_MUTE_CHANGED change conference event.\n * @param track the JitsiTrack object related to the event.\n */\nJitsiConference.prototype._fireMuteChangeEvent = function(track) {\n    // check if track was muted by focus and now is unmuted by user\n    if (this.isMutedByFocus && track.isAudioTrack() && !track.isMuted()) {\n        this.isMutedByFocus = false;\n\n        // unmute local user on server\n        this.room.muteParticipant(this.room.myroomjid, false, MediaType.AUDIO);\n    } else if (this.isVideoMutedByFocus && track.isVideoTrack() && !track.isMuted()) {\n        this.isVideoMutedByFocus = false;\n\n        // unmute local user on server\n        this.room.muteParticipant(this.room.myroomjid, false, MediaType.VIDEO);\n    }\n\n    let actorParticipant;\n\n    if (this.mutedByFocusActor && track.isAudioTrack()) {\n        const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);\n\n        actorParticipant = this.participants[actorId];\n    } else if (this.mutedVideoByFocusActor && track.isVideoTrack()) {\n        const actorId = Strophe.getResourceFromJid(this.mutedVideoByFocusActor);\n\n        actorParticipant = this.participants[actorId];\n    }\n\n    // Send the video type message to the bridge if the track is not removed/added to the pc as part of\n    // the mute/unmute operation. This currently happens only on Firefox.\n    if (track.isVideoTrack() && !browser.doesVideoMuteByStreamRemove()) {\n        this._sendBridgeVideoTypeMessage(track);\n    }\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track, actorParticipant);\n};\n\n/**\n * Returns the list of local tracks that need to be added to the peerconnection on join.\n * This takes the startAudioMuted/startVideoMuted flags into consideration since we do not\n * want to add the tracks if the user joins the call audio/video muted. The tracks will be\n * added when the user unmutes for the first time.\n * @returns {Array<JitsiLocalTrack>} - list of local tracks that are unmuted.\n */\nJitsiConference.prototype._getInitialLocalTracks = function() {\n    // Always add the audio track on certain platforms:\n    //  * Safari / WebKit: because of a known issue where audio playout doesn't happen\n    //    if the user joins audio and video muted.\n    //  * React Native: after iOS 15, if a user joins muted they won't be able to unmute.\n    return this.getLocalTracks()\n        .filter(track => {\n            const trackType = track.getType();\n\n            if (trackType === MediaType.AUDIO\n                    && (!this.isStartAudioMuted() || browser.isWebKitBased() || browser.isReactNative())) {\n                return true;\n            } else if (trackType === MediaType.VIDEO && !this.isStartVideoMuted()) {\n                return true;\n            }\n\n            return false;\n        });\n};\n\n/**\n * Clear JitsiLocalTrack properties and listeners.\n * @param track the JitsiLocalTrack object.\n */\nJitsiConference.prototype.onLocalTrackRemoved = function(track) {\n    track.setConference(null);\n    this.rtc.removeLocalTrack(track);\n    track.removeEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, track.muteHandler);\n    if (track.isAudioTrack()) {\n        track.removeEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, track.audioLevelHandler);\n    }\n\n    // send event for stopping screen sharing\n    // FIXME: we assume we have only one screen sharing track\n    // if we change this we need to fix this check\n    if (track.isVideoTrack() && track.videoType === VideoType.DESKTOP) {\n        this.statistics.sendScreenSharingEvent(false);\n    }\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);\n};\n\n/**\n * Removes JitsiLocalTrack from the conference and performs\n * a new offer/answer cycle.\n * @param {JitsiLocalTrack} track\n * @returns {Promise}\n */\nJitsiConference.prototype.removeTrack = function(track) {\n    return this.replaceTrack(track, null);\n};\n\n/**\n * Replaces oldTrack with newTrack and performs a single offer/answer\n *  cycle after both operations are done.  Either oldTrack or newTrack\n *  can be null; replacing a valid 'oldTrack' with a null 'newTrack'\n *  effectively just removes 'oldTrack'\n * @param {JitsiLocalTrack} oldTrack the current stream in use to be replaced\n * @param {JitsiLocalTrack} newTrack the new stream to use\n * @returns {Promise} resolves when the replacement is finished\n */\nJitsiConference.prototype.replaceTrack = function(oldTrack, newTrack) {\n    const oldVideoType = oldTrack?.getVideoType();\n    const newVideoType = newTrack?.getVideoType();\n\n    if (FeatureFlags.isMultiStreamSupportEnabled() && oldTrack && newTrack && oldVideoType !== newVideoType) {\n        throw new Error(`Replacing a track of videoType=${oldVideoType} with a track of videoType=${newVideoType} is`\n            + ' not supported in this mode.');\n    }\n    const oldTrackBelongsToConference = this === oldTrack?.conference;\n\n    if (oldTrackBelongsToConference && oldTrack.disposed) {\n        return Promise.reject(new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n    }\n    if (newTrack?.disposed) {\n        return Promise.reject(new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n    }\n\n    if (oldTrack && !oldTrackBelongsToConference) {\n        logger.warn(`JitsiConference.replaceTrack oldTrack (${oldTrack} does not belong to this conference`);\n    }\n\n    // Now replace the stream at the lower levels\n    return this._doReplaceTrack(oldTrackBelongsToConference ? oldTrack : null, newTrack)\n        .then(() => {\n            oldTrackBelongsToConference && this.onLocalTrackRemoved(oldTrack);\n            newTrack && this._setupNewTrack(newTrack);\n\n            // Send 'VideoTypeMessage' on the bridge channel when a video track is added/removed.\n            if ((oldTrackBelongsToConference && oldTrack?.isVideoTrack()) || newTrack?.isVideoTrack()) {\n                this._sendBridgeVideoTypeMessage(newTrack);\n            }\n\n            // updates presence when we replace the video tracks desktop with screen and screen with desktop\n            if (oldTrackBelongsToConference && oldTrack?.isVideoTrack()\n\n                // we do not want to send presence update during setEffect switching, which does remove and then add\n                && !(oldTrack?._setEffectInProgress || newTrack?._setEffectInProgress)) {\n                this._updateRoomPresence(this.getActiveMediaSession());\n            }\n\n            if (newTrack !== null && (this.isMutedByFocus || this.isVideoMutedByFocus)) {\n                this._fireMuteChangeEvent(newTrack);\n            }\n\n            return Promise.resolve();\n        })\n        .catch(error => {\n            logger.error(`replaceTrack failed: ${error?.stack}`);\n\n            return Promise.reject(error);\n        });\n};\n\n/**\n * Replaces the tracks at the lower level by going through the Jingle session\n * and WebRTC peer connection. The method will resolve immediately if there is\n * currently no JingleSession started.\n * @param {JitsiLocalTrack|null} oldTrack the track to be removed during\n * the process or <tt>null</t> if the method should act as \"add track\"\n * @param {JitsiLocalTrack|null} newTrack the new track to be added or\n * <tt>null</tt> if the method should act as \"remove track\"\n * @return {Promise} resolved when the process is done or rejected with a string\n * which describes the error.\n * @private\n */\nJitsiConference.prototype._doReplaceTrack = function(oldTrack, newTrack) {\n    const replaceTrackPromises = [];\n\n    if (this.jvbJingleSession) {\n        replaceTrackPromises.push(this.jvbJingleSession.replaceTrack(oldTrack, newTrack));\n    } else {\n        logger.info('_doReplaceTrack - no JVB JingleSession');\n    }\n\n    if (this.p2pJingleSession) {\n        replaceTrackPromises.push(this.p2pJingleSession.replaceTrack(oldTrack, newTrack));\n    } else {\n        logger.info('_doReplaceTrack - no P2P JingleSession');\n    }\n\n    return Promise.all(replaceTrackPromises);\n};\n\n/**\n * Handler for when a source-add for a local source is rejected by Jicofo.\n *\n * @param {JingleSessionPC} jingleSession - The media session.\n * @param {Error} error - The error message.\n * @param {MediaType} mediaType - The media type of the track associated with the source that was rejected.\n * @returns {void}\n */\nJitsiConference.prototype._removeLocalSourceOnReject = function(jingleSession, error, mediaType) {\n    if (!jingleSession) {\n        return;\n    }\n    logger.warn(`Source-add rejected on ${jingleSession}, reason=\"${error?.reason}\", message=\"${error?.msg}\"`);\n    const track = this.getLocalTracks(mediaType)[0];\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_UNMUTE_REJECTED, track);\n};\n\n/**\n * Operations related to creating a new track\n * @param {JitsiLocalTrack} newTrack the new track being created\n */\nJitsiConference.prototype._setupNewTrack = function(newTrack) {\n    const mediaType = newTrack.getType();\n\n    if (newTrack.isAudioTrack() || (newTrack.isVideoTrack() && newTrack.videoType !== VideoType.DESKTOP)) {\n        // Report active device to statistics\n        const devices = RTC.getCurrentlyAvailableMediaDevices();\n        const device = devices\n            .find(d => d.kind === `${newTrack.getTrack().kind}input` && d.label === newTrack.getTrack().label);\n\n        if (device) {\n            Statistics.sendActiveDeviceListEvent(RTC.getEventDataForActiveDevice(device));\n        }\n    }\n\n    // Create a source name for this track if it doesn't exist.\n    if (FeatureFlags.isSourceNameSignalingEnabled() && !newTrack.getSourceName()) {\n        const sourceName = getSourceNameForJitsiTrack(\n            this.myUserId(),\n            mediaType,\n            this.getLocalTracks(mediaType)?.length);\n\n        newTrack.setSourceName(sourceName);\n    }\n\n    this.rtc.addLocalTrack(newTrack);\n    newTrack.setConference(this);\n\n    // Add event handlers.\n    newTrack.muteHandler = this._fireMuteChangeEvent.bind(this, newTrack);\n    newTrack.addEventListener(JitsiTrackEvents.TRACK_MUTE_CHANGED, newTrack.muteHandler);\n\n    if (newTrack.isAudioTrack()) {\n        newTrack.audioLevelHandler = this._fireAudioLevelChangeEvent.bind(this);\n        newTrack.addEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, newTrack.audioLevelHandler);\n    }\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, newTrack);\n};\n\n/**\n * Sets the video type.\n * @param track\n * @return <tt>true</tt> if video type was changed in presence.\n * @private\n */\nJitsiConference.prototype._setNewVideoType = function(track) {\n    let videoTypeChanged = false;\n\n    if (FeatureFlags.isSourceNameSignalingEnabled() && track) {\n        videoTypeChanged = this._signalingLayer.setTrackVideoType(track.getSourceName(), track.videoType);\n    }\n\n    if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n        const videoTypeTagName = 'videoType';\n\n        // If track is missing we revert to default type Camera, the case where we screenshare and\n        // we return to be video muted.\n        const trackVideoType = track ? track.videoType : VideoType.CAMERA;\n\n        // If video type is camera and there is no videoType in presence, we skip adding it, as this is the default one\n        if (trackVideoType !== VideoType.CAMERA || this.room.getFromPresence(videoTypeTagName)) {\n            // We will not use this.sendCommand here to avoid sending the presence immediately, as later we may also\n            // set the mute status.\n            const legacyTypeChanged = this.room.addOrReplaceInPresence(videoTypeTagName, { value: trackVideoType });\n\n            videoTypeChanged = videoTypeChanged || legacyTypeChanged;\n        }\n    }\n\n    return videoTypeChanged;\n};\n\n/**\n * Sets mute status.\n * @param mediaType\n * @param localTrack\n * @param isMuted\n * @param <tt>true</tt> when presence was changed, <tt>false</tt> otherwise.\n * @private\n */\nJitsiConference.prototype._setTrackMuteStatus = function(mediaType, localTrack, isMuted) {\n    let presenceChanged = false;\n\n    if (FeatureFlags.isSourceNameSignalingEnabled() && localTrack) {\n        presenceChanged = this._signalingLayer.setTrackMuteStatus(localTrack.getSourceName(), isMuted);\n    }\n\n    // Add the 'audioMuted' and 'videoMuted' tags when source name signaling is enabled for backward compatibility.\n    // It won't be used anymore when multiple stream support is enabled.\n    if (!FeatureFlags.isMultiStreamSupportEnabled()) {\n        let audioMuteChanged, videoMuteChanged;\n\n        if (!this.room) {\n            return false;\n        }\n\n        if (mediaType === MediaType.AUDIO) {\n            audioMuteChanged = this.room.addAudioInfoToPresence(isMuted);\n        } else {\n            videoMuteChanged = this.room.addVideoInfoToPresence(isMuted);\n        }\n\n        presenceChanged = presenceChanged || audioMuteChanged || videoMuteChanged;\n    }\n\n    return presenceChanged;\n};\n\n/**\n * Method called by the {@link JitsiLocalTrack} (a video one) in order to add\n * back the underlying WebRTC MediaStream to the PeerConnection (which has\n * removed on video mute).\n * @param {JitsiLocalTrack} track the local track that will be added as part of\n * the unmute operation.\n * @return {Promise} resolved when the process is done or rejected with a string\n * which describes the error.\n */\nJitsiConference.prototype._addLocalTrackAsUnmute = function(track) {\n    const addAsUnmutePromises = [];\n\n    if (this.jvbJingleSession) {\n        addAsUnmutePromises.push(this.jvbJingleSession.addTrackAsUnmute(track));\n    } else {\n        logger.debug('Add local MediaStream as unmute - no JVB Jingle session started yet');\n    }\n\n    if (this.p2pJingleSession) {\n        addAsUnmutePromises.push(this.p2pJingleSession.addTrackAsUnmute(track));\n    } else {\n        logger.debug('Add local MediaStream as unmute - no P2P Jingle session started yet');\n    }\n\n    return Promise.allSettled(addAsUnmutePromises);\n};\n\n/**\n * Method called by the {@link JitsiLocalTrack} (a video one) in order to remove\n * the underlying WebRTC MediaStream from the PeerConnection. The purpose of\n * that is to stop sending any data and turn off the HW camera device.\n * @param {JitsiLocalTrack} track the local track that will be removed.\n * @return {Promise}\n */\nJitsiConference.prototype._removeLocalTrackAsMute = function(track) {\n    const removeAsMutePromises = [];\n\n    if (this.jvbJingleSession) {\n        removeAsMutePromises.push(this.jvbJingleSession.removeTrackAsMute(track));\n    } else {\n        logger.debug('Remove local MediaStream - no JVB JingleSession started yet');\n    }\n    if (this.p2pJingleSession) {\n        removeAsMutePromises.push(this.p2pJingleSession.removeTrackAsMute(track));\n    } else {\n        logger.debug('Remove local MediaStream - no P2P JingleSession started yet');\n    }\n\n    return Promise.allSettled(removeAsMutePromises);\n};\n\n/**\n * Get role of the local user.\n * @returns {string} user role: 'moderator' or 'none'\n */\nJitsiConference.prototype.getRole = function() {\n    return this.room.role;\n};\n\n/**\n * Returns whether or not the current conference has been joined as a hidden\n * user.\n *\n * @returns {boolean|null} True if hidden, false otherwise. Will return null if\n * no connection is active.\n */\nJitsiConference.prototype.isHidden = function() {\n    if (!this.connection) {\n        return null;\n    }\n\n    return Strophe.getDomainFromJid(this.connection.getJid())\n        === this.options.config.hiddenDomain;\n};\n\n/**\n * Check if local user is moderator.\n * @returns {boolean|null} true if local user is moderator, false otherwise. If\n * we're no longer in the conference room then <tt>null</tt> is returned.\n */\nJitsiConference.prototype.isModerator = function() {\n    return this.room ? this.room.isModerator() : null;\n};\n\n/**\n * Set password for the room.\n * @param {string} password new password for the room.\n * @returns {Promise}\n */\nJitsiConference.prototype.lock = function(password) {\n    if (!this.isModerator()) {\n        return Promise.reject(new Error('You are not moderator.'));\n    }\n\n    return new Promise((resolve, reject) => {\n        this.room.lockRoom(\n            password || '',\n            () => resolve(),\n            err => reject(err),\n            () => reject(JitsiConferenceErrors.PASSWORD_NOT_SUPPORTED));\n    });\n};\n\n/**\n * Remove password from the room.\n * @returns {Promise}\n */\nJitsiConference.prototype.unlock = function() {\n    return this.lock();\n};\n\n/**\n * Elects the participant with the given id to be the selected participant in\n * order to receive higher video quality (if simulcast is enabled).\n * Or cache it if channel is not created and send it once channel is available.\n * @param participantId the identifier of the participant\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @returns {void}\n */\nJitsiConference.prototype.selectParticipant = function(participantId) {\n    this.selectParticipants([ participantId ]);\n};\n\n/*\n * Elects participants with given ids to be the selected participants in order\n * to receive higher video quality (if simulcast is enabled). The argument\n * should be an array of participant id strings or an empty array; an error will\n * be thrown if a non-array is passed in. The error is thrown as a layer of\n * protection against passing an invalid argument, as the error will happen in\n * the bridge and may not be visible in the client.\n *\n * @param {Array<strings>} participantIds - An array of identifiers for\n * participants.\n * @returns {void}\n */\nJitsiConference.prototype.selectParticipants = function(participantIds) {\n    if (!Array.isArray(participantIds)) {\n        throw new Error('Invalid argument; participantIds must be an array.');\n    }\n\n    this.receiveVideoController.selectEndpoints(participantIds);\n};\n\n/**\n * Obtains the current value for \"lastN\". See {@link setLastN} for more info.\n * @returns {number}\n */\nJitsiConference.prototype.getLastN = function() {\n    return this.receiveVideoController.getLastN();\n};\n\n/**\n * Obtains the forwarded sources list in this conference.\n * @return {Array<string>|null}\n */\nJitsiConference.prototype.getForwardedSources = function() {\n    return this.rtc.getForwardedSources();\n};\n\n/**\n * Selects a new value for \"lastN\". The requested amount of videos are going\n * to be delivered after the value is in effect. Set to -1 for unlimited or\n * all available videos.\n * @param lastN the new number of videos the user would like to receive.\n * @throws Error or RangeError if the given value is not a number or is smaller\n * than -1.\n */\nJitsiConference.prototype.setLastN = function(lastN) {\n    if (!Number.isInteger(lastN) && !Number.parseInt(lastN, 10)) {\n        throw new Error(`Invalid value for lastN: ${lastN}`);\n    }\n    const n = Number(lastN);\n\n    if (n < -1) {\n        throw new RangeError('lastN cannot be smaller than -1');\n    }\n    this.receiveVideoController.setLastN(n);\n\n    // If the P2P session is not fully established yet, we wait until it gets\n    // established.\n    if (this.p2pJingleSession) {\n        const isVideoActive = n !== 0;\n\n        this.p2pJingleSession\n            .setMediaTransferActive(true, isVideoActive)\n            .catch(error => {\n                logger.error(\n                    `Failed to adjust video transfer status (${isVideoActive})`,\n                    error);\n            });\n    }\n};\n\n/**\n * Checks if the participant given by participantId is currently included in\n * the last N.\n * @param {string} participantId the identifier of the participant we would\n * like to check.\n * @return {boolean} true if the participant with id is in the last N set or\n * if there's no last N set, false otherwise.\n * @deprecated this method should never be used to figure out the UI, but\n * {@link ParticipantConnectionStatus} should be used instead.\n */\nJitsiConference.prototype.isInLastN = function(participantId) {\n    return this.rtc.isInLastN(participantId);\n};\n\n/**\n * @return Array<JitsiParticipant> an array of all participants in this\n * conference.\n */\nJitsiConference.prototype.getParticipants = function() {\n    return Object.values(this.participants);\n};\n\n/**\n * Returns the number of participants in the conference, including the local\n * participant.\n * @param countHidden {boolean} Whether or not to include hidden participants\n * in the count. Default: false.\n **/\nJitsiConference.prototype.getParticipantCount = function(countHidden = false) {\n    let participants = this.getParticipants();\n\n    if (!countHidden) {\n        participants = participants.filter(p => !p.isHidden());\n    }\n\n    // Add one for the local participant.\n    return participants.length + 1;\n};\n\n/**\n * @returns {JitsiParticipant} the participant in this conference with the\n * specified id (or undefined if there isn't one).\n * @param id the id of the participant.\n */\nJitsiConference.prototype.getParticipantById = function(id) {\n    return this.participants[id];\n};\n\n/**\n * Grant owner rights to the participant.\n * @param {string} id id of the participant to grant owner rights to.\n */\nJitsiConference.prototype.grantOwner = function(id) {\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    this.room.setAffiliation(participant.getJid(), 'owner');\n};\n\n/**\n * Revoke owner rights to the participant or local Participant as\n * the user might want to refuse to be a moderator.\n * @param {string} id id of the participant to revoke owner rights to.\n */\nJitsiConference.prototype.revokeOwner = function(id) {\n    const participant = this.getParticipantById(id);\n    const isMyself = this.myUserId() === id;\n    const role = this.isMembersOnly() ? 'member' : 'none';\n\n    if (isMyself) {\n        this.room.setAffiliation(this.room.myroomjid, role);\n    } else if (participant) {\n        this.room.setAffiliation(participant.getJid(), role);\n    }\n};\n\n\n/**\n * Kick participant from this conference.\n * @param {string} id id of the participant to kick\n * @param {string} reason reason of the participant to kick\n */\nJitsiConference.prototype.kickParticipant = function(id, reason) {\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    this.room.kick(participant.getJid(), reason);\n};\n\n/**\n * Maybe clears the timeout which emits {@link ACTION_JINGLE_SI_TIMEOUT}\n * analytics event.\n * @private\n */\nJitsiConference.prototype._maybeClearSITimeout = function() {\n    if (this._sessionInitiateTimeout\n            && (this.jvbJingleSession || this.getParticipantCount() < 2)) {\n        window.clearTimeout(this._sessionInitiateTimeout);\n        this._sessionInitiateTimeout = null;\n    }\n};\n\n/**\n * Sets a timeout which will emit {@link ACTION_JINGLE_SI_TIMEOUT} analytics\n * event.\n * @private\n */\nJitsiConference.prototype._maybeSetSITimeout = function() {\n    // Jicofo is supposed to invite if there are at least 2 participants\n    if (!this.jvbJingleSession\n            && this.getParticipantCount() >= 2\n            && !this._sessionInitiateTimeout) {\n        this._sessionInitiateTimeout = window.setTimeout(() => {\n            this._sessionInitiateTimeout = null;\n            Statistics.sendAnalytics(createJingleEvent(\n                ACTION_JINGLE_SI_TIMEOUT,\n                {\n                    p2p: false,\n                    value: JINGLE_SI_TIMEOUT\n                }));\n        }, JINGLE_SI_TIMEOUT);\n    }\n};\n\n/**\n * Mutes a participant.\n * @param {string} id The id of the participant to mute.\n */\nJitsiConference.prototype.muteParticipant = function(id, mediaType) {\n    const muteMediaType = mediaType ? mediaType : MediaType.AUDIO;\n\n    if (muteMediaType !== MediaType.AUDIO && muteMediaType !== MediaType.VIDEO) {\n        logger.error(`Unsupported media type: ${muteMediaType}`);\n\n        return;\n    }\n\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    this.room.muteParticipant(participant.getJid(), true, muteMediaType);\n};\n\n/* eslint-disable max-params */\n\n/**\n * Notifies this JitsiConference that a new member has joined its chat room.\n *\n * FIXME This should NOT be exposed!\n *\n * @param jid the jid of the participant in the MUC\n * @param nick the display name of the participant\n * @param role the role of the participant in the MUC\n * @param isHidden indicates if this is a hidden participant (system\n * participant for example a recorder).\n * @param statsID the participant statsID (optional)\n * @param status the initial status if any\n * @param identity the member identity, if any\n * @param botType the member botType, if any\n * @param fullJid the member full jid, if any\n * @param features the member botType, if any\n * @param isReplaceParticipant whether this join replaces a participant with\n * the same jwt.\n */\nJitsiConference.prototype.onMemberJoined = function(\n        jid, nick, role, isHidden, statsID, status, identity, botType, fullJid, features, isReplaceParticipant) {\n    const id = Strophe.getResourceFromJid(jid);\n\n    if (id === 'focus' || this.myUserId() === id) {\n        return;\n    }\n\n    const participant\n        = new JitsiParticipant(jid, this, nick, isHidden, statsID, status, identity);\n\n    participant.setRole(role);\n    participant.setBotType(botType);\n    participant.setFeatures(features);\n    participant.setIsReplacing(isReplaceParticipant);\n\n    this.participants[id] = participant;\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.USER_JOINED,\n        id,\n        participant);\n\n    this._updateFeatures(participant);\n\n    // maybeStart only if we had finished joining as then we will have information for the number of participants\n    if (this.isJoined()) {\n        this._maybeStartOrStopP2P();\n    }\n\n    this._maybeSetSITimeout();\n};\n\n/* eslint-enable max-params */\n\n/**\n * Get notified when we joined the room.\n *\n * FIXME This should NOT be exposed!\n *\n * @private\n */\nJitsiConference.prototype._onMucJoined = function() {\n    this._maybeStartOrStopP2P();\n};\n\n/**\n * Updates features for a participant.\n * @param {JitsiParticipant} participant - The participant to query for features.\n * @returns {void}\n * @private\n */\nJitsiConference.prototype._updateFeatures = function(participant) {\n    participant.getFeatures()\n        .then(features => {\n            participant._supportsDTMF = features.has('urn:xmpp:jingle:dtmf:0');\n            this.updateDTMFSupport();\n\n            if (features.has(FEATURE_JIGASI)) {\n                participant.setProperty('features_jigasi', true);\n            }\n\n            if (features.has(FEATURE_E2EE)) {\n                participant.setProperty('features_e2ee', true);\n            }\n        })\n        .catch(() => false);\n};\n\n/**\n * Get notified when member bot type had changed.\n * @param jid the member jid\n * @param botType the new botType value\n * @private\n */\nJitsiConference.prototype._onMemberBotTypeChanged = function(jid, botType) {\n\n    // find the participant and mark it as non bot, as the real one will join\n    // in a moment\n    const peers = this.getParticipants();\n    const botParticipant = peers.find(p => p.getJid() === jid);\n\n    if (botParticipant) {\n        botParticipant.setBotType(botType);\n        const id = Strophe.getResourceFromJid(jid);\n\n        this.eventEmitter.emit(\n            JitsiConferenceEvents.BOT_TYPE_CHANGED,\n            id,\n            botType);\n    }\n\n    // if botType changed to undefined, botType was removed, in case of\n    // poltergeist mode this is the moment when the poltergeist had exited and\n    // the real participant had already replaced it.\n    // In this case we can check and try p2p\n    if (!botParticipant.getBotType()) {\n        this._maybeStartOrStopP2P();\n    }\n};\n\nJitsiConference.prototype.onMemberLeft = function(jid) {\n    const id = Strophe.getResourceFromJid(jid);\n\n    if (id === 'focus' || this.myUserId() === id) {\n        return;\n    }\n\n    const participant = this.participants[id];\n    const mediaSessions = this.getMediaSessions();\n    let tracksToBeRemoved = [];\n\n    for (const session of mediaSessions) {\n        const remoteTracks = session.peerconnection.getRemoteTracks(id);\n\n        remoteTracks && (tracksToBeRemoved = [ ...tracksToBeRemoved, ...remoteTracks ]);\n\n        // Remove the ssrcs from the remote description and renegotiate.\n        session.removeRemoteStreamsOnLeave(id);\n    }\n\n    // Fire the event before renegotiation is done so that the thumbnails can be removed immediately.\n    tracksToBeRemoved.forEach(track => {\n        this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track);\n    });\n\n    if (participant) {\n        delete this.participants[id];\n        this.eventEmitter.emit(JitsiConferenceEvents.USER_LEFT, id, participant);\n    }\n\n    if (this.room !== null) { // Skip if we have left the room already.\n        this._maybeStartOrStopP2P(true /* triggered by user left event */);\n        this._maybeClearSITimeout();\n    }\n};\n\n/* eslint-disable max-params */\n\n/**\n * Designates an event indicating that we were kicked from the XMPP MUC.\n * @param {boolean} isSelfPresence - whether it is for local participant\n * or another participant.\n * @param {string} actorId - the id of the participant who was initiator\n * of the kick.\n * @param {string?} kickedParticipantId - when it is not a kick for local participant,\n * this is the id of the participant which was kicked.\n * @param {string} reason - reason of the participant to kick\n * @param {boolean?} isReplaceParticipant - whether this is a server initiated kick in order\n * to replace it with a participant with same jwt.\n */\nJitsiConference.prototype.onMemberKicked = function(\n        isSelfPresence,\n        actorId,\n        kickedParticipantId,\n        reason,\n        isReplaceParticipant) {\n    // This check which be true when we kick someone else. With the introduction of lobby\n    // the ChatRoom KICKED event is now also emitted for ourselves (the kicker) so we want to\n    // avoid emitting an event where `undefined` kicked someone.\n    if (actorId === this.myUserId()) {\n        return;\n    }\n\n    const actorParticipant = this.participants[actorId];\n\n    if (isSelfPresence) {\n        this.eventEmitter.emit(\n            JitsiConferenceEvents.KICKED, actorParticipant, reason, isReplaceParticipant);\n\n        this.leave();\n\n        return;\n    }\n\n    const kickedParticipant = this.participants[kickedParticipantId];\n\n    kickedParticipant.setIsReplaced(isReplaceParticipant);\n\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.PARTICIPANT_KICKED, actorParticipant, kickedParticipant, reason);\n};\n\n/**\n * Method called on local MUC role change.\n * @param {string} role the name of new user's role as defined by XMPP MUC.\n */\nJitsiConference.prototype.onLocalRoleChanged = function(role) {\n    // Emit role changed for local  JID\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.USER_ROLE_CHANGED, this.myUserId(), role);\n};\n\nJitsiConference.prototype.onUserRoleChanged = function(jid, role) {\n    const id = Strophe.getResourceFromJid(jid);\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    participant.setRole(role);\n    this.eventEmitter.emit(JitsiConferenceEvents.USER_ROLE_CHANGED, id, role);\n};\n\nJitsiConference.prototype.onDisplayNameChanged = function(jid, displayName) {\n    const id = Strophe.getResourceFromJid(jid);\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n\n    if (participant._displayName === displayName) {\n        return;\n    }\n\n    participant._displayName = displayName;\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.DISPLAY_NAME_CHANGED,\n        id,\n        displayName);\n};\n\n/**\n * Notifies this JitsiConference that a JitsiRemoteTrack was added to the conference.\n *\n * @param {JitsiRemoteTrack} track the JitsiRemoteTrack which was added to this JitsiConference.\n */\nJitsiConference.prototype.onRemoteTrackAdded = function(track) {\n    if (track.isP2P && !this.isP2PActive()) {\n        logger.info('Trying to add remote P2P track, when not in P2P - IGNORED');\n\n        return;\n    } else if (!track.isP2P && this.isP2PActive()) {\n        logger.info('Trying to add remote JVB track, when in P2P - IGNORED');\n\n        return;\n    }\n\n    const id = track.getParticipantId();\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        logger.error(`No participant found for id: ${id}`);\n\n        return;\n    }\n\n    // Add track to JitsiParticipant.\n    participant._tracks.push(track);\n\n    if (this.transcriber) {\n        this.transcriber.addTrack(track);\n    }\n\n    const emitter = this.eventEmitter;\n\n    track.addEventListener(\n        JitsiTrackEvents.TRACK_MUTE_CHANGED,\n        () => emitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track));\n    track.isAudioTrack() && track.addEventListener(\n        JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n        (audioLevel, tpc) => {\n            const activeTPC = this.getActivePeerConnection();\n\n            if (activeTPC === tpc) {\n                emitter.emit(JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED, id, audioLevel);\n            }\n        }\n    );\n\n    emitter.emit(JitsiConferenceEvents.TRACK_ADDED, track);\n};\n\n/**\n * Callback called by the Jingle plugin when 'session-answer' is received.\n * @param {JingleSessionPC} session the Jingle session for which an answer was\n * received.\n * @param {jQuery} answer a jQuery selector pointing to 'jingle' IQ element\n */\n// eslint-disable-next-line no-unused-vars\nJitsiConference.prototype.onCallAccepted = function(session, answer) {\n    if (this.p2pJingleSession === session) {\n        logger.info('P2P setAnswer');\n\n        this.p2pJingleSession.setAnswer(answer);\n        this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_STARTED, this.p2pJingleSession);\n    }\n};\n\n/**\n * Callback called by the Jingle plugin when 'transport-info' is received.\n * @param {JingleSessionPC} session the Jingle session for which the IQ was\n * received\n * @param {jQuery} transportInfo a jQuery selector pointing to 'jingle' IQ\n * element\n */\n// eslint-disable-next-line no-unused-vars\nJitsiConference.prototype.onTransportInfo = function(session, transportInfo) {\n    if (this.p2pJingleSession === session) {\n        logger.info('P2P addIceCandidates');\n        this.p2pJingleSession.addIceCandidates(transportInfo);\n    }\n};\n\n/**\n * Notifies this JitsiConference that a JitsiRemoteTrack was removed from\n * the conference.\n *\n * @param {JitsiRemoteTrack} removedTrack\n */\nJitsiConference.prototype.onRemoteTrackRemoved = function(removedTrack) {\n    this.getParticipants().forEach(participant => {\n        const tracks = participant.getTracks();\n\n        for (let i = 0; i < tracks.length; i++) {\n            if (tracks[i] === removedTrack) {\n                // Since the tracks have been compared and are\n                // considered equal the result of splice can be ignored.\n                participant._tracks.splice(i, 1);\n\n                this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, removedTrack);\n\n                if (this.transcriber) {\n                    this.transcriber.removeTrack(removedTrack);\n                }\n\n                break;\n            }\n        }\n    }, this);\n};\n\n/**\n * Handles an incoming call event for the P2P jingle session.\n */\nJitsiConference.prototype._onIncomingCallP2P = function(jingleSession, jingleOffer) {\n\n    let rejectReason;\n\n    if ((!this.isP2PEnabled() && !this.isP2PTestModeEnabled()) || browser.isFirefox() || browser.isWebKitBased()) {\n        rejectReason = {\n            reason: 'decline',\n            reasonDescription: 'P2P disabled',\n            errorMsg: 'P2P mode disabled in the configuration'\n        };\n    } else if (this.p2pJingleSession) {\n        // Reject incoming P2P call (already in progress)\n        rejectReason = {\n            reason: 'busy',\n            reasonDescription: 'P2P already in progress',\n            errorMsg: 'Duplicated P2P \"session-initiate\"'\n        };\n    } else if (!this._shouldBeInP2PMode()) {\n        rejectReason = {\n            reason: 'decline',\n            reasonDescription: 'P2P requirements not met',\n            errorMsg: 'Received P2P \"session-initiate\" when should not be in P2P mode'\n        };\n        Statistics.sendAnalytics(createJingleEvent(ACTION_P2P_DECLINED));\n    }\n\n    if (rejectReason) {\n        this._rejectIncomingCall(jingleSession, rejectReason);\n    } else {\n        this._acceptP2PIncomingCall(jingleSession, jingleOffer);\n    }\n};\n\n/**\n * Handles an incoming call event.\n */\nJitsiConference.prototype.onIncomingCall = function(jingleSession, jingleOffer, now) {\n    // Handle incoming P2P call\n    if (jingleSession.isP2P) {\n        this._onIncomingCallP2P(jingleSession, jingleOffer);\n    } else {\n        if (!this.isFocus(jingleSession.remoteJid)) {\n            const description = 'Rejecting session-initiate from non-focus.';\n\n            this._rejectIncomingCall(\n                jingleSession, {\n                    reason: 'security-error',\n                    reasonDescription: description,\n                    errorMsg: description\n                });\n\n            return;\n        }\n        this._acceptJvbIncomingCall(jingleSession, jingleOffer, now);\n    }\n};\n\n/**\n * Accepts an incoming call event for the JVB jingle session.\n */\nJitsiConference.prototype._acceptJvbIncomingCall = function(jingleSession, jingleOffer, now) {\n\n    // Accept incoming call\n    this.jvbJingleSession = jingleSession;\n    this.room.connectionTimes['session.initiate'] = now;\n    this._sendConferenceJoinAnalyticsEvent();\n\n    if (this.wasStopped) {\n        Statistics.sendAnalyticsAndLog(createJingleEvent(ACTION_JINGLE_RESTART, { p2p: false }));\n    }\n\n    const serverRegion\n        = $(jingleOffer)\n            .find('>bridge-session[xmlns=\"http://jitsi.org/protocol/focus\"]')\n            .attr('region');\n\n    this.eventEmitter.emit(JitsiConferenceEvents.SERVER_REGION_CHANGED, serverRegion);\n\n    this._maybeClearSITimeout();\n    Statistics.sendAnalytics(createJingleEvent(\n        ACTION_JINGLE_SI_RECEIVED,\n        {\n            p2p: false,\n            value: now\n        }));\n\n    try {\n        jingleSession.initialize(\n            this.room,\n            this.rtc,\n            this._signalingLayer,\n            {\n                ...this.options.config,\n                enableInsertableStreams: this.isE2EEEnabled()\n            });\n    } catch (error) {\n        GlobalOnErrorHandler.callErrorHandler(error);\n        logger.error(error);\n\n        return;\n    }\n\n    // Open a channel with the videobridge.\n    this._setBridgeChannel(jingleOffer, jingleSession.peerconnection);\n\n    const localTracks = this._getInitialLocalTracks();\n\n    try {\n        jingleSession.acceptOffer(\n            jingleOffer,\n            () => {\n                // If for any reason invite for the JVB session arrived after\n                // the P2P has been established already the media transfer needs\n                // to be turned off here.\n                if (this.isP2PActive() && this.jvbJingleSession) {\n                    this._suspendMediaTransferForJvbConnection();\n                }\n\n                this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_STARTED, jingleSession);\n                if (!this.isP2PActive()) {\n                    this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED, jingleSession);\n                }\n            },\n            error => {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error('Failed to accept incoming Jingle session', error);\n            },\n            localTracks\n        );\n\n        // Enable or disable simulcast for plan-b screensharing based on the capture fps if it is set through the UI.\n        this._desktopSharingFrameRate\n            && jingleSession.peerconnection.setDesktopSharingFrameRate(this._desktopSharingFrameRate);\n\n        // Start callstats as soon as peerconnection is initialized,\n        // do not wait for XMPPEvents.PEERCONNECTION_READY, as it may never\n        // happen in case if user doesn't have or denied permission to\n        // both camera and microphone.\n        logger.info('Starting CallStats for JVB connection...');\n        this.statistics.startCallStats(\n            this.jvbJingleSession.peerconnection,\n            'jitsi' /* Remote user ID for JVB is 'jitsi' */);\n        this.statistics.startRemoteStats(this.jvbJingleSession.peerconnection);\n    } catch (e) {\n        GlobalOnErrorHandler.callErrorHandler(e);\n        logger.error(e);\n    }\n};\n\n/**\n * Sets the BridgeChannel.\n *\n * @param {jQuery} offerIq a jQuery selector pointing to the jingle element of\n * the offer IQ which may carry the WebSocket URL for the 'websocket'\n * BridgeChannel mode.\n * @param {TraceablePeerConnection} pc the peer connection which will be used\n * to listen for new WebRTC Data Channels (in the 'datachannel' mode).\n */\nJitsiConference.prototype._setBridgeChannel = function(offerIq, pc) {\n    let wsUrl = null;\n    const webSocket\n        = $(offerIq)\n            .find('>content>transport>web-socket')\n            .first();\n\n    if (webSocket.length === 1) {\n        wsUrl = webSocket[0].getAttribute('url');\n    }\n\n    if (wsUrl) {\n        // If the offer contains a websocket use it.\n        this.rtc.initializeBridgeChannel(null, wsUrl);\n    } else {\n        // Otherwise, fall back to an attempt to use SCTP.\n        this.rtc.initializeBridgeChannel(pc, null);\n    }\n};\n\n/**\n * Rejects incoming Jingle call.\n * @param {JingleSessionPC} jingleSession the session instance to be rejected.\n * @param {object} [options]\n * @param {string} options.reason the name of the reason element as defined\n * by Jingle\n * @param {string} options.reasonDescription the reason description which will\n * be included in Jingle 'session-terminate' message.\n * @param {string} options.errorMsg an error message to be logged on global\n * error handler\n * @private\n */\nJitsiConference.prototype._rejectIncomingCall = function(jingleSession, options) {\n    if (options && options.errorMsg) {\n        GlobalOnErrorHandler.callErrorHandler(new Error(options.errorMsg));\n    }\n\n    // Terminate the jingle session with a reason\n    jingleSession.terminate(\n        null /* success callback => we don't care */,\n        error => {\n            logger.warn(\n                'An error occurred while trying to terminate'\n                    + ' invalid Jingle session', error);\n        }, {\n            reason: options && options.reason,\n            reasonDescription: options && options.reasonDescription,\n            sendSessionTerminate: true\n        });\n};\n\n/**\n * Handles the call ended event.\n * XXX is this due to the remote side terminating the Jingle session?\n *\n * @param {JingleSessionPC} jingleSession the jingle session which has been\n * terminated.\n * @param {String} reasonCondition the Jingle reason condition.\n * @param {String|null} reasonText human readable reason text which may provide\n * more details about why the call has been terminated.\n */\nJitsiConference.prototype.onCallEnded = function(jingleSession, reasonCondition, reasonText) {\n    logger.info(\n        `Call ended: ${reasonCondition} - ${reasonText} P2P ?${\n            jingleSession.isP2P}`);\n    if (jingleSession === this.jvbJingleSession) {\n        this.wasStopped = true;\n\n        Statistics.sendAnalytics(\n            createJingleEvent(ACTION_JINGLE_TERMINATE, { p2p: false }));\n\n        // Stop the stats\n        if (this.statistics) {\n            this.statistics.stopRemoteStats(\n                this.jvbJingleSession.peerconnection);\n            logger.info('Stopping JVB CallStats');\n            this.statistics.stopCallStats(\n                this.jvbJingleSession.peerconnection);\n        }\n\n        // Current JVB JingleSession is no longer valid, so set it to null\n        this.jvbJingleSession = null;\n\n        // Let the RTC service do any cleanups\n        this.rtc.onCallEnded();\n    } else if (jingleSession === this.p2pJingleSession) {\n        const stopOptions = {};\n\n        // It's the responder who decides to enforce JVB mode, so that both\n        // initiator and responder are aware if it was intentional.\n        if (reasonCondition === 'decline' && reasonText === 'force JVB121') {\n            logger.info('In forced JVB 121 mode...');\n            Statistics.analytics.addPermanentProperties({ forceJvb121: true });\n        } else if (reasonCondition === 'connectivity-error'\n            && reasonText === 'ICE FAILED') {\n            // It can happen that the other peer detects ICE failed and\n            // terminates the session, before we get the event on our side.\n            // But we are able to parse the reason and mark it here.\n            Statistics.analytics.addPermanentProperties({ p2pFailed: true });\n        } else if (reasonCondition === 'success' && reasonText === 'restart') {\n            // When we are restarting media sessions we don't want to switch the tracks\n            // to the JVB just yet.\n            stopOptions.requestRestart = true;\n        }\n        this._stopP2PSession(stopOptions);\n    } else {\n        logger.error(\n            'Received onCallEnded for invalid session',\n            jingleSession.sid,\n            jingleSession.remoteJid,\n            reasonCondition,\n            reasonText);\n    }\n};\n\n/**\n * Handles the suspend detected event. Leaves the room and fires suspended.\n * @param {JingleSessionPC} jingleSession\n */\nJitsiConference.prototype.onSuspendDetected = function(jingleSession) {\n    if (!jingleSession.isP2P) {\n        this.leave();\n        this.eventEmitter.emit(JitsiConferenceEvents.SUSPEND_DETECTED);\n    }\n};\n\nJitsiConference.prototype.updateDTMFSupport = function() {\n    let somebodySupportsDTMF = false;\n    const participants = this.getParticipants();\n\n    // check if at least 1 participant supports DTMF\n    for (let i = 0; i < participants.length; i += 1) {\n        if (participants[i].supportsDTMF()) {\n            somebodySupportsDTMF = true;\n            break;\n        }\n    }\n    if (somebodySupportsDTMF !== this.somebodySupportsDTMF) {\n        this.somebodySupportsDTMF = somebodySupportsDTMF;\n        this.eventEmitter.emit(\n            JitsiConferenceEvents.DTMF_SUPPORT_CHANGED,\n            somebodySupportsDTMF);\n    }\n};\n\n/**\n * Allows to check if there is at least one user in the conference\n * that supports DTMF.\n * @returns {boolean} true if somebody supports DTMF, false otherwise\n */\nJitsiConference.prototype.isDTMFSupported = function() {\n    return this.somebodySupportsDTMF;\n};\n\n/**\n * Returns the local user's ID\n * @return {string} local user's ID\n */\nJitsiConference.prototype.myUserId = function() {\n    return (\n        this.room && this.room.myroomjid\n            ? Strophe.getResourceFromJid(this.room.myroomjid)\n            : null);\n};\n\nJitsiConference.prototype.sendTones = function(tones, duration, pause) {\n    const peerConnection = this.getActivePeerConnection();\n\n    if (peerConnection) {\n        peerConnection.sendTones(tones, duration, pause);\n    } else {\n        logger.warn('cannot sendTones: no peer connection');\n    }\n};\n\n/**\n * Starts recording the current conference.\n *\n * @param {Object} options - Configuration for the recording. See\n * {@link Chatroom#startRecording} for more info.\n * @returns {Promise} See {@link Chatroom#startRecording} for more info.\n */\nJitsiConference.prototype.startRecording = function(options) {\n    if (this.room) {\n        return this.recordingManager.startRecording(options);\n    }\n\n    return Promise.reject(new Error('The conference is not created yet!'));\n};\n\n/**\n * Stop a recording session.\n *\n * @param {string} sessionID - The ID of the recording session that\n * should be stopped.\n * @returns {Promise} See {@link Chatroom#stopRecording} for more info.\n */\nJitsiConference.prototype.stopRecording = function(sessionID) {\n    if (this.room) {\n        return this.recordingManager.stopRecording(sessionID);\n    }\n\n    return Promise.reject(new Error('The conference is not created yet!'));\n};\n\n/**\n * Returns true if the SIP calls are supported and false otherwise\n */\nJitsiConference.prototype.isSIPCallingSupported = function() {\n    if (this.room) {\n        return this.room.isSIPCallingSupported();\n    }\n\n    return false;\n};\n\n/**\n * Dials a number.\n * @param number the number\n */\nJitsiConference.prototype.dial = function(number) {\n    if (this.room) {\n        return this.room.dial(number);\n    }\n\n    return new Promise((resolve, reject) => {\n        reject(new Error('The conference is not created yet!'));\n    });\n};\n\n/**\n * Hangup an existing call\n */\nJitsiConference.prototype.hangup = function() {\n    if (this.room) {\n        return this.room.hangup();\n    }\n\n    return new Promise((resolve, reject) => {\n        reject(new Error('The conference is not created yet!'));\n    });\n};\n\n/**\n * Starts the transcription service.\n */\nJitsiConference.prototype.startTranscriber = function() {\n    return this.dial('jitsi_meet_transcribe');\n};\n\n\n/**\n * Stops the transcription service.\n */\nJitsiConference.prototype.stopTranscriber = JitsiConference.prototype.hangup;\n\n/**\n * Returns the phone number for joining the conference.\n */\nJitsiConference.prototype.getPhoneNumber = function() {\n    if (this.room) {\n        return this.room.getPhoneNumber();\n    }\n\n    return null;\n};\n\n/**\n * Returns the pin for joining the conference with phone.\n */\nJitsiConference.prototype.getPhonePin = function() {\n    if (this.room) {\n        return this.room.getPhonePin();\n    }\n\n    return null;\n};\n\n/**\n * Returns the meeting unique ID if any.\n *\n * @returns {string|undefined}\n */\nJitsiConference.prototype.getMeetingUniqueId = function() {\n    if (this.room) {\n        return this.room.getMeetingId();\n    }\n};\n\n/**\n * Will return P2P or JVB <tt>TraceablePeerConnection</tt> depending on\n * which connection is currently active.\n *\n * @return {TraceablePeerConnection|null} null if there isn't any active\n * <tt>TraceablePeerConnection</tt> currently available.\n * @public (FIXME how to make package local ?)\n */\nJitsiConference.prototype.getActivePeerConnection = function() {\n    const session = this.isP2PActive() ? this.p2pJingleSession : this.jvbJingleSession;\n\n    return session ? session.peerconnection : null;\n};\n\n/**\n * Returns the connection state for the current room. Its ice connection state\n * for its session.\n * NOTE that \"completed\" ICE state which can appear on the P2P connection will\n * be converted to \"connected\".\n * @return {string|null} ICE state name or <tt>null</tt> if there is no active\n * peer connection at this time.\n */\nJitsiConference.prototype.getConnectionState = function() {\n    const peerConnection = this.getActivePeerConnection();\n\n    return peerConnection ? peerConnection.getConnectionState() : null;\n};\n\n/**\n * Make all new participants mute their audio/video on join.\n * @param policy {Object} object with 2 boolean properties for video and audio:\n * @param {boolean} audio if audio should be muted.\n * @param {boolean} video if video should be muted.\n */\nJitsiConference.prototype.setStartMutedPolicy = function(policy) {\n    if (!this.isModerator()) {\n        logger.warn(`Failed to set start muted policy, ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator'}`);\n\n        return;\n    }\n    this.startMutedPolicy = policy;\n    this.room.addOrReplaceInPresence('startmuted', {\n        attributes: {\n            audio: policy.audio,\n            video: policy.video,\n            xmlns: 'http://jitsi.org/jitmeet/start-muted'\n        }\n    }) && this.room.sendPresence();\n};\n\n/**\n * Returns current start muted policy\n * @returns {Object} with 2 properties - audio and video.\n */\nJitsiConference.prototype.getStartMutedPolicy = function() {\n    return this.startMutedPolicy;\n};\n\n/**\n * Check if audio is muted on join.\n */\nJitsiConference.prototype.isStartAudioMuted = function() {\n    return this.startAudioMuted;\n};\n\n/**\n * Check if video is muted on join.\n */\nJitsiConference.prototype.isStartVideoMuted = function() {\n    return this.startVideoMuted;\n};\n\n/**\n * Returns measured connectionTimes.\n */\nJitsiConference.prototype.getConnectionTimes = function() {\n    return this.room.connectionTimes;\n};\n\n/**\n * Sets a property for the local participant.\n */\nJitsiConference.prototype.setLocalParticipantProperty = function(name, value) {\n    this.sendCommand(`jitsi_participant_${name}`, { value });\n};\n\n/**\n *  Removes a property for the local participant and sends the updated presence.\n */\nJitsiConference.prototype.removeLocalParticipantProperty = function(name) {\n    this.removeCommand(`jitsi_participant_${name}`);\n    this.room.sendPresence();\n};\n\n/**\n * Gets a local participant property.\n *\n * @return value of the local participant property if the tagName exists in the\n * list of properties, otherwise returns undefined.\n */\nJitsiConference.prototype.getLocalParticipantProperty = function(name) {\n    const property = this.room.presMap.nodes.find(prop =>\n        prop.tagName === `jitsi_participant_${name}`\n    );\n\n    return property ? property.value : undefined;\n};\n\n/**\n * Sends the given feedback through CallStats if enabled.\n *\n * @param overallFeedback an integer between 1 and 5 indicating the\n * user feedback\n * @param detailedFeedback detailed feedback from the user. Not yet used\n * @returns {Promise} Resolves if feedback is submitted successfully.\n */\nJitsiConference.prototype.sendFeedback = function(overallFeedback, detailedFeedback) {\n    return this.statistics.sendFeedback(overallFeedback, detailedFeedback);\n};\n\n/**\n * Returns true if the callstats integration is enabled, otherwise returns\n * false.\n *\n * @returns true if the callstats integration is enabled, otherwise returns\n * false.\n */\nJitsiConference.prototype.isCallstatsEnabled = function() {\n    return this.statistics.isCallstatsEnabled();\n};\n\n/**\n * Finds the SSRC of a given track\n *\n * @param track\n * @returns {number|undefined} the SSRC of the specificed track, otherwise undefined.\n */\nJitsiConference.prototype.getSsrcByTrack = function(track) {\n    return track.isLocal() ? this.getActivePeerConnection()?.getLocalSSRC(track) : track.getSSRC();\n};\n\n/**\n * Handles track attached to container (Calls associateStreamWithVideoTag method\n * from statistics module)\n * @param {JitsiLocalTrack|JitsiRemoteTrack} track the track\n * @param container the container\n */\nJitsiConference.prototype._onTrackAttach = function(track, container) {\n    const isLocal = track.isLocal();\n    let ssrc = null;\n    const isP2P = track.isP2P;\n    const remoteUserId = isP2P ? track.getParticipantId() : 'jitsi';\n    const peerConnection\n        = isP2P\n            ? this.p2pJingleSession && this.p2pJingleSession.peerconnection\n            : this.jvbJingleSession && this.jvbJingleSession.peerconnection;\n\n    if (isLocal) {\n        // Local tracks have SSRC stored on per peer connection basis.\n        if (peerConnection) {\n            ssrc = peerConnection.getLocalSSRC(track);\n        }\n    } else {\n        ssrc = track.getSSRC();\n    }\n    if (!container.id || !ssrc || !peerConnection) {\n        return;\n    }\n\n    this.statistics.associateStreamWithVideoTag(\n        peerConnection,\n        ssrc,\n        isLocal,\n        remoteUserId,\n        track.getUsageLabel(),\n        container.id);\n};\n\n/**\n * Logs an \"application log\" message.\n * @param message {string} The message to log. Note that while this can be a\n * generic string, the convention used by lib-jitsi-meet and jitsi-meet is to\n * log valid JSON strings, with an \"id\" field used for distinguishing between\n * message types. E.g.: {id: \"recorder_status\", status: \"off\"}\n */\nJitsiConference.prototype.sendApplicationLog = function(message) {\n    Statistics.sendLog(message);\n};\n\n/**\n * Checks if the user identified by given <tt>mucJid</tt> is the conference focus.\n * @param mucJid the full MUC address of the user to be checked.\n * @returns {boolean|null} <tt>true</tt> if MUC user is the conference focus,\n * <tt>false</tt> when is not. <tt>null</tt> if we're not in the MUC anymore and\n * are unable to figure out the status or if given <tt>mucJid</tt> is invalid.\n */\nJitsiConference.prototype.isFocus = function(mucJid) {\n    return this.room ? this.room.isFocus(mucJid) : null;\n};\n\n/**\n * Fires CONFERENCE_FAILED event with INCOMPATIBLE_SERVER_VERSIONS parameter\n */\nJitsiConference.prototype._fireIncompatibleVersionsEvent = function() {\n    this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED,\n        JitsiConferenceErrors.INCOMPATIBLE_SERVER_VERSIONS);\n};\n\n/**\n * Sends a message via the data channel.\n * @param to {string} the id of the endpoint that should receive the message.\n * If \"\" the message will be sent to all participants.\n * @param payload {object} the payload of the message.\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.sendEndpointMessage = function(to, payload) {\n    this.rtc.sendChannelMessage(to, payload);\n};\n\n/**\n * Sends local stats via the bridge channel which then forwards to other endpoints selectively.\n * @param {Object} payload The payload of the message.\n * @throws NetworkError/InvalidStateError/Error if the operation fails or if there is no data channel created.\n */\nJitsiConference.prototype.sendEndpointStatsMessage = function(payload) {\n    this.rtc.sendEndpointStatsMessage(payload);\n};\n\n/**\n * Sends a broadcast message via the data channel.\n * @param payload {object} the payload of the message.\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n * @deprecated Use 'sendMessage' instead. TODO: this should be private.\n */\nJitsiConference.prototype.broadcastEndpointMessage = function(payload) {\n    this.sendEndpointMessage('', payload);\n};\n\n/**\n * Sends a message to a given endpoint (if 'to' is a non-empty string), or\n * broadcasts it to all endpoints in the conference.\n * @param {string} to The ID of the endpoint/participant which is to receive\n * the message, or '' to broadcast the message to all endpoints in the\n * conference.\n * @param {string|object} message the message to send. If this is of type\n * 'string' it will be sent as a chat message. If it is of type 'object', it\n * will be encapsulated in a format recognized by jitsi-meet and converted to\n * JSON before being sent.\n * @param {boolean} sendThroughVideobridge Whether to send the message through\n * jitsi-videobridge (via the COLIBRI data channel or web socket), or through\n * the XMPP MUC. Currently only objects can be sent through jitsi-videobridge.\n */\nJitsiConference.prototype.sendMessage = function(message, to = '', sendThroughVideobridge = false) {\n    const messageType = typeof message;\n\n    // Through videobridge we support only objects. Through XMPP we support\n    // objects (encapsulated in a specific JSON format) and strings (i.e.\n    // regular chat messages).\n    if (messageType !== 'object'\n            && (sendThroughVideobridge || messageType !== 'string')) {\n        logger.error(`Can not send a message of type ${messageType}`);\n\n        return;\n    }\n\n    if (sendThroughVideobridge) {\n        this.sendEndpointMessage(to, message);\n    } else {\n        let messageToSend = message;\n\n        // Name of packet extension of message stanza to send the required\n        // message in.\n        let elementName = 'body';\n\n        if (messageType === 'object') {\n            elementName = 'json-message';\n\n            // Mark as valid JSON message if not already\n            if (!messageToSend.hasOwnProperty(JITSI_MEET_MUC_TYPE)) {\n                messageToSend[JITSI_MEET_MUC_TYPE] = '';\n            }\n\n            try {\n                messageToSend = JSON.stringify(messageToSend);\n            } catch (e) {\n                logger.error('Can not send a message, stringify failed: ', e);\n\n                return;\n            }\n        }\n\n        if (to) {\n            this.sendPrivateTextMessage(to, messageToSend, elementName);\n        } else {\n            // Broadcast\n            this.sendTextMessage(messageToSend, elementName);\n        }\n    }\n\n};\n\nJitsiConference.prototype.isConnectionInterrupted = function() {\n    return this.isP2PActive()\n        ? this.isP2PConnectionInterrupted : this.isJvbConnectionInterrupted;\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_RESTARTED} event. This happens when the bridge goes down\n * and Jicofo moves conferences away to a different bridge.\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onConferenceRestarted = function(session) {\n    if (!session.isP2P && this.options.config.enableForcedReload) {\n        this.eventEmitter.emit(JitsiConferenceEvents.CONFERENCE_FAILED, JitsiConferenceErrors.CONFERENCE_RESTARTED);\n    }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_INTERRUPTED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionInterrupted = function(session) {\n    if (session.isP2P) {\n        this.isP2PConnectionInterrupted = true;\n    } else {\n        this.isJvbConnectionInterrupted = true;\n    }\n    if (session.isP2P === this.isP2PActive()) {\n        this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_INTERRUPTED);\n    }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_ICE_FAILED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionFailed = function(session) {\n    // We do nothing for the JVB connection, because it's up to the Jicofo to\n    // eventually come up with the new offer (at least for the time being).\n    if (session.isP2P) {\n        // Add p2pFailed property to analytics to distinguish, between \"good\"\n        // and \"bad\" connection\n        Statistics.analytics.addPermanentProperties({ p2pFailed: true });\n\n        if (this.p2pJingleSession) {\n            Statistics.sendAnalyticsAndLog(\n                createP2PEvent(\n                    ACTION_P2P_FAILED,\n                    {\n                        initiator: this.p2pJingleSession.isInitiator\n                    }));\n\n        }\n        this._stopP2PSession({\n            reason: 'connectivity-error',\n            reasonDescription: 'ICE FAILED'\n        });\n    } else if (session && this.jvbJingleSession === session) {\n        this._delayedIceFailed = new IceFailedHandling(this);\n        this._delayedIceFailed.start(session);\n    }\n};\n\n/**\n * Handles {@link XMPPEvents.CONNECTION_RESTORED}\n * @param {JingleSessionPC} session\n * @private\n */\nJitsiConference.prototype._onIceConnectionRestored = function(session) {\n    if (session.isP2P) {\n        this.isP2PConnectionInterrupted = false;\n    } else {\n        this.isJvbConnectionInterrupted = false;\n        this._delayedIceFailed && this._delayedIceFailed.cancel();\n    }\n\n    if (session.isP2P === this.isP2PActive()) {\n        this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_RESTORED);\n    }\n};\n\n/**\n * Accept incoming P2P Jingle call.\n * @param {JingleSessionPC} jingleSession the session instance\n * @param {jQuery} jingleOffer a jQuery selector pointing to 'jingle' IQ element\n * @private\n */\nJitsiConference.prototype._acceptP2PIncomingCall = function(jingleSession, jingleOffer) {\n    this.isP2PConnectionInterrupted = false;\n\n    // Accept the offer\n    this.p2pJingleSession = jingleSession;\n    this._sendConferenceJoinAnalyticsEvent();\n\n    this.p2pJingleSession.initialize(\n        this.room,\n        this.rtc,\n        this._signalingLayer,\n        {\n            ...this.options.config,\n            enableInsertableStreams: this.isE2EEEnabled()\n        });\n\n    logger.info('Starting CallStats for P2P connection...');\n\n    let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);\n\n    const participant = this.participants[remoteID];\n\n    if (participant) {\n        remoteID = participant.getStatsID() || remoteID;\n    }\n\n    this.statistics.startCallStats(\n        this.p2pJingleSession.peerconnection,\n        remoteID);\n\n    const localTracks = this.getLocalTracks();\n\n    this.p2pJingleSession.acceptOffer(\n        jingleOffer,\n        () => {\n            logger.debug('Got RESULT for P2P \"session-accept\"');\n\n            this.eventEmitter.emit(\n                JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n                jingleSession);\n        },\n        error => {\n            logger.error(\n                'Failed to accept incoming P2P Jingle session', error);\n        },\n        localTracks);\n};\n\n/**\n * Adds remote tracks to the conference associated with the JVB session.\n * @private\n */\nJitsiConference.prototype._addRemoteJVBTracks = function() {\n    this._addRemoteTracks('JVB', this.jvbJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Adds remote tracks to the conference associated with the P2P session.\n * @private\n */\nJitsiConference.prototype._addRemoteP2PTracks = function() {\n    this._addRemoteTracks('P2P', this.p2pJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Generates fake \"remote track added\" events for given Jingle session.\n * @param {string} logName the session's nickname which will appear in log\n * messages.\n * @param {Array<JitsiRemoteTrack>} remoteTracks the tracks that will be added\n * @private\n */\nJitsiConference.prototype._addRemoteTracks = function(logName, remoteTracks) {\n    for (const track of remoteTracks) {\n        logger.info(`Adding remote ${logName} track: ${track}`);\n        this.onRemoteTrackAdded(track);\n    }\n};\n\n/**\n * Called when {@link XMPPEvents.CONNECTION_ESTABLISHED} event is\n * triggered for a {@link JingleSessionPC}. Switches the conference to use\n * the P2P connection if the event comes from the P2P session.\n * @param {JingleSessionPC} jingleSession the session instance.\n * @private\n */\nJitsiConference.prototype._onIceConnectionEstablished = function(jingleSession) {\n    if (this.p2pJingleSession !== null) {\n        // store the establishment time of the p2p session as a field of the\n        // JitsiConference because the p2pJingleSession might get disposed (thus\n        // the value is lost).\n        this.p2pEstablishmentDuration\n            = this.p2pJingleSession.establishmentDuration;\n    }\n\n    if (this.jvbJingleSession !== null) {\n        this.jvbEstablishmentDuration\n            = this.jvbJingleSession.establishmentDuration;\n    }\n\n    let done = false;\n    const forceJVB121Ratio = this.options.config.forceJVB121Ratio;\n\n    // We don't care about the JVB case, there's nothing to be done\n    if (!jingleSession.isP2P) {\n        done = true;\n    } else if (this.p2pJingleSession !== jingleSession) {\n        logger.error('CONNECTION_ESTABLISHED - wrong P2P session instance ?!');\n\n        done = true;\n    } else if (!jingleSession.isInitiator\n        && typeof forceJVB121Ratio === 'number'\n        && Math.random() < forceJVB121Ratio) {\n        logger.info(`Forcing JVB 121 mode (ratio=${forceJVB121Ratio})...`);\n        Statistics.analytics.addPermanentProperties({ forceJvb121: true });\n        this._stopP2PSession({\n            reason: 'decline',\n            reasonDescription: 'force JVB121'\n        });\n\n        done = true;\n    }\n\n    if (!isNaN(this.p2pEstablishmentDuration)\n        && !isNaN(this.jvbEstablishmentDuration)) {\n        const establishmentDurationDiff\n            = this.p2pEstablishmentDuration - this.jvbEstablishmentDuration;\n\n        Statistics.sendAnalytics(\n            ICE_ESTABLISHMENT_DURATION_DIFF,\n            { value: establishmentDurationDiff });\n    }\n\n    if (jingleSession.isP2P === this.isP2PActive()) {\n        this.eventEmitter.emit(JitsiConferenceEvents.CONNECTION_ESTABLISHED);\n    }\n\n    if (done) {\n\n        return;\n    }\n\n    // Update P2P status and emit events\n    this._setP2PStatus(true);\n\n    // Remove remote tracks\n    if (this.jvbJingleSession) {\n        this._removeRemoteJVBTracks();\n    } else {\n        logger.info('Not removing remote JVB tracks - no session yet');\n    }\n\n    this._addRemoteP2PTracks();\n\n    // Stop media transfer over the JVB connection\n    if (this.jvbJingleSession) {\n        this._suspendMediaTransferForJvbConnection();\n    }\n\n    logger.info('Starting remote stats with p2p connection');\n    this.statistics.startRemoteStats(this.p2pJingleSession.peerconnection);\n\n    Statistics.sendAnalyticsAndLog(\n        createP2PEvent(\n            ACTION_P2P_ESTABLISHED,\n            {\n                initiator: this.p2pJingleSession.isInitiator\n            }));\n\n};\n\n/**\n * Called when the chat room reads a new list of properties from jicofo's\n * presence. The properties may have changed, but they don't have to.\n *\n * @param {Object} properties - The properties keyed by the property name\n * ('key').\n * @private\n */\nJitsiConference.prototype._updateProperties = function(properties = {}) {\n    const changed = !isEqual(properties, this.properties);\n\n    this.properties = properties;\n    if (changed) {\n        this.eventEmitter.emit(JitsiConferenceEvents.PROPERTIES_CHANGED, this.properties);\n\n        const audioLimitReached = this.properties['audio-limit-reached'] === 'true';\n        const videoLimitReached = this.properties['video-limit-reached'] === 'true';\n\n        if (this._audioSenderLimitReached !== audioLimitReached) {\n            this._audioSenderLimitReached = audioLimitReached;\n            this.eventEmitter.emit(JitsiConferenceEvents.AUDIO_UNMUTE_PERMISSIONS_CHANGED, audioLimitReached);\n            logger.info(`Audio unmute permissions set by Jicofo to ${audioLimitReached}`);\n        }\n\n        if (this._videoSenderLimitReached !== videoLimitReached) {\n            this._videoSenderLimitReached = videoLimitReached;\n            this.eventEmitter.emit(JitsiConferenceEvents.VIDEO_UNMUTE_PERMISSIONS_CHANGED, videoLimitReached);\n            logger.info(`Video unmute permissions set by Jicofo to ${videoLimitReached}`);\n        }\n\n        // Some of the properties need to be added to analytics events.\n        const analyticsKeys = [\n\n            // The number of jitsi-videobridge instances currently used for the\n            // conference.\n            'bridge-count',\n\n            // The conference creation time (set by jicofo).\n            'created-ms'\n        ];\n\n        analyticsKeys.forEach(key => {\n            if (properties[key] !== undefined) {\n                Statistics.analytics.addPermanentProperties({\n                    [key.replace('-', '_')]: properties[key]\n                });\n            }\n        });\n    }\n};\n\n/**\n * Gets a conference property with a given key.\n *\n * @param {string} key - The key.\n * @returns {*} The value\n */\nJitsiConference.prototype.getProperty = function(key) {\n    return this.properties[key];\n};\n\n/**\n * Clears the deferred start P2P task if it has been scheduled.\n * @private\n */\nJitsiConference.prototype._maybeClearDeferredStartP2P = function() {\n    if (this.deferredStartP2PTask) {\n        logger.info('Cleared deferred start P2P task');\n        clearTimeout(this.deferredStartP2PTask);\n        this.deferredStartP2PTask = null;\n    }\n};\n\n/**\n * Removes from the conference remote tracks associated with the JVB\n * connection.\n * @private\n */\nJitsiConference.prototype._removeRemoteJVBTracks = function() {\n    this._removeRemoteTracks(\n        'JVB', this.jvbJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Removes from the conference remote tracks associated with the P2P\n * connection.\n * @private\n */\nJitsiConference.prototype._removeRemoteP2PTracks = function() {\n    this._removeRemoteTracks(\n        'P2P', this.p2pJingleSession.peerconnection.getRemoteTracks());\n};\n\n/**\n * Generates fake \"remote track removed\" events for given Jingle session.\n * @param {string} sessionNickname the session's nickname which will appear in\n * log messages.\n * @param {Array<JitsiRemoteTrack>} remoteTracks the tracks that will be removed\n * @private\n */\nJitsiConference.prototype._removeRemoteTracks = function(sessionNickname, remoteTracks) {\n    for (const track of remoteTracks) {\n        logger.info(`Removing remote ${sessionNickname} track: ${track}`);\n        this.onRemoteTrackRemoved(track);\n    }\n};\n\n/**\n * Resumes media transfer over the JVB connection.\n * @private\n */\nJitsiConference.prototype._resumeMediaTransferForJvbConnection = function() {\n    logger.info('Resuming media transfer over the JVB connection...');\n    this.jvbJingleSession.setMediaTransferActive(true, true).then(\n        () => {\n            logger.info('Resumed media transfer over the JVB connection!');\n        },\n        error => {\n            logger.error(\n                'Failed to resume media transfer over the JVB connection:',\n                error);\n        });\n};\n\n/**\n * Sets new P2P status and updates some events/states hijacked from\n * the <tt>JitsiConference</tt>.\n * @param {boolean} newStatus the new P2P status value, <tt>true</tt> means that\n * P2P is now in use, <tt>false</tt> means that the JVB connection is now in use\n * @private\n */\nJitsiConference.prototype._setP2PStatus = function(newStatus) {\n    if (this.p2p === newStatus) {\n        logger.debug(`Called _setP2PStatus with the same status: ${newStatus}`);\n\n        return;\n    }\n    this.p2p = newStatus;\n    if (newStatus) {\n        logger.info('Peer to peer connection established!');\n\n        // When we end up in a valid P2P session need to reset the properties\n        // in case they have persisted, after session with another peer.\n        Statistics.analytics.addPermanentProperties({\n            p2pFailed: false,\n            forceJvb121: false\n        });\n\n        // Sync up video transfer active in case p2pJingleSession not existed\n        // when the lastN value was being adjusted.\n        const isVideoActive = this.getLastN() !== 0;\n\n        this.p2pJingleSession\n            .setMediaTransferActive(true, isVideoActive)\n            .catch(error => {\n                logger.error(\n                    'Failed to sync up P2P video transfer status'\n                        + `(${isVideoActive})`, error);\n            });\n    } else {\n        logger.info('Peer to peer connection closed!');\n    }\n\n    // Put the JVB connection on hold/resume\n    if (this.jvbJingleSession) {\n        this.statistics.sendConnectionResumeOrHoldEvent(\n            this.jvbJingleSession.peerconnection, !newStatus);\n    }\n\n    // Clear dtmfManager, so that it can be recreated with new connection\n    this.dtmfManager = null;\n\n    // Update P2P status\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.P2P_STATUS,\n        this,\n        this.p2p);\n    this.eventEmitter.emit(JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED, this.getActiveMediaSession());\n\n    // Refresh connection interrupted/restored\n    this.eventEmitter.emit(\n        this.isConnectionInterrupted()\n            ? JitsiConferenceEvents.CONNECTION_INTERRUPTED\n            : JitsiConferenceEvents.CONNECTION_RESTORED);\n};\n\n/**\n * Starts new P2P session.\n * @param {string} remoteJid the JID of the remote participant\n * @private\n */\nJitsiConference.prototype._startP2PSession = function(remoteJid) {\n    this._maybeClearDeferredStartP2P();\n    if (this.p2pJingleSession) {\n        logger.error('P2P session already started!');\n\n        return;\n    }\n\n    this.isP2PConnectionInterrupted = false;\n    this.p2pJingleSession\n        = this.xmpp.connection.jingle.newP2PJingleSession(\n            this.room.myroomjid,\n            remoteJid);\n    logger.info(\n        'Created new P2P JingleSession', this.room.myroomjid, remoteJid);\n    this._sendConferenceJoinAnalyticsEvent();\n\n    this.p2pJingleSession.initialize(\n        this.room,\n        this.rtc,\n        this._signalingLayer,\n        {\n            ...this.options.config,\n            enableInsertableStreams: this.isE2EEEnabled()\n        });\n\n    logger.info('Starting CallStats for P2P connection...');\n\n    let remoteID = Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid);\n\n    const participant = this.participants[remoteID];\n\n    if (participant) {\n        remoteID = participant.getStatsID() || remoteID;\n    }\n\n    this.statistics.startCallStats(\n        this.p2pJingleSession.peerconnection,\n        remoteID);\n\n    const localTracks = this.getLocalTracks();\n\n    this.p2pJingleSession.invite(localTracks);\n};\n\n/**\n * Suspends media transfer over the JVB connection.\n * @private\n */\nJitsiConference.prototype._suspendMediaTransferForJvbConnection = function() {\n    logger.info('Suspending media transfer over the JVB connection...');\n    this.jvbJingleSession.setMediaTransferActive(false, false).then(\n        () => {\n            logger.info('Suspended media transfer over the JVB connection !');\n        },\n        error => {\n            logger.error(\n                'Failed to suspend media transfer over the JVB connection:',\n                error);\n        });\n};\n\n/**\n * Method when called will decide whether it's the time to start or stop\n * the P2P session.\n * @param {boolean} userLeftEvent if <tt>true</tt> it means that the call\n * originates from the user left event.\n * @private\n */\nJitsiConference.prototype._maybeStartOrStopP2P = function(userLeftEvent) {\n    if (!this.isP2PEnabled()\n            || this.isP2PTestModeEnabled()\n            || browser.isFirefox()\n            || browser.isWebKitBased()\n            || this.isE2EEEnabled()) {\n        logger.info('Auto P2P disabled');\n\n        return;\n    }\n    const peers = this.getParticipants();\n    const peerCount = peers.length;\n\n    // FIXME 1 peer and it must *support* P2P switching\n    const shouldBeInP2P = this._shouldBeInP2PMode();\n\n    // Clear deferred \"start P2P\" task\n    if (!shouldBeInP2P && this.deferredStartP2PTask) {\n        this._maybeClearDeferredStartP2P();\n    }\n\n    // Start peer to peer session\n    if (!this.p2pJingleSession && shouldBeInP2P) {\n        const peer = peerCount && peers[0];\n\n\n        const myId = this.myUserId();\n        const peersId = peer.getId();\n\n        if (myId > peersId) {\n            logger.debug(\n                'I\\'m the bigger peersId - '\n                + 'the other peer should start P2P', myId, peersId);\n\n            return;\n        } else if (myId === peersId) {\n            logger.error('The same IDs ? ', myId, peersId);\n\n            return;\n        }\n\n        const jid = peer.getJid();\n\n        if (userLeftEvent) {\n            if (this.deferredStartP2PTask) {\n                logger.error('Deferred start P2P task\\'s been set already!');\n\n                return;\n            }\n            logger.info(\n                `Will start P2P with: ${jid} after ${\n                    this.backToP2PDelay} seconds...`);\n            this.deferredStartP2PTask = setTimeout(\n                this._startP2PSession.bind(this, jid),\n                this.backToP2PDelay * 1000);\n        } else {\n            logger.info(`Will start P2P with: ${jid}`);\n            this._startP2PSession(jid);\n        }\n    } else if (this.p2pJingleSession && !shouldBeInP2P) {\n        logger.info(`Will stop P2P with: ${this.p2pJingleSession.remoteJid}`);\n\n        // Log that there will be a switch back to the JVB connection\n        if (this.p2pJingleSession.isInitiator && peerCount > 1) {\n            Statistics.sendAnalyticsAndLog(\n                createP2PEvent(ACTION_P2P_SWITCH_TO_JVB));\n        }\n        this._stopP2PSession();\n    }\n};\n\n/**\n * Tells whether or not this conference should be currently in the P2P mode.\n *\n * @private\n * @returns {boolean}\n */\nJitsiConference.prototype._shouldBeInP2PMode = function() {\n    const peers = this.getParticipants();\n    const peerCount = peers.length;\n    const hasBotPeer = peers.find(p => p.getBotType() === 'poltergeist' || p.hasFeature(FEATURE_JIGASI)) !== undefined;\n    const shouldBeInP2P = peerCount === 1 && !hasBotPeer;\n\n    logger.debug(`P2P? peerCount: ${peerCount}, hasBotPeer: ${hasBotPeer} => ${shouldBeInP2P}`);\n\n    return shouldBeInP2P;\n};\n\n/**\n * Stops the current P2P session.\n * @param {Object} options - Options for stopping P2P.\n * @param {string} options.reason - One of the Jingle \"reason\" element\n * names as defined by https://xmpp.org/extensions/xep-0166.html#def-reason\n * @param {string} options.reasonDescription - Text\n * description that will be included in the session terminate message\n * @param {boolean} requestRestart - Whether this is due to a session restart, in which case\n * media will not be resumed on the JVB.\n * @private\n */\nJitsiConference.prototype._stopP2PSession = function(options = {}) {\n    const {\n        reason = 'success',\n        reasonDescription = 'Turning off P2P session',\n        requestRestart = false\n    } = options;\n\n    if (!this.p2pJingleSession) {\n        logger.error('No P2P session to be stopped!');\n\n        return;\n    }\n\n    const wasP2PEstablished = this.isP2PActive();\n\n    // Swap remote tracks, but only if the P2P has been fully established\n    if (wasP2PEstablished) {\n        if (this.jvbJingleSession && !requestRestart) {\n            this._resumeMediaTransferForJvbConnection();\n        }\n\n        // Remove remote P2P tracks\n        this._removeRemoteP2PTracks();\n    }\n\n    // Stop P2P stats\n    logger.info('Stopping remote stats for P2P connection');\n    this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection);\n    logger.info('Stopping CallStats for P2P connection');\n    this.statistics.stopCallStats(this.p2pJingleSession.peerconnection);\n\n    this.p2pJingleSession.terminate(\n        () => {\n            logger.info('P2P session terminate RESULT');\n        },\n        error => {\n            // Because both initiator and responder are simultaneously\n            // terminating their JingleSessions in case of the 'to JVB switch'\n            // when 3rd participant joins, both will dispose their sessions and\n            // reply with 'item-not-found' (see strophe.jingle.js). We don't\n            // want to log this as an error since it's expected behaviour.\n            //\n            // We want them both to terminate, because in case of initiator's\n            // crash the responder would stay in P2P mode until ICE fails which\n            // could take up to 20 seconds.\n            //\n            // NOTE: whilst this is an error callback,  'success' as a reason is\n            // considered as graceful session terminate\n            // where both initiator and responder terminate their sessions\n            // simultaneously.\n            if (reason !== 'success') {\n                logger.error('An error occurred while trying to terminate P2P Jingle session', error);\n            }\n        }, {\n            reason,\n            reasonDescription,\n            sendSessionTerminate: this.room\n                && this.getParticipantById(\n                    Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid))\n        });\n\n    this.p2pJingleSession = null;\n\n    // Update P2P status and other affected events/states\n    this._setP2PStatus(false);\n\n    if (wasP2PEstablished) {\n        // Add back remote JVB tracks\n        if (this.jvbJingleSession && !requestRestart) {\n            this._addRemoteJVBTracks();\n        } else {\n            logger.info('Not adding remote JVB tracks - no session yet');\n        }\n    }\n};\n\n/**\n * Updates room presence if needed and send the packet in case of a modification.\n * @param {JingleSessionPC} jingleSession the session firing the event, contains the peer connection which\n * tracks we will check.\n * @param {Object|null} ctx a context object we can distinguish multiple calls of the same pass of updating tracks.\n */\nJitsiConference.prototype._updateRoomPresence = function(jingleSession, ctx) {\n    if (!jingleSession) {\n        return;\n    }\n\n    // skips sending presence twice for the same pass of updating ssrcs\n    if (ctx) {\n        if (ctx.skip) {\n            return;\n        }\n        ctx.skip = true;\n    }\n\n    let presenceChanged = false;\n    let muteStatusChanged, videoTypeChanged;\n    const localTracks = this.getLocalTracks();\n\n    // Set presence for all the available local tracks.\n    for (const track of localTracks) {\n        muteStatusChanged = this._setTrackMuteStatus(track.getType(), track, track.isMuted());\n        if (track.getType() === MediaType.VIDEO) {\n            videoTypeChanged = this._setNewVideoType(track);\n        }\n        presenceChanged = presenceChanged || muteStatusChanged || videoTypeChanged;\n    }\n\n    // Set the presence in the legacy format if there are no local tracks and multi stream support is not enabled.\n    if (!localTracks.length && !FeatureFlags.isMultiStreamSupportEnabled()) {\n        const audioMuteStatusChanged = this._setTrackMuteStatus(MediaType.AUDIO, undefined, true);\n        const videoMuteStatusChanged = this._setTrackMuteStatus(MediaType.VIDEO, undefined, true);\n\n        videoTypeChanged = this._setNewVideoType();\n        presenceChanged = audioMuteStatusChanged || videoMuteStatusChanged || videoTypeChanged;\n    }\n\n    presenceChanged && this.room.sendPresence();\n};\n\n/**\n * Checks whether or not the conference is currently in the peer to peer mode.\n * Being in peer to peer mode means that the direct connection has been\n * established and the P2P connection is being used for media transmission.\n * @return {boolean} <tt>true</tt> if in P2P mode or <tt>false</tt> otherwise.\n */\nJitsiConference.prototype.isP2PActive = function() {\n    return this.p2p;\n};\n\n/**\n * Returns the current ICE state of the P2P connection.\n * NOTE: method is used by the jitsi-meet-torture tests.\n * @return {string|null} an ICE state or <tt>null</tt> if there's currently\n * no P2P connection.\n */\nJitsiConference.prototype.getP2PConnectionState = function() {\n    if (this.isP2PActive()) {\n        return this.p2pJingleSession.peerconnection.getConnectionState();\n    }\n\n    return null;\n};\n\n/**\n * Configures the peerconnection so that a given framre rate can be achieved for desktop share.\n *\n * @param {number} maxFps The capture framerate to be used for desktop tracks.\n * @returns {boolean} true if the operation is successful, false otherwise.\n */\nJitsiConference.prototype.setDesktopSharingFrameRate = function(maxFps) {\n    if (typeof maxFps !== 'number' || isNaN(maxFps)) {\n        logger.error(`Invalid value ${maxFps} specified for desktop capture frame rate`);\n\n        return false;\n    }\n\n    this._desktopSharingFrameRate = maxFps;\n\n    // Enable or disable simulcast for plan-b screensharing based on the capture fps.\n    this.jvbJingleSession && this.jvbJingleSession.peerconnection.setDesktopSharingFrameRate(maxFps);\n\n    // Set the capture rate for desktop sharing.\n    this.rtc.setDesktopSharingFrameRate(maxFps);\n\n    return true;\n};\n\n/**\n * Manually starts new P2P session (should be used only in the tests).\n */\nJitsiConference.prototype.startP2PSession = function() {\n    const peers = this.getParticipants();\n\n    // Start peer to peer session\n    if (peers.length === 1) {\n        const peerJid = peers[0].getJid();\n\n        this._startP2PSession(peerJid);\n    } else {\n        throw new Error(\n            'There must be exactly 1 participant to start the P2P session !');\n    }\n};\n\n/**\n * Manually stops the current P2P session (should be used only in the tests).\n */\nJitsiConference.prototype.stopP2PSession = function(options) {\n    this._stopP2PSession(options);\n};\n\n/**\n * Get a summary of how long current participants have been the dominant speaker\n * @returns {object}\n */\nJitsiConference.prototype.getSpeakerStats = function() {\n    return this.speakerStatsCollector.getStats();\n};\n\n/**\n * Sends a facial expression with its duration to the xmpp server.\n * @param {Object} payload\n */\nJitsiConference.prototype.sendFacialExpression = function(payload) {\n    this.xmpp.sendFacialExpressionEvent(this.room.roomjid, payload);\n};\n\n/**\n * Sets the constraints for the video that is requested from the bridge.\n *\n * @param {Object} videoConstraints The constraints which are specified in the\n * following format. The message updates the fields that are present and leaves the\n * rest unchanged on the bridge. Therefore, any field that is not applicable anymore\n * should be cleared by passing an empty object or list (whatever is applicable).\n * {\n *      'lastN': 20,\n *      'selectedEndpoints': ['A', 'B', 'C'],\n *      'onStageEndpoints': ['A'],\n *      'defaultConstraints': { 'maxHeight': 180 },\n *      'constraints': {\n *          'A': { 'maxHeight': 720 }\n *      }\n * }\n */\nJitsiConference.prototype.setReceiverConstraints = function(videoConstraints) {\n    this.receiveVideoController.setReceiverConstraints(videoConstraints);\n};\n\n/**\n * Sets the maximum video size the local participant should receive from remote\n * participants.\n *\n * @param {number} maxFrameHeight - the maximum frame height, in pixels,\n * this receiver is willing to receive.\n * @returns {void}\n */\nJitsiConference.prototype.setReceiverVideoConstraint = function(maxFrameHeight) {\n    this.receiveVideoController.setPreferredReceiveMaxFrameHeight(maxFrameHeight);\n};\n\n/**\n * Sets the maximum video size the local participant should send to remote\n * participants.\n * @param {number} maxFrameHeight - The user preferred max frame height.\n * @returns {Promise} promise that will be resolved when the operation is\n * successful and rejected otherwise.\n */\nJitsiConference.prototype.setSenderVideoConstraint = function(maxFrameHeight) {\n    return this.sendVideoController.setPreferredSendMaxFrameHeight(maxFrameHeight);\n};\n\n/**\n * Creates a video SIP GW session and returns it if service is enabled. Before\n * creating a session one need to check whether video SIP GW service is\n * available in the system {@link JitsiConference.isVideoSIPGWAvailable}. Even\n * if there are available nodes to serve this request, after creating the\n * session those nodes can be taken and the request about using the\n * created session can fail.\n *\n * @param {string} sipAddress - The sip address to be used.\n * @param {string} displayName - The display name to be used for this session.\n * @returns {JitsiVideoSIPGWSession|Error} Returns null if conference is not\n * initialised and there is no room.\n */\nJitsiConference.prototype.createVideoSIPGWSession = function(sipAddress, displayName) {\n    if (!this.room) {\n        return new Error(VideoSIPGWConstants.ERROR_NO_CONNECTION);\n    }\n\n    return this.videoSIPGWHandler\n        .createVideoSIPGWSession(sipAddress, displayName);\n};\n\n/**\n * Sends a conference.join analytics event.\n *\n * @returns {void}\n */\nJitsiConference.prototype._sendConferenceJoinAnalyticsEvent = function() {\n    const meetingId = this.getMeetingUniqueId();\n\n    if (this._conferenceJoinAnalyticsEventSent || !meetingId || this.getActivePeerConnection() === null) {\n        return;\n    }\n\n    Statistics.sendAnalytics(createConferenceEvent('joined', {\n        meetingId,\n        participantId: `${meetingId}.${this._statsCurrentId}`\n    }));\n    this._conferenceJoinAnalyticsEventSent = Date.now();\n};\n\n/**\n * Sends conference.left analytics event.\n * @private\n */\nJitsiConference.prototype._sendConferenceLeftAnalyticsEvent = function() {\n    const meetingId = this.getMeetingUniqueId();\n\n    if (!meetingId || !this._conferenceJoinAnalyticsEventSent) {\n\n        return;\n    }\n\n    Statistics.sendAnalytics(createConferenceEvent('left', {\n        meetingId,\n        participantId: `${meetingId}.${this._statsCurrentId}`,\n        stats: {\n            duration: Math.floor((Date.now() - this._conferenceJoinAnalyticsEventSent) / 1000),\n            perf: this.getPerformanceStats()\n        }\n    }));\n};\n\n/**\n * Restarts all active media sessions.\n *\n * @returns {void}\n */\nJitsiConference.prototype._restartMediaSessions = function() {\n    if (this.p2pJingleSession) {\n        this._stopP2PSession({\n            reasonDescription: 'restart',\n            requestRestart: true\n        });\n    }\n\n    if (this.jvbJingleSession) {\n        this.jvbJingleSession.terminate(\n            null /* success callback => we don't care */,\n            error => {\n                logger.warn('An error occurred while trying to terminate the JVB session', error);\n            }, {\n                reason: 'success',\n                reasonDescription: 'restart required',\n                requestRestart: true,\n                sendSessionTerminate: true\n            });\n    }\n\n    this._maybeStartOrStopP2P(false);\n};\n\n/**\n * Returns whether End-To-End encryption is enabled.\n *\n * @returns {boolean}\n */\nJitsiConference.prototype.isE2EEEnabled = function() {\n    return Boolean(this._e2eEncryption && this._e2eEncryption.isEnabled());\n};\n\n/**\n * Returns whether End-To-End encryption is supported. Note that not all participants\n * in the conference may support it.\n *\n * @returns {boolean}\n */\nJitsiConference.prototype.isE2EESupported = function() {\n    return E2EEncryption.isSupported(this.options.config);\n};\n\n/**\n * Enables / disables End-to-End encryption.\n *\n * @param {boolean} enabled whether to enable E2EE or not.\n * @returns {void}\n */\nJitsiConference.prototype.toggleE2EE = function(enabled) {\n    if (!this.isE2EESupported()) {\n        logger.warn('Cannot enable / disable E2EE: platform is not supported.');\n\n        return;\n    }\n\n    this._e2eEncryption.setEnabled(enabled);\n};\n\n/**\n * Sets the key and index for End-to-End encryption.\n *\n * @param {CryptoKey} [keyInfo.encryptionKey] - encryption key.\n * @param {Number} [keyInfo.index] - the index of the encryption key.\n * @returns {void}\n */\nJitsiConference.prototype.setMediaEncryptionKey = function(keyInfo) {\n    this._e2eEncryption.setEncryptionKey(keyInfo);\n};\n\n/**\n * Returns <tt>true</tt> if lobby support is enabled in the backend.\n *\n * @returns {boolean} whether lobby is supported in the backend.\n */\nJitsiConference.prototype.isLobbySupported = function() {\n    return Boolean(this.room && this.room.getLobby().isSupported());\n};\n\n/**\n * Returns <tt>true</tt> if the room has members only enabled.\n *\n * @returns {boolean} whether conference room is members only.\n */\nJitsiConference.prototype.isMembersOnly = function() {\n    return Boolean(this.room && this.room.membersOnlyEnabled);\n};\n\n/**\n * Enables lobby by moderators\n *\n * @returns {Promise} resolves when lobby room is joined or rejects with the error.\n */\nJitsiConference.prototype.enableLobby = function() {\n    if (this.room && this.isModerator()) {\n        return this.room.getLobby().enable();\n    }\n\n    return Promise.reject(\n        new Error('The conference not started or user is not moderator'));\n};\n\n/**\n * Disabled lobby by moderators\n *\n * @returns {void}\n */\nJitsiConference.prototype.disableLobby = function() {\n    if (this.room && this.isModerator()) {\n        this.room.getLobby().disable();\n    } else {\n        logger.warn(`Failed to disable lobby, ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator'}`);\n    }\n};\n\n/**\n * Joins the lobby room with display name and optional email or with a shared password to skip waiting.\n *\n * @param {string} displayName Display name should be set to show it to moderators.\n * @param {string} email Optional email is used to present avatar to the moderator.\n * @returns {Promise<never>}\n */\nJitsiConference.prototype.joinLobby = function(displayName, email) {\n    if (this.room) {\n        return this.room.getLobby().join(displayName, email);\n    }\n\n    return Promise.reject(new Error('The conference not started'));\n};\n\n/**\n * Gets the local id for a participant in a lobby room.\n * Returns undefined when current participant is not in the lobby room.\n * This is used for lobby room private chat messages.\n *\n * @returns {string}\n */\nJitsiConference.prototype.myLobbyUserId = function() {\n    if (this.room) {\n        return this.room.getLobby().getLocalId();\n    }\n};\n\n/**\n * Sends a message to a lobby room.\n * When id is specified it sends a private message.\n * Otherwise it sends the message to all moderators.\n * @param {message} Object The message to send\n * @param {string} id The participant id.\n *\n * @returns {void}\n */\nJitsiConference.prototype.sendLobbyMessage = function(message, id) {\n    if (this.room) {\n        if (id) {\n            return this.room.getLobby().sendPrivateMessage(id, message);\n        }\n\n        return this.room.getLobby().sendMessage(message);\n    }\n};\n\n/**\n * Adds a message listener to the lobby room\n * @param {Function} listener The listener function,\n * called when a new message is received in the lobby room.\n *\n * @returns {Function} Handler returned to be able to remove it later.\n */\nJitsiConference.prototype.addLobbyMessageListener = function(listener) {\n    if (this.room) {\n        return this.room.getLobby().addMessageListener(listener);\n    }\n};\n\n/**\n * Removes a message handler from the lobby room\n * @param {Function} handler The handler function  to remove.\n *\n * @returns {void}\n */\nJitsiConference.prototype.removeLobbyMessageHandler = function(handler) {\n    if (this.room) {\n        return this.room.getLobby().removeMessageHandler(handler);\n    }\n};\n\n/**\n * Denies an occupant in the lobby room access to the conference.\n * @param {string} id The participant id.\n */\nJitsiConference.prototype.lobbyDenyAccess = function(id) {\n    if (this.room) {\n        this.room.getLobby().denyAccess(id);\n    }\n};\n\n/**\n * Approves the request to join the conference to a participant waiting in the lobby.\n *\n * @param {string} id The participant id.\n */\nJitsiConference.prototype.lobbyApproveAccess = function(id) {\n    if (this.room) {\n        this.room.getLobby().approveAccess(id);\n    }\n};\n\n/**\n * Returns <tt>true</tt> if AV Moderation support is enabled in the backend.\n *\n * @returns {boolean} whether AV Moderation is supported in the backend.\n */\nJitsiConference.prototype.isAVModerationSupported = function() {\n    return Boolean(this.room && this.room.getAVModeration().isSupported());\n};\n\n/**\n * Enables AV Moderation.\n * @param {MediaType} mediaType \"audio\" or \"video\"\n */\nJitsiConference.prototype.enableAVModeration = function(mediaType) {\n    if (this.room && this.isModerator()\n        && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n        this.room.getAVModeration().enable(true, mediaType);\n    } else {\n        logger.warn(`Failed to enable AV moderation, ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator, '}${\n            this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n    }\n};\n\n/**\n * Disables AV Moderation.\n * @param {MediaType} mediaType \"audio\" or \"video\"\n */\nJitsiConference.prototype.disableAVModeration = function(mediaType) {\n    if (this.room && this.isModerator()\n        && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n        this.room.getAVModeration().enable(false, mediaType);\n    } else {\n        logger.warn(`Failed to disable AV moderation, ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator, '}${\n            this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n    }\n};\n\n/**\n * Approve participant access to certain media, allows unmuting audio or video.\n *\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param id the id of the participant.\n */\nJitsiConference.prototype.avModerationApprove = function(mediaType, id) {\n    if (this.room && this.isModerator()\n        && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n\n        const participant = this.getParticipantById(id);\n\n        if (!participant) {\n            return;\n        }\n\n        this.room.getAVModeration().approve(mediaType, participant.getJid());\n    } else {\n        logger.warn(`AV moderation approve skipped , ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator, '}${\n            this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n    }\n};\n\n/**\n * Reject participant access to certain media, blocks unmuting audio or video.\n *\n * @param {MediaType} mediaType \"audio\" or \"video\"\n * @param id the id of the participant.\n */\nJitsiConference.prototype.avModerationReject = function(mediaType, id) {\n    if (this.room && this.isModerator()\n        && (mediaType === MediaType.AUDIO || mediaType === MediaType.VIDEO)) {\n\n        const participant = this.getParticipantById(id);\n\n        if (!participant) {\n            return;\n        }\n\n        this.room.getAVModeration().reject(mediaType, participant.getJid());\n    } else {\n        logger.warn(`AV moderation reject skipped , ${this.room ? '' : 'not in a room, '}${\n            this.isModerator() ? '' : 'participant is not a moderator, '}${\n            this.room && this.isModerator() ? 'wrong media type passed' : ''}`);\n    }\n};\n\n/**\n * Returns the breakout rooms manager object.\n *\n * @returns {Object} the breakout rooms manager.\n */\nJitsiConference.prototype.getBreakoutRooms = function() {\n    return this.room?.getBreakoutRooms();\n};\n","import JitsiConference from './JitsiConference';\nimport * as JitsiConnectionEvents from './JitsiConnectionEvents';\nimport Statistics from './modules/statistics/statistics';\nimport XMPP from './modules/xmpp/xmpp';\nimport {\n    CONNECTION_DISCONNECTED as ANALYTICS_CONNECTION_DISCONNECTED,\n    createConnectionFailedEvent\n} from './service/statistics/AnalyticsEvents';\n\n/**\n * Creates a new connection object for the Jitsi Meet server side video\n * conferencing service. Provides access to the JitsiConference interface.\n * @param appID identification for the provider of Jitsi Meet video conferencing\n * services.\n * @param token the JWT token used to authenticate with the server(optional)\n * @param options Object with properties / settings related to connection with\n * the server.\n * @constructor\n */\nexport default function JitsiConnection(appID, token, options) {\n    this.appID = appID;\n    this.token = token;\n    this.options = options;\n    this.xmpp = new XMPP(options, token);\n\n    /* eslint-disable max-params */\n    this.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED,\n        (errType, msg, credentials, details) => {\n            Statistics.sendAnalyticsAndLog(\n                createConnectionFailedEvent(errType, msg, details));\n        });\n    /* eslint-enable max-params */\n\n    this.addEventListener(JitsiConnectionEvents.CONNECTION_DISCONNECTED,\n        msg => {\n            // we can see disconnects from normal tab closing of the browser\n            // and then there are no msgs, but we want to log only disconnects\n            // when there is real error\n            // XXX Do we need the difference in handling between the log and\n            // analytics event here?\n            if (msg) {\n                Statistics.sendAnalytics(\n                    ANALYTICS_CONNECTION_DISCONNECTED,\n                    { message: msg });\n            }\n            Statistics.sendLog(\n                JSON.stringify(\n                    {\n                        id: ANALYTICS_CONNECTION_DISCONNECTED,\n                        msg\n                    }));\n        });\n}\n\n/**\n * Connect the client with the server.\n * @param options {object} connecting options\n * (for example authentications parameters).\n */\nJitsiConnection.prototype.connect = function(options = {}) {\n    this.xmpp.connect(options.id, options.password);\n};\n\n/**\n * Attach to existing connection. Can be used for optimizations. For example:\n * if the connection is created on the server we can attach to it and start\n * using it.\n *\n * @param options {object} connecting options - rid, sid and jid.\n */\nJitsiConnection.prototype.attach = function(options) {\n    this.xmpp.attach(options);\n};\n\n/**\n * Disconnect the client from the server.\n * @returns {Promise} - Resolves when the disconnect process is finished or rejects with an error.\n */\nJitsiConnection.prototype.disconnect = function(...args) {\n    // XXX Forward any arguments passed to JitsiConnection.disconnect to\n    // XMPP.disconnect. For example, the caller of JitsiConnection.disconnect\n    // may optionally pass the event which triggered the disconnect in order to\n    // provide the implementation with finer-grained context.\n    return this.xmpp.disconnect(...args);\n};\n\n/**\n * Returns the jid of the participant associated with the XMPP connection.\n *\n * @returns {string} The jid of the participant.\n */\nJitsiConnection.prototype.getJid = function() {\n    return this.xmpp.getJid();\n};\n\n/**\n * This method allows renewal of the tokens if they are expiring.\n * @param token the new token.\n */\nJitsiConnection.prototype.setToken = function(token) {\n    this.token = token;\n};\n\n/**\n * Creates and joins new conference.\n * @param name the name of the conference; if null - a generated name will be\n * provided from the api\n * @param options Object with properties / settings related to the conference\n * that will be created.\n * @returns {JitsiConference} returns the new conference object.\n */\nJitsiConnection.prototype.initJitsiConference = function(name, options) {\n    return new JitsiConference({\n        name,\n        config: options,\n        connection: this\n    });\n};\n\n/**\n * Subscribes the passed listener to the event.\n * @param event {JitsiConnectionEvents} the connection event.\n * @param listener {Function} the function that will receive the event\n */\nJitsiConnection.prototype.addEventListener = function(event, listener) {\n    this.xmpp.addListener(event, listener);\n};\n\n/**\n * Unsubscribes the passed handler.\n * @param event {JitsiConnectionEvents} the connection event.\n * @param listener {Function} the function that will receive the event\n */\nJitsiConnection.prototype.removeEventListener = function(event, listener) {\n    this.xmpp.removeListener(event, listener);\n};\n\n/**\n * Returns measured connectionTimes.\n */\nJitsiConnection.prototype.getConnectionTimes = function() {\n    return this.xmpp.connectionTimes;\n};\n\n/**\n * Adds new feature to the list of supported features for the local\n * participant.\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - the new list of features will be\n * immediately submitted to the others.\n */\nJitsiConnection.prototype.addFeature = function(feature, submit = false) {\n    this.xmpp.caps.addFeature(feature, submit, true);\n};\n\n/**\n * Removes a feature from the list of supported features for the local\n * participant\n * @param {String} feature the name of the feature.\n * @param {boolean} submit if true - the new list of features will be\n * immediately submitted to the others.\n */\nJitsiConnection.prototype.removeFeature = function(feature, submit = false) {\n    this.xmpp.caps.removeFeature(feature, submit, true);\n};\n\n/**\n * Get object with internal logs.\n */\nJitsiConnection.prototype.getLogs = function() {\n    const data = this.xmpp.getJingleLog();\n\n    const metadata = {};\n\n    metadata.time = new Date();\n    metadata.url = window.location.href;\n    metadata.ua = navigator.userAgent;\n\n    const log = this.xmpp.getXmppLog();\n\n    if (log) {\n        metadata.xmpp = log;\n    }\n\n    data.metadata = metadata;\n\n    return data;\n};\n","/**\n * The events for the media devices.\n */\n\nexport enum JitsiMediaDevicesEvents {\n    /**\n     * Indicates that the list of available media devices has been changed. The\n     * event provides the following parameters to its listeners:\n     *\n     * @param {MediaDeviceInfo[]} devices - array of MediaDeviceInfo or\n     *  MediaDeviceInfo-like objects that are currently connected.\n     *  @see https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo\n     */\n    DEVICE_LIST_CHANGED = 'mediaDevices.devicechange',\n\n    /**\n     * Event emitted when the user granted/blocked a permission for the camera / mic.\n     * Used to keep track of the granted permissions on browsers which don't\n     * support the Permissions API.\n     */\n    PERMISSIONS_CHANGED = 'rtc.permissions_changed',\n\n    /**\n     * Indicates that the environment is currently showing permission prompt to\n     * access camera and/or microphone. The event provides the following\n     * parameters to its listeners:\n     *\n     * @param {'chrome'|'opera'|'firefox'|'safari'|'nwjs'\n     *  |'react-native'|'android'} environmentType - type of browser or\n     *  other execution environment.\n     */\n    PERMISSION_PROMPT_IS_SHOWN = 'mediaDevices.permissionPromptIsShown',\n\n    SLOW_GET_USER_MEDIA = 'mediaDevices.slowGetUserMedia'\n};\n\n// exported for backward compatibility\nexport const DEVICE_LIST_CHANGED = JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED;\nexport const PERMISSIONS_CHANGED = JitsiMediaDevicesEvents.PERMISSIONS_CHANGED;\nexport const PERMISSION_PROMPT_IS_SHOWN = JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN;\nexport const SLOW_GET_USER_MEDIA = JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA;\n","import EventEmitter from 'events';\n\nimport * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';\nimport RTC from './modules/RTC/RTC';\nimport browser from './modules/browser';\nimport Statistics from './modules/statistics/statistics';\nimport { MediaType } from './service/RTC/MediaType';\nimport RTCEvents from './service/RTC/RTCEvents';\n\nconst AUDIO_PERMISSION_NAME = 'microphone';\nconst PERMISSION_GRANTED_STATUS = 'granted';\nconst VIDEO_PERMISSION_NAME = 'camera';\n\n/**\n * Media devices utilities for Jitsi.\n */\nclass JitsiMediaDevices {\n    /**\n     * Initializes a {@code JitsiMediaDevices} object. There will be a single\n     * instance of this class.\n     */\n    constructor() {\n        this._eventEmitter = new EventEmitter();\n        this._permissions = {};\n\n        RTC.addListener(\n            RTCEvents.DEVICE_LIST_CHANGED,\n            devices =>\n                this._eventEmitter.emit(\n                    JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,\n                    devices));\n        RTC.addListener(\n            RTCEvents.DEVICE_LIST_AVAILABLE,\n            devices =>\n                this._logOutputDevice(\n                    this.getAudioOutputDevice(),\n                    devices));\n\n        // We would still want to update the permissions cache in case the permissions API is not supported.\n        RTC.addListener(\n            RTCEvents.PERMISSIONS_CHANGED,\n            permissions => this._handlePermissionsChange(permissions));\n\n        // Test if the W3C Permissions API is implemented and the 'camera' and 'microphone' permissions are\n        // implemented. If supported add onchange listeners.\n        this._permissionsApiSupported = new Promise(resolve => {\n            if (!navigator.permissions) {\n                resolve(false);\n\n                return;\n            }\n\n            const self = this;\n\n            const promises = [];\n\n            promises.push(navigator.permissions.query({ name: VIDEO_PERMISSION_NAME })\n                .then(status => {\n                    this._handlePermissionsChange({\n                        [MediaType.VIDEO]: this._parsePermissionState(status)\n                    });\n                    status.onchange = function() {\n                        try {\n                            self._handlePermissionsChange({\n                                [MediaType.VIDEO]: self._parsePermissionState(this)\n                            });\n                        } catch (error) {\n                            // Nothing to do.\n                        }\n                    };\n\n                    return true;\n                })\n                .catch(() => false));\n\n            promises.push(navigator.permissions.query({ name: AUDIO_PERMISSION_NAME })\n                .then(status => {\n                    this._handlePermissionsChange({\n                        [MediaType.AUDIO]: this._parsePermissionState(status)\n                    });\n                    status.onchange = function() {\n                        try {\n                            self._handlePermissionsChange({\n                                [MediaType.AUDIO]: self._parsePermissionState(this)\n                            });\n                        } catch (error) {\n                            // Nothing to do.\n                        }\n                    };\n\n                    return true;\n                })\n                .catch(() => false));\n\n            Promise.all(promises).then(results => resolve(results.every(supported => supported)));\n\n        });\n    }\n\n\n    /**\n     * Parses a PermissionState object and returns true for granted and false otherwise.\n     *\n     * @param {PermissionState} permissionStatus - The PermissionState object retrieved from the Permissions API.\n     * @returns {boolean} - True for granted and false for denied.\n     * @throws {TypeError}\n     */\n    _parsePermissionState(permissionStatus = {}) {\n        // The status attribute is deprecated, and state\n        // should be used instead, but check both for now\n        // for backwards compatibility.\n        const status = permissionStatus.state || permissionStatus.status;\n\n        if (typeof status !== 'string') {\n            throw new TypeError();\n        }\n\n        return status === PERMISSION_GRANTED_STATUS;\n    }\n\n    /**\n     * Updates the local granted/denied permissions cache. A permissions might be\n     * granted, denied, or undefined. This is represented by having its media\n     * type key set to {@code true} or {@code false} respectively.\n     *\n     * @param {Object} permissions - Object with the permissions.\n     */\n    _handlePermissionsChange(permissions) {\n        const hasPermissionsChanged\n            = [ MediaType.AUDIO, MediaType.VIDEO ]\n                .some(type => type in permissions && permissions[type] !== this._permissions[type]);\n\n        if (hasPermissionsChanged) {\n            this._permissions = {\n                ...this._permissions,\n                ...permissions\n            };\n            this._eventEmitter.emit(JitsiMediaDevicesEvents.PERMISSIONS_CHANGED, this._permissions);\n\n            if (this._permissions[MediaType.AUDIO] || this._permissions[MediaType.VIDEO]) {\n                // Triggering device list update when the permissiions are granted in order to update\n                // the labels the devices.\n                // eslint-disable-next-line no-empty-function\n                this.enumerateDevices(() => {});\n            }\n        }\n    }\n\n    /**\n     * Gathers data and sends it to statistics.\n     * @param deviceID the device id to log\n     * @param devices list of devices\n     */\n    _logOutputDevice(deviceID, devices) {\n        const device\n            = devices.find(\n                d => d.kind === 'audiooutput' && d.deviceId === deviceID);\n\n        if (device) {\n            Statistics.sendActiveDeviceListEvent(\n                RTC.getEventDataForActiveDevice(device));\n        }\n    }\n\n    /**\n     * Executes callback with list of media devices connected.\n     * @param {function} callback\n     */\n    enumerateDevices(callback) {\n        RTC.enumerateDevices(callback);\n    }\n\n    /**\n     * Checks if its possible to enumerate available cameras/micropones.\n     * @returns {Promise<boolean>} a Promise which will be resolved only once\n     * the WebRTC stack is ready, either with true if the device listing is\n     * available available or with false otherwise.\n     */\n    isDeviceListAvailable() {\n        return RTC.isDeviceListAvailable();\n    }\n\n    /**\n     * Returns true if changing the input (camera / microphone) or output\n     * (audio) device is supported and false if not.\n     * @param {string} [deviceType] - type of device to change. Default is\n     *      undefined or 'input', 'output' - for audio output device change.\n     * @returns {boolean} true if available, false otherwise.\n     */\n    isDeviceChangeAvailable(deviceType) {\n        return RTC.isDeviceChangeAvailable(deviceType);\n    }\n\n    /**\n     * Checks if the permission for the given device was granted.\n     *\n     * @param {'audio'|'video'} [type] - type of devices to check,\n     *      undefined stands for both 'audio' and 'video' together\n     * @returns {Promise<boolean>}\n     */\n    isDevicePermissionGranted(type) {\n        return new Promise(resolve => {\n            // Shortcut: first check if we already know the permission was\n            // granted.\n            if (type in this._permissions) {\n                resolve(this._permissions[type]);\n\n                return;\n            }\n\n            // Check using the Permissions API.\n            this._permissionsApiSupported.then(supported => {\n                if (!supported) {\n                    resolve(false);\n\n                    return;\n                }\n\n                const promises = [];\n\n                switch (type) {\n                case MediaType.VIDEO:\n                    promises.push(\n                        navigator.permissions.query({\n                            name: VIDEO_PERMISSION_NAME\n                        }));\n                    break;\n                case MediaType.AUDIO:\n                    promises.push(\n                        navigator.permissions.query({\n                            name: AUDIO_PERMISSION_NAME\n                        }));\n                    break;\n                default:\n                    promises.push(\n                        navigator.permissions.query({\n                            name: VIDEO_PERMISSION_NAME\n                        }));\n                    promises.push(\n                        navigator.permissions.query({\n                            name: AUDIO_PERMISSION_NAME\n                        }));\n                }\n\n                Promise.all(promises).then(\n                    results => resolve(results.every(permissionStatus => {\n                        try {\n                            return this._parsePermissionState(permissionStatus);\n                        } catch {\n                            return false;\n                        }\n                    })),\n                    () => resolve(false)\n                );\n            });\n        });\n    }\n\n    /**\n     * Returns true if it is possible to be simultaneously capturing audio from more than one device.\n     *\n     * @returns {boolean}\n     */\n    isMultipleAudioInputSupported() {\n        return !(browser.isFirefox() || browser.isIosBrowser());\n    }\n\n    /**\n     * Returns currently used audio output device id, 'default' stands\n     * for default device\n     * @returns {string}\n     */\n    getAudioOutputDevice() {\n        return RTC.getAudioOutputDevice();\n    }\n\n    /**\n     * Sets current audio output device.\n     * @param {string} deviceId - id of 'audiooutput' device from\n     *      navigator.mediaDevices.enumerateDevices(), 'default' is for\n     *      default device\n     * @returns {Promise} - resolves when audio output is changed, is rejected\n     *      otherwise\n     */\n    setAudioOutputDevice(deviceId) {\n        const availableDevices = RTC.getCurrentlyAvailableMediaDevices();\n\n        if (availableDevices.length > 0) {\n            // if we have devices info report device to stats\n            // normally this will not happen on startup as this method is called\n            // too early. This will happen only on user selection of new device\n            this._logOutputDevice(\n                deviceId, RTC.getCurrentlyAvailableMediaDevices());\n        }\n\n        return RTC.setAudioOutputDevice(deviceId);\n    }\n\n    /**\n     * Adds an event handler.\n     * @param {string} event - event name\n     * @param {function} handler - event handler\n     */\n    addEventListener(event, handler) {\n        this._eventEmitter.addListener(event, handler);\n    }\n\n    /**\n     * Removes event handler.\n     * @param {string} event - event name\n     * @param {function} handler - event handler\n     */\n    removeEventListener(event, handler) {\n        this._eventEmitter.removeListener(event, handler);\n    }\n\n    /**\n     * Emits an event.\n     * @param {string} event - event name\n     */\n    emitEvent(event, ...args) {\n        this._eventEmitter.emit(event, ...args);\n    }\n}\n\nexport default new JitsiMediaDevices();\n","import { getLogger } from '@jitsi/logger';\n\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport RTC from '../RTC/RTC';\nimport Statistics from '../statistics/statistics';\n\n\nconst logger = getLogger(__filename);\n\n// If after 3000 ms the detector did not find any active devices consider that there aren't any usable ones available\n// i.e. audioLevel > 0.008\nconst DETECTION_TIMEOUT = 3000;\n\n\n/**\n * Go through all audio devices on the system and return one that is active, i.e. has audio signal.\n *\n * @returns Promise<Object> - Object containing information about the found device.\n */\nexport default function getActiveAudioDevice() {\n\n    return new Promise(resolve => {\n        RTC.enumerateDevices(devices => {\n            const audioDevices = devices.filter(device => device.kind === 'audioinput');\n            const devicePromiseArray = [];\n\n\n            for (const micDevice of audioDevices) {\n                const devicePromise = RTC.obtainAudioAndVideoPermissions({ devices: [ 'audio' ],\n                    micDeviceId: micDevice.deviceId }).then(tracks => {\n\n                    // We expect a single device to be available when obtained from obtainAudioAndVideoPermissions\n                    // that's  why only take p.value[0].\n                    const track = tracks[0];\n                    const originalStream = track.getOriginalStream();\n\n                    Statistics.startLocalStats(originalStream, track.setAudioLevel.bind(track));\n                    track.addEventListener(JitsiTrackEvents.LOCAL_TRACK_STOPPED, () => {\n                        Statistics.stopLocalStats(originalStream);\n                    });\n\n                    return track;\n                });\n\n                devicePromiseArray.push(devicePromise);\n            }\n\n            Promise.allSettled(devicePromiseArray).then(outcomeArray => {\n                const successfulPromises = outcomeArray.filter(p => p.status === 'fulfilled');\n                const rejectedPromises = outcomeArray.filter(p => p.status === 'rejected');\n\n\n                const availableDevices = successfulPromises.map(p => p.value);\n                const rejectReasons = rejectedPromises.map(p => p.value);\n\n                for (const reason of rejectReasons) {\n                    logger.error('Failed to acquire audio device with error: ', reason);\n                }\n\n                // Setup event handlers for monitored devices.\n                for (const device of availableDevices) {\n                    device.on(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED, audioLevel => {\n                        // This is a very naive approach but works, a more accurate one would be to use rnnoise in\n                        // order to limit  the number of false positives. The 0.008 constant is due to how\n                        // LocalStatsCollector from lib-jitsi-meet publishes audio-levels, in this case 0.008 denotes //\n                        // no input.\n                        if (audioLevel > 0.008) {\n                            stopActiveDevices(availableDevices);\n                            resolve({ deviceId: device.deviceId,\n                                deviceLabel: device.track.label });\n                        }\n                    });\n                }\n\n                // Cancel the detection in case no devices was found with audioLevel > 0 in the set timeout.\n                setTimeout(() => {\n                    stopActiveDevices(availableDevices);\n                    resolve({\n                        deviceId: '',\n                        deviceLabel: '' }\n                    );\n                }, DETECTION_TIMEOUT);\n\n            });\n\n        });\n    });\n}\n\n/**\n * Stop the streams of the provided JitsiLocalTracks.\n *\n * @param {Array<JitsiLocalTrack>} deviceList - Array of JitsiLocalTracks to stop.\n * @returns {void}\n */\nfunction stopActiveDevices(deviceList) {\n    for (const device of deviceList) {\n        device.stopStream();\n    }\n}\n","/**\n * The know jingle actions that can be sent and should be acted upon by\n * {@code ProxyConnectionService} and {@code ProxyConnectionPC}.\n */\nexport enum ACTIONS {\n    ACCEPT = 'session-accept',\n    CONNECTION_ERROR = 'connection-error-encountered',\n    INITIATE = 'session-initiate',\n    TERMINATE = 'session-terminate',\n    TRANSPORT_INFO = 'transport-info',\n    UNAVAILABLE = 'unavailable'\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { XMPPEvents } from '../../service/xmpp/XMPPEvents';\nimport RTC from '../RTC/RTC';\nimport JingleSessionPC from '../xmpp/JingleSessionPC';\nimport SignalingLayerImpl from '../xmpp/SignalingLayerImpl';\nimport { DEFAULT_STUN_SERVERS } from '../xmpp/xmpp';\n\nimport { ACTIONS } from './constants';\n\nconst logger = getLogger(__filename);\n\n/**\n * An adapter around {@code JingleSessionPC} so its logic can be re-used without\n * an XMPP connection. It is being re-used for consistency with the rest of the\n * codebase and to leverage existing peer connection event handling. Also\n * this class provides a facade to hide most of the API for\n * {@code JingleSessionPC}.\n */\nexport default class ProxyConnectionPC {\n    /**\n     * Initializes a new {@code ProxyConnectionPC} instance.\n     *\n     * @param {Object} options - Values to initialize the instance with.\n     * @param {Object} [options.pcConfig] - The {@code RTCConfiguration} to use for the WebRTC peer connection.\n     * @param {boolean} [options.isInitiator] - If true, the local client should send offers. If false, the local\n     * client should send answers. Defaults to false.\n     * @param {Function} options.onRemoteStream - Callback to invoke when a remote media stream has been received\n     * through the peer connection.\n     * @param {string} options.peerJid - The jid of the remote client with which the peer connection is being establish\n     * and which should receive direct messages regarding peer connection updates.\n     * @param {boolean} [options.receiveVideo] - Whether or not the peer connection should accept incoming video\n     * streams. Defaults to false.\n     * @param {Function} options.onSendMessage - Callback to invoke when a message has to be sent (signaled) out.\n     */\n    constructor(options = {}) {\n        this._options = {\n            pcConfig: {},\n            isInitiator: false,\n            receiveAudio: false,\n            receiveVideo: false,\n            ...options\n        };\n\n        /**\n         * Instances of {@code JitsiTrack} associated with this instance of\n         * {@code ProxyConnectionPC}.\n         *\n         * @type {Array<JitsiTrack>}\n         */\n        this._tracks = [];\n\n        /**\n         * The active instance of {@code JingleSessionPC}.\n         *\n         * @type {JingleSessionPC|null}\n         */\n        this._peerConnection = null;\n\n        // Bind event handlers so they are only bound once for every instance.\n        this._onError = this._onError.bind(this);\n        this._onRemoteStream = this._onRemoteStream.bind(this);\n        this._onSendMessage = this._onSendMessage.bind(this);\n    }\n\n    /**\n     * Returns the jid of the remote peer with which this peer connection should\n     * be established with.\n     *\n     * @returns {string}\n     */\n    getPeerJid() {\n        return this._options.peerJid;\n    }\n\n    /**\n     * Updates the peer connection based on the passed in jingle.\n     *\n     * @param {Object} $jingle - An XML jingle element, wrapped in query,\n     * describing how the peer connection should be updated.\n     * @returns {void}\n     */\n    processMessage($jingle) {\n        switch ($jingle.attr('action')) {\n        case ACTIONS.ACCEPT:\n            this._onSessionAccept($jingle);\n            break;\n\n        case ACTIONS.INITIATE:\n            this._onSessionInitiate($jingle);\n            break;\n\n        case ACTIONS.TERMINATE:\n            this._onSessionTerminate($jingle);\n            break;\n\n        case ACTIONS.TRANSPORT_INFO:\n            this._onTransportInfo($jingle);\n            break;\n        }\n    }\n\n    /**\n     * Instantiates a peer connection and starts the offer/answer cycle to\n     * establish a connection with a remote peer.\n     *\n     * @param {Array<JitsiLocalTrack>} localTracks - Initial local tracks to add\n     * to add to the peer connection.\n     * @returns {void}\n     */\n    start(localTracks = []) {\n        if (this._peerConnection) {\n            return;\n        }\n\n        this._tracks = this._tracks.concat(localTracks);\n\n        this._peerConnection = this._createPeerConnection();\n\n        this._peerConnection.invite(localTracks);\n    }\n\n    /**\n     * Begins the process of disconnecting from a remote peer and cleaning up\n     * the peer connection.\n     *\n     * @returns {void}\n     */\n    stop() {\n        if (this._peerConnection) {\n            this._peerConnection.terminate();\n        }\n\n        this._onSessionTerminate();\n    }\n\n    /**\n     * Instantiates a new {@code JingleSessionPC} by stubbing out the various\n     * dependencies of {@code JingleSessionPC}.\n     *\n     * @private\n     * @returns {JingleSessionPC}\n     */\n    _createPeerConnection() {\n        /**\n         * {@code JingleSessionPC} takes in the entire jitsi-meet config.js\n         * object, which may not be accessible from the caller.\n         *\n         * @type {Object}\n         */\n        const configStub = {};\n\n        /**\n         * {@code JingleSessionPC} assumes an XMPP/Strophe connection object is\n         * passed through, which also has the jingle plugin initialized on it.\n         * This connection object is used to signal out peer connection updates\n         * via iqs, and those updates need to be piped back out to the remote\n         * peer.\n         *\n         * @type {Object}\n         */\n        const connectionStub = {\n            // At the time this is used for Spot and it's okay to say the connection is always connected, because if\n            // spot has no signalling it will not be in a meeting where this is used.\n            connected: true,\n            jingle: {\n                terminate: () => { /** no-op */ }\n            },\n            sendIQ: this._onSendMessage,\n\n            // Returns empty function, because it does not add any listeners for real\n            // eslint-disable-next-line no-empty-function\n            addEventListener: () => () => { }\n        };\n\n        /**\n         * {@code JingleSessionPC} can take in a custom ice configuration,\n         * depending on the peer connection type, peer-to-peer or other.\n         * However, {@code ProxyConnectionPC} always assume a peer-to-peer\n         * connection so the ice configuration is hard-coded with defaults.\n         *\n         * @type {Object}\n         */\n        const pcConfigStub = {\n            iceServers: DEFAULT_STUN_SERVERS,\n            ...this._options.pcConfig\n        };\n\n        /**\n         * {@code JingleSessionPC} expects an instance of\n         * {@code JitsiConference}, which has an event emitter that is used\n         * to signal various connection updates that the local client should\n         * act upon. The conference instance is not a dependency of a proxy\n         * connection, but the emitted events can be relevant to the proxy\n         * connection so the event emitter is stubbed.\n         *\n         * @param {string} event - The constant for the event type.\n         * @type {Function}\n         * @returns {void}\n         */\n        const emitter = event => {\n            switch (event) {\n            case XMPPEvents.CONNECTION_ICE_FAILED:\n            case XMPPEvents.CONNECTION_FAILED:\n                this._onError(ACTIONS.CONNECTION_ERROR, event);\n                break;\n            }\n        };\n\n        /**\n         * {@link JingleSessionPC} expects an instance of\n         * {@link ChatRoom} to be passed in. {@link ProxyConnectionPC}\n         * is instantiated outside of the {@code JitsiConference}, so it must be\n         * stubbed to prevent errors.\n         *\n         * @type {Object}\n         */\n        const roomStub = {\n            addPresenceListener: () => { /** no-op */ },\n            connectionTimes: [],\n            eventEmitter: { emit: emitter },\n            getMediaPresenceInfo: () => {\n                // Errors occur if this function does not return an object\n\n                return {};\n            },\n            removePresenceListener: () => { /** no-op */ },\n            supportsRestartByTerminate: () => false\n        };\n\n        /**\n         * A {@code JitsiConference} stub passed to the {@link RTC} module.\n         * @type {Object}\n         */\n        const conferenceStub = {\n            myUserId: () => ''\n        };\n\n        /**\n         * Create an instance of {@code RTC} as it is required for peer\n         * connection creation by {@code JingleSessionPC}. An existing instance\n         * of {@code RTC} from elsewhere should not be re-used because it is\n         * a stateful grouping of utilities.\n         */\n        this._rtc = new RTC(conferenceStub, {});\n\n        /**\n         * Add the remote track listener here as {@code JingleSessionPC} has\n         * {@code TraceablePeerConnection} which uses {@code RTC}'s event\n         * emitter.\n         */\n        this._rtc.addListener(\n            RTCEvents.REMOTE_TRACK_ADDED,\n            this._onRemoteStream\n        );\n\n        const peerConnection = new JingleSessionPC(\n            undefined, // sid\n            undefined, // localJid\n            this._options.peerJid, // remoteJid\n            connectionStub, // connection\n            {\n                offerToReceiveAudio: this._options.receiveAudio,\n                offerToReceiveVideo: this._options.receiveVideo\n            }, // mediaConstraints\n            pcConfigStub, // pcConfig\n            true, // isP2P\n            this._options.isInitiator // isInitiator\n        );\n\n        const signalingLayer = new SignalingLayerImpl();\n\n        signalingLayer.setChatRoom(roomStub);\n\n        /**\n         * An additional initialize call is necessary to properly set instance\n         * variable for calling.\n         */\n        peerConnection.initialize(roomStub, this._rtc, signalingLayer, configStub);\n\n        return peerConnection;\n    }\n\n    /**\n     * Invoked when a connection related issue has been encountered.\n     *\n     * @param {string} errorType - The constant indicating the type of the error\n     * that occured.\n     * @param {string} details - Optional additional data about the error.\n     * @private\n     * @returns {void}\n     */\n    _onError(errorType, details = '') {\n        this._options.onError(this._options.peerJid, errorType, details);\n    }\n\n    /**\n     * Callback invoked when the peer connection has received a remote media\n     * stream.\n     *\n     * @param {JitsiRemoteTrack} jitsiRemoteTrack - The remote media stream\n     * wrapped in {@code JitsiRemoteTrack}.\n     * @private\n     * @returns {void}\n     */\n    _onRemoteStream(jitsiRemoteTrack) {\n        this._tracks.push(jitsiRemoteTrack);\n\n        this._options.onRemoteStream(jitsiRemoteTrack);\n    }\n\n    /**\n     * Callback invoked when {@code JingleSessionPC} needs to signal a message\n     * out to the remote peer.\n     *\n     * @param {XML} iq - The message to signal out.\n     * @private\n     * @returns {void}\n     */\n    _onSendMessage(iq) {\n        this._options.onSendMessage(this._options.peerJid, iq);\n    }\n\n    /**\n     * Callback invoked in response to an agreement to start a proxy connection.\n     * The passed in jingle element should contain an SDP answer to a previously\n     * sent SDP offer.\n     *\n     * @param {Object} $jingle - The jingle element wrapped in jQuery.\n     * @private\n     * @returns {void}\n     */\n    _onSessionAccept($jingle) {\n        if (!this._peerConnection) {\n            logger.error('Received an answer when no peer connection exists.');\n\n            return;\n        }\n\n        this._peerConnection.setAnswer($jingle);\n    }\n\n    /**\n     * Callback invoked in response to a request to start a proxy connection.\n     * The passed in jingle element should contain an SDP offer.\n     *\n     * @param {Object} $jingle - The jingle element wrapped in jQuery.\n     * @private\n     * @returns {void}\n     */\n    _onSessionInitiate($jingle) {\n        if (this._peerConnection) {\n            logger.error('Received an offer when an offer was already sent.');\n\n            return;\n        }\n\n        this._peerConnection = this._createPeerConnection();\n\n        this._peerConnection.acceptOffer(\n            $jingle,\n            () => { /** no-op */ },\n            () => this._onError(\n                this._options.peerJid,\n                ACTIONS.CONNECTION_ERROR,\n                'session initiate error'\n            )\n        );\n    }\n\n    /**\n     * Callback invoked in response to a request to disconnect an active proxy\n     * connection. Cleans up tracks and the peer connection.\n     *\n     * @private\n     * @returns {void}\n     */\n    _onSessionTerminate() {\n        this._tracks.forEach(track => track.dispose());\n        this._tracks = [];\n\n        if (this._peerConnection) {\n            this._peerConnection.onTerminated();\n        }\n\n        if (this._rtc) {\n            this._rtc.removeListener(\n                RTCEvents.REMOTE_TRACK_ADDED,\n                this._onRemoteStream\n            );\n\n            this._rtc.destroy();\n        }\n    }\n\n    /**\n     * Callback invoked in response to ICE candidates from the remote peer.\n     * The passed in jingle element should contain an ICE candidate.\n     *\n     * @param {Object} $jingle - The jingle element wrapped in jQuery.\n     * @private\n     * @returns {void}\n     */\n    _onTransportInfo($jingle) {\n        this._peerConnection.addIceCandidates($jingle);\n    }\n}\n","/* globals $ */\n\nimport { getLogger } from '@jitsi/logger';\nimport { $iq } from 'strophe.js';\n\nimport { MediaType } from '../../service/RTC/MediaType';\nimport VideoType from '../../service/RTC/VideoType';\nimport RTC from '../RTC/RTC';\n\nimport ProxyConnectionPC from './ProxyConnectionPC';\nimport { ACTIONS } from './constants';\n\nconst logger = getLogger(__filename);\n\n/**\n * Instantiates a new ProxyConnectionPC and ensures only one exists at a given\n * time. Currently it assumes ProxyConnectionPC is used only for screensharing\n * and assumes IQs to be used for communication.\n */\nexport default class ProxyConnectionService {\n    /**\n     * Initializes a new {@code ProxyConnectionService} instance.\n     *\n     * @param {Object} options - Values to initialize the instance with.\n     * @param {boolean} [options.convertVideoToDesktop] - Whether or not proxied video should be returned as a desktop\n     * stream. Defaults to false.\n     * @param {Object} [options.pcConfig] - The {@code RTCConfiguration} to use for the WebRTC peer connection.\n     * @param {JitsiConnection} [options.jitsiConnection] - The {@code JitsiConnection} which will be used to fetch\n     * TURN credentials for the P2P connection.\n     * @param {Function} options.onRemoteStream - Callback to invoke when a remote video stream has been received and\n     * converted to a {@code JitsiLocakTrack}. The {@code JitsiLocakTrack} will be passed in.\n     * @param {Function} options.onSendMessage - Callback to invoke when a message has to be sent (signaled) out. The\n     * arguments passed in are the jid to send the message to and the message.\n     */\n    constructor(options = {}) {\n        const {\n            jitsiConnection,\n            ...otherOptions\n        } = options;\n\n        /**\n         * Holds a reference to the collection of all callbacks.\n         *\n         * @type {Object}\n         */\n        this._options = {\n            pcConfig: jitsiConnection && jitsiConnection.xmpp.connection.jingle.p2pIceConfig,\n            ...otherOptions\n        };\n\n        /**\n         * The active instance of {@code ProxyConnectionService}.\n         *\n         * @type {ProxyConnectionPC|null}\n         */\n        this._peerConnection = null;\n\n        // Bind event handlers so they are only bound once for every instance.\n        this._onFatalError = this._onFatalError.bind(this);\n        this._onSendMessage = this._onSendMessage.bind(this);\n        this._onRemoteStream = this._onRemoteStream.bind(this);\n    }\n\n    /**\n     * Parses a message object regarding a proxy connection to create a new\n     * proxy connection or update and existing connection.\n     *\n     * @param {Object} message - A message object regarding establishing or\n     * updating a proxy connection.\n     * @param {Object} message.data - An object containing additional message\n     * details.\n     * @param {string} message.data.iq - The stringified iq which explains how\n     * and what to update regarding the proxy connection.\n     * @param {string} message.from - The message sender's full jid. Used for\n     * sending replies.\n     * @returns {void}\n     */\n    processMessage(message) {\n        const peerJid = message.from;\n\n        if (!peerJid) {\n            return;\n        }\n\n        // If a proxy connection has already been established and messages come\n        // from another peer jid then those messages should be replied to with\n        // a rejection.\n        if (this._peerConnection\n            && this._peerConnection.getPeerJid() !== peerJid) {\n            this._onFatalError(\n                peerJid,\n                ACTIONS.CONNECTION_ERROR,\n                'rejected'\n            );\n\n            return;\n        }\n\n        const iq = this._convertStringToXML(message.data.iq);\n        const $jingle = iq && iq.find('jingle');\n        const action = $jingle && $jingle.attr('action');\n\n        if (action === ACTIONS.INITIATE) {\n            this._peerConnection = this._createPeerConnection(peerJid, {\n                isInitiator: false,\n                receiveVideo: true\n            });\n        }\n\n        // Truthy check for peer connection added to protect against possibly\n        // receiving actions before an ACTIONS.INITIATE.\n        if (this._peerConnection) {\n            this._peerConnection.processMessage($jingle);\n        }\n\n        // Take additional steps to ensure the peer connection is cleaned up\n        // if it is to be closed.\n        if (action === ACTIONS.CONNECTION_ERROR\n            || action === ACTIONS.UNAVAILABLE\n            || action === ACTIONS.TERMINATE) {\n            this._selfCloseConnection();\n        }\n\n        return;\n    }\n\n    /**\n     * Instantiates and initiates a proxy peer connection.\n     *\n     * @param {string} peerJid - The jid of the remote client that should\n     * receive messages.\n     * @param {Array<JitsiLocalTrack>} localTracks - Initial media tracks to\n     * send through to the peer.\n     * @returns {void}\n     */\n    start(peerJid, localTracks = []) {\n        this._peerConnection = this._createPeerConnection(peerJid, {\n            isInitiator: true,\n            receiveVideo: false\n        });\n\n        this._peerConnection.start(localTracks);\n    }\n\n    /**\n     * Terminates any active proxy peer connection.\n     *\n     * @returns {void}\n     */\n    stop() {\n        if (this._peerConnection) {\n            this._peerConnection.stop();\n        }\n\n        this._peerConnection = null;\n    }\n\n    /**\n     * Transforms a stringified xML into a XML wrapped in jQuery.\n     *\n     * @param {string} xml - The XML in string form.\n     * @private\n     * @returns {Object|null} A jQuery version of the xml. Null will be returned\n     * if an error is encountered during transformation.\n     */\n    _convertStringToXML(xml) {\n        try {\n            const xmlDom = new DOMParser().parseFromString(xml, 'text/xml');\n\n            return $(xmlDom);\n        } catch (e) {\n            logger.error('Attempted to convert incorrectly formatted xml');\n\n            return null;\n        }\n    }\n\n    /**\n     * Helper for creating an instance of {@code ProxyConnectionPC}.\n     *\n     * @param {string} peerJid - The jid of the remote peer with which the\n     * {@code ProxyConnectionPC} will be established with.\n     * @param {Object} options - Additional defaults to instantiate the\n     * {@code ProxyConnectionPC} with. See the constructor of ProxyConnectionPC\n     * for more details.\n     * @private\n     * @returns {ProxyConnectionPC}\n     */\n    _createPeerConnection(peerJid, options = {}) {\n        if (!peerJid) {\n            throw new Error('Cannot create ProxyConnectionPC without a peer.');\n        }\n\n        const pcOptions = {\n            pcConfig: this._options.pcConfig,\n            onError: this._onFatalError,\n            onRemoteStream: this._onRemoteStream,\n            onSendMessage: this._onSendMessage,\n            peerJid,\n            ...options\n        };\n\n        return new ProxyConnectionPC(pcOptions);\n    }\n\n    /**\n     * Callback invoked when an error occurs that should cause\n     * {@code ProxyConnectionPC} to be closed if the peer is currently\n     * connected. Sends an error message/reply back to the peer.\n     *\n     * @param {string} peerJid - The peer jid with which the connection was\n     * attempted or started, and to which an iq with error details should be\n     * sent.\n     * @param {string} errorType - The constant indicating the type of the error\n     * that occured.\n     * @param {string} details - Optional additional data about the error.\n     * @private\n     * @returns {void}\n     */\n    _onFatalError(peerJid, errorType, details = '') {\n        logger.error(\n            'Received a proxy connection error', peerJid, errorType, details);\n\n        const iq = $iq({\n            to: peerJid,\n            type: 'set'\n        })\n            .c('jingle', {\n                xmlns: 'urn:xmpp:jingle:1',\n                action: errorType\n            })\n            .c('details')\n            .t(details)\n            .up();\n\n        this._onSendMessage(peerJid, iq);\n\n        if (this._peerConnection\n            && this._peerConnection.getPeerJid() === peerJid) {\n            this._selfCloseConnection();\n        }\n    }\n\n    /**\n     * Callback invoked when the remote peer of the {@code ProxyConnectionPC}\n     * has offered a media stream. The stream is converted into a\n     * {@code JitsiLocalTrack} for local usage if the {@code onRemoteStream}\n     * callback is defined.\n     *\n     * @param {JitsiRemoteTrack} jitsiRemoteTrack - The {@code JitsiRemoteTrack}\n     * for the peer's media stream.\n     * @private\n     * @returns {void}\n     */\n    _onRemoteStream(jitsiRemoteTrack) {\n        if (!this._options.onRemoteStream) {\n            logger.error('Remote track received without callback.');\n            jitsiRemoteTrack.dispose();\n\n            return;\n        }\n\n        const isVideo = jitsiRemoteTrack.isVideoTrack();\n        let videoType;\n\n        if (isVideo) {\n            videoType = this._options.convertVideoToDesktop\n                ? VideoType.DESKTOP : VideoType.CAMERA;\n        }\n\n        // Grab the webrtc media stream and pipe it through the same processing\n        // that would occur for a locally obtained media stream.\n        const mediaStream = jitsiRemoteTrack.getOriginalStream();\n        const jitsiLocalTracks = RTC.createLocalTracks(\n            [\n                {\n                    deviceId:\n                        `proxy:${this._peerConnection.getPeerJid()}`,\n                    mediaType: isVideo ? MediaType.VIDEO : MediaType.AUDIO,\n                    sourceType: 'proxy',\n                    stream: mediaStream,\n                    track: mediaStream.getVideoTracks()[0],\n                    videoType\n                }\n            ]);\n\n        this._options.onRemoteStream(jitsiLocalTracks[0]);\n    }\n\n    /**\n     * Formats and forwards a message an iq to be sent to a peer jid.\n     *\n     * @param {string} peerJid - The jid the iq should be sent to.\n     * @param {Object} iq - The iq which would be sent to the peer jid.\n     * @private\n     * @returns {void}\n     */\n    _onSendMessage(peerJid, iq) {\n        if (!this._options.onSendMessage) {\n            return;\n        }\n\n        try {\n            const stringifiedIq\n                = new XMLSerializer().serializeToString(iq.nodeTree || iq);\n\n            this._options.onSendMessage(peerJid, { iq: stringifiedIq });\n        } catch (e) {\n            logger.error('Attempted to send an incorrectly formatted iq.');\n        }\n    }\n\n    /**\n     * Invoked when preemptively closing the {@code ProxyConnectionPC}.\n     *\n     * @private\n     * @returns {void}\n     */\n    _selfCloseConnection() {\n        this.stop();\n\n        this._options.onConnectionClosed\n            && this._options.onConnectionClosed();\n    }\n}\n","import EventEmitter from 'events';\n\nimport browser from '../browser';\nimport Settings from '../settings/Settings';\nimport ScriptUtil from '../util/ScriptUtil';\n\nimport { CALLSTATS_SCRIPT_URL } from './constants';\n\nconst PRECALL_TEST_RESULTS = 'preCallTestResults';\nconst emitter = new EventEmitter();\nlet _initialized = false;\nlet api = null;\n\n/**\n * Loads the callstats io script.\n *\n * @returns {Promise<void>}\n */\nfunction _loadScript(options) {\n    if (browser.isReactNative()) {\n        return;\n    }\n\n    return new Promise(resolve => {\n        ScriptUtil.loadScript(\n            options.callStatsCustomScriptUrl || CALLSTATS_SCRIPT_URL,\n            /* async */ true,\n            /* prepend */ true,\n            /* relativeURL */ undefined,\n            /* loadCallback */ resolve);\n    });\n}\n\n/**\n * Initializes the callstats lib and registers a callback to be invoked\n * when there are 'preCallTestResults'.\n *\n * @typedef PrecallTestOptions\n * @type {Object}\n * @property {string} callStatsID - Callstats credentials - the id.\n * @property {string} callStatsSecret - Callstats credentials - the secret.\n * @property {string} statisticsId - The user name to use when initializing callstats.\n * @property {string} statisticsDisplayName - The user display name.\n *\n * @param { PrecallTestOptions} options - The init options.\n * @returns {Promise<void>}\n */\nfunction _initialize(options) {\n    return new Promise((resolve, reject) => {\n        const appId = options.callStatsID;\n        const appSecret = options.callStatsSecret;\n        const userId = options.statisticsId || options.statisticsDisplayName || Settings.callStatsUserName;\n\n        api.initialize(appId, appSecret, userId, (status, message) => {\n            if (status === 'success') {\n                api.on(PRECALL_TEST_RESULTS, (...args) => {\n                    emitter.emit(PRECALL_TEST_RESULTS, ...args);\n                });\n                _initialized = true;\n                resolve();\n            } else {\n                reject({\n                    status,\n                    message\n                });\n            }\n        }, null, { disablePrecalltest: true });\n    });\n}\n\n/**\n * Loads the callstats script and initializes the library.\n *\n * @param {Function} onResult - The callback to be invoked when results are received.\n * @returns {Promise<void>}\n */\nexport async function init(options) {\n    if (_initialized) {\n        throw new Error('Precall Test already initialized');\n    }\n\n    const { callStatsID, callStatsSecret, disableThirdPartyRequests } = options;\n\n    if (!callStatsID || !callStatsSecret || disableThirdPartyRequests) {\n        throw new Error('Callstats is disabled');\n    }\n\n    await _loadScript(options);\n    // eslint-disable-next-line new-cap\n    api = new window.callstats();\n\n    return _initialize(options);\n}\n\n/**\n * Executes a pre call test.\n *\n * @typedef PrecallTestResults\n * @type {Object}\n * @property {boolean} mediaConnectivity - If there is media connectivity or not.\n * @property {number} throughput  - The average throughput.\n * @property {number} fractionalLoss - The packet loss.\n * @property {number} rtt - The round trip time.\n * @property {string} provider - It is usually 'callstats'.\n *\n * @returns {Promise<{PrecallTestResults}>}\n */\nexport function execute() {\n    if (!_initialized) {\n        return Promise.reject('uninitialized');\n    }\n\n    return new Promise((resolve, reject) => {\n        emitter.on(PRECALL_TEST_RESULTS, (status, payload) => {\n            if (status === 'success') {\n                resolve(payload);\n            } else {\n                reject({\n                    status,\n                    payload\n                });\n            }\n\n        });\n\n        api.makePrecallTest();\n    });\n}\n\nexport default {\n    init,\n    execute\n};\n","import { getLogger } from '@jitsi/logger';\n\nimport { createAudioContext } from './WebAudioUtils';\n\nconst logger = getLogger(__filename);\n\n/**\n * The AudioMixer, as the name implies, mixes a number of MediaStreams containing audio tracks into a single\n * MediaStream.\n */\nexport default class AudioMixer {\n    /**\n     * Create AudioMixer instance.\n     */\n    constructor() {\n        this._started = false;\n        this._streamsToMix = [];\n        this._streamMSSArray = [];\n    }\n\n    /**\n     * Add audio MediaStream to be mixed, if the stream doesn't contain any audio tracks it will be ignored.\n     *\n     * @param {MediaStream} stream - MediaStream to be mixed.\n     */\n    addMediaStream(stream) {\n        if (!stream.getAudioTracks()) {\n            logger.warn('Added MediaStream doesn\\'t contain audio tracks.');\n        }\n\n        this._streamsToMix.push(stream);\n    }\n\n    /**\n     * At this point a WebAudio ChannelMergerNode is created and and the two associated MediaStreams are connected to\n     * it; the resulting mixed MediaStream is returned.\n     *\n     * @returns {MediaStream} - MediaStream containing added streams mixed together, or null if no MediaStream\n     * is added.\n     */\n    start() {\n        // If the mixer was already started just return the existing mixed stream.\n        if (this._started) {\n            return this._mixedMSD.stream;\n        }\n\n        this._audioContext = createAudioContext();\n\n        if (!this._streamsToMix.length) {\n            logger.warn('No MediaStream\\'s added to AudioMixer, nothing will happen.');\n\n            return null;\n        }\n\n        this._started = true;\n\n        this._mixedMSD = this._audioContext.createMediaStreamDestination();\n\n        for (const stream of this._streamsToMix) {\n            const streamMSS = this._audioContext.createMediaStreamSource(stream);\n\n            streamMSS.connect(this._mixedMSD);\n\n            // Maintain a list of MediaStreamAudioSourceNode so we can disconnect them on reset.\n            this._streamMSSArray.push(streamMSS);\n        }\n\n        return this._mixedMSD.stream;\n    }\n\n    /**\n     * Disconnect MediaStreamAudioSourceNode and clear references.\n     *\n     * @returns {void}\n     */\n    reset() {\n        this._started = false;\n        this._streamsToMix = [];\n\n        // Clean up created MediaStreamAudioSourceNode.\n        for (const streamMSS of this._streamMSSArray) {\n            streamMSS.disconnect();\n        }\n\n        this._streamMSSArray = [];\n\n        if (this._audioContext) {\n            this._audioContext = undefined;\n        }\n    }\n}\n","import Logger from '@jitsi/logger';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport JitsiConnection from './JitsiConnection';\nimport * as JitsiConnectionErrors from './JitsiConnectionErrors';\nimport * as JitsiConnectionEvents from './JitsiConnectionEvents';\nimport JitsiMediaDevices from './JitsiMediaDevices';\nimport * as JitsiMediaDevicesEvents from './JitsiMediaDevicesEvents';\nimport JitsiTrackError from './JitsiTrackError';\nimport * as JitsiTrackErrors from './JitsiTrackErrors';\nimport * as JitsiTrackEvents from './JitsiTrackEvents';\nimport * as JitsiTranscriptionStatus from './JitsiTranscriptionStatus';\nimport RTC from './modules/RTC/RTC';\nimport browser from './modules/browser';\nimport NetworkInfo from './modules/connectivity/NetworkInfo';\nimport { ParticipantConnectionStatus }\n    from './modules/connectivity/ParticipantConnectionStatus';\nimport { TrackStreamingStatus } from './modules/connectivity/TrackStreamingStatus';\nimport getActiveAudioDevice from './modules/detection/ActiveDeviceDetector';\nimport * as DetectionEvents from './modules/detection/DetectionEvents';\nimport TrackVADEmitter from './modules/detection/TrackVADEmitter';\nimport FeatureFlags from './modules/flags/FeatureFlags';\nimport ProxyConnectionService\n    from './modules/proxyconnection/ProxyConnectionService';\nimport recordingConstants from './modules/recording/recordingConstants';\nimport Settings from './modules/settings/Settings';\nimport LocalStatsCollector from './modules/statistics/LocalStatsCollector';\nimport precallTest from './modules/statistics/PrecallTest';\nimport Statistics from './modules/statistics/statistics';\nimport AuthUtil from './modules/util/AuthUtil';\nimport GlobalOnErrorHandler from './modules/util/GlobalOnErrorHandler';\nimport ScriptUtil from './modules/util/ScriptUtil';\nimport * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';\nimport AudioMixer from './modules/webaudio/AudioMixer';\nimport { MediaType } from './service/RTC/MediaType';\nimport * as ConnectionQualityEvents\n    from './service/connectivity/ConnectionQualityEvents';\nimport * as E2ePingEvents from './service/e2eping/E2ePingEvents';\nimport { createGetUserMediaEvent } from './service/statistics/AnalyticsEvents';\n\nconst logger = Logger.getLogger(__filename);\n\n/**\n * The amount of time to wait until firing\n * {@link JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN} event.\n */\nconst USER_MEDIA_SLOW_PROMISE_TIMEOUT = 1000;\n\n/**\n * Extracts from an 'options' objects with a specific format (TODO what IS the\n * format?) the attributes which are to be logged in analytics events.\n *\n * @param options gum options (???)\n * @returns {*} the attributes to attach to analytics events.\n */\nfunction getAnalyticsAttributesFromOptions(options) {\n    const attributes = {\n        'audio_requested':\n            options.devices.includes('audio'),\n        'video_requested':\n            options.devices.includes('video'),\n        'screen_sharing_requested':\n            options.devices.includes('desktop')\n    };\n\n    if (attributes.video_requested) {\n        attributes.resolution = options.resolution;\n    }\n\n    return attributes;\n}\n\n/**\n * Tries to deal with the following problem: {@code JitsiMeetJS} is not only\n * this module, it's also a global (i.e. attached to {@code window}) namespace\n * for all globals of the projects in the Jitsi Meet family. If lib-jitsi-meet\n * is loaded through an HTML {@code script} tag, {@code JitsiMeetJS} will\n * automatically be attached to {@code window} by webpack. Unfortunately,\n * webpack's source code does not check whether the global variable has already\n * been assigned and overwrites it. Which is OK for the module\n * {@code JitsiMeetJS} but is not OK for the namespace {@code JitsiMeetJS}\n * because it may already contain the values of other projects in the Jitsi Meet\n * family. The solution offered here works around webpack by merging all\n * existing values of the namespace {@code JitsiMeetJS} into the module\n * {@code JitsiMeetJS}.\n *\n * @param {Object} module - The module {@code JitsiMeetJS} (which will be\n * exported and may be attached to {@code window} by webpack later on).\n * @private\n * @returns {Object} - A {@code JitsiMeetJS} module which contains all existing\n * value of the namespace {@code JitsiMeetJS} (if any).\n */\nfunction _mergeNamespaceAndModule(module) {\n    return (\n        typeof window.JitsiMeetJS === 'object'\n            ? Object.assign({}, window.JitsiMeetJS, module)\n            : module);\n}\n\n/**\n * The public API of the Jitsi Meet library (a.k.a. {@code JitsiMeetJS}).\n */\nexport default _mergeNamespaceAndModule({\n\n    version: '{#COMMIT_HASH#}',\n\n    JitsiConnection,\n\n    /**\n     * {@code ProxyConnectionService} is used to connect a remote peer to a\n     * local Jitsi participant without going through a Jitsi conference. It is\n     * currently used for room integration development, specifically wireless\n     * screensharing. Its API is experimental and will likely change; usage of\n     * it is advised against.\n     */\n    ProxyConnectionService,\n\n    constants: {\n        participantConnectionStatus: ParticipantConnectionStatus,\n        recording: recordingConstants,\n        sipVideoGW: VideoSIPGWConstants,\n        transcriptionStatus: JitsiTranscriptionStatus,\n        trackStreamingStatus: TrackStreamingStatus\n    },\n    events: {\n        conference: JitsiConferenceEvents,\n        connection: JitsiConnectionEvents,\n        detection: DetectionEvents,\n        track: JitsiTrackEvents,\n        mediaDevices: JitsiMediaDevicesEvents,\n        connectionQuality: ConnectionQualityEvents,\n        e2eping: E2ePingEvents\n    },\n    errors: {\n        conference: JitsiConferenceErrors,\n        connection: JitsiConnectionErrors,\n        track: JitsiTrackErrors\n    },\n    errorTypes: {\n        JitsiTrackError\n    },\n    logLevels: Logger.levels,\n    mediaDevices: JitsiMediaDevices,\n    analytics: Statistics.analytics,\n    init(options = {}) {\n        Settings.init(options.externalStorage);\n        Statistics.init(options);\n\n        // Configure the feature flags.\n        FeatureFlags.init(options.flags || { });\n\n        // Initialize global window.connectionTimes\n        // FIXME do not use 'window'\n        if (!window.connectionTimes) {\n            window.connectionTimes = {};\n        }\n\n        if (options.enableAnalyticsLogging !== true) {\n            logger.warn('Analytics disabled, disposing.');\n            this.analytics.dispose();\n        }\n\n        if (options.enableWindowOnErrorHandler) {\n            GlobalOnErrorHandler.addHandler(\n                this.getGlobalOnErrorHandler.bind(this));\n        }\n\n        if (this.version) {\n            const logObject = {\n                id: 'component_version',\n                component: 'lib-jitsi-meet',\n                version: this.version\n            };\n\n            Statistics.sendLog(JSON.stringify(logObject));\n        }\n\n        return RTC.init(options);\n    },\n\n    /**\n     * Returns whether the desktop sharing is enabled or not.\n     *\n     * @returns {boolean}\n     */\n    isDesktopSharingEnabled() {\n        return RTC.isDesktopSharingEnabled();\n    },\n\n    /**\n     * Returns whether the current execution environment supports WebRTC (for\n     * use within this library).\n     *\n     * @returns {boolean} {@code true} if WebRTC is supported in the current\n     * execution environment (for use within this library); {@code false},\n     * otherwise.\n     */\n    isWebRtcSupported() {\n        return RTC.isWebRtcSupported();\n    },\n\n    setLogLevel(level) {\n        Logger.setLogLevel(level);\n    },\n\n    /**\n     * Sets the log level to the <tt>Logger</tt> instance with given id.\n     *\n     * @param {Logger.levels} level the logging level to be set\n     * @param {string} id the logger id to which new logging level will be set.\n     * Usually it's the name of the JavaScript source file including the path\n     * ex. \"modules/xmpp/ChatRoom.js\"\n     */\n    setLogLevelById(level, id) {\n        Logger.setLogLevelById(level, id);\n    },\n\n    /**\n     * Registers new global logger transport to the library logging framework.\n     *\n     * @param globalTransport\n     * @see Logger.addGlobalTransport\n     */\n    addGlobalLogTransport(globalTransport) {\n        Logger.addGlobalTransport(globalTransport);\n    },\n\n    /**\n     * Removes global logging transport from the library logging framework.\n     *\n     * @param globalTransport\n     * @see Logger.removeGlobalTransport\n     */\n    removeGlobalLogTransport(globalTransport) {\n        Logger.removeGlobalTransport(globalTransport);\n    },\n\n    /**\n    * Sets global options which will be used by all loggers. Changing these\n    * works even after other loggers are created.\n    *\n    * @param options\n    * @see Logger.setGlobalOptions\n    */\n    setGlobalLogOptions(options) {\n        Logger.setGlobalOptions(options);\n    },\n\n    /**\n     * Creates the media tracks and returns them trough the callback.\n     *\n     * @param options Object with properties / settings specifying the tracks\n     * which should be created. should be created or some additional\n     * configurations about resolution for example.\n     * @param {Array} options.effects optional effects array for the track\n     * @param {boolean} options.firePermissionPromptIsShownEvent - if event\n     * JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN should be fired\n     * @param {boolean} options.fireSlowPromiseEvent - if event\n     * JitsiMediaDevicesEvents.USER_MEDIA_SLOW_PROMISE_TIMEOUT should be fired\n     * @param {Array} options.devices the devices that will be requested\n     * @param {string} options.resolution resolution constraints\n     * @param {string} options.cameraDeviceId\n     * @param {string} options.micDeviceId\n     * @param {intiger} interval - the interval (in ms) for\n     * checking whether the desktop sharing extension is installed or not\n     * @param {Function} checkAgain - returns boolean. While checkAgain()==true\n     * createLocalTracks will wait and check on every \"interval\" ms for the\n     * extension. If the desktop extension is not install and checkAgain()==true\n     * createLocalTracks will finish with rejected Promise.\n     * @param {Function} listener - The listener will be called to notify the\n     * user of lib-jitsi-meet that createLocalTracks is starting external\n     * extension installation process.\n     * NOTE: If the inline installation process is not possible and external\n     * installation is enabled the listener property will be called to notify\n     * the start of external installation process. After that createLocalTracks\n     * will start to check for the extension on every interval ms until the\n     * plugin is installed or until checkAgain return false. If the extension\n     * is found createLocalTracks will try to get the desktop sharing track and\n     * will finish the execution. If checkAgain returns false, createLocalTracks\n     * will finish the execution with rejected Promise.\n     *\n     * @deprecated old firePermissionPromptIsShownEvent\n     * @returns {Promise.<{Array.<JitsiTrack>}, JitsiConferenceError>} A promise\n     * that returns an array of created JitsiTracks if resolved, or a\n     * JitsiConferenceError if rejected.\n     */\n    createLocalTracks(options = {}, oldfirePermissionPromptIsShownEvent) {\n        let promiseFulfilled = false;\n\n        const { firePermissionPromptIsShownEvent, fireSlowPromiseEvent, ...restOptions } = options;\n        const firePermissionPrompt = firePermissionPromptIsShownEvent || oldfirePermissionPromptIsShownEvent;\n\n        if (firePermissionPrompt && !RTC.arePermissionsGrantedForAvailableDevices()) {\n            JitsiMediaDevices.emitEvent(\n                JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,\n                browser.getName());\n        } else if (fireSlowPromiseEvent) {\n            window.setTimeout(() => {\n                if (!promiseFulfilled) {\n                    JitsiMediaDevices.emitEvent(JitsiMediaDevicesEvents.SLOW_GET_USER_MEDIA);\n                }\n            }, USER_MEDIA_SLOW_PROMISE_TIMEOUT);\n        }\n\n        if (!window.connectionTimes) {\n            window.connectionTimes = {};\n        }\n        window.connectionTimes['obtainPermissions.start']\n            = window.performance.now();\n\n        return RTC.obtainAudioAndVideoPermissions(restOptions)\n            .then(tracks => {\n                promiseFulfilled = true;\n\n                window.connectionTimes['obtainPermissions.end']\n                    = window.performance.now();\n\n                Statistics.sendAnalytics(\n                    createGetUserMediaEvent(\n                        'success',\n                        getAnalyticsAttributesFromOptions(restOptions)));\n\n                if (!RTC.options.disableAudioLevels) {\n                    for (let i = 0; i < tracks.length; i++) {\n                        const track = tracks[i];\n                        const mStream = track.getOriginalStream();\n\n                        if (track.getType() === MediaType.AUDIO) {\n                            Statistics.startLocalStats(mStream,\n                                track.setAudioLevel.bind(track));\n                            track.addEventListener(\n                                JitsiTrackEvents.LOCAL_TRACK_STOPPED,\n                                () => {\n                                    Statistics.stopLocalStats(mStream);\n                                });\n                        }\n                    }\n                }\n\n                // set real device ids\n                const currentlyAvailableMediaDevices\n                    = RTC.getCurrentlyAvailableMediaDevices();\n\n                if (currentlyAvailableMediaDevices) {\n                    for (let i = 0; i < tracks.length; i++) {\n                        const track = tracks[i];\n\n                        track._setRealDeviceIdFromDeviceList(\n                            currentlyAvailableMediaDevices);\n                    }\n                }\n\n                // set the contentHint to \"detail\" for desktop tracks\n                // eslint-disable-next-line prefer-const\n                for (const track of tracks) {\n                    if (track.type === MediaType.VIDEO\n                        && track.videoType === 'desktop') {\n                        this.setVideoTrackContentHints(track.track, 'detail');\n                    }\n                }\n\n                return tracks;\n            })\n            .catch(error => {\n                promiseFulfilled = true;\n\n                if (error.name === JitsiTrackErrors.SCREENSHARING_USER_CANCELED) {\n                    // User cancelled action is not really an error, so only\n                    // log it as an event to avoid having conference classified\n                    // as partially failed\n                    const logObject = {\n                        id: 'screensharing_user_canceled',\n                        message: error.message\n                    };\n\n                    Statistics.sendLog(JSON.stringify(logObject));\n\n                    Statistics.sendAnalytics(\n                        createGetUserMediaEvent(\n                            'warning',\n                            {\n                                reason: 'extension install user canceled'\n                            }));\n                } else if (error.name === JitsiTrackErrors.NOT_FOUND) {\n                    // logs not found devices with just application log to cs\n                    const logObject = {\n                        id: 'usermedia_missing_device',\n                        status: error.gum.devices\n                    };\n\n                    Statistics.sendLog(JSON.stringify(logObject));\n\n                    const attributes\n                        = getAnalyticsAttributesFromOptions(options);\n\n                    attributes.reason = 'device not found';\n                    attributes.devices = error.gum.devices.join('.');\n                    Statistics.sendAnalytics(\n                        createGetUserMediaEvent('error', attributes));\n                } else {\n                    // Report gUM failed to the stats\n                    Statistics.sendGetUserMediaFailed(error);\n\n                    const attributes\n                        = getAnalyticsAttributesFromOptions(options);\n\n                    attributes.reason = error.name;\n                    Statistics.sendAnalytics(\n                        createGetUserMediaEvent('error', attributes));\n                }\n\n                window.connectionTimes['obtainPermissions.end']\n                    = window.performance.now();\n\n                return Promise.reject(error);\n            });\n    },\n\n    /**\n     * Create a TrackVADEmitter service that connects an audio track to an VAD (voice activity detection) processor in\n     * order to obtain VAD scores for individual PCM audio samples.\n     * @param {string} localAudioDeviceId - The target local audio device.\n     * @param {number} sampleRate - Sample rate at which the emitter will operate. Possible values  256, 512, 1024,\n     * 4096, 8192, 16384. Passing other values will default to closes neighbor.\n     * I.e. Providing a value of 4096 means that the emitter will process 4096 PCM samples at a time, higher values mean\n     * longer calls, lowers values mean more calls but shorter.\n     * @param {Object} vadProcessor - VAD Processors that does the actual compute on a PCM sample.The processor needs\n     * to implement the following functions:\n     * - <tt>getSampleLength()</tt> - Returns the sample size accepted by calculateAudioFrameVAD.\n     * - <tt>getRequiredPCMFrequency()</tt> - Returns the PCM frequency at which the processor operates.\n     * i.e. (16KHz, 44.1 KHz etc.)\n     * - <tt>calculateAudioFrameVAD(pcmSample)</tt> - Process a 32 float pcm sample of getSampleLength size.\n     * @returns {Promise<TrackVADEmitter>}\n     */\n    createTrackVADEmitter(localAudioDeviceId, sampleRate, vadProcessor) {\n        return TrackVADEmitter.create(localAudioDeviceId, sampleRate, vadProcessor);\n    },\n\n    /**\n     * Create AudioMixer, which is essentially a wrapper over web audio ChannelMergerNode. It essentially allows the\n     * user to mix multiple MediaStreams into a single one.\n     *\n     * @returns {AudioMixer}\n     */\n    createAudioMixer() {\n        return new AudioMixer();\n    },\n\n    /**\n     * Go through all audio devices on the system and return one that is active, i.e. has audio signal.\n     *\n     * @returns Promise<Object> - Object containing information about the found device.\n     */\n    getActiveAudioDevice() {\n        return getActiveAudioDevice();\n    },\n\n    /**\n     * Checks if its possible to enumerate available cameras/microphones.\n     *\n     * @returns {Promise<boolean>} a Promise which will be resolved only once\n     * the WebRTC stack is ready, either with true if the device listing is\n     * available available or with false otherwise.\n     * @deprecated use JitsiMeetJS.mediaDevices.isDeviceListAvailable instead\n     */\n    isDeviceListAvailable() {\n        logger.warn('This method is deprecated, use '\n            + 'JitsiMeetJS.mediaDevices.isDeviceListAvailable instead');\n\n        return this.mediaDevices.isDeviceListAvailable();\n    },\n\n    /**\n     * Returns true if changing the input (camera / microphone) or output\n     * (audio) device is supported and false if not.\n     *\n     * @param {string} [deviceType] - type of device to change. Default is\n     * {@code undefined} or 'input', 'output' - for audio output device change.\n     * @returns {boolean} {@code true} if available; {@code false}, otherwise.\n     * @deprecated use JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead\n     */\n    isDeviceChangeAvailable(deviceType) {\n        logger.warn('This method is deprecated, use '\n            + 'JitsiMeetJS.mediaDevices.isDeviceChangeAvailable instead');\n\n        return this.mediaDevices.isDeviceChangeAvailable(deviceType);\n    },\n\n\n    /**\n     * Checks if the current environment supports having multiple audio\n     * input devices in use simultaneously.\n     *\n     * @returns {boolean} True if multiple audio input devices can be used.\n     */\n    isMultipleAudioInputSupported() {\n        return this.mediaDevices.isMultipleAudioInputSupported();\n    },\n\n    /**\n     * Checks if local tracks can collect stats and collection is enabled.\n     *\n     * @param {boolean} True if stats are being collected for local tracks.\n     */\n    isCollectingLocalStats() {\n        return Statistics.audioLevelsEnabled\n            && LocalStatsCollector.isLocalStatsSupported();\n    },\n\n    /**\n     * Executes callback with list of media devices connected.\n     *\n     * @param {function} callback\n     * @deprecated use JitsiMeetJS.mediaDevices.enumerateDevices instead\n     */\n    enumerateDevices(callback) {\n        logger.warn('This method is deprecated, use '\n            + 'JitsiMeetJS.mediaDevices.enumerateDevices instead');\n        this.mediaDevices.enumerateDevices(callback);\n    },\n\n    /* eslint-disable max-params */\n\n    /**\n     * @returns function that can be used to be attached to window.onerror and\n     * if options.enableWindowOnErrorHandler is enabled returns\n     * the function used by the lib.\n     * (function(message, source, lineno, colno, error)).\n     */\n    getGlobalOnErrorHandler(message, source, lineno, colno, error) {\n        logger.error(\n            `UnhandledError: ${message}`,\n            `Script: ${source}`,\n            `Line: ${lineno}`,\n            `Column: ${colno}`,\n            'StackTrace: ', error);\n        Statistics.reportGlobalError(error);\n    },\n\n    /**\n     * Informs lib-jitsi-meet about the current network status.\n     *\n     * @param {boolean} isOnline - {@code true} if the internet connectivity is online or {@code false}\n     * otherwise.\n     */\n    setNetworkInfo({ isOnline }) {\n        NetworkInfo.updateNetworkInfo({ isOnline });\n    },\n\n    /**\n     * Set the contentHint on the transmitted stream track to indicate\n     * charaterstics in the video stream, which informs PeerConnection\n     * on how to encode the track (to prefer motion or individual frame detail)\n     * @param {MediaStreamTrack} track - the track that is transmitted\n     * @param {String} hint - contentHint value that needs to be set on the track\n     */\n    setVideoTrackContentHints(track, hint) {\n        if ('contentHint' in track) {\n            track.contentHint = hint;\n            if (track.contentHint !== hint) {\n                logger.debug('Invalid video track contentHint');\n            }\n        } else {\n            logger.debug('MediaStreamTrack contentHint attribute not supported');\n        }\n    },\n\n    precallTest,\n\n    /* eslint-enable max-params */\n\n    /**\n     * Represents a hub/namespace for utility functionality which may be of\n     * interest to lib-jitsi-meet clients.\n     */\n    util: {\n        AuthUtil,\n        ScriptUtil,\n        browser\n    }\n});\n","export default {\n    error: {\n        BUSY: 'busy',\n        ERROR: 'error',\n        RESOURCE_CONSTRAINT: 'resource-constraint',\n        UNEXPECTED_REQUEST: 'unexpected-request',\n        SERVICE_UNAVAILABLE: 'service-unavailable'\n    },\n    mode: {\n        FILE: 'file',\n        STREAM: 'stream'\n    },\n    status: {\n        OFF: 'off',\n        ON: 'on',\n        PENDING: 'pending'\n    }\n};\n","(function (global, factory) {\n            typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :\n            typeof define === 'function' && define.amd ? define(factory) :\n            (global = global || self, global.strophe = factory());\n}(this, function () { 'use strict';\n\n            var global$1 = (typeof global !== \"undefined\" ? global :\n                        typeof self !== \"undefined\" ? self :\n                        typeof window !== \"undefined\" ? window : {});\n\n            function _typeof(obj) {\n              if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n                _typeof = function (obj) {\n                  return typeof obj;\n                };\n              } else {\n                _typeof = function (obj) {\n                  return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n                };\n              }\n\n              return _typeof(obj);\n            }\n\n            function _toConsumableArray(arr) {\n              return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();\n            }\n\n            function _arrayWithoutHoles(arr) {\n              if (Array.isArray(arr)) {\n                for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n\n                return arr2;\n              }\n            }\n\n            function _iterableToArray(iter) {\n              if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === \"[object Arguments]\") return Array.from(iter);\n            }\n\n            function _nonIterableSpread() {\n              throw new TypeError(\"Invalid attempt to spread non-iterable instance\");\n            }\n\n            /*\n             * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n             * Digest Algorithm, as defined in RFC 1321.\n             * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n             * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n             * Distributed under the BSD License\n             * See http://pajhome.org.uk/crypt/md5 for more info.\n             */\n\n            /*\n             * Everything that isn't used by Strophe has been stripped here!\n             */\n\n            /*\n             * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n             * to work around bugs in some JS interpreters.\n             */\n            var safe_add = function safe_add(x, y) {\n              var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n              var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n              return msw << 16 | lsw & 0xFFFF;\n            };\n            /*\n             * Bitwise rotate a 32-bit number to the left.\n             */\n\n\n            var bit_rol = function bit_rol(num, cnt) {\n              return num << cnt | num >>> 32 - cnt;\n            };\n            /*\n             * Convert a string to an array of little-endian words\n             */\n\n\n            var str2binl = function str2binl(str) {\n              if (typeof str !== \"string\") {\n                throw new Error(\"str2binl was passed a non-string\");\n              }\n\n              var bin = [];\n\n              for (var i = 0; i < str.length * 8; i += 8) {\n                bin[i >> 5] |= (str.charCodeAt(i / 8) & 255) << i % 32;\n              }\n\n              return bin;\n            };\n            /*\n             * Convert an array of little-endian words to a string\n             */\n\n\n            var binl2str = function binl2str(bin) {\n              var str = \"\";\n\n              for (var i = 0; i < bin.length * 32; i += 8) {\n                str += String.fromCharCode(bin[i >> 5] >>> i % 32 & 255);\n              }\n\n              return str;\n            };\n            /*\n             * Convert an array of little-endian words to a hex string.\n             */\n\n\n            var binl2hex = function binl2hex(binarray) {\n              var hex_tab = \"0123456789abcdef\";\n              var str = \"\";\n\n              for (var i = 0; i < binarray.length * 4; i++) {\n                str += hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 + 4 & 0xF) + hex_tab.charAt(binarray[i >> 2] >> i % 4 * 8 & 0xF);\n              }\n\n              return str;\n            };\n            /*\n             * These functions implement the four basic operations the algorithm uses.\n             */\n\n\n            var md5_cmn = function md5_cmn(q, a, b, x, s, t) {\n              return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);\n            };\n\n            var md5_ff = function md5_ff(a, b, c, d, x, s, t) {\n              return md5_cmn(b & c | ~b & d, a, b, x, s, t);\n            };\n\n            var md5_gg = function md5_gg(a, b, c, d, x, s, t) {\n              return md5_cmn(b & d | c & ~d, a, b, x, s, t);\n            };\n\n            var md5_hh = function md5_hh(a, b, c, d, x, s, t) {\n              return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n            };\n\n            var md5_ii = function md5_ii(a, b, c, d, x, s, t) {\n              return md5_cmn(c ^ (b | ~d), a, b, x, s, t);\n            };\n            /*\n             * Calculate the MD5 of an array of little-endian words, and a bit length\n             */\n\n\n            var core_md5 = function core_md5(x, len) {\n              /* append padding */\n              x[len >> 5] |= 0x80 << len % 32;\n              x[(len + 64 >>> 9 << 4) + 14] = len;\n              var a = 1732584193;\n              var b = -271733879;\n              var c = -1732584194;\n              var d = 271733878;\n              var olda, oldb, oldc, oldd;\n\n              for (var i = 0; i < x.length; i += 16) {\n                olda = a;\n                oldb = b;\n                oldc = c;\n                oldd = d;\n                a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);\n                d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);\n                c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);\n                b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);\n                a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);\n                d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);\n                c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);\n                b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);\n                a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);\n                d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);\n                c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);\n                b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);\n                a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);\n                d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);\n                c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);\n                b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);\n                a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);\n                d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);\n                c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);\n                b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);\n                a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);\n                d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);\n                c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);\n                b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);\n                a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);\n                d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);\n                c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);\n                b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);\n                a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);\n                d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);\n                c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);\n                b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);\n                a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);\n                d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);\n                c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);\n                b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);\n                a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);\n                d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);\n                c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);\n                b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);\n                a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);\n                d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);\n                c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);\n                b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);\n                a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);\n                d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);\n                c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);\n                b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);\n                a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);\n                d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);\n                c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);\n                b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);\n                a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);\n                d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);\n                c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);\n                b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);\n                a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);\n                d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);\n                c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);\n                b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);\n                a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);\n                d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);\n                c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);\n                b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);\n                a = safe_add(a, olda);\n                b = safe_add(b, oldb);\n                c = safe_add(c, oldc);\n                d = safe_add(d, oldd);\n              }\n\n              return [a, b, c, d];\n            };\n            /*\n             * These are the functions you'll usually want to call.\n             * They take string arguments and return either hex or base-64 encoded\n             * strings.\n             */\n\n\n            var MD5 = {\n              hexdigest: function hexdigest(s) {\n                return binl2hex(core_md5(str2binl(s), s.length * 8));\n              },\n              hash: function hash(s) {\n                return binl2str(core_md5(str2binl(s), s.length * 8));\n              }\n            };\n\n            /*\n             * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined\n             * in FIPS PUB 180-1\n             * Version 2.1a Copyright Paul Johnston 2000 - 2002.\n             * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n             * Distributed under the BSD License\n             * See http://pajhome.org.uk/crypt/md5 for details.\n             */\n\n            /* global define */\n\n            /* Some functions and variables have been stripped for use with Strophe */\n\n            /*\n             * Calculate the SHA-1 of an array of big-endian words, and a bit length\n             */\n            function core_sha1(x, len) {\n              /* append padding */\n              x[len >> 5] |= 0x80 << 24 - len % 32;\n              x[(len + 64 >> 9 << 4) + 15] = len;\n              var w = new Array(80);\n              var a = 1732584193;\n              var b = -271733879;\n              var c = -1732584194;\n              var d = 271733878;\n              var e = -1009589776;\n              var i, j, t, olda, oldb, oldc, oldd, olde;\n\n              for (i = 0; i < x.length; i += 16) {\n                olda = a;\n                oldb = b;\n                oldc = c;\n                oldd = d;\n                olde = e;\n\n                for (j = 0; j < 80; j++) {\n                  if (j < 16) {\n                    w[j] = x[i + j];\n                  } else {\n                    w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);\n                  }\n\n                  t = safe_add$1(safe_add$1(rol(a, 5), sha1_ft(j, b, c, d)), safe_add$1(safe_add$1(e, w[j]), sha1_kt(j)));\n                  e = d;\n                  d = c;\n                  c = rol(b, 30);\n                  b = a;\n                  a = t;\n                }\n\n                a = safe_add$1(a, olda);\n                b = safe_add$1(b, oldb);\n                c = safe_add$1(c, oldc);\n                d = safe_add$1(d, oldd);\n                e = safe_add$1(e, olde);\n              }\n\n              return [a, b, c, d, e];\n            }\n            /*\n             * Perform the appropriate triplet combination function for the current\n             * iteration\n             */\n\n\n            function sha1_ft(t, b, c, d) {\n              if (t < 20) {\n                return b & c | ~b & d;\n              }\n\n              if (t < 40) {\n                return b ^ c ^ d;\n              }\n\n              if (t < 60) {\n                return b & c | b & d | c & d;\n              }\n\n              return b ^ c ^ d;\n            }\n            /*\n             * Determine the appropriate additive constant for the current iteration\n             */\n\n\n            function sha1_kt(t) {\n              return t < 20 ? 1518500249 : t < 40 ? 1859775393 : t < 60 ? -1894007588 : -899497514;\n            }\n            /*\n             * Calculate the HMAC-SHA1 of a key and some data\n             */\n\n\n            function core_hmac_sha1(key, data) {\n              var bkey = str2binb(key);\n\n              if (bkey.length > 16) {\n                bkey = core_sha1(bkey, key.length * 8);\n              }\n\n              var ipad = new Array(16),\n                  opad = new Array(16);\n\n              for (var i = 0; i < 16; i++) {\n                ipad[i] = bkey[i] ^ 0x36363636;\n                opad[i] = bkey[i] ^ 0x5C5C5C5C;\n              }\n\n              var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);\n              return core_sha1(opad.concat(hash), 512 + 160);\n            }\n            /*\n             * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n             * to work around bugs in some JS interpreters.\n             */\n\n\n            function safe_add$1(x, y) {\n              var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n              var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n              return msw << 16 | lsw & 0xFFFF;\n            }\n            /*\n             * Bitwise rotate a 32-bit number to the left.\n             */\n\n\n            function rol(num, cnt) {\n              return num << cnt | num >>> 32 - cnt;\n            }\n            /*\n             * Convert an 8-bit or 16-bit string to an array of big-endian words\n             * In 8-bit function, characters >255 have their hi-byte silently ignored.\n             */\n\n\n            function str2binb(str) {\n              var bin = [];\n              var mask = 255;\n\n              for (var i = 0; i < str.length * 8; i += 8) {\n                bin[i >> 5] |= (str.charCodeAt(i / 8) & mask) << 24 - i % 32;\n              }\n\n              return bin;\n            }\n            /*\n             * Convert an array of big-endian words to a base-64 string\n             */\n\n\n            function binb2b64(binarray) {\n              var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n              var str = \"\";\n              var triplet, j;\n\n              for (var i = 0; i < binarray.length * 4; i += 3) {\n                triplet = (binarray[i >> 2] >> 8 * (3 - i % 4) & 0xFF) << 16 | (binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4) & 0xFF) << 8 | binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4) & 0xFF;\n\n                for (j = 0; j < 4; j++) {\n                  if (i * 8 + j * 6 > binarray.length * 32) {\n                    str += \"=\";\n                  } else {\n                    str += tab.charAt(triplet >> 6 * (3 - j) & 0x3F);\n                  }\n                }\n              }\n\n              return str;\n            }\n            /*\n             * Convert an array of big-endian words to a string\n             */\n\n\n            function binb2str(bin) {\n              var str = \"\";\n              var mask = 255;\n\n              for (var i = 0; i < bin.length * 32; i += 8) {\n                str += String.fromCharCode(bin[i >> 5] >>> 24 - i % 32 & mask);\n              }\n\n              return str;\n            }\n            /*\n             * These are the functions you'll usually want to call\n             * They take string arguments and return either hex or base-64 encoded strings\n             */\n\n\n            var SHA1 = {\n              b64_hmac_sha1: function b64_hmac_sha1(key, data) {\n                return binb2b64(core_hmac_sha1(key, data));\n              },\n              b64_sha1: function b64_sha1(s) {\n                return binb2b64(core_sha1(str2binb(s), s.length * 8));\n              },\n              binb2str: binb2str,\n              core_hmac_sha1: core_hmac_sha1,\n              str_hmac_sha1: function str_hmac_sha1(key, data) {\n                return binb2str(core_hmac_sha1(key, data));\n              },\n              str_sha1: function str_sha1(s) {\n                return binb2str(core_sha1(str2binb(s), s.length * 8));\n              }\n            };\n\n            var utils = {\n              utf16to8: function utf16to8(str) {\n                var i, c;\n                var out = \"\";\n                var len = str.length;\n\n                for (i = 0; i < len; i++) {\n                  c = str.charCodeAt(i);\n\n                  if (c >= 0x0000 && c <= 0x007F) {\n                    out += str.charAt(i);\n                  } else if (c > 0x07FF) {\n                    out += String.fromCharCode(0xE0 | c >> 12 & 0x0F);\n                    out += String.fromCharCode(0x80 | c >> 6 & 0x3F);\n                    out += String.fromCharCode(0x80 | c >> 0 & 0x3F);\n                  } else {\n                    out += String.fromCharCode(0xC0 | c >> 6 & 0x1F);\n                    out += String.fromCharCode(0x80 | c >> 0 & 0x3F);\n                  }\n                }\n\n                return out;\n              },\n              addCookies: function addCookies(cookies) {\n                /* Parameters:\n                 *  (Object) cookies - either a map of cookie names\n                 *    to string values or to maps of cookie values.\n                 *\n                 * For example:\n                 * { \"myCookie\": \"1234\" }\n                 *\n                 * or:\n                 * { \"myCookie\": {\n                 *      \"value\": \"1234\",\n                 *      \"domain\": \".example.org\",\n                 *      \"path\": \"/\",\n                 *      \"expires\": expirationDate\n                 *      }\n                 *  }\n                 *\n                 *  These values get passed to Strophe.Connection via\n                 *   options.cookies\n                 */\n                cookies = cookies || {};\n\n                for (var cookieName in cookies) {\n                  if (Object.prototype.hasOwnProperty.call(cookies, cookieName)) {\n                    var expires = '';\n                    var domain = '';\n                    var path = '';\n                    var cookieObj = cookies[cookieName];\n                    var isObj = _typeof(cookieObj) === \"object\";\n                    var cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj));\n\n                    if (isObj) {\n                      expires = cookieObj.expires ? \";expires=\" + cookieObj.expires : '';\n                      domain = cookieObj.domain ? \";domain=\" + cookieObj.domain : '';\n                      path = cookieObj.path ? \";path=\" + cookieObj.path : '';\n                    }\n\n                    document.cookie = cookieName + '=' + cookieValue + expires + domain + path;\n                  }\n                }\n              }\n            };\n\n            /** Function: $build\n             *  Create a Strophe.Builder.\n             *  This is an alias for 'new Strophe.Builder(name, attrs)'.\n             *\n             *  Parameters:\n             *    (String) name - The root element name.\n             *    (Object) attrs - The attributes for the root element in object notation.\n             *\n             *  Returns:\n             *    A new Strophe.Builder object.\n             */\n\n            function $build(name, attrs) {\n              return new Strophe.Builder(name, attrs);\n            }\n            /** Function: $msg\n             *  Create a Strophe.Builder with a <message/> element as the root.\n             *\n             *  Parameters:\n             *    (Object) attrs - The <message/> element attributes in object notation.\n             *\n             *  Returns:\n             *    A new Strophe.Builder object.\n             */\n\n\n            function $msg(attrs) {\n              return new Strophe.Builder(\"message\", attrs);\n            }\n            /** Function: $iq\n             *  Create a Strophe.Builder with an <iq/> element as the root.\n             *\n             *  Parameters:\n             *    (Object) attrs - The <iq/> element attributes in object notation.\n             *\n             *  Returns:\n             *    A new Strophe.Builder object.\n             */\n\n\n            function $iq(attrs) {\n              return new Strophe.Builder(\"iq\", attrs);\n            }\n            /** Function: $pres\n             *  Create a Strophe.Builder with a <presence/> element as the root.\n             *\n             *  Parameters:\n             *    (Object) attrs - The <presence/> element attributes in object notation.\n             *\n             *  Returns:\n             *    A new Strophe.Builder object.\n             */\n\n\n            function $pres(attrs) {\n              return new Strophe.Builder(\"presence\", attrs);\n            }\n            /** Class: Strophe\n             *  An object container for all Strophe library functions.\n             *\n             *  This class is just a container for all the objects and constants\n             *  used in the library.  It is not meant to be instantiated, but to\n             *  provide a namespace for library objects, constants, and functions.\n             */\n\n\n            var Strophe = {\n              /** Constant: VERSION */\n              VERSION: \"@VERSION@\",\n\n              /** Constants: XMPP Namespace Constants\n               *  Common namespace constants from the XMPP RFCs and XEPs.\n               *\n               *  NS.HTTPBIND - HTTP BIND namespace from XEP 124.\n               *  NS.BOSH - BOSH namespace from XEP 206.\n               *  NS.CLIENT - Main XMPP client namespace.\n               *  NS.AUTH - Legacy authentication namespace.\n               *  NS.ROSTER - Roster operations namespace.\n               *  NS.PROFILE - Profile namespace.\n               *  NS.DISCO_INFO - Service discovery info namespace from XEP 30.\n               *  NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.\n               *  NS.MUC - Multi-User Chat namespace from XEP 45.\n               *  NS.SASL - XMPP SASL namespace from RFC 3920.\n               *  NS.STREAM - XMPP Streams namespace from RFC 3920.\n               *  NS.BIND - XMPP Binding namespace from RFC 3920 and RFC 6120.\n               *  NS.SESSION - XMPP Session namespace from RFC 3920.\n               *  NS.XHTML_IM - XHTML-IM namespace from XEP 71.\n               *  NS.XHTML - XHTML body namespace from XEP 71.\n               */\n              NS: {\n                HTTPBIND: \"http://jabber.org/protocol/httpbind\",\n                BOSH: \"urn:xmpp:xbosh\",\n                CLIENT: \"jabber:client\",\n                AUTH: \"jabber:iq:auth\",\n                ROSTER: \"jabber:iq:roster\",\n                PROFILE: \"jabber:iq:profile\",\n                DISCO_INFO: \"http://jabber.org/protocol/disco#info\",\n                DISCO_ITEMS: \"http://jabber.org/protocol/disco#items\",\n                MUC: \"http://jabber.org/protocol/muc\",\n                SASL: \"urn:ietf:params:xml:ns:xmpp-sasl\",\n                STREAM: \"http://etherx.jabber.org/streams\",\n                FRAMING: \"urn:ietf:params:xml:ns:xmpp-framing\",\n                BIND: \"urn:ietf:params:xml:ns:xmpp-bind\",\n                SESSION: \"urn:ietf:params:xml:ns:xmpp-session\",\n                VERSION: \"jabber:iq:version\",\n                STANZAS: \"urn:ietf:params:xml:ns:xmpp-stanzas\",\n                XHTML_IM: \"http://jabber.org/protocol/xhtml-im\",\n                XHTML: \"http://www.w3.org/1999/xhtml\"\n              },\n\n              /** Constants: XHTML_IM Namespace\n               *  contains allowed tags, tag attributes, and css properties.\n               *  Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.\n               *  See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended\n               *  allowed tags and their attributes.\n               */\n              XHTML: {\n                tags: ['a', 'blockquote', 'br', 'cite', 'em', 'img', 'li', 'ol', 'p', 'span', 'strong', 'ul', 'body'],\n                attributes: {\n                  'a': ['href'],\n                  'blockquote': ['style'],\n                  'br': [],\n                  'cite': ['style'],\n                  'em': [],\n                  'img': ['src', 'alt', 'style', 'height', 'width'],\n                  'li': ['style'],\n                  'ol': ['style'],\n                  'p': ['style'],\n                  'span': ['style'],\n                  'strong': [],\n                  'ul': ['style'],\n                  'body': []\n                },\n                css: ['background-color', 'color', 'font-family', 'font-size', 'font-style', 'font-weight', 'margin-left', 'margin-right', 'text-align', 'text-decoration'],\n\n                /** Function: XHTML.validTag\n                 *\n                 * Utility method to determine whether a tag is allowed\n                 * in the XHTML_IM namespace.\n                 *\n                 * XHTML tag names are case sensitive and must be lower case.\n                 */\n                validTag: function validTag(tag) {\n                  for (var i = 0; i < Strophe.XHTML.tags.length; i++) {\n                    if (tag === Strophe.XHTML.tags[i]) {\n                      return true;\n                    }\n                  }\n\n                  return false;\n                },\n\n                /** Function: XHTML.validAttribute\n                 *\n                 * Utility method to determine whether an attribute is allowed\n                 * as recommended per XEP-0071\n                 *\n                 * XHTML attribute names are case sensitive and must be lower case.\n                 */\n                validAttribute: function validAttribute(tag, attribute) {\n                  if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {\n                    for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {\n                      if (attribute === Strophe.XHTML.attributes[tag][i]) {\n                        return true;\n                      }\n                    }\n                  }\n\n                  return false;\n                },\n                validCSS: function validCSS(style) {\n                  for (var i = 0; i < Strophe.XHTML.css.length; i++) {\n                    if (style === Strophe.XHTML.css[i]) {\n                      return true;\n                    }\n                  }\n\n                  return false;\n                }\n              },\n\n              /** Constants: Connection Status Constants\n               *  Connection status constants for use by the connection handler\n               *  callback.\n               *\n               *  Status.ERROR - An error has occurred\n               *  Status.CONNECTING - The connection is currently being made\n               *  Status.CONNFAIL - The connection attempt failed\n               *  Status.AUTHENTICATING - The connection is authenticating\n               *  Status.AUTHFAIL - The authentication attempt failed\n               *  Status.CONNECTED - The connection has succeeded\n               *  Status.DISCONNECTED - The connection has been terminated\n               *  Status.DISCONNECTING - The connection is currently being terminated\n               *  Status.ATTACHED - The connection has been attached\n               *  Status.REDIRECT - The connection has been redirected\n               *  Status.CONNTIMEOUT - The connection has timed out\n               */\n              Status: {\n                ERROR: 0,\n                CONNECTING: 1,\n                CONNFAIL: 2,\n                AUTHENTICATING: 3,\n                AUTHFAIL: 4,\n                CONNECTED: 5,\n                DISCONNECTED: 6,\n                DISCONNECTING: 7,\n                ATTACHED: 8,\n                REDIRECT: 9,\n                CONNTIMEOUT: 10,\n                BINDREQUIRED: 11\n              },\n              ErrorCondition: {\n                BAD_FORMAT: \"bad-format\",\n                CONFLICT: \"conflict\",\n                MISSING_JID_NODE: \"x-strophe-bad-non-anon-jid\",\n                NO_AUTH_MECH: \"no-auth-mech\",\n                UNKNOWN_REASON: \"unknown\"\n              },\n\n              /** Constants: Log Level Constants\n               *  Logging level indicators.\n               *\n               *  LogLevel.DEBUG - Debug output\n               *  LogLevel.INFO - Informational output\n               *  LogLevel.WARN - Warnings\n               *  LogLevel.ERROR - Errors\n               *  LogLevel.FATAL - Fatal errors\n               */\n              LogLevel: {\n                DEBUG: 0,\n                INFO: 1,\n                WARN: 2,\n                ERROR: 3,\n                FATAL: 4\n              },\n\n              /** PrivateConstants: DOM Element Type Constants\n               *  DOM element types.\n               *\n               *  ElementType.NORMAL - Normal element.\n               *  ElementType.TEXT - Text data element.\n               *  ElementType.FRAGMENT - XHTML fragment element.\n               */\n              ElementType: {\n                NORMAL: 1,\n                TEXT: 3,\n                CDATA: 4,\n                FRAGMENT: 11\n              },\n\n              /** PrivateConstants: Timeout Values\n               *  Timeout values for error states.  These values are in seconds.\n               *  These should not be changed unless you know exactly what you are\n               *  doing.\n               *\n               *  TIMEOUT - Timeout multiplier. A waiting request will be considered\n               *      failed after Math.floor(TIMEOUT * wait) seconds have elapsed.\n               *      This defaults to 1.1, and with default wait, 66 seconds.\n               *  SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where\n               *      Strophe can detect early failure, it will consider the request\n               *      failed if it doesn't return after\n               *      Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.\n               *      This defaults to 0.1, and with default wait, 6 seconds.\n               */\n              TIMEOUT: 1.1,\n              SECONDARY_TIMEOUT: 0.1,\n\n              /** Function: addNamespace\n               *  This function is used to extend the current namespaces in\n               *  Strophe.NS.  It takes a key and a value with the key being the\n               *  name of the new namespace, with its actual value.\n               *  For example:\n               *  Strophe.addNamespace('PUBSUB', \"http://jabber.org/protocol/pubsub\");\n               *\n               *  Parameters:\n               *    (String) name - The name under which the namespace will be\n               *      referenced under Strophe.NS\n               *    (String) value - The actual namespace.\n               */\n              addNamespace: function addNamespace(name, value) {\n                Strophe.NS[name] = value;\n              },\n\n              /** Function: forEachChild\n               *  Map a function over some or all child elements of a given element.\n               *\n               *  This is a small convenience function for mapping a function over\n               *  some or all of the children of an element.  If elemName is null, all\n               *  children will be passed to the function, otherwise only children\n               *  whose tag names match elemName will be passed.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The element to operate on.\n               *    (String) elemName - The child element tag name filter.\n               *    (Function) func - The function to apply to each child.  This\n               *      function should take a single argument, a DOM element.\n               */\n              forEachChild: function forEachChild(elem, elemName, func) {\n                for (var i = 0; i < elem.childNodes.length; i++) {\n                  var childNode = elem.childNodes[i];\n\n                  if (childNode.nodeType === Strophe.ElementType.NORMAL && (!elemName || this.isTagEqual(childNode, elemName))) {\n                    func(childNode);\n                  }\n                }\n              },\n\n              /** Function: isTagEqual\n               *  Compare an element's tag name with a string.\n               *\n               *  This function is case sensitive.\n               *\n               *  Parameters:\n               *    (XMLElement) el - A DOM element.\n               *    (String) name - The element name.\n               *\n               *  Returns:\n               *    true if the element's tag name matches _el_, and false\n               *    otherwise.\n               */\n              isTagEqual: function isTagEqual(el, name) {\n                return el.tagName === name;\n              },\n\n              /** PrivateVariable: _xmlGenerator\n               *  _Private_ variable that caches a DOM document to\n               *  generate elements.\n               */\n              _xmlGenerator: null,\n\n              /** PrivateFunction: _makeGenerator\n               *  _Private_ function that creates a dummy XML DOM document to serve as\n               *  an element and text node generator.\n               */\n              _makeGenerator: function _makeGenerator() {\n                var doc; // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.\n                // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be\n                // less than 10 in the case of IE9 and below.\n\n                if (document.implementation.createDocument === undefined || document.implementation.createDocument && document.documentMode && document.documentMode < 10) {\n                  doc = this._getIEXmlDom();\n                  doc.appendChild(doc.createElement('strophe'));\n                } else {\n                  doc = document.implementation.createDocument('jabber:client', 'strophe', null);\n                }\n\n                return doc;\n              },\n\n              /** Function: xmlGenerator\n               *  Get the DOM document to generate elements.\n               *\n               *  Returns:\n               *    The currently used DOM document.\n               */\n              xmlGenerator: function xmlGenerator() {\n                if (!Strophe._xmlGenerator) {\n                  Strophe._xmlGenerator = Strophe._makeGenerator();\n                }\n\n                return Strophe._xmlGenerator;\n              },\n\n              /** PrivateFunction: _getIEXmlDom\n               *  Gets IE xml doc object\n               *\n               *  Returns:\n               *    A Microsoft XML DOM Object\n               *  See Also:\n               *    http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx\n               */\n              _getIEXmlDom: function _getIEXmlDom() {\n                var doc = null;\n                var docStrings = [\"Msxml2.DOMDocument.6.0\", \"Msxml2.DOMDocument.5.0\", \"Msxml2.DOMDocument.4.0\", \"MSXML2.DOMDocument.3.0\", \"MSXML2.DOMDocument\", \"MSXML.DOMDocument\", \"Microsoft.XMLDOM\"];\n\n                for (var d = 0; d < docStrings.length; d++) {\n                  if (doc === null) {\n                    try {\n                      doc = new ActiveXObject(docStrings[d]);\n                    } catch (e) {\n                      doc = null;\n                    }\n                  } else {\n                    break;\n                  }\n                }\n\n                return doc;\n              },\n\n              /** Function: xmlElement\n               *  Create an XML DOM element.\n               *\n               *  This function creates an XML DOM element correctly across all\n               *  implementations. Note that these are not HTML DOM elements, which\n               *  aren't appropriate for XMPP stanzas.\n               *\n               *  Parameters:\n               *    (String) name - The name for the element.\n               *    (Array|Object) attrs - An optional array or object containing\n               *      key/value pairs to use as element attributes. The object should\n               *      be in the format {'key': 'value'} or {key: 'value'}. The array\n               *      should have the format [['key1', 'value1'], ['key2', 'value2']].\n               *    (String) text - The text child data for the element.\n               *\n               *  Returns:\n               *    A new XML DOM element.\n               */\n              xmlElement: function xmlElement(name) {\n                if (!name) {\n                  return null;\n                }\n\n                var node = Strophe.xmlGenerator().createElement(name); // FIXME: this should throw errors if args are the wrong type or\n                // there are more than two optional args\n\n                for (var a = 1; a < arguments.length; a++) {\n                  var arg = arguments[a];\n\n                  if (!arg) {\n                    continue;\n                  }\n\n                  if (typeof arg === \"string\" || typeof arg === \"number\") {\n                    node.appendChild(Strophe.xmlTextNode(arg));\n                  } else if (_typeof(arg) === \"object\" && typeof arg.sort === \"function\") {\n                    for (var i = 0; i < arg.length; i++) {\n                      var attr = arg[i];\n\n                      if (_typeof(attr) === \"object\" && typeof attr.sort === \"function\" && attr[1] !== undefined && attr[1] !== null) {\n                        node.setAttribute(attr[0], attr[1]);\n                      }\n                    }\n                  } else if (_typeof(arg) === \"object\") {\n                    for (var k in arg) {\n                      if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) {\n                        node.setAttribute(k, arg[k]);\n                      }\n                    }\n                  }\n                }\n\n                return node;\n              },\n\n              /*  Function: xmlescape\n               *  Excapes invalid xml characters.\n               *\n               *  Parameters:\n               *     (String) text - text to escape.\n               *\n               *  Returns:\n               *      Escaped text.\n               */\n              xmlescape: function xmlescape(text) {\n                text = text.replace(/\\&/g, \"&amp;\");\n                text = text.replace(/</g, \"&lt;\");\n                text = text.replace(/>/g, \"&gt;\");\n                text = text.replace(/'/g, \"&apos;\");\n                text = text.replace(/\"/g, \"&quot;\");\n                return text;\n              },\n\n              /*  Function: xmlunescape\n              *  Unexcapes invalid xml characters.\n              *\n              *  Parameters:\n              *     (String) text - text to unescape.\n              *\n              *  Returns:\n              *      Unescaped text.\n              */\n              xmlunescape: function xmlunescape(text) {\n                text = text.replace(/\\&amp;/g, \"&\");\n                text = text.replace(/&lt;/g, \"<\");\n                text = text.replace(/&gt;/g, \">\");\n                text = text.replace(/&apos;/g, \"'\");\n                text = text.replace(/&quot;/g, \"\\\"\");\n                return text;\n              },\n\n              /** Function: xmlTextNode\n               *  Creates an XML DOM text node.\n               *\n               *  Provides a cross implementation version of document.createTextNode.\n               *\n               *  Parameters:\n               *    (String) text - The content of the text node.\n               *\n               *  Returns:\n               *    A new XML DOM text node.\n               */\n              xmlTextNode: function xmlTextNode(text) {\n                return Strophe.xmlGenerator().createTextNode(text);\n              },\n\n              /** Function: xmlHtmlNode\n               *  Creates an XML DOM html node.\n               *\n               *  Parameters:\n               *    (String) html - The content of the html node.\n               *\n               *  Returns:\n               *    A new XML DOM text node.\n               */\n              xmlHtmlNode: function xmlHtmlNode(html) {\n                var node; //ensure text is escaped\n\n                if (DOMParser) {\n                  var parser = new DOMParser();\n                  node = parser.parseFromString(html, \"text/xml\");\n                } else {\n                  node = new ActiveXObject(\"Microsoft.XMLDOM\");\n                  node.async = \"false\";\n                  node.loadXML(html);\n                }\n\n                return node;\n              },\n\n              /** Function: getText\n               *  Get the concatenation of all text children of an element.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - A DOM element.\n               *\n               *  Returns:\n               *    A String with the concatenated text of all text element children.\n               */\n              getText: function getText(elem) {\n                if (!elem) {\n                  return null;\n                }\n\n                var str = \"\";\n\n                if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {\n                  str += elem.nodeValue;\n                }\n\n                for (var i = 0; i < elem.childNodes.length; i++) {\n                  if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {\n                    str += elem.childNodes[i].nodeValue;\n                  }\n                }\n\n                return Strophe.xmlescape(str);\n              },\n\n              /** Function: copyElement\n               *  Copy an XML DOM element.\n               *\n               *  This function copies a DOM element and all its descendants and returns\n               *  the new copy.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - A DOM element.\n               *\n               *  Returns:\n               *    A new, copied DOM element tree.\n               */\n              copyElement: function copyElement(elem) {\n                var el;\n\n                if (elem.nodeType === Strophe.ElementType.NORMAL) {\n                  el = Strophe.xmlElement(elem.tagName);\n\n                  for (var i = 0; i < elem.attributes.length; i++) {\n                    el.setAttribute(elem.attributes[i].nodeName, elem.attributes[i].value);\n                  }\n\n                  for (var _i = 0; _i < elem.childNodes.length; _i++) {\n                    el.appendChild(Strophe.copyElement(elem.childNodes[_i]));\n                  }\n                } else if (elem.nodeType === Strophe.ElementType.TEXT) {\n                  el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);\n                }\n\n                return el;\n              },\n\n              /** Function: createHtml\n               *  Copy an HTML DOM element into an XML DOM.\n               *\n               *  This function copies a DOM element and all its descendants and returns\n               *  the new copy.\n               *\n               *  Parameters:\n               *    (HTMLElement) elem - A DOM element.\n               *\n               *  Returns:\n               *    A new, copied DOM element tree.\n               */\n              createHtml: function createHtml(elem) {\n                var el;\n\n                if (elem.nodeType === Strophe.ElementType.NORMAL) {\n                  var tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.\n\n                  if (Strophe.XHTML.validTag(tag)) {\n                    try {\n                      el = Strophe.xmlElement(tag);\n\n                      for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {\n                        var attribute = Strophe.XHTML.attributes[tag][i];\n                        var value = elem.getAttribute(attribute);\n\n                        if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {\n                          continue;\n                        }\n\n                        if (attribute === 'style' && _typeof(value) === 'object' && typeof value.cssText !== 'undefined') {\n                          value = value.cssText; // we're dealing with IE, need to get CSS out\n                        } // filter out invalid css styles\n\n\n                        if (attribute === 'style') {\n                          var css = [];\n                          var cssAttrs = value.split(';');\n\n                          for (var j = 0; j < cssAttrs.length; j++) {\n                            var attr = cssAttrs[j].split(':');\n                            var cssName = attr[0].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\").toLowerCase();\n\n                            if (Strophe.XHTML.validCSS(cssName)) {\n                              var cssValue = attr[1].replace(/^\\s*/, \"\").replace(/\\s*$/, \"\");\n                              css.push(cssName + ': ' + cssValue);\n                            }\n                          }\n\n                          if (css.length > 0) {\n                            value = css.join('; ');\n                            el.setAttribute(attribute, value);\n                          }\n                        } else {\n                          el.setAttribute(attribute, value);\n                        }\n                      }\n\n                      for (var _i2 = 0; _i2 < elem.childNodes.length; _i2++) {\n                        el.appendChild(Strophe.createHtml(elem.childNodes[_i2]));\n                      }\n                    } catch (e) {\n                      // invalid elements\n                      el = Strophe.xmlTextNode('');\n                    }\n                  } else {\n                    el = Strophe.xmlGenerator().createDocumentFragment();\n\n                    for (var _i3 = 0; _i3 < elem.childNodes.length; _i3++) {\n                      el.appendChild(Strophe.createHtml(elem.childNodes[_i3]));\n                    }\n                  }\n                } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {\n                  el = Strophe.xmlGenerator().createDocumentFragment();\n\n                  for (var _i4 = 0; _i4 < elem.childNodes.length; _i4++) {\n                    el.appendChild(Strophe.createHtml(elem.childNodes[_i4]));\n                  }\n                } else if (elem.nodeType === Strophe.ElementType.TEXT) {\n                  el = Strophe.xmlTextNode(elem.nodeValue);\n                }\n\n                return el;\n              },\n\n              /** Function: escapeNode\n               *  Escape the node part (also called local part) of a JID.\n               *\n               *  Parameters:\n               *    (String) node - A node (or local part).\n               *\n               *  Returns:\n               *    An escaped node (or local part).\n               */\n              escapeNode: function escapeNode(node) {\n                if (typeof node !== \"string\") {\n                  return node;\n                }\n\n                return node.replace(/^\\s+|\\s+$/g, '').replace(/\\\\/g, \"\\\\5c\").replace(/ /g, \"\\\\20\").replace(/\\\"/g, \"\\\\22\").replace(/\\&/g, \"\\\\26\").replace(/\\'/g, \"\\\\27\").replace(/\\//g, \"\\\\2f\").replace(/:/g, \"\\\\3a\").replace(/</g, \"\\\\3c\").replace(/>/g, \"\\\\3e\").replace(/@/g, \"\\\\40\");\n              },\n\n              /** Function: unescapeNode\n               *  Unescape a node part (also called local part) of a JID.\n               *\n               *  Parameters:\n               *    (String) node - A node (or local part).\n               *\n               *  Returns:\n               *    An unescaped node (or local part).\n               */\n              unescapeNode: function unescapeNode(node) {\n                if (typeof node !== \"string\") {\n                  return node;\n                }\n\n                return node.replace(/\\\\20/g, \" \").replace(/\\\\22/g, '\"').replace(/\\\\26/g, \"&\").replace(/\\\\27/g, \"'\").replace(/\\\\2f/g, \"/\").replace(/\\\\3a/g, \":\").replace(/\\\\3c/g, \"<\").replace(/\\\\3e/g, \">\").replace(/\\\\40/g, \"@\").replace(/\\\\5c/g, \"\\\\\");\n              },\n\n              /** Function: getNodeFromJid\n               *  Get the node portion of a JID String.\n               *\n               *  Parameters:\n               *    (String) jid - A JID.\n               *\n               *  Returns:\n               *    A String containing the node.\n               */\n              getNodeFromJid: function getNodeFromJid(jid) {\n                if (jid.indexOf(\"@\") < 0) {\n                  return null;\n                }\n\n                return jid.split(\"@\")[0];\n              },\n\n              /** Function: getDomainFromJid\n               *  Get the domain portion of a JID String.\n               *\n               *  Parameters:\n               *    (String) jid - A JID.\n               *\n               *  Returns:\n               *    A String containing the domain.\n               */\n              getDomainFromJid: function getDomainFromJid(jid) {\n                var bare = Strophe.getBareJidFromJid(jid);\n\n                if (bare.indexOf(\"@\") < 0) {\n                  return bare;\n                } else {\n                  var parts = bare.split(\"@\");\n                  parts.splice(0, 1);\n                  return parts.join('@');\n                }\n              },\n\n              /** Function: getResourceFromJid\n               *  Get the resource portion of a JID String.\n               *\n               *  Parameters:\n               *    (String) jid - A JID.\n               *\n               *  Returns:\n               *    A String containing the resource.\n               */\n              getResourceFromJid: function getResourceFromJid(jid) {\n                if (!jid) {\n                  return null;\n                }\n\n                var s = jid.split(\"/\");\n\n                if (s.length < 2) {\n                  return null;\n                }\n\n                s.splice(0, 1);\n                return s.join('/');\n              },\n\n              /** Function: getBareJidFromJid\n               *  Get the bare JID from a JID String.\n               *\n               *  Parameters:\n               *    (String) jid - A JID.\n               *\n               *  Returns:\n               *    A String containing the bare JID.\n               */\n              getBareJidFromJid: function getBareJidFromJid(jid) {\n                return jid ? jid.split(\"/\")[0] : null;\n              },\n\n              /** PrivateFunction: _handleError\n               *  _Private_ function that properly logs an error to the console\n               */\n              _handleError: function _handleError(e) {\n                if (typeof e.stack !== \"undefined\") {\n                  Strophe.fatal(e.stack);\n                }\n\n                if (e.sourceURL) {\n                  Strophe.fatal(\"error: \" + this.handler + \" \" + e.sourceURL + \":\" + e.line + \" - \" + e.name + \": \" + e.message);\n                } else if (e.fileName) {\n                  Strophe.fatal(\"error: \" + this.handler + \" \" + e.fileName + \":\" + e.lineNumber + \" - \" + e.name + \": \" + e.message);\n                } else {\n                  Strophe.fatal(\"error: \" + e.message);\n                }\n              },\n\n              /** Function: log\n               *  User overrideable logging function.\n               *\n               *  This function is called whenever the Strophe library calls any\n               *  of the logging functions.  The default implementation of this\n               *  function logs only fatal errors.  If client code wishes to handle the logging\n               *  messages, it should override this with\n               *  > Strophe.log = function (level, msg) {\n               *  >   (user code here)\n               *  > };\n               *\n               *  Please note that data sent and received over the wire is logged\n               *  via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().\n               *\n               *  The different levels and their meanings are\n               *\n               *    DEBUG - Messages useful for debugging purposes.\n               *    INFO - Informational messages.  This is mostly information like\n               *      'disconnect was called' or 'SASL auth succeeded'.\n               *    WARN - Warnings about potential problems.  This is mostly used\n               *      to report transient connection errors like request timeouts.\n               *    ERROR - Some error occurred.\n               *    FATAL - A non-recoverable fatal error occurred.\n               *\n               *  Parameters:\n               *    (Integer) level - The log level of the log message.  This will\n               *      be one of the values in Strophe.LogLevel.\n               *    (String) msg - The log message.\n               */\n              log: function log(level, msg) {\n                if (level === this.LogLevel.FATAL && _typeof(window.console) === 'object' && typeof window.console.error === 'function') {\n                  window.console.error(msg);\n                }\n              },\n\n              /** Function: debug\n               *  Log a message at the Strophe.LogLevel.DEBUG level.\n               *\n               *  Parameters:\n               *    (String) msg - The log message.\n               */\n              debug: function debug(msg) {\n                this.log(this.LogLevel.DEBUG, msg);\n              },\n\n              /** Function: info\n               *  Log a message at the Strophe.LogLevel.INFO level.\n               *\n               *  Parameters:\n               *    (String) msg - The log message.\n               */\n              info: function info(msg) {\n                this.log(this.LogLevel.INFO, msg);\n              },\n\n              /** Function: warn\n               *  Log a message at the Strophe.LogLevel.WARN level.\n               *\n               *  Parameters:\n               *    (String) msg - The log message.\n               */\n              warn: function warn(msg) {\n                this.log(this.LogLevel.WARN, msg);\n              },\n\n              /** Function: error\n               *  Log a message at the Strophe.LogLevel.ERROR level.\n               *\n               *  Parameters:\n               *    (String) msg - The log message.\n               */\n              error: function error(msg) {\n                this.log(this.LogLevel.ERROR, msg);\n              },\n\n              /** Function: fatal\n               *  Log a message at the Strophe.LogLevel.FATAL level.\n               *\n               *  Parameters:\n               *    (String) msg - The log message.\n               */\n              fatal: function fatal(msg) {\n                this.log(this.LogLevel.FATAL, msg);\n              },\n\n              /** Function: serialize\n               *  Render a DOM element and all descendants to a String.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - A DOM element.\n               *\n               *  Returns:\n               *    The serialized element tree as a String.\n               */\n              serialize: function serialize(elem) {\n                if (!elem) {\n                  return null;\n                }\n\n                if (typeof elem.tree === \"function\") {\n                  elem = elem.tree();\n                }\n\n                var names = _toConsumableArray(Array(elem.attributes.length).keys()).map(function (i) {\n                  return elem.attributes[i].nodeName;\n                });\n\n                names.sort();\n                var result = names.reduce(function (a, n) {\n                  return \"\".concat(a, \" \").concat(n, \"=\\\"\").concat(Strophe.xmlescape(elem.attributes.getNamedItem(n).value), \"\\\"\");\n                }, \"<\".concat(elem.nodeName));\n\n                if (elem.childNodes.length > 0) {\n                  result += \">\";\n\n                  for (var i = 0; i < elem.childNodes.length; i++) {\n                    var child = elem.childNodes[i];\n\n                    switch (child.nodeType) {\n                      case Strophe.ElementType.NORMAL:\n                        // normal element, so recurse\n                        result += Strophe.serialize(child);\n                        break;\n\n                      case Strophe.ElementType.TEXT:\n                        // text element to escape values\n                        result += Strophe.xmlescape(child.nodeValue);\n                        break;\n\n                      case Strophe.ElementType.CDATA:\n                        // cdata section so don't escape values\n                        result += \"<![CDATA[\" + child.nodeValue + \"]]>\";\n                    }\n                  }\n\n                  result += \"</\" + elem.nodeName + \">\";\n                } else {\n                  result += \"/>\";\n                }\n\n                return result;\n              },\n\n              /** PrivateVariable: _requestId\n               *  _Private_ variable that keeps track of the request ids for\n               *  connections.\n               */\n              _requestId: 0,\n\n              /** PrivateVariable: Strophe.connectionPlugins\n               *  _Private_ variable Used to store plugin names that need\n               *  initialization on Strophe.Connection construction.\n               */\n              _connectionPlugins: {},\n\n              /** Function: addConnectionPlugin\n               *  Extends the Strophe.Connection object with the given plugin.\n               *\n               *  Parameters:\n               *    (String) name - The name of the extension.\n               *    (Object) ptype - The plugin's prototype.\n               */\n              addConnectionPlugin: function addConnectionPlugin(name, ptype) {\n                Strophe._connectionPlugins[name] = ptype;\n              }\n            };\n            /** Class: Strophe.Builder\n             *  XML DOM builder.\n             *\n             *  This object provides an interface similar to JQuery but for building\n             *  DOM elements easily and rapidly.  All the functions except for toString()\n             *  and tree() return the object, so calls can be chained.  Here's an\n             *  example using the $iq() builder helper.\n             *  > $iq({to: 'you', from: 'me', type: 'get', id: '1'})\n             *  >     .c('query', {xmlns: 'strophe:example'})\n             *  >     .c('example')\n             *  >     .toString()\n             *\n             *  The above generates this XML fragment\n             *  > <iq to='you' from='me' type='get' id='1'>\n             *  >   <query xmlns='strophe:example'>\n             *  >     <example/>\n             *  >   </query>\n             *  > </iq>\n             *  The corresponding DOM manipulations to get a similar fragment would be\n             *  a lot more tedious and probably involve several helper variables.\n             *\n             *  Since adding children makes new operations operate on the child, up()\n             *  is provided to traverse up the tree.  To add two children, do\n             *  > builder.c('child1', ...).up().c('child2', ...)\n             *  The next operation on the Builder will be relative to the second child.\n             */\n\n            /** Constructor: Strophe.Builder\n             *  Create a Strophe.Builder object.\n             *\n             *  The attributes should be passed in object notation.  For example\n             *  > let b = new Builder('message', {to: 'you', from: 'me'});\n             *  or\n             *  > let b = new Builder('messsage', {'xml:lang': 'en'});\n             *\n             *  Parameters:\n             *    (String) name - The name of the root element.\n             *    (Object) attrs - The attributes for the root element in object notation.\n             *\n             *  Returns:\n             *    A new Strophe.Builder.\n             */\n\n            Strophe.Builder = function (name, attrs) {\n              // Set correct namespace for jabber:client elements\n              if (name === \"presence\" || name === \"message\" || name === \"iq\") {\n                if (attrs && !attrs.xmlns) {\n                  attrs.xmlns = Strophe.NS.CLIENT;\n                } else if (!attrs) {\n                  attrs = {\n                    xmlns: Strophe.NS.CLIENT\n                  };\n                }\n              } // Holds the tree being built.\n\n\n              this.nodeTree = Strophe.xmlElement(name, attrs); // Points to the current operation node.\n\n              this.node = this.nodeTree;\n            };\n\n            Strophe.Builder.prototype = {\n              /** Function: tree\n               *  Return the DOM tree.\n               *\n               *  This function returns the current DOM tree as an element object.  This\n               *  is suitable for passing to functions like Strophe.Connection.send().\n               *\n               *  Returns:\n               *    The DOM tree as a element object.\n               */\n              tree: function tree() {\n                return this.nodeTree;\n              },\n\n              /** Function: toString\n               *  Serialize the DOM tree to a String.\n               *\n               *  This function returns a string serialization of the current DOM\n               *  tree.  It is often used internally to pass data to a\n               *  Strophe.Request object.\n               *\n               *  Returns:\n               *    The serialized DOM tree in a String.\n               */\n              toString: function toString() {\n                return Strophe.serialize(this.nodeTree);\n              },\n\n              /** Function: up\n               *  Make the current parent element the new current element.\n               *\n               *  This function is often used after c() to traverse back up the tree.\n               *  For example, to add two children to the same element\n               *  > builder.c('child1', {}).up().c('child2', {});\n               *\n               *  Returns:\n               *    The Stophe.Builder object.\n               */\n              up: function up() {\n                this.node = this.node.parentNode;\n                return this;\n              },\n\n              /** Function: root\n               *  Make the root element the new current element.\n               *\n               *  When at a deeply nested element in the tree, this function can be used\n               *  to jump back to the root of the tree, instead of having to repeatedly\n               *  call up().\n               *\n               *  Returns:\n               *    The Stophe.Builder object.\n               */\n              root: function root() {\n                this.node = this.nodeTree;\n                return this;\n              },\n\n              /** Function: attrs\n               *  Add or modify attributes of the current element.\n               *\n               *  The attributes should be passed in object notation.  This function\n               *  does not move the current element pointer.\n               *\n               *  Parameters:\n               *    (Object) moreattrs - The attributes to add/modify in object notation.\n               *\n               *  Returns:\n               *    The Strophe.Builder object.\n               */\n              attrs: function attrs(moreattrs) {\n                for (var k in moreattrs) {\n                  if (Object.prototype.hasOwnProperty.call(moreattrs, k)) {\n                    if (moreattrs[k] === undefined) {\n                      this.node.removeAttribute(k);\n                    } else {\n                      this.node.setAttribute(k, moreattrs[k]);\n                    }\n                  }\n                }\n\n                return this;\n              },\n\n              /** Function: c\n               *  Add a child to the current element and make it the new current\n               *  element.\n               *\n               *  This function moves the current element pointer to the child,\n               *  unless text is provided.  If you need to add another child, it\n               *  is necessary to use up() to go back to the parent in the tree.\n               *\n               *  Parameters:\n               *    (String) name - The name of the child.\n               *    (Object) attrs - The attributes of the child in object notation.\n               *    (String) text - The text to add to the child.\n               *\n               *  Returns:\n               *    The Strophe.Builder object.\n               */\n              c: function c(name, attrs, text) {\n                var child = Strophe.xmlElement(name, attrs, text);\n                this.node.appendChild(child);\n\n                if (typeof text !== \"string\" && typeof text !== \"number\") {\n                  this.node = child;\n                }\n\n                return this;\n              },\n\n              /** Function: cnode\n               *  Add a child to the current element and make it the new current\n               *  element.\n               *\n               *  This function is the same as c() except that instead of using a\n               *  name and an attributes object to create the child it uses an\n               *  existing DOM element object.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - A DOM element.\n               *\n               *  Returns:\n               *    The Strophe.Builder object.\n               */\n              cnode: function cnode(elem) {\n                var impNode;\n                var xmlGen = Strophe.xmlGenerator();\n\n                try {\n                  impNode = xmlGen.importNode !== undefined;\n                } catch (e) {\n                  impNode = false;\n                }\n\n                var newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);\n                this.node.appendChild(newElem);\n                this.node = newElem;\n                return this;\n              },\n\n              /** Function: t\n               *  Add a child text element.\n               *\n               *  This *does not* make the child the new current element since there\n               *  are no children of text elements.\n               *\n               *  Parameters:\n               *    (String) text - The text data to append to the current element.\n               *\n               *  Returns:\n               *    The Strophe.Builder object.\n               */\n              t: function t(text) {\n                var child = Strophe.xmlTextNode(text);\n                this.node.appendChild(child);\n                return this;\n              },\n\n              /** Function: h\n               *  Replace current element contents with the HTML passed in.\n               *\n               *  This *does not* make the child the new current element\n               *\n               *  Parameters:\n               *    (String) html - The html to insert as contents of current element.\n               *\n               *  Returns:\n               *    The Strophe.Builder object.\n               */\n              h: function h(html) {\n                var fragment = document.createElement('body'); // force the browser to try and fix any invalid HTML tags\n\n                fragment.innerHTML = html; // copy cleaned html into an xml dom\n\n                var xhtml = Strophe.createHtml(fragment);\n\n                while (xhtml.childNodes.length > 0) {\n                  this.node.appendChild(xhtml.childNodes[0]);\n                }\n\n                return this;\n              }\n            };\n            /** PrivateClass: Strophe.Handler\n             *  _Private_ helper class for managing stanza handlers.\n             *\n             *  A Strophe.Handler encapsulates a user provided callback function to be\n             *  executed when matching stanzas are received by the connection.\n             *  Handlers can be either one-off or persistant depending on their\n             *  return value. Returning true will cause a Handler to remain active, and\n             *  returning false will remove the Handler.\n             *\n             *  Users will not use Strophe.Handler objects directly, but instead they\n             *  will use Strophe.Connection.addHandler() and\n             *  Strophe.Connection.deleteHandler().\n             */\n\n            /** PrivateConstructor: Strophe.Handler\n             *  Create and initialize a new Strophe.Handler.\n             *\n             *  Parameters:\n             *    (Function) handler - A function to be executed when the handler is run.\n             *    (String) ns - The namespace to match.\n             *    (String) name - The element name to match.\n             *    (String) type - The element type to match.\n             *    (String) id - The element id attribute to match.\n             *    (String) from - The element from attribute to match.\n             *    (Object) options - Handler options\n             *\n             *  Returns:\n             *    A new Strophe.Handler object.\n             */\n\n            Strophe.Handler = function (handler, ns, name, type, id, from, options) {\n              this.handler = handler;\n              this.ns = ns;\n              this.name = name;\n              this.type = type;\n              this.id = id;\n              this.options = options || {\n                'matchBareFromJid': false,\n                'ignoreNamespaceFragment': false\n              }; // BBB: Maintain backward compatibility with old `matchBare` option\n\n              if (this.options.matchBare) {\n                Strophe.warn('The \"matchBare\" option is deprecated, use \"matchBareFromJid\" instead.');\n                this.options.matchBareFromJid = this.options.matchBare;\n                delete this.options.matchBare;\n              }\n\n              if (this.options.matchBareFromJid) {\n                this.from = from ? Strophe.getBareJidFromJid(from) : null;\n              } else {\n                this.from = from;\n              } // whether the handler is a user handler or a system handler\n\n\n              this.user = true;\n            };\n\n            Strophe.Handler.prototype = {\n              /** PrivateFunction: getNamespace\n               *  Returns the XML namespace attribute on an element.\n               *  If `ignoreNamespaceFragment` was passed in for this handler, then the\n               *  URL fragment will be stripped.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The XML element with the namespace.\n               *\n               *  Returns:\n               *    The namespace, with optionally the fragment stripped.\n               */\n              getNamespace: function getNamespace(elem) {\n                var elNamespace = elem.getAttribute(\"xmlns\");\n\n                if (elNamespace && this.options.ignoreNamespaceFragment) {\n                  elNamespace = elNamespace.split('#')[0];\n                }\n\n                return elNamespace;\n              },\n\n              /** PrivateFunction: namespaceMatch\n               *  Tests if a stanza matches the namespace set for this Strophe.Handler.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The XML element to test.\n               *\n               *  Returns:\n               *    true if the stanza matches and false otherwise.\n               */\n              namespaceMatch: function namespaceMatch(elem) {\n                var _this = this;\n\n                var nsMatch = false;\n\n                if (!this.ns) {\n                  return true;\n                } else {\n                  Strophe.forEachChild(elem, null, function (elem) {\n                    if (_this.getNamespace(elem) === _this.ns) {\n                      nsMatch = true;\n                    }\n                  });\n                  return nsMatch || this.getNamespace(elem) === this.ns;\n                }\n              },\n\n              /** PrivateFunction: isMatch\n               *  Tests if a stanza matches the Strophe.Handler.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The XML element to test.\n               *\n               *  Returns:\n               *    true if the stanza matches and false otherwise.\n               */\n              isMatch: function isMatch(elem) {\n                var from = elem.getAttribute('from');\n\n                if (this.options.matchBareFromJid) {\n                  from = Strophe.getBareJidFromJid(from);\n                }\n\n                var elem_type = elem.getAttribute(\"type\");\n\n                if (this.namespaceMatch(elem) && (!this.name || Strophe.isTagEqual(elem, this.name)) && (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) && (!this.id || elem.getAttribute(\"id\") === this.id) && (!this.from || from === this.from)) {\n                  return true;\n                }\n\n                return false;\n              },\n\n              /** PrivateFunction: run\n               *  Run the callback on a matching stanza.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The DOM element that triggered the\n               *      Strophe.Handler.\n               *\n               *  Returns:\n               *    A boolean indicating if the handler should remain active.\n               */\n              run: function run(elem) {\n                var result = null;\n\n                try {\n                  result = this.handler(elem);\n                } catch (e) {\n                  Strophe._handleError(e);\n\n                  throw e;\n                }\n\n                return result;\n              },\n\n              /** PrivateFunction: toString\n               *  Get a String representation of the Strophe.Handler object.\n               *\n               *  Returns:\n               *    A String.\n               */\n              toString: function toString() {\n                return \"{Handler: \" + this.handler + \"(\" + this.name + \",\" + this.id + \",\" + this.ns + \")}\";\n              }\n            };\n            /** PrivateClass: Strophe.TimedHandler\n             *  _Private_ helper class for managing timed handlers.\n             *\n             *  A Strophe.TimedHandler encapsulates a user provided callback that\n             *  should be called after a certain period of time or at regular\n             *  intervals.  The return value of the callback determines whether the\n             *  Strophe.TimedHandler will continue to fire.\n             *\n             *  Users will not use Strophe.TimedHandler objects directly, but instead\n             *  they will use Strophe.Connection.addTimedHandler() and\n             *  Strophe.Connection.deleteTimedHandler().\n             */\n\n            /** PrivateConstructor: Strophe.TimedHandler\n             *  Create and initialize a new Strophe.TimedHandler object.\n             *\n             *  Parameters:\n             *    (Integer) period - The number of milliseconds to wait before the\n             *      handler is called.\n             *    (Function) handler - The callback to run when the handler fires.  This\n             *      function should take no arguments.\n             *\n             *  Returns:\n             *    A new Strophe.TimedHandler object.\n             */\n\n            Strophe.TimedHandler = function (period, handler) {\n              this.period = period;\n              this.handler = handler;\n              this.lastCalled = new Date().getTime();\n              this.user = true;\n            };\n\n            Strophe.TimedHandler.prototype = {\n              /** PrivateFunction: run\n               *  Run the callback for the Strophe.TimedHandler.\n               *\n               *  Returns:\n               *    true if the Strophe.TimedHandler should be called again, and false\n               *      otherwise.\n               */\n              run: function run() {\n                this.lastCalled = new Date().getTime();\n                return this.handler();\n              },\n\n              /** PrivateFunction: reset\n               *  Reset the last called time for the Strophe.TimedHandler.\n               */\n              reset: function reset() {\n                this.lastCalled = new Date().getTime();\n              },\n\n              /** PrivateFunction: toString\n               *  Get a string representation of the Strophe.TimedHandler object.\n               *\n               *  Returns:\n               *    The string representation.\n               */\n              toString: function toString() {\n                return \"{TimedHandler: \" + this.handler + \"(\" + this.period + \")}\";\n              }\n            };\n            /** Class: Strophe.Connection\n             *  XMPP Connection manager.\n             *\n             *  This class is the main part of Strophe.  It manages a BOSH or websocket\n             *  connection to an XMPP server and dispatches events to the user callbacks\n             *  as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1\n             *  and legacy authentication.\n             *\n             *  After creating a Strophe.Connection object, the user will typically\n             *  call connect() with a user supplied callback to handle connection level\n             *  events like authentication failure, disconnection, or connection\n             *  complete.\n             *\n             *  The user will also have several event handlers defined by using\n             *  addHandler() and addTimedHandler().  These will allow the user code to\n             *  respond to interesting stanzas or do something periodically with the\n             *  connection. These handlers will be active once authentication is\n             *  finished.\n             *\n             *  To send data to the connection, use send().\n             */\n\n            /** Constructor: Strophe.Connection\n             *  Create and initialize a Strophe.Connection object.\n             *\n             *  The transport-protocol for this connection will be chosen automatically\n             *  based on the given service parameter. URLs starting with \"ws://\" or\n             *  \"wss://\" will use WebSockets, URLs starting with \"http://\", \"https://\"\n             *  or without a protocol will use BOSH.\n             *\n             *  To make Strophe connect to the current host you can leave out the protocol\n             *  and host part and just pass the path, e.g.\n             *\n             *  > let conn = new Strophe.Connection(\"/http-bind/\");\n             *\n             *  Options common to both Websocket and BOSH:\n             *  ------------------------------------------\n             *\n             *  cookies:\n             *\n             *  The *cookies* option allows you to pass in cookies to be added to the\n             *  document. These cookies will then be included in the BOSH XMLHttpRequest\n             *  or in the websocket connection.\n             *\n             *  The passed in value must be a map of cookie names and string values.\n             *\n             *  > { \"myCookie\": {\n             *  >     \"value\": \"1234\",\n             *  >     \"domain\": \".example.org\",\n             *  >     \"path\": \"/\",\n             *  >     \"expires\": expirationDate\n             *  >     }\n             *  > }\n             *\n             *  Note that cookies can't be set in this way for other domains (i.e. cross-domain).\n             *  Those cookies need to be set under those domains, for example they can be\n             *  set server-side by making a XHR call to that domain to ask it to set any\n             *  necessary cookies.\n             *\n             *  mechanisms:\n             *\n             *  The *mechanisms* option allows you to specify the SASL mechanisms that this\n             *  instance of Strophe.Connection (and therefore your XMPP client) will\n             *  support.\n             *\n             *  The value must be an array of objects with Strophe.SASLMechanism\n             *  prototypes.\n             *\n             *  If nothing is specified, then the following mechanisms (and their\n             *  priorities) are registered:\n             *\n             *      SCRAM-SHA1 - 70\n             *      DIGEST-MD5 - 60\n             *      PLAIN - 50\n             *      OAUTH-BEARER - 40\n             *      OAUTH-2 - 30\n             *      ANONYMOUS - 20\n             *      EXTERNAL - 10\n             *\n             *  explicitResourceBinding:\n             *\n             *  If `explicitResourceBinding` is set to a truthy value, then the XMPP client\n             *  needs to explicitly call `Strophe.Connection.prototype.bind` once the XMPP\n             *  server has advertised the \"urn:ietf:params:xml:ns:xmpp-bind\" feature.\n             *\n             *  Making this step explicit allows client authors to first finish other\n             *  stream related tasks, such as setting up an XEP-0198 Stream Management\n             *  session, before binding the JID resource for this session.\n             *\n             *  WebSocket options:\n             *  ------------------\n             *\n             *  If you want to connect to the current host with a WebSocket connection you\n             *  can tell Strophe to use WebSockets through a \"protocol\" attribute in the\n             *  optional options parameter. Valid values are \"ws\" for WebSocket and \"wss\"\n             *  for Secure WebSocket.\n             *  So to connect to \"wss://CURRENT_HOSTNAME/xmpp-websocket\" you would call\n             *\n             *  > let conn = new Strophe.Connection(\"/xmpp-websocket/\", {protocol: \"wss\"});\n             *\n             *  Note that relative URLs _NOT_ starting with a \"/\" will also include the path\n             *  of the current site.\n             *\n             *  Also because downgrading security is not permitted by browsers, when using\n             *  relative URLs both BOSH and WebSocket connections will use their secure\n             *  variants if the current connection to the site is also secure (https).\n             *\n             *  BOSH options:\n             *  -------------\n             *\n             *  By adding \"sync\" to the options, you can control if requests will\n             *  be made synchronously or not. The default behaviour is asynchronous.\n             *  If you want to make requests synchronous, make \"sync\" evaluate to true.\n             *  > let conn = new Strophe.Connection(\"/http-bind/\", {sync: true});\n             *\n             *  You can also toggle this on an already established connection.\n             *  > conn.options.sync = true;\n             *\n             *  The *customHeaders* option can be used to provide custom HTTP headers to be\n             *  included in the XMLHttpRequests made.\n             *\n             *  The *keepalive* option can be used to instruct Strophe to maintain the\n             *  current BOSH session across interruptions such as webpage reloads.\n             *\n             *  It will do this by caching the sessions tokens in sessionStorage, and when\n             *  \"restore\" is called it will check whether there are cached tokens with\n             *  which it can resume an existing session.\n             *\n             *  The *withCredentials* option should receive a Boolean value and is used to\n             *  indicate wether cookies should be included in ajax requests (by default\n             *  they're not).\n             *  Set this value to true if you are connecting to a BOSH service\n             *  and for some reason need to send cookies to it.\n             *  In order for this to work cross-domain, the server must also enable\n             *  credentials by setting the Access-Control-Allow-Credentials response header\n             *  to \"true\". For most usecases however this setting should be false (which\n             *  is the default).\n             *  Additionally, when using Access-Control-Allow-Credentials, the\n             *  Access-Control-Allow-Origin header can't be set to the wildcard \"*\", but\n             *  instead must be restricted to actual domains.\n             *\n             *  The *contentType* option can be set to change the default Content-Type\n             *  of \"text/xml; charset=utf-8\", which can be useful to reduce the amount of\n             *  CORS preflight requests that are sent to the server.\n             *\n             *  Parameters:\n             *    (String) service - The BOSH or WebSocket service URL.\n             *    (Object) options - A hash of configuration options\n             *\n             *  Returns:\n             *    A new Strophe.Connection object.\n             */\n\n            Strophe.Connection = function (service, options) {\n              var _this2 = this;\n\n              // The service URL\n              this.service = service; // Configuration options\n\n              this.options = options || {};\n              var proto = this.options.protocol || \"\"; // Select protocal based on service or options\n\n              if (service.indexOf(\"ws:\") === 0 || service.indexOf(\"wss:\") === 0 || proto.indexOf(\"ws\") === 0) {\n                this._proto = new Strophe.Websocket(this);\n              } else {\n                this._proto = new Strophe.Bosh(this);\n              }\n              /* The connected JID. */\n\n\n              this.jid = \"\";\n              /* the JIDs domain */\n\n              this.domain = null;\n              /* stream:features */\n\n              this.features = null; // SASL\n\n              this._sasl_data = {};\n              this.do_session = false;\n              this.do_bind = false; // handler lists\n\n              this.timedHandlers = [];\n              this.handlers = [];\n              this.removeTimeds = [];\n              this.removeHandlers = [];\n              this.addTimeds = [];\n              this.addHandlers = [];\n              this.protocolErrorHandlers = {\n                'HTTP': {},\n                'websocket': {}\n              };\n              this._idleTimeout = null;\n              this._disconnectTimeout = null;\n              this.authenticated = false;\n              this.connected = false;\n              this.disconnecting = false;\n              this.do_authentication = true;\n              this.paused = false;\n              this.restored = false;\n              this._data = [];\n              this._uniqueId = 0;\n              this._sasl_success_handler = null;\n              this._sasl_failure_handler = null;\n              this._sasl_challenge_handler = null; // Max retries before disconnecting\n\n              this.maxRetries = 5; // Call onIdle callback every 1/10th of a second\n\n              this._idleTimeout = setTimeout(function () {\n                return _this2._onIdle();\n              }, 100);\n              utils.addCookies(this.options.cookies);\n              this.registerSASLMechanisms(this.options.mechanisms); // initialize plugins\n\n              for (var k in Strophe._connectionPlugins) {\n                if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {\n                  var F = function F() {};\n\n                  F.prototype = Strophe._connectionPlugins[k];\n                  this[k] = new F();\n                  this[k].init(this);\n                }\n              }\n            };\n\n            Strophe.Connection.prototype = {\n              /** Function: reset\n               *  Reset the connection.\n               *\n               *  This function should be called after a connection is disconnected\n               *  before that connection is reused.\n               */\n              reset: function reset() {\n                this._proto._reset(); // SASL\n\n\n                this.do_session = false;\n                this.do_bind = false; // handler lists\n\n                this.timedHandlers = [];\n                this.handlers = [];\n                this.removeTimeds = [];\n                this.removeHandlers = [];\n                this.addTimeds = [];\n                this.addHandlers = [];\n                this.authenticated = false;\n                this.connected = false;\n                this.disconnecting = false;\n                this.restored = false;\n                this._data = [];\n                this._requests = [];\n                this._uniqueId = 0;\n              },\n\n              /** Function: pause\n               *  Pause the request manager.\n               *\n               *  This will prevent Strophe from sending any more requests to the\n               *  server.  This is very useful for temporarily pausing\n               *  BOSH-Connections while a lot of send() calls are happening quickly.\n               *  This causes Strophe to send the data in a single request, saving\n               *  many request trips.\n               */\n              pause: function pause() {\n                this.paused = true;\n              },\n\n              /** Function: resume\n               *  Resume the request manager.\n               *\n               *  This resumes after pause() has been called.\n               */\n              resume: function resume() {\n                this.paused = false;\n              },\n\n              /** Function: getUniqueId\n               *  Generate a unique ID for use in <iq/> elements.\n               *\n               *  All <iq/> stanzas are required to have unique id attributes.  This\n               *  function makes creating these easy.  Each connection instance has\n               *  a counter which starts from zero, and the value of this counter\n               *  plus a colon followed by the suffix becomes the unique id. If no\n               *  suffix is supplied, the counter is used as the unique id.\n               *\n               *  Suffixes are used to make debugging easier when reading the stream\n               *  data, and their use is recommended.  The counter resets to 0 for\n               *  every new connection for the same reason.  For connections to the\n               *  same server that authenticate the same way, all the ids should be\n               *  the same, which makes it easy to see changes.  This is useful for\n               *  automated testing as well.\n               *\n               *  Parameters:\n               *    (String) suffix - A optional suffix to append to the id.\n               *\n               *  Returns:\n               *    A unique string to be used for the id attribute.\n               */\n              getUniqueId: function getUniqueId(suffix) {\n                var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\n                  var r = Math.random() * 16 | 0,\n                      v = c === 'x' ? r : r & 0x3 | 0x8;\n                  return v.toString(16);\n                });\n\n                if (typeof suffix === \"string\" || typeof suffix === \"number\") {\n                  return uuid + \":\" + suffix;\n                } else {\n                  return uuid + \"\";\n                }\n              },\n\n              /** Function: addProtocolErrorHandler\n               *  Register a handler function for when a protocol (websocker or HTTP)\n               *  error occurs.\n               *\n               *  NOTE: Currently only HTTP errors for BOSH requests are handled.\n               *  Patches that handle websocket errors would be very welcome.\n               *\n               *  Parameters:\n               *    (String) protocol - 'HTTP' or 'websocket'\n               *    (Integer) status_code - Error status code (e.g 500, 400 or 404)\n               *    (Function) callback - Function that will fire on Http error\n               *\n               *  Example:\n               *  function onError(err_code){\n               *    //do stuff\n               *  }\n               *\n               *  let conn = Strophe.connect('http://example.com/http-bind');\n               *  conn.addProtocolErrorHandler('HTTP', 500, onError);\n               *  // Triggers HTTP 500 error and onError handler will be called\n               *  conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);\n               */\n              addProtocolErrorHandler: function addProtocolErrorHandler(protocol, status_code, callback) {\n                this.protocolErrorHandlers[protocol][status_code] = callback;\n              },\n\n              /** Function: connect\n               *  Starts the connection process.\n               *\n               *  As the connection process proceeds, the user supplied callback will\n               *  be triggered multiple times with status updates.  The callback\n               *  should take two arguments - the status code and the error condition.\n               *\n               *  The status code will be one of the values in the Strophe.Status\n               *  constants.  The error condition will be one of the conditions\n               *  defined in RFC 3920 or the condition 'strophe-parsererror'.\n               *\n               *  The Parameters _wait_, _hold_ and _route_ are optional and only relevant\n               *  for BOSH connections. Please see XEP 124 for a more detailed explanation\n               *  of the optional parameters.\n               *\n               *  Parameters:\n               *    (String) jid - The user's JID.  This may be a bare JID,\n               *      or a full JID.  If a node is not supplied, SASL OAUTHBEARER or\n               *      SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will\n               *      process the provided password value as an access token).\n               *    (String) pass - The user's password.\n               *    (Function) callback - The connect callback function.\n               *    (Integer) wait - The optional HTTPBIND wait value.  This is the\n               *      time the server will wait before returning an empty result for\n               *      a request.  The default setting of 60 seconds is recommended.\n               *    (Integer) hold - The optional HTTPBIND hold value.  This is the\n               *      number of connections the server will hold at one time.  This\n               *      should almost always be set to 1 (the default).\n               *    (String) route - The optional route value.\n               *    (String) authcid - The optional alternative authentication identity\n               *      (username) if intending to impersonate another user.\n               *      When using the SASL-EXTERNAL authentication mechanism, for example\n               *      with client certificates, then the authcid value is used to\n               *      determine whether an authorization JID (authzid) should be sent to\n               *      the server. The authzid should not be sent to the server if the\n               *      authzid and authcid are the same. So to prevent it from being sent\n               *      (for example when the JID is already contained in the client\n               *      certificate), set authcid to that same JID. See XEP-178 for more\n               *      details.\n               */\n              connect: function connect(jid, pass, callback, wait, hold, route, authcid) {\n                this.jid = jid;\n                /** Variable: authzid\n                 *  Authorization identity.\n                 */\n\n                this.authzid = Strophe.getBareJidFromJid(this.jid);\n                /** Variable: authcid\n                 *  Authentication identity (User name).\n                 */\n\n                this.authcid = authcid || Strophe.getNodeFromJid(this.jid);\n                /** Variable: pass\n                 *  Authentication identity (User password).\n                 */\n\n                this.pass = pass;\n                /** Variable: servtype\n                 *  Digest MD5 compatibility.\n                 */\n\n                this.servtype = \"xmpp\";\n                this.connect_callback = callback;\n                this.disconnecting = false;\n                this.connected = false;\n                this.authenticated = false;\n                this.restored = false; // parse jid for domain\n\n                this.domain = Strophe.getDomainFromJid(this.jid);\n\n                this._changeConnectStatus(Strophe.Status.CONNECTING, null);\n\n                this._proto._connect(wait, hold, route);\n              },\n\n              /** Function: attach\n               *  Attach to an already created and authenticated BOSH session.\n               *\n               *  This function is provided to allow Strophe to attach to BOSH\n               *  sessions which have been created externally, perhaps by a Web\n               *  application.  This is often used to support auto-login type features\n               *  without putting user credentials into the page.\n               *\n               *  Parameters:\n               *    (String) jid - The full JID that is bound by the session.\n               *    (String) sid - The SID of the BOSH session.\n               *    (String) rid - The current RID of the BOSH session.  This RID\n               *      will be used by the next request.\n               *    (Function) callback The connect callback function.\n               *    (Integer) wait - The optional HTTPBIND wait value.  This is the\n               *      time the server will wait before returning an empty result for\n               *      a request.  The default setting of 60 seconds is recommended.\n               *      Other settings will require tweaks to the Strophe.TIMEOUT value.\n               *    (Integer) hold - The optional HTTPBIND hold value.  This is the\n               *      number of connections the server will hold at one time.  This\n               *      should almost always be set to 1 (the default).\n               *    (Integer) wind - The optional HTTBIND window value.  This is the\n               *      allowed range of request ids that are valid.  The default is 5.\n               */\n              attach: function attach(jid, sid, rid, callback, wait, hold, wind) {\n                if (this._proto instanceof Strophe.Bosh) {\n                  this._proto._attach(jid, sid, rid, callback, wait, hold, wind);\n                } else {\n                  var error = new Error('The \"attach\" method can only be used with a BOSH connection.');\n                  error.name = 'StropheSessionError';\n                  throw error;\n                }\n              },\n\n              /** Function: restore\n               *  Attempt to restore a cached BOSH session.\n               *\n               *  This function is only useful in conjunction with providing the\n               *  \"keepalive\":true option when instantiating a new Strophe.Connection.\n               *\n               *  When \"keepalive\" is set to true, Strophe will cache the BOSH tokens\n               *  RID (Request ID) and SID (Session ID) and then when this function is\n               *  called, it will attempt to restore the session from those cached\n               *  tokens.\n               *\n               *  This function must therefore be called instead of connect or attach.\n               *\n               *  For an example on how to use it, please see examples/restore.js\n               *\n               *  Parameters:\n               *    (String) jid - The user's JID.  This may be a bare JID or a full JID.\n               *    (Function) callback - The connect callback function.\n               *    (Integer) wait - The optional HTTPBIND wait value.  This is the\n               *      time the server will wait before returning an empty result for\n               *      a request.  The default setting of 60 seconds is recommended.\n               *    (Integer) hold - The optional HTTPBIND hold value.  This is the\n               *      number of connections the server will hold at one time.  This\n               *      should almost always be set to 1 (the default).\n               *    (Integer) wind - The optional HTTBIND window value.  This is the\n               *      allowed range of request ids that are valid.  The default is 5.\n               */\n              restore: function restore(jid, callback, wait, hold, wind) {\n                if (this._sessionCachingSupported()) {\n                  this._proto._restore(jid, callback, wait, hold, wind);\n                } else {\n                  var error = new Error('The \"restore\" method can only be used with a BOSH connection.');\n                  error.name = 'StropheSessionError';\n                  throw error;\n                }\n              },\n\n              /** PrivateFunction: _sessionCachingSupported\n               * Checks whether sessionStorage and JSON are supported and whether we're\n               * using BOSH.\n               */\n              _sessionCachingSupported: function _sessionCachingSupported() {\n                if (this._proto instanceof Strophe.Bosh) {\n                  if (!JSON) {\n                    return false;\n                  }\n\n                  try {\n                    sessionStorage.setItem('_strophe_', '_strophe_');\n                    sessionStorage.removeItem('_strophe_');\n                  } catch (e) {\n                    return false;\n                  }\n\n                  return true;\n                }\n\n                return false;\n              },\n\n              /** Function: xmlInput\n               *  User overrideable function that receives XML data coming into the\n               *  connection.\n               *\n               *  The default function does nothing.  User code can override this with\n               *  > Strophe.Connection.xmlInput = function (elem) {\n               *  >   (user code)\n               *  > };\n               *\n               *  Due to limitations of current Browsers' XML-Parsers the opening and closing\n               *  <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.\n               *\n               *  BOSH-Connections will have all stanzas wrapped in a <body> tag. See\n               *  <Strophe.Bosh.strip> if you want to strip this tag.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The XML data received by the connection.\n               */\n              xmlInput: function xmlInput(elem) {\n                return;\n              },\n\n              /** Function: xmlOutput\n               *  User overrideable function that receives XML data sent to the\n               *  connection.\n               *\n               *  The default function does nothing.  User code can override this with\n               *  > Strophe.Connection.xmlOutput = function (elem) {\n               *  >   (user code)\n               *  > };\n               *\n               *  Due to limitations of current Browsers' XML-Parsers the opening and closing\n               *  <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.\n               *\n               *  BOSH-Connections will have all stanzas wrapped in a <body> tag. See\n               *  <Strophe.Bosh.strip> if you want to strip this tag.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The XMLdata sent by the connection.\n               */\n              xmlOutput: function xmlOutput(elem) {\n                return;\n              },\n\n              /** Function: rawInput\n               *  User overrideable function that receives raw data coming into the\n               *  connection.\n               *\n               *  The default function does nothing.  User code can override this with\n               *  > Strophe.Connection.rawInput = function (data) {\n               *  >   (user code)\n               *  > };\n               *\n               *  Parameters:\n               *    (String) data - The data received by the connection.\n               */\n              rawInput: function rawInput(data) {\n                return;\n              },\n\n              /** Function: rawOutput\n               *  User overrideable function that receives raw data sent to the\n               *  connection.\n               *\n               *  The default function does nothing.  User code can override this with\n               *  > Strophe.Connection.rawOutput = function (data) {\n               *  >   (user code)\n               *  > };\n               *\n               *  Parameters:\n               *    (String) data - The data sent by the connection.\n               */\n              rawOutput: function rawOutput(data) {\n                return;\n              },\n\n              /** Function: nextValidRid\n               *  User overrideable function that receives the new valid rid.\n               *\n               *  The default function does nothing. User code can override this with\n               *  > Strophe.Connection.nextValidRid = function (rid) {\n               *  >    (user code)\n               *  > };\n               *\n               *  Parameters:\n               *    (Number) rid - The next valid rid\n               */\n              nextValidRid: function nextValidRid(rid) {\n                return;\n              },\n\n              /** Function: send\n               *  Send a stanza.\n               *\n               *  This function is called to push data onto the send queue to\n               *  go out over the wire.  Whenever a request is sent to the BOSH\n               *  server, all pending data is sent and the queue is flushed.\n               *\n               *  Parameters:\n               *    (XMLElement |\n               *     [XMLElement] |\n               *     Strophe.Builder) elem - The stanza to send.\n               */\n              send: function send(elem) {\n                if (elem === null) {\n                  return;\n                }\n\n                if (typeof elem.sort === \"function\") {\n                  for (var i = 0; i < elem.length; i++) {\n                    this._queueData(elem[i]);\n                  }\n                } else if (typeof elem.tree === \"function\") {\n                  this._queueData(elem.tree());\n                } else {\n                  this._queueData(elem);\n                }\n\n                this._proto._send();\n              },\n\n              /** Function: flush\n               *  Immediately send any pending outgoing data.\n               *\n               *  Normally send() queues outgoing data until the next idle period\n               *  (100ms), which optimizes network use in the common cases when\n               *  several send()s are called in succession. flush() can be used to\n               *  immediately send all pending data.\n               */\n              flush: function flush() {\n                // cancel the pending idle period and run the idle function\n                // immediately\n                clearTimeout(this._idleTimeout);\n\n                this._onIdle();\n              },\n\n              /** Function: sendPresence\n               *  Helper function to send presence stanzas. The main benefit is for\n               *  sending presence stanzas for which you expect a responding presence\n               *  stanza with the same id (for example when leaving a chat room).\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The stanza to send.\n               *    (Function) callback - The callback function for a successful request.\n               *    (Function) errback - The callback function for a failed or timed\n               *      out request.  On timeout, the stanza will be null.\n               *    (Integer) timeout - The time specified in milliseconds for a\n               *      timeout to occur.\n               *\n               *  Returns:\n               *    The id used to send the presence.\n               */\n              sendPresence: function sendPresence(elem, callback, errback, timeout) {\n                var _this3 = this;\n\n                var timeoutHandler = null;\n\n                if (typeof elem.tree === \"function\") {\n                  elem = elem.tree();\n                }\n\n                var id = elem.getAttribute('id');\n\n                if (!id) {\n                  // inject id if not found\n                  id = this.getUniqueId(\"sendPresence\");\n                  elem.setAttribute(\"id\", id);\n                }\n\n                if (typeof callback === \"function\" || typeof errback === \"function\") {\n                  var handler = this.addHandler(function (stanza) {\n                    // remove timeout handler if there is one\n                    if (timeoutHandler) {\n                      _this3.deleteTimedHandler(timeoutHandler);\n                    }\n\n                    if (stanza.getAttribute('type') === 'error') {\n                      if (errback) {\n                        errback(stanza);\n                      }\n                    } else if (callback) {\n                      callback(stanza);\n                    }\n                  }, null, 'presence', null, id); // if timeout specified, set up a timeout handler.\n\n                  if (timeout) {\n                    timeoutHandler = this.addTimedHandler(timeout, function () {\n                      // get rid of normal handler\n                      _this3.deleteHandler(handler); // call errback on timeout with null stanza\n\n\n                      if (errback) {\n                        errback(null);\n                      }\n\n                      return false;\n                    });\n                  }\n                }\n\n                this.send(elem);\n                return id;\n              },\n\n              /** Function: sendIQ\n               *  Helper function to send IQ stanzas.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The stanza to send.\n               *    (Function) callback - The callback function for a successful request.\n               *    (Function) errback - The callback function for a failed or timed\n               *      out request.  On timeout, the stanza will be null.\n               *    (Integer) timeout - The time specified in milliseconds for a\n               *      timeout to occur.\n               *\n               *  Returns:\n               *    The id used to send the IQ.\n              */\n              sendIQ: function sendIQ(elem, callback, errback, timeout) {\n                var _this4 = this;\n\n                var timeoutHandler = null;\n\n                if (typeof elem.tree === \"function\") {\n                  elem = elem.tree();\n                }\n\n                var id = elem.getAttribute('id');\n\n                if (!id) {\n                  // inject id if not found\n                  id = this.getUniqueId(\"sendIQ\");\n                  elem.setAttribute(\"id\", id);\n                }\n\n                if (typeof callback === \"function\" || typeof errback === \"function\") {\n                  var handler = this.addHandler(function (stanza) {\n                    // remove timeout handler if there is one\n                    if (timeoutHandler) {\n                      _this4.deleteTimedHandler(timeoutHandler);\n                    }\n\n                    var iqtype = stanza.getAttribute('type');\n\n                    if (iqtype === 'result') {\n                      if (callback) {\n                        callback(stanza);\n                      }\n                    } else if (iqtype === 'error') {\n                      if (errback) {\n                        errback(stanza);\n                      }\n                    } else {\n                      var error = new Error(\"Got bad IQ type of \".concat(iqtype));\n                      error.name = \"StropheError\";\n                      throw error;\n                    }\n                  }, null, 'iq', ['error', 'result'], id); // if timeout specified, set up a timeout handler.\n\n                  if (timeout) {\n                    timeoutHandler = this.addTimedHandler(timeout, function () {\n                      // get rid of normal handler\n                      _this4.deleteHandler(handler); // call errback on timeout with null stanza\n\n\n                      if (errback) {\n                        errback(null);\n                      }\n\n                      return false;\n                    });\n                  }\n                }\n\n                this.send(elem);\n                return id;\n              },\n\n              /** PrivateFunction: _queueData\n               *  Queue outgoing data for later sending.  Also ensures that the data\n               *  is a DOMElement.\n               */\n              _queueData: function _queueData(element) {\n                if (element === null || !element.tagName || !element.childNodes) {\n                  var error = new Error(\"Cannot queue non-DOMElement.\");\n                  error.name = \"StropheError\";\n                  throw error;\n                }\n\n                this._data.push(element);\n              },\n\n              /** PrivateFunction: _sendRestart\n               *  Send an xmpp:restart stanza.\n               */\n              _sendRestart: function _sendRestart() {\n                var _this5 = this;\n\n                this._data.push(\"restart\");\n\n                this._proto._sendRestart();\n\n                this._idleTimeout = setTimeout(function () {\n                  return _this5._onIdle();\n                }, 100);\n              },\n\n              /** Function: addTimedHandler\n               *  Add a timed handler to the connection.\n               *\n               *  This function adds a timed handler.  The provided handler will\n               *  be called every period milliseconds until it returns false,\n               *  the connection is terminated, or the handler is removed.  Handlers\n               *  that wish to continue being invoked should return true.\n               *\n               *  Because of method binding it is necessary to save the result of\n               *  this function if you wish to remove a handler with\n               *  deleteTimedHandler().\n               *\n               *  Note that user handlers are not active until authentication is\n               *  successful.\n               *\n               *  Parameters:\n               *    (Integer) period - The period of the handler.\n               *    (Function) handler - The callback function.\n               *\n               *  Returns:\n               *    A reference to the handler that can be used to remove it.\n               */\n              addTimedHandler: function addTimedHandler(period, handler) {\n                var thand = new Strophe.TimedHandler(period, handler);\n                this.addTimeds.push(thand);\n                return thand;\n              },\n\n              /** Function: deleteTimedHandler\n               *  Delete a timed handler for a connection.\n               *\n               *  This function removes a timed handler from the connection.  The\n               *  handRef parameter is *not* the function passed to addTimedHandler(),\n               *  but is the reference returned from addTimedHandler().\n               *\n               *  Parameters:\n               *    (Strophe.TimedHandler) handRef - The handler reference.\n               */\n              deleteTimedHandler: function deleteTimedHandler(handRef) {\n                // this must be done in the Idle loop so that we don't change\n                // the handlers during iteration\n                this.removeTimeds.push(handRef);\n              },\n\n              /** Function: addHandler\n               *  Add a stanza handler for the connection.\n               *\n               *  This function adds a stanza handler to the connection.  The\n               *  handler callback will be called for any stanza that matches\n               *  the parameters.  Note that if multiple parameters are supplied,\n               *  they must all match for the handler to be invoked.\n               *\n               *  The handler will receive the stanza that triggered it as its argument.\n               *  *The handler should return true if it is to be invoked again;\n               *  returning false will remove the handler after it returns.*\n               *\n               *  As a convenience, the ns parameters applies to the top level element\n               *  and also any of its immediate children.  This is primarily to make\n               *  matching /iq/query elements easy.\n               *\n               *  Options\n               *  ~~~~~~~\n               *  With the options argument, you can specify boolean flags that affect how\n               *  matches are being done.\n               *\n               *  Currently two flags exist:\n               *\n               *  - matchBareFromJid:\n               *      When set to true, the from parameter and the\n               *      from attribute on the stanza will be matched as bare JIDs instead\n               *      of full JIDs. To use this, pass {matchBareFromJid: true} as the\n               *      value of options. The default value for matchBareFromJid is false.\n               *\n               *  - ignoreNamespaceFragment:\n               *      When set to true, a fragment specified on the stanza's namespace\n               *      URL will be ignored when it's matched with the one configured for\n               *      the handler.\n               *\n               *      This means that if you register like this:\n               *      >   connection.addHandler(\n               *      >       handler,\n               *      >       'http://jabber.org/protocol/muc',\n               *      >       null, null, null, null,\n               *      >       {'ignoreNamespaceFragment': true}\n               *      >   );\n               *\n               *      Then a stanza with XML namespace of\n               *      'http://jabber.org/protocol/muc#user' will also be matched. If\n               *      'ignoreNamespaceFragment' is false, then only stanzas with\n               *      'http://jabber.org/protocol/muc' will be matched.\n               *\n               *  Deleting the handler\n               *  ~~~~~~~~~~~~~~~~~~~~\n               *  The return value should be saved if you wish to remove the handler\n               *  with deleteHandler().\n               *\n               *  Parameters:\n               *    (Function) handler - The user callback.\n               *    (String) ns - The namespace to match.\n               *    (String) name - The stanza name to match.\n               *    (String|Array) type - The stanza type (or types if an array) to match.\n               *    (String) id - The stanza id attribute to match.\n               *    (String) from - The stanza from attribute to match.\n               *    (String) options - The handler options\n               *\n               *  Returns:\n               *    A reference to the handler that can be used to remove it.\n               */\n              addHandler: function addHandler(handler, ns, name, type, id, from, options) {\n                var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);\n                this.addHandlers.push(hand);\n                return hand;\n              },\n\n              /** Function: deleteHandler\n               *  Delete a stanza handler for a connection.\n               *\n               *  This function removes a stanza handler from the connection.  The\n               *  handRef parameter is *not* the function passed to addHandler(),\n               *  but is the reference returned from addHandler().\n               *\n               *  Parameters:\n               *    (Strophe.Handler) handRef - The handler reference.\n               */\n              deleteHandler: function deleteHandler(handRef) {\n                // this must be done in the Idle loop so that we don't change\n                // the handlers during iteration\n                this.removeHandlers.push(handRef); // If a handler is being deleted while it is being added,\n                // prevent it from getting added\n\n                var i = this.addHandlers.indexOf(handRef);\n\n                if (i >= 0) {\n                  this.addHandlers.splice(i, 1);\n                }\n              },\n\n              /** Function: registerSASLMechanisms\n               *\n               * Register the SASL mechanisms which will be supported by this instance of\n               * Strophe.Connection (i.e. which this XMPP client will support).\n               *\n               *  Parameters:\n               *    (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes\n               *\n               */\n              registerSASLMechanisms: function registerSASLMechanisms(mechanisms) {\n                this.mechanisms = {};\n                mechanisms = mechanisms || [Strophe.SASLAnonymous, Strophe.SASLExternal, Strophe.SASLMD5, Strophe.SASLOAuthBearer, Strophe.SASLXOAuth2, Strophe.SASLPlain, Strophe.SASLSHA1];\n                mechanisms.forEach(this.registerSASLMechanism.bind(this));\n              },\n\n              /** Function: registerSASLMechanism\n               *\n               * Register a single SASL mechanism, to be supported by this client.\n               *\n               *  Parameters:\n               *    (Object) mechanism - Object with a Strophe.SASLMechanism prototype\n               *\n               */\n              registerSASLMechanism: function registerSASLMechanism(mechanism) {\n                this.mechanisms[mechanism.prototype.name] = mechanism;\n              },\n\n              /** Function: disconnect\n               *  Start the graceful disconnection process.\n               *\n               *  This function starts the disconnection process.  This process starts\n               *  by sending unavailable presence and sending BOSH body of type\n               *  terminate.  A timeout handler makes sure that disconnection happens\n               *  even if the BOSH server does not respond.\n               *  If the Connection object isn't connected, at least tries to abort all pending requests\n               *  so the connection object won't generate successful requests (which were already opened).\n               *\n               *  The user supplied connection callback will be notified of the\n               *  progress as this process happens.\n               *\n               *  Parameters:\n               *    (String) reason - The reason the disconnect is occuring.\n               */\n              disconnect: function disconnect(reason) {\n                this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);\n\n                Strophe.warn(\"Disconnect was called because: \" + reason);\n\n                if (this.connected) {\n                  var pres = false;\n                  this.disconnecting = true;\n\n                  if (this.authenticated) {\n                    pres = $pres({\n                      'xmlns': Strophe.NS.CLIENT,\n                      'type': 'unavailable'\n                    });\n                  } // setup timeout handler\n\n\n                  this._disconnectTimeout = this._addSysTimedHandler(3000, this._onDisconnectTimeout.bind(this));\n\n                  this._proto._disconnect(pres);\n                } else {\n                  Strophe.warn(\"Disconnect was called before Strophe connected to the server\");\n\n                  this._proto._abortAllRequests();\n\n                  this._doDisconnect();\n                }\n              },\n\n              /** PrivateFunction: _changeConnectStatus\n               *  _Private_ helper function that makes sure plugins and the user's\n               *  callback are notified of connection status changes.\n               *\n               *  Parameters:\n               *    (Integer) status - the new connection status, one of the values\n               *      in Strophe.Status\n               *    (String) condition - the error condition or null\n               *    (XMLElement) elem - The triggering stanza.\n               */\n              _changeConnectStatus: function _changeConnectStatus(status, condition, elem) {\n                // notify all plugins listening for status changes\n                for (var k in Strophe._connectionPlugins) {\n                  if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {\n                    var plugin = this[k];\n\n                    if (plugin.statusChanged) {\n                      try {\n                        plugin.statusChanged(status, condition);\n                      } catch (err) {\n                        Strophe.error(\"\".concat(k, \" plugin caused an exception changing status: \").concat(err));\n                      }\n                    }\n                  }\n                } // notify the user's callback\n\n\n                if (this.connect_callback) {\n                  try {\n                    this.connect_callback(status, condition, elem);\n                  } catch (e) {\n                    Strophe._handleError(e);\n\n                    Strophe.error(\"User connection callback caused an exception: \".concat(e));\n                  }\n                }\n              },\n\n              /** PrivateFunction: _doDisconnect\n               *  _Private_ function to disconnect.\n               *\n               *  This is the last piece of the disconnection logic.  This resets the\n               *  connection and alerts the user's connection callback.\n               */\n              _doDisconnect: function _doDisconnect(condition) {\n                if (typeof this._idleTimeout === \"number\") {\n                  clearTimeout(this._idleTimeout);\n                } // Cancel Disconnect Timeout\n\n\n                if (this._disconnectTimeout !== null) {\n                  this.deleteTimedHandler(this._disconnectTimeout);\n                  this._disconnectTimeout = null;\n                }\n\n                Strophe.debug(\"_doDisconnect was called\");\n\n                this._proto._doDisconnect();\n\n                this.authenticated = false;\n                this.disconnecting = false;\n                this.restored = false; // delete handlers\n\n                this.handlers = [];\n                this.timedHandlers = [];\n                this.removeTimeds = [];\n                this.removeHandlers = [];\n                this.addTimeds = [];\n                this.addHandlers = []; // tell the parent we disconnected\n\n                this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);\n\n                this.connected = false;\n              },\n\n              /** PrivateFunction: _dataRecv\n               *  _Private_ handler to processes incoming data from the the connection.\n               *\n               *  Except for _connect_cb handling the initial connection request,\n               *  this function handles the incoming data for all requests.  This\n               *  function also fires stanza handlers that match each incoming\n               *  stanza.\n               *\n               *  Parameters:\n               *    (Strophe.Request) req - The request that has data ready.\n               *    (string) req - The stanza a raw string (optiona).\n               */\n              _dataRecv: function _dataRecv(req, raw) {\n                var _this6 = this;\n\n                Strophe.debug(\"_dataRecv called\");\n\n                var elem = this._proto._reqToData(req);\n\n                if (elem === null) {\n                  return;\n                }\n\n                if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {\n                  if (elem.nodeName === this._proto.strip && elem.childNodes.length) {\n                    this.xmlInput(elem.childNodes[0]);\n                  } else {\n                    this.xmlInput(elem);\n                  }\n                }\n\n                if (this.rawInput !== Strophe.Connection.prototype.rawInput) {\n                  if (raw) {\n                    this.rawInput(raw);\n                  } else {\n                    this.rawInput(Strophe.serialize(elem));\n                  }\n                } // remove handlers scheduled for deletion\n\n\n                while (this.removeHandlers.length > 0) {\n                  var hand = this.removeHandlers.pop();\n                  var i = this.handlers.indexOf(hand);\n\n                  if (i >= 0) {\n                    this.handlers.splice(i, 1);\n                  }\n                } // add handlers scheduled for addition\n\n\n                while (this.addHandlers.length > 0) {\n                  this.handlers.push(this.addHandlers.pop());\n                } // handle graceful disconnect\n\n\n                if (this.disconnecting && this._proto._emptyQueue()) {\n                  this._doDisconnect();\n\n                  return;\n                }\n\n                var type = elem.getAttribute(\"type\");\n\n                if (type !== null && type === \"terminate\") {\n                  // Don't process stanzas that come in after disconnect\n                  if (this.disconnecting) {\n                    return;\n                  } // an error occurred\n\n\n                  var cond = elem.getAttribute(\"condition\");\n                  var conflict = elem.getElementsByTagName(\"conflict\");\n\n                  if (cond !== null) {\n                    if (cond === \"remote-stream-error\" && conflict.length > 0) {\n                      cond = \"conflict\";\n                    }\n\n                    this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);\n                  } else {\n                    this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.UNKOWN_REASON);\n                  }\n\n                  this._doDisconnect(cond);\n\n                  return;\n                } // send each incoming stanza through the handler chain\n\n\n                Strophe.forEachChild(elem, null, function (child) {\n                  // process handlers\n                  var newList = _this6.handlers;\n                  _this6.handlers = [];\n\n                  for (var _i5 = 0; _i5 < newList.length; _i5++) {\n                    var _hand = newList[_i5]; // encapsulate 'handler.run' not to lose the whole handler list if\n                    // one of the handlers throws an exception\n\n                    try {\n                      if (_hand.isMatch(child) && (_this6.authenticated || !_hand.user)) {\n                        if (_hand.run(child)) {\n                          _this6.handlers.push(_hand);\n                        }\n                      } else {\n                        _this6.handlers.push(_hand);\n                      }\n                    } catch (e) {\n                      // if the handler throws an exception, we consider it as false\n                      Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);\n                    }\n                  }\n                });\n              },\n\n              /** Attribute: mechanisms\n               *  SASL Mechanisms available for Connection.\n               */\n              mechanisms: {},\n\n              /** PrivateFunction: _connect_cb\n               *  _Private_ handler for initial connection request.\n               *\n               *  This handler is used to process the initial connection request\n               *  response from the BOSH server. It is used to set up authentication\n               *  handlers and start the authentication process.\n               *\n               *  SASL authentication will be attempted if available, otherwise\n               *  the code will fall back to legacy authentication.\n               *\n               *  Parameters:\n               *    (Strophe.Request) req - The current request.\n               *    (Function) _callback - low level (xmpp) connect callback function.\n               *      Useful for plugins with their own xmpp connect callback (when they\n               *      want to do something special).\n               */\n              _connect_cb: function _connect_cb(req, _callback, raw) {\n                Strophe.debug(\"_connect_cb was called\");\n                this.connected = true;\n                var bodyWrap;\n\n                try {\n                  bodyWrap = this._proto._reqToData(req);\n                } catch (e) {\n                  if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) {\n                    throw e;\n                  }\n\n                  this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.BAD_FORMAT);\n\n                  this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);\n                }\n\n                if (!bodyWrap) {\n                  return;\n                }\n\n                if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {\n                  if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {\n                    this.xmlInput(bodyWrap.childNodes[0]);\n                  } else {\n                    this.xmlInput(bodyWrap);\n                  }\n                }\n\n                if (this.rawInput !== Strophe.Connection.prototype.rawInput) {\n                  if (raw) {\n                    this.rawInput(raw);\n                  } else {\n                    this.rawInput(Strophe.serialize(bodyWrap));\n                  }\n                }\n\n                var conncheck = this._proto._connect_cb(bodyWrap);\n\n                if (conncheck === Strophe.Status.CONNFAIL) {\n                  return;\n                } // Check for the stream:features tag\n\n\n                var hasFeatures;\n\n                if (bodyWrap.getElementsByTagNameNS) {\n                  hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, \"features\").length > 0;\n                } else {\n                  hasFeatures = bodyWrap.getElementsByTagName(\"stream:features\").length > 0 || bodyWrap.getElementsByTagName(\"features\").length > 0;\n                }\n\n                if (!hasFeatures) {\n                  this._proto._no_auth_received(_callback);\n\n                  return;\n                }\n\n                var matched = [];\n                var mechanisms = bodyWrap.getElementsByTagName(\"mechanism\");\n\n                if (mechanisms.length > 0) {\n                  for (var i = 0; i < mechanisms.length; i++) {\n                    var mech = Strophe.getText(mechanisms[i]);\n                    if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);\n                  }\n                }\n\n                if (matched.length === 0) {\n                  if (bodyWrap.getElementsByTagName(\"auth\").length === 0) {\n                    // There are no matching SASL mechanisms and also no legacy\n                    // auth available.\n                    this._proto._no_auth_received(_callback);\n\n                    return;\n                  }\n                }\n\n                if (this.do_authentication !== false) {\n                  this.authenticate(matched);\n                }\n              },\n\n              /** Function: sortMechanismsByPriority\n               *\n               *  Sorts an array of objects with prototype SASLMechanism according to\n               *  their priorities.\n               *\n               *  Parameters:\n               *    (Array) mechanisms - Array of SASL mechanisms.\n               *\n               */\n              sortMechanismsByPriority: function sortMechanismsByPriority(mechanisms) {\n                // Sorting mechanisms according to priority.\n                for (var i = 0; i < mechanisms.length - 1; ++i) {\n                  var higher = i;\n\n                  for (var j = i + 1; j < mechanisms.length; ++j) {\n                    if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) {\n                      higher = j;\n                    }\n                  }\n\n                  if (higher !== i) {\n                    var swap = mechanisms[i];\n                    mechanisms[i] = mechanisms[higher];\n                    mechanisms[higher] = swap;\n                  }\n                }\n\n                return mechanisms;\n              },\n\n              /** Function: authenticate\n               * Set up authentication\n               *\n               *  Continues the initial connection request by setting up authentication\n               *  handlers and starting the authentication process.\n               *\n               *  SASL authentication will be attempted if available, otherwise\n               *  the code will fall back to legacy authentication.\n               *\n               *  Parameters:\n               *    (Array) matched - Array of SASL mechanisms supported.\n               *\n               */\n              authenticate: function authenticate(matched) {\n                if (!this._attemptSASLAuth(matched)) {\n                  this._attemptLegacyAuth();\n                }\n              },\n\n              /** PrivateFunction: _attemptSASLAuth\n               *\n               *  Iterate through an array of SASL mechanisms and attempt authentication\n               *  with the highest priority (enabled) mechanism.\n               *\n               *  Parameters:\n               *    (Array) mechanisms - Array of SASL mechanisms.\n               *\n               *  Returns:\n               *    (Boolean) mechanism_found - true or false, depending on whether a\n               *          valid SASL mechanism was found with which authentication could be\n               *          started.\n               */\n              _attemptSASLAuth: function _attemptSASLAuth(mechanisms) {\n                mechanisms = this.sortMechanismsByPriority(mechanisms || []);\n                var mechanism_found = false;\n\n                for (var i = 0; i < mechanisms.length; ++i) {\n                  if (!mechanisms[i].prototype.test(this)) {\n                    continue;\n                  }\n\n                  this._sasl_success_handler = this._addSysHandler(this._sasl_success_cb.bind(this), null, \"success\", null, null);\n                  this._sasl_failure_handler = this._addSysHandler(this._sasl_failure_cb.bind(this), null, \"failure\", null, null);\n                  this._sasl_challenge_handler = this._addSysHandler(this._sasl_challenge_cb.bind(this), null, \"challenge\", null, null);\n                  this._sasl_mechanism = new mechanisms[i]();\n\n                  this._sasl_mechanism.onStart(this);\n\n                  var request_auth_exchange = $build(\"auth\", {\n                    'xmlns': Strophe.NS.SASL,\n                    'mechanism': this._sasl_mechanism.name\n                  });\n\n                  if (this._sasl_mechanism.isClientFirst) {\n                    var response = this._sasl_mechanism.onChallenge(this, null);\n\n                    request_auth_exchange.t(btoa(response));\n                  }\n\n                  this.send(request_auth_exchange.tree());\n                  mechanism_found = true;\n                  break;\n                }\n\n                return mechanism_found;\n              },\n\n              /** PrivateFunction: _sasl_challenge_cb\n               *  _Private_ handler for the SASL challenge\n               *\n               */\n              _sasl_challenge_cb: function _sasl_challenge_cb(elem) {\n                var challenge = atob(Strophe.getText(elem));\n\n                var response = this._sasl_mechanism.onChallenge(this, challenge);\n\n                var stanza = $build('response', {\n                  'xmlns': Strophe.NS.SASL\n                });\n\n                if (response !== \"\") {\n                  stanza.t(btoa(response));\n                }\n\n                this.send(stanza.tree());\n                return true;\n              },\n\n              /** PrivateFunction: _attemptLegacyAuth\n               *\n               *  Attempt legacy (i.e. non-SASL) authentication.\n               */\n              _attemptLegacyAuth: function _attemptLegacyAuth() {\n                if (Strophe.getNodeFromJid(this.jid) === null) {\n                  // we don't have a node, which is required for non-anonymous\n                  // client connections\n                  this._changeConnectStatus(Strophe.Status.CONNFAIL, Strophe.ErrorCondition.MISSING_JID_NODE);\n\n                  this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);\n                } else {\n                  // Fall back to legacy authentication\n                  this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);\n\n                  this._addSysHandler(this._onLegacyAuthIQResult.bind(this), null, null, null, \"_auth_1\");\n\n                  this.send($iq({\n                    'type': \"get\",\n                    'to': this.domain,\n                    'id': \"_auth_1\"\n                  }).c(\"query\", {\n                    xmlns: Strophe.NS.AUTH\n                  }).c(\"username\", {}).t(Strophe.getNodeFromJid(this.jid)).tree());\n                }\n              },\n\n              /** PrivateFunction: _onLegacyAuthIQResult\n               *  _Private_ handler for legacy authentication.\n               *\n               *  This handler is called in response to the initial <iq type='get'/>\n               *  for legacy authentication.  It builds an authentication <iq/> and\n               *  sends it, creating a handler (calling back to _auth2_cb()) to\n               *  handle the result\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The stanza that triggered the callback.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _onLegacyAuthIQResult: function _onLegacyAuthIQResult(elem) {\n                // build plaintext auth iq\n                var iq = $iq({\n                  type: \"set\",\n                  id: \"_auth_2\"\n                }).c('query', {\n                  xmlns: Strophe.NS.AUTH\n                }).c('username', {}).t(Strophe.getNodeFromJid(this.jid)).up().c('password').t(this.pass);\n\n                if (!Strophe.getResourceFromJid(this.jid)) {\n                  // since the user has not supplied a resource, we pick\n                  // a default one here.  unlike other auth methods, the server\n                  // cannot do this for us.\n                  this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';\n                }\n\n                iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));\n\n                this._addSysHandler(this._auth2_cb.bind(this), null, null, null, \"_auth_2\");\n\n                this.send(iq.tree());\n                return false;\n              },\n\n              /** PrivateFunction: _sasl_success_cb\n               *  _Private_ handler for succesful SASL authentication.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The matching stanza.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _sasl_success_cb: function _sasl_success_cb(elem) {\n                var _this7 = this;\n\n                if (this._sasl_data[\"server-signature\"]) {\n                  var serverSignature;\n                  var success = atob(Strophe.getText(elem));\n                  var attribMatch = /([a-z]+)=([^,]+)(,|$)/;\n                  var matches = success.match(attribMatch);\n\n                  if (matches[1] === \"v\") {\n                    serverSignature = matches[2];\n                  }\n\n                  if (serverSignature !== this._sasl_data[\"server-signature\"]) {\n                    // remove old handlers\n                    this.deleteHandler(this._sasl_failure_handler);\n                    this._sasl_failure_handler = null;\n\n                    if (this._sasl_challenge_handler) {\n                      this.deleteHandler(this._sasl_challenge_handler);\n                      this._sasl_challenge_handler = null;\n                    }\n\n                    this._sasl_data = {};\n                    return this._sasl_failure_cb(null);\n                  }\n                }\n\n                Strophe.info(\"SASL authentication succeeded.\");\n\n                if (this._sasl_mechanism) {\n                  this._sasl_mechanism.onSuccess();\n                } // remove old handlers\n\n\n                this.deleteHandler(this._sasl_failure_handler);\n                this._sasl_failure_handler = null;\n\n                if (this._sasl_challenge_handler) {\n                  this.deleteHandler(this._sasl_challenge_handler);\n                  this._sasl_challenge_handler = null;\n                }\n\n                var streamfeature_handlers = [];\n\n                var wrapper = function wrapper(handlers, elem) {\n                  while (handlers.length) {\n                    _this7.deleteHandler(handlers.pop());\n                  }\n\n                  _this7._onStreamFeaturesAfterSASL(elem);\n\n                  return false;\n                };\n\n                streamfeature_handlers.push(this._addSysHandler(function (elem) {\n                  return wrapper(streamfeature_handlers, elem);\n                }, null, \"stream:features\", null, null));\n                streamfeature_handlers.push(this._addSysHandler(function (elem) {\n                  return wrapper(streamfeature_handlers, elem);\n                }, Strophe.NS.STREAM, \"features\", null, null)); // we must send an xmpp:restart now\n\n                this._sendRestart();\n\n                return false;\n              },\n\n              /** PrivateFunction: _onStreamFeaturesAfterSASL\n               *  Parameters:\n               *    (XMLElement) elem - The matching stanza.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _onStreamFeaturesAfterSASL: function _onStreamFeaturesAfterSASL(elem) {\n                // save stream:features for future usage\n                this.features = elem;\n\n                for (var i = 0; i < elem.childNodes.length; i++) {\n                  var child = elem.childNodes[i];\n\n                  if (child.nodeName === 'bind') {\n                    this.do_bind = true;\n                  }\n\n                  if (child.nodeName === 'session') {\n                    this.do_session = true;\n                  }\n                }\n\n                if (!this.do_bind) {\n                  this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);\n\n                  return false;\n                } else if (!this.options.explicitResourceBinding) {\n                  this.bind();\n                } else {\n                  this._changeConnectStatus(Strophe.Status.BINDREQUIRED, null);\n                }\n\n                return false;\n              },\n\n              /** Function: bind\n               *\n               *  Sends an IQ to the XMPP server to bind a JID resource for this session.\n               *\n               *  https://tools.ietf.org/html/rfc6120#section-7.5\n               *\n               *  If `explicitResourceBinding` was set to a truthy value in the options\n               *  passed to the Strophe.Connection constructor, then this function needs\n               *  to be called explicitly by the client author.\n               *\n               *  Otherwise it'll be called automatically as soon as the XMPP server\n               *  advertises the \"urn:ietf:params:xml:ns:xmpp-bind\" stream feature.\n               */\n              bind: function bind() {\n                if (!this.do_bind) {\n                  Strophe.log(Strophe.LogLevel.INFO, \"Strophe.Connection.prototype.bind called but \\\"do_bind\\\" is false\");\n                  return;\n                }\n\n                this._addSysHandler(this._onResourceBindResultIQ.bind(this), null, null, null, \"_bind_auth_2\");\n\n                var resource = Strophe.getResourceFromJid(this.jid);\n\n                if (resource) {\n                  this.send($iq({\n                    type: \"set\",\n                    id: \"_bind_auth_2\"\n                  }).c('bind', {\n                    xmlns: Strophe.NS.BIND\n                  }).c('resource', {}).t(resource).tree());\n                } else {\n                  this.send($iq({\n                    type: \"set\",\n                    id: \"_bind_auth_2\"\n                  }).c('bind', {\n                    xmlns: Strophe.NS.BIND\n                  }).tree());\n                }\n              },\n\n              /** PrivateFunction: _onResourceBindIQ\n               *  _Private_ handler for binding result and session start.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The matching stanza.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _onResourceBindResultIQ: function _onResourceBindResultIQ(elem) {\n                if (elem.getAttribute(\"type\") === \"error\") {\n                  Strophe.warn(\"Resource binding failed.\");\n                  var conflict = elem.getElementsByTagName(\"conflict\");\n                  var condition;\n\n                  if (conflict.length > 0) {\n                    condition = Strophe.ErrorCondition.CONFLICT;\n                  }\n\n                  this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);\n\n                  return false;\n                } // TODO - need to grab errors\n\n\n                var bind = elem.getElementsByTagName(\"bind\");\n\n                if (bind.length > 0) {\n                  var jidNode = bind[0].getElementsByTagName(\"jid\");\n\n                  if (jidNode.length > 0) {\n                    this.jid = Strophe.getText(jidNode[0]);\n\n                    if (this.do_session) {\n                      this._establishSession();\n                    } else {\n                      this.authenticated = true;\n\n                      this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n                    }\n                  }\n                } else {\n                  Strophe.warn(\"Resource binding failed.\");\n\n                  this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n                  return false;\n                }\n              },\n\n              /** PrivateFunction: _establishSession\n               *  Send IQ request to establish a session with the XMPP server.\n               *\n               *  See https://xmpp.org/rfcs/rfc3921.html#session\n               *\n               *  Note: The protocol for session establishment has been determined as\n               *  unnecessary and removed in RFC-6121.\n               */\n              _establishSession: function _establishSession() {\n                if (!this.do_session) {\n                  throw new Error(\"Strophe.Connection.prototype._establishSession \" + \"called but apparently \".concat(Strophe.NS.SESSION, \" wasn't advertised by the server\"));\n                }\n\n                this._addSysHandler(this._onSessionResultIQ.bind(this), null, null, null, \"_session_auth_2\");\n\n                this.send($iq({\n                  type: \"set\",\n                  id: \"_session_auth_2\"\n                }).c('session', {\n                  xmlns: Strophe.NS.SESSION\n                }).tree());\n              },\n\n              /** PrivateFunction: _onSessionResultIQ\n               *  _Private_ handler for the server's IQ response to a client's session\n               *  request.\n               *\n               *  This sets Connection.authenticated to true on success, which\n               *  starts the processing of user handlers.\n               *\n               *  See https://xmpp.org/rfcs/rfc3921.html#session\n               *\n               *  Note: The protocol for session establishment has been determined as\n               *  unnecessary and removed in RFC-6121.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The matching stanza.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _onSessionResultIQ: function _onSessionResultIQ(elem) {\n                if (elem.getAttribute(\"type\") === \"result\") {\n                  this.authenticated = true;\n\n                  this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n                } else if (elem.getAttribute(\"type\") === \"error\") {\n                  Strophe.warn(\"Session creation failed.\");\n\n                  this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n                  return false;\n                }\n\n                return false;\n              },\n\n              /** PrivateFunction: _sasl_failure_cb\n               *  _Private_ handler for SASL authentication failure.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The matching stanza.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _sasl_failure_cb: function _sasl_failure_cb(elem) {\n                // delete unneeded handlers\n                if (this._sasl_success_handler) {\n                  this.deleteHandler(this._sasl_success_handler);\n                  this._sasl_success_handler = null;\n                }\n\n                if (this._sasl_challenge_handler) {\n                  this.deleteHandler(this._sasl_challenge_handler);\n                  this._sasl_challenge_handler = null;\n                }\n\n                if (this._sasl_mechanism) this._sasl_mechanism.onFailure();\n\n                this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n                return false;\n              },\n\n              /** PrivateFunction: _auth2_cb\n               *  _Private_ handler to finish legacy authentication.\n               *\n               *  This handler is called when the result from the jabber:iq:auth\n               *  <iq/> stanza is returned.\n               *\n               *  Parameters:\n               *    (XMLElement) elem - The stanza that triggered the callback.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _auth2_cb: function _auth2_cb(elem) {\n                if (elem.getAttribute(\"type\") === \"result\") {\n                  this.authenticated = true;\n\n                  this._changeConnectStatus(Strophe.Status.CONNECTED, null);\n                } else if (elem.getAttribute(\"type\") === \"error\") {\n                  this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);\n\n                  this.disconnect('authentication failed');\n                }\n\n                return false;\n              },\n\n              /** PrivateFunction: _addSysTimedHandler\n               *  _Private_ function to add a system level timed handler.\n               *\n               *  This function is used to add a Strophe.TimedHandler for the\n               *  library code.  System timed handlers are allowed to run before\n               *  authentication is complete.\n               *\n               *  Parameters:\n               *    (Integer) period - The period of the handler.\n               *    (Function) handler - The callback function.\n               */\n              _addSysTimedHandler: function _addSysTimedHandler(period, handler) {\n                var thand = new Strophe.TimedHandler(period, handler);\n                thand.user = false;\n                this.addTimeds.push(thand);\n                return thand;\n              },\n\n              /** PrivateFunction: _addSysHandler\n               *  _Private_ function to add a system level stanza handler.\n               *\n               *  This function is used to add a Strophe.Handler for the\n               *  library code.  System stanza handlers are allowed to run before\n               *  authentication is complete.\n               *\n               *  Parameters:\n               *    (Function) handler - The callback function.\n               *    (String) ns - The namespace to match.\n               *    (String) name - The stanza name to match.\n               *    (String) type - The stanza type attribute to match.\n               *    (String) id - The stanza id attribute to match.\n               */\n              _addSysHandler: function _addSysHandler(handler, ns, name, type, id) {\n                var hand = new Strophe.Handler(handler, ns, name, type, id);\n                hand.user = false;\n                this.addHandlers.push(hand);\n                return hand;\n              },\n\n              /** PrivateFunction: _onDisconnectTimeout\n               *  _Private_ timeout handler for handling non-graceful disconnection.\n               *\n               *  If the graceful disconnect process does not complete within the\n               *  time allotted, this handler finishes the disconnect anyway.\n               *\n               *  Returns:\n               *    false to remove the handler.\n               */\n              _onDisconnectTimeout: function _onDisconnectTimeout() {\n                Strophe.debug(\"_onDisconnectTimeout was called\");\n\n                this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);\n\n                this._proto._onDisconnectTimeout(); // actually disconnect\n\n\n                this._doDisconnect();\n\n                return false;\n              },\n\n              /** PrivateFunction: _onIdle\n               *  _Private_ handler to process events during idle cycle.\n               *\n               *  This handler is called every 100ms to fire timed handlers that\n               *  are ready and keep poll requests going.\n               */\n              _onIdle: function _onIdle() {\n                var _this8 = this;\n\n                // add timed handlers scheduled for addition\n                // NOTE: we add before remove in the case a timed handler is\n                // added and then deleted before the next _onIdle() call.\n                while (this.addTimeds.length > 0) {\n                  this.timedHandlers.push(this.addTimeds.pop());\n                } // remove timed handlers that have been scheduled for deletion\n\n\n                while (this.removeTimeds.length > 0) {\n                  var thand = this.removeTimeds.pop();\n                  var i = this.timedHandlers.indexOf(thand);\n\n                  if (i >= 0) {\n                    this.timedHandlers.splice(i, 1);\n                  }\n                } // call ready timed handlers\n\n\n                var now = new Date().getTime();\n                var newList = [];\n\n                for (var _i6 = 0; _i6 < this.timedHandlers.length; _i6++) {\n                  var _thand = this.timedHandlers[_i6];\n\n                  if (this.authenticated || !_thand.user) {\n                    var since = _thand.lastCalled + _thand.period;\n\n                    if (since - now <= 0) {\n                      if (_thand.run()) {\n                        newList.push(_thand);\n                      }\n                    } else {\n                      newList.push(_thand);\n                    }\n                  }\n                }\n\n                this.timedHandlers = newList;\n                clearTimeout(this._idleTimeout);\n\n                this._proto._onIdle(); // reactivate the timer only if connected\n\n\n                if (this.connected) {\n                  this._idleTimeout = setTimeout(function () {\n                    return _this8._onIdle();\n                  }, 100);\n                }\n              }\n            };\n            /** Class: Strophe.SASLMechanism\n             *\n             *  encapsulates SASL authentication mechanisms.\n             *\n             *  User code may override the priority for each mechanism or disable it completely.\n             *  See <priority> for information about changing priority and <test> for informatian on\n             *  how to disable a mechanism.\n             *\n             *  By default, all mechanisms are enabled and the priorities are\n             *\n             *      OAUTHBEARER - 60\n             *      SCRAM-SHA1 - 50\n             *      DIGEST-MD5 - 40\n             *      PLAIN - 30\n             *      ANONYMOUS - 20\n             *      EXTERNAL - 10\n             *\n             *  See: Strophe.Connection.addSupportedSASLMechanisms\n             */\n\n            /**\n             * PrivateConstructor: Strophe.SASLMechanism\n             * SASL auth mechanism abstraction.\n             *\n             *  Parameters:\n             *    (String) name - SASL Mechanism name.\n             *    (Boolean) isClientFirst - If client should send response first without challenge.\n             *    (Number) priority - Priority.\n             *\n             *  Returns:\n             *    A new Strophe.SASLMechanism object.\n             */\n\n            Strophe.SASLMechanism = function (name, isClientFirst, priority) {\n              /** PrivateVariable: name\n               *  Mechanism name.\n               */\n              this.name = name;\n              /** PrivateVariable: isClientFirst\n               *  If client sends response without initial server challenge.\n               */\n\n              this.isClientFirst = isClientFirst;\n              /** Variable: priority\n               *  Determines which <SASLMechanism> is chosen for authentication (Higher is better).\n               *  Users may override this to prioritize mechanisms differently.\n               *\n               *  In the default configuration the priorities are\n               *\n               *  SCRAM-SHA1 - 40\n               *  DIGEST-MD5 - 30\n               *  Plain - 20\n               *\n               *  Example: (This will cause Strophe to choose the mechanism that the server sent first)\n               *\n               *  > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;\n               *\n               *  See <SASL mechanisms> for a list of available mechanisms.\n               *\n               */\n\n              this.priority = priority;\n            };\n\n            Strophe.SASLMechanism.prototype = {\n              /**\n               *  Function: test\n               *  Checks if mechanism able to run.\n               *  To disable a mechanism, make this return false;\n               *\n               *  To disable plain authentication run\n               *  > Strophe.SASLPlain.test = function() {\n               *  >   return false;\n               *  > }\n               *\n               *  See <SASL mechanisms> for a list of available mechanisms.\n               *\n               *  Parameters:\n               *    (Strophe.Connection) connection - Target Connection.\n               *\n               *  Returns:\n               *    (Boolean) If mechanism was able to run.\n               */\n              test: function test(connection) {\n                return true;\n              },\n\n              /** PrivateFunction: onStart\n               *  Called before starting mechanism on some connection.\n               *\n               *  Parameters:\n               *    (Strophe.Connection) connection - Target Connection.\n               */\n              onStart: function onStart(connection) {\n                this._connection = connection;\n              },\n\n              /** PrivateFunction: onChallenge\n               *  Called by protocol implementation on incoming challenge. If client is\n               *  first (isClientFirst === true) challenge will be null on the first call.\n               *\n               *  Parameters:\n               *    (Strophe.Connection) connection - Target Connection.\n               *    (String) challenge - current challenge to handle.\n               *\n               *  Returns:\n               *    (String) Mechanism response.\n               */\n              onChallenge: function onChallenge(connection, challenge) {\n                throw new Error(\"You should implement challenge handling!\");\n              },\n\n              /** PrivateFunction: onFailure\n               *  Protocol informs mechanism implementation about SASL failure.\n               */\n              onFailure: function onFailure() {\n                this._connection = null;\n              },\n\n              /** PrivateFunction: onSuccess\n               *  Protocol informs mechanism implementation about SASL success.\n               */\n              onSuccess: function onSuccess() {\n                this._connection = null;\n              }\n            };\n            /** Constants: SASL mechanisms\n             *  Available authentication mechanisms\n             *\n             *  Strophe.SASLAnonymous - SASL ANONYMOUS authentication.\n             *  Strophe.SASLPlain - SASL PLAIN authentication.\n             *  Strophe.SASLMD5 - SASL DIGEST-MD5 authentication\n             *  Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication\n             *  Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication\n             *  Strophe.SASLExternal - SASL EXTERNAL authentication\n             *  Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication\n             */\n            // Building SASL callbacks\n\n            /** PrivateConstructor: SASLAnonymous\n             *  SASL ANONYMOUS authentication.\n             */\n\n            Strophe.SASLAnonymous = function () {};\n\n            Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism(\"ANONYMOUS\", false, 20);\n\n            Strophe.SASLAnonymous.prototype.test = function (connection) {\n              return connection.authcid === null;\n            };\n            /** PrivateConstructor: SASLPlain\n             *  SASL PLAIN authentication.\n             */\n\n\n            Strophe.SASLPlain = function () {};\n\n            Strophe.SASLPlain.prototype = new Strophe.SASLMechanism(\"PLAIN\", true, 50);\n\n            Strophe.SASLPlain.prototype.test = function (connection) {\n              return connection.authcid !== null;\n            };\n\n            Strophe.SASLPlain.prototype.onChallenge = function (connection) {\n              var auth_str = connection.authzid;\n              auth_str = auth_str + \"\\0\";\n              auth_str = auth_str + connection.authcid;\n              auth_str = auth_str + \"\\0\";\n              auth_str = auth_str + connection.pass;\n              return utils.utf16to8(auth_str);\n            };\n            /** PrivateConstructor: SASLSHA1\n             *  SASL SCRAM SHA 1 authentication.\n             */\n\n\n            Strophe.SASLSHA1 = function () {};\n\n            Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism(\"SCRAM-SHA-1\", true, 70);\n\n            Strophe.SASLSHA1.prototype.test = function (connection) {\n              return connection.authcid !== null;\n            };\n\n            Strophe.SASLSHA1.prototype.onChallenge = function (connection, challenge, test_cnonce) {\n              var cnonce = test_cnonce || MD5.hexdigest(\"\" + Math.random() * 1234567890);\n              var auth_str = \"n=\" + utils.utf16to8(connection.authcid);\n              auth_str += \",r=\";\n              auth_str += cnonce;\n              connection._sasl_data.cnonce = cnonce;\n              connection._sasl_data[\"client-first-message-bare\"] = auth_str;\n              auth_str = \"n,,\" + auth_str;\n\n              this.onChallenge = function (connection, challenge) {\n                var nonce, salt, iter, Hi, U, U_old, i, k;\n                var responseText = \"c=biws,\";\n                var authMessage = \"\".concat(connection._sasl_data[\"client-first-message-bare\"], \",\").concat(challenge, \",\");\n                var cnonce = connection._sasl_data.cnonce;\n                var attribMatch = /([a-z]+)=([^,]+)(,|$)/;\n\n                while (challenge.match(attribMatch)) {\n                  var matches = challenge.match(attribMatch);\n                  challenge = challenge.replace(matches[0], \"\");\n\n                  switch (matches[1]) {\n                    case \"r\":\n                      nonce = matches[2];\n                      break;\n\n                    case \"s\":\n                      salt = matches[2];\n                      break;\n\n                    case \"i\":\n                      iter = matches[2];\n                      break;\n                  }\n                }\n\n                if (nonce.substr(0, cnonce.length) !== cnonce) {\n                  connection._sasl_data = {};\n                  return connection._sasl_failure_cb();\n                }\n\n                responseText += \"r=\" + nonce;\n                authMessage += responseText;\n                salt = atob(salt);\n                salt += \"\\x00\\x00\\x00\\x01\";\n                var pass = utils.utf16to8(connection.pass);\n                Hi = U_old = SHA1.core_hmac_sha1(pass, salt);\n\n                for (i = 1; i < iter; i++) {\n                  U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));\n\n                  for (k = 0; k < 5; k++) {\n                    Hi[k] ^= U[k];\n                  }\n\n                  U_old = U;\n                }\n\n                Hi = SHA1.binb2str(Hi);\n                var clientKey = SHA1.core_hmac_sha1(Hi, \"Client Key\");\n                var serverKey = SHA1.str_hmac_sha1(Hi, \"Server Key\");\n                var clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);\n                connection._sasl_data[\"server-signature\"] = SHA1.b64_hmac_sha1(serverKey, authMessage);\n\n                for (k = 0; k < 5; k++) {\n                  clientKey[k] ^= clientSignature[k];\n                }\n\n                responseText += \",p=\" + btoa(SHA1.binb2str(clientKey));\n                return responseText;\n              };\n\n              return auth_str;\n            };\n            /** PrivateConstructor: SASLMD5\n             *  SASL DIGEST MD5 authentication.\n             */\n\n\n            Strophe.SASLMD5 = function () {};\n\n            Strophe.SASLMD5.prototype = new Strophe.SASLMechanism(\"DIGEST-MD5\", false, 60);\n\n            Strophe.SASLMD5.prototype.test = function (connection) {\n              return connection.authcid !== null;\n            };\n            /** PrivateFunction: _quote\n             *  _Private_ utility function to backslash escape and quote strings.\n             *\n             *  Parameters:\n             *    (String) str - The string to be quoted.\n             *\n             *  Returns:\n             *    quoted string\n             */\n\n\n            Strophe.SASLMD5.prototype._quote = function (str) {\n              return '\"' + str.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"') + '\"'; //\" end string workaround for emacs\n            };\n\n            Strophe.SASLMD5.prototype.onChallenge = function (connection, challenge, test_cnonce) {\n              var attribMatch = /([a-z]+)=(\"[^\"]+\"|[^,\"]+)(?:,|$)/;\n              var cnonce = test_cnonce || MD5.hexdigest(\"\" + Math.random() * 1234567890);\n              var realm = \"\";\n              var host = null;\n              var nonce = \"\";\n              var qop = \"\";\n\n              while (challenge.match(attribMatch)) {\n                var matches = challenge.match(attribMatch);\n                challenge = challenge.replace(matches[0], \"\");\n                matches[2] = matches[2].replace(/^\"(.+)\"$/, \"$1\");\n\n                switch (matches[1]) {\n                  case \"realm\":\n                    realm = matches[2];\n                    break;\n\n                  case \"nonce\":\n                    nonce = matches[2];\n                    break;\n\n                  case \"qop\":\n                    qop = matches[2];\n                    break;\n\n                  case \"host\":\n                    host = matches[2];\n                    break;\n                }\n              }\n\n              var digest_uri = connection.servtype + \"/\" + connection.domain;\n\n              if (host !== null) {\n                digest_uri = digest_uri + \"/\" + host;\n              }\n\n              var cred = utils.utf16to8(connection.authcid + \":\" + realm + \":\" + this._connection.pass);\n              var A1 = MD5.hash(cred) + \":\" + nonce + \":\" + cnonce;\n              var A2 = 'AUTHENTICATE:' + digest_uri;\n              var responseText = \"\";\n              responseText += 'charset=utf-8,';\n              responseText += 'username=' + this._quote(utils.utf16to8(connection.authcid)) + ',';\n              responseText += 'realm=' + this._quote(realm) + ',';\n              responseText += 'nonce=' + this._quote(nonce) + ',';\n              responseText += 'nc=00000001,';\n              responseText += 'cnonce=' + this._quote(cnonce) + ',';\n              responseText += 'digest-uri=' + this._quote(digest_uri) + ',';\n              responseText += 'response=' + MD5.hexdigest(MD5.hexdigest(A1) + \":\" + nonce + \":00000001:\" + cnonce + \":auth:\" + MD5.hexdigest(A2)) + \",\";\n              responseText += 'qop=auth';\n\n              this.onChallenge = function () {\n                return \"\";\n              };\n\n              return responseText;\n            };\n            /** PrivateConstructor: SASLOAuthBearer\n             *  SASL OAuth Bearer authentication.\n             */\n\n\n            Strophe.SASLOAuthBearer = function () {};\n\n            Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism(\"OAUTHBEARER\", true, 40);\n\n            Strophe.SASLOAuthBearer.prototype.test = function (connection) {\n              return connection.pass !== null;\n            };\n\n            Strophe.SASLOAuthBearer.prototype.onChallenge = function (connection) {\n              var auth_str = 'n,';\n\n              if (connection.authcid !== null) {\n                auth_str = auth_str + 'a=' + connection.authzid;\n              }\n\n              auth_str = auth_str + ',';\n              auth_str = auth_str + \"\\x01\";\n              auth_str = auth_str + 'auth=Bearer ';\n              auth_str = auth_str + connection.pass;\n              auth_str = auth_str + \"\\x01\";\n              auth_str = auth_str + \"\\x01\";\n              return utils.utf16to8(auth_str);\n            };\n            /** PrivateConstructor: SASLExternal\n             *  SASL EXTERNAL authentication.\n             *\n             *  The EXTERNAL mechanism allows a client to request the server to use\n             *  credentials established by means external to the mechanism to\n             *  authenticate the client. The external means may be, for instance,\n             *  TLS services.\n             */\n\n\n            Strophe.SASLExternal = function () {};\n\n            Strophe.SASLExternal.prototype = new Strophe.SASLMechanism(\"EXTERNAL\", true, 10);\n\n            Strophe.SASLExternal.prototype.onChallenge = function (connection) {\n              /** According to XEP-178, an authzid SHOULD NOT be presented when the\n               * authcid contained or implied in the client certificate is the JID (i.e.\n               * authzid) with which the user wants to log in as.\n               *\n               * To NOT send the authzid, the user should therefore set the authcid equal\n               * to the JID when instantiating a new Strophe.Connection object.\n               */\n              return connection.authcid === connection.authzid ? '' : connection.authzid;\n            };\n            /** PrivateConstructor: SASLXOAuth2\n             *  SASL X-OAuth2 authentication.\n             */\n\n\n            Strophe.SASLXOAuth2 = function () {};\n\n            Strophe.SASLXOAuth2.prototype = new Strophe.SASLMechanism(\"X-OAUTH2\", true, 30);\n\n            Strophe.SASLXOAuth2.prototype.test = function (connection) {\n              return connection.pass !== null;\n            };\n\n            Strophe.SASLXOAuth2.prototype.onChallenge = function (connection) {\n              var auth_str = \"\\0\";\n\n              if (connection.authcid !== null) {\n                auth_str = auth_str + connection.authzid;\n              }\n\n              auth_str = auth_str + \"\\0\";\n              auth_str = auth_str + connection.pass;\n              return utils.utf16to8(auth_str);\n            };\n            var core = {\n              'Strophe': Strophe,\n              '$build': $build,\n              '$iq': $iq,\n              '$msg': $msg,\n              '$pres': $pres,\n              'SHA1': SHA1,\n              'MD5': MD5,\n              'b64_hmac_sha1': SHA1.b64_hmac_sha1,\n              'b64_sha1': SHA1.b64_sha1,\n              'str_hmac_sha1': SHA1.str_hmac_sha1,\n              'str_sha1': SHA1.str_sha1\n            };\n\n            /*\n                This program is distributed under the terms of the MIT license.\n                Please see the LICENSE file for details.\n\n                Copyright 2006-2008, OGG, LLC\n            */\n            var Strophe$1 = core.Strophe;\n            var $build$1 = core.$build;\n            /** PrivateClass: Strophe.Request\n             *  _Private_ helper class that provides a cross implementation abstraction\n             *  for a BOSH related XMLHttpRequest.\n             *\n             *  The Strophe.Request class is used internally to encapsulate BOSH request\n             *  information.  It is not meant to be used from user's code.\n             */\n\n            /** PrivateConstructor: Strophe.Request\n             *  Create and initialize a new Strophe.Request object.\n             *\n             *  Parameters:\n             *    (XMLElement) elem - The XML data to be sent in the request.\n             *    (Function) func - The function that will be called when the\n             *      XMLHttpRequest readyState changes.\n             *    (Integer) rid - The BOSH rid attribute associated with this request.\n             *    (Integer) sends - The number of times this same request has been sent.\n             */\n\n            Strophe$1.Request = function (elem, func, rid, sends) {\n              this.id = ++Strophe$1._requestId;\n              this.xmlData = elem;\n              this.data = Strophe$1.serialize(elem); // save original function in case we need to make a new request\n              // from this one.\n\n              this.origFunc = func;\n              this.func = func;\n              this.rid = rid;\n              this.date = NaN;\n              this.sends = sends || 0;\n              this.abort = false;\n              this.dead = null;\n\n              this.age = function () {\n                if (!this.date) {\n                  return 0;\n                }\n\n                var now = new Date();\n                return (now - this.date) / 1000;\n              };\n\n              this.timeDead = function () {\n                if (!this.dead) {\n                  return 0;\n                }\n\n                var now = new Date();\n                return (now - this.dead) / 1000;\n              };\n\n              this.xhr = this._newXHR();\n            };\n\n            Strophe$1.Request.prototype = {\n              /** PrivateFunction: getResponse\n               *  Get a response from the underlying XMLHttpRequest.\n               *\n               *  This function attempts to get a response from the request and checks\n               *  for errors.\n               *\n               *  Throws:\n               *    \"parsererror\" - A parser error occured.\n               *    \"bad-format\" - The entity has sent XML that cannot be processed.\n               *\n               *  Returns:\n               *    The DOM element tree of the response.\n               */\n              getResponse: function getResponse() {\n                var node = null;\n\n                if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {\n                  node = this.xhr.responseXML.documentElement;\n\n                  if (node.tagName === \"parsererror\") {\n                    Strophe$1.error(\"invalid response received\");\n                    Strophe$1.error(\"responseText: \" + this.xhr.responseText);\n                    Strophe$1.error(\"responseXML: \" + Strophe$1.serialize(this.xhr.responseXML));\n                    throw new Error(\"parsererror\");\n                  }\n                } else if (this.xhr.responseText) {\n                  // In React Native, we may get responseText but no responseXML.  We can try to parse it manually.\n                  Strophe$1.debug(\"Got responseText but no responseXML; attempting to parse it with DOMParser...\");\n                  node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement;\n\n                  if (!node) {\n                    throw new Error('Parsing produced null node');\n                  } else if (node.querySelector('parsererror')) {\n                    Strophe$1.error(\"invalid response received: \" + node.querySelector('parsererror').textContent);\n                    Strophe$1.error(\"responseText: \" + this.xhr.responseText);\n                    var error = new Error();\n                    error.name = Strophe$1.ErrorCondition.BAD_FORMAT;\n                    throw error;\n                  }\n                }\n\n                return node;\n              },\n\n              /** PrivateFunction: _newXHR\n               *  _Private_ helper function to create XMLHttpRequests.\n               *\n               *  This function creates XMLHttpRequests across all implementations.\n               *\n               *  Returns:\n               *    A new XMLHttpRequest.\n               */\n              _newXHR: function _newXHR() {\n                var xhr = null;\n\n                if (window.XMLHttpRequest) {\n                  xhr = new XMLHttpRequest();\n\n                  if (xhr.overrideMimeType) {\n                    xhr.overrideMimeType(\"text/xml; charset=utf-8\");\n                  }\n                } else if (window.ActiveXObject) {\n                  xhr = new ActiveXObject(\"Microsoft.XMLHTTP\");\n                } // use Function.bind() to prepend ourselves as an argument\n\n\n                xhr.onreadystatechange = this.func.bind(null, this);\n                return xhr;\n              }\n            };\n            /** Class: Strophe.Bosh\n             *  _Private_ helper class that handles BOSH Connections\n             *\n             *  The Strophe.Bosh class is used internally by Strophe.Connection\n             *  to encapsulate BOSH sessions. It is not meant to be used from user's code.\n             */\n\n            /** File: bosh.js\n             *  A JavaScript library to enable BOSH in Strophejs.\n             *\n             *  this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)\n             *  to emulate a persistent, stateful, two-way connection to an XMPP server.\n             *  More information on BOSH can be found in XEP 124.\n             */\n\n            /** PrivateConstructor: Strophe.Bosh\n             *  Create and initialize a Strophe.Bosh object.\n             *\n             *  Parameters:\n             *    (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.\n             *\n             *  Returns:\n             *    A new Strophe.Bosh object.\n             */\n\n            Strophe$1.Bosh = function (connection) {\n              this._conn = connection;\n              /* request id for body tags */\n\n              this.rid = Math.floor(Math.random() * 4294967295);\n              /* The current session ID. */\n\n              this.sid = null; // default BOSH values\n\n              this.hold = 1;\n              this.wait = 60;\n              this.window = 5;\n              this.errors = 0;\n              this.inactivity = null;\n              this.lastResponseHeaders = null;\n              this._requests = [];\n            };\n\n            Strophe$1.Bosh.prototype = {\n              /** Variable: strip\n               *\n               *  BOSH-Connections will have all stanzas wrapped in a <body> tag when\n               *  passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.\n               *  To strip this tag, User code can set <Strophe.Bosh.strip> to \"body\":\n               *\n               *  > Strophe.Bosh.prototype.strip = \"body\";\n               *\n               *  This will enable stripping of the body tag in both\n               *  <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.\n               */\n              strip: null,\n\n              /** PrivateFunction: _buildBody\n               *  _Private_ helper function to generate the <body/> wrapper for BOSH.\n               *\n               *  Returns:\n               *    A Strophe.Builder with a <body/> element.\n               */\n              _buildBody: function _buildBody() {\n                var bodyWrap = $build$1('body', {\n                  'rid': this.rid++,\n                  'xmlns': Strophe$1.NS.HTTPBIND\n                });\n\n                if (this.sid !== null) {\n                  bodyWrap.attrs({\n                    'sid': this.sid\n                  });\n                }\n\n                if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) {\n                  this._cacheSession();\n                }\n\n                return bodyWrap;\n              },\n\n              /** PrivateFunction: _reset\n               *  Reset the connection.\n               *\n               *  This function is called by the reset function of the Strophe Connection\n               */\n              _reset: function _reset() {\n                this.rid = Math.floor(Math.random() * 4294967295);\n                this.sid = null;\n                this.errors = 0;\n\n                if (this._conn._sessionCachingSupported()) {\n                  window.sessionStorage.removeItem('strophe-bosh-session');\n                }\n\n                this._conn.nextValidRid(this.rid);\n              },\n\n              /** PrivateFunction: _connect\n               *  _Private_ function that initializes the BOSH connection.\n               *\n               *  Creates and sends the Request that initializes the BOSH connection.\n               */\n              _connect: function _connect(wait, hold, route) {\n                this.wait = wait || this.wait;\n                this.hold = hold || this.hold;\n                this.errors = 0;\n\n                var body = this._buildBody().attrs({\n                  \"to\": this._conn.domain,\n                  \"xml:lang\": \"en\",\n                  \"wait\": this.wait,\n                  \"hold\": this.hold,\n                  \"content\": \"text/xml; charset=utf-8\",\n                  \"ver\": \"1.6\",\n                  \"xmpp:version\": \"1.0\",\n                  \"xmlns:xmpp\": Strophe$1.NS.BOSH\n                });\n\n                if (route) {\n                  body.attrs({\n                    'route': route\n                  });\n                }\n\n                var _connect_cb = this._conn._connect_cb;\n\n                this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, _connect_cb.bind(this._conn)), body.tree().getAttribute(\"rid\")));\n\n                this._throttledRequestHandler();\n              },\n\n              /** PrivateFunction: _attach\n               *  Attach to an already created and authenticated BOSH session.\n               *\n               *  This function is provided to allow Strophe to attach to BOSH\n               *  sessions which have been created externally, perhaps by a Web\n               *  application.  This is often used to support auto-login type features\n               *  without putting user credentials into the page.\n               *\n               *  Parameters:\n               *    (String) jid - The full JID that is bound by the session.\n               *    (String) sid - The SID of the BOSH session.\n               *    (String) rid - The current RID of the BOSH session.  This RID\n               *      will be used by the next request.\n               *    (Function) callback The connect callback function.\n               *    (Integer) wait - The optional HTTPBIND wait value.  This is the\n               *      time the server will wait before returning an empty result for\n               *      a request.  The default setting of 60 seconds is recommended.\n               *      Other settings will require tweaks to the Strophe.TIMEOUT value.\n               *    (Integer) hold - The optional HTTPBIND hold value.  This is the\n               *      number of connections the server will hold at one time.  This\n               *      should almost always be set to 1 (the default).\n               *    (Integer) wind - The optional HTTBIND window value.  This is the\n               *      allowed range of request ids that are valid.  The default is 5.\n               */\n              _attach: function _attach(jid, sid, rid, callback, wait, hold, wind) {\n                this._conn.jid = jid;\n                this.sid = sid;\n                this.rid = rid;\n                this._conn.connect_callback = callback;\n                this._conn.domain = Strophe$1.getDomainFromJid(this._conn.jid);\n                this._conn.authenticated = true;\n                this._conn.connected = true;\n                this.wait = wait || this.wait;\n                this.hold = hold || this.hold;\n                this.window = wind || this.window;\n\n                this._conn._changeConnectStatus(Strophe$1.Status.ATTACHED, null);\n              },\n\n              /** PrivateFunction: _restore\n               *  Attempt to restore a cached BOSH session\n               *\n               *  Parameters:\n               *    (String) jid - The full JID that is bound by the session.\n               *      This parameter is optional but recommended, specifically in cases\n               *      where prebinded BOSH sessions are used where it's important to know\n               *      that the right session is being restored.\n               *    (Function) callback The connect callback function.\n               *    (Integer) wait - The optional HTTPBIND wait value.  This is the\n               *      time the server will wait before returning an empty result for\n               *      a request.  The default setting of 60 seconds is recommended.\n               *      Other settings will require tweaks to the Strophe.TIMEOUT value.\n               *    (Integer) hold - The optional HTTPBIND hold value.  This is the\n               *      number of connections the server will hold at one time.  This\n               *      should almost always be set to 1 (the default).\n               *    (Integer) wind - The optional HTTBIND window value.  This is the\n               *      allowed range of request ids that are valid.  The default is 5.\n               */\n              _restore: function _restore(jid, callback, wait, hold, wind) {\n                var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));\n\n                if (typeof session !== \"undefined\" && session !== null && session.rid && session.sid && session.jid && (typeof jid === \"undefined\" || jid === null || Strophe$1.getBareJidFromJid(session.jid) === Strophe$1.getBareJidFromJid(jid) || // If authcid is null, then it's an anonymous login, so\n                // we compare only the domains:\n                Strophe$1.getNodeFromJid(jid) === null && Strophe$1.getDomainFromJid(session.jid) === jid)) {\n                  this._conn.restored = true;\n\n                  this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);\n                } else {\n                  var error = new Error(\"_restore: no restoreable session.\");\n                  error.name = \"StropheSessionError\";\n                  throw error;\n                }\n              },\n\n              /** PrivateFunction: _cacheSession\n               *  _Private_ handler for the beforeunload event.\n               *\n               *  This handler is used to process the Bosh-part of the initial request.\n               *  Parameters:\n               *    (Strophe.Request) bodyWrap - The received stanza.\n               */\n              _cacheSession: function _cacheSession() {\n                if (this._conn.authenticated) {\n                  if (this._conn.jid && this.rid && this.sid) {\n                    window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({\n                      'jid': this._conn.jid,\n                      'rid': this.rid,\n                      'sid': this.sid\n                    }));\n                  }\n                } else {\n                  window.sessionStorage.removeItem('strophe-bosh-session');\n                }\n              },\n\n              /** PrivateFunction: _connect_cb\n               *  _Private_ handler for initial connection request.\n               *\n               *  This handler is used to process the Bosh-part of the initial request.\n               *  Parameters:\n               *    (Strophe.Request) bodyWrap - The received stanza.\n               */\n              _connect_cb: function _connect_cb(bodyWrap) {\n                var typ = bodyWrap.getAttribute(\"type\");\n\n                if (typ !== null && typ === \"terminate\") {\n                  // an error occurred\n                  var cond = bodyWrap.getAttribute(\"condition\");\n                  Strophe$1.error(\"BOSH-Connection failed: \" + cond);\n                  var conflict = bodyWrap.getElementsByTagName(\"conflict\");\n\n                  if (cond !== null) {\n                    if (cond === \"remote-stream-error\" && conflict.length > 0) {\n                      cond = \"conflict\";\n                    }\n\n                    this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, cond);\n                  } else {\n                    this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"unknown\");\n                  }\n\n                  this._conn._doDisconnect(cond);\n\n                  return Strophe$1.Status.CONNFAIL;\n                } // check to make sure we don't overwrite these if _connect_cb is\n                // called multiple times in the case of missing stream:features\n\n\n                if (!this.sid) {\n                  this.sid = bodyWrap.getAttribute(\"sid\");\n                }\n\n                var wind = bodyWrap.getAttribute('requests');\n\n                if (wind) {\n                  this.window = parseInt(wind, 10);\n                }\n\n                var hold = bodyWrap.getAttribute('hold');\n\n                if (hold) {\n                  this.hold = parseInt(hold, 10);\n                }\n\n                var wait = bodyWrap.getAttribute('wait');\n\n                if (wait) {\n                  this.wait = parseInt(wait, 10);\n                }\n\n                var inactivity = bodyWrap.getAttribute('inactivity');\n\n                if (inactivity) {\n                  this.inactivity = parseInt(inactivity, 10);\n                }\n              },\n\n              /** PrivateFunction: _disconnect\n               *  _Private_ part of Connection.disconnect for Bosh\n               *\n               *  Parameters:\n               *    (Request) pres - This stanza will be sent before disconnecting.\n               */\n              _disconnect: function _disconnect(pres) {\n                this._sendTerminate(pres);\n              },\n\n              /** PrivateFunction: _doDisconnect\n               *  _Private_ function to disconnect.\n               *\n               *  Resets the SID and RID.\n               */\n              _doDisconnect: function _doDisconnect() {\n                this.sid = null;\n                this.rid = Math.floor(Math.random() * 4294967295);\n\n                if (this._conn._sessionCachingSupported()) {\n                  window.sessionStorage.removeItem('strophe-bosh-session');\n                }\n\n                this._conn.nextValidRid(this.rid);\n              },\n\n              /** PrivateFunction: _emptyQueue\n               * _Private_ function to check if the Request queue is empty.\n               *\n               *  Returns:\n               *    True, if there are no Requests queued, False otherwise.\n               */\n              _emptyQueue: function _emptyQueue() {\n                return this._requests.length === 0;\n              },\n\n              /** PrivateFunction: _callProtocolErrorHandlers\n               *  _Private_ function to call error handlers registered for HTTP errors.\n               *\n               *  Parameters:\n               *    (Strophe.Request) req - The request that is changing readyState.\n               */\n              _callProtocolErrorHandlers: function _callProtocolErrorHandlers(req) {\n                var reqStatus = this._getRequestStatus(req);\n\n                var err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus];\n\n                if (err_callback) {\n                  err_callback.call(this, reqStatus);\n                }\n              },\n\n              /** PrivateFunction: _hitError\n               *  _Private_ function to handle the error count.\n               *\n               *  Requests are resent automatically until their error count reaches\n               *  5.  Each time an error is encountered, this function is called to\n               *  increment the count and disconnect if the count is too high.\n               *\n               *  Parameters:\n               *    (Integer) reqStatus - The request status.\n               */\n              _hitError: function _hitError(reqStatus) {\n                this.errors++;\n                Strophe$1.warn(\"request errored, status: \" + reqStatus + \", number of errors: \" + this.errors);\n\n                if (this.errors > 4) {\n                  this._conn._onDisconnectTimeout();\n                }\n              },\n\n              /** PrivateFunction: _no_auth_received\n               *\n               * Called on stream start/restart when no stream:features\n               * has been received and sends a blank poll request.\n               */\n              _no_auth_received: function _no_auth_received(callback) {\n                Strophe$1.warn(\"Server did not yet offer a supported authentication \" + \"mechanism. Sending a blank poll request.\");\n\n                if (callback) {\n                  callback = callback.bind(this._conn);\n                } else {\n                  callback = this._conn._connect_cb.bind(this._conn);\n                }\n\n                var body = this._buildBody();\n\n                this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, callback), body.tree().getAttribute(\"rid\")));\n\n                this._throttledRequestHandler();\n              },\n\n              /** PrivateFunction: _onDisconnectTimeout\n               *  _Private_ timeout handler for handling non-graceful disconnection.\n               *\n               *  Cancels all remaining Requests and clears the queue.\n               */\n              _onDisconnectTimeout: function _onDisconnectTimeout() {\n                this._abortAllRequests();\n              },\n\n              /** PrivateFunction: _abortAllRequests\n               *  _Private_ helper function that makes sure all pending requests are aborted.\n               */\n              _abortAllRequests: function _abortAllRequests() {\n                while (this._requests.length > 0) {\n                  var req = this._requests.pop();\n\n                  req.abort = true;\n                  req.xhr.abort();\n\n                  req.xhr.onreadystatechange = function () {};\n                }\n              },\n\n              /** PrivateFunction: _onIdle\n               *  _Private_ handler called by Strophe.Connection._onIdle\n               *\n               *  Sends all queued Requests or polls with empty Request if there are none.\n               */\n              _onIdle: function _onIdle() {\n                var data = this._conn._data; // if no requests are in progress, poll\n\n                if (this._conn.authenticated && this._requests.length === 0 && data.length === 0 && !this._conn.disconnecting) {\n                  Strophe$1.debug(\"no requests during idle cycle, sending blank request\");\n                  data.push(null);\n                }\n\n                if (this._conn.paused) {\n                  return;\n                }\n\n                if (this._requests.length < 2 && data.length > 0) {\n                  var body = this._buildBody();\n\n                  for (var i = 0; i < data.length; i++) {\n                    if (data[i] !== null) {\n                      if (data[i] === \"restart\") {\n                        body.attrs({\n                          \"to\": this._conn.domain,\n                          \"xml:lang\": \"en\",\n                          \"xmpp:restart\": \"true\",\n                          \"xmlns:xmpp\": Strophe$1.NS.BOSH\n                        });\n                      } else {\n                        body.cnode(data[i]).up();\n                      }\n                    }\n                  }\n\n                  delete this._conn._data;\n                  this._conn._data = [];\n\n                  this._requests.push(new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute(\"rid\")));\n\n                  this._throttledRequestHandler();\n                }\n\n                if (this._requests.length > 0) {\n                  var time_elapsed = this._requests[0].age();\n\n                  if (this._requests[0].dead !== null) {\n                    if (this._requests[0].timeDead() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait)) {\n                      this._throttledRequestHandler();\n                    }\n                  }\n\n                  if (time_elapsed > Math.floor(Strophe$1.TIMEOUT * this.wait)) {\n                    Strophe$1.warn(\"Request \" + this._requests[0].id + \" timed out, over \" + Math.floor(Strophe$1.TIMEOUT * this.wait) + \" seconds since last activity\");\n\n                    this._throttledRequestHandler();\n                  }\n                }\n              },\n\n              /** PrivateFunction: _getRequestStatus\n               *\n               *  Returns the HTTP status code from a Strophe.Request\n               *\n               *  Parameters:\n               *    (Strophe.Request) req - The Strophe.Request instance.\n               *    (Integer) def - The default value that should be returned if no\n               *          status value was found.\n               */\n              _getRequestStatus: function _getRequestStatus(req, def) {\n                var reqStatus;\n\n                if (req.xhr.readyState === 4) {\n                  try {\n                    reqStatus = req.xhr.status;\n                  } catch (e) {\n                    // ignore errors from undefined status attribute. Works\n                    // around a browser bug\n                    Strophe$1.error(\"Caught an error while retrieving a request's status, \" + \"reqStatus: \" + reqStatus);\n                  }\n                }\n\n                if (typeof reqStatus === \"undefined\") {\n                  reqStatus = typeof def === 'number' ? def : 0;\n                }\n\n                return reqStatus;\n              },\n\n              /** PrivateFunction: _onRequestStateChange\n               *  _Private_ handler for Strophe.Request state changes.\n               *\n               *  This function is called when the XMLHttpRequest readyState changes.\n               *  It contains a lot of error handling logic for the many ways that\n               *  requests can fail, and calls the request callback when requests\n               *  succeed.\n               *\n               *  Parameters:\n               *    (Function) func - The handler for the request.\n               *    (Strophe.Request) req - The request that is changing readyState.\n               */\n              _onRequestStateChange: function _onRequestStateChange(func, req) {\n                Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" state changed to \" + req.xhr.readyState);\n\n                if (req.abort) {\n                  req.abort = false;\n                  return;\n                }\n\n                if (req.xhr.readyState !== 4) {\n                  // The request is not yet complete\n                  return;\n                }\n\n                var reqStatus = this._getRequestStatus(req);\n\n                this.lastResponseHeaders = req.xhr.getAllResponseHeaders();\n\n                if (this.disconnecting && reqStatus >= 400) {\n                  this._hitError(reqStatus);\n\n                  this._callProtocolErrorHandlers(req);\n\n                  return;\n                }\n\n                var valid_request = reqStatus > 0 && reqStatus < 500;\n                var too_many_retries = req.sends > this._conn.maxRetries;\n\n                if (valid_request || too_many_retries) {\n                  // remove from internal queue\n                  this._removeRequest(req);\n\n                  Strophe$1.debug(\"request id \" + req.id + \" should now be removed\");\n                }\n\n                if (reqStatus === 200) {\n                  // request succeeded\n                  var reqIs0 = this._requests[0] === req;\n                  var reqIs1 = this._requests[1] === req; // if request 1 finished, or request 0 finished and request\n                  // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to\n                  // restart the other - both will be in the first spot, as the\n                  // completed request has been removed from the queue already\n\n                  if (reqIs1 || reqIs0 && this._requests.length > 0 && this._requests[0].age() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait)) {\n                    this._restartRequest(0);\n                  }\n\n                  this._conn.nextValidRid(Number(req.rid) + 1);\n\n                  Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" got 200\");\n                  func(req); // call handler\n\n                  this.errors = 0;\n                } else if (reqStatus === 0 || reqStatus >= 400 && reqStatus < 600 || reqStatus >= 12000) {\n                  // request failed\n                  Strophe$1.error(\"request id \" + req.id + \".\" + req.sends + \" error \" + reqStatus + \" happened\");\n\n                  this._hitError(reqStatus);\n\n                  this._callProtocolErrorHandlers(req);\n\n                  if (reqStatus >= 400 && reqStatus < 500) {\n                    this._conn._changeConnectStatus(Strophe$1.Status.DISCONNECTING, null);\n\n                    this._conn._doDisconnect();\n                  }\n                } else {\n                  Strophe$1.error(\"request id \" + req.id + \".\" + req.sends + \" error \" + reqStatus + \" happened\");\n                }\n\n                if (!valid_request && !too_many_retries) {\n                  this._throttledRequestHandler();\n                } else if (too_many_retries && !this._conn.connected) {\n                  this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"giving-up\");\n                }\n              },\n\n              /** PrivateFunction: _processRequest\n               *  _Private_ function to process a request in the queue.\n               *\n               *  This function takes requests off the queue and sends them and\n               *  restarts dead requests.\n               *\n               *  Parameters:\n               *    (Integer) i - The index of the request in the queue.\n               */\n              _processRequest: function _processRequest(i) {\n                var _this = this;\n\n                var req = this._requests[i];\n\n                var reqStatus = this._getRequestStatus(req, -1); // make sure we limit the number of retries\n\n\n                if (req.sends > this._conn.maxRetries) {\n                  this._conn._onDisconnectTimeout();\n\n                  return;\n                }\n\n                var time_elapsed = req.age();\n                var primary_timeout = !isNaN(time_elapsed) && time_elapsed > Math.floor(Strophe$1.TIMEOUT * this.wait);\n                var secondary_timeout = req.dead !== null && req.timeDead() > Math.floor(Strophe$1.SECONDARY_TIMEOUT * this.wait);\n                var server_error = req.xhr.readyState === 4 && (reqStatus < 1 || reqStatus >= 500);\n\n                if (primary_timeout || secondary_timeout || server_error) {\n                  if (secondary_timeout) {\n                    Strophe$1.error(\"Request \".concat(this._requests[i].id, \" timed out (secondary), restarting\"));\n                  }\n\n                  req.abort = true;\n                  req.xhr.abort(); // setting to null fails on IE6, so set to empty function\n\n                  req.xhr.onreadystatechange = function () {};\n\n                  this._requests[i] = new Strophe$1.Request(req.xmlData, req.origFunc, req.rid, req.sends);\n                  req = this._requests[i];\n                }\n\n                if (req.xhr.readyState === 0) {\n                  Strophe$1.debug(\"request id \" + req.id + \".\" + req.sends + \" posting\");\n\n                  try {\n                    var content_type = this._conn.options.contentType || \"text/xml; charset=utf-8\";\n                    req.xhr.open(\"POST\", this._conn.service, this._conn.options.sync ? false : true);\n\n                    if (typeof req.xhr.setRequestHeader !== 'undefined') {\n                      // IE9 doesn't have setRequestHeader\n                      req.xhr.setRequestHeader(\"Content-Type\", content_type);\n                    }\n\n                    if (this._conn.options.withCredentials) {\n                      req.xhr.withCredentials = true;\n                    }\n                  } catch (e2) {\n                    Strophe$1.error(\"XHR open failed: \" + e2.toString());\n\n                    if (!this._conn.connected) {\n                      this._conn._changeConnectStatus(Strophe$1.Status.CONNFAIL, \"bad-service\");\n                    }\n\n                    this._conn.disconnect();\n\n                    return;\n                  } // Fires the XHR request -- may be invoked immediately\n                  // or on a gradually expanding retry window for reconnects\n\n\n                  var sendFunc = function sendFunc() {\n                    req.date = new Date();\n\n                    if (_this._conn.options.customHeaders) {\n                      var headers = _this._conn.options.customHeaders;\n\n                      for (var header in headers) {\n                        if (Object.prototype.hasOwnProperty.call(headers, header)) {\n                          req.xhr.setRequestHeader(header, headers[header]);\n                        }\n                      }\n                    }\n\n                    req.xhr.send(req.data);\n                  }; // Implement progressive backoff for reconnects --\n                  // First retry (send === 1) should also be instantaneous\n\n\n                  if (req.sends > 1) {\n                    // Using a cube of the retry number creates a nicely\n                    // expanding retry window\n                    var backoff = Math.min(Math.floor(Strophe$1.TIMEOUT * this.wait), Math.pow(req.sends, 3)) * 1000;\n                    setTimeout(function () {\n                      // XXX: setTimeout should be called only with function expressions (23974bc1)\n                      sendFunc();\n                    }, backoff);\n                  } else {\n                    sendFunc();\n                  }\n\n                  req.sends++;\n\n                  if (this._conn.xmlOutput !== Strophe$1.Connection.prototype.xmlOutput) {\n                    if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {\n                      this._conn.xmlOutput(req.xmlData.childNodes[0]);\n                    } else {\n                      this._conn.xmlOutput(req.xmlData);\n                    }\n                  }\n\n                  if (this._conn.rawOutput !== Strophe$1.Connection.prototype.rawOutput) {\n                    this._conn.rawOutput(req.data);\n                  }\n                } else {\n                  Strophe$1.debug(\"_processRequest: \" + (i === 0 ? \"first\" : \"second\") + \" request has readyState of \" + req.xhr.readyState);\n                }\n              },\n\n              /** PrivateFunction: _removeRequest\n               *  _Private_ function to remove a request from the queue.\n               *\n               *  Parameters:\n               *    (Strophe.Request) req - The request to remove.\n               */\n              _removeRequest: function _removeRequest(req) {\n                Strophe$1.debug(\"removing request\");\n\n                for (var i = this._requests.length - 1; i >= 0; i--) {\n                  if (req === this._requests[i]) {\n                    this._requests.splice(i, 1);\n                  }\n                } // IE6 fails on setting to null, so set to empty function\n\n\n                req.xhr.onreadystatechange = function () {};\n\n                this._throttledRequestHandler();\n              },\n\n              /** PrivateFunction: _restartRequest\n               *  _Private_ function to restart a request that is presumed dead.\n               *\n               *  Parameters:\n               *    (Integer) i - The index of the request in the queue.\n               */\n              _restartRequest: function _restartRequest(i) {\n                var req = this._requests[i];\n\n                if (req.dead === null) {\n                  req.dead = new Date();\n                }\n\n                this._processRequest(i);\n              },\n\n              /** PrivateFunction: _reqToData\n               * _Private_ function to get a stanza out of a request.\n               *\n               * Tries to extract a stanza out of a Request Object.\n               * When this fails the current connection will be disconnected.\n               *\n               *  Parameters:\n               *    (Object) req - The Request.\n               *\n               *  Returns:\n               *    The stanza that was passed.\n               */\n              _reqToData: function _reqToData(req) {\n                try {\n                  return req.getResponse();\n                } catch (e) {\n                  if (e.message !== \"parsererror\") {\n                    throw e;\n                  }\n\n                  this._conn.disconnect(\"strophe-parsererror\");\n                }\n              },\n\n              /** PrivateFunction: _sendTerminate\n               *  _Private_ function to send initial disconnect sequence.\n               *\n               *  This is the first step in a graceful disconnect.  It sends\n               *  the BOSH server a terminate body and includes an unavailable\n               *  presence if authentication has completed.\n               */\n              _sendTerminate: function _sendTerminate(pres) {\n                Strophe$1.debug(\"_sendTerminate was called\");\n\n                var body = this._buildBody().attrs({\n                  type: \"terminate\"\n                });\n\n                if (pres) {\n                  body.cnode(pres.tree());\n                }\n\n                var req = new Strophe$1.Request(body.tree(), this._onRequestStateChange.bind(this, this._conn._dataRecv.bind(this._conn)), body.tree().getAttribute(\"rid\"));\n\n                this._requests.push(req);\n\n                this._throttledRequestHandler();\n              },\n\n              /** PrivateFunction: _send\n               *  _Private_ part of the Connection.send function for BOSH\n               *\n               * Just triggers the RequestHandler to send the messages that are in the queue\n               */\n              _send: function _send() {\n                var _this2 = this;\n\n                clearTimeout(this._conn._idleTimeout);\n\n                this._throttledRequestHandler();\n\n                this._conn._idleTimeout = setTimeout(function () {\n                  return _this2._conn._onIdle();\n                }, 100);\n              },\n\n              /** PrivateFunction: _sendRestart\n               *\n               *  Send an xmpp:restart stanza.\n               */\n              _sendRestart: function _sendRestart() {\n                this._throttledRequestHandler();\n\n                clearTimeout(this._conn._idleTimeout);\n              },\n\n              /** PrivateFunction: _throttledRequestHandler\n               *  _Private_ function to throttle requests to the connection window.\n               *\n               *  This function makes sure we don't send requests so fast that the\n               *  request ids overflow the connection window in the case that one\n               *  request died.\n               */\n              _throttledRequestHandler: function _throttledRequestHandler() {\n                if (!this._requests) {\n                  Strophe$1.debug(\"_throttledRequestHandler called with \" + \"undefined requests\");\n                } else {\n                  Strophe$1.debug(\"_throttledRequestHandler called with \" + this._requests.length + \" requests\");\n                }\n\n                if (!this._requests || this._requests.length === 0) {\n                  return;\n                }\n\n                if (this._requests.length > 0) {\n                  this._processRequest(0);\n                }\n\n                if (this._requests.length > 1 && Math.abs(this._requests[0].rid - this._requests[1].rid) < this.window) {\n                  this._processRequest(1);\n                }\n              }\n            };\n\n            /*\n                This program is distributed under the terms of the MIT license.\n                Please see the LICENSE file for details.\n\n                Copyright 2006-2008, OGG, LLC\n            */\n            var Strophe$2 = core.Strophe;\n            var $build$2 = core.$build;\n            /** Class: Strophe.WebSocket\n             *  _Private_ helper class that handles WebSocket Connections\n             *\n             *  The Strophe.WebSocket class is used internally by Strophe.Connection\n             *  to encapsulate WebSocket sessions. It is not meant to be used from user's code.\n             */\n\n            /** File: websocket.js\n             *  A JavaScript library to enable XMPP over Websocket in Strophejs.\n             *\n             *  This file implements XMPP over WebSockets for Strophejs.\n             *  If a Connection is established with a Websocket url (ws://...)\n             *  Strophe will use WebSockets.\n             *  For more information on XMPP-over-WebSocket see RFC 7395:\n             *  http://tools.ietf.org/html/rfc7395\n             *\n             *  WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)\n             */\n\n            /** PrivateConstructor: Strophe.Websocket\n             *  Create and initialize a Strophe.WebSocket object.\n             *  Currently only sets the connection Object.\n             *\n             *  Parameters:\n             *    (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.\n             *\n             *  Returns:\n             *    A new Strophe.WebSocket object.\n             */\n\n            Strophe$2.Websocket = function (connection) {\n              this._conn = connection;\n              this.strip = \"wrapper\";\n              var service = connection.service;\n\n              if (service.indexOf(\"ws:\") !== 0 && service.indexOf(\"wss:\") !== 0) {\n                // If the service is not an absolute URL, assume it is a path and put the absolute\n                // URL together from options, current URL and the path.\n                var new_service = \"\";\n\n                if (connection.options.protocol === \"ws\" && window.location.protocol !== \"https:\") {\n                  new_service += \"ws\";\n                } else {\n                  new_service += \"wss\";\n                }\n\n                new_service += \"://\" + window.location.host;\n\n                if (service.indexOf(\"/\") !== 0) {\n                  new_service += window.location.pathname + service;\n                } else {\n                  new_service += service;\n                }\n\n                connection.service = new_service;\n              }\n            };\n\n            Strophe$2.Websocket.prototype = {\n              /** PrivateFunction: _buildStream\n               *  _Private_ helper function to generate the <stream> start tag for WebSockets\n               *\n               *  Returns:\n               *    A Strophe.Builder with a <stream> element.\n               */\n              _buildStream: function _buildStream() {\n                return $build$2(\"open\", {\n                  \"xmlns\": Strophe$2.NS.FRAMING,\n                  \"to\": this._conn.domain,\n                  \"version\": '1.0'\n                });\n              },\n\n              /** PrivateFunction: _check_streamerror\n               * _Private_ checks a message for stream:error\n               *\n               *  Parameters:\n               *    (Strophe.Request) bodyWrap - The received stanza.\n               *    connectstatus - The ConnectStatus that will be set on error.\n               *  Returns:\n               *     true if there was a streamerror, false otherwise.\n               */\n              _check_streamerror: function _check_streamerror(bodyWrap, connectstatus) {\n                var errors;\n\n                if (bodyWrap.getElementsByTagNameNS) {\n                  errors = bodyWrap.getElementsByTagNameNS(Strophe$2.NS.STREAM, \"error\");\n                } else {\n                  errors = bodyWrap.getElementsByTagName(\"stream:error\");\n                }\n\n                if (errors.length === 0) {\n                  return false;\n                }\n\n                var error = errors[0];\n                var condition = \"\";\n                var text = \"\";\n                var ns = \"urn:ietf:params:xml:ns:xmpp-streams\";\n\n                for (var i = 0; i < error.childNodes.length; i++) {\n                  var e = error.childNodes[i];\n\n                  if (e.getAttribute(\"xmlns\") !== ns) {\n                    break;\n                  }\n\n                  if (e.nodeName === \"text\") {\n                    text = e.textContent;\n                  } else {\n                    condition = e.nodeName;\n                  }\n                }\n\n                var errorString = \"WebSocket stream error: \";\n\n                if (condition) {\n                  errorString += condition;\n                } else {\n                  errorString += \"unknown\";\n                }\n\n                if (text) {\n                  errorString += \" - \" + text;\n                }\n\n                Strophe$2.error(errorString); // close the connection on stream_error\n\n                this._conn._changeConnectStatus(connectstatus, condition);\n\n                this._conn._doDisconnect();\n\n                return true;\n              },\n\n              /** PrivateFunction: _reset\n               *  Reset the connection.\n               *\n               *  This function is called by the reset function of the Strophe Connection.\n               *  Is not needed by WebSockets.\n               */\n              _reset: function _reset() {\n                return;\n              },\n\n              /** PrivateFunction: _connect\n               *  _Private_ function called by Strophe.Connection.connect\n               *\n               *  Creates a WebSocket for a connection and assigns Callbacks to it.\n               *  Does nothing if there already is a WebSocket.\n               */\n              _connect: function _connect() {\n                // Ensure that there is no open WebSocket from a previous Connection.\n                this._closeSocket(); // Create the new WobSocket\n\n\n                this.socket = new WebSocket(this._conn.service, \"xmpp\");\n                this.socket.onopen = this._onOpen.bind(this);\n                this.socket.onerror = this._onError.bind(this);\n                this.socket.onclose = this._onClose.bind(this);\n                this.socket.onmessage = this._connect_cb_wrapper.bind(this);\n              },\n\n              /** PrivateFunction: _connect_cb\n               *  _Private_ function called by Strophe.Connection._connect_cb\n               *\n               * checks for stream:error\n               *\n               *  Parameters:\n               *    (Strophe.Request) bodyWrap - The received stanza.\n               */\n              _connect_cb: function _connect_cb(bodyWrap) {\n                var error = this._check_streamerror(bodyWrap, Strophe$2.Status.CONNFAIL);\n\n                if (error) {\n                  return Strophe$2.Status.CONNFAIL;\n                }\n              },\n\n              /** PrivateFunction: _handleStreamStart\n               * _Private_ function that checks the opening <open /> tag for errors.\n               *\n               * Disconnects if there is an error and returns false, true otherwise.\n               *\n               *  Parameters:\n               *    (Node) message - Stanza containing the <open /> tag.\n               */\n              _handleStreamStart: function _handleStreamStart(message) {\n                var error = false; // Check for errors in the <open /> tag\n\n                var ns = message.getAttribute(\"xmlns\");\n\n                if (typeof ns !== \"string\") {\n                  error = \"Missing xmlns in <open />\";\n                } else if (ns !== Strophe$2.NS.FRAMING) {\n                  error = \"Wrong xmlns in <open />: \" + ns;\n                }\n\n                var ver = message.getAttribute(\"version\");\n\n                if (typeof ver !== \"string\") {\n                  error = \"Missing version in <open />\";\n                } else if (ver !== \"1.0\") {\n                  error = \"Wrong version in <open />: \" + ver;\n                }\n\n                if (error) {\n                  this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, error);\n\n                  this._conn._doDisconnect();\n\n                  return false;\n                }\n\n                return true;\n              },\n\n              /** PrivateFunction: _connect_cb_wrapper\n               * _Private_ function that handles the first connection messages.\n               *\n               * On receiving an opening stream tag this callback replaces itself with the real\n               * message handler. On receiving a stream error the connection is terminated.\n               */\n              _connect_cb_wrapper: function _connect_cb_wrapper(message) {\n                if (message.data.indexOf(\"<open \") === 0 || message.data.indexOf(\"<?xml\") === 0) {\n                  // Strip the XML Declaration, if there is one\n                  var data = message.data.replace(/^(<\\?.*?\\?>\\s*)*/, \"\");\n                  if (data === '') return;\n                  var streamStart = new DOMParser().parseFromString(data, \"text/xml\").documentElement;\n\n                  this._conn.xmlInput(streamStart);\n\n                  this._conn.rawInput(message.data); //_handleStreamSteart will check for XML errors and disconnect on error\n\n\n                  if (this._handleStreamStart(streamStart)) {\n                    //_connect_cb will check for stream:error and disconnect on error\n                    this._connect_cb(streamStart);\n                  }\n                } else if (message.data.indexOf(\"<close \") === 0) {\n                  // <close xmlns=\"urn:ietf:params:xml:ns:xmpp-framing />\n                  // Parse the raw string to an XML element\n                  var parsedMessage = new DOMParser().parseFromString(message.data, \"text/xml\").documentElement; // Report this input to the raw and xml handlers\n\n                  this._conn.xmlInput(parsedMessage);\n\n                  this._conn.rawInput(message.data);\n\n                  var see_uri = parsedMessage.getAttribute(\"see-other-uri\");\n\n                  if (see_uri) {\n                    var service = this._conn.service; // Valid scenarios: WSS->WSS, WS->ANY\n\n                    var isSecureRedirect = service.indexOf(\"wss:\") >= 0 && see_uri.indexOf(\"wss:\") >= 0 || service.indexOf(\"ws:\") >= 0;\n\n                    if (isSecureRedirect) {\n                      this._conn._changeConnectStatus(Strophe$2.Status.REDIRECT, \"Received see-other-uri, resetting connection\");\n\n                      this._conn.reset();\n\n                      this._conn.service = see_uri;\n\n                      this._connect();\n                    }\n                  } else {\n                    this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"Received closing stream\");\n\n                    this._conn._doDisconnect();\n                  }\n                } else {\n                  var string = this._streamWrap(message.data);\n\n                  var elem = new DOMParser().parseFromString(string, \"text/xml\").documentElement;\n                  this.socket.onmessage = this._onMessage.bind(this);\n\n                  this._conn._connect_cb(elem, null, message.data);\n                }\n              },\n\n              /** PrivateFunction: _disconnect\n               *  _Private_ function called by Strophe.Connection.disconnect\n               *\n               *  Disconnects and sends a last stanza if one is given\n               *\n               *  Parameters:\n               *    (Request) pres - This stanza will be sent before disconnecting.\n               */\n              _disconnect: function _disconnect(pres) {\n                if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {\n                  if (pres) {\n                    this._conn.send(pres);\n                  }\n\n                  var close = $build$2(\"close\", {\n                    \"xmlns\": Strophe$2.NS.FRAMING\n                  });\n\n                  this._conn.xmlOutput(close.tree());\n\n                  var closeString = Strophe$2.serialize(close);\n\n                  this._conn.rawOutput(closeString);\n\n                  try {\n                    this.socket.send(closeString);\n                  } catch (e) {\n                    Strophe$2.warn(\"Couldn't send <close /> tag.\");\n                  }\n                }\n\n                this._conn._doDisconnect();\n              },\n\n              /** PrivateFunction: _doDisconnect\n               *  _Private_ function to disconnect.\n               *\n               *  Just closes the Socket for WebSockets\n               */\n              _doDisconnect: function _doDisconnect() {\n                Strophe$2.debug(\"WebSockets _doDisconnect was called\");\n\n                this._closeSocket();\n              },\n\n              /** PrivateFunction _streamWrap\n               *  _Private_ helper function to wrap a stanza in a <stream> tag.\n               *  This is used so Strophe can process stanzas from WebSockets like BOSH\n               */\n              _streamWrap: function _streamWrap(stanza) {\n                return \"<wrapper>\" + stanza + '</wrapper>';\n              },\n\n              /** PrivateFunction: _closeSocket\n               *  _Private_ function to close the WebSocket.\n               *\n               *  Closes the socket if it is still open and deletes it\n               */\n              _closeSocket: function _closeSocket() {\n                if (this.socket) {\n                  try {\n                    this.socket.onclose = null;\n                    this.socket.onerror = null;\n                    this.socket.onmessage = null;\n                    this.socket.close();\n                  } catch (e) {\n                    Strophe$2.debug(e.message);\n                  }\n                }\n\n                this.socket = null;\n              },\n\n              /** PrivateFunction: _emptyQueue\n               * _Private_ function to check if the message queue is empty.\n               *\n               *  Returns:\n               *    True, because WebSocket messages are send immediately after queueing.\n               */\n              _emptyQueue: function _emptyQueue() {\n                return true;\n              },\n\n              /** PrivateFunction: _onClose\n               * _Private_ function to handle websockets closing.\n               *\n               * Nothing to do here for WebSockets\n               */\n              _onClose: function _onClose(e) {\n                if (this._conn.connected && !this._conn.disconnecting) {\n                  Strophe$2.error(\"Websocket closed unexpectedly\");\n\n                  this._conn._doDisconnect();\n                } else if (e && e.code === 1006 && !this._conn.connected && this.socket) {\n                  // in case the onError callback was not called (Safari 10 does not\n                  // call onerror when the initial connection fails) we need to\n                  // dispatch a CONNFAIL status update to be consistent with the\n                  // behavior on other browsers.\n                  Strophe$2.error(\"Websocket closed unexcectedly\");\n\n                  this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"The WebSocket connection could not be established or was disconnected.\");\n\n                  this._conn._doDisconnect();\n                } else {\n                  Strophe$2.debug(\"Websocket closed\");\n                }\n              },\n\n              /** PrivateFunction: _no_auth_received\n               *\n               * Called on stream start/restart when no stream:features\n               * has been received.\n               */\n              _no_auth_received: function _no_auth_received(callback) {\n                Strophe$2.error(\"Server did not offer a supported authentication mechanism\");\n\n                this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, Strophe$2.ErrorCondition.NO_AUTH_MECH);\n\n                if (callback) {\n                  callback.call(this._conn);\n                }\n\n                this._conn._doDisconnect();\n              },\n\n              /** PrivateFunction: _onDisconnectTimeout\n               *  _Private_ timeout handler for handling non-graceful disconnection.\n               *\n               *  This does nothing for WebSockets\n               */\n              _onDisconnectTimeout: function _onDisconnectTimeout() {},\n\n              /** PrivateFunction: _abortAllRequests\n               *  _Private_ helper function that makes sure all pending requests are aborted.\n               */\n              _abortAllRequests: function _abortAllRequests() {},\n\n              /** PrivateFunction: _onError\n               * _Private_ function to handle websockets errors.\n               *\n               * Parameters:\n               * (Object) error - The websocket error.\n               */\n              _onError: function _onError(error) {\n                Strophe$2.error(\"Websocket error \" + error);\n\n                this._conn._changeConnectStatus(Strophe$2.Status.CONNFAIL, \"The WebSocket connection could not be established or was disconnected.\");\n\n                this._disconnect();\n              },\n\n              /** PrivateFunction: _onIdle\n               *  _Private_ function called by Strophe.Connection._onIdle\n               *\n               *  sends all queued stanzas\n               */\n              _onIdle: function _onIdle() {\n                var data = this._conn._data;\n\n                if (data.length > 0 && !this._conn.paused) {\n                  for (var i = 0; i < data.length; i++) {\n                    if (data[i] !== null) {\n                      var stanza = void 0;\n\n                      if (data[i] === \"restart\") {\n                        stanza = this._buildStream().tree();\n                      } else {\n                        stanza = data[i];\n                      }\n\n                      var rawStanza = Strophe$2.serialize(stanza);\n\n                      this._conn.xmlOutput(stanza);\n\n                      this._conn.rawOutput(rawStanza);\n\n                      this.socket.send(rawStanza);\n                    }\n                  }\n\n                  this._conn._data = [];\n                }\n              },\n\n              /** PrivateFunction: _onMessage\n               * _Private_ function to handle websockets messages.\n               *\n               * This function parses each of the messages as if they are full documents.\n               * [TODO : We may actually want to use a SAX Push parser].\n               *\n               * Since all XMPP traffic starts with\n               *  <stream:stream version='1.0'\n               *                 xml:lang='en'\n               *                 xmlns='jabber:client'\n               *                 xmlns:stream='http://etherx.jabber.org/streams'\n               *                 id='3697395463'\n               *                 from='SERVER'>\n               *\n               * The first stanza will always fail to be parsed.\n               *\n               * Additionally, the seconds stanza will always be <stream:features> with\n               * the stream NS defined in the previous stanza, so we need to 'force'\n               * the inclusion of the NS in this stanza.\n               *\n               * Parameters:\n               * (string) message - The websocket message.\n               */\n              _onMessage: function _onMessage(message) {\n                var elem; // check for closing stream\n\n                var close = '<close xmlns=\"urn:ietf:params:xml:ns:xmpp-framing\" />';\n\n                if (message.data === close) {\n                  this._conn.rawInput(close);\n\n                  this._conn.xmlInput(message);\n\n                  if (!this._conn.disconnecting) {\n                    this._conn._doDisconnect();\n                  }\n\n                  return;\n                } else if (message.data.search(\"<open \") === 0) {\n                  // This handles stream restarts\n                  elem = new DOMParser().parseFromString(message.data, \"text/xml\").documentElement;\n\n                  if (!this._handleStreamStart(elem)) {\n                    return;\n                  }\n                } else {\n                  var data = this._streamWrap(message.data);\n\n                  elem = new DOMParser().parseFromString(data, \"text/xml\").documentElement;\n                }\n\n                if (this._check_streamerror(elem, Strophe$2.Status.ERROR)) {\n                  return;\n                } //handle unavailable presence stanza before disconnecting\n\n\n                if (this._conn.disconnecting && elem.firstChild.nodeName === \"presence\" && elem.firstChild.getAttribute(\"type\") === \"unavailable\") {\n                  this._conn.xmlInput(elem);\n\n                  this._conn.rawInput(Strophe$2.serialize(elem)); // if we are already disconnecting we will ignore the unavailable stanza and\n                  // wait for the </stream:stream> tag before we close the connection\n\n\n                  return;\n                }\n\n                this._conn._dataRecv(elem, message.data);\n              },\n\n              /** PrivateFunction: _onOpen\n               * _Private_ function to handle websockets connection setup.\n               *\n               * The opening stream tag is sent here.\n               */\n              _onOpen: function _onOpen() {\n                Strophe$2.debug(\"Websocket open\");\n\n                var start = this._buildStream();\n\n                this._conn.xmlOutput(start.tree());\n\n                var startString = Strophe$2.serialize(start);\n\n                this._conn.rawOutput(startString);\n\n                this.socket.send(startString);\n              },\n\n              /** PrivateFunction: _reqToData\n               * _Private_ function to get a stanza out of a request.\n               *\n               * WebSockets don't use requests, so the passed argument is just returned.\n               *\n               *  Parameters:\n               *    (Object) stanza - The stanza.\n               *\n               *  Returns:\n               *    The stanza that was passed.\n               */\n              _reqToData: function _reqToData(stanza) {\n                return stanza;\n              },\n\n              /** PrivateFunction: _send\n               *  _Private_ part of the Connection.send function for WebSocket\n               *\n               * Just flushes the messages that are in the queue\n               */\n              _send: function _send() {\n                this._conn.flush();\n              },\n\n              /** PrivateFunction: _sendRestart\n               *\n               *  Send an xmpp:restart stanza.\n               */\n              _sendRestart: function _sendRestart() {\n                clearTimeout(this._conn._idleTimeout);\n\n                this._conn._onIdle.bind(this._conn)();\n              }\n            };\n\n            global$1.Strophe = core.Strophe;\n            global$1.$build = core.$build;\n            global$1.$iq = core.$iq;\n            global$1.$msg = core.$msg;\n            global$1.$pres = core.$pres;\n\n            return core;\n\n}));\n","(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(require('strophe.js')) :\n\ttypeof define === 'function' && define.amd ? define(['strophe.js'], factory) :\n\t(factory(global.window));\n}(this, (function (strophe_js) { 'use strict';\n\nstrophe_js.Strophe.addConnectionPlugin('disco',\n{\n    _connection: null,\n    _identities : [],\n    _features : [],\n    _items : [],\n    /** Function: init\n     * Plugin init\n     *\n     * Parameters:\n     *   (Strophe.Connection) conn - Strophe connection\n     */\n    init: function(conn)\n    {\n    this._connection = conn;\n        this._identities = [];\n        this._features   = [];\n        this._items      = [];\n        // disco info\n        conn.addHandler(this._onDiscoInfo.bind(this), strophe_js.Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);\n        // disco items\n        conn.addHandler(this._onDiscoItems.bind(this), strophe_js.Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);\n    },\n    /** Function: addIdentity\n     * See http://xmpp.org/registrar/disco-categories.html\n     * Parameters:\n     *   (String) category - category of identity (like client, automation, etc ...)\n     *   (String) type - type of identity (like pc, web, bot , etc ...)\n     *   (String) name - name of identity in natural language\n     *   (String) lang - lang of name parameter\n     *\n     * Returns:\n     *   Boolean\n     */\n    addIdentity: function(category, type, name, lang)\n    {\n        for (var i=0; i<this._identities.length; i++)\n        {\n            if (this._identities[i].category == category &&\n                this._identities[i].type == type &&\n                this._identities[i].name == name &&\n                this._identities[i].lang == lang)\n            {\n                return false;\n            }\n        }\n        this._identities.push({category: category, type: type, name: name, lang: lang});\n        return true;\n    },\n    /** Function: addFeature\n     *\n     * Parameters:\n     *   (String) var_name - feature name (like jabber:iq:version)\n     *\n     * Returns:\n     *   boolean\n     */\n    addFeature: function(var_name)\n    {\n        for (var i=0; i<this._features.length; i++)\n        {\n             if (this._features[i] == var_name)\n                 return false;\n        }\n        this._features.push(var_name);\n        return true;\n    },\n    /** Function: removeFeature\n     *\n     * Parameters:\n     *   (String) var_name - feature name (like jabber:iq:version)\n     *\n     * Returns:\n     *   boolean\n     */\n    removeFeature: function(var_name)\n    {\n        for (var i=0; i<this._features.length; i++)\n        {\n             if (this._features[i] === var_name){\n                 this._features.splice(i,1);\n                 return true;\n             }\n        }\n        return false;\n    },\n    /** Function: addItem\n     *\n     * Parameters:\n     *   (String) jid\n     *   (String) name\n     *   (String) node\n     *   (Function) call_back\n     *\n     * Returns:\n     *   boolean\n     */\n    addItem: function(jid, name, node, call_back)\n    {\n        if (node && !call_back)\n            return false;\n        this._items.push({jid: jid, name: name, node: node, call_back: call_back});\n        return true;\n    },\n    /** Function: info\n     * Info query\n     *\n     * Parameters:\n     *   (Function) call_back\n     *   (String) jid\n     *   (String) node\n     */\n    info: function(jid, node, success, error, timeout)\n    {\n        var attrs = {xmlns: strophe_js.Strophe.NS.DISCO_INFO};\n        if (node)\n            attrs.node = node;\n\n        var info = strophe_js.$iq({from:this._connection.jid,\n                         to:jid, type:'get'}).c('query', attrs);\n        this._connection.sendIQ(info, success, error, timeout);\n    },\n    /** Function: items\n     * Items query\n     *\n     * Parameters:\n     *   (Function) call_back\n     *   (String) jid\n     *   (String) node\n     */\n    items: function(jid, node, success, error, timeout)\n    {\n        var attrs = {xmlns: strophe_js.Strophe.NS.DISCO_ITEMS};\n        if (node)\n            attrs.node = node;\n\n        var items = strophe_js.$iq({from:this._connection.jid,\n                         to:jid, type:'get'}).c('query', attrs);\n        this._connection.sendIQ(items, success, error, timeout);\n    },\n\n    /** PrivateFunction: _buildIQResult\n     */\n    _buildIQResult: function(stanza, query_attrs)\n    {\n        var id   =  stanza.getAttribute('id');\n        var from = stanza.getAttribute('from');\n        var iqresult = strophe_js.$iq({type: 'result', id: id});\n\n        if (from !== null) {\n            iqresult.attrs({to: from});\n        }\n\n        return iqresult.c('query', query_attrs);\n    },\n\n    /** PrivateFunction: _onDiscoInfo\n     * Called when receive info request\n     */\n    _onDiscoInfo: function(stanza)\n    {\n        var node = stanza.getElementsByTagName('query')[0].getAttribute('node');\n        var attrs = {xmlns: strophe_js.Strophe.NS.DISCO_INFO};\n        var i;\n        if (node)\n        {\n            attrs.node = node;\n        }\n        var iqresult = this._buildIQResult(stanza, attrs);\n        for (i=0; i<this._identities.length; i++)\n        {\n            attrs = {category: this._identities[i].category,\n                         type    : this._identities[i].type};\n            if (this._identities[i].name)\n                attrs.name = this._identities[i].name;\n            if (this._identities[i].lang)\n                attrs['xml:lang'] = this._identities[i].lang;\n            iqresult.c('identity', attrs).up();\n        }\n        for (i=0; i<this._features.length; i++)\n        {\n            iqresult.c('feature', {'var':this._features[i]}).up();\n        }\n        this._connection.send(iqresult.tree());\n        return true;\n    },\n    /** PrivateFunction: _onDiscoItems\n     * Called when receive items request\n     */\n    _onDiscoItems: function(stanza)\n    {\n        var query_attrs = {xmlns: strophe_js.Strophe.NS.DISCO_ITEMS};\n        var node = stanza.getElementsByTagName('query')[0].getAttribute('node');\n        var items, i;\n        if (node)\n        {\n            query_attrs.node = node;\n            items = [];\n            for (i = 0; i < this._items.length; i++)\n            {\n                if (this._items[i].node == node)\n                {\n                    items = this._items[i].call_back(stanza);\n                    break;\n                }\n            }\n        }\n        else\n        {\n            items = this._items;\n        }\n        var iqresult = this._buildIQResult(stanza, query_attrs);\n        for (i = 0; i < items.length; i++)\n        {\n            var attrs = {jid:  items[i].jid};\n            if (items[i].name)\n                attrs.name = items[i].name;\n            if (items[i].node)\n                attrs.node = items[i].node;\n            iqresult.c('item', attrs).up();\n        }\n        this._connection.send(iqresult.tree());\n        return true;\n    }\n});\n\n})));\n//# sourceMappingURL=strophe.disco.js.map\n","(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(require('strophe.js')) :\n\ttypeof define === 'function' && define.amd ? define(['strophe.js'], factory) :\n\t(global = global || self, factory(global.window));\n}(this, (function (strophe_js) { 'use strict';\n\n\t/**\n\t* StropheJS - Stream Management XEP-0198\n\t*\n\t* This plugin implements stream management ACK capabilities of the specs XEP-0198.\n\t* Note: Resumption is not supported in this current implementation.\n\t*\n\t* Reference: http://xmpp.org/extensions/xep-0198.html\n\t*\n\t* @class streamManagement\n\t*/\n\tstrophe_js.Strophe.addConnectionPlugin('streamManagement', {\n\n\t\t/**\n\t\t* @property {Boolean} logging: Set to true to enable logging regarding out of sync stanzas.\n\t\t*/\n\t\tlogging: false,\n\n\t\t/**\n\t\t* @property {Boolean} autoSendCountOnEveryIncomingStanza: Set to true to send an 'a' response after every stanza.\n\t\t* @default false\n\t\t* @public\n\t\t*/\n\t\tautoSendCountOnEveryIncomingStanza: false,\n\n\t\t/**\n\t\t* @property {Integer} requestResponseInterval: Set this value to send a request for counter on very interval\n\t\t* number of stanzas sent. Set to 0 to disable.\n\t\t* @default 5\n\t\t* @public\n\t\t*/\n\t\trequestResponseInterval: 5,\n\n\t\t/**\n\t\t* @property {Pointer} _c: Strophe connection instance.\n\t\t* @private\n\t\t*/\n\t\t_c: null,\n\n\t\t/**\n\t\t* @property {String} _NS XMPP Namespace.\n\t\t* @private\n\t\t*/\n\t\t_NS: 'urn:xmpp:sm:3',\n\n\t\t/**\n\t\t* @property {Boolean} _isStreamManagementEnabled\n\t\t* @private\n\t\t*/\n\t\t_isStreamManagementEnabled: false,\n\n\t\t/**\n\t\t* @property {Integer} _serverProcesssedStanzasCounter: Keeps count of stanzas confirmed processed by the server.\n\t\t* The server is the source of truth of this value. It is the 'h' attribute on the latest 'a' element received\n\t\t* from the server.\n\t\t* @private\n\t\t*/\n\t\t_serverProcesssedStanzasCounter: null,\n\n\t\t/**\n\t\t* @property {Integer} _clientProcessedStanzasCounter: Counter of stanzas received by the client from the server.\n\t\t* Client is the source of truth of this value. It is the 'h' attribute in the 'a' sent from the client to\n\t\t* the server.\n\t\t* @private\n\t\t*/\n\t\t_clientProcessedStanzasCounter: null,\n\n\t\t/**\n\t\t* @property {Integer} _clientSentStanzasCounter\n\t\t* @private\n\t\t*/\n\t\t_clientSentStanzasCounter: null,\n\n\t\t/**\n\t\t* Stores a reference to Strophe connection xmlOutput function to wrap counting functionality.\n\t\t* @method _originalXMLOutput\n\t\t* @type {Handler}\n\t\t* @private\n\t\t*/\n\t\t_originalXMLOutput: null,\n\n\t\t/**\n\t\t* @property {Handler} _requestHandler: Stores reference to handler that process count request from server.\n\t\t* @private\n\t\t*/\n\t\t_requestHandler: null,\n\n\t\t/**\n\t\t* @property {Handler} _incomingHandler: Stores reference to handler that processes incoming stanzas count.\n\t\t* @private\n\t\t*/\n\t\t_incomingHandler: null,\n\n\t\t/**\n\t\t* @property {Integer} _requestResponseIntervalCount: Counts sent stanzas since last response request.\n\t\t*/\n\t\t_requestResponseIntervalCount: 0,\n\n\t\t/**\n\t\t * @property {boolean} _isSupported: indicates whether or not the server has advertised support for the stream\n\t\t * management namespace.\n\t\t */\n\t\t_isSupported: false,\n\n\t\t/**\n\t\t* @property {Queue} _unacknowledgedStanzas: Maintains a list of packet ids for stanzas which have yet to be acknowledged.\n\t\t*/\n\t\t_unacknowledgedStanzas: [],\n\n\t\t/**\n\t\t* @property {Array} _acknowledgedStanzaListeners: Stores callbacks for each stanza acknowledged by the server.\n\t\t* Provides the packet id of the stanza as a parameter.\n\t\t* @private\n\t\t*/\n\t\t_acknowledgedStanzaListeners: [],\n\n\t\taddAcknowledgedStanzaListener: function(listener) {\n\t\t\tthis._acknowledgedStanzaListeners.push(listener);\n\t\t},\n\n\t\tenable: function(resume) {\n\t\t\tif (!this._isSupported) {\n\t\t\t\tthrow new Error('The server doesn\\'t support urn:xmpp:sm:3 namespace');\n\t\t\t} else if (this._connectionStatus !== strophe_js.Strophe.Status.CONNECTED) {\n\t\t\t\tthrow new Error('enable() can only be called in the CONNECTED state');\n\t\t\t}\n\t\t\tthis._c.send(strophe_js.$build('enable', { xmlns: this._NS, resume }));\n\t\t\tthis._c.flush();\n\t\t\tthis._c.pause();\n\t\t},\n\n\t\tgetResumeToken: function() {\n\t\t\treturn this._resumeToken;\n\t\t},\n\n\t\tisSupported() {\n\t\t\treturn this._isSupported;\n\t\t},\n\n\t\tresume: function() {\n\t\t\tif (!this.getResumeToken()) {\n\t\t\t\tthrow new Error('No resume token');\n\t\t\t}\n\t\t\tif (this._connectionStatus !== strophe_js.Strophe.Status.DISCONNECTED) {\n\t\t\t\tthrow new Error('resume() can only be called in the DISCONNECTED state');\n\t\t\t}\n\n\t\t\tthis._c.options.explicitResourceBinding = true;\n\t\t\tthis._resuming = true;\n\n\t\t\tthis._originalConnect.apply(this._c, this._connectArgs);\n\t\t},\n\n\t\trequestAcknowledgement: function() {\n\t\t\tif (this._connectionStatus !== strophe_js.Strophe.Status.CONNECTED) {\n\t\t\t\tthrow new Error('requestAcknowledgement() can only be called in the CONNECTED state');\n\t\t\t}\n\t\t\tthis._requestResponseIntervalCount = 0;\n\t\t\tthis._c.send(strophe_js.$build('r', { xmlns: this._NS }));\n\t\t},\n\n\t\tgetOutgoingCounter: function() {\n\t\t\treturn this._clientSentStanzasCounter;\n\t\t},\n\n\t\tgetIncomingCounter: function() {\n\t\t\treturn this._clientProcessedStanzasCounter;\n\t\t},\n\n\t\tinit: function(conn) {\n\t\t\tthis._c = conn;\n\t\t\tstrophe_js.Strophe.addNamespace('SM', this._NS);\n\n\t\t\t// Storing original xmlOutput function to use additional logic\n\t\t\tthis._originalXMLOutput = this._c.xmlOutput;\n\t\t\tthis._c.xmlOutput = this.xmlOutput.bind(this);\n\n\t\t\tthis._originalConnect = this._c.connect;\n\t\t\tthis._c.connect = this._interceptConnectArgs.bind(this);\n\n\t\t\tthis._originalOnStreamFeaturesAfterSASL = this._c._onStreamFeaturesAfterSASL;\n\t\t\tthis._c._onStreamFeaturesAfterSASL = this._onStreamFeaturesAfterSASL.bind(this);\n\n\t\t\tthis._originalDoDisconnect = this._c._doDisconnect;\n\t\t\tthis._c._doDisconnect = this._interceptDoDisconnect.bind(this);\n\n\t\t\tthis._originalDisconnect = this._c.disconnect;\n\t\t\tthis._c.disconnect = this._interceptDisconnect.bind(this);\n\t\t},\n\n\t\t_interceptDisconnect: function() {\n\t\t\tthis._resumeToken = undefined;\n\t\t\tthis._originalDisconnect.apply(this._c, arguments);\n\t\t},\n\n\t\t_interceptDoDisconnect: function() {\n\t\t\tif (this.getResumeToken()\n\t\t\t\t\t&& !this._resuming\n\t\t\t\t\t&& this._c.connected && !this._c.disconnecting) {\n\t\t\t\tthis._resumeState = {\n\t\t\t\t\thandlers: this._c.handlers,\n\t\t\t\t\ttimedHandlers: this._c.timedHandlers,\n\t\t\t\t\tremoveTimeds: this._c.removeTimeds,\n\t\t\t\t\tremoveHandlers: this._c.removeHandlers,\n\t\t\t\t\taddTimeds: this._c.addTimeds,\n\t\t\t\t\taddHandlers: this._c.addHandlers\n\t\t\t\t};\n\t\t\t\tthis._storedJid = this._c.jid;\n\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM stored resume state, handler count: ' + this._resumeState.handlers.length);\n\t\t\t}\n\n\t\t\t// Remove any queued stanzas from the buffer that have failed to send while the socket was closed,\n\t\t\t// as they would interfere with the resume flow. They will be resent anyway.\n\t\t\tthis._c._data = [];\n\n\t\t\tthis._originalDoDisconnect.apply(this._c, arguments);\n\t\t},\n\n\t\t_interceptConnectArgs: function() {\n\t\t\tthis._connectArgs = arguments;\n\n\t\t\tthis._originalConnect.apply(this._c, arguments);\n\t\t},\n\n\t\t_onStreamFeaturesAfterSASL: function(elem) {\n\t\t\tthis._isSupported = elem.getElementsByTagNameNS(this._NS, \"sm\").length > 0;\n\n\t\t\treturn this._originalOnStreamFeaturesAfterSASL.apply(this._c, arguments);\n\t\t},\n\n\t\tstatusChanged: function (status) {\n\t\t\tthis._connectionStatus = status;\n\t\t\tif (!this.getResumeToken()\n\t\t\t\t&& (status === strophe_js.Strophe.Status.CONNECTED || status === strophe_js.Strophe.Status.DISCONNECTED)) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM reset state');\n\n\t\t\t\tthis._serverProcesssedStanzasCounter = 0;\n\t\t\t\tthis._clientProcessedStanzasCounter = 0;\n\n\t\t\t\tthis._clientSentStanzasCounter = 0;\n\n\t\t\t\tthis._isStreamManagementEnabled = false;\n\t\t\t\tthis._requestResponseIntervalCount = 0;\n\n\t\t\t\t// FIXME not described in JSDocs\n\t\t\t\tthis._resuming = false;\n\n\t\t\t\tif (status === strophe_js.Strophe.Status.DISCONNECTED) {\n\t\t\t\t\tthis._isSupported = false;\n\t\t\t\t}\n\n\t\t\t\tthis._unacknowledgedStanzas = [];\n\n\t\t\t\tif (this._requestHandler) {\n\t\t\t\t\tthis._c.deleteHandler(this._requestHandler);\n\t\t\t\t}\n\n\t\t\t\tif (this._incomingHandler) {\n\t\t\t\t\tthis._c.deleteHandler(this._incomingHandler);\n\t\t\t\t}\n\n\t\t\t\tthis._requestHandler = this._c.addHandler(this._handleServerRequestHandler.bind(this), this._NS, 'r');\n\t\t\t\tthis._ackHandler = this._c.addHandler(this._handleServerAck.bind(this), this._NS, 'a');\n\t\t\t\tthis._incomingHandler = this._c.addHandler(this._incomingStanzaHandler.bind(this));\n\n\t\t\t\t// FIXME handler instances stored, but never used\n\t\t\t\tthis._enabledHandler = this._c._addSysHandler(this._handleEnabled.bind(this), this._NS, 'enabled');\n\t\t\t\tthis._resumeFailedHandler = this._c._addSysHandler(this._handleResumeFailed.bind(this), this._NS, 'failed');\n\t\t\t\tthis._resumedHandler =  this._c._addSysHandler(this._handleResumed.bind(this), this._NS,'resumed');\n\n\t\t\t} else if (status === strophe_js.Strophe.Status.BINDREQUIRED)  {\n\t\t\t\tthis._c.jid = this._storedJid;\n\n\t\t\t\t// Restore Strophe handlers\n\t\t\t\tfor (const property in this._resumeState) {\n\t\t\t\t\tthis._c[property] = this._resumeState[property];\n\t\t\t\t}\n\n\t\t\t\t// FIXME check conditions if there's session ID and if enabled\n\t\t\t\tthis._c.send(strophe_js.$build('resume', {\n\t\t\t\t\txmlns: this._NS,\n\t\t\t\t\th: this._clientProcessedStanzasCounter,\n\t\t\t\t\tprevid: this._resumeToken\n\t\t\t\t}));\n\t\t\t\tthis._c.flush();\n\t\t\t} else if (status === strophe_js.Strophe.Status.ERROR) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM cleared resume token on error');\n\t\t\t\tthis._resumeToken = undefined;\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t* This method overrides the send method implemented by Strophe.Connection\n\t\t* to count outgoing stanzas\n\t\t*\n\t\t* @method Send\n\t\t* @public\n\t\t*/\n\t\txmlOutput: function(elem) {\n\t\t\tif (strophe_js.Strophe.isTagEqual(elem, 'iq') ||\n\t\t\t\tstrophe_js.Strophe.isTagEqual(elem, 'presence') ||\n\t\t\t\tstrophe_js.Strophe.isTagEqual(elem, 'message')) {\n\t\t\t\tthis._increaseSentStanzasCounter(elem);\n\t\t\t}\n\n\t\t\treturn this._originalXMLOutput.call(this._c, elem);\n\t\t},\n\n\t\t_handleEnabled: function(elem) {\n\t\t\tthis._isStreamManagementEnabled = true;\n\t\t\t// FIXME fail if requested, but not enabled\n\t\t\tthis._resumeToken = elem.getAttribute('resume') === 'true' && elem.getAttribute('id');\n\n\t\t\tthis._c.resume();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleResumeFailed: function(elem) {\n\t\t\tconst error = elem && elem.firstElementChild && elem.firstElementChild.tagName;\n\n\t\t\tthis._c._changeConnectStatus(strophe_js.Strophe.Status.ERROR, error, elem);\n\t\t\tthis._c._doDisconnect();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleResumed: function(elem) {\n\t\t\t// FIXME check if in the correct state\n\t\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\t\tthis._resuming = false;\n\t\t\tthis._c.do_bind = false; // No need to bind our resource anymore\n\t\t\tthis._c.authenticated = true;\n\t\t\tthis._c.restored = true;\n\n\t\t\tif (this._unacknowledgedStanzas.length > 0) {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM Sending unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t\tfor(const stanza of this._unacknowledgedStanzas) {\n\t\t\t\t\tthis._c.send(stanza);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tthis.logging && strophe_js.Strophe.debug('SM No unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t}\n\n\t\t\tthis._c._changeConnectStatus(strophe_js.Strophe.Status.CONNECTED, null);\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_incomingStanzaHandler: function(elem) {\n\t\t\tif (strophe_js.Strophe.isTagEqual(elem, 'iq') || strophe_js.Strophe.isTagEqual(elem, 'presence') || strophe_js.Strophe.isTagEqual(elem, 'message'))  {\n\t\t\t\tthis._increaseReceivedStanzasCounter();\n\n\t\t\t\tif (this.autoSendCountOnEveryIncomingStanza) {\n\t\t\t\t\tthis._answerProcessedStanzas();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleAcknowledgedStanzas: function(reportedHandledCount, lastKnownHandledCount) {\n\t\t\tvar delta = reportedHandledCount - lastKnownHandledCount;\n\n\t\t\tif (delta < 0) {\n\t\t\t\tthis._throwError('New reported stanza count lower than previous. New: ' + reportedHandledCount + ' - Previous: ' + lastKnownHandledCount);\n\t\t\t}\n\n\t\t\tif (delta > this._unacknowledgedStanzas.length) {\n\t\t\t\tthis._throwError('Higher reported acknowledge count than unacknowledged stanzas. Reported Acknowledge Count: ' + delta + ' - Unacknowledge Stanza Count: ' + this._unacknowledgedStanzas.length + ' - New: ' + reportedHandledCount + ' - Previous: ' + lastKnownHandledCount);\n\t\t\t}\n\n\t\t\tfor(var i = 0; i < delta; i++) {\n\t\t\t\tvar stanza = this._unacknowledgedStanzas.shift();\n\t\t\t\tfor (var j = 0; j < this._acknowledgedStanzaListeners.length; j++) {\n\t\t\t\t\tthis._acknowledgedStanzaListeners[j](stanza);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.logging && this._unacknowledgedStanzas.length > 0) {\n\t\t\t\tstrophe_js.Strophe.warn('SM Unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\t}\n\n\t\t\tthis._serverProcesssedStanzasCounter = reportedHandledCount;\n\n\t\t\tif (this.requestResponseInterval > 0) {\n\t\t\t\tthis._requestResponseIntervalCount = 0;\n\t\t\t}\n\t\t},\n\n\t\t_handleServerRequestHandler: function() {\n\t\t\tthis._answerProcessedStanzas();\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_handleServerAck: function(elem){\n\t\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\t\treturn true;\n\t\t},\n\n\t\t_answerProcessedStanzas: function() {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tthis._c.send(strophe_js.$build('a', { xmlns: this._NS, h: this._clientProcessedStanzasCounter }));\n\t\t\t}\n\t\t},\n\n\t\t_increaseSentStanzasCounter: function(elem) {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tif (this._unacknowledgedStanzas.indexOf(elem) !== -1) {\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis._unacknowledgedStanzas.push(elem);\n\t\t\t\tthis._clientSentStanzasCounter++;\n\n\t\t\t\tif (this.requestResponseInterval > 0) {\n\t\t\t\t\tthis._requestResponseIntervalCount++;\n\n\t\t\t\t\tif (this._requestResponseIntervalCount === this.requestResponseInterval) {\n\t\t\t\t\t\t// FIXME Can not call send from onIdle.\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tif (this._connectionStatus === strophe_js.Strophe.Status.CONNECTED) {\n\t\t\t\t\t\t\t\tthis.requestAcknowledgement();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}, 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t_increaseReceivedStanzasCounter: function() {\n\t\t\tif (this._isStreamManagementEnabled) {\n\t\t\t\tthis._clientProcessedStanzasCounter++;\n\t\t\t}\n\t\t},\n\n\t\t_throwError: function(msg) {\n\t\t\tstrophe_js.Strophe.error(msg);\n\t\t\tthrow new Error(msg);\n\t\t}\n\n\t});\n\n})));\n//# sourceMappingURL=strophe.stream-management.js.map\n","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nconst byteToHex = [];\n\nfor (let i = 0; i < 256; ++i) {\n  byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction bytesToUuid(buf, offset) {\n  const i = offset || 0;\n  const bth = byteToHex; // Note: Be careful editing this code!  It's been tuned for performance\n  // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n\n  return (bth[buf[i + 0]] + bth[buf[i + 1]] + bth[buf[i + 2]] + bth[buf[i + 3]] + '-' + bth[buf[i + 4]] + bth[buf[i + 5]] + '-' + bth[buf[i + 6]] + bth[buf[i + 7]] + '-' + bth[buf[i + 8]] + bth[buf[i + 9]] + '-' + bth[buf[i + 10]] + bth[buf[i + 11]] + bth[buf[i + 12]] + bth[buf[i + 13]] + bth[buf[i + 14]] + bth[buf[i + 15]]).toLowerCase();\n}\n\nvar _default = bytesToUuid;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nObject.defineProperty(exports, \"v1\", {\n  enumerable: true,\n  get: function () {\n    return _v.default;\n  }\n});\nObject.defineProperty(exports, \"v3\", {\n  enumerable: true,\n  get: function () {\n    return _v2.default;\n  }\n});\nObject.defineProperty(exports, \"v4\", {\n  enumerable: true,\n  get: function () {\n    return _v3.default;\n  }\n});\nObject.defineProperty(exports, \"v5\", {\n  enumerable: true,\n  get: function () {\n    return _v4.default;\n  }\n});\n\nvar _v = _interopRequireDefault(require(\"./v1.js\"));\n\nvar _v2 = _interopRequireDefault(require(\"./v3.js\"));\n\nvar _v3 = _interopRequireDefault(require(\"./v4.js\"));\n\nvar _v4 = _interopRequireDefault(require(\"./v5.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\n/*\n * Browser-compatible JavaScript MD5\n *\n * Modification of JavaScript MD5\n * https://github.com/blueimp/JavaScript-MD5\n *\n * Copyright 2011, Sebastian Tschan\n * https://blueimp.net\n *\n * Licensed under the MIT license:\n * https://opensource.org/licenses/MIT\n *\n * Based on\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\nfunction md5(bytes) {\n  if (typeof bytes === 'string') {\n    const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape\n\n    bytes = new Uint8Array(msg.length);\n\n    for (let i = 0; i < msg.length; ++i) {\n      bytes[i] = msg.charCodeAt(i);\n    }\n  }\n\n  return md5ToHexEncodedArray(wordsToMd5(bytesToWords(bytes), bytes.length * 8));\n}\n/*\n * Convert an array of little-endian words to an array of bytes\n */\n\n\nfunction md5ToHexEncodedArray(input) {\n  const output = [];\n  const length32 = input.length * 32;\n  const hexTab = '0123456789abcdef';\n\n  for (let i = 0; i < length32; i += 8) {\n    const x = input[i >> 5] >>> i % 32 & 0xff;\n    const hex = parseInt(hexTab.charAt(x >>> 4 & 0x0f) + hexTab.charAt(x & 0x0f), 16);\n    output.push(hex);\n  }\n\n  return output;\n}\n/**\n * Calculate output length with padding and bit length\n */\n\n\nfunction getOutputLength(inputLength8) {\n  return (inputLength8 + 64 >>> 9 << 4) + 14 + 1;\n}\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length.\n */\n\n\nfunction wordsToMd5(x, len) {\n  /* append padding */\n  x[len >> 5] |= 0x80 << len % 32;\n  x[getOutputLength(len) - 1] = len;\n  let a = 1732584193;\n  let b = -271733879;\n  let c = -1732584194;\n  let d = 271733878;\n\n  for (let i = 0; i < x.length; i += 16) {\n    const olda = a;\n    const oldb = b;\n    const oldc = c;\n    const oldd = d;\n    a = md5ff(a, b, c, d, x[i], 7, -680876936);\n    d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);\n    c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);\n    b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);\n    a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);\n    d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);\n    c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);\n    b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);\n    a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);\n    d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);\n    c = md5ff(c, d, a, b, x[i + 10], 17, -42063);\n    b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);\n    a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);\n    d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);\n    c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);\n    b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);\n    a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);\n    d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);\n    c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);\n    b = md5gg(b, c, d, a, x[i], 20, -373897302);\n    a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);\n    d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);\n    c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);\n    b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);\n    a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);\n    d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);\n    c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);\n    b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);\n    a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);\n    d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);\n    c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);\n    b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);\n    a = md5hh(a, b, c, d, x[i + 5], 4, -378558);\n    d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);\n    c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);\n    b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);\n    a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);\n    d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);\n    c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);\n    b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);\n    a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);\n    d = md5hh(d, a, b, c, x[i], 11, -358537222);\n    c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);\n    b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);\n    a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);\n    d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);\n    c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);\n    b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);\n    a = md5ii(a, b, c, d, x[i], 6, -198630844);\n    d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);\n    c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);\n    b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);\n    a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);\n    d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);\n    c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);\n    b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);\n    a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);\n    d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);\n    c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);\n    b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);\n    a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);\n    d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);\n    c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);\n    b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);\n    a = safeAdd(a, olda);\n    b = safeAdd(b, oldb);\n    c = safeAdd(c, oldc);\n    d = safeAdd(d, oldd);\n  }\n\n  return [a, b, c, d];\n}\n/*\n * Convert an array bytes to an array of little-endian words\n * Characters >255 have their high-byte silently ignored.\n */\n\n\nfunction bytesToWords(input) {\n  if (input.length === 0) {\n    return [];\n  }\n\n  const length8 = input.length * 8;\n  const output = new Uint32Array(getOutputLength(length8));\n\n  for (let i = 0; i < length8; i += 8) {\n    output[i >> 5] |= (input[i / 8] & 0xff) << i % 32;\n  }\n\n  return output;\n}\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\n\n\nfunction safeAdd(x, y) {\n  const lsw = (x & 0xffff) + (y & 0xffff);\n  const msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n  return msw << 16 | lsw & 0xffff;\n}\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\n\n\nfunction bitRotateLeft(num, cnt) {\n  return num << cnt | num >>> 32 - cnt;\n}\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\n\n\nfunction md5cmn(q, a, b, x, s, t) {\n  return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);\n}\n\nfunction md5ff(a, b, c, d, x, s, t) {\n  return md5cmn(b & c | ~b & d, a, b, x, s, t);\n}\n\nfunction md5gg(a, b, c, d, x, s, t) {\n  return md5cmn(b & d | c & ~d, a, b, x, s, t);\n}\n\nfunction md5hh(a, b, c, d, x, s, t) {\n  return md5cmn(b ^ c ^ d, a, b, x, s, t);\n}\n\nfunction md5ii(a, b, c, d, x, s, t) {\n  return md5cmn(c ^ (b | ~d), a, b, x, s, t);\n}\n\nvar _default = md5;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = rng;\n// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\n// getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation. Also,\n// find the complete implementation of crypto (msCrypto) on IE11.\nconst getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\nconst rnds8 = new Uint8Array(16);\n\nfunction rng() {\n  if (!getRandomValues) {\n    throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n  }\n\n  return getRandomValues(rnds8);\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\n// Adapted from Chris Veness' SHA1 code at\n// http://www.movable-type.co.uk/scripts/sha1.html\nfunction f(s, x, y, z) {\n  switch (s) {\n    case 0:\n      return x & y ^ ~x & z;\n\n    case 1:\n      return x ^ y ^ z;\n\n    case 2:\n      return x & y ^ x & z ^ y & z;\n\n    case 3:\n      return x ^ y ^ z;\n  }\n}\n\nfunction ROTL(x, n) {\n  return x << n | x >>> 32 - n;\n}\n\nfunction sha1(bytes) {\n  const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];\n  const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];\n\n  if (typeof bytes === 'string') {\n    const msg = unescape(encodeURIComponent(bytes)); // UTF8 escape\n\n    bytes = [];\n\n    for (let i = 0; i < msg.length; ++i) {\n      bytes.push(msg.charCodeAt(i));\n    }\n  }\n\n  bytes.push(0x80);\n  const l = bytes.length / 4 + 2;\n  const N = Math.ceil(l / 16);\n  const M = new Array(N);\n\n  for (let i = 0; i < N; ++i) {\n    const arr = new Uint32Array(16);\n\n    for (let j = 0; j < 16; ++j) {\n      arr[j] = bytes[i * 64 + j * 4] << 24 | bytes[i * 64 + j * 4 + 1] << 16 | bytes[i * 64 + j * 4 + 2] << 8 | bytes[i * 64 + j * 4 + 3];\n    }\n\n    M[i] = arr;\n  }\n\n  M[N - 1][14] = (bytes.length - 1) * 8 / Math.pow(2, 32);\n  M[N - 1][14] = Math.floor(M[N - 1][14]);\n  M[N - 1][15] = (bytes.length - 1) * 8 & 0xffffffff;\n\n  for (let i = 0; i < N; ++i) {\n    const W = new Uint32Array(80);\n\n    for (let t = 0; t < 16; ++t) {\n      W[t] = M[i][t];\n    }\n\n    for (let t = 16; t < 80; ++t) {\n      W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);\n    }\n\n    let a = H[0];\n    let b = H[1];\n    let c = H[2];\n    let d = H[3];\n    let e = H[4];\n\n    for (let t = 0; t < 80; ++t) {\n      const s = Math.floor(t / 20);\n      const T = ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t] >>> 0;\n      e = d;\n      d = c;\n      c = ROTL(b, 30) >>> 0;\n      b = a;\n      a = T;\n    }\n\n    H[0] = H[0] + a >>> 0;\n    H[1] = H[1] + b >>> 0;\n    H[2] = H[2] + c >>> 0;\n    H[3] = H[3] + d >>> 0;\n    H[4] = H[4] + e >>> 0;\n  }\n\n  return [H[0] >> 24 & 0xff, H[0] >> 16 & 0xff, H[0] >> 8 & 0xff, H[0] & 0xff, H[1] >> 24 & 0xff, H[1] >> 16 & 0xff, H[1] >> 8 & 0xff, H[1] & 0xff, H[2] >> 24 & 0xff, H[2] >> 16 & 0xff, H[2] >> 8 & 0xff, H[2] & 0xff, H[3] >> 24 & 0xff, H[3] >> 16 & 0xff, H[3] >> 8 & 0xff, H[3] & 0xff, H[4] >> 24 & 0xff, H[4] >> 16 & 0xff, H[4] >> 8 & 0xff, H[4] & 0xff];\n}\n\nvar _default = sha1;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _rng = _interopRequireDefault(require(\"./rng.js\"));\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n// **`v1()` - Generate time-based UUID**\n//\n// Inspired by https://github.com/LiosK/UUID.js\n// and http://docs.python.org/library/uuid.html\nlet _nodeId;\n\nlet _clockseq; // Previous uuid creation time\n\n\nlet _lastMSecs = 0;\nlet _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details\n\nfunction v1(options, buf, offset) {\n  let i = buf && offset || 0;\n  const b = buf || [];\n  options = options || {};\n  let node = options.node || _nodeId;\n  let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not\n  // specified.  We do this lazily to minimize issues related to insufficient\n  // system entropy.  See #189\n\n  if (node == null || clockseq == null) {\n    const seedBytes = options.random || (options.rng || _rng.default)();\n\n    if (node == null) {\n      // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)\n      node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]];\n    }\n\n    if (clockseq == null) {\n      // Per 4.2.2, randomize (14 bit) clockseq\n      clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;\n    }\n  } // UUID timestamps are 100 nano-second units since the Gregorian epoch,\n  // (1582-10-15 00:00).  JSNumbers aren't precise enough for this, so\n  // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'\n  // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.\n\n\n  let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock\n  // cycle to simulate higher resolution clock\n\n  let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs)\n\n  const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression\n\n  if (dt < 0 && options.clockseq === undefined) {\n    clockseq = clockseq + 1 & 0x3fff;\n  } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new\n  // time interval\n\n\n  if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {\n    nsecs = 0;\n  } // Per 4.2.1.2 Throw error if too many uuids are requested\n\n\n  if (nsecs >= 10000) {\n    throw new Error(\"uuid.v1(): Can't create more than 10M uuids/sec\");\n  }\n\n  _lastMSecs = msecs;\n  _lastNSecs = nsecs;\n  _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch\n\n  msecs += 12219292800000; // `time_low`\n\n  const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;\n  b[i++] = tl >>> 24 & 0xff;\n  b[i++] = tl >>> 16 & 0xff;\n  b[i++] = tl >>> 8 & 0xff;\n  b[i++] = tl & 0xff; // `time_mid`\n\n  const tmh = msecs / 0x100000000 * 10000 & 0xfffffff;\n  b[i++] = tmh >>> 8 & 0xff;\n  b[i++] = tmh & 0xff; // `time_high_and_version`\n\n  b[i++] = tmh >>> 24 & 0xf | 0x10; // include version\n\n  b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)\n\n  b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low`\n\n  b[i++] = clockseq & 0xff; // `node`\n\n  for (let n = 0; n < 6; ++n) {\n    b[i + n] = node[n];\n  }\n\n  return buf || (0, _bytesToUuid.default)(b);\n}\n\nvar _default = v1;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _v = _interopRequireDefault(require(\"./v35.js\"));\n\nvar _md = _interopRequireDefault(require(\"./md5.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconst v3 = (0, _v.default)('v3', 0x30, _md.default);\nvar _default = v3;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = _default;\nexports.URL = exports.DNS = void 0;\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction uuidToBytes(uuid) {\n  // Note: We assume we're being passed a valid uuid string\n  const bytes = [];\n  uuid.replace(/[a-fA-F0-9]{2}/g, function (hex) {\n    bytes.push(parseInt(hex, 16));\n  });\n  return bytes;\n}\n\nfunction stringToBytes(str) {\n  str = unescape(encodeURIComponent(str)); // UTF8 escape\n\n  const bytes = [];\n\n  for (let i = 0; i < str.length; ++i) {\n    bytes.push(str.charCodeAt(i));\n  }\n\n  return bytes;\n}\n\nconst DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';\nexports.DNS = DNS;\nconst URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';\nexports.URL = URL;\n\nfunction _default(name, version, hashfunc) {\n  function generateUUID(value, namespace, buf, offset) {\n    const off = buf && offset || 0;\n    if (typeof value === 'string') value = stringToBytes(value);\n    if (typeof namespace === 'string') namespace = uuidToBytes(namespace);\n\n    if (!Array.isArray(value)) {\n      throw TypeError('value must be an array of bytes');\n    }\n\n    if (!Array.isArray(namespace) || namespace.length !== 16) {\n      throw TypeError('namespace must be uuid string or an Array of 16 byte values');\n    } // Per 4.3\n\n\n    const bytes = hashfunc(namespace.concat(value));\n    bytes[6] = bytes[6] & 0x0f | version;\n    bytes[8] = bytes[8] & 0x3f | 0x80;\n\n    if (buf) {\n      for (let idx = 0; idx < 16; ++idx) {\n        buf[off + idx] = bytes[idx];\n      }\n    }\n\n    return buf || (0, _bytesToUuid.default)(bytes);\n  } // Function#name is not settable on some platforms (#270)\n\n\n  try {\n    generateUUID.name = name; // eslint-disable-next-line no-empty\n  } catch (err) {} // For CommonJS default export support\n\n\n  generateUUID.DNS = DNS;\n  generateUUID.URL = URL;\n  return generateUUID;\n}","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _rng = _interopRequireDefault(require(\"./rng.js\"));\n\nvar _bytesToUuid = _interopRequireDefault(require(\"./bytesToUuid.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nfunction v4(options, buf, offset) {\n  if (typeof options === 'string') {\n    buf = options === 'binary' ? new Uint8Array(16) : null;\n    options = null;\n  }\n\n  options = options || {};\n\n  const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n\n  rnds[6] = rnds[6] & 0x0f | 0x40;\n  rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n  if (buf) {\n    const start = offset || 0;\n\n    for (let i = 0; i < 16; ++i) {\n      buf[start + i] = rnds[i];\n    }\n\n    return buf;\n  }\n\n  return (0, _bytesToUuid.default)(rnds);\n}\n\nvar _default = v4;\nexports.default = _default;","\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n  value: true\n});\nexports.default = void 0;\n\nvar _v = _interopRequireDefault(require(\"./v35.js\"));\n\nvar _sha = _interopRequireDefault(require(\"./sha1.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nconst v5 = (0, _v.default)('v5', 0x50, _sha.default);\nvar _default = v5;\nexports.default = _default;","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\n\nlet logDisabled_ = true;\nlet deprecationWarnings_ = true;\n\n/**\n * Extract browser version out of the provided user agent string.\n *\n * @param {!string} uastring userAgent string.\n * @param {!string} expr Regular expression used as match criteria.\n * @param {!number} pos position in the version string to be returned.\n * @return {!number} browser version.\n */\nexport function extractVersion(uastring, expr, pos) {\n  const match = uastring.match(expr);\n  return match && match.length >= pos && parseInt(match[pos], 10);\n}\n\n// Wraps the peerconnection event eventNameToWrap in a function\n// which returns the modified event object (or false to prevent\n// the event).\nexport function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  const proto = window.RTCPeerConnection.prototype;\n  const nativeAddEventListener = proto.addEventListener;\n  proto.addEventListener = function(nativeEventName, cb) {\n    if (nativeEventName !== eventNameToWrap) {\n      return nativeAddEventListener.apply(this, arguments);\n    }\n    const wrappedCallback = (e) => {\n      const modifiedEvent = wrapper(e);\n      if (modifiedEvent) {\n        if (cb.handleEvent) {\n          cb.handleEvent(modifiedEvent);\n        } else {\n          cb(modifiedEvent);\n        }\n      }\n    };\n    this._eventMap = this._eventMap || {};\n    if (!this._eventMap[eventNameToWrap]) {\n      this._eventMap[eventNameToWrap] = new Map();\n    }\n    this._eventMap[eventNameToWrap].set(cb, wrappedCallback);\n    return nativeAddEventListener.apply(this, [nativeEventName,\n      wrappedCallback]);\n  };\n\n  const nativeRemoveEventListener = proto.removeEventListener;\n  proto.removeEventListener = function(nativeEventName, cb) {\n    if (nativeEventName !== eventNameToWrap || !this._eventMap\n        || !this._eventMap[eventNameToWrap]) {\n      return nativeRemoveEventListener.apply(this, arguments);\n    }\n    if (!this._eventMap[eventNameToWrap].has(cb)) {\n      return nativeRemoveEventListener.apply(this, arguments);\n    }\n    const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);\n    this._eventMap[eventNameToWrap].delete(cb);\n    if (this._eventMap[eventNameToWrap].size === 0) {\n      delete this._eventMap[eventNameToWrap];\n    }\n    if (Object.keys(this._eventMap).length === 0) {\n      delete this._eventMap;\n    }\n    return nativeRemoveEventListener.apply(this, [nativeEventName,\n      unwrappedCb]);\n  };\n\n  Object.defineProperty(proto, 'on' + eventNameToWrap, {\n    get() {\n      return this['_on' + eventNameToWrap];\n    },\n    set(cb) {\n      if (this['_on' + eventNameToWrap]) {\n        this.removeEventListener(eventNameToWrap,\n            this['_on' + eventNameToWrap]);\n        delete this['_on' + eventNameToWrap];\n      }\n      if (cb) {\n        this.addEventListener(eventNameToWrap,\n            this['_on' + eventNameToWrap] = cb);\n      }\n    },\n    enumerable: true,\n    configurable: true\n  });\n}\n\nexport function disableLog(bool) {\n  if (typeof bool !== 'boolean') {\n    return new Error('Argument type: ' + typeof bool +\n        '. Please use a boolean.');\n  }\n  logDisabled_ = bool;\n  return (bool) ? 'adapter.js logging disabled' :\n      'adapter.js logging enabled';\n}\n\n/**\n * Disable or enable deprecation warnings\n * @param {!boolean} bool set to true to disable warnings.\n */\nexport function disableWarnings(bool) {\n  if (typeof bool !== 'boolean') {\n    return new Error('Argument type: ' + typeof bool +\n        '. Please use a boolean.');\n  }\n  deprecationWarnings_ = !bool;\n  return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');\n}\n\nexport function log() {\n  if (typeof window === 'object') {\n    if (logDisabled_) {\n      return;\n    }\n    if (typeof console !== 'undefined' && typeof console.log === 'function') {\n      console.log.apply(console, arguments);\n    }\n  }\n}\n\n/**\n * Shows a deprecation warning suggesting the modern and spec-compatible API.\n */\nexport function deprecated(oldMethod, newMethod) {\n  if (!deprecationWarnings_) {\n    return;\n  }\n  console.warn(oldMethod + ' is deprecated, please use ' + newMethod +\n      ' instead.');\n}\n\n/**\n * Browser detector.\n *\n * @return {object} result containing browser and version\n *     properties.\n */\nexport function detectBrowser(window) {\n  // Returned result object.\n  const result = {browser: null, version: null};\n\n  // Fail early if it's not a browser\n  if (typeof window === 'undefined' || !window.navigator) {\n    result.browser = 'Not a browser.';\n    return result;\n  }\n\n  const {navigator} = window;\n\n  if (navigator.mozGetUserMedia) { // Firefox.\n    result.browser = 'firefox';\n    result.version = extractVersion(navigator.userAgent,\n        /Firefox\\/(\\d+)\\./, 1);\n  } else if (navigator.webkitGetUserMedia ||\n      (window.isSecureContext === false && window.webkitRTCPeerConnection &&\n       !window.RTCIceGatherer)) {\n    // Chrome, Chromium, Webview, Opera.\n    // Version matches Chrome/WebRTC version.\n    // Chrome 74 removed webkitGetUserMedia on http as well so we need the\n    // more complicated fallback to webkitRTCPeerConnection.\n    result.browser = 'chrome';\n    result.version = extractVersion(navigator.userAgent,\n        /Chrom(e|ium)\\/(\\d+)\\./, 2);\n  } else if (window.RTCPeerConnection &&\n      navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) { // Safari.\n    result.browser = 'safari';\n    result.version = extractVersion(navigator.userAgent,\n        /AppleWebKit\\/(\\d+)\\./, 1);\n    result.supportsUnifiedPlan = window.RTCRtpTransceiver &&\n        'currentDirection' in window.RTCRtpTransceiver.prototype;\n  } else { // Default fallthrough: not supported.\n    result.browser = 'Not a supported browser.';\n    return result;\n  }\n\n  return result;\n}\n\n/**\n * Checks if something is an object.\n *\n * @param {*} val The something you want to check.\n * @return true if val is an object, false otherwise.\n */\nfunction isObject(val) {\n  return Object.prototype.toString.call(val) === '[object Object]';\n}\n\n/**\n * Remove all empty objects and undefined values\n * from a nested object -- an enhanced and vanilla version\n * of Lodash's `compact`.\n */\nexport function compactObject(data) {\n  if (!isObject(data)) {\n    return data;\n  }\n\n  return Object.keys(data).reduce(function(accumulator, key) {\n    const isObj = isObject(data[key]);\n    const value = isObj ? compactObject(data[key]) : data[key];\n    const isEmptyObject = isObj && !Object.keys(value).length;\n    if (value === undefined || isEmptyObject) {\n      return accumulator;\n    }\n    return Object.assign(accumulator, {[key]: value});\n  }, {});\n}\n\n/* iterates the stats graph recursively. */\nexport function walkStats(stats, base, resultSet) {\n  if (!base || resultSet.has(base.id)) {\n    return;\n  }\n  resultSet.set(base.id, base);\n  Object.keys(base).forEach(name => {\n    if (name.endsWith('Id')) {\n      walkStats(stats, stats.get(base[name]), resultSet);\n    } else if (name.endsWith('Ids')) {\n      base[name].forEach(id => {\n        walkStats(stats, stats.get(id), resultSet);\n      });\n    }\n  });\n}\n\n/* filter getStats for a sender/receiver track. */\nexport function filterStats(result, track, outbound) {\n  const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';\n  const filteredResult = new Map();\n  if (track === null) {\n    return filteredResult;\n  }\n  const trackStats = [];\n  result.forEach(value => {\n    if (value.type === 'track' &&\n        value.trackIdentifier === track.id) {\n      trackStats.push(value);\n    }\n  });\n  trackStats.forEach(trackStat => {\n    result.forEach(stats => {\n      if (stats.type === streamStatsType && stats.trackId === trackStat.id) {\n        walkStats(result, stats, filteredResult);\n      }\n    });\n  });\n  return filteredResult;\n}\n\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\nconst logging = utils.log;\n\nexport function shimGetUserMedia(window, browserDetails) {\n  const navigator = window && window.navigator;\n\n  if (!navigator.mediaDevices) {\n    return;\n  }\n\n  const constraintsToChrome_ = function(c) {\n    if (typeof c !== 'object' || c.mandatory || c.optional) {\n      return c;\n    }\n    const cc = {};\n    Object.keys(c).forEach(key => {\n      if (key === 'require' || key === 'advanced' || key === 'mediaSource') {\n        return;\n      }\n      const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};\n      if (r.exact !== undefined && typeof r.exact === 'number') {\n        r.min = r.max = r.exact;\n      }\n      const oldname_ = function(prefix, name) {\n        if (prefix) {\n          return prefix + name.charAt(0).toUpperCase() + name.slice(1);\n        }\n        return (name === 'deviceId') ? 'sourceId' : name;\n      };\n      if (r.ideal !== undefined) {\n        cc.optional = cc.optional || [];\n        let oc = {};\n        if (typeof r.ideal === 'number') {\n          oc[oldname_('min', key)] = r.ideal;\n          cc.optional.push(oc);\n          oc = {};\n          oc[oldname_('max', key)] = r.ideal;\n          cc.optional.push(oc);\n        } else {\n          oc[oldname_('', key)] = r.ideal;\n          cc.optional.push(oc);\n        }\n      }\n      if (r.exact !== undefined && typeof r.exact !== 'number') {\n        cc.mandatory = cc.mandatory || {};\n        cc.mandatory[oldname_('', key)] = r.exact;\n      } else {\n        ['min', 'max'].forEach(mix => {\n          if (r[mix] !== undefined) {\n            cc.mandatory = cc.mandatory || {};\n            cc.mandatory[oldname_(mix, key)] = r[mix];\n          }\n        });\n      }\n    });\n    if (c.advanced) {\n      cc.optional = (cc.optional || []).concat(c.advanced);\n    }\n    return cc;\n  };\n\n  const shimConstraints_ = function(constraints, func) {\n    if (browserDetails.version >= 61) {\n      return func(constraints);\n    }\n    constraints = JSON.parse(JSON.stringify(constraints));\n    if (constraints && typeof constraints.audio === 'object') {\n      const remap = function(obj, a, b) {\n        if (a in obj && !(b in obj)) {\n          obj[b] = obj[a];\n          delete obj[a];\n        }\n      };\n      constraints = JSON.parse(JSON.stringify(constraints));\n      remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');\n      remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');\n      constraints.audio = constraintsToChrome_(constraints.audio);\n    }\n    if (constraints && typeof constraints.video === 'object') {\n      // Shim facingMode for mobile & surface pro.\n      let face = constraints.video.facingMode;\n      face = face && ((typeof face === 'object') ? face : {ideal: face});\n      const getSupportedFacingModeLies = browserDetails.version < 66;\n\n      if ((face && (face.exact === 'user' || face.exact === 'environment' ||\n                    face.ideal === 'user' || face.ideal === 'environment')) &&\n          !(navigator.mediaDevices.getSupportedConstraints &&\n            navigator.mediaDevices.getSupportedConstraints().facingMode &&\n            !getSupportedFacingModeLies)) {\n        delete constraints.video.facingMode;\n        let matches;\n        if (face.exact === 'environment' || face.ideal === 'environment') {\n          matches = ['back', 'rear'];\n        } else if (face.exact === 'user' || face.ideal === 'user') {\n          matches = ['front'];\n        }\n        if (matches) {\n          // Look for matches in label, or use last cam for back (typical).\n          return navigator.mediaDevices.enumerateDevices()\n          .then(devices => {\n            devices = devices.filter(d => d.kind === 'videoinput');\n            let dev = devices.find(d => matches.some(match =>\n              d.label.toLowerCase().includes(match)));\n            if (!dev && devices.length && matches.includes('back')) {\n              dev = devices[devices.length - 1]; // more likely the back cam\n            }\n            if (dev) {\n              constraints.video.deviceId = face.exact ? {exact: dev.deviceId} :\n                                                        {ideal: dev.deviceId};\n            }\n            constraints.video = constraintsToChrome_(constraints.video);\n            logging('chrome: ' + JSON.stringify(constraints));\n            return func(constraints);\n          });\n        }\n      }\n      constraints.video = constraintsToChrome_(constraints.video);\n    }\n    logging('chrome: ' + JSON.stringify(constraints));\n    return func(constraints);\n  };\n\n  const shimError_ = function(e) {\n    if (browserDetails.version >= 64) {\n      return e;\n    }\n    return {\n      name: {\n        PermissionDeniedError: 'NotAllowedError',\n        PermissionDismissedError: 'NotAllowedError',\n        InvalidStateError: 'NotAllowedError',\n        DevicesNotFoundError: 'NotFoundError',\n        ConstraintNotSatisfiedError: 'OverconstrainedError',\n        TrackStartError: 'NotReadableError',\n        MediaDeviceFailedDueToShutdown: 'NotAllowedError',\n        MediaDeviceKillSwitchOn: 'NotAllowedError',\n        TabCaptureError: 'AbortError',\n        ScreenCaptureError: 'AbortError',\n        DeviceCaptureError: 'AbortError'\n      }[e.name] || e.name,\n      message: e.message,\n      constraint: e.constraint || e.constraintName,\n      toString() {\n        return this.name + (this.message && ': ') + this.message;\n      }\n    };\n  };\n\n  const getUserMedia_ = function(constraints, onSuccess, onError) {\n    shimConstraints_(constraints, c => {\n      navigator.webkitGetUserMedia(c, onSuccess, e => {\n        if (onError) {\n          onError(shimError_(e));\n        }\n      });\n    });\n  };\n  navigator.getUserMedia = getUserMedia_.bind(navigator);\n\n  // Even though Chrome 45 has navigator.mediaDevices and a getUserMedia\n  // function which returns a Promise, it does not accept spec-style\n  // constraints.\n  if (navigator.mediaDevices.getUserMedia) {\n    const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n        bind(navigator.mediaDevices);\n    navigator.mediaDevices.getUserMedia = function(cs) {\n      return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {\n        if (c.audio && !stream.getAudioTracks().length ||\n            c.video && !stream.getVideoTracks().length) {\n          stream.getTracks().forEach(track => {\n            track.stop();\n          });\n          throw new DOMException('', 'NotFoundError');\n        }\n        return stream;\n      }, e => Promise.reject(shimError_(e))));\n    };\n  }\n}\n","/*\n *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\nexport function shimGetDisplayMedia(window, getSourceId) {\n  if (window.navigator.mediaDevices &&\n    'getDisplayMedia' in window.navigator.mediaDevices) {\n    return;\n  }\n  if (!(window.navigator.mediaDevices)) {\n    return;\n  }\n  // getSourceId is a function that returns a promise resolving with\n  // the sourceId of the screen/window/tab to be shared.\n  if (typeof getSourceId !== 'function') {\n    console.error('shimGetDisplayMedia: getSourceId argument is not ' +\n        'a function');\n    return;\n  }\n  window.navigator.mediaDevices.getDisplayMedia =\n    function getDisplayMedia(constraints) {\n      return getSourceId(constraints)\n        .then(sourceId => {\n          const widthSpecified = constraints.video && constraints.video.width;\n          const heightSpecified = constraints.video &&\n            constraints.video.height;\n          const frameRateSpecified = constraints.video &&\n            constraints.video.frameRate;\n          constraints.video = {\n            mandatory: {\n              chromeMediaSource: 'desktop',\n              chromeMediaSourceId: sourceId,\n              maxFrameRate: frameRateSpecified || 3\n            }\n          };\n          if (widthSpecified) {\n            constraints.video.mandatory.maxWidth = widthSpecified;\n          }\n          if (heightSpecified) {\n            constraints.video.mandatory.maxHeight = heightSpecified;\n          }\n          return window.navigator.mediaDevices.getUserMedia(constraints);\n        });\n    };\n}\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n /* eslint-env node */\n'use strict';\nimport * as utils from '../utils.js';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimMediaStream(window) {\n  window.MediaStream = window.MediaStream || window.webkitMediaStream;\n}\n\nexport function shimOnTrack(window) {\n  if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in\n      window.RTCPeerConnection.prototype)) {\n    Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {\n      get() {\n        return this._ontrack;\n      },\n      set(f) {\n        if (this._ontrack) {\n          this.removeEventListener('track', this._ontrack);\n        }\n        this.addEventListener('track', this._ontrack = f);\n      },\n      enumerable: true,\n      configurable: true\n    });\n    const origSetRemoteDescription =\n        window.RTCPeerConnection.prototype.setRemoteDescription;\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n      function setRemoteDescription() {\n        if (!this._ontrackpoly) {\n          this._ontrackpoly = (e) => {\n            // onaddstream does not fire when a track is added to an existing\n            // stream. But stream.onaddtrack is implemented so we use that.\n            e.stream.addEventListener('addtrack', te => {\n              let receiver;\n              if (window.RTCPeerConnection.prototype.getReceivers) {\n                receiver = this.getReceivers()\n                  .find(r => r.track && r.track.id === te.track.id);\n              } else {\n                receiver = {track: te.track};\n              }\n\n              const event = new Event('track');\n              event.track = te.track;\n              event.receiver = receiver;\n              event.transceiver = {receiver};\n              event.streams = [e.stream];\n              this.dispatchEvent(event);\n            });\n            e.stream.getTracks().forEach(track => {\n              let receiver;\n              if (window.RTCPeerConnection.prototype.getReceivers) {\n                receiver = this.getReceivers()\n                  .find(r => r.track && r.track.id === track.id);\n              } else {\n                receiver = {track};\n              }\n              const event = new Event('track');\n              event.track = track;\n              event.receiver = receiver;\n              event.transceiver = {receiver};\n              event.streams = [e.stream];\n              this.dispatchEvent(event);\n            });\n          };\n          this.addEventListener('addstream', this._ontrackpoly);\n        }\n        return origSetRemoteDescription.apply(this, arguments);\n      };\n  } else {\n    // even if RTCRtpTransceiver is in window, it is only used and\n    // emitted in unified-plan. Unfortunately this means we need\n    // to unconditionally wrap the event.\n    utils.wrapPeerConnectionEvent(window, 'track', e => {\n      if (!e.transceiver) {\n        Object.defineProperty(e, 'transceiver',\n          {value: {receiver: e.receiver}});\n      }\n      return e;\n    });\n  }\n}\n\nexport function shimGetSendersWithDtmf(window) {\n  // Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.\n  if (typeof window === 'object' && window.RTCPeerConnection &&\n      !('getSenders' in window.RTCPeerConnection.prototype) &&\n      'createDTMFSender' in window.RTCPeerConnection.prototype) {\n    const shimSenderWithDtmf = function(pc, track) {\n      return {\n        track,\n        get dtmf() {\n          if (this._dtmf === undefined) {\n            if (track.kind === 'audio') {\n              this._dtmf = pc.createDTMFSender(track);\n            } else {\n              this._dtmf = null;\n            }\n          }\n          return this._dtmf;\n        },\n        _pc: pc\n      };\n    };\n\n    // augment addTrack when getSenders is not available.\n    if (!window.RTCPeerConnection.prototype.getSenders) {\n      window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n        this._senders = this._senders || [];\n        return this._senders.slice(); // return a copy of the internal state.\n      };\n      const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n      window.RTCPeerConnection.prototype.addTrack =\n        function addTrack(track, stream) {\n          let sender = origAddTrack.apply(this, arguments);\n          if (!sender) {\n            sender = shimSenderWithDtmf(this, track);\n            this._senders.push(sender);\n          }\n          return sender;\n        };\n\n      const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n      window.RTCPeerConnection.prototype.removeTrack =\n        function removeTrack(sender) {\n          origRemoveTrack.apply(this, arguments);\n          const idx = this._senders.indexOf(sender);\n          if (idx !== -1) {\n            this._senders.splice(idx, 1);\n          }\n        };\n    }\n    const origAddStream = window.RTCPeerConnection.prototype.addStream;\n    window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n      this._senders = this._senders || [];\n      origAddStream.apply(this, [stream]);\n      stream.getTracks().forEach(track => {\n        this._senders.push(shimSenderWithDtmf(this, track));\n      });\n    };\n\n    const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n    window.RTCPeerConnection.prototype.removeStream =\n      function removeStream(stream) {\n        this._senders = this._senders || [];\n        origRemoveStream.apply(this, [stream]);\n\n        stream.getTracks().forEach(track => {\n          const sender = this._senders.find(s => s.track === track);\n          if (sender) { // remove sender\n            this._senders.splice(this._senders.indexOf(sender), 1);\n          }\n        });\n      };\n  } else if (typeof window === 'object' && window.RTCPeerConnection &&\n             'getSenders' in window.RTCPeerConnection.prototype &&\n             'createDTMFSender' in window.RTCPeerConnection.prototype &&\n             window.RTCRtpSender &&\n             !('dtmf' in window.RTCRtpSender.prototype)) {\n    const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n    window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n      const senders = origGetSenders.apply(this, []);\n      senders.forEach(sender => sender._pc = this);\n      return senders;\n    };\n\n    Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n      get() {\n        if (this._dtmf === undefined) {\n          if (this.track.kind === 'audio') {\n            this._dtmf = this._pc.createDTMFSender(this.track);\n          } else {\n            this._dtmf = null;\n          }\n        }\n        return this._dtmf;\n      }\n    });\n  }\n}\n\nexport function shimGetStats(window) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n\n  const origGetStats = window.RTCPeerConnection.prototype.getStats;\n  window.RTCPeerConnection.prototype.getStats = function getStats() {\n    const [selector, onSucc, onErr] = arguments;\n\n    // If selector is a function then we are in the old style stats so just\n    // pass back the original getStats format to avoid breaking old users.\n    if (arguments.length > 0 && typeof selector === 'function') {\n      return origGetStats.apply(this, arguments);\n    }\n\n    // When spec-style getStats is supported, return those when called with\n    // either no arguments or the selector argument is null.\n    if (origGetStats.length === 0 && (arguments.length === 0 ||\n        typeof selector !== 'function')) {\n      return origGetStats.apply(this, []);\n    }\n\n    const fixChromeStats_ = function(response) {\n      const standardReport = {};\n      const reports = response.result();\n      reports.forEach(report => {\n        const standardStats = {\n          id: report.id,\n          timestamp: report.timestamp,\n          type: {\n            localcandidate: 'local-candidate',\n            remotecandidate: 'remote-candidate'\n          }[report.type] || report.type\n        };\n        report.names().forEach(name => {\n          standardStats[name] = report.stat(name);\n        });\n        standardReport[standardStats.id] = standardStats;\n      });\n\n      return standardReport;\n    };\n\n    // shim getStats with maplike support\n    const makeMapStats = function(stats) {\n      return new Map(Object.keys(stats).map(key => [key, stats[key]]));\n    };\n\n    if (arguments.length >= 2) {\n      const successCallbackWrapper_ = function(response) {\n        onSucc(makeMapStats(fixChromeStats_(response)));\n      };\n\n      return origGetStats.apply(this, [successCallbackWrapper_,\n        selector]);\n    }\n\n    // promise-support\n    return new Promise((resolve, reject) => {\n      origGetStats.apply(this, [\n        function(response) {\n          resolve(makeMapStats(fixChromeStats_(response)));\n        }, reject]);\n    }).then(onSucc, onErr);\n  };\n}\n\nexport function shimSenderReceiverGetStats(window) {\n  if (!(typeof window === 'object' && window.RTCPeerConnection &&\n      window.RTCRtpSender && window.RTCRtpReceiver)) {\n    return;\n  }\n\n  // shim sender stats.\n  if (!('getStats' in window.RTCRtpSender.prototype)) {\n    const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n    if (origGetSenders) {\n      window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n        const senders = origGetSenders.apply(this, []);\n        senders.forEach(sender => sender._pc = this);\n        return senders;\n      };\n    }\n\n    const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n    if (origAddTrack) {\n      window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n        const sender = origAddTrack.apply(this, arguments);\n        sender._pc = this;\n        return sender;\n      };\n    }\n    window.RTCRtpSender.prototype.getStats = function getStats() {\n      const sender = this;\n      return this._pc.getStats().then(result =>\n        /* Note: this will include stats of all senders that\n         *   send a track with the same id as sender.track as\n         *   it is not possible to identify the RTCRtpSender.\n         */\n        utils.filterStats(result, sender.track, true));\n    };\n  }\n\n  // shim receiver stats.\n  if (!('getStats' in window.RTCRtpReceiver.prototype)) {\n    const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n    if (origGetReceivers) {\n      window.RTCPeerConnection.prototype.getReceivers =\n        function getReceivers() {\n          const receivers = origGetReceivers.apply(this, []);\n          receivers.forEach(receiver => receiver._pc = this);\n          return receivers;\n        };\n    }\n    utils.wrapPeerConnectionEvent(window, 'track', e => {\n      e.receiver._pc = e.srcElement;\n      return e;\n    });\n    window.RTCRtpReceiver.prototype.getStats = function getStats() {\n      const receiver = this;\n      return this._pc.getStats().then(result =>\n        utils.filterStats(result, receiver.track, false));\n    };\n  }\n\n  if (!('getStats' in window.RTCRtpSender.prototype &&\n      'getStats' in window.RTCRtpReceiver.prototype)) {\n    return;\n  }\n\n  // shim RTCPeerConnection.getStats(track).\n  const origGetStats = window.RTCPeerConnection.prototype.getStats;\n  window.RTCPeerConnection.prototype.getStats = function getStats() {\n    if (arguments.length > 0 &&\n        arguments[0] instanceof window.MediaStreamTrack) {\n      const track = arguments[0];\n      let sender;\n      let receiver;\n      let err;\n      this.getSenders().forEach(s => {\n        if (s.track === track) {\n          if (sender) {\n            err = true;\n          } else {\n            sender = s;\n          }\n        }\n      });\n      this.getReceivers().forEach(r => {\n        if (r.track === track) {\n          if (receiver) {\n            err = true;\n          } else {\n            receiver = r;\n          }\n        }\n        return r.track === track;\n      });\n      if (err || (sender && receiver)) {\n        return Promise.reject(new DOMException(\n          'There are more than one sender or receiver for the track.',\n          'InvalidAccessError'));\n      } else if (sender) {\n        return sender.getStats();\n      } else if (receiver) {\n        return receiver.getStats();\n      }\n      return Promise.reject(new DOMException(\n        'There is no sender or receiver for the track.',\n        'InvalidAccessError'));\n    }\n    return origGetStats.apply(this, arguments);\n  };\n}\n\nexport function shimAddTrackRemoveTrackWithNative(window) {\n  // shim addTrack/removeTrack with native variants in order to make\n  // the interactions with legacy getLocalStreams behave as in other browsers.\n  // Keeps a mapping stream.id => [stream, rtpsenders...]\n  window.RTCPeerConnection.prototype.getLocalStreams =\n    function getLocalStreams() {\n      this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n      return Object.keys(this._shimmedLocalStreams)\n        .map(streamId => this._shimmedLocalStreams[streamId][0]);\n    };\n\n  const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n  window.RTCPeerConnection.prototype.addTrack =\n    function addTrack(track, stream) {\n      if (!stream) {\n        return origAddTrack.apply(this, arguments);\n      }\n      this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n      const sender = origAddTrack.apply(this, arguments);\n      if (!this._shimmedLocalStreams[stream.id]) {\n        this._shimmedLocalStreams[stream.id] = [stream, sender];\n      } else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {\n        this._shimmedLocalStreams[stream.id].push(sender);\n      }\n      return sender;\n    };\n\n  const origAddStream = window.RTCPeerConnection.prototype.addStream;\n  window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n    this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n\n    stream.getTracks().forEach(track => {\n      const alreadyExists = this.getSenders().find(s => s.track === track);\n      if (alreadyExists) {\n        throw new DOMException('Track already exists.',\n            'InvalidAccessError');\n      }\n    });\n    const existingSenders = this.getSenders();\n    origAddStream.apply(this, arguments);\n    const newSenders = this.getSenders()\n      .filter(newSender => existingSenders.indexOf(newSender) === -1);\n    this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);\n  };\n\n  const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n  window.RTCPeerConnection.prototype.removeStream =\n    function removeStream(stream) {\n      this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n      delete this._shimmedLocalStreams[stream.id];\n      return origRemoveStream.apply(this, arguments);\n    };\n\n  const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;\n  window.RTCPeerConnection.prototype.removeTrack =\n    function removeTrack(sender) {\n      this._shimmedLocalStreams = this._shimmedLocalStreams || {};\n      if (sender) {\n        Object.keys(this._shimmedLocalStreams).forEach(streamId => {\n          const idx = this._shimmedLocalStreams[streamId].indexOf(sender);\n          if (idx !== -1) {\n            this._shimmedLocalStreams[streamId].splice(idx, 1);\n          }\n          if (this._shimmedLocalStreams[streamId].length === 1) {\n            delete this._shimmedLocalStreams[streamId];\n          }\n        });\n      }\n      return origRemoveTrack.apply(this, arguments);\n    };\n}\n\nexport function shimAddTrackRemoveTrack(window, browserDetails) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  // shim addTrack and removeTrack.\n  if (window.RTCPeerConnection.prototype.addTrack &&\n      browserDetails.version >= 65) {\n    return shimAddTrackRemoveTrackWithNative(window);\n  }\n\n  // also shim pc.getLocalStreams when addTrack is shimmed\n  // to return the original streams.\n  const origGetLocalStreams = window.RTCPeerConnection.prototype\n      .getLocalStreams;\n  window.RTCPeerConnection.prototype.getLocalStreams =\n    function getLocalStreams() {\n      const nativeStreams = origGetLocalStreams.apply(this);\n      this._reverseStreams = this._reverseStreams || {};\n      return nativeStreams.map(stream => this._reverseStreams[stream.id]);\n    };\n\n  const origAddStream = window.RTCPeerConnection.prototype.addStream;\n  window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n    this._streams = this._streams || {};\n    this._reverseStreams = this._reverseStreams || {};\n\n    stream.getTracks().forEach(track => {\n      const alreadyExists = this.getSenders().find(s => s.track === track);\n      if (alreadyExists) {\n        throw new DOMException('Track already exists.',\n            'InvalidAccessError');\n      }\n    });\n    // Add identity mapping for consistency with addTrack.\n    // Unless this is being used with a stream from addTrack.\n    if (!this._reverseStreams[stream.id]) {\n      const newStream = new window.MediaStream(stream.getTracks());\n      this._streams[stream.id] = newStream;\n      this._reverseStreams[newStream.id] = stream;\n      stream = newStream;\n    }\n    origAddStream.apply(this, [stream]);\n  };\n\n  const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;\n  window.RTCPeerConnection.prototype.removeStream =\n    function removeStream(stream) {\n      this._streams = this._streams || {};\n      this._reverseStreams = this._reverseStreams || {};\n\n      origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);\n      delete this._reverseStreams[(this._streams[stream.id] ?\n          this._streams[stream.id].id : stream.id)];\n      delete this._streams[stream.id];\n    };\n\n  window.RTCPeerConnection.prototype.addTrack =\n    function addTrack(track, stream) {\n      if (this.signalingState === 'closed') {\n        throw new DOMException(\n          'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n          'InvalidStateError');\n      }\n      const streams = [].slice.call(arguments, 1);\n      if (streams.length !== 1 ||\n          !streams[0].getTracks().find(t => t === track)) {\n        // this is not fully correct but all we can manage without\n        // [[associated MediaStreams]] internal slot.\n        throw new DOMException(\n          'The adapter.js addTrack polyfill only supports a single ' +\n          ' stream which is associated with the specified track.',\n          'NotSupportedError');\n      }\n\n      const alreadyExists = this.getSenders().find(s => s.track === track);\n      if (alreadyExists) {\n        throw new DOMException('Track already exists.',\n            'InvalidAccessError');\n      }\n\n      this._streams = this._streams || {};\n      this._reverseStreams = this._reverseStreams || {};\n      const oldStream = this._streams[stream.id];\n      if (oldStream) {\n        // this is using odd Chrome behaviour, use with caution:\n        // https://bugs.chromium.org/p/webrtc/issues/detail?id=7815\n        // Note: we rely on the high-level addTrack/dtmf shim to\n        // create the sender with a dtmf sender.\n        oldStream.addTrack(track);\n\n        // Trigger ONN async.\n        Promise.resolve().then(() => {\n          this.dispatchEvent(new Event('negotiationneeded'));\n        });\n      } else {\n        const newStream = new window.MediaStream([track]);\n        this._streams[stream.id] = newStream;\n        this._reverseStreams[newStream.id] = stream;\n        this.addStream(newStream);\n      }\n      return this.getSenders().find(s => s.track === track);\n    };\n\n  // replace the internal stream id with the external one and\n  // vice versa.\n  function replaceInternalStreamId(pc, description) {\n    let sdp = description.sdp;\n    Object.keys(pc._reverseStreams || []).forEach(internalId => {\n      const externalStream = pc._reverseStreams[internalId];\n      const internalStream = pc._streams[externalStream.id];\n      sdp = sdp.replace(new RegExp(internalStream.id, 'g'),\n          externalStream.id);\n    });\n    return new RTCSessionDescription({\n      type: description.type,\n      sdp\n    });\n  }\n  function replaceExternalStreamId(pc, description) {\n    let sdp = description.sdp;\n    Object.keys(pc._reverseStreams || []).forEach(internalId => {\n      const externalStream = pc._reverseStreams[internalId];\n      const internalStream = pc._streams[externalStream.id];\n      sdp = sdp.replace(new RegExp(externalStream.id, 'g'),\n          internalStream.id);\n    });\n    return new RTCSessionDescription({\n      type: description.type,\n      sdp\n    });\n  }\n  ['createOffer', 'createAnswer'].forEach(function(method) {\n    const nativeMethod = window.RTCPeerConnection.prototype[method];\n    const methodObj = {[method]() {\n      const args = arguments;\n      const isLegacyCall = arguments.length &&\n          typeof arguments[0] === 'function';\n      if (isLegacyCall) {\n        return nativeMethod.apply(this, [\n          (description) => {\n            const desc = replaceInternalStreamId(this, description);\n            args[0].apply(null, [desc]);\n          },\n          (err) => {\n            if (args[1]) {\n              args[1].apply(null, err);\n            }\n          }, arguments[2]\n        ]);\n      }\n      return nativeMethod.apply(this, arguments)\n      .then(description => replaceInternalStreamId(this, description));\n    }};\n    window.RTCPeerConnection.prototype[method] = methodObj[method];\n  });\n\n  const origSetLocalDescription =\n      window.RTCPeerConnection.prototype.setLocalDescription;\n  window.RTCPeerConnection.prototype.setLocalDescription =\n    function setLocalDescription() {\n      if (!arguments.length || !arguments[0].type) {\n        return origSetLocalDescription.apply(this, arguments);\n      }\n      arguments[0] = replaceExternalStreamId(this, arguments[0]);\n      return origSetLocalDescription.apply(this, arguments);\n    };\n\n  // TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier\n\n  const origLocalDescription = Object.getOwnPropertyDescriptor(\n      window.RTCPeerConnection.prototype, 'localDescription');\n  Object.defineProperty(window.RTCPeerConnection.prototype,\n      'localDescription', {\n        get() {\n          const description = origLocalDescription.get.apply(this);\n          if (description.type === '') {\n            return description;\n          }\n          return replaceInternalStreamId(this, description);\n        }\n      });\n\n  window.RTCPeerConnection.prototype.removeTrack =\n    function removeTrack(sender) {\n      if (this.signalingState === 'closed') {\n        throw new DOMException(\n          'The RTCPeerConnection\\'s signalingState is \\'closed\\'.',\n          'InvalidStateError');\n      }\n      // We can not yet check for sender instanceof RTCRtpSender\n      // since we shim RTPSender. So we check if sender._pc is set.\n      if (!sender._pc) {\n        throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +\n            'does not implement interface RTCRtpSender.', 'TypeError');\n      }\n      const isLocal = sender._pc === this;\n      if (!isLocal) {\n        throw new DOMException('Sender was not created by this connection.',\n            'InvalidAccessError');\n      }\n\n      // Search for the native stream the senders track belongs to.\n      this._streams = this._streams || {};\n      let stream;\n      Object.keys(this._streams).forEach(streamid => {\n        const hasTrack = this._streams[streamid].getTracks()\n          .find(track => sender.track === track);\n        if (hasTrack) {\n          stream = this._streams[streamid];\n        }\n      });\n\n      if (stream) {\n        if (stream.getTracks().length === 1) {\n          // if this is the last track of the stream, remove the stream. This\n          // takes care of any shimmed _senders.\n          this.removeStream(this._reverseStreams[stream.id]);\n        } else {\n          // relying on the same odd chrome behaviour as above.\n          stream.removeTrack(sender.track);\n        }\n        this.dispatchEvent(new Event('negotiationneeded'));\n      }\n    };\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n  if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {\n    // very basic support for old versions.\n    window.RTCPeerConnection = window.webkitRTCPeerConnection;\n  }\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n\n  // shim implicit creation of RTCSessionDescription/RTCIceCandidate\n  if (browserDetails.version < 53) {\n    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n        .forEach(function(method) {\n          const nativeMethod = window.RTCPeerConnection.prototype[method];\n          const methodObj = {[method]() {\n            arguments[0] = new ((method === 'addIceCandidate') ?\n                window.RTCIceCandidate :\n                window.RTCSessionDescription)(arguments[0]);\n            return nativeMethod.apply(this, arguments);\n          }};\n          window.RTCPeerConnection.prototype[method] = methodObj[method];\n        });\n  }\n}\n\n// Attempt to fix ONN in plan-b mode.\nexport function fixNegotiationNeeded(window, browserDetails) {\n  utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n    const pc = e.target;\n    if (browserDetails.version < 72 || (pc.getConfiguration &&\n        pc.getConfiguration().sdpSemantics === 'plan-b')) {\n      if (pc.signalingState !== 'stable') {\n        return;\n      }\n    }\n    return e;\n  });\n}\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window, browserDetails) {\n  const navigator = window && window.navigator;\n  const MediaStreamTrack = window && window.MediaStreamTrack;\n\n  navigator.getUserMedia = function(constraints, onSuccess, onError) {\n    // Replace Firefox 44+'s deprecation warning with unprefixed version.\n    utils.deprecated('navigator.getUserMedia',\n        'navigator.mediaDevices.getUserMedia');\n    navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);\n  };\n\n  if (!(browserDetails.version > 55 &&\n      'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {\n    const remap = function(obj, a, b) {\n      if (a in obj && !(b in obj)) {\n        obj[b] = obj[a];\n        delete obj[a];\n      }\n    };\n\n    const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.\n        bind(navigator.mediaDevices);\n    navigator.mediaDevices.getUserMedia = function(c) {\n      if (typeof c === 'object' && typeof c.audio === 'object') {\n        c = JSON.parse(JSON.stringify(c));\n        remap(c.audio, 'autoGainControl', 'mozAutoGainControl');\n        remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');\n      }\n      return nativeGetUserMedia(c);\n    };\n\n    if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {\n      const nativeGetSettings = MediaStreamTrack.prototype.getSettings;\n      MediaStreamTrack.prototype.getSettings = function() {\n        const obj = nativeGetSettings.apply(this, arguments);\n        remap(obj, 'mozAutoGainControl', 'autoGainControl');\n        remap(obj, 'mozNoiseSuppression', 'noiseSuppression');\n        return obj;\n      };\n    }\n\n    if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {\n      const nativeApplyConstraints =\n        MediaStreamTrack.prototype.applyConstraints;\n      MediaStreamTrack.prototype.applyConstraints = function(c) {\n        if (this.kind === 'audio' && typeof c === 'object') {\n          c = JSON.parse(JSON.stringify(c));\n          remap(c, 'autoGainControl', 'mozAutoGainControl');\n          remap(c, 'noiseSuppression', 'mozNoiseSuppression');\n        }\n        return nativeApplyConstraints.apply(this, [c]);\n      };\n    }\n  }\n}\n","/*\n *  Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\n\nexport function shimGetDisplayMedia(window, preferredMediaSource) {\n  if (window.navigator.mediaDevices &&\n    'getDisplayMedia' in window.navigator.mediaDevices) {\n    return;\n  }\n  if (!(window.navigator.mediaDevices)) {\n    return;\n  }\n  window.navigator.mediaDevices.getDisplayMedia =\n    function getDisplayMedia(constraints) {\n      if (!(constraints && constraints.video)) {\n        const err = new DOMException('getDisplayMedia without video ' +\n            'constraints is undefined');\n        err.name = 'NotFoundError';\n        // from https://heycam.github.io/webidl/#idl-DOMException-error-names\n        err.code = 8;\n        return Promise.reject(err);\n      }\n      if (constraints.video === true) {\n        constraints.video = {mediaSource: preferredMediaSource};\n      } else {\n        constraints.video.mediaSource = preferredMediaSource;\n      }\n      return window.navigator.mediaDevices.getUserMedia(constraints);\n    };\n}\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport * as utils from '../utils';\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimOnTrack(window) {\n  if (typeof window === 'object' && window.RTCTrackEvent &&\n      ('receiver' in window.RTCTrackEvent.prototype) &&\n      !('transceiver' in window.RTCTrackEvent.prototype)) {\n    Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n      get() {\n        return {receiver: this.receiver};\n      }\n    });\n  }\n}\n\nexport function shimPeerConnection(window, browserDetails) {\n  if (typeof window !== 'object' ||\n      !(window.RTCPeerConnection || window.mozRTCPeerConnection)) {\n    return; // probably media.peerconnection.enabled=false in about:config\n  }\n  if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {\n    // very basic support for old versions.\n    window.RTCPeerConnection = window.mozRTCPeerConnection;\n  }\n\n  if (browserDetails.version < 53) {\n    // shim away need for obsolete RTCIceCandidate/RTCSessionDescription.\n    ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']\n        .forEach(function(method) {\n          const nativeMethod = window.RTCPeerConnection.prototype[method];\n          const methodObj = {[method]() {\n            arguments[0] = new ((method === 'addIceCandidate') ?\n                window.RTCIceCandidate :\n                window.RTCSessionDescription)(arguments[0]);\n            return nativeMethod.apply(this, arguments);\n          }};\n          window.RTCPeerConnection.prototype[method] = methodObj[method];\n        });\n  }\n\n  const modernStatsTypes = {\n    inboundrtp: 'inbound-rtp',\n    outboundrtp: 'outbound-rtp',\n    candidatepair: 'candidate-pair',\n    localcandidate: 'local-candidate',\n    remotecandidate: 'remote-candidate'\n  };\n\n  const nativeGetStats = window.RTCPeerConnection.prototype.getStats;\n  window.RTCPeerConnection.prototype.getStats = function getStats() {\n    const [selector, onSucc, onErr] = arguments;\n    return nativeGetStats.apply(this, [selector || null])\n      .then(stats => {\n        if (browserDetails.version < 53 && !onSucc) {\n          // Shim only promise getStats with spec-hyphens in type names\n          // Leave callback version alone; misc old uses of forEach before Map\n          try {\n            stats.forEach(stat => {\n              stat.type = modernStatsTypes[stat.type] || stat.type;\n            });\n          } catch (e) {\n            if (e.name !== 'TypeError') {\n              throw e;\n            }\n            // Avoid TypeError: \"type\" is read-only, in old versions. 34-43ish\n            stats.forEach((stat, i) => {\n              stats.set(i, Object.assign({}, stat, {\n                type: modernStatsTypes[stat.type] || stat.type\n              }));\n            });\n          }\n        }\n        return stats;\n      })\n      .then(onSucc, onErr);\n  };\n}\n\nexport function shimSenderGetStats(window) {\n  if (!(typeof window === 'object' && window.RTCPeerConnection &&\n      window.RTCRtpSender)) {\n    return;\n  }\n  if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {\n    return;\n  }\n  const origGetSenders = window.RTCPeerConnection.prototype.getSenders;\n  if (origGetSenders) {\n    window.RTCPeerConnection.prototype.getSenders = function getSenders() {\n      const senders = origGetSenders.apply(this, []);\n      senders.forEach(sender => sender._pc = this);\n      return senders;\n    };\n  }\n\n  const origAddTrack = window.RTCPeerConnection.prototype.addTrack;\n  if (origAddTrack) {\n    window.RTCPeerConnection.prototype.addTrack = function addTrack() {\n      const sender = origAddTrack.apply(this, arguments);\n      sender._pc = this;\n      return sender;\n    };\n  }\n  window.RTCRtpSender.prototype.getStats = function getStats() {\n    return this.track ? this._pc.getStats(this.track) :\n        Promise.resolve(new Map());\n  };\n}\n\nexport function shimReceiverGetStats(window) {\n  if (!(typeof window === 'object' && window.RTCPeerConnection &&\n      window.RTCRtpSender)) {\n    return;\n  }\n  if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {\n    return;\n  }\n  const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;\n  if (origGetReceivers) {\n    window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {\n      const receivers = origGetReceivers.apply(this, []);\n      receivers.forEach(receiver => receiver._pc = this);\n      return receivers;\n    };\n  }\n  utils.wrapPeerConnectionEvent(window, 'track', e => {\n    e.receiver._pc = e.srcElement;\n    return e;\n  });\n  window.RTCRtpReceiver.prototype.getStats = function getStats() {\n    return this._pc.getStats(this.track);\n  };\n}\n\nexport function shimRemoveStream(window) {\n  if (!window.RTCPeerConnection ||\n      'removeStream' in window.RTCPeerConnection.prototype) {\n    return;\n  }\n  window.RTCPeerConnection.prototype.removeStream =\n    function removeStream(stream) {\n      utils.deprecated('removeStream', 'removeTrack');\n      this.getSenders().forEach(sender => {\n        if (sender.track && stream.getTracks().includes(sender.track)) {\n          this.removeTrack(sender);\n        }\n      });\n    };\n}\n\nexport function shimRTCDataChannel(window) {\n  // rename DataChannel to RTCDataChannel (native fix in FF60):\n  // https://bugzilla.mozilla.org/show_bug.cgi?id=1173851\n  if (window.DataChannel && !window.RTCDataChannel) {\n    window.RTCDataChannel = window.DataChannel;\n  }\n}\n\nexport function shimAddTransceiver(window) {\n  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n  // Firefox ignores the init sendEncodings options passed to addTransceiver\n  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n  if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n    return;\n  }\n  const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;\n  if (origAddTransceiver) {\n    window.RTCPeerConnection.prototype.addTransceiver =\n      function addTransceiver() {\n        this.setParametersPromises = [];\n        const initParameters = arguments[1];\n        const shouldPerformCheck = initParameters &&\n                                  'sendEncodings' in initParameters;\n        if (shouldPerformCheck) {\n          // If sendEncodings params are provided, validate grammar\n          initParameters.sendEncodings.forEach((encodingParam) => {\n            if ('rid' in encodingParam) {\n              const ridRegex = /^[a-z0-9]{0,16}$/i;\n              if (!ridRegex.test(encodingParam.rid)) {\n                throw new TypeError('Invalid RID value provided.');\n              }\n            }\n            if ('scaleResolutionDownBy' in encodingParam) {\n              if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {\n                throw new RangeError('scale_resolution_down_by must be >= 1.0');\n              }\n            }\n            if ('maxFramerate' in encodingParam) {\n              if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {\n                throw new RangeError('max_framerate must be >= 0.0');\n              }\n            }\n          });\n        }\n        const transceiver = origAddTransceiver.apply(this, arguments);\n        if (shouldPerformCheck) {\n          // Check if the init options were applied. If not we do this in an\n          // asynchronous way and save the promise reference in a global object.\n          // This is an ugly hack, but at the same time is way more robust than\n          // checking the sender parameters before and after the createOffer\n          // Also note that after the createoffer we are not 100% sure that\n          // the params were asynchronously applied so we might miss the\n          // opportunity to recreate offer.\n          const {sender} = transceiver;\n          const params = sender.getParameters();\n          if (!('encodings' in params) ||\n              // Avoid being fooled by patched getParameters() below.\n              (params.encodings.length === 1 &&\n               Object.keys(params.encodings[0]).length === 0)) {\n            params.encodings = initParameters.sendEncodings;\n            sender.sendEncodings = initParameters.sendEncodings;\n            this.setParametersPromises.push(sender.setParameters(params)\n              .then(() => {\n                delete sender.sendEncodings;\n              }).catch(() => {\n                delete sender.sendEncodings;\n              })\n            );\n          }\n        }\n        return transceiver;\n      };\n  }\n}\n\nexport function shimGetParameters(window) {\n  if (!(typeof window === 'object' && window.RTCRtpSender)) {\n    return;\n  }\n  const origGetParameters = window.RTCRtpSender.prototype.getParameters;\n  if (origGetParameters) {\n    window.RTCRtpSender.prototype.getParameters =\n      function getParameters() {\n        const params = origGetParameters.apply(this, arguments);\n        if (!('encodings' in params)) {\n          params.encodings = [].concat(this.sendEncodings || [{}]);\n        }\n        return params;\n      };\n  }\n}\n\nexport function shimCreateOffer(window) {\n  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n  // Firefox ignores the init sendEncodings options passed to addTransceiver\n  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n  if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n    return;\n  }\n  const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n  window.RTCPeerConnection.prototype.createOffer = function createOffer() {\n    if (this.setParametersPromises && this.setParametersPromises.length) {\n      return Promise.all(this.setParametersPromises)\n      .then(() => {\n        return origCreateOffer.apply(this, arguments);\n      })\n      .finally(() => {\n        this.setParametersPromises = [];\n      });\n    }\n    return origCreateOffer.apply(this, arguments);\n  };\n}\n\nexport function shimCreateAnswer(window) {\n  // https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647\n  // Firefox ignores the init sendEncodings options passed to addTransceiver\n  // https://bugzilla.mozilla.org/show_bug.cgi?id=1396918\n  if (!(typeof window === 'object' && window.RTCPeerConnection)) {\n    return;\n  }\n  const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;\n  window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {\n    if (this.setParametersPromises && this.setParametersPromises.length) {\n      return Promise.all(this.setParametersPromises)\n      .then(() => {\n        return origCreateAnswer.apply(this, arguments);\n      })\n      .finally(() => {\n        this.setParametersPromises = [];\n      });\n    }\n    return origCreateAnswer.apply(this, arguments);\n  };\n}\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n'use strict';\nimport * as utils from '../utils';\n\nexport function shimLocalStreamsAPI(window) {\n  if (typeof window !== 'object' || !window.RTCPeerConnection) {\n    return;\n  }\n  if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {\n    window.RTCPeerConnection.prototype.getLocalStreams =\n      function getLocalStreams() {\n        if (!this._localStreams) {\n          this._localStreams = [];\n        }\n        return this._localStreams;\n      };\n  }\n  if (!('addStream' in window.RTCPeerConnection.prototype)) {\n    const _addTrack = window.RTCPeerConnection.prototype.addTrack;\n    window.RTCPeerConnection.prototype.addStream = function addStream(stream) {\n      if (!this._localStreams) {\n        this._localStreams = [];\n      }\n      if (!this._localStreams.includes(stream)) {\n        this._localStreams.push(stream);\n      }\n      // Try to emulate Chrome's behaviour of adding in audio-video order.\n      // Safari orders by track id.\n      stream.getAudioTracks().forEach(track => _addTrack.call(this, track,\n        stream));\n      stream.getVideoTracks().forEach(track => _addTrack.call(this, track,\n        stream));\n    };\n\n    window.RTCPeerConnection.prototype.addTrack =\n      function addTrack(track, ...streams) {\n        if (streams) {\n          streams.forEach((stream) => {\n            if (!this._localStreams) {\n              this._localStreams = [stream];\n            } else if (!this._localStreams.includes(stream)) {\n              this._localStreams.push(stream);\n            }\n          });\n        }\n        return _addTrack.apply(this, arguments);\n      };\n  }\n  if (!('removeStream' in window.RTCPeerConnection.prototype)) {\n    window.RTCPeerConnection.prototype.removeStream =\n      function removeStream(stream) {\n        if (!this._localStreams) {\n          this._localStreams = [];\n        }\n        const index = this._localStreams.indexOf(stream);\n        if (index === -1) {\n          return;\n        }\n        this._localStreams.splice(index, 1);\n        const tracks = stream.getTracks();\n        this.getSenders().forEach(sender => {\n          if (tracks.includes(sender.track)) {\n            this.removeTrack(sender);\n          }\n        });\n      };\n  }\n}\n\nexport function shimRemoteStreamsAPI(window) {\n  if (typeof window !== 'object' || !window.RTCPeerConnection) {\n    return;\n  }\n  if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {\n    window.RTCPeerConnection.prototype.getRemoteStreams =\n      function getRemoteStreams() {\n        return this._remoteStreams ? this._remoteStreams : [];\n      };\n  }\n  if (!('onaddstream' in window.RTCPeerConnection.prototype)) {\n    Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {\n      get() {\n        return this._onaddstream;\n      },\n      set(f) {\n        if (this._onaddstream) {\n          this.removeEventListener('addstream', this._onaddstream);\n          this.removeEventListener('track', this._onaddstreampoly);\n        }\n        this.addEventListener('addstream', this._onaddstream = f);\n        this.addEventListener('track', this._onaddstreampoly = (e) => {\n          e.streams.forEach(stream => {\n            if (!this._remoteStreams) {\n              this._remoteStreams = [];\n            }\n            if (this._remoteStreams.includes(stream)) {\n              return;\n            }\n            this._remoteStreams.push(stream);\n            const event = new Event('addstream');\n            event.stream = stream;\n            this.dispatchEvent(event);\n          });\n        });\n      }\n    });\n    const origSetRemoteDescription =\n      window.RTCPeerConnection.prototype.setRemoteDescription;\n    window.RTCPeerConnection.prototype.setRemoteDescription =\n      function setRemoteDescription() {\n        const pc = this;\n        if (!this._onaddstreampoly) {\n          this.addEventListener('track', this._onaddstreampoly = function(e) {\n            e.streams.forEach(stream => {\n              if (!pc._remoteStreams) {\n                pc._remoteStreams = [];\n              }\n              if (pc._remoteStreams.indexOf(stream) >= 0) {\n                return;\n              }\n              pc._remoteStreams.push(stream);\n              const event = new Event('addstream');\n              event.stream = stream;\n              pc.dispatchEvent(event);\n            });\n          });\n        }\n        return origSetRemoteDescription.apply(pc, arguments);\n      };\n  }\n}\n\nexport function shimCallbacksAPI(window) {\n  if (typeof window !== 'object' || !window.RTCPeerConnection) {\n    return;\n  }\n  const prototype = window.RTCPeerConnection.prototype;\n  const origCreateOffer = prototype.createOffer;\n  const origCreateAnswer = prototype.createAnswer;\n  const setLocalDescription = prototype.setLocalDescription;\n  const setRemoteDescription = prototype.setRemoteDescription;\n  const addIceCandidate = prototype.addIceCandidate;\n\n  prototype.createOffer =\n    function createOffer(successCallback, failureCallback) {\n      const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n      const promise = origCreateOffer.apply(this, [options]);\n      if (!failureCallback) {\n        return promise;\n      }\n      promise.then(successCallback, failureCallback);\n      return Promise.resolve();\n    };\n\n  prototype.createAnswer =\n    function createAnswer(successCallback, failureCallback) {\n      const options = (arguments.length >= 2) ? arguments[2] : arguments[0];\n      const promise = origCreateAnswer.apply(this, [options]);\n      if (!failureCallback) {\n        return promise;\n      }\n      promise.then(successCallback, failureCallback);\n      return Promise.resolve();\n    };\n\n  let withCallback = function(description, successCallback, failureCallback) {\n    const promise = setLocalDescription.apply(this, [description]);\n    if (!failureCallback) {\n      return promise;\n    }\n    promise.then(successCallback, failureCallback);\n    return Promise.resolve();\n  };\n  prototype.setLocalDescription = withCallback;\n\n  withCallback = function(description, successCallback, failureCallback) {\n    const promise = setRemoteDescription.apply(this, [description]);\n    if (!failureCallback) {\n      return promise;\n    }\n    promise.then(successCallback, failureCallback);\n    return Promise.resolve();\n  };\n  prototype.setRemoteDescription = withCallback;\n\n  withCallback = function(candidate, successCallback, failureCallback) {\n    const promise = addIceCandidate.apply(this, [candidate]);\n    if (!failureCallback) {\n      return promise;\n    }\n    promise.then(successCallback, failureCallback);\n    return Promise.resolve();\n  };\n  prototype.addIceCandidate = withCallback;\n}\n\nexport function shimGetUserMedia(window) {\n  const navigator = window && window.navigator;\n\n  if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n    // shim not needed in Safari 12.1\n    const mediaDevices = navigator.mediaDevices;\n    const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);\n    navigator.mediaDevices.getUserMedia = (constraints) => {\n      return _getUserMedia(shimConstraints(constraints));\n    };\n  }\n\n  if (!navigator.getUserMedia && navigator.mediaDevices &&\n    navigator.mediaDevices.getUserMedia) {\n    navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {\n      navigator.mediaDevices.getUserMedia(constraints)\n      .then(cb, errcb);\n    }.bind(navigator);\n  }\n}\n\nexport function shimConstraints(constraints) {\n  if (constraints && constraints.video !== undefined) {\n    return Object.assign({},\n      constraints,\n      {video: utils.compactObject(constraints.video)}\n    );\n  }\n\n  return constraints;\n}\n\nexport function shimRTCIceServerUrls(window) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  // migrate from non-spec RTCIceServer.url to RTCIceServer.urls\n  const OrigPeerConnection = window.RTCPeerConnection;\n  window.RTCPeerConnection =\n    function RTCPeerConnection(pcConfig, pcConstraints) {\n      if (pcConfig && pcConfig.iceServers) {\n        const newIceServers = [];\n        for (let i = 0; i < pcConfig.iceServers.length; i++) {\n          let server = pcConfig.iceServers[i];\n          if (!server.hasOwnProperty('urls') &&\n              server.hasOwnProperty('url')) {\n            utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n            server = JSON.parse(JSON.stringify(server));\n            server.urls = server.url;\n            delete server.url;\n            newIceServers.push(server);\n          } else {\n            newIceServers.push(pcConfig.iceServers[i]);\n          }\n        }\n        pcConfig.iceServers = newIceServers;\n      }\n      return new OrigPeerConnection(pcConfig, pcConstraints);\n    };\n  window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;\n  // wrap static methods. Currently just generateCertificate.\n  if ('generateCertificate' in OrigPeerConnection) {\n    Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {\n      get() {\n        return OrigPeerConnection.generateCertificate;\n      }\n    });\n  }\n}\n\nexport function shimTrackEventTransceiver(window) {\n  // Add event.transceiver member over deprecated event.receiver\n  if (typeof window === 'object' && window.RTCTrackEvent &&\n      'receiver' in window.RTCTrackEvent.prototype &&\n      !('transceiver' in window.RTCTrackEvent.prototype)) {\n    Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {\n      get() {\n        return {receiver: this.receiver};\n      }\n    });\n  }\n}\n\nexport function shimCreateOfferLegacy(window) {\n  const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;\n  window.RTCPeerConnection.prototype.createOffer =\n    function createOffer(offerOptions) {\n      if (offerOptions) {\n        if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {\n          // support bit values\n          offerOptions.offerToReceiveAudio =\n            !!offerOptions.offerToReceiveAudio;\n        }\n        const audioTransceiver = this.getTransceivers().find(transceiver =>\n          transceiver.receiver.track.kind === 'audio');\n        if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {\n          if (audioTransceiver.direction === 'sendrecv') {\n            if (audioTransceiver.setDirection) {\n              audioTransceiver.setDirection('sendonly');\n            } else {\n              audioTransceiver.direction = 'sendonly';\n            }\n          } else if (audioTransceiver.direction === 'recvonly') {\n            if (audioTransceiver.setDirection) {\n              audioTransceiver.setDirection('inactive');\n            } else {\n              audioTransceiver.direction = 'inactive';\n            }\n          }\n        } else if (offerOptions.offerToReceiveAudio === true &&\n            !audioTransceiver) {\n          this.addTransceiver('audio');\n        }\n\n        if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {\n          // support bit values\n          offerOptions.offerToReceiveVideo =\n            !!offerOptions.offerToReceiveVideo;\n        }\n        const videoTransceiver = this.getTransceivers().find(transceiver =>\n          transceiver.receiver.track.kind === 'video');\n        if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {\n          if (videoTransceiver.direction === 'sendrecv') {\n            if (videoTransceiver.setDirection) {\n              videoTransceiver.setDirection('sendonly');\n            } else {\n              videoTransceiver.direction = 'sendonly';\n            }\n          } else if (videoTransceiver.direction === 'recvonly') {\n            if (videoTransceiver.setDirection) {\n              videoTransceiver.setDirection('inactive');\n            } else {\n              videoTransceiver.direction = 'inactive';\n            }\n          }\n        } else if (offerOptions.offerToReceiveVideo === true &&\n            !videoTransceiver) {\n          this.addTransceiver('video');\n        }\n      }\n      return origCreateOffer.apply(this, arguments);\n    };\n}\n\nexport function shimAudioContext(window) {\n  if (typeof window !== 'object' || window.AudioContext) {\n    return;\n  }\n  window.AudioContext = window.webkitAudioContext;\n}\n","/*\n *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n'use strict';\n\nimport SDPUtils from 'sdp';\nimport * as utils from './utils';\n\nexport function shimRTCIceCandidate(window) {\n  // foundation is arbitrarily chosen as an indicator for full support for\n  // https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface\n  if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in\n      window.RTCIceCandidate.prototype)) {\n    return;\n  }\n\n  const NativeRTCIceCandidate = window.RTCIceCandidate;\n  window.RTCIceCandidate = function RTCIceCandidate(args) {\n    // Remove the a= which shouldn't be part of the candidate string.\n    if (typeof args === 'object' && args.candidate &&\n        args.candidate.indexOf('a=') === 0) {\n      args = JSON.parse(JSON.stringify(args));\n      args.candidate = args.candidate.substr(2);\n    }\n\n    if (args.candidate && args.candidate.length) {\n      // Augment the native candidate with the parsed fields.\n      const nativeCandidate = new NativeRTCIceCandidate(args);\n      const parsedCandidate = SDPUtils.parseCandidate(args.candidate);\n      const augmentedCandidate = Object.assign(nativeCandidate,\n          parsedCandidate);\n\n      // Add a serializer that does not serialize the extra attributes.\n      augmentedCandidate.toJSON = function toJSON() {\n        return {\n          candidate: augmentedCandidate.candidate,\n          sdpMid: augmentedCandidate.sdpMid,\n          sdpMLineIndex: augmentedCandidate.sdpMLineIndex,\n          usernameFragment: augmentedCandidate.usernameFragment,\n        };\n      };\n      return augmentedCandidate;\n    }\n    return new NativeRTCIceCandidate(args);\n  };\n  window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;\n\n  // Hook up the augmented candidate in onicecandidate and\n  // addEventListener('icecandidate', ...)\n  utils.wrapPeerConnectionEvent(window, 'icecandidate', e => {\n    if (e.candidate) {\n      Object.defineProperty(e, 'candidate', {\n        value: new window.RTCIceCandidate(e.candidate),\n        writable: 'false'\n      });\n    }\n    return e;\n  });\n}\n\nexport function shimMaxMessageSize(window, browserDetails) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n\n  if (!('sctp' in window.RTCPeerConnection.prototype)) {\n    Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {\n      get() {\n        return typeof this._sctp === 'undefined' ? null : this._sctp;\n      }\n    });\n  }\n\n  const sctpInDescription = function(description) {\n    if (!description || !description.sdp) {\n      return false;\n    }\n    const sections = SDPUtils.splitSections(description.sdp);\n    sections.shift();\n    return sections.some(mediaSection => {\n      const mLine = SDPUtils.parseMLine(mediaSection);\n      return mLine && mLine.kind === 'application'\n          && mLine.protocol.indexOf('SCTP') !== -1;\n    });\n  };\n\n  const getRemoteFirefoxVersion = function(description) {\n    // TODO: Is there a better solution for detecting Firefox?\n    const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\\d+)/);\n    if (match === null || match.length < 2) {\n      return -1;\n    }\n    const version = parseInt(match[1], 10);\n    // Test for NaN (yes, this is ugly)\n    return version !== version ? -1 : version;\n  };\n\n  const getCanSendMaxMessageSize = function(remoteIsFirefox) {\n    // Every implementation we know can send at least 64 KiB.\n    // Note: Although Chrome is technically able to send up to 256 KiB, the\n    //       data does not reach the other peer reliably.\n    //       See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419\n    let canSendMaxMessageSize = 65536;\n    if (browserDetails.browser === 'firefox') {\n      if (browserDetails.version < 57) {\n        if (remoteIsFirefox === -1) {\n          // FF < 57 will send in 16 KiB chunks using the deprecated PPID\n          // fragmentation.\n          canSendMaxMessageSize = 16384;\n        } else {\n          // However, other FF (and RAWRTC) can reassemble PPID-fragmented\n          // messages. Thus, supporting ~2 GiB when sending.\n          canSendMaxMessageSize = 2147483637;\n        }\n      } else if (browserDetails.version < 60) {\n        // Currently, all FF >= 57 will reset the remote maximum message size\n        // to the default value when a data channel is created at a later\n        // stage. :(\n        // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n        canSendMaxMessageSize =\n          browserDetails.version === 57 ? 65535 : 65536;\n      } else {\n        // FF >= 60 supports sending ~2 GiB\n        canSendMaxMessageSize = 2147483637;\n      }\n    }\n    return canSendMaxMessageSize;\n  };\n\n  const getMaxMessageSize = function(description, remoteIsFirefox) {\n    // Note: 65536 bytes is the default value from the SDP spec. Also,\n    //       every implementation we know supports receiving 65536 bytes.\n    let maxMessageSize = 65536;\n\n    // FF 57 has a slightly incorrect default remote max message size, so\n    // we need to adjust it here to avoid a failure when sending.\n    // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697\n    if (browserDetails.browser === 'firefox'\n         && browserDetails.version === 57) {\n      maxMessageSize = 65535;\n    }\n\n    const match = SDPUtils.matchPrefix(description.sdp,\n      'a=max-message-size:');\n    if (match.length > 0) {\n      maxMessageSize = parseInt(match[0].substr(19), 10);\n    } else if (browserDetails.browser === 'firefox' &&\n                remoteIsFirefox !== -1) {\n      // If the maximum message size is not present in the remote SDP and\n      // both local and remote are Firefox, the remote peer can receive\n      // ~2 GiB.\n      maxMessageSize = 2147483637;\n    }\n    return maxMessageSize;\n  };\n\n  const origSetRemoteDescription =\n      window.RTCPeerConnection.prototype.setRemoteDescription;\n  window.RTCPeerConnection.prototype.setRemoteDescription =\n    function setRemoteDescription() {\n      this._sctp = null;\n      // Chrome decided to not expose .sctp in plan-b mode.\n      // As usual, adapter.js has to do an 'ugly worakaround'\n      // to cover up the mess.\n      if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {\n        const {sdpSemantics} = this.getConfiguration();\n        if (sdpSemantics === 'plan-b') {\n          Object.defineProperty(this, 'sctp', {\n            get() {\n              return typeof this._sctp === 'undefined' ? null : this._sctp;\n            },\n            enumerable: true,\n            configurable: true,\n          });\n        }\n      }\n\n      if (sctpInDescription(arguments[0])) {\n        // Check if the remote is FF.\n        const isFirefox = getRemoteFirefoxVersion(arguments[0]);\n\n        // Get the maximum message size the local peer is capable of sending\n        const canSendMMS = getCanSendMaxMessageSize(isFirefox);\n\n        // Get the maximum message size of the remote peer.\n        const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);\n\n        // Determine final maximum message size\n        let maxMessageSize;\n        if (canSendMMS === 0 && remoteMMS === 0) {\n          maxMessageSize = Number.POSITIVE_INFINITY;\n        } else if (canSendMMS === 0 || remoteMMS === 0) {\n          maxMessageSize = Math.max(canSendMMS, remoteMMS);\n        } else {\n          maxMessageSize = Math.min(canSendMMS, remoteMMS);\n        }\n\n        // Create a dummy RTCSctpTransport object and the 'maxMessageSize'\n        // attribute.\n        const sctp = {};\n        Object.defineProperty(sctp, 'maxMessageSize', {\n          get() {\n            return maxMessageSize;\n          }\n        });\n        this._sctp = sctp;\n      }\n\n      return origSetRemoteDescription.apply(this, arguments);\n    };\n}\n\nexport function shimSendThrowTypeError(window) {\n  if (!(window.RTCPeerConnection &&\n      'createDataChannel' in window.RTCPeerConnection.prototype)) {\n    return;\n  }\n\n  // Note: Although Firefox >= 57 has a native implementation, the maximum\n  //       message size can be reset for all data channels at a later stage.\n  //       See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831\n\n  function wrapDcSend(dc, pc) {\n    const origDataChannelSend = dc.send;\n    dc.send = function send() {\n      const data = arguments[0];\n      const length = data.length || data.size || data.byteLength;\n      if (dc.readyState === 'open' &&\n          pc.sctp && length > pc.sctp.maxMessageSize) {\n        throw new TypeError('Message too large (can send a maximum of ' +\n          pc.sctp.maxMessageSize + ' bytes)');\n      }\n      return origDataChannelSend.apply(dc, arguments);\n    };\n  }\n  const origCreateDataChannel =\n    window.RTCPeerConnection.prototype.createDataChannel;\n  window.RTCPeerConnection.prototype.createDataChannel =\n    function createDataChannel() {\n      const dataChannel = origCreateDataChannel.apply(this, arguments);\n      wrapDcSend(dataChannel, this);\n      return dataChannel;\n    };\n  utils.wrapPeerConnectionEvent(window, 'datachannel', e => {\n    wrapDcSend(e.channel, e.target);\n    return e;\n  });\n}\n\n\n/* shims RTCConnectionState by pretending it is the same as iceConnectionState.\n * See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12\n * for why this is a valid hack in Chrome. In Firefox it is slightly incorrect\n * since DTLS failures would be hidden. See\n * https://bugzilla.mozilla.org/show_bug.cgi?id=1265827\n * for the Firefox tracking bug.\n */\nexport function shimConnectionState(window) {\n  if (!window.RTCPeerConnection ||\n      'connectionState' in window.RTCPeerConnection.prototype) {\n    return;\n  }\n  const proto = window.RTCPeerConnection.prototype;\n  Object.defineProperty(proto, 'connectionState', {\n    get() {\n      return {\n        completed: 'connected',\n        checking: 'connecting'\n      }[this.iceConnectionState] || this.iceConnectionState;\n    },\n    enumerable: true,\n    configurable: true\n  });\n  Object.defineProperty(proto, 'onconnectionstatechange', {\n    get() {\n      return this._onconnectionstatechange || null;\n    },\n    set(cb) {\n      if (this._onconnectionstatechange) {\n        this.removeEventListener('connectionstatechange',\n            this._onconnectionstatechange);\n        delete this._onconnectionstatechange;\n      }\n      if (cb) {\n        this.addEventListener('connectionstatechange',\n            this._onconnectionstatechange = cb);\n      }\n    },\n    enumerable: true,\n    configurable: true\n  });\n\n  ['setLocalDescription', 'setRemoteDescription'].forEach((method) => {\n    const origMethod = proto[method];\n    proto[method] = function() {\n      if (!this._connectionstatechangepoly) {\n        this._connectionstatechangepoly = e => {\n          const pc = e.target;\n          if (pc._lastConnectionState !== pc.connectionState) {\n            pc._lastConnectionState = pc.connectionState;\n            const newEvent = new Event('connectionstatechange', e);\n            pc.dispatchEvent(newEvent);\n          }\n          return e;\n        };\n        this.addEventListener('iceconnectionstatechange',\n          this._connectionstatechangepoly);\n      }\n      return origMethod.apply(this, arguments);\n    };\n  });\n}\n\nexport function removeExtmapAllowMixed(window, browserDetails) {\n  /* remove a=extmap-allow-mixed for webrtc.org < M71 */\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\n    return;\n  }\n  if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {\n    return;\n  }\n  const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;\n  window.RTCPeerConnection.prototype.setRemoteDescription =\n  function setRemoteDescription(desc) {\n    if (desc && desc.sdp && desc.sdp.indexOf('\\na=extmap-allow-mixed') !== -1) {\n      const sdp = desc.sdp.split('\\n').filter((line) => {\n        return line.trim() !== 'a=extmap-allow-mixed';\n      }).join('\\n');\n      // Safari enforces read-only-ness of RTCSessionDescription fields.\n      if (window.RTCSessionDescription &&\n          desc instanceof window.RTCSessionDescription) {\n        arguments[0] = new window.RTCSessionDescription({\n          type: desc.type,\n          sdp,\n        });\n      } else {\n        desc.sdp = sdp;\n      }\n    }\n    return nativeSRD.apply(this, arguments);\n  };\n}\n\nexport function shimAddIceCandidateNullOrEmpty(window, browserDetails) {\n  // Support for addIceCandidate(null or undefined)\n  // as well as addIceCandidate({candidate: \"\", ...})\n  // https://bugs.chromium.org/p/chromium/issues/detail?id=978582\n  // Note: must be called before other polyfills which change the signature.\n  if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {\n    return;\n  }\n  const nativeAddIceCandidate =\n      window.RTCPeerConnection.prototype.addIceCandidate;\n  if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {\n    return;\n  }\n  window.RTCPeerConnection.prototype.addIceCandidate =\n    function addIceCandidate() {\n      if (!arguments[0]) {\n        if (arguments[1]) {\n          arguments[1].apply(null);\n        }\n        return Promise.resolve();\n      }\n      // Firefox 68+ emits and processes {candidate: \"\", ...}, ignore\n      // in older versions.\n      // Native support for ignoring exists for Chrome M77+.\n      // Safari ignores as well, exact version unknown but works in the same\n      // version that also ignores addIceCandidate(null).\n      if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)\n           || (browserDetails.browser === 'firefox'\n               && browserDetails.version < 68)\n           || (browserDetails.browser === 'safari'))\n          && arguments[0] && arguments[0].candidate === '') {\n        return Promise.resolve();\n      }\n      return nativeAddIceCandidate.apply(this, arguments);\n    };\n}\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\n/* eslint-env node */\n\n'use strict';\n\nimport {adapterFactory} from './adapter_factory.js';\n\nconst adapter =\n  adapterFactory({window: typeof window === 'undefined' ? undefined : window});\nexport default adapter;\n","/*\n *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.\n *\n *  Use of this source code is governed by a BSD-style license\n *  that can be found in the LICENSE file in the root of the source\n *  tree.\n */\nimport * as utils from './utils';\n\n  // Browser shims.\nimport * as chromeShim from './chrome/chrome_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\nimport * as sdp from 'sdp';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n  shimChrome: true,\n  shimFirefox: true,\n  shimSafari: true,\n}) {\n  // Utils.\n  const logging = utils.log;\n  const browserDetails = utils.detectBrowser(window);\n\n  const adapter = {\n    browserDetails,\n    commonShim,\n    extractVersion: utils.extractVersion,\n    disableLog: utils.disableLog,\n    disableWarnings: utils.disableWarnings,\n    // Expose sdp as a convenience. For production apps include directly.\n    sdp,\n  };\n\n  // Shim browser if found.\n  switch (browserDetails.browser) {\n    case 'chrome':\n      if (!chromeShim || !chromeShim.shimPeerConnection ||\n          !options.shimChrome) {\n        logging('Chrome shim is not included in this adapter release.');\n        return adapter;\n      }\n      if (browserDetails.version === null) {\n        logging('Chrome shim can not determine version, not shimming.');\n        return adapter;\n      }\n      logging('adapter.js shimming chrome.');\n      // Export to the adapter global object visible in the browser.\n      adapter.browserShim = chromeShim;\n\n      // Must be called before shimPeerConnection.\n      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n      chromeShim.shimGetUserMedia(window, browserDetails);\n      chromeShim.shimMediaStream(window, browserDetails);\n      chromeShim.shimPeerConnection(window, browserDetails);\n      chromeShim.shimOnTrack(window, browserDetails);\n      chromeShim.shimAddTrackRemoveTrack(window, browserDetails);\n      chromeShim.shimGetSendersWithDtmf(window, browserDetails);\n      chromeShim.shimGetStats(window, browserDetails);\n      chromeShim.shimSenderReceiverGetStats(window, browserDetails);\n      chromeShim.fixNegotiationNeeded(window, browserDetails);\n\n      commonShim.shimRTCIceCandidate(window, browserDetails);\n      commonShim.shimConnectionState(window, browserDetails);\n      commonShim.shimMaxMessageSize(window, browserDetails);\n      commonShim.shimSendThrowTypeError(window, browserDetails);\n      commonShim.removeExtmapAllowMixed(window, browserDetails);\n      break;\n    case 'firefox':\n      if (!firefoxShim || !firefoxShim.shimPeerConnection ||\n          !options.shimFirefox) {\n        logging('Firefox shim is not included in this adapter release.');\n        return adapter;\n      }\n      logging('adapter.js shimming firefox.');\n      // Export to the adapter global object visible in the browser.\n      adapter.browserShim = firefoxShim;\n\n      // Must be called before shimPeerConnection.\n      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n      firefoxShim.shimGetUserMedia(window, browserDetails);\n      firefoxShim.shimPeerConnection(window, browserDetails);\n      firefoxShim.shimOnTrack(window, browserDetails);\n      firefoxShim.shimRemoveStream(window, browserDetails);\n      firefoxShim.shimSenderGetStats(window, browserDetails);\n      firefoxShim.shimReceiverGetStats(window, browserDetails);\n      firefoxShim.shimRTCDataChannel(window, browserDetails);\n      firefoxShim.shimAddTransceiver(window, browserDetails);\n      firefoxShim.shimGetParameters(window, browserDetails);\n      firefoxShim.shimCreateOffer(window, browserDetails);\n      firefoxShim.shimCreateAnswer(window, browserDetails);\n\n      commonShim.shimRTCIceCandidate(window, browserDetails);\n      commonShim.shimConnectionState(window, browserDetails);\n      commonShim.shimMaxMessageSize(window, browserDetails);\n      commonShim.shimSendThrowTypeError(window, browserDetails);\n      break;\n    case 'safari':\n      if (!safariShim || !options.shimSafari) {\n        logging('Safari shim is not included in this adapter release.');\n        return adapter;\n      }\n      logging('adapter.js shimming safari.');\n      // Export to the adapter global object visible in the browser.\n      adapter.browserShim = safariShim;\n\n      // Must be called before shimCallbackAPI.\n      commonShim.shimAddIceCandidateNullOrEmpty(window, browserDetails);\n\n      safariShim.shimRTCIceServerUrls(window, browserDetails);\n      safariShim.shimCreateOfferLegacy(window, browserDetails);\n      safariShim.shimCallbacksAPI(window, browserDetails);\n      safariShim.shimLocalStreamsAPI(window, browserDetails);\n      safariShim.shimRemoteStreamsAPI(window, browserDetails);\n      safariShim.shimTrackEventTransceiver(window, browserDetails);\n      safariShim.shimGetUserMedia(window, browserDetails);\n      safariShim.shimAudioContext(window, browserDetails);\n\n      commonShim.shimRTCIceCandidate(window, browserDetails);\n      commonShim.shimMaxMessageSize(window, browserDetails);\n      commonShim.shimSendThrowTypeError(window, browserDetails);\n      commonShim.removeExtmapAllowMixed(window, browserDetails);\n      break;\n    default:\n      logging('Unsupported browser!');\n      break;\n  }\n\n  return adapter;\n}\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\tloaded: false,\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Flag the module as loaded\n\tmodule.loaded = true;\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","__webpack_require__.amdO = {};","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nmd = (module) => {\n\tmodule.paths = [];\n\tif (!module.children) module.children = [];\n\treturn module;\n};","// startup\n// Load entry module and return exports\n// This entry module used 'module' so it can't be inlined\nvar __webpack_exports__ = __webpack_require__(4449);\n"],"names":["root","factory","exports","module","define","amd","self","Logger","LogCollector","logStorage","options","this","stringifyObjects","storeInterval","maxEntryLength","Object","keys","levels","forEach","logLevel","_log","apply","arguments","bind","storeLogsIntervalID","queue","totalLen","outputCache","prototype","stringify","someObject","JSON","error","formatLogMessage","msg","i","len","length","arg","ERROR","timestamp","prevMessage","prevMessageText","text","count","push","_flush","start","_reschedulePublishInterval","window","clearTimeout","setTimeout","flush","force","reschedule","isReady","cachedQueue","storeLogs","stop","consoleTransport","console","globalTransports","addGlobalTransport","transport","indexOf","removeGlobalTransport","transportIdx","splice","globalOptions","getCallerInfo","callerInfo","methodName","fileLocation","line","column","Error","stack","split","m","match","substr","log","logger","level","args","Array","slice","call","disableCallerInfo","transports","concat","t","l","logPrefixes","Date","toISOString","id","fullLogParts","methods","setGlobalOptions","setLevel","TRACE","DEBUG","INFO","LOG","WARN","idLoggers","loggers","curLevel","getLogger","setLogLevelById","setLogLevel","grammar","v","name","reg","o","names","format","s","u","e","p","z","r","c","b","a","rate","address","subtype","direction","config","sessionConfig","str","raddr","tcptype","generation","attribute","value","maxMessageSize","RegExp","dir2","clksrcExt","mediaClockValue","rateNumerator","rateDenominator","key","obj","parser","writer","write","parse","parseParams","parseFmtpConfig","parsePayloads","parseRemoteCandidates","parseImageAttributes","parseSimulcastStreamList","toIntIfInt","String","Number","parseReg","location","content","needsBlank","keyLocation","rawName","attachProperties","validLine","test","sdp","session","media","filter","type","rtp","fmtp","j","paramReducer","acc","expr","undefined","reduce","toString","map","candidates","parts","component","ip","port","item","substring","stream","scid","paused","formatRegExp","formatStr","replace","x","makeLine","Function","n","defaultOuterOrder","defaultInnerOrder","opts","version","mLine","payloads","outerOrder","innerOrder","el","join","transform","transformUtils","parseSsrcs","writeSsrcs","getSsrcAttribute","ssrc","attributeName","ssrcs","ssrcInfo","Simulcast","numOfLayers","ssrcCache","processVideo","action","isArray","validateDescription","desc","clearSsrcCache","setSsrcCache","_parseSimLayers","simGroup","ssrcGroups","find","group","semantics","ssrcStr","parseInt","_buildNewToOldSsrcMap","newSsrcList","oldSsrcList","ssrcMap","newSsrc","oldSsrc","_fillInSourceDataFromCache","newSimSsrcs","newMsid","newCname","ssrcsToReplace","ssrcsToAdd","values","_generateSourceData","primarySsrc","addAssociatedStream","primarySsrcCname","primarySsrcMsid","usesUnifiedPlan","msid","simSsrcs","simSsrc","Math","floor","random","_restoreSimulcast","numSsrcs","index","array","numGroups","fidGroup","mungeRemoteDescription","enableConferenceFlag","explodeRemoteSimulcast","sources","order","simulcastSsrcs","cname","relatedGroup","relatedSsrcs","relatedSSRC","simulcastGroup","info","nuke","implodeRemoteSimulcast","invalid","some","assertGoogConference","xGoogleFlag","removeGoogConference","RTCSessionDescription","mungeLocalDescription","source","previous_async","async","only_once","fn","called","noConflict","_toString","_isArray","_each","arr","iterator","_map","results","_keys","k","hasOwnProperty","process","nextTick","setImmediate","each","callback","completed","done","err","eachSeries","iterate","forEachSeries","eachLimit","limit","_eachLimit","forEachLimit","started","running","replenish","doParallel","doSeries","_asyncMap","eachfn","mapSeries","mapLimit","_mapLimit","doParallelLimit","memo","inject","foldl","reduceRight","reversed","reverse","foldr","_filter","sort","filterSeries","select","selectSeries","_reject","reject","rejectSeries","_detect","main_callback","result","detect","detectSeries","any","every","all","sortBy","criteria","left","right","auto","tasks","remainingTasks","listeners","addListener","unshift","taskComplete","theCallback","task","taskCallback","safeResults","rkey","requires","abs","ready","listener","removeListener","retry","times","attempts","wrappedTask","wrappedCallback","wrappedResults","retryAttempt","finalAttempt","seriesCallback","series","data","waterfall","wrapIterator","next","_parallel","parallel","parallelLimit","makeCallback","_concat","cb","y","concatSeries","whilst","doWhilst","until","doUntil","worker","concurrency","_insert","q","pos","drain","saturated","workers","empty","kill","shift","idle","pause","resume","priorityQueue","_compareTasks","priority","sequence","compare","beg","end","mid","_binarySearch","cargo","payload","working","drained","ts","ds","_console_fn","dir","memoize","hasher","queues","memoized","pop","unmemoized","unmemoize","counter","timesSeries","seq","fns","that","newargs","nextargs","compose","_applyEach","go","applyEach","applyEachSeries","forever","constructor","userId","displayName","isLocalStats","_userId","setDisplayName","_isLocalStats","setDominantSpeaker","totalDominantSpeakerTime","_dominantSpeakerStart","_hasLeft","_facialExpressions","happy","neutral","surprised","angry","fearful","disgusted","sad","getUserId","getDisplayName","newName","isDominantSpeaker","isNowDominantSpeaker","now","timeElapsed","getTotalDominantSpeakerTime","total","hasLeft","markAsHasLeft","getFacialExpressions","setFacialExpressions","facialExpressions","addFacialExpression","facialExpression","duration","getTokenAuthUrl","urlPattern","roomName","roleUpgrade","EventEmitterForwarder","src","dest","emit","forward","srcEvent","handlers","oldOnErrorHandler","onerror","oldOnUnhandledRejection","onunhandledrejection","handler","event","reason","GlobalOnErrorHandler","addHandler","callErrorHandler","errHandler","callUnhandledRejectionHandler","randomInt","min","max","randomElement","RandomUtil","randomHexDigit","randomHexString","ret","randomAlphanumStr","currentExecutingScript","require","ScriptUtil","loadScript","prepend","relativeURL","loadCallback","errorCallback","d","document","tagName","script","createElement","referenceNode","getElementsByTagName","scriptEl","scriptSrc","baseScriptSrc","lastIndexOf","onload","parentNode","insertBefore","appendChild","integerHash","string","char","hash","charCodeAt","pow","generateUsername","CAMERA","DESKTOP","DESKTOP_HIGH_FPS","NONE","ENVIRONMENT","USER","H264","OPUS","ULPFEC","VP8","VP9","INACTIVE","RECVONLY","SENDONLY","SENDRECV","RTCEvents","CREATE_ANSWER_FAILED","CREATE_OFFER_FAILED","DATA_CHANNEL_OPEN","ENDPOINT_CONN_STATUS_CHANGED","DOMINANT_SPEAKER_CHANGED","LASTN_ENDPOINT_CHANGED","FORWARDED_SOURCES_CHANGED","PERMISSIONS_CHANGED","SENDER_VIDEO_CONSTRAINTS_CHANGED","LASTN_VALUE_CHANGED","LOCAL_TRACK_SSRC_UPDATED","LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED","TRACK_ATTACHED","REMOTE_TRACK_ADDED","REMOTE_TRACK_MUTE","REMOTE_TRACK_REMOVED","REMOTE_TRACK_UNMUTE","SET_LOCAL_DESCRIPTION_FAILED","SET_REMOTE_DESCRIPTION_FAILED","AUDIO_OUTPUT_DEVICE_CHANGED","DEVICE_LIST_CHANGED","DEVICE_LIST_WILL_CHANGE","DEVICE_LIST_AVAILABLE","ENDPOINT_MESSAGE_RECEIVED","ENDPOINT_STATS_RECEIVED","LOCAL_UFRAG_CHANGED","REMOTE_UFRAG_CHANGED","width","height","IDENTITY_UPDATED","LOCAL_JID","XMPPEvents","byteLength","b64","lens","getLens","validLen","placeHoldersLen","toByteArray","tmp","Arr","_byteLength","curByte","revLookup","fromByteArray","uint8","extraBytes","maxChunkLength","len2","encodeChunk","lookup","Uint8Array","code","num","output","defineProperty","enumerable","get","Symbol","toStringTag","__esModule","create","default","getFirstMatch","getSecondMatch","matchAndReturnConst","getWindowsVersionName","getMacOSVersionName","getAndroidVersionName","getVersionPrecision","compareVersions","getBrowserAlias","BROWSER_ALIASES_MAP","getBrowserTypeByAlias","BROWSER_MAP","ENGINE_MAP","OS_MAP","PLATFORMS_MAP","Bada","BlackBerry","Chrome","Chromium","Epiphany","Firefox","Focus","Generic","Googlebot","Maxthon","Opera","PhantomJS","Puffin","QupZilla","QQ","QQLite","Safari","Sailfish","SeaMonkey","Sleipnir","Swing","Tizen","Vivaldi","WeChat","Roku","amazon_silk","android","bada","blackberry","chrome","chromium","epiphany","firefox","focus","generic","googlebot","google_search","ie","k_meleon","maxthon","edge","mz","naver","opera","opera_coast","phantomjs","puffin","qupzilla","qq","qqlite","safari","sailfish","samsung_internet","seamonkey","sleipnir","swing","tizen","uc","vivaldi","webos","wechat","yandex","tablet","mobile","desktop","tv","WindowsPhone","Windows","MacOS","iOS","Android","WebOS","Linux","ChromeOS","PlayStation4","EdgeHTML","Blink","Trident","Presto","Gecko","WebKit","configurable","writable","getParser","getResult","_ua","parsedResult","getUA","parseBrowser","browser","describe","getBrowser","getBrowserName","toLowerCase","getBrowserVersion","getOS","os","parseOS","getOSName","getOSVersion","getPlatform","platform","parsePlatform","getPlatformType","getEngine","engine","parseEngine","getEngineName","assign","satisfies","isOS","isPlatform","f","isBrowser","compareVersion","isEngine","is","search","versionName","vendor","model","scriptReadyRegex","fullPageUrl","href","pageUrl","scripts","supportsScriptReadyState","isNotOpera","hasNativeCurrentScriptAccessor","stackTraceLimit","Infinity","hasStackBeforeThrowing","hasStackAfterThrowing","getScriptUrlFromStack","skipStackDepth","matches","url","ignoreMessage","round","_nearestExecutingScript","eligibleScripts","readyState","getScriptFromUrl","hasAttribute","getSoleInlineScript","currentScript","thrownErr","near","far","origin","ReflectOwnKeys","R","Reflect","ReflectApply","target","receiver","ownKeys","getOwnPropertySymbols","getOwnPropertyNames","NumberIsNaN","isNaN","EventEmitter","init","once","emitter","Promise","resolve","errorListener","resolver","eventTargetAgnosticAddListener","flags","on","addErrorHandlerIfEventEmitter","_events","_eventsCount","_maxListeners","defaultMaxListeners","checkListener","TypeError","_getMaxListeners","_addListener","events","existing","warning","newListener","warned","w","warn","onceWrapper","fired","wrapFn","_onceWrap","state","wrapped","_listeners","unwrap","evlistener","unwrapListeners","arrayClone","listenerCount","copy","addEventListener","wrapListener","removeEventListener","set","RangeError","getPrototypeOf","setMaxListeners","getMaxListeners","doError","er","message","context","prependListener","prependOnceListener","list","position","originalListener","spliceOne","off","removeAllListeners","rawListeners","eventNames","WINDOW","JS_MD5_NO_WINDOW","WEB_WORKER","NODE_JS","JS_MD5_NO_NODE_JS","versions","node","g","COMMON_JS","JS_MD5_NO_COMMON_JS","AMD","ARRAY_BUFFER","JS_MD5_NO_ARRAY_BUFFER","ArrayBuffer","HEX_CHARS","EXTRA","SHIFT","OUTPUT_TYPES","BASE64_ENCODE_CHAR","blocks","buffer8","buffer","Uint32Array","JS_MD5_NO_ARRAY_BUFFER_IS_VIEW","isView","createOutputMethod","outputType","Md5","update","createMethod","method","nodeWrap","crypto","eval","Buffer","nodeMethod","createHash","digest","sharedMemory","h0","h1","h2","h3","bytes","hBytes","finalized","hashed","first","notString","lastByteIndex","finalize","bc","da","hex","arrayBuffer","base64","v1","v2","v3","base64Str","md5","HASH_UNDEFINED","MAX_SAFE_INTEGER","argsTag","boolTag","dateTag","funcTag","genTag","mapTag","numberTag","objectTag","promiseTag","regexpTag","setTag","stringTag","symbolTag","weakMapTag","arrayBufferTag","dataViewTag","float32Tag","float64Tag","int8Tag","int16Tag","int32Tag","uint8Tag","uint8ClampedTag","uint16Tag","uint32Tag","reFlags","reIsHostCtor","reIsUint","cloneableTags","freeGlobal","freeSelf","freeExports","nodeType","freeModule","moduleExports","addMapEntry","pair","addSetEntry","add","arrayReduce","iteratee","accumulator","initAccum","isHostObject","mapToArray","size","overArg","func","setToArray","uid","arrayProto","funcProto","objectProto","coreJsData","maskSrcKey","exec","IE_PROTO","funcToString","objectToString","reIsNative","getPrototype","objectCreate","propertyIsEnumerable","nativeGetSymbols","nativeIsBuffer","isBuffer","nativeKeys","DataView","getNative","Map","Set","WeakMap","nativeCreate","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","valueOf","Hash","entries","clear","entry","ListCache","MapCache","Stack","__data__","assignValue","object","objValue","eq","assocIndexOf","baseClone","isDeep","isFull","customizer","isObject","isArr","input","initCloneArray","copyArray","tag","getTag","isFunc","cloneBuffer","isPrototype","proto","initCloneObject","copyObject","getSymbols","copySymbols","baseAssign","cloneFunc","symbol","Ctor","cloneArrayBuffer","dataView","byteOffset","cloneDataView","typedArray","cloneTypedArray","cloneMap","regexp","lastIndex","cloneRegExp","cloneSet","initCloneByTag","stacked","props","keysFunc","symbolsFunc","offset","arrayPush","baseGetAllKeys","getAllKeys","arrayEach","subValue","newValue","getMapData","getValue","isFunction","baseIsNative","has","cache","pairs","LARGE_ARRAY_SIZE","isIndex","other","ctorString","isArrayLike","isLength","inherited","isObjectLike","isArrayLikeObject","isArguments","baseTimes","skipIndexes","arrayLikeKeys","baseKeys","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","nativeMax","nativeMin","toNumber","isSymbol","isBinary","wait","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","invokeFunc","time","thisArg","leadingEdge","timerExpired","shouldInvoke","timeSinceLastCall","trailingEdge","remainingWait","debounced","isInvoking","cancel","arrayTag","errorTag","typedArrayTags","freeProcess","nodeUtil","binding","nodeIsTypedArray","isTypedArray","arraySome","predicate","nativeObjectToString","symToStringTag","SetCache","baseGetTag","isOwn","unmasked","getRawTag","baseIsArguments","baseIsEqual","bitmask","equalFunc","objIsArr","othIsArr","objTag","othTag","objIsObj","othIsObj","isSameTag","equalArrays","convert","isPartial","equalByTag","objIsWrapped","othIsWrapped","objUnwrapped","othUnwrapped","objProps","objLength","skipCtor","othValue","compared","objCtor","othCtor","equalObjects","baseIsEqualDeep","arrLength","othLength","seen","arrValue","othIndex","isMasked","resIndex","arrayFilter","baseUnary","isArg","isBuff","isType","SDPUtils","localCName","generateIdentifier","splitLines","blob","trim","splitSections","part","getDescription","sections","getMediaSections","matchPrefix","prefix","parseCandidate","candidate","foundation","protocol","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","writeCandidate","toUpperCase","parseIceOptions","parseRtpMap","parsed","payloadType","clockRate","channels","numChannels","writeRtpMap","codec","pt","preferredPayloadType","parseExtmap","uri","writeExtmap","headerExtension","preferredId","parseFmtp","kv","writeFmtp","parameters","params","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","colon","parseSsrcGroup","getMid","mediaSection","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","rtpmapline","fmtps","writeRtpDescription","kind","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","track","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","sctpPort","fmt","sctpMapLines","writeSctpDescription","sctp","generateSessionId","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","charAt","JitsiConferenceErrors","AUTHENTICATION_REQUIRED","CHAT_ERROR","SETTINGS_ERROR","CONFERENCE_DESTROYED","CONFERENCE_MAX_USERS","CONNECTION_ERROR","CONFERENCE_RESTARTED","NOT_ALLOWED_ERROR","MEMBERS_ONLY_ERROR","CONFERENCE_ACCESS_DENIED","FOCUS_DISCONNECTED","FOCUS_LEFT","GRACEFUL_SHUTDOWN","ICE_FAILED","INCOMPATIBLE_SERVER_VERSIONS","OFFER_ANSWER_FAILED","PASSWORD_NOT_SUPPORTED","PASSWORD_REQUIRED","RESERVATION_ERROR","VIDEOBRIDGE_NOT_AVAILABLE","JitsiConferenceEvents","AUDIO_INPUT_STATE_CHANGE","AUDIO_UNMUTE_PERMISSIONS_CHANGED","AUTH_STATUS_CHANGED","BEFORE_STATISTICS_DISPOSED","CONFERENCE_ERROR","CONFERENCE_FAILED","CONFERENCE_JOIN_IN_PROGRESS","CONFERENCE_JOINED","CONFERENCE_LEFT","CONFERENCE_UNIQUE_ID_SET","CONNECTION_ESTABLISHED","CONNECTION_INTERRUPTED","CONNECTION_RESTORED","DATA_CHANNEL_OPENED","DISPLAY_NAME_CHANGED","CONFERENCE_CREATED_TIMESTAMP","DTMF_SUPPORT_CHANGED","JVB121_STATUS","KICKED","PARTICIPANT_KICKED","LAST_N_ENDPOINTS_CHANGED","LOCK_STATE_CHANGED","SERVER_REGION_CHANGED","_MEDIA_SESSION_STARTED","_MEDIA_SESSION_ACTIVE_CHANGED","MEMBERS_ONLY_CHANGED","MESSAGE_RECEIVED","NO_AUDIO_INPUT","NOISY_MIC","NON_PARTICIPANT_MESSAGE_RECEIVED","PRIVATE_MESSAGE_RECEIVED","PARTICIPANT_CONN_STATUS_CHANGED","PARTCIPANT_FEATURES_CHANGED","PARTICIPANT_PROPERTY_CHANGED","P2P_STATUS","PHONE_NUMBER_CHANGED","PROPERTIES_CHANGED","RECORDER_STATE_CHANGED","VIDEO_SIP_GW_AVAILABILITY_CHANGED","VIDEO_SIP_GW_SESSION_STATE_CHANGED","START_MUTED_POLICY_CHANGED","STARTED_MUTED","SUBJECT_CHANGED","SUSPEND_DETECTED","TALK_WHILE_MUTED","TRACK_ADDED","TRACK_AUDIO_LEVEL_CHANGED","TRACK_MUTE_CHANGED","TRACK_REMOVED","TRACK_UNMUTE_REJECTED","TRANSCRIPTION_STATUS_CHANGED","USER_JOINED","USER_LEFT","USER_ROLE_CHANGED","USER_STATUS_CHANGED","VIDEO_UNMUTE_PERMISSIONS_CHANGED","BOT_TYPE_CHANGED","LOBBY_USER_JOINED","LOBBY_USER_UPDATED","LOBBY_USER_LEFT","AV_MODERATION_APPROVED","AV_MODERATION_REJECTED","AV_MODERATION_CHANGED","AV_MODERATION_PARTICIPANT_APPROVED","AV_MODERATION_PARTICIPANT_REJECTED","FACIAL_EXPRESSION_ADDED","BREAKOUT_ROOMS_MOVE_TO_ROOM","BREAKOUT_ROOMS_UPDATED","CALLSTATS_SCRIPT_URL","JitsiTrackErrors","CONSTRAINT_FAILED","ELECTRON_DESKTOP_PICKER_ERROR","ELECTRON_DESKTOP_PICKER_NOT_FOUND","GENERAL","NOT_FOUND","PERMISSION_DENIED","SCREENSHARING_GENERIC_ERROR","SCREENSHARING_USER_CANCELED","TIMEOUT","TRACK_IS_DISPOSED","TRACK_NO_STREAM_FOUND","UNSUPPORTED_RESOLUTION","TRACK_ERROR_TO_MESSAGE_MAP","JitsiTrackError","devices","gum","constraints","constraintName","constraint","video","failedConstraintName","mandatory","minWidth","minHeight","getResolutionFromFailedConstraint","AnalyticsEvents","TYPE_OPERATIONAL","TYPE_PAGE","TYPE_TRACK","TYPE_UI","ACTION_JINGLE_RESTART","ACTION_JINGLE_SA_TIMEOUT","ACTION_JINGLE_SI_RECEIVED","ACTION_JINGLE_SI_TIMEOUT","ACTION_JINGLE_TERMINATE","ACTION_JINGLE_TR_RECEIVED","ACTION_JINGLE_TR_SUCCESS","ACTION_P2P_DECLINED","ACTION_P2P_ESTABLISHED","ACTION_P2P_FAILED","ACTION_P2P_SWITCH_TO_JVB","AVAILABLE_DEVICE","CONNECTION_DISCONNECTED","FEEDBACK","ICE_DURATION","ICE_ESTABLISHMENT_DURATION_DIFF","ICE_STATE_CHANGED","NO_BYTES_SENT","TRACK_UNMUTED","createConferenceEvent","attributes","createConnectionStageReachedEvent","stage","actionSubject","createGetUserMediaEvent","createJingleEvent","createP2PEvent","createRemotelyMutedEvent","mediaType","createRtpStatsEvent","Events","AUDIO_LEVEL","BEFORE_DISPOSED","BYTE_SENT_STATS","CONNECTION_STATS","LONG_TASKS_STATS","CHROME","OPERA","FIREFOX","INTERNET_EXPLORER","SAFARI","NWJS","ELECTRON","REACT_NATIVE","UNKNOWN","bowserNameToJitsiName","_detectElectron","userAgent","navigator","JitsiMeetElectron","_detectNWJS","_detectReactNative","product","DummyLocalStorage","_storage","getItem","keyName","setItem","keyValue","removeItem","serialize","jitsiLocalStorage","super","localStorage","_localStorageDisabled","ignore","isLocalStorageDisabled","dontEmitChangedEvent","localStorageContent","browserInfo","_bowser","Bowser","detectedBrowserInfo","bowser","detectors","_detectChromiumBased","_name","_version","getName","isChrome","isOpera","isFirefox","isIExplorer","isSafari","isNWJS","isElectron","isReactNative","getVersion","_checkCondition","checkTree","isVersionGreaterThan","isVersionLessThan","isVersionEqualTo","doesVideoMuteByStreamRemove","isChromiumBased","isWebKitBased","isIosBrowser","maxTouchPoints","Boolean","mediaDevices","getUserMedia","RTCRtpTransceiver","isTwa","matchMedia","isSupported","_getSafariVersion","_getChromiumBasedVersion","isSupportedAndroidBrowser","isSupportedIOSBrowser","_getIOSVersion","isUserInteractionRequiredForUnmute","supportsVideoMuteOnConnInterrupted","supportsBandwidthStatistics","supportsCodecPreferences","RTCRtpReceiver","getCapabilities","supportsDeviceChangeEvent","ondevicechange","supportsLocalCandidateRttStatistics","supportsPerformanceObserver","PerformanceObserver","supportedEntryTypes","supportsReceiverStats","supportsRTTStatistics","supportsVP9","usesSdpMungingForSimulcast","usesAdapter","usesRidsForSimulcast","supportsGetDisplayMedia","getDisplayMedia","supportsEncodedTransform","RTCRtpScriptTransform","supportsInsertableStreams","RTCRtpSender","createEncodedStreams","ReadableStream","postMessage","supportsAudioRed","mimeType","supportsUnifiedPlan","supportsVADDetection","supportsRTX","ua","reset","disposed","analyticsHandlers","permanentProperties","conferenceName","addPermanentProperties","dispose","setAnalyticsHandlers","_setUserProperties","_sendEvent","setUserProperties","properties","setConferenceName","sendEvent","eventName","_verifyRequiredFields","objectType","containerType","containerId","objectId","_maybeCacheEvent","wrtcFuncNames","reportType","_fabrics","CallStats","backend","backendInitialized","atLeastOneFabric","defaultInstance","callStatsInstance","fabrics","hasFabric","debug","_addNewFabric","_emptyReportQueue","csInstance","defaultConfID","confID","defaultPC","peerconnection","report","reportsQueue","errorData","_reportError","pc","eventData","sendFabricEvent","associateMstWithUserID","callStatsId","usageLabel","cs","_error","reportError","theBackend","tryCatchMethods","originalMethod","theArguments","debugMethods","originalReportError","exception","CallStatsBackend","callstats","_traceAndCatchBackendCalls","userID","aliasName","userName","callStatsID","callStatsSecret","configParams","applicationName","applicationVersion","siteID","initialize","_initCallback","getWiFiStatsMethod","attachWifiStatsHandler","then","addresses","catch","devicesData","_reportEvent","conferenceID","overall","comment","sendUserFeedback","status","mute","tpc","remoteUserID","fabricAttributes","remoteEndpointType","isP2P","endpointType","peer","server","addNewFabric","fabricUsage","multiplex","_addNewFabricCallback","success","associateStreamWithVideoTag","isLocal","streamEndpointId","sendDominantSpeakerEvent","sendTerminateEvent","fabricEvent","fabricTerminated","delete","sendIceConnectionFailedEvent","sendCreateOfferFailed","sendCreateAnswerFailed","sendResumeOrHoldEvent","isResume","sendScreenSharingEvent","sendSetLocalDescFailed","sendSetRemoteDescFailed","sendAddIceCandidateFailed","AudioContext","webkitAudioContext","LocalStatsCollector","interval","intervalId","intervalMilis","audioLevel","safeCounterIncrement","number","nextValue","calculateAverage","valueArray","filterPositiveValues","suspend","isLocalStatsSupported","analyser","createAnalyser","smoothingTimeConstant","fftSize","createMediaStreamSource","connect","setInterval","frequencyBinCount","getByteTimeDomainData","samples","maxVolume","parseFloat","toFixed","timeDomainDataToAudioLevel","newLevel","lastLevel","diff","animateLevel","clearInterval","RunningAverage","average","addNext","getAverage","PerformanceObserverStats","statsInterval","eventEmitter","longTasks","maxDuration","performanceStatsInterval","stats","getLongTasksStats","avgRatePerMinute","maxDurationMs","startObserver","longTaskEventHandler","getEntries","observer","observe","buffered","startTime","longTasksIntervalId","_lastTimeStamp","StatisticsEvents","stopObserver","disconnect","MediaType","calculatePacketLoss","lostPackets","totalPackets","SsrcStats","loss","bitrate","download","upload","resolution","framerate","ConferenceStats","packetLoss","StatsCollector","audioLevelsInterval","baselineAudioLevelsReport","currentAudioLevelsReport","currentStatsReport","previousStatsReport","audioLevelReportHistory","audioLevelsIntervalId","conferenceStats","audioLevelsIntervalMilis","speakerList","statsIntervalId","statsIntervalMilis","ssrc2stats","setLoss","setResolution","addBitrate","resetBitrate","setFramerate","setCodec","setSpeakerList","startAudioLevelStats","audioLevels","getAudioLevels","getStats","processAudioLevelReport","processStats","processStatsReport","_processAndEmitReport","bitrateDownload","bitrateUpload","resolutions","framerates","audioCodec","videoCodec","audioBitrateDownload","audioBitrateUpload","videoBitrateDownload","videoBitrateUpload","ssrcStats","isDownloadStream","packetsTotal","packetsLost","getTrackBySSRC","isAudioTrack","participantId","getParticipantId","userResolutions","userFramerates","codecDesc","userCodecs","audio","avgAudioLevels","localAvgAudioLevels","avgAudioLevel","sum","currentValue","getNonNegativeValue","_calculateBitrate","before","fieldName","bytesNow","bytesBefore","bytesProcessed","timeMs","bitrateKbps","byteSentStats","nominated","availableIncomingBitrate","availableOutgoingBitrate","remoteUsedCandidate","remoteCandidateId","localUsedCandidate","localCandidateId","localip","conferenceStatsTransport","p2p","localCandidateType","candidateType","remoteCandidateType","networkType","rtt","currentRoundTripTime","packetsNow","packetsBefore","packetsDiff","packetsLostNow","packetsLostBefore","packetsLostDiff","frameHeight","frameWidth","frameRate","framesPerSecond","bytesSent","codecId","codecShortType","VIDEO","remoteSource","localVideoTracks","getLocalTracks","getLocalSSRC","framesSent","numberOfActiveStreams","getActiveSimulcastStreams","trackIdentifier","getSsrcByTrackId","AUDIO","_instances","isCallstatsLoaded","_initCallStatsBackend","isBackendInitialized","initBackend","Statistics","xmpp","rtpStatsMap","callStatsIntegrationEnabled","enableCallStats","disableThirdPartyRequests","callStatsApplicationLogsDisabled","customScriptUrl","loadCallStatsAPI","callsStatsInstances","instances","audioLevelsEnabled","disableAudioLevels","pcStatsInterval","longTasksStatsInterval","analytics","startRemoteStats","stopRemoteStats","rtpStats","RTPStats","localStats","startLocalStats","LocalStats","addAudioLevelListener","removeAudioLevelListener","addBeforeDisposedListener","removeBeforeDisposedListener","addConnectionStatsListener","removeConnectionStatsListener","addByteSentStatsListener","removeByteSentStatsListener","addLongTasksStatsListener","attachLongTasksStats","conference","performanceObserverStats","removeLongTasksStatsListener","from","callStats","stopCallStats","tpcId","_stopRemoteStats","stopLocalStats","startCallStats","newInstance","_getAllCallStatsInstances","csInstances","statistics","isCallstatsEnabled","sendConnectionResumeOrHoldEvent","instance","sendMuteEvent","muted","roomJid","sendActiveDeviceListEvent","globalSet","sendGetUserMediaFailed","formatJitsiTrackErrorForCallStats","sendLog","globalSubSet","csPerStats","sendApplicationLog","sendFeedback","rating","reportGlobalError","sendAnalyticsAndLog","eventToLog","sendAnalytics","JitsiConferenceEventManager","xmppListeners","p2pJingleSession","jvbJingleSession","isMuted","getType","setupChatRoomListeners","chatRoom","room","chatRoomForwarder","jingleSession","rtc","closeBridgeChannel","features","participant","getParticipantById","Strophe","setFeatures","offerIq","_setBridgeChannel","actor","mutedByFocusActor","setAudioMute","isMutedByFocus","mutedVideoByFocusActor","setVideoMute","isVideoMutedByFocus","_onMucJoined","isJvbConnectionInterrupted","connectionTimes","_onConferenceRestarted","connection","emuc","doLeave","roomjid","_onIceConnectionFailed","jid","resource","getStatus","setTerminator","setInitiator","setParticipantPropertyListener","setProperty","onMemberKicked","onSuspendDetected","onMemberJoined","_onMemberBotTypeChanged","onMemberLeft","onDisplayNameChanged","onLocalRoleChanged","isModerator","recorderSession","logObject","getError","onUserRoleChanged","AuthenticationEvents","authEnabled","authIdentity","txt","myJid","_status","addPresenceListener","myUserId","startAudioMuted","startVideoMuted","updated","startMutedPolicy","setupRTCListeners","onRemoteTrackAdded","onRemoteTrackRemoved","dominant","previous","lastDominantSpeaker","performance","isVideoTrack","videoType","VideoType","removeXMPPListeners","setupXMPPListeners","_addConferenceXMPPListener","onIncomingCall","onCallAccepted","onTransportInfo","onCallEnded","audioMuted","videoMuted","ignoreStartMuted","createdTimestamp","actorJid","actorParticipant","getParticipants","getJid","enabled","setupStatisticsListeners","setAudioLevel","startSilent","onByteSentStatsReceived","LOCAL_TRACK_STOPPED","TRACK_AUDIO_OUTPUT_CHANGED","TRACK_VIDEOTYPE_CHANGED","NO_DATA_FROM_SOURCE","TRACK_STREAMING_STATUS_CHANGED","ParticipantConnectionStatus","ACTIVE","INTERRUPTED","RESTORING","ParticipantConnectionStatusHandler","isConnectionActiveByJvb","isInLastN","isRestoringTimedout","isVideoMuted","isVideoTrackFrozen","trackTimers","connStatusFromJvb","outOfLastNTimeout","p2pRtcMuteTimeout","rtcMuteTimeout","rtcMutedTimestamp","enteredLastNTimestamp","restoringTimers","connectionStatusMap","_getVideoFrozenTimeout","isP2PActive","_onEndpointConnStatusChanged","onEndpointConnStatusChanged","_onP2PStatus","refreshConnectionStatusForAll","_onUserLeft","onUserLeft","_onTrackRtcMuted","onTrackRtcMuted","_onTrackRtcUnmuted","onTrackRtcUnmuted","_onRemoteTrackAdded","_onRemoteTrackRemoved","_onSignallingMuteChanged","onSignallingMuteChanged","_onTrackVideoTypeChanged","onTrackVideoTypeChanged","_onLastNChanged","_onLastNValueChanged","participantIds","clearRtcMutedTimestamp","endpointId","isActive","figureOutConnectionStatus","_changeConnectionStatus","newStatus","getConnectionStatus","getId","_setConnectionStatus","remoteTrack","JitsiTrackEvents","hasAnyVideoRTCMuted","hasAnyVideoTrackWebRTCMuted","timeout","participants","inP2PMode","isRestoringTimedOut","_isRestoringTimedout","audioOnlyMode","getLastN","isConnActiveByJvb","newState","_getNewStateForP2PMode","_getNewStateForJvbMode","_clearRestoringTimer","oldConnectionStatus","connectionStatus","nowMs","maybeSendParticipantConnectionStatusEvent","startedMs","videoTracks","getTracksByMediaType","participantConnectionStatus","createParticipantConnectionStatusEvent","leavingLastN","enteringLastN","rTimer","JitsiParticipant","hidden","statsID","identity","isReplacing","isReplaced","_jid","_id","_conference","_displayName","_supportsDTMF","_tracks","_role","_hidden","_statsID","_connectionStatus","_properties","_identity","_isReplacing","_isReplaced","_features","getConference","getProperty","getTracks","jitsiTrack","isWebRTCTrackMuted","oldValue","getStatsID","isHidden","isHiddenFromRecorder","user","isAudioMuted","_isMediaTypeMuted","getRole","setRole","newRole","setIsReplacing","newIsReplacing","setIsReplaced","newIsReplaced","supportsDTMF","getFeatures","hasFeature","feature","newFeatures","getBotType","_botType","setBotType","newBotType","JitsiConnectionEvents","CONNECTION_FAILED","WRONG_STATE","DISPLAY_NAME_REQUIRED","JitsiConnectionErrors","CONNECTION_DROPPED_ERROR","OTHER_ERROR","SERVER_ERROR","Deferred","promise","clearRejectTimeout","_timeout","setRejectTimeout","ms","Listenable","kJitsiE2EE","E2EEcontext","sharedKey","baseUrl","ljm","querySelector","idx","workerUrl","workerBlob","Blob","URL","createObjectURL","_worker","Worker","operation","cleanup","cleanupAll","handleReceiver","receiverStreams","readableStream","readable","writableStream","handleSender","sender","senderStreams","setKey","keyIndex","KeyHandler","e2eeCtx","E2EEContext","_enabling","_onMediaSessionStarted","_onLocalTrackAdded","_setupReceiverE2EEForTrack","_trackMuteChanged","isEnabled","_setEnabled","setLocalParticipantProperty","_restartMediaSessions","setEncryptionKey","getMediaSessions","_setupSenderE2EEForTrack","localTracks","findReceiverForTrack","findSenderForTrack","ExternallyManagedKeyHandler","keyInfo","encryptionKey","v4","OLM_MESSAGE_TYPE","OLM_MESSAGE_TYPES","kOlmData","OlmAdapterEvents","OLM_ID_KEY_READY","PARTICIPANT_E2EE_CHANNEL_READY","PARTICIPANT_KEY_UPDATED","OlmAdapter","_conf","_init","_key","_keyIndex","_reqs","_sessionInitialization","_bootstrapOlm","_onEndpointMessageReceived","_onConferenceLeft","_onParticipantLeft","_onParticipantPropertyChanged","promises","localParticipantId","FEATURE_E2EE","_sendSessionInit","allSettled","Olm","pId","olmData","_getParticipantOlmData","uuid","uuidv4","JITSI_MEET_MUC_TYPE","olm","ciphertext","_encryptKeyInfo","_sendMessage","updateCurrentKey","clearParticipantSession","free","clearAllParticipantsSessions","_olmAccount","Account","idKeys","identity_keys","_idKey","curve25519","get_library_version","_onIdKeyReady","idKey","_onParticipantE2EEChannelReady","base64js","encrypt","_sendError","Session","create_outbound","otKey","ack","pendingSessionUuid","create_inbound","body","remove_one_time_keys","decrypt","json","safeJsonParse","lastKey","isEqual","isE2EEEnabled","sendMessage","generate_one_time_keys","otKeys","one_time_keys","mark_keys_as_published","ManagedKeyHandler","_conferenceJoined","_olmAdapter","_rotateKey","debounce","_rotateKeyImpl","_ratchetKey","_ratchetKeyImpl","_onParticipantKeyUpdated","_onParticipantJoined","initSessions","_generateKey","updateKey","material","keyBytes","subtle","importKey","newKey","textEncoder","TextEncoder","deriveBits","salt","encode","ratchet","getRandomValues","E2EEncryption","e2ee","_externallyManaged","externallyManagedKey","_keyHandler","testing","disableE2EE","enableEncodedTransformSupport","setEnabled","IDENTITY_PROPERTIES","IDENTITY_PROPERTIES_FOR_COMPARE","compareIdentities","res","parseDiscoInfo","identities","$","_","getAttribute","category","Caps","disco","rooms","externalFeatures","_addChatRoom","_removeChatRoom","addFeature","submit","external","_generateVersion","_updateRoomWithExternalFeatures","removeFeature","sendPresence","removeFromPresence","children","addOrReplaceInPresence","getFeaturesAndIdentities","_getDiscoInfo","response","_fixChatRoomPresenceMap","xmlns","ver","_notifyVersionChanged","sortedIdentities","accumulatedValue","sortedFeatures","b64_sha1","generateSha","_identities","NETWORK_INFO_EVENT","_current","isOnline","updateNetworkInfo","ResumeTask","stropheConnection","_stropheConn","_resumeRetryN","_retryDelay","retryDelay","schedule","_cancelResume","_networkOnlineListener","NetworkInfo","_scheduleResume","_resumeTimeout","minDelay","base","getJitterDelay","_resumeConnection","streamManagement","resumeToken","getResumeToken","service","pattern","oldToken","LastRequestTracker","_lastSuccess","_lastFailedMessage","startTracking","xmppConnection","originalRawInput","rawInput","rawMessage","includes","connected","getLastFailedMessage","getTimeSinceLastSuccess","getConnectionPluginDefinition","ConnectionPluginListenable","PingConnectionPlugin","ConnectionPlugin","getTimeSinceLastServerResponse","onPingThresholdExceeded","pingOptions","failedPings","_onPingThresholdExceeded","_getTimeSinceLastServerResponse","pingInterval","pingTimeout","pingThreshold","threshold","pingTimestampsToKeep","pingExecIntervals","ping","_addPingExecutionTimestamp","iq","$iq","to","sendIQ2","startInterval","remoteJid","_lastServerCheck","errmsg","stopInterval","getTime","getPingSuspendTime","pingIntervals","maxInterval","previousTS","currentInterval","XmppConnection","CONN_STATUS_CHANGED","CONN_SHARD_CHANGED","Status","enableWebsocketResume","websocketKeepAlive","websocketKeepAliveUrl","serviceUrl","shard","xmppPing","_options","_usesWebsocket","startsWith","maxRetries","_rawInputTracker","LastSuccessTracker","_resumeTask","_deferredIQs","addConnectionPlugin","_onPingErrorThresholdExceeded","_oneSuccessfulConnect","websocket","_proto","socket","isUsingWebSocket","WebSocket","OPEN","disconnecting","domain","lastResponseHeaders","pingDomain","_maybeStartWSKeepAlive","plugin","attach","sid","rid","_stropheConnectionCb","pass","targetCallback","blockCallback","_maybeEnableStreamResume","_keepAliveAndCheckShard","_processDeferredIQs","_tryResumingConnection","_wsKeepAlive","_clearDeferredIQs","deferred","closeWebsocket","_closeSocket","_onClose","enable","intervalWithJitter","fetch","responseShard","headers","timeLeft","sendIQ","send","stanza","elem","errback","sendUnavailableBeacon","sendBeacon","_changeConnectStatus","_buildBody","attrs","pres","$pres","cnode","tree","_abortAllRequests","_doDisconnect","JitsiTranscriptionStatus","ON","OFF","AVModeration","_xmpp","_mainRoom","_moderationEnabledByType","_whitelistAudio","_whitelistVideo","_onMessage","avModerationComponentAddress","$msg","up","approve","jidToWhitelist","jidToBlacklist","removed","approved","whitelists","newWhitelists","oldList","newList","FEATURE_KEY","BREAKOUT_ROOM_ACTIONS","ADD","REMOVE","MOVE_TO_ROOM","BREAKOUT_ROOM_EVENTS","BreakoutRooms","_handleMessages","_rooms","createBreakoutRoom","subject","removeBreakoutRoom","breakoutRoomJid","sendParticipantToRoom","participantJid","getComponentAddress","breakoutRoomsComponentAddress","_setIsBreakoutRoom","isBreakoutRoom","_isBreakoutRoom","_setMainRoomJid","_mainRoomJid","getMainRoomJid","EMAIL_COMMAND","Lobby","mainRoom","maybeJoinLobbyRoom","_maybeJoinLobbyRoom","lobbyRoomJid","lobbySupported","setMembersOnly","disable","lobbyRoom","membersOnlyEnabled","sendPrivateMessage","getLocalId","myroomjid","addMessageListener","removeMessageHandler","leave","setLobbyRoomJid","joined","email","customDomain","createRoom","disableDiscoInfo","disableFocus","enableLobby","nick","isHiddenDomain","botType","members","getBreakoutRooms","avatar","clean","isSelfPresence","invitePassword","denyAccess","kick","approveAccess","mainRoomJid","memberRoomJid","msgToSend","_callStatsUserName","_machineId","externalStorage","callStatsUserName","UsernameGenerator","generateCallStatsUserName","machineId","amDid","jitsiMeetId","_p8","generateJitsiMeetId","createExpBackoffTimer","step","Moderator","Settings","xmppService","getNextTimeout","getNextErrorTimeout","externalAuthEnabled","sipGatewayEnabled","attachEvent","isExternalAuthEnabled","isSipGatewayEnabled","onMucMemberLeft","setFocusUserJid","focusJid","focusUserJid","getFocusUserJid","getFocusComponent","focusComponent","hosts","createConferenceIq","machineUID","disableRtx","audioPacketDelay","startBitrate","minBitrate","rtcstatsEnabled","callstatsDisabled","parseSessionId","resultIq","attr","parseConfigOptions","authenticationEnabled","allocateConferenceFocus","_allocateConferenceFocusSuccess","_allocateConferenceFocusError","invalidSession","reservationErr","errorCode","errorTextNode","errorMsg","anonymousdomain","waitMs","retrySec","authenticate","errorIq","prop","getLoginUrl","urlCallback","failureCallback","_getLoginUrl","popup","urlCb","failureCb","decodeURIComponent","getPopupLoginUrl","logout","logoutUrl","packet2JSON","xmlElement","nodes","child","json2packet","packet","filterNodeFromPresenceJSON","nodeName","MEMBERS_AFFILIATIONS","ChatRoom","XMPP","replaceParticipant","presMap","presHandlers","_removeConnListeners","inProgressEmitted","focusMucJid","noBridgeAvailable","moderator","lobby","avModeration","breakoutRooms","initPresenceMap","lastPresences","phoneNumber","phonePin","participantPropertyListener","locked","transcriptionStatus","xns","statsId","presenceUpdateTime","onConnStatusChanged","fromJoin","billingId","presenceSyncTime","discoRoomInfo","getInfo","meetingIdValEl","setMeetingId","membersOnly","lobbyRoomField","isBreakoutField","breakoutMainRoomField","meetingId","createNonAnonymousRoom","getForm","form","formSubmit","CONNECTED","onPresence","member","statusEl","textContent","hasStatusUpdate","hasVersionUpdate","xElement","getElementsByTagNameNS","mucUserItem","isReplaceParticipant","affiliation","isFocus","hiddenDomain","fromHiddenDomain","presence","xEl","remove","extractIdentityInformation","userInfo","tags","hiddenFromRecorderFeatureEnabled","groupInfo","_extractFeatures","_initFocus","memberOfThis","displayJids","restartByTerminateSupported","supportsRestartByTerminate","att","phone","pin","processNode","var","focusFeatures","tagHandlers","elementName","setSubject","onParticipantLeft","skipEvents","onPresenceUnavailable","destroySelect","reasonSelect","isKick","membersKeys","actorSelect","actorNick","onMessage","settingsErrorMsg","subjectText","stamp","dateParts","invite","passwordSelect","jsonMessage","parsedJson","tryParseJSONAndVerify","onPresenceError","lobbyRoomNode","lobbyRoomOldNode","setAffiliation","grantIQ","kickIQ","lockRoom","onSuccess","onError","onNotSupported","formsubmit","formToSubmit","addToPresence","matchingNodes","getFromPresence","removePresenceListener","handlerIdx","mucJid","getMemberRole","peerJid","addAudioInfoToPresence","audioMutedTagName","addVideoInfoToPresence","videoMutedTagName","getMediaPresenceInfo","mutedNode","codecTypeNode","videoTypeNode","codecType","getLastPresence","mucNick","isSIPCallingSupported","dial","rayo","hangup","getLobby","getAVModeration","getPhoneNumber","getPhonePin","getMeetingId","muteParticipant","iqToFocus","onMute","onMuteVideo","onMucLeft","doReject","MucConnectionPlugin","isRoomCreated","_sourceNameSignaling","sourceNameSignaling","_sendMultipleVideoStreams","sendMultipleVideoStreams","isMultiStreamSupportEnabled","isSourceNameSignalingEnabled","_createSourceExtension","owner","sourceCompactJson","$build","FeatureFlags","_createSsrcGroupExtension","ssrcGroupCompactJson","_getOrCreateRtpDescription","jingle","expandSourcesFromJson","jsonMessageXml","audioRtpDescription","videoRtpDescription","ownerSources","videoSources","videoSsrcGroups","audioSources","audioSsrcGroups","ScreenObtainer","obtainStream","_createObtainStreamMethod","onFailure","JitsiMeetNW","obtainDesktopStream","jitsiError","obtainScreenOnElectron","obtainScreenFromGetDisplayMediaRN","obtainScreenFromGetDisplayMedia","_getAudioConstraints","audioQuality","stereo","autoGainControl","channelCount","echoCancellation","noiseSuppression","JitsiMeetScreenObtainer","openDesktopPicker","desktopSharingFrameRate","desktopSharingSources","streamId","streamType","screenShareAudio","audioConstraints","optionalConstraints","optional","chromeMediaSource","chromeMediaSourceId","minFrameRate","maxFrameRate","maxWidth","screen","maxHeight","sourceId","sourceType","cursor","errorDetails","errorName","errorStack","setDesktopSharingFrameRate","maxFps","SDPUtil","filterSpecialChars","iceparams","mediadesc","sessiondesc","findLine","parseICEUfrag","parseICEPwd","buildICEUfrag","frag","buildICEPwd","parseMID","parseMSIDAttribute","ssrcLines","msidLine","buildMLine","parseRTPMap","clockrate","parseSCTPMap","parseSCTPPort","buildRTPMap","parseCrypto","fingerprint","parseICECandidate","elems","network","buildICECandidate","cand","hasOwnAttribute","parseSSRC","parseSourceNameLine","sourceNameLine","ssrcSdpLine","parseRTCPFB","haystack","needle","findLines","needles","candidateToJingle","candidateFromJingle","parsePrimaryVideoSsrc","videoMLine","generateSsrc","ssrcLine","parseGroupSsrcs","ssrcGroup","getMedia","getUfrag","ufragLines","preferCodec","codecName","matchingPayloadTypes","payloadTypes","payloadIndex","stripCodec","highProfile","h264Pts","removePts","stripH264HighCodec","CodecMimeType","rtxApts","rtxPts","keepPts","MediaDirection","rtcpFb","SDP","mediaI","raw","arrayEquals","array1","array2","equals","SDPDiffer","mySDP","otherSDP","failICE","removeTcpCandidates","removeUdpCandidates","addMlineForNewLocalSource","clonedeep","groups","mids","getMediaSsrcMap","mediaSSRCs","mediaindex","linessrc","containsSSRC","medias","toJingle","thecreator","assrcline","creator","amidline","rtpmap","afmtpline","fmtpParameters","rtcpFbToJingle","availableSsrc","ssrcParameters","sourceName","ridLines","rids","ridLine","ridInfo","extmapLines","extmap","senders","transportToJingle","sctpport","sctpmap","sctpAttrs","streams","setupLine","setup","iceParameters","payloadtype","feedback","rtcpFbFromJingle","feedbackElementTrrInt","fromJingle","contents","jingle2media","setAttribute","__","hdrExt","userSources","nonUserSources","isUserSource","sourceStr","getNewMedia","myMedias","othersMedias","newMedia","othersMediaIdx","myMedia","othersMedia","otherSsrcGroup","matched","mySsrcGroup","modify","sdpMediaSsrcs","modified","ssrcNum","mediaSsrc","AsyncQueue","_queue","_processQueueTasks","_stopped","finishedCallback","shutdown","JingleSessionState","PENDING","ENDED","JingleSession","localJid","mediaConstraints","pcConfig","isInitiator","usedrip","dripContainer","_signalingLayer","initiatorJid","responderJid","signalingLayer","doInitialize","addIceCandidates","getState","addSources","removeSources","terminate","failure","acceptOffer","_getInitiatorJid","MediaSessionEvents","IQ_TIMEOUT","JingleSessionPC","jingleContents","videoContents","maxFrameHeightSel","_bridgeSessionId","_cachedOldLocalSdp","_cachedNewLocalSdp","_iceCheckingStartedTimestamp","_gatheringStartedTimestamp","localRecvMaxFrameHeight","_localVideoActive","_remoteVideoActive","_gatheringReported","lasticecandidate","closed","remoteRecvMaxFrameHeight","modificationQueue","wasConnected","establishmentDuration","_xmppListeners","onXmppStatusChanged","_removeSenderVideoConstraintsChangeListener","_assertNotEnded","isReconnect","wasstable","webrtcIceUdpDisable","webrtcIceTcpDisable","pcOptions","gatherStats","maxstats","capScreenshareBitrate","enableInsertableStreams","videoQuality","forceTurnRelay","enableUnifiedOnChrome","disableSimulcast","abtestSuspendVideo","_abtestSuspendVideoEnabled","preferH264","disableH264","preferredCodec","createPeerConnection","onicecandidate","ev","phase","initiator","sendIceCandidate","onsignalingstatechange","signalingState","connectionState","oniceconnectionstatechange","isStable","iceConnectionState","reconnect","usesTerminateForRestart","enableIceRestart","iceStarted","onconnectionstatechange","icestate","onnegotiationneeded","remoteDescription","workFunction","oldSdp","localDescription","_renegotiate","configureSenderVideoEncodings","newSdp","notifyMySSRCUpdate","getRemoteRecvMaxFrameHeight","localSDP","ice","sdpMLineIndex","jcand","errorMesssage","sendIceCandidates","cands","sdpMid","fingerprintLine","required","newJingleErrorHandler","sendIceFailedNotification","sessionInfo","iceCandidates","rtcCandidate","RTCIceCandidate","iceCandidate","addIceCandidate","outerHTML","readSsrcInfo","ssrcElement","setSSRCOwner","setTrackSourceName","i3","ssrcInfoElement","jidOrEndpointId","generateRecvonlySsrc","getConfiguredVideoCodec","jingleOffer","setOfferAnswerCycle","sendSessionAccept","addTracks","localTrack","addTrack","createOffer","offerSdp","setLocalDescription","sendSessionInitiate","setAnswer","jingleAnswer","remoteSdp","_responderRenegotiate","jingleOfferAnswerIq","newRemoteSdp","_processNewJingleOfferIq","oldLocalSdp","bridgeSessionId","sendContentModify","newLocalSdp","setVideoCodecs","preferred","disabled","current","replaceTransport","jingleOfferElem","enableForcedReload","sendTransportAccept","originalOffer","clone","newFingerprint","accept","responder","maxFrameHeight","sessionModify","setReceiverVideoConstraint","transportAccept","medialines","sendTransportReject","transportReject","setSenderVideoConstraint","jitsiLocalTrack","getLocalVideoTracks","getSourceName","getLocalVideoTrack","setSenderVideoConstraints","sendSessionTerminate","sessionTerminate","reasonDescription","restart","requestRestart","onTerminated","reasonCondition","reasonText","close","_parseSsrcInfoFromSourceAdd","sourceAddElem","currentRemoteSdp","addSsrcInfo","i1","i2","addRemoteStream","_addOrRemoveRemoteStream","removeRemoteStream","removeRemoteStreamsOnLeave","finishCallback","removeSsrcInfo","getRemoteSourceInfoByParticipant","_processRemoteRemoveSource","newLocalSDP","isAdd","logPrefix","errMsg","addOrRemoveSsrcInfo","_parseSsrcInfoFromSourceRemove","_processRemoteAddSource","findIndex","desiredDirection","getDesiredMediaDirection","optionalRemoteSdp","_initiatorRenegotiate","setRemoteDescription","createAnswer","answer","offer","replaceTrack","oldTrack","newTrack","clearRecvonlySsrc","shouldRenegotiate","sourceRemoveElem","_verifyNoSSRCChanged","operationName","oldSDP","currentLocalSDP","sdpDiff","addedMedia","removedMedia","addTrackAsUnmute","_addRemoveTrackAsMuteUnmute","removeTrackAsMute","isMute","oldLocalSDP","removeTrackMute","addTrackUnmute","setMediaTransferActive","audioActive","videoActive","logAudioStr","logVideoStr","isSessionActive","audioActiveChanged","setAudioTransferActive","pcVideoActiveChanged","setVideoTransferActive","modifyContents","newVideoSenders","parseVideoSenders","newMaxFrameHeight","parseMaxFrameHeight","_modifyRemoteVideoActive","remoteVideoSenders","isRemoteVideoActive","newSDP","getSignaledSourceInfo","sdpDiffer","mediaIndex","signaledSsrcs","removedAnySSRCs","ctx","sourceInfo","request","errResponse","errorElSel","errorReasonSel","errorMsgSel","getIceConnectionState","getConnectionState","abTesting","enableSuspendVideoTest","_parseIceCandidates","parseCandidates","candidateAttrs","JingleConnectionPlugin","iceConfig","sessions","jvbIceConfig","jvb","p2pIceConfig","offerToReceiveAudio","offerToReceiveVideo","onJingle","fromJid","sess","jsonMessages","audioVideoSsrcs","logMessage","endpoint","startMuted","iceUfrag","icePwd","dtlsFingerprint","successTime","newP2PJingleSession","me","getStunAndTurnCredentials","v2Res","onReceiveStunAndTurnCredentials","v1Res","iceservers","dict","urls","credential","temp","useTurnUdp","iceServers","getLog","updateLog","StropheLogger","logIncoming","rawOutput","logOutgoing","RAYO_XMLNS","RayoConnectionPlugin","onRayo","roomPass","req","callResource","lastErrorStatus","resetLastErrorStatusRegExpr","lastErrorStatusRegExpr","FAILURE_REGEX","DEFAULT_STUN_SERVERS","FEATURE_JIGASI","token","disconnectInProgress","authenticatedUser","trace","errStatusCapture","createConnection","bosh","deploymentInfo","details","shard_changed","suspend_time","time_since_last_success","_initStrophePlugins","initFeaturesList","disableBeforeUnloadHandlers","enableOpusRed","enableRemb","enableTcc","enableLipSync","getConnection","connectionHandler","credentials","statusStr","_maybeSendDeploymentInfoStat","_sysMessageHandler","deleteHandler","sendDiscoInfo","_resetState","sendDeploymentInfo","_processDiscoInfoIdentities","anonymousConnectionFailed","connectionFailed","lastErrorMsg","wasIntentionalDisconnect","_getConnectionFailedReasonDetails","lastFailedRawMessage","_parseConnectionFailedMessage","speakerStatsComponentAddress","conferenceDurationComponentAddress","processLobbyFeatures","fr","endsWith","region","_onPrivateMessage","_connect","_addSysHandler","_onSystemMessage","foundIceServers","attaching","configDomain","hostname","onCreateResource","getRoomJid","mucNickname","muc","getJingleLog","getXmppLog","getSessions","disconnectListener","_cleanupXmppConnection","evType","sync","p2pStunServers","stunServers","iceTransportPolicy","headersArr","header","sendFacialExpressionEvent","expression","jsonString","users","created_timestamp","acceptedStatuses","aprops","authenticateAndUpgradeRole","rejectPromise","onLoginSuccessful","roomPassword","canceled","authenticationError","connectionError","CodecSelection","disabledCodec","_getCodecMimeType","jvbCodec","p2pCodec","jvbPreferredCodec","_isCodecSupported","p2pPreferredCodec","_selectPreferredCodec","mediaSession","currentCodec","selectedCodec","enforcePreferredCodec","remoteParticipants","remote","peerMediaInfo","getPeerMediaInfo","peerCodec","getPreferredCodec","BridgeChannel","wsUrl","_channel","_eventEmitter","_mode","_areRetriesEnabled","_closedFromClient","datachannel","createDataChannel","_handleChannel","_wsUrl","_initWebSocket","ws","_startConnectionRetries","timeoutS","reload","isOpen","_retryTimeout","_stopConnectionRetries","_retryWebSocketConnection","closeEvent","createBridgeChannelClosedEvent","mode","sendEndpointStatsMessage","_send","colibriClass","msgPayload","sendSetLastNMessage","lastN","sendSelectedEndpointsMessage","endpointIds","selectedEndpoints","sendReceiverVideoConstraintMessage","maxFrameHeightPixels","sendNewReceiverVideoConstraintsMessage","sendVideoTypeMessage","sendSourceVideoTypeMessage","channel","onopen","onmessage","dominantSpeakerEndpoint","previousSpeakers","active","lastNEndpoints","forwardedSources","videoConstraints","onclose","jsonObject","DEFAULT_CONSTRAINTS","ideal","audioOutputDeviceId","audioOutputChanged","disableAP","disableAEC","disableNS","disableAGC","featureDetectionAudioEl","isAudioOutputDeviceChangeAvailable","setSinkId","availableDevicesPollTimer","availableDevices","emptyFuncton","updateGrantedPermissions","um","audioTracksReceived","getAudioTracks","videoTracksReceived","getVideoTracks","grantedPermissions","sendDeviceListToAnalytics","deviceList","audioInputDeviceCount","audioOutputDeviceCount","videoInputDeviceCount","videoOutputDeviceCount","device","deviceId","groupId","label","updateKnownDevices","pds","newDevices","mediaDeviceInfoToJSON","facing","compareAvailableMediaDevices","rtcUtils","origAttachMediaStream","RTCPeerConnectionType","RTCPeerConnection","attachMediaStream","getStreamID","getTrackID","element","srcObject","isDeviceChangeAvailable","getAudioOutputDevice","ex","pcConstraints","googScreencastMinBitrate","googCpuOveruseDetection","screenObtainer","isDeviceListAvailable","enumerateDevices","_getUserMedia","umDevices","gumTimeout","timeoutExpired","_getDesktopMedia","_getMissingTracks","requestedDevices","missingDevices","audioDeviceRequested","videoDeviceRequested","obtainAudioAndVideoPermissions","otherOptions","mediaStreamsMetaData","maybeRequestDesktopDevice","desktopSharingSourceDevice","matchingDevice","maybeRequestCaptureDevices","requestedCaptureDevices","Resolutions","cameraDeviceId","facingMode","CameraFacingMode","micDeviceId","getConstraints","desktopStream","desktopAudioTracks","desktopAudioStream","MediaStream","desktopVideoTracks","desktopVideoStream","avStream","audioTracks","audioStream","effects","videoStream","stopMediaStream","deviceType","mediaStream","release","isDesktopSharingEnabled","setAudioOutputDevice","getCurrentlyAvailableMediaDevices","arePermissionsGrantedForAvailableDevices","getEventDataForActiveDevice","deviceData","setSuspendVideo","googSuspendBelowMinBitrate","trackHandler2Prop","JitsiTrack","streamInactiveHandler","trackMediaType","containers","_streamInactiveHandler","_setStream","_addMediaStreamInactiveHandler","onended","oninactive","_setHandler","_unregisterHandlers","videoTrack","getVideoType","isLocalAudioTrack","getOriginalStream","getStreamId","getTrack","getTrackLabel","getTrackId","getUsageLabel","_maybeFireTrackAttached","container","_onTrackAttach","RTCUtils","_attachTTFMTracker","detach","_onTrackDetach","isScreenSharing","newAudioLevel","getMSID","trackId","setAudioOutput","JitsiLocalTrack","rtcId","_setEffectInProgress","effect","_startStreamEffect","displaySurface","getSettings","metadata","maxEnabledResolution","_constraints","_prevSetMuted","_facingMode","_trackEnded","_hasSentData","_testDataSent","_realDeviceId","_sourceName","_trackMutedTS","_onDeviceListWillChange","oldRealDeviceId","_setRealDeviceIdFromDeviceList","_onAudioOutputDeviceChanged","_initNoDataFromSourceHandlers","_addStreamToConferenceAsUnmute","_addLocalTrackAsUnmute","_fireNoDataFromSourceEvent","isReceivingData","createNoDataFromSourceEvent","_isNoDataFromSourceEventsEnabled","_queueSetMuted","setMuted","_setMuted","_removeStreamFromConferenceAsMute","successCallback","_removeLocalTrackAsMute","_sendMuteStatus","_setTrackMuteStatus","logMuteInfo","_streamEffect","_stopStreamEffect","stopStream","streamOptions","getDeviceId","getCameraFacingMode","streamsInfo","PRESENTER","streamInfo","cont","_sendBridgeVideoTypeMessage","storedMSID","_originalStream","startEffect","stopEffect","_switchCamera","_switchStreamEffect","setEffect","removeTrack","trackSettings","getDuration","isEnded","_stopStreamInProgress","_effectEnabled","setConference","setSourceName","unmute","found","PLAN_B_MIDS","findSimGroup","grp","findFidGroup","addSimGroupSources","sourceGroups","sourceList","findSourcebyId","relatedFidGroup","relatedSsrc","addSourcesToMline","otherSsrc","simGroup2","checkIfMlineForSsrcExists","mlines","Interop","toPlanB","sessionMedia","bLine","bundle","msidSemantic","semantic","resStr","toUnifiedPlan","currentDesc","iceRestart","newDesc","oldDesc","newMLine","oldMLine","checkForIceRestart","newIceUfrag","newIcePwd","newMline","mLineForData","ssrc2group","createSourceGroupMap","bundleOnly","existingMline","resultSdp","SignalingEvents","PEER_MUTED_CHANGED","PEER_VIDEO_TYPE_CHANGED","getSourceNameForJitsiTrack","trackIdx","getMediaTypeFromSourceName","firstLetterOfMediaTypeIdx","firstLetterOfMediaType","SignalingLayer","getSSRCOwner","getPeerSourceInfo","getTrackSourceName","parsePrimarySSRC","parseSecondarySSRC","_getSSRCCount","MLineWrap","getSSRCAttrValue","ssrcNumber","attrName","ssrcObj","removeSSRC","addSSRCAttribute","findGroup","findGroups","findGroupByPrimarySSRC","primarySSRC","findSSRCByMSID","getSSRCCount","containsAnySSRCGroups","getPrimaryVideoSsrc","fecGroup","getRtxSSRC","getSSRCs","getPrimaryVideoSSRCs","videoSSRCs","ssrcGroupInfo","dumpSSRCGroups","removeGroupsWithSSRC","removeGroupsBySemantics","replaceSSRC","oldSSRC","newSSRC","addSSRCGroup","SdpTransformWrap","rawSDP","parsedSDP","selectMedia","selectedMLines","toRawSDP","LocalSdpMunger","localEndpointId","_addMutedLocalVideoTracksToSDP","transformer","localVideos","isCamera","isInPeerConnection","isMediaStreamInPc","requiredSSRCs","isSimulcastOn","simulcast","sdpConsistency","cachedPrimarySsrc","primaryCname","rtxModifier","modifyRtxSsrcs2","_generateMsidAttribute","pcId","_transformMediaIdentifiers","sourceToMsidMap","streamAndTrackIDs","mediaDirection","generatedMsid","maybeAddMutedLocalVideoTracksToSDP","transformStreamIdentifiers","sessionDesc","audioMLine","_injectSourceNames","updateAssociatedRtxStream","primarySsrcInfo","rtxSsrc","previousRtxSSRC","RtxModifier","correspondingRtxSsrcs","ssrcMapping","modifyRtxSsrcs","sdpStr","sdpTransformer","videoMLines","primaryVideoSsrcs","correspondingRtxSsrc","stripRtx","fidGroups","SdpConsistency","clearVideoSsrcCache","injectRecvOnly","setPrimarySsrc","hasPrimarySsrcCached","makeVideoPrimarySsrcsConsistent","newPrimarySsrc","SdpSimulcast","_ssrcCache","_fillSsrcsFromCache","cachedSsrcs","newSsrcs","_getSsrcAttribute","_generateNewSsrcsForSimulcast","addAssociatedAttributes","_generateSsrc","mungedSsrcs","mungedSsrcGroups","TrackStreamingStatus","TrackStreamingStatusImpl","isInForwardedSources","restoringTimer","streamingStatusMap","trackTimer","outOfForwardedSourcesTimeout","figureOutStreamingStatus","_onForwardedSourcesChanged","onForwardedSourcesChanged","maybeSendTrackStreamingStatusEvent","_changeStreamingStatus","getTrackStreamingStatus","_setTrackStreamingStatus","isVideoRTCMuted","oldStreamingStatus","streamingStatus","trackStreamingStatus","createTrackStreamingStatusEvent","leavingForwardedSources","enteringForwardedSources","_clearEnteredForwardedSourcesTimestamp","_setEnteredForwardedSourcesTimestamp","enteredForwardedSourcesTimestamp","_getEnteredForwardedSourcesTimestamp","_isCurrentTrack","ttfmTrackerAudioAttached","ttfmTrackerVideoAttached","containerEvents","JitsiRemoteTrack","ownerEndpointId","_trackStreamingStatus","_trackStreamingStatusImpl","_enteredForwardedSourcesTimestamp","_addEventListener","_removeEventListener","hasBeenMuted","_bindTrackHandlers","_containerHandlers","_containerEventHandler","_onTrackMute","_onTrackUnmute","_initTrackStreamingStatus","_disposeTrackStreamingStatus","setMute","getSSRC","_setVideoType","_playCallback","getConnectionTimes","gumStart","gumEnd","gumDuration","ttfm","_getStatus","_p2pConnStatusRtcMuteTimeout","_peerConnStatusRtcMuteTimeout","_peerConnStatusOutOfLastNTimeout","HD_BITRATE","SIM_LAYER_RIDS","TPCUtils","bitrateSettings","maxBitratesVideo","standardBitrates","low","standard","high","videoBitrates","encodingBitrates","localStreamEncodingsConfig","scaleResolutionDownBy","_getStreamEncodings","ensureCorrectOrderOfSsrcs","parsedSdp","reorderedSsrcs","findTransceiver","getTransceivers","insertUnifiedPlanSimulcastReceive","simulcast_03","simulcastLine","transceiverInit","sendEncodings","addTransceiver","calculateEncodingsActiveState","localVideoTrack","newHeight","encoding","isSharingLowFpsScreen","calculateEncodingsBitrates","desktopShareBitrate","desktopBitrate","presenterEnabled","transceiver","currentDirection","trackIndex","setEncodings","getParameters","encodings","setParameters","transceivers","updateEncodingsResolution","TraceablePeerConnection","audioTransferActive","_dtmfSender","_dtmfTonesQueue","videoTransferActive","remoteTracks","_addedStreams","localSSRCs","localUfrag","remoteUfrag","_dtlsTransport","_peerVideoTypeChanged","_peerMutedChanged","safeConstraints","rtcStatsSFUP2P","tpcUtils","statsinterval","_capScreenshareBitrate","_usesUnifiedPlan","_usesTransceiverCodecPreferences","interop","localSdpMunger","getLocalEndpointId","_senderVideoMaxHeight","_senderMaxHeights","what","onTrack","evt","_remoteTrackAdded","_remoteTrackRemoved","onaddstream","_remoteStreamAdded","onremovestream","_remoteStreamRemoved","ondatachannel","_processStat","stat","statValue","endTime","dumpSDP","isAddOperation","hasLocalSource","hasAnyTracksOfType","mediaTransferActive","_getReceiversByEndpointIds","endpoints","receivers","getRemoteTracks","remoteTrackIds","getReceivers","audioReceivers","getSynchronizationSources","tracks","endpointTracksByMediaType","primarySsrcs","fidLines","getTargetVideoBitrates","findTrackById","RTC","isUserStreamById","onaddtrack","onremovetrack","streamAudioTracks","audioTrack","streamVideoTracks","remoteSDP","mediaLines","mls","trackSsrc","_createRemoteTrack","remoteTracksMap","userTracksByMediaType","existingTrack","isUserStream","toBeRemoved","_removeRemoteTrack","removeRemoteTracks","removedTracks","remoteTracksByMedia","_extractSSRCMap","groupsMap","groupSSRCs","_getSSRC","_injectSsrcGroupForUnifiedSimulcast","getters","audioMedia","changed","videoMedia","enforceSendRecv","_adjustRemoteMediaDirection","_isSharingScreen","_mungeCodecOrder","codecPreference","bitrates","hdBitrate","webrtcStream","_addStream","generateNewStreamSSRCInfo","rtxSsrcMapping","promiseChain","_assertTrackBelongs","webRtcStream","addStream","_removeStream","removeStream","doesBelong","defaultCodec","lowFps","findSenderByKind","getSenders","negotiationNeeded","mediaActive","_ensureSimulcastGroupIsLast","localSdp","videoStartIndex","simStartIndex","otherStartIndex","simEndIndex","simStr","otherEndIndex","sdpHead","simStrTrimmed","sdpTail","_adjustLocalMediaDirection","modifiedDirection","desiredAudioDirection","desiredVideoDirection","hasRemoteSource","_mungeOpus","opusMaxAverageBitrate","mLines","fmtpOpus","fmtpConfig","sdpChanged","maxaveragebitrate","mungedConfig","_initializeDtlsTransport","onstatechange","currentDescription","firstSsrcs","newSsrcLines","filteredLines","ssrcId","cnameLine","replaceDefaultUnifiedPlanMsid","normalizePlanB","videoSender","preference","degradationPreference","encodingsEnabledState","maxBitrates","scaleFactor","layer","sendTones","tones","interToneGap","rtpSender","dtmf","localAudioTrack","createDTMFSender","ontonechange","_onToneChange","toneBuffer","insertDTMF","tone","peerTracks","_removePeerConnection","_createOfferOrAnswer","isOffer","logName","handleSuccess","resolveFn","rejectFn","_processLocalSSRCsMap","handleFailure","eventType","capabilities","setCodecPreferences","oaPromise","_extractPrimarySSRC","sourceIdentifier","newSSRCNum","oldSSRCNum","activeStreams","currNumSsrcs","ConnectionQualityEvents","peerConnectionIdCounter","rtcTrackIdCounter","_createLocalTracks","mediaStreamMetaData","metaData","peerConnections","_lastN","_lastNEndpoints","_forwardedSources","_maxFrameHeight","_selectedEndpoints","_lastNChangeListener","_forwardedSourcesChangeListener","_onDeviceListChanged","_updateAudioOutputForAudioTracks","_videoType","BridgeVideoType","destroy","_channelOpenListener","tracksInfo","initializeBridgeChannel","logError","msgType","_receiverVideoConstraints","oldLastNEndpoints","leavingLastNEndpoints","enteringLastNEndpoints","oldForwardedSources","setNewReceiverVideoConstraints","setVideoType","sendSourceVideoType","selectEndpoints","ids","signaling","encodedInsertableStreams","sdpSemantics","bundlePolicy","newConnection","traceablePeerConnection","addLocalTrack","getForwardedSources","localVideo","getLocalAudioTrack","localAudio","pcRemoteTracks","mutePromises","removeLocalTrack","elSelector","sendChannelMessage","setLastN","remoteAudioTracks","LOCAL_STATS_UPDATED","REMOTE_STATS_UPDATED","kSimulcastFormats","layers","targetRN","ConnectionQuality","_localStats","connectionQuality","jvbRTT","_lastConnectionQualityUpdate","_remoteStats","_timeIceConnected","_timeVideoUnmuted","ConferenceEvents","_updateLocalConnectionQuality","_broadcastLocalStats","ICE_CONNECTION_STATE_CHANGED","_updateRemoteStats","_updateLocalStats","_maybeUpdateUnmuteTime","serverRegion","bridgeCount","_calculateConnectionQuality","resolutionName","quality","activeTPC","getActivePeerConnection","videoQualitySettings","millisSinceStart","simulcastFormat","targetHeight","rampUp","getTarget","maxIncreasePerSecond","prevConnectionQuality","diffSeconds","updateLocalConnectionQuality","isConnectionInterrupted","IceFailedHandling","_actOnIceFailed","explicitlyDisabled","useTerminateForRestart","jvbConnection","jvbConnIceState","_canceled","_iceFailedTimeout","DetectionEvents","DETECTOR_STATE_CHANGE","VAD_NOISY_DEVICE","VAD_REPORT_PUBLISHED","VAD_SCORE_PUBLISHED","VAD_TALK_WHILE_MUTED","NoAudioSignalDetection","_timeoutTrigger","_hasAudioInput","_audioLevel","_trackAdded","_clearTriggerTimeout","_handleAudioInputStateChange","_handleNoAudioInputDetection","_eventFired","_audioTrack","P2PDominantSpeakerDetection","myUserID","createAudioContext","AudioContextImpl","TrackVADEmitter","procNodeSampleRate","vadProcessor","_procNodeSampleRate","_vadProcessor","_localTrack","_bufferResidue","Float32Array","_audioContext","sampleRate","getRequiredPCMFrequency","_vadSampleSize","getSampleLength","_onAudioProcess","_initializeAudioContext","_audioSource","_audioProcessingNode","createScriptProcessor","audioEvent","inData","inputBuffer","getChannelData","completeInData","sampleTimestamp","pcmSample","vadScore","calculateAudioFrameVAD","score","pcmData","_connectAudioGraph","onaudioprocess","destination","_disconnectAudioGraph","_cleanupResources","getDeviceLabel","_destroyed","VADAudioAnalyser","createVADProcessor","_createVADProcessor","_vadEmitter","_isVADEmitterRunning","_detectionServices","_vadInitTracker","_processVADScore","_trackRemoved","addVADDetectionService","vadService","detector","_stopVADEmitter","_startVADEmitter","processVADScore","_changeDetectorsMuteState","changeMuteState","vadEmitter","VADNoiseDetection","_processing","_scoreArray","_audioLvlArray","_active","_calculateNoisyScore","scoreAvg","audioLevelAvg","_setActiveState","_recordValues","avgAudioLvl","_processTimeout","posAudioLevels","VADTalkMutedDetection","_calculateVADScore","E2ePingEvents","E2E_RTT_CHANGED","E2E_PING_REQUEST","E2E_PING_RESPONSE","ParticipantWrapper","e2eping","requests","lastRequestId","clearIntervals","sendRequest","handleResponse","maybeSendAnalytics","isDataChannelOpen","pingIntervalMs","analyticsInterval","analyticsIntervalMs","requestId","requestMessage","timeSent","createE2eRttEvent","E2ePing","participantJoined","participantLeft","messageReceived","dataChannelOpened","participantWrapper","handleRequest","Jvb121EventGenerator","_jvb121","evaluateStatus","oldStatus","getParticipantCount","ReceiverVideoConstraints","_defaultConstraints","defaultConstraints","onStageEndpoints","updateLastN","updateReceiveResolution","updateReceiverVideoConstraints","updateSelectedEndpoints","ReceiveVideoController","_rtc","startLastN","channelLastN","useNewBandwidthAllocationStrategy","remoteEndpointIds","oldConstraints","newConstraints","setPreferredReceiveMaxFrameHeight","setReceiverConstraints","isEndpointsFormat","isSourcesFormat","p2pSession","SendVideoController","_preferredSendMaxFrameHeight","_sourceSenderConstraints","_configureConstraintsForLocalSources","_onSenderConstraintsReceived","_propagateSendMaxFrameHeight","getActiveMediaSession","_senderVideoConstraints","idealHeight","sendMaxFrameHeight","_selectSendMaxFrameHeight","activeMediaSession","setPreferredSendMaxFrameHeight","getFocusRecordingUpdate","jibriStatus","recordingMode","sessionID","getHiddenDomainUpdate","liveStreamViewURLContainer","liveStreamViewURL","modeContainer","sessionIDContainer","getSessionIdFromIq","jibri","getSessionId","sessionIdContainer","isFromFocus","JibriSession","_connection","_setSessionID","setStatus","getID","_sessionID","getInitiator","_initiator","getLiveStreamViewURL","_liveStreamViewURL","getTerminator","_terminator","getMode","setError","setLiveStreamViewURL","appData","broadcastId","_createIQ","recordingXMLUtils","_setErrorFromIq","AudioOutputProblemDetector","_localAudioLevelCache","_reportedParticipants","_audioProblemCandidates","_numberOfRemoteAudioLevelsReceived","_onLocalAudioLevelsReport","_onRemoteAudioLevelReceived","_clearUserData","numberOfReports","localAudioLevels","remoteAudioLevels","localAudioLevelsString","createAudioOutputProblemEvent","AverageStatReport","calculate","appendReport","ConnectionAvgStats","avgRtpStatsReporter","_n","_sampleIdx","_avgRTT","_avgRemoteRTTMap","_avgRtpStatsReporter","_avgEnd2EndRTT","_onConnectionStats","_calculateAvgStats","_onRemoteStatsUpdated","_processRemoteStats","batchReport","jvbEnd2EndRTT","jvbStatsMonitor","avgRemoteRTT","_calculateAvgRemoteRTT","avgLocalRTT","_resetAvgStats","remoteAvg","avg","validData","rttAvg","AvgRTPStatsReporter","_avgAudioBitrateUp","_avgAudioBitrateDown","_avgVideoBitrateUp","_avgVideoBitrateDown","_avgBandwidthUp","_avgBandwidthDown","_avgPacketLossTotal","_avgPacketLossUp","_avgPacketLossDown","_avgRemoteFPS","_avgRemoteScreenFPS","_avgLocalFPS","_avgLocalScreenFPS","_avgRemoteCameraPixels","_avgRemoteScreenPixels","_avgLocalCameraPixels","_avgLocalScreenPixels","_avgCQ","_cachedTransportStats","_onLocalStatsUpdated","_maybeSendTransportAnalyticsEvent","_onP2PStatusChanged","p2pStatsMonitor","_onJvb121StatusChanged","_resetAvgJvbStats","confSize","_calculateAvgVideoFps","_calculateAvgVideoPixels","peerResolutions","peerPixelsSum","peerCount","myID","peerID","videosResolution","peerAvgPixels","_calculatePeerAvgVideoPixels","videos","peerSsrcCount","peerSsrcPixels","peerFpsSum","videosFps","peerAvgFPS","_calculatePeerAvgVideoFps","peerSsrcFps","transportStats","SpeakerStatsCollector","dominantSpeakerId","SpeakerStats","_onDominantSpeaker","_onUserJoin","_onUserLeave","_onDisplayNameChange","_onFacialExpressionAdd","_updateStats","oldDominantSpeaker","newDominantSpeaker","savedUser","newStats","speakerStatsToUpdate","newParticipant","RecordingResult","wordArray","TrackRecorder","recorder","AUDIO_WEBM","AUDIO_OGG","startRecorder","trackRecorder","stopRecorder","determineCorrectFileType","MediaRecorder","isTypeSupported","AudioRecorder","jitsiConference","recorders","fileType","isRecording","instantiateTrackRecorder","updateNames","originalStream","ondataavailable","dataEvent","recorderToRemove","style","click","revokeObjectURL","getRecordingResults","getFileType","Word","word","begin","getWord","getBeginTime","getEndTime","SphinxService","recordingResult","verify","formatResponse","audioBlob","sphinxURL","toReturn","getURL","audioFileBlob","XMLHttpRequest","onreadystatechange","DONE","responseText","open","setRequestHeader","audioRecorder","objects","filler","BEFORE_STATE","RECORDING_STATE","TRANSCRIBING_STATE","FINISHED_STATE","Transcriber","transcriptionService","transcription","lineLength","blobCallBack","transcriber","getUTCMilliseconds","wordObject","maybeMerge","hasPopulatedArrays","twoDimensionalArray","callBack","merge","arrays","potentialWords","pushWordToSortedArray","lowestWordArray","wordToAdd","updateTranscription","foundSmaller","wordToCompare","getTranscription","ComponentsVersions","addCommandListener","processVersions","VideoSIPGWStatusConstants","VideoSIPGWStateConstants","VideoSIPGWErrorConstants","mucResource","getComponentVersion","componentName","STATUS_AVAILABLE","STATUS_UNDEFINED","STATUS_BUSY","STATE_ON","STATE_OFF","STATE_PENDING","STATE_RETRYING","STATE_FAILED","ERROR_NO_CONNECTION","ERROR_SESSION_EXISTS","STATE_CHANGED","JitsiVideoSIPGWSession","sipAddress","VideoSIPGWConstants","_sendJibriIQ","setState","failureReason","oldState","addStateListener","removeStateListener","sipaddress","displayname","nodeTree","VideoSIPGW","sessionStateChangeListener","sessionStateChanged","handleJibriSIPState","Constants","failure_reason","createVideoSIPGWSession","SOURCE_INFO_PRESENCE_ELEMENT","SignalingLayerImpl","ssrcOwners","_localSourceState","_remoteSourceState","_sourceNames","_addLocalSourceInfoToPresence","_doesEndpointSendNewSourceInfo","setChatRoom","oldChatRoom","_audioMuteHandler","_videoMuteHandler","_videoTypeHandler","_sourceInfoHandler","_memberLeftHandler","_bindChatRoomEventHandlers","emitAudioMutedEvent","emitVideoMutedEvent","emitVideoTypeEvent","sourceInfoJSON","emitEventsFromHere","endpointSourceState","newMutedState","oldSourceState","newVideoType","newSourceNames","_findEndpointSourceInfoForMediaType","remoteSourceState","legacyGetPeerMediaInfo","lastPresence","mediaInfo","endpointMediaSource","existingOwner","setTrackMuteStatus","setTrackVideoType","existingName","JitsiConference","eventManager","componentsVersions","dtmfManager","somebodySupportsDTMF","wasStopped","avgRtpStatsN","_audioOutputProblemDetector","speakerStatsCollector","deferredStartP2PTask","delay","backToP2PDelay","isP2PConnectionInterrupted","videoSIPGWHandler","recordingManager","_sessions","_chatRoom","getSession","_handleFocusPresence","_handleJibriPresence","startRecording","_addSession","_emitSessionUpdate","stopRecording","_createSession","_conferenceJoinAnalyticsEventSent","isE2EESupported","_e2eEncryption","_audioSenderLimitReached","_videoSenderLimitReached","JitsiConnection","appID","errType","errorType","errorMessage","createConnectionFailedEvent","ANALYTICS_CONNECTION_DISCONNECTED","JitsiMediaDevicesEvents","resourceCreator","isAuthenticatedUser","re","codecSettings","codecSelection","_statsCurrentId","statisticsId","_onIceConnectionInterrupted","_onIceConnectionRestored","_onIceConnectionEstablished","_updateProperties","_sendConferenceJoinAnalyticsEvent","_removeLocalSourceOnReject","_updateRoomPresence","_registerRtcListeners","receiveVideoController","sendVideoController","callStatsThreshold","statisticsDisplayName","callStatsCustomScriptUrl","callStatsConfigParams","enableTalkWhileMuted","_audioAnalyser","vadTalkMutedDetection","enableNoisyMicDetection","vadNoiseDetection","enableNoAudioDetection","_noAudioSignalDetection","hasAudioSignal","jvb121Status","p2pDominantSpeakerDetection","userRegion","transcriptionLanguage","_maybeSetSITimeout","isJoined","isP2PEnabled","isP2PTestModeEnabled","p2pTestMode","onLocalTrackRemoved","_sendConferenceLeftAnalyticsEvent","_delayedIceFailed","_maybeClearSITimeout","leaveError","localtrack","_desktopSharingFrameRate","isAuthEnabled","isLoggedIn","getAuthLogin","getExternalAuthUrl","urlForPopup","getPerformanceStats","longTasksStats","eventId","command","removeCommandListener","sendTextMessage","sendPrivateTextMessage","sendCommand","sendCommandOnce","removeCommand","nickKey","getTranscriber","localAudioTracks","getTranscriptionStatus","addTrackPromises","_setupNewTrack","_fireMuteChangeEvent","_fireAudioLevelChangeEvent","activeTpc","actorId","_getInitialLocalTracks","trackType","isStartAudioMuted","isStartVideoMuted","muteHandler","audioLevelHandler","oldVideoType","oldTrackBelongsToConference","_doReplaceTrack","replaceTrackPromises","_setNewVideoType","videoTypeChanged","videoTypeTagName","trackVideoType","legacyTypeChanged","presenceChanged","audioMuteChanged","videoMuteChanged","addAsUnmutePromises","removeAsMutePromises","lock","unlock","selectParticipant","selectParticipants","isInteger","isVideoActive","countHidden","grantOwner","revokeOwner","isMyself","isMembersOnly","kickParticipant","_sessionInitiateTimeout","muteMediaType","fullJid","_updateFeatures","_maybeStartOrStopP2P","updateDTMFSupport","botParticipant","mediaSessions","tracksToBeRemoved","kickedParticipantId","kickedParticipant","transportInfo","removedTrack","_onIncomingCallP2P","rejectReason","_shouldBeInP2PMode","_rejectIncomingCall","_acceptP2PIncomingCall","_acceptJvbIncomingCall","_suspendMediaTransferForJvbConnection","webSocket","stopOptions","forceJvb121","p2pFailed","_stopP2PSession","isDTMFSupported","peerConnection","startTranscriber","stopTranscriber","getMeetingUniqueId","setStartMutedPolicy","policy","getStartMutedPolicy","removeLocalParticipantProperty","getLocalParticipantProperty","property","overallFeedback","detailedFeedback","getSsrcByTrack","remoteUserId","_fireIncompatibleVersionsEvent","sendEndpointMessage","broadcastEndpointMessage","sendThroughVideobridge","messageType","messageToSend","remoteID","_addRemoteJVBTracks","_addRemoteTracks","_addRemoteP2PTracks","p2pEstablishmentDuration","jvbEstablishmentDuration","forceJVB121Ratio","establishmentDurationDiff","_setP2PStatus","_removeRemoteJVBTracks","audioLimitReached","videoLimitReached","_maybeClearDeferredStartP2P","_removeRemoteTracks","_removeRemoteP2PTracks","sessionNickname","_resumeMediaTransferForJvbConnection","_startP2PSession","userLeftEvent","peers","shouldBeInP2P","myId","peersId","hasBotPeer","wasP2PEstablished","skip","muteStatusChanged","audioMuteStatusChanged","videoMuteStatusChanged","getP2PConnectionState","startP2PSession","stopP2PSession","getSpeakerStats","sendFacialExpression","perf","toggleE2EE","setMediaEncryptionKey","isLobbySupported","disableLobby","joinLobby","myLobbyUserId","sendLobbyMessage","addLobbyMessageListener","removeLobbyMessageHandler","lobbyDenyAccess","lobbyApproveAccess","isAVModerationSupported","enableAVModeration","disableAVModeration","avModerationApprove","avModerationReject","setToken","initJitsiConference","getLogs","PERMISSION_PROMPT_IS_SHOWN","SLOW_GET_USER_MEDIA","AUDIO_PERMISSION_NAME","VIDEO_PERMISSION_NAME","_permissions","_logOutputDevice","permissions","_handlePermissionsChange","_permissionsApiSupported","query","_parsePermissionState","onchange","supported","permissionStatus","deviceID","isDevicePermissionGranted","isMultipleAudioInputSupported","emitEvent","stopActiveDevices","ACTIONS","ProxyConnectionPC","receiveAudio","receiveVideo","_peerConnection","_onError","_onRemoteStream","_onSendMessage","getPeerJid","processMessage","$jingle","ACCEPT","_onSessionAccept","INITIATE","_onSessionInitiate","TERMINATE","_onSessionTerminate","TRANSPORT_INFO","_onTransportInfo","_createPeerConnection","connectionStub","pcConfigStub","roomStub","jitsiRemoteTrack","onRemoteStream","onSendMessage","PRECALL_TEST_RESULTS","_initialized","api","_loadScript","appId","appSecret","disablePrecalltest","_initialize","execute","makePrecallTest","AudioMixer","_started","_streamsToMix","_streamMSSArray","addMediaStream","_mixedMSD","createMediaStreamDestination","streamMSS","getAnalyticsAttributesFromOptions","video_requested","JitsiMeetJS","ProxyConnectionService","jitsiConnection","_onFatalError","_convertStringToXML","UNAVAILABLE","_selfCloseConnection","xml","xmlDom","DOMParser","parseFromString","isVideo","convertVideoToDesktop","jitsiLocalTracks","createLocalTracks","stringifiedIq","XMLSerializer","serializeToString","onConnectionClosed","constants","recording","BUSY","RESOURCE_CONSTRAINT","UNEXPECTED_REQUEST","SERVICE_UNAVAILABLE","FILE","STREAM","sipVideoGW","detection","errors","errorTypes","logLevels","JitsiMediaDevices","enableAnalyticsLogging","enableWindowOnErrorHandler","getGlobalOnErrorHandler","isWebRtcSupported","addGlobalLogTransport","globalTransport","removeGlobalLogTransport","setGlobalLogOptions","oldfirePermissionPromptIsShownEvent","promiseFulfilled","firePermissionPromptIsShownEvent","fireSlowPromiseEvent","restOptions","mStream","currentlyAvailableMediaDevices","setVideoTrackContentHints","createTrackVADEmitter","localAudioDeviceId","createAudioMixer","getActiveAudioDevice","audioDevices","devicePromiseArray","micDevice","devicePromise","outcomeArray","successfulPromises","rejectedPromises","rejectReasons","deviceLabel","isCollectingLocalStats","lineno","colno","setNetworkInfo","hint","contentHint","precallTest","util","AuthUtil","global$1","_typeof","_toConsumableArray","arr2","_arrayWithoutHoles","iter","_iterableToArray","_nonIterableSpread","safe_add","lsw","str2binl","bin","md5_cmn","cnt","md5_ff","md5_gg","md5_hh","md5_ii","core_md5","olda","oldb","oldc","oldd","MD5","hexdigest","binarray","hex_tab","binl2hex","fromCharCode","binl2str","core_sha1","olde","rol","safe_add$1","sha1_ft","sha1_kt","core_hmac_sha1","bkey","str2binb","ipad","opad","binb2b64","triplet","binb2str","SHA1","b64_hmac_sha1","str_hmac_sha1","str_sha1","utils","out","cookies","cookieName","expires","path","cookieObj","isObj","cookieValue","escape","unescape","cookie","Builder","VERSION","NS","HTTPBIND","BOSH","CLIENT","AUTH","ROSTER","PROFILE","DISCO_INFO","DISCO_ITEMS","MUC","SASL","FRAMING","BIND","SESSION","STANZAS","XHTML_IM","XHTML","css","validTag","validAttribute","validCSS","CONNECTING","CONNFAIL","AUTHENTICATING","AUTHFAIL","DISCONNECTED","DISCONNECTING","ATTACHED","REDIRECT","CONNTIMEOUT","BINDREQUIRED","ErrorCondition","BAD_FORMAT","CONFLICT","MISSING_JID_NODE","NO_AUTH_MECH","UNKNOWN_REASON","LogLevel","FATAL","ElementType","NORMAL","TEXT","CDATA","FRAGMENT","SECONDARY_TIMEOUT","addNamespace","forEachChild","elemName","childNodes","childNode","isTagEqual","_xmlGenerator","_makeGenerator","doc","implementation","createDocument","documentMode","_getIEXmlDom","xmlGenerator","docStrings","ActiveXObject","xmlTextNode","xmlescape","xmlunescape","createTextNode","xmlHtmlNode","html","loadXML","getText","nodeValue","copyElement","_i","createHtml","cssText","cssAttrs","cssName","cssValue","_i2","createDocumentFragment","_i3","_i4","escapeNode","unescapeNode","getNodeFromJid","getDomainFromJid","bare","getBareJidFromJid","getResourceFromJid","_handleError","fatal","sourceURL","fileName","lineNumber","getNamedItem","_requestId","_connectionPlugins","ptype","moreattrs","removeAttribute","impNode","xmlGen","importNode","newElem","h","fragment","innerHTML","xhtml","Handler","ns","matchBare","matchBareFromJid","getNamespace","elNamespace","ignoreNamespaceFragment","namespaceMatch","_this","nsMatch","isMatch","elem_type","run","TimedHandler","period","lastCalled","Connection","_this2","Websocket","Bosh","_sasl_data","do_session","do_bind","timedHandlers","removeTimeds","removeHandlers","addTimeds","addHandlers","protocolErrorHandlers","_idleTimeout","_disconnectTimeout","authenticated","do_authentication","restored","_data","_uniqueId","_sasl_success_handler","_sasl_failure_handler","_sasl_challenge_handler","_onIdle","registerSASLMechanisms","mechanisms","F","_reset","_requests","getUniqueId","suffix","addProtocolErrorHandler","status_code","hold","route","authcid","authzid","servtype","connect_callback","wind","_attach","restore","_sessionCachingSupported","_restore","sessionStorage","xmlInput","xmlOutput","nextValidRid","_queueData","_this3","timeoutHandler","deleteTimedHandler","addTimedHandler","_this4","iqtype","_sendRestart","_this5","thand","handRef","hand","SASLAnonymous","SASLExternal","SASLMD5","SASLOAuthBearer","SASLXOAuth2","SASLPlain","SASLSHA1","registerSASLMechanism","_addSysTimedHandler","_onDisconnectTimeout","_disconnect","condition","statusChanged","_dataRecv","_this6","_reqToData","strip","_emptyQueue","cond","conflict","UNKOWN_REASON","_i5","_hand","_connect_cb","_callback","bodyWrap","mech","_no_auth_received","sortMechanismsByPriority","higher","swap","_attemptSASLAuth","_attemptLegacyAuth","mechanism_found","_sasl_success_cb","_sasl_failure_cb","_sasl_challenge_cb","_sasl_mechanism","onStart","request_auth_exchange","isClientFirst","onChallenge","btoa","challenge","atob","_onLegacyAuthIQResult","_auth2_cb","_this7","serverSignature","streamfeature_handlers","wrapper","_onStreamFeaturesAfterSASL","explicitResourceBinding","_onResourceBindResultIQ","jidNode","_establishSession","_onSessionResultIQ","_this8","_i6","_thand","SASLMechanism","auth_str","test_cnonce","cnonce","nonce","Hi","U","U_old","authMessage","attribMatch","clientKey","serverKey","clientSignature","_quote","realm","host","digest_uri","cred","A1","A2","core","Strophe$1","$build$1","Request","sends","xmlData","origFunc","date","NaN","abort","dead","age","timeDead","xhr","_newXHR","getResponse","responseXML","documentElement","overrideMimeType","_conn","inactivity","keepalive","_cacheSession","_onRequestStateChange","_throttledRequestHandler","typ","_sendTerminate","_callProtocolErrorHandlers","reqStatus","_getRequestStatus","err_callback","HTTP","_hitError","time_elapsed","def","getAllResponseHeaders","valid_request","too_many_retries","_removeRequest","reqIs0","_restartRequest","_processRequest","primary_timeout","secondary_timeout","server_error","content_type","contentType","withCredentials","e2","sendFunc","customHeaders","backoff","Strophe$2","$build$2","new_service","pathname","_buildStream","_check_streamerror","connectstatus","errorString","_onOpen","_connect_cb_wrapper","_handleStreamStart","streamStart","parsedMessage","see_uri","_streamWrap","CLOSED","closeString","rawStanza","firstChild","startString","strophe_js","_items","conn","_onDiscoInfo","_onDiscoItems","addIdentity","lang","var_name","addItem","call_back","items","_buildIQResult","query_attrs","iqresult","logging","autoSendCountOnEveryIncomingStanza","requestResponseInterval","_c","_NS","_isStreamManagementEnabled","_serverProcesssedStanzasCounter","_clientProcessedStanzasCounter","_clientSentStanzasCounter","_originalXMLOutput","_requestHandler","_incomingHandler","_requestResponseIntervalCount","_isSupported","_unacknowledgedStanzas","_acknowledgedStanzaListeners","addAcknowledgedStanzaListener","_resumeToken","_resuming","_originalConnect","_connectArgs","requestAcknowledgement","getOutgoingCounter","getIncomingCounter","_interceptConnectArgs","_originalOnStreamFeaturesAfterSASL","_originalDoDisconnect","_interceptDoDisconnect","_originalDisconnect","_interceptDisconnect","_resumeState","_storedJid","previd","_handleServerRequestHandler","_ackHandler","_handleServerAck","_incomingStanzaHandler","_enabledHandler","_handleEnabled","_resumeFailedHandler","_handleResumeFailed","_resumedHandler","_handleResumed","_increaseSentStanzasCounter","firstElementChild","handledCount","_handleAcknowledgedStanzas","_increaseReceivedStanzasCounter","_answerProcessedStanzas","reportedHandledCount","lastKnownHandledCount","delta","_throwError","byteToHex","buf","bth","_v","_v2","_v3","_v4","_interopRequireDefault","getOutputLength","inputLength8","safeAdd","md5cmn","md5ff","md5gg","md5hh","md5ii","encodeURIComponent","length32","hexTab","md5ToHexEncodedArray","wordsToMd5","length8","bytesToWords","rnds8","msCrypto","ROTL","K","H","N","ceil","M","W","T","_rng","_bytesToUuid","_nodeId","_clockseq","_lastMSecs","_lastNSecs","clockseq","seedBytes","rng","msecs","nsecs","dt","tl","tmh","_md","_default","hashfunc","generateUUID","namespace","stringToBytes","uuidToBytes","DNS","rnds","_sha","logDisabled_","deprecationWarnings_","extractVersion","uastring","wrapPeerConnectionEvent","eventNameToWrap","nativeAddEventListener","nativeEventName","modifiedEvent","handleEvent","_eventMap","nativeRemoveEventListener","unwrappedCb","disableLog","bool","disableWarnings","deprecated","oldMethod","newMethod","val","compactObject","isEmptyObject","walkStats","resultSet","filterStats","outbound","streamStatsType","filteredResult","trackStats","trackStat","shimGetUserMedia","browserDetails","constraintsToChrome_","cc","exact","oldname_","oc","mix","advanced","shimConstraints_","remap","face","getSupportedFacingModeLies","getSupportedConstraints","dev","shimError_","PermissionDeniedError","PermissionDismissedError","InvalidStateError","DevicesNotFoundError","ConstraintNotSatisfiedError","TrackStartError","MediaDeviceFailedDueToShutdown","MediaDeviceKillSwitchOn","TabCaptureError","ScreenCaptureError","DeviceCaptureError","webkitGetUserMedia","origGetUserMedia","DOMException","shimGetDisplayMedia","getSourceId","widthSpecified","heightSpecified","frameRateSpecified","shimMediaStream","webkitMediaStream","shimOnTrack","_ontrack","origSetRemoteDescription","_ontrackpoly","te","Event","dispatchEvent","shimGetSendersWithDtmf","shimSenderWithDtmf","_dtmf","_pc","_senders","origAddTrack","origRemoveTrack","origAddStream","origRemoveStream","origGetSenders","shimGetStats","origGetStats","selector","onSucc","onErr","fixChromeStats_","standardReport","standardStats","localcandidate","remotecandidate","makeMapStats","successCallbackWrapper_","shimSenderReceiverGetStats","origGetReceivers","srcElement","MediaStreamTrack","shimAddTrackRemoveTrackWithNative","getLocalStreams","_shimmedLocalStreams","existingSenders","newSenders","newSender","shimAddTrackRemoveTrack","origGetLocalStreams","nativeStreams","_reverseStreams","_streams","newStream","replaceInternalStreamId","internalId","externalStream","internalStream","replaceExternalStreamId","alreadyExists","oldStream","nativeMethod","methodObj","origSetLocalDescription","origLocalDescription","getOwnPropertyDescriptor","streamid","shimPeerConnection","webkitRTCPeerConnection","fixNegotiationNeeded","getConfiguration","nativeGetUserMedia","nativeGetSettings","applyConstraints","nativeApplyConstraints","preferredMediaSource","mediaSource","RTCTrackEvent","mozRTCPeerConnection","modernStatsTypes","inboundrtp","outboundrtp","candidatepair","nativeGetStats","shimSenderGetStats","shimReceiverGetStats","shimRemoveStream","shimRTCDataChannel","DataChannel","RTCDataChannel","shimAddTransceiver","origAddTransceiver","setParametersPromises","initParameters","shouldPerformCheck","encodingParam","maxFramerate","shimGetParameters","origGetParameters","shimCreateOffer","origCreateOffer","finally","shimCreateAnswer","origCreateAnswer","shimLocalStreamsAPI","_localStreams","_addTrack","shimRemoteStreamsAPI","getRemoteStreams","_remoteStreams","_onaddstream","_onaddstreampoly","shimCallbacksAPI","withCallback","shimConstraints","errcb","shimRTCIceServerUrls","OrigPeerConnection","newIceServers","generateCertificate","shimTrackEventTransceiver","shimCreateOfferLegacy","offerOptions","audioTransceiver","setDirection","videoTransceiver","shimAudioContext","shimRTCIceCandidate","NativeRTCIceCandidate","nativeCandidate","parsedCandidate","augmentedCandidate","toJSON","shimMaxMessageSize","_sctp","sctpInDescription","getRemoteFirefoxVersion","getCanSendMaxMessageSize","remoteIsFirefox","canSendMaxMessageSize","getMaxMessageSize","canSendMMS","remoteMMS","POSITIVE_INFINITY","shimSendThrowTypeError","wrapDcSend","dc","origDataChannelSend","origCreateDataChannel","dataChannel","shimConnectionState","checking","_onconnectionstatechange","origMethod","_connectionstatechangepoly","_lastConnectionState","newEvent","removeExtmapAllowMixed","nativeSRD","shimAddIceCandidateNullOrEmpty","nativeAddIceCandidate","adapter","shimChrome","shimFirefox","shimSafari","mozGetUserMedia","isSecureContext","RTCIceGatherer","commonShim","browserShim","adapterFactory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","loaded","__webpack_modules__","amdO","getter","definition","globalThis","nmd","paths","__webpack_exports__"],"sourceRoot":""}