{"version":3,"sources":["webpack://JitsiMeetJS/webpack/universalModuleDefinition","webpack://JitsiMeetJS/webpack/bootstrap","webpack://JitsiMeetJS/./service/xmpp/XMPPEvents.js","webpack://JitsiMeetJS/./JitsiConferenceEvents.js","webpack://JitsiMeetJS/../strophe.js/dist/strophe.umd.js","webpack://JitsiMeetJS/./modules/browser/index.js","webpack://JitsiMeetJS/../jitsi-meet-logger/lib/index.js","webpack://JitsiMeetJS/./modules/statistics/statistics.js","webpack://JitsiMeetJS/./service/RTC/MediaType.js","webpack://JitsiMeetJS/./service/RTC/RTCEvents.js","webpack://JitsiMeetJS/./service/statistics/AnalyticsEvents.js","webpack://JitsiMeetJS/./modules/xmpp/SDPUtil.js","webpack://JitsiMeetJS/./modules/RTC/RTCUtils.js","webpack://JitsiMeetJS/./modules/util/GlobalOnErrorHandler.js","webpack://JitsiMeetJS/./modules/RTC/RTC.js","webpack://JitsiMeetJS/./JitsiTrackErrors.js","webpack://JitsiMeetJS/./service/RTC/VideoType.js","webpack://JitsiMeetJS/./JitsiTrackEvents.js","webpack://JitsiMeetJS/./JitsiConferenceErrors.js","webpack://JitsiMeetJS/../events/events.js","webpack://JitsiMeetJS/./JitsiTrackError.js","webpack://JitsiMeetJS/./modules/xmpp/SDP.js","webpack://JitsiMeetJS/./modules/util/Listenable.js","webpack://JitsiMeetJS/./modules/detection/DetectionEvents.js","webpack://JitsiMeetJS/./service/statistics/Events.js","webpack://JitsiMeetJS/../sdp-transform/lib/index.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGWConstants.js","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/./JitsiConnectionEvents.js","webpack://JitsiMeetJS/./service/RTC/CodecMimeType.js","webpack://JitsiMeetJS/./modules/xmpp/xmpp.js","webpack://JitsiMeetJS/./modules/util/MathUtil.js","webpack://JitsiMeetJS/./modules/settings/Settings.js","webpack://JitsiMeetJS/./modules/xmpp/SdpTransformUtil.js","webpack://JitsiMeetJS/./service/connectivity/ConnectionQualityEvents.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionState.js","webpack://JitsiMeetJS/./modules/proxyconnection/constants.js","webpack://JitsiMeetJS/./service/RTC/Resolutions.js","webpack://JitsiMeetJS/./modules/statistics/CallStats.js","webpack://JitsiMeetJS/./modules/util/RandomUtil.js","webpack://JitsiMeetJS/./modules/xmpp/ConnectionPlugin.js","webpack://JitsiMeetJS/./JitsiConnectionErrors.js","webpack://JitsiMeetJS/../lodash.isequal/index.js","webpack://JitsiMeetJS/./modules/xmpp/XmppConnection.js","webpack://JitsiMeetJS/./service/RTC/SignalingEvents.js","webpack://JitsiMeetJS/../lodash.clonedeep/index.js","webpack://JitsiMeetJS/./service/RTC/CameraFacingMode.js","webpack://JitsiMeetJS/../webpack/buildin/global.js","webpack://JitsiMeetJS/./modules/RTC/ScreenObtainer.js","webpack://JitsiMeetJS/./modules/util/ScriptUtil.js","webpack://JitsiMeetJS/./modules/connectivity/ParticipantConnectionStatus.js","webpack://JitsiMeetJS/./modules/connectivity/NetworkInfo.js","webpack://JitsiMeetJS/../sdp/sdp.js","webpack://JitsiMeetJS/./modules/recording/recordingXMLUtils.js","webpack://JitsiMeetJS/./JitsiMediaDevicesEvents.js","webpack://JitsiMeetJS/./modules/e2ee/OlmAdapter.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEncryption.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionPC.js","webpack://JitsiMeetJS/./modules/xmpp/MediaSessionEvents.js","webpack://JitsiMeetJS/../base64-js/index.js","webpack://JitsiMeetJS/./modules/xmpp/SDPDiffer.js","webpack://JitsiMeetJS/../process/browser.js","webpack://JitsiMeetJS/./modules/statistics/LocalStatsCollector.js","webpack://JitsiMeetJS/./modules/statistics/constants.js","webpack://JitsiMeetJS/./modules/xmpp/Caps.js","webpack://JitsiMeetJS/./JitsiTranscriptionStatus.js","webpack://JitsiMeetJS/./modules/RTC/JitsiTrack.js","webpack://JitsiMeetJS/./modules/detection/TrackVADEmitter.js","webpack://JitsiMeetJS/./modules/webaudio/WebAudioUtils.js","webpack://JitsiMeetJS/./service/e2eping/E2ePingEvents.js","webpack://JitsiMeetJS/./modules/util/Deferred.js","webpack://JitsiMeetJS/./modules/RTC/TPCUtils.js","webpack://JitsiMeetJS/./modules/statistics/SpeakerStats.js","webpack://JitsiMeetJS/./service/authentication/AuthenticationEvents.js","webpack://JitsiMeetJS/./node_modules/js-md5/src/md5.js","webpack://JitsiMeetJS/../lodash.debounce/index.js","webpack://JitsiMeetJS/./modules/e2ee/crypto-utils.js","webpack://JitsiMeetJS/./modules/RTC/JitsiLocalTrack.js","webpack://JitsiMeetJS/./modules/detection/VADAudioAnalyser.js","webpack://JitsiMeetJS/./modules/recording/JibriSession.js","webpack://JitsiMeetJS/./JitsiMediaDevices.js","webpack://JitsiMeetJS/../jitsi-meet-logger/lib/Logger.js","webpack://JitsiMeetJS/../webpack/buildin/module.js","webpack://JitsiMeetJS/../sdp-transform/lib/grammar.js","webpack://JitsiMeetJS/./modules/transcription/audioRecorder.js","webpack://JitsiMeetJS/./JitsiConnection.js","webpack://JitsiMeetJS/./JitsiConference.js","webpack://JitsiMeetJS/./JitsiConferenceEventManager.js","webpack://JitsiMeetJS/./modules/browser/BrowserCapabilities.js","webpack://JitsiMeetJS/../bowser/es5.js","webpack://JitsiMeetJS/./modules/statistics/AnalyticsAdapter.js","webpack://JitsiMeetJS/./modules/statistics/PerformanceObserverStats.js","webpack://JitsiMeetJS/./modules/statistics/RTPStatsCollector.js","webpack://JitsiMeetJS/./modules/util/EventEmitterForwarder.js","webpack://JitsiMeetJS/./JitsiParticipant.js","webpack://JitsiMeetJS/./authenticateAndUpgradeRole.js","webpack://JitsiMeetJS/./modules/e2ee/E2EEContext.js","webpack://JitsiMeetJS/./modules/xmpp/ResumeTask.js","webpack://JitsiMeetJS/./modules/util/Retry.js","webpack://JitsiMeetJS/./modules/xmpp/StropheLastSuccess.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.ping.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.emuc.js","webpack://JitsiMeetJS/./modules/xmpp/ChatRoom.js","webpack://JitsiMeetJS/./modules/xmpp/Lobby.js","webpack://JitsiMeetJS/./modules/xmpp/moderator.js","webpack://JitsiMeetJS/./modules/util/UsernameGenerator.js","webpack://JitsiMeetJS/./modules/xmpp/strophe.jingle.js","webpack://JitsiMeetJS/./modules/util/AsyncQueue.js","webpack://JitsiMeetJS/../async/lib/async.js","webpack://JitsiMeetJS/./modules/util/StringUtils.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSession.js","webpack://JitsiMeetJS/./modules/xmpp/SignalingLayerImpl.js","webpack://JitsiMeetJS/./service/RTC/SignalingLayer.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/RTC/CodecSelection.js","webpack://JitsiMeetJS/./modules/RTC/BridgeChannel.js","webpack://JitsiMeetJS/../rtcpeerconnection-shim/rtcpeerconnection.js","webpack://JitsiMeetJS/./modules/RTC/TraceablePeerConnection.js","webpack://JitsiMeetJS/./modules/xmpp/RtxModifier.js","webpack://JitsiMeetJS/./modules/xmpp/SdpConsistency.js","webpack://JitsiMeetJS/./modules/RTC/JitsiRemoteTrack.js","webpack://JitsiMeetJS/./modules/RTC/LocalSdpMunger.js","webpack://JitsiMeetJS/./modules/connectivity/ConnectionQuality.js","webpack://JitsiMeetJS/./modules/connectivity/IceFailedHandling.js","webpack://JitsiMeetJS/./modules/detection/NoAudioSignalDetection.js","webpack://JitsiMeetJS/./modules/detection/P2PDominantSpeakerDetection.js","webpack://JitsiMeetJS/./modules/detection/VADNoiseDetection.js","webpack://JitsiMeetJS/./modules/detection/VADTalkMutedDetection.js","webpack://JitsiMeetJS/./modules/e2eping/e2eping.js","webpack://JitsiMeetJS/./modules/event/Jvb121EventGenerator.js","webpack://JitsiMeetJS/./modules/qualitycontrol/QualityController.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/transcriber.js","webpack://JitsiMeetJS/./modules/version/ComponentsVersions.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGW.js","webpack://JitsiMeetJS/./modules/videosipgw/JitsiVideoSIPGWSession.js","webpack://JitsiMeetJS/./modules/detection/ActiveDeviceDetector.js","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionService.js","webpack://JitsiMeetJS/./modules/proxyconnection/ProxyConnectionPC.js","webpack://JitsiMeetJS/./modules/recording/recordingConstants.js","webpack://JitsiMeetJS/./modules/statistics/PrecallTest.js","webpack://JitsiMeetJS/./modules/util/AuthUtil.js","webpack://JitsiMeetJS/./modules/webaudio/AudioMixer.js","webpack://JitsiMeetJS/../@jitsi/sdp-interop/lib/transform.js","webpack://JitsiMeetJS/../@jitsi/sdp-interop/lib/interop.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/esm-browser/rng.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/esm-browser/bytesToUuid.js","webpack://JitsiMeetJS/./node_modules/uuid/dist/esm-browser/v4.js","webpack://JitsiMeetJS/./index.js","webpack://JitsiMeetJS/./JitsiMeetJS.js","webpack://JitsiMeetJS/../jitsi-meet-logger/lib/LogCollector.js","webpack://JitsiMeetJS/../webpack/buildin/amd-options.js","webpack://JitsiMeetJS/../current-executing-script/dist/currentExecutingScript.js","webpack://JitsiMeetJS/./service/statistics/constants.js","webpack://JitsiMeetJS/../src/strophe.disco.js","webpack://JitsiMeetJS/../src/strophe.stream-management.js","webpack://JitsiMeetJS/../timers-browserify/main.js","webpack://JitsiMeetJS/../setimmediate/setImmediate.js","webpack://JitsiMeetJS/../sdp-transform/lib/parser.js","webpack://JitsiMeetJS/../sdp-transform/lib/writer.js","webpack://JitsiMeetJS/../@jitsi/sdp-simulcast/lib/index.js","webpack://JitsiMeetJS/../@jitsi/sdp-simulcast/lib/transform-utils.js","webpack://JitsiMeetJS/./modules/transcription/recordingResult.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/SphinxTranscriptionService.js","webpack://JitsiMeetJS/./modules/transcription/word.js","webpack://JitsiMeetJS/./modules/transcription/transcriptionServices/AbstractTranscriptionService.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/utils.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/chrome/getusermedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/chrome/getdisplaymedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/chrome/chrome_shim.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/edge/getusermedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/edge/getdisplaymedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/edge/edge_shim.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/edge/filtericeservers.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/firefox/getusermedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/firefox/getdisplaymedia.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/firefox/firefox_shim.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/safari/safari_shim.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/common_shim.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/adapter_core.js","webpack://JitsiMeetJS/../webrtc-adapter/src/js/adapter_factory.js"],"names":["root","factory","exports","module","define","amd","window","installedModules","__webpack_require__","moduleId","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","ADD_ICE_CANDIDATE_FAILED","AUDIO_MUTED_BY_FOCUS","AUTHENTICATION_REQUIRED","BRIDGE_DOWN","CALL_ACCEPTED","CALL_INCOMING","CALL_ENDED","CHAT_ERROR_RECEIVED","CONFERENCE_PROPERTIES_CHANGED","CONNECTION_ESTABLISHED","CONNECTION_FAILED","CONNECTION_INTERRUPTED","CONNECTION_RESTORED","CONNECTION_ICE_FAILED","CONNECTION_RESTARTED","CONNECTION_STATUS_CHANGED","DISPLAY_NAME_CHANGED","EMUC_ROOM_ADDED","EMUC_ROOM_REMOVED","ETHERPAD","FOCUS_DISCONNECTED","FOCUS_LEFT","GRACEFUL_SHUTDOWN","ICE_RESTARTING","ICE_RESTART_SUCCESS","KICKED","LOCAL_ROLE_CHANGED","MEETING_ID_SET","MESSAGE_RECEIVED","INVITE_MESSAGE_RECEIVED","PRIVATE_MESSAGE_RECEIVED","MUC_MEMBER_BOT_TYPE_CHANGED","MUC_DESTROYED","MUC_JOINED","MUC_MEMBER_JOINED","MUC_MEMBER_LEFT","MUC_LOBBY_MEMBER_JOINED","MUC_LOBBY_MEMBER_UPDATED","MUC_LOBBY_MEMBER_LEFT","MUC_DENIED_ACCESS","MUC_LEFT","MUC_ROLE_CHANGED","MUC_LOCK_CHANGED","MUC_MEMBERS_ONLY_CHANGED","PARTICIPANT_AUDIO_MUTED","PARTICIPANT_VIDEO_MUTED","PARTICIPANT_VIDEO_TYPE_CHANGED","PARTICIPANT_FEATURES_CHANGED","PASSWORD_REQUIRED","PHONE_NUMBER_CHANGED","PRESENCE_RECEIVED","PRESENCE_STATUS","PROMPT_FOR_LOGIN","READY_TO_JOIN","RECORDER_STATE_CHANGED","REMOTE_STATS","RENEGOTIATION_FAILED","RESERVATION_ERROR","ROOM_CONNECT_ERROR","ROOM_CONNECT_NOT_ALLOWED_ERROR","ROOM_JOIN_ERROR","ROOM_CONNECT_MEMBERS_ONLY_ERROR","ROOM_MAX_USERS_ERROR","SENDING_CHAT_MESSAGE","SENDING_PRIVATE_CHAT_MESSAGE","SESSION_ACCEPT_TIMEOUT","SPEAKER_STATS_RECEIVED","CONFERENCE_TIMESTAMP_RECEIVED","START_MUTED_FROM_FOCUS","SUBJECT_CHANGED","SUSPEND_DETECTED","TRANSCRIPTION_STATUS_CHANGED","TRANSPORT_INFO","VIDEO_SIP_GW_AVAILABILITY_CHANGED","VIDEO_SIP_GW_SESSION_STATE_CHANGED","ICE_CONNECTION_STATE_CHANGED","JSON_MESSAGE_RECEIVED","AUDIO_INPUT_STATE_CHANGE","AUTH_STATUS_CHANGED","BEFORE_STATISTICS_DISPOSED","CONFERENCE_ERROR","CONFERENCE_FAILED","CONFERENCE_JOINED","CONFERENCE_LEFT","CONFERENCE_UNIQUE_ID_SET","DATA_CHANNEL_OPENED","DOMINANT_SPEAKER_CHANGED","CONFERENCE_CREATED_TIMESTAMP","DTMF_SUPPORT_CHANGED","ENDPOINT_MESSAGE_RECEIVED","JVB121_STATUS","PARTICIPANT_KICKED","LAST_N_ENDPOINTS_CHANGED","LOCK_STATE_CHANGED","SERVER_REGION_CHANGED","_MEDIA_SESSION_STARTED","_MEDIA_SESSION_ACTIVE_CHANGED","MEMBERS_ONLY_CHANGED","NO_AUDIO_INPUT","NOISY_MIC","PARTICIPANT_CONN_STATUS_CHANGED","PARTCIPANT_FEATURES_CHANGED","PARTICIPANT_PROPERTY_CHANGED","P2P_STATUS","PROPERTIES_CHANGED","START_MUTED_POLICY_CHANGED","STARTED_MUTED","TALK_WHILE_MUTED","TRACK_ADDED","TRACK_AUDIO_LEVEL_CHANGED","TRACK_MUTE_CHANGED","TRACK_REMOVED","USER_JOINED","USER_LEFT","USER_ROLE_CHANGED","USER_STATUS_CHANGED","BOT_TYPE_CHANGED","LOBBY_USER_JOINED","LOBBY_USER_UPDATED","LOBBY_USER_LEFT","global$1","global","self","_typeof","obj","iterator","constructor","_toConsumableArray","arr","Array","isArray","arr2","length","_arrayWithoutHoles","iter","toString","from","_iterableToArray","TypeError","_nonIterableSpread","safe_add","x","y","lsw","str2binl","str","Error","bin","charCodeAt","md5_cmn","q","a","b","num","cnt","md5_ff","md5_gg","md5_hh","md5_ii","core_md5","len","olda","oldb","oldc","oldd","MD5","hexdigest","binarray","charAt","binl2hex","hash","String","fromCharCode","binl2str","core_sha1","j","olde","w","e","rol","safe_add$1","sha1_ft","sha1_kt","core_hmac_sha1","data","bkey","str2binb","ipad","opad","concat","binb2b64","triplet","binb2str","SHA1","b64_hmac_sha1","b64_sha1","str_hmac_sha1","str_sha1","utils","out","cookies","cookieName","expires","domain","path","cookieObj","isObj","cookieValue","escape","unescape","document","cookie","$build","attrs","Strophe","Builder","$iq","$pres","VERSION","NS","HTTPBIND","BOSH","CLIENT","AUTH","ROSTER","PROFILE","DISCO_INFO","DISCO_ITEMS","MUC","SASL","STREAM","FRAMING","BIND","SESSION","STANZAS","XHTML_IM","XHTML","tags","attributes","css","validTag","tag","validAttribute","attribute","validCSS","style","Status","ERROR","CONNECTING","CONNFAIL","AUTHENTICATING","AUTHFAIL","CONNECTED","DISCONNECTED","DISCONNECTING","ATTACHED","REDIRECT","CONNTIMEOUT","BINDREQUIRED","ErrorCondition","BAD_FORMAT","CONFLICT","MISSING_JID_NODE","NO_AUTH_MECH","UNKNOWN_REASON","LogLevel","DEBUG","INFO","WARN","FATAL","ElementType","NORMAL","TEXT","CDATA","FRAGMENT","TIMEOUT","SECONDARY_TIMEOUT","addNamespace","forEachChild","elem","elemName","func","childNodes","childNode","nodeType","this","isTagEqual","el","tagName","_xmlGenerator","_makeGenerator","doc","undefined","implementation","createDocument","documentMode","_getIEXmlDom","appendChild","createElement","xmlGenerator","docStrings","ActiveXObject","xmlElement","node","arguments","arg","xmlTextNode","sort","attr","setAttribute","k","xmlescape","text","replace","xmlunescape","createTextNode","xmlHtmlNode","html","DOMParser","parseFromString","async","loadXML","getText","nodeValue","copyElement","nodeName","_i","createHtml","toLowerCase","getAttribute","cssText","cssAttrs","split","cssName","cssValue","push","join","_i2","createDocumentFragment","_i3","_i4","escapeNode","unescapeNode","getNodeFromJid","jid","indexOf","getDomainFromJid","bare","getBareJidFromJid","parts","splice","getResourceFromJid","_handleError","stack","fatal","sourceURL","handler","line","message","fileName","lineNumber","log","level","msg","console","error","debug","info","warn","serialize","tree","names","keys","map","result","reduce","getNamedItem","child","_requestId","_connectionPlugins","addConnectionPlugin","ptype","xmlns","nodeTree","up","parentNode","moreattrs","removeAttribute","cnode","impNode","xmlGen","importNode","newElem","h","fragment","innerHTML","xhtml","Handler","type","id","options","matchBare","matchBareFromJid","user","getNamespace","elNamespace","ignoreNamespaceFragment","namespaceMatch","_this","nsMatch","isMatch","elem_type","run","TimedHandler","period","lastCalled","Date","getTime","reset","Connection","service","_this2","proto","protocol","_proto","Websocket","Bosh","features","_sasl_data","do_session","do_bind","timedHandlers","handlers","removeTimeds","removeHandlers","addTimeds","addHandlers","protocolErrorHandlers","_idleTimeout","_disconnectTimeout","authenticated","connected","disconnecting","do_authentication","paused","restored","_data","_uniqueId","_sasl_success_handler","_sasl_failure_handler","_sasl_challenge_handler","maxRetries","setTimeout","_onIdle","registerSASLMechanisms","mechanisms","F","init","_reset","_requests","pause","resume","getUniqueId","suffix","uuid","Math","random","addProtocolErrorHandler","status_code","callback","connect","pass","wait","hold","route","authcid","authzid","servtype","connect_callback","_changeConnectStatus","_connect","attach","sid","rid","wind","_attach","restore","_sessionCachingSupported","_restore","JSON","sessionStorage","setItem","removeItem","xmlInput","xmlOutput","rawInput","rawOutput","nextValidRid","send","_queueData","_send","flush","clearTimeout","sendPresence","errback","timeout","_this3","timeoutHandler","addHandler","stanza","deleteTimedHandler","addTimedHandler","deleteHandler","sendIQ","_this4","iqtype","element","_sendRestart","_this5","thand","handRef","hand","SASLAnonymous","SASLExternal","SASLMD5","SASLOAuthBearer","SASLXOAuth2","SASLPlain","SASLSHA1","forEach","registerSASLMechanism","mechanism","disconnect","reason","pres","_addSysTimedHandler","_onDisconnectTimeout","_disconnect","_abortAllRequests","_doDisconnect","status","condition","plugin","statusChanged","err","_dataRecv","req","raw","_this6","_reqToData","strip","pop","_emptyQueue","cond","conflict","getElementsByTagName","UNKOWN_REASON","newList","_i5","_hand","_connect_cb","_callback","bodyWrap","getElementsByTagNameNS","matched","mech","authenticate","_no_auth_received","sortMechanismsByPriority","higher","priority","swap","_attemptSASLAuth","_attemptLegacyAuth","mechanism_found","test","_addSysHandler","_sasl_success_cb","_sasl_failure_cb","_sasl_challenge_cb","_sasl_mechanism","onStart","request_auth_exchange","isClientFirst","response","onChallenge","btoa","challenge","atob","_onLegacyAuthIQResult","iq","_auth2_cb","_this7","serverSignature","matches","match","onSuccess","streamfeature_handlers","wrapper","_onStreamFeaturesAfterSASL","explicitResourceBinding","_onResourceBindResultIQ","resource","jidNode","_establishSession","_onSessionResultIQ","onFailure","_this8","now","_i6","_thand","SASLMechanism","connection","_connection","auth_str","test_cnonce","cnonce","nonce","salt","Hi","U","U_old","responseText","authMessage","attribMatch","substr","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","querySelector","textContent","XMLHttpRequest","overrideMimeType","onreadystatechange","_conn","floor","errors","inactivity","lastResponseHeaders","_buildBody","keepalive","_cacheSession","body","_onRequestStateChange","_throttledRequestHandler","session","parse","getItem","stringify","typ","parseInt","_sendTerminate","_callProtocolErrorHandlers","reqStatus","_getRequestStatus","err_callback","HTTP","_hitError","time_elapsed","def","readyState","getAllResponseHeaders","valid_request","too_many_retries","_removeRequest","reqIs0","_restartRequest","Number","_processRequest","primary_timeout","isNaN","secondary_timeout","server_error","content_type","contentType","open","sync","setRequestHeader","withCredentials","e2","sendFunc","customHeaders","headers","header","backoff","min","pow","abs","Strophe$2","$build$2","new_service","location","pathname","_buildStream","_check_streamerror","connectstatus","errorString","_closeSocket","socket","WebSocket","onopen","_onOpen","onerror","_onError","onclose","_onClose","onmessage","_connect_cb_wrapper","_handleStreamStart","ver","streamStart","parsedMessage","see_uri","string","_streamWrap","_onMessage","CLOSED","close","closeString","code","rawStanza","search","firstChild","start","startString","$msg","BrowserCapabilities","Logger","require","LogCollector","idLoggers","loggers","curLevel","levels","TRACE","addGlobalTransport","transport","removeGlobalTransport","setGlobalOptions","getLogger","transports","logger","setLogLevelById","setLevel","setLogLevel","__filename","_instances","isCallstatsLoaded","_initCallStatsBackend","CallStats","isBackendInitialized","initBackend","callStatsID","callStatsSecret","userName","aliasName","applicationName","getWiFiStatsMethod","confID","siteID","Statistics","xmpp","rtpStatsMap","Map","eventEmitter","EventEmitter","callStatsIntegrationEnabled","enableCallStats","disableThirdPartyRequests","callStatsApplicationLogsDisabled","browser","isReactNative","ScriptUtil","loadScript","customScriptUrl","CALLSTATS_SCRIPT_URL","loadCallStatsAPI","callsStatsInstances","instances","add","audioLevelsEnabled","disableAudioLevels","pcStatsInterval","audioLevelsInterval","longTasksStatsInterval","analytics","Set","startRemoteStats","peerconnection","stopRemoteStats","rtpStats","RTPStats","set","localStats","startLocalStats","stream","LocalStats","addAudioLevelListener","listener","on","StatisticsEvents","removeAudioLevelListener","removeListener","addBeforeDisposedListener","removeBeforeDisposedListener","addConnectionStatsListener","removeConnectionStatsListener","addByteSentStatsListener","removeByteSentStatsListener","addLongTasksStatsListener","attachLongTasksStats","conference","supportsPerformanceObserver","performanceObserverStats","PerformanceObserverStats","JitsiConferenceEvents","startObserver","stopObserver","getLongTasksStats","removeLongTasksStatsListener","dispose","size","emit","callStats","values","stopCallStats","tpc","tpcId","_stopRemoteStats","removeAllListeners","delete","stopLocalStats","stop","startCallStats","remoteUserID","has","newInstance","_getAllCallStatsInstances","csInstances","statistics","cs","callStatsInstance","sendTerminateEvent","isCallstatsEnabled","sendConnectionResumeOrHoldEvent","isResume","instance","sendResumeOrHoldEvent","sendIceConnectionFailedEvent","sendMuteEvent","muted","sendScreenSharingEvent","ssrc","sendDominantSpeakerEvent","roomJid","sendActiveDeviceListEvent","devicesData","globalSet","associateStreamWithVideoTag","isLocal","userId","usageLabel","containerId","sendGetUserMediaFailed","JitsiTrackError","gum","constraintName","constraints","formatJitsiTrackErrorForCallStats","sendCreateOfferFailed","sendCreateAnswerFailed","sendSetLocalDescFailed","sendSetRemoteDescFailed","sendAddIceCandidateFailed","sendLog","globalSubSet","stats","next","csPerStats","sendApplicationLog","sendFeedback","overall","comment","sendEvent","FEEDBACK","rating","LOCAL_JID","reportGlobalError","sendAnalyticsAndLog","event","properties","eventToLog","sendAnalytics","eventName","AUDIO","PRESENTER","VIDEO","CREATE_ANSWER_FAILED","CREATE_OFFER_FAILED","DATA_CHANNEL_OPEN","ENDPOINT_CONN_STATUS_CHANGED","LASTN_ENDPOINT_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","LOCAL_UFRAG_CHANGED","REMOTE_UFRAG_CHANGED","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","ICE_DURATION","ICE_ESTABLISHMENT_DURATION_DIFF","ICE_STATE_CHANGED","NO_BYTES_SENT","TRACK_UNMUTED","createBridgeDownEvent","action","actionSubject","createConnectionFailedEvent","errorType","errorMessage","details","createConferenceEvent","source","createConnectionStageReachedEvent","stage","createE2eRttEvent","participantId","region","rtt","createFocusLeftEvent","createGetUserMediaEvent","createParticipantConnectionStatusEvent","createJingleEvent","createNoDataFromSourceEvent","mediaType","createP2PEvent","createRemotelyMutedEvent","createRtpStatsEvent","createTransportStatsEvent","createAudioOutputProblemEvent","userID","localAudioLevels","remoteAudioLevels","createBridgeChannelClosedEvent","createTtfmEvent","SDPUtil","filterSpecialChars","iceparams","mediadesc","sessiondesc","pwd","ufrag","findLine","parseICEUfrag","parseICEPwd","substring","buildICEUfrag","frag","buildICEPwd","parseMID","parseMLine","media","shift","port","fmt","buildMLine","mline","parseRTPMap","clockrate","channels","parseSCTPMap","buildRTPMap","parseCrypto","parseFingerprint","fingerprint","parseFmtp","parseICECandidate","candidate","elems","foundation","component","ip","generation","tcptype","network","buildICECandidate","cand","hasOwnAttribute","parseSSRC","desc","lines","parseRTCPFB","pt","params","parseExtmap","direction","uri","haystack","needle","sessionpart","findLines","needles","candidateToJingle","candidateFromJingle","isFirefox","parsePrimaryVideoSsrc","videoMLine","numSsrcs","ssrcs","ssrcInfo","filter","index","array","numGroups","ssrcGroups","primarySsrc","fidGroup","find","group","semantics","simGroup","generateSsrc","RandomUtil","randomInt","getSsrcAttribute","mLine","attributeName","ssrcLine","parseGroupSsrcs","ssrcGroup","ssrcStr","getMedia","sdp","getUfrag","ufragLines","startsWith","preferCodec","codecName","matchingPayloadTypes","rtp","codec","payload","payloadTypes","payloads","reverse","payloadIndex","unshift","stripCodec","highProfile","h264Pts","removePts","stripH264HighCodec","CodecMimeType","H264","fmtp","item","config","includes","rtxApts","rtxPts","keepPts","rtcpFb","usesAdapter","OLD_GUM_DEFAULT_DEVICES","DEFAULT_CONSTRAINTS","video","height","ideal","max","SS_DEFAULT_FRAME_RATE","audioOutputDeviceId","audioOutputChanged","disableAP","disableAEC","disableNS","disableAGC","disableHPF","featureDetectionAudioEl","isAudioOutputDeviceChangeAvailable","setSinkId","availableDevices","availableDevicesPollTimer","emptyFuncton","getConstraints","um","audio","isNewStyleConstraintsSupported","isSafari","mandatory","optional","cameraDeviceId","deviceId","sourceId","facingMode","CameraFacingMode","USER","minFps","maxFps","fps","minFrameRate","maxFrameRate","resolution","Resolutions","width","minWidth","minHeight","maxWidth","maxHeight","setResolutionConstraints","micDeviceId","echoCancellation","googEchoCancellation","googAutoGainControl","googNoiseSuppression","googHighpassFilter","googNoiseSuppression2","googEchoCancellation2","googAutoGainControl2","isChrome","getSSConstraints","mozMediaSource","mediaSource","frameRate","errmsg","GlobalOnErrorHandler","callErrorHandler","isElectron","screenShareAudio","desktopStream","chromeMediaSource","bandwidth","firefox_fake_device","fake","screen","chromeMediaSourceId","getTrackSSConstraints","desktopSharingFrameRate","updateGrantedPermissions","audioTracksReceived","Boolean","getAudioTracks","videoTracksReceived","getVideoTracks","grantedPermissions","RTCEvents","sendDeviceListToAnalytics","deviceList","audioInputDeviceCount","kind","audioOutputDeviceCount","videoInputDeviceCount","videoOutputDeviceCount","device","groupId","label","updateKnownDevices","pds","newDevices","mediaDeviceInfoToJSON","facing","compareAvailableMediaDevices","slice","RTCUtils","Listenable","super","clearInterval","enumerateDevices","navigator","mediaDevices","then","devices","initEnumerateDevicesWithCallback","usesNewGumFlow","RTCPeerConnectionType","RTCPeerConnection","attachMediaStream","origAttachMediaStream","srcObject","res","apply","rtcUtils","isDeviceChangeAvailable","getAudioOutputDevice","catch","ex","callUnhandledRejectionHandler","promise","getStreamID","getTrackID","_initPCConstraints","screenObtainer","getUserMediaWithConstraints","isDeviceListAvailable","ds","supportsDeviceChangeEvent","addEventListener","setInterval","pcConstraints","isChromiumBased","googHighStartBitrate","googPayloadPadding","googScreencastMinBitrate","googCpuOveruseDetection","googCpuOveruseEncodeUsage","googCpuUnderuseThreshold","googCpuOveruseThreshold","p2pPcConstraints","_getUserMedia","umDevices","Promise","resolve","reject","gumTimeout","timeoutExpired","JitsiTrackErrors","getUserMedia","jitsiError","_newGetDesktopMedia","isSupported","obtainStream","_parseDesktopSharingOptions","obtainAudioAndVideoPermissions","_getAudioAndVideoStreams","streams","audioStream","videoStream","audioVideo","audioTracks","MediaStream","addTrack","videoTracks","release","desktop","sourceType","track","MediaType","videoType","VideoType","DESKTOP","CAMERA","handleLocalStream","requestingDesktop","avStream","missingTracks","_getMissingTracks","stopMediaStream","desktopSharingSourceDevice","requestedDevices","missingDevices","audioDeviceRequested","videoDeviceRequested","desktopSharingSources","gumOptions","trackOptions","newObtainAudioAndVideoPermissions","otherOptions","mediaStreamsMetaData","maybeRequestDesktopDevice","matchingDevice","getTracks","applyConstraints","maybeRequestCaptureDevices","requestedCaptureDevices","clonedeep","autoGainControl","noiseSuppression","newGetConstraints","desktopAudioTracks","desktopAudioStream","desktopVideoTracks","desktopVideoStream","effects","deviceType","mediaStream","isDesktopSharingEnabled","setAudioOutputDevice","getCurrentlyAvailableMediaDevices","getEventDataForActiveDevice","deviceData","setSuspendVideo","enable","googSuspendBelowMinBitrate","oldOnErrorHandler","oldOnUnhandledRejection","onunhandledrejection","args","errHandler","peerConnectionIdCounter","rtcTrackIdCounter","createLocalTracks","tracksInfo","newTracks","trackInfo","safeCounterIncrement","localTrack","JitsiLocalTrack","rtcId","_newCreateLocalTracks","mediaStreamMetaData","metaData","getSettings","RTC","peerConnections","localTracks","_channel","_lastN","_lastNEndpoints","_senderVideoConstraints","_maxFrameHeight","_selectedEndpoints","_lastNChangeListener","_onLastNChanged","_onDeviceListChanged","_updateAudioOutputForAudioTracks","addListener","destroy","_channelOpenListener","initializeBridgeChannel","wsUrl","BridgeChannel","_senderVideoConstraintsChanged","sendSelectedEndpointsMessage","sendReceiverVideoConstraintMessage","_selectedEndpoint","sendSetLastNMessage","senderVideoConstraints","lastNEndpoints","oldLastNEndpoints","leavingLastNEndpoints","enteringLastNEndpoints","isInLastN","onCallEnded","setReceiverVideoConstraint","maxFrameHeight","isOpen","selectEndpoints","ids","eventType","createPeerConnection","signaling","iceConfig","isP2P","getPCConstraints","abtestSuspendVideo","addPermanentProperties","enableInsertableStreams","encodedInsertableStreams","forceEncodedAudioInsertableStreams","forceEncodedVideoInsertableStreams","supportsSdpSemantics","sdpSemantics","forceTurnRelay","iceTransportPolicy","bundlePolicy","newConnection","TraceablePeerConnection","_removePeerConnection","traceablePeerConnection","addLocalTrack","getLastN","getSenderVideoConstraints","getLocalVideoTrack","localVideo","getLocalTracks","getLocalAudioTrack","localAudio","tracks","getType","getRemoteTracks","remoteTracks","pcRemoteTracks","setAudioMute","mutePromises","audioTrack","mute","unmute","all","removeLocalTrack","pos","removeRemoteTracks","owner","removedTracks","pcRemovedTracks","elSelector","isUserStreamById","streamId","closeBridgeChannel","setAudioLevel","audioLevel","getTrackBySSRC","isAudioTrack","sendChannelMessage","to","sendMessage","setLastN","remoteAudioTracks","setAudioOutput","CONSTRAINT_FAILED","ELECTRON_DESKTOP_PICKER_ERROR","ELECTRON_DESKTOP_PICKER_NOT_FOUND","GENERAL","NOT_FOUND","PERMISSION_DENIED","SCREENSHARING_GENERIC_ERROR","SCREENSHARING_USER_CANCELED","TRACK_IS_DISPOSED","TRACK_NO_STREAM_FOUND","UNSUPPORTED_RESOLUTION","LOCAL_TRACK_STOPPED","TRACK_AUDIO_OUTPUT_CHANGED","TRACK_VIDEOTYPE_CHANGED","NO_DATA_FROM_SOURCE","CHAT_ERROR","CONFERENCE_DESTROYED","CONFERENCE_MAX_USERS","CONNECTION_ERROR","CONFERENCE_RESTARTED","NOT_ALLOWED_ERROR","MEMBERS_ONLY_ERROR","CONFERENCE_ACCESS_DENIED","ICE_FAILED","INCOMPATIBLE_SERVER_VERSIONS","OFFER_ANSWER_FAILED","PASSWORD_NOT_SUPPORTED","VIDEOBRIDGE_NOT_AVAILABLE","ReflectOwnKeys","R","Reflect","ReflectApply","target","receiver","Function","ownKeys","getOwnPropertySymbols","getOwnPropertyNames","NumberIsNaN","_events","_eventsCount","_maxListeners","defaultMaxListeners","checkListener","_getMaxListeners","that","_addListener","prepend","events","existing","warning","newListener","warned","emitter","count","onceWrapper","fired","wrapFn","_onceWrap","state","wrapped","_listeners","unwrap","evlistener","ret","unwrapListeners","arrayClone","listenerCount","copy","RangeError","getPrototypeOf","setMaxListeners","getMaxListeners","doError","er","context","listeners","prependListener","once","prependOnceListener","list","position","originalListener","spliceOne","off","rawListeners","eventNames","TRACK_ERROR_TO_MESSAGE_MAP","constraint","failedConstraintName","getResolutionFromFailedConstraint","SDP","mediaI","failICE","removeTcpCandidates","removeUdpCandidates","getMediaSsrcMap","mediaSSRCs","mediaindex","mid","linessrc","idx","containsSSRC","medias","toJingle","thecreator","assrcline","creator","amidline","rtpmap","afmtpline","fmtpParameters","rtcpFbToJingle","ssrcMap","availableSsrc","ssrcParameters","ssrcSdpLine","kv","v","ridLines","usesRidsForSimulcast","rids","ridLine","ridInfo","extmapLines","extmap","senders","transportToJingle","sctpmap","sctpAttrs","number","setupLine","setup","iceParameters","payloadtype","feedback","rtcpFbFromJingle","feedbackElementTrrInt","each","_","fb","hasAttribute","fromJingle","jingle","sessionId","groups","$","contents","content","jingle2media","sctp","streamCount","payloadType","__","parameter","hdrExt","removeEventListener","DETECTOR_STATE_CHANGE","VAD_NOISY_DEVICE","VAD_REPORT_PUBLISHED","VAD_SCORE_PUBLISHED","VAD_TALK_WHILE_MUTED","AUDIO_LEVEL","BEFORE_DISPOSED","BYTE_SENT_STATS","CONNECTION_STATS","LONG_TASKS_STATS","parser","writer","write","parseFmtpConfig","parseParams","parsePayloads","parseRemoteCandidates","parseImageAttributes","parseSimulcastStreamList","STATUS_AVAILABLE","STATUS_UNDEFINED","STATUS_BUSY","STATE_ON","STATE_OFF","STATE_PENDING","STATE_RETRYING","STATE_FAILED","ERROR_NO_CONNECTION","ERROR_SESSION_EXISTS","CHROME","OPERA","FIREFOX","INTERNET_EXPLORER","SAFARI","NWJS","ELECTRON","REACT_NATIVE","UNKNOWN","bowserNameToJitsiName","_detectElectron","userAgent","version","_detectNWJS","_detectReactNative","product","_detect","bowser","browserInfo","detectors","getBrowserName","getBrowserVersion","_detectChromiumBased","BrowserDetection","_bowser","Bowser","getParser","detectedBrowserInfo","_name","_version","getName","isOpera","isIExplorer","isNWJS","getVersion","_checkCondition","checkTree","satisfies","isVersionGreaterThan","isVersionLessThan","isVersionEqualTo","DummyLocalStorage","clear","_storage","keyName","keyValue","JitsiLocalStorage","localStorage","_localStorageDisabled","ignore","isLocalStorageDisabled","dontEmitChangedEvent","localStorageContent","jitsiLocalStorage","WRONG_STATE","DISPLAY_NAME_REQUIRED","VP8","VP9","DEFAULT_STUN_SERVERS","urls","JITSI_MEET_MUC_TYPE","FEATURE_JIGASI","FEATURE_E2EE","XMPP","token","disconnectInProgress","connectionTimes","authenticatedUser","initStropheUtil","initStropheLogger","xmppPing","hosts","enableWebsocketResume","serviceUrl","shard","websocketKeepAlive","websocketKeepAliveUrl","XmppConnection","createConnection","bosh","deploymentInfo","Events","CONN_SHARD_CHANGED","shard_changed","suspend_time","ping","getPingSuspendTime","time_since_last_success","getTimeSinceLastSuccess","JitsiConnectionEvents","JitsiConnectionErrors","_initStrophePlugins","caps","Caps","clientNode","initFeaturesList","ev","addFeature","disableRtx","enableOpusRed","supportsAudioRed","enableRemb","enableTcc","enableLipSync","rayo","E2EEncryption","getConnection","connectionHandler","credentials","performance","statusStr","getStatusString","XMPPEvents","getStunAndTurnCredentials","_resetState","getFeaturesAndIdentities","identities","PING","identity","speakerStatsComponentAddress","conferenceDurationComponentAddress","lobbySupported","f","fr","endsWith","_onPrivateMessage","password","anonymousConnectionFailed","connectionFailed","lastErrorMsg","stopInterval","wasIntentionalDisconnect","errMsg","_getConnectionFailedReasonDetails","lastErrorStatus","getLastErrorStatus","attaching","anonymousdomain","configDomain","hostname","createRoom","roomName","onCreateResource","roomjid","customDomain","muc","mucNickname","randomHexString","emuc","getJid","getJingleLog","getLog","getXmppLog","dial","pingDomain","getSessions","sessions","disconnectListener","_cleanupXmppConnection","isUsingWebSocket","evType","sendUnavailableBeacon","jvb","iceServers","p2p","p2pStunServers","stunServers","MucConnectionPlugin","JingleConnectionPlugin","RayoConnectionPlugin","headersArr","trim","room","tryParseJSONAndVerify","jsonString","json","jsonMessage","parsedJson","users","created_timestamp","nextValue","MAX_SAFE_INTEGER","calculateAverage","valueArray","filterPositiveValues","RunningAverage","average","addNext","getAverage","_callStatsUserName","_machineId","_p8","externalStorage","username","UsernameGenerator","generateUsername","generateCallStatsUserName","jitsiMeetId","generateJitsiMeetId","parsePrimarySSRC","parseSecondarySSRC","_getSSRCCount","MLineWrap","getSSRCAttrValue","ssrcNumber","attrName","ssrcObj","removeSSRC","ssrcNum","addSSRCAttribute","findGroup","findGroups","findGroupByPrimarySSRC","primarySSRC","findSSRCByMSID","msid","getSSRCCount","containsAnySSRCGroups","getPrimaryVideoSsrc","fecGroup","getRtxSSRC","getSSRCs","getPrimaryVideoSSRCs","videoSSRCs","ssrcGroupInfo","secondarySsrc","dumpSSRCGroups","removeGroupsWithSSRC","groupInfo","removeGroupsBySemantics","replaceSSRC","oldSSRC","newSSRC","addSSRCGroup","SdpTransformWrap","rawSDP","parsedSDP","transform","selectMedia","selectedMLine","toRawSDP","LOCAL_STATS_UPDATED","REMOTE_STATS_UPDATED","PENDING","ACTIVE","ENDED","ACTIONS","ACCEPT","INITIATE","TERMINATE","UNAVAILABLE","order","wrtcFuncNames","fabricEvent","reportType","_fabrics","backend","backendInitialized","atLeastOneFabric","defaultInstance","fabrics","hasFabric","_addNewFabric","_emptyReportQueue","csInstance","defaultConfID","defaultPC","report","reportsQueue","errorData","_reportError","pc","eventData","sendFabricEvent","associateMstWithUserID","callStatsId","_error","reportError","theBackend","tryCatchMethods","methodName","originalMethod","theArguments","debugMethods","originalReportError","exception","CallStatsBackend","callstats","configParams","_traceAndCatchBackendCalls","applicationVersion","initialize","_initCallback","attachWifiStatsHandler","addresses","_reportEvent","conferenceID","sendUserFeedback","fabricAttributes","remoteEndpointType","endpointType","peer","server","addNewFabric","fabricUsage","multiplex","_addNewFabricCallback","success","streamEndpointId","fabricTerminated","randomElement","randomHexDigit","randomAlphanumStr","getConnectionPluginDefinition","base","ConnectionPluginListenable","CONNECTION_DROPPED_ERROR","OTHER_ERROR","SERVER_ERROR","argsTag","mapTag","objectTag","setTag","reIsHostCtor","reIsUint","typedArrayTags","freeGlobal","freeSelf","freeExports","freeModule","moduleExports","freeProcess","process","nodeUtil","binding","nodeIsTypedArray","isTypedArray","arraySome","predicate","mapToArray","setToArray","uid","arrayProto","funcProto","objectProto","coreJsData","funcToString","maskSrcKey","exec","IE_PROTO","nativeObjectToString","reIsNative","RegExp","Buffer","Uint8Array","propertyIsEnumerable","symToStringTag","nativeGetSymbols","nativeIsBuffer","isBuffer","nativeKeys","DataView","getNative","WeakMap","nativeCreate","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","valueOf","Hash","entries","entry","ListCache","MapCache","SetCache","__data__","Stack","arrayLikeKeys","inherited","isArr","isArg","isArguments","isBuff","isType","skipIndexes","iteratee","baseTimes","isIndex","assocIndexOf","eq","baseGetTag","isOwn","unmasked","getRawTag","objectToString","baseIsArguments","isObjectLike","baseIsEqual","other","bitmask","customizer","equalFunc","objIsArr","othIsArr","objTag","getTag","othTag","objIsObj","othIsObj","isSameTag","equalArrays","byteLength","byteOffset","buffer","convert","isPartial","stacked","equalByTag","objIsWrapped","othIsWrapped","objUnwrapped","othUnwrapped","objProps","getAllKeys","objLength","othLength","skipCtor","objValue","othValue","compared","objCtor","othCtor","equalObjects","baseIsEqualDeep","baseIsNative","isObject","isMasked","isFunction","baseKeys","Ctor","arrLength","seen","arrValue","othIndex","keysFunc","symbolsFunc","offset","arrayPush","baseGetAllKeys","getSymbols","getMapData","getValue","pairs","LARGE_ARRAY_SIZE","resIndex","arrayFilter","symbol","ArrayBuffer","ctorString","isLength","baseUnary","CONN_STATUS_CHANGED","_options","pingOptions","_stropheConn","_usesWebsocket","_lastSuccessTracker","LastSuccessTracker","startTracking","_resumeTask","ResumeTask","_deferredIQs","PingConnectionPlugin","getTimeSinceLastServerResponse","onPingThresholdExceeded","_onPingErrorThresholdExceeded","_oneSuccessfulConnect","websocket","_status","OPEN","disco","_stropheConnectionCb","targetCallback","blockCallback","_maybeEnableStreamResume","_keepAliveAndCheckShard","_maybeStartWSKeepAlive","_processDeferredIQs","cancel","startInterval","_tryResumingConnection","_wsKeepAlive","_clearDeferredIQs","deferred","closeWebsocket","streamManagement","getResumeToken","intervalWithJitter","url","fetch","responseShard","timeLeft","sendIQ2","sendBeacon","schedule","PEER_MUTED_CHANGED","PEER_VIDEO_TYPE_CHANGED","funcTag","genTag","reFlags","cloneableTags","addMapEntry","pair","addSetEntry","arrayReduce","accumulator","initAccum","isHostObject","overArg","getPrototype","objectCreate","isArrayLike","isArrayLikeObject","assignValue","baseClone","isDeep","isFull","input","initCloneArray","copyArray","isFunc","cloneBuffer","isPrototype","initCloneObject","copyObject","copySymbols","baseAssign","cloneFunc","cloneArrayBuffer","dataView","cloneDataView","typedArray","cloneTypedArray","cloneMap","regexp","lastIndex","cloneRegExp","cloneSet","initCloneByTag","props","arrayEach","subValue","arrayBuffer","newValue","cache","ENVIRONMENT","g","gumFunction","ScreenObtainer","_createObtainStreamMethod","JitsiMeetNW","obtainDesktopStream","obtainScreenOnElectron","supportsGetDisplayMedia","obtainScreenFromGetDisplayMediaRN","obtainScreenFromGetDisplayMedia","JitsiMeetScreenObtainer","openDesktopPicker","streamType","onGetStreamResponse","errorCallback","getDisplayMedia","cursor","applyConstraintsPromise","videoTrack","errorDetails","errorName","errorMsg","errorStack","currentExecutingScript","src","relativeURL","loadCallback","script","referenceNode","scriptEl","scriptSrc","baseScriptSrc","lastIndexOf","onload","insertBefore","ParticipantConnectionStatus","INACTIVE","INTERRUPTED","RESTORING","ParticipantConnectionStatusHandler","isConnectionActiveByJvb","isRestoringTimedout","isVideoMuted","isVideoTrackFrozen","supportsVideoMuteOnConnInterrupted","rtc","trackTimers","connStatusFromJvb","outOfLastNTimeout","rtcMuteTimeout","rtcMutedTimestamp","enteredLastNTimestamp","restoringTimers","connectionStatusMap","_getVideoFrozenTimeout","_onEndpointConnStatusChanged","onEndpointConnStatusChanged","_onP2PStatus","refreshConnectionStatusForAll","_onUserLeft","onUserLeft","_onTrackRtcMuted","onTrackRtcMuted","_onTrackRtcUnmuted","onTrackRtcUnmuted","_onRemoteTrackAdded","onRemoteTrackAdded","_onRemoteTrackRemoved","onRemoteTrackRemoved","_onSignallingMuteChanged","onSignallingMuteChanged","_onTrackVideoTypeChanged","onTrackVideoTypeChanged","_onLastNValueChanged","participantIds","clearRtcMutedTimestamp","endpointId","isActive","myUserId","figureOutConnectionStatus","_changeConnectionStatus","participant","newStatus","getConnectionStatus","getId","_setConnectionStatus","remoteTrack","getParticipantId","JitsiTrackEvents","hasAnyVideoRTCMuted","hasAnyVideoTrackWebRTCMuted","participants","getParticipants","getParticipantById","inP2PMode","isP2PActive","isRestoringTimedOut","_isRestoringTimedout","audioOnlyMode","isConnActiveByJvb","newState","_getNewStateForP2PMode","_getNewStateForJvbMode","_clearRestoringTimer","oldConnectionStatus","connectionStatus","nowMs","maybeSendParticipantConnectionStatusEvent","startedMs","getTracksByMediaType","participantConnectionStatus","leavingLastN","enteringLastN","rTimer","isMuted","NETWORK_INFO_EVENT","NetworkInfo","_current","isOnline","updateNetworkInfo","networkInfo","SDPUtils","localCName","generateIdentifier","splitLines","blob","splitSections","part","getDescription","sections","getMediaSections","matchPrefix","prefix","parseCandidate","address","relatedAddress","relatedPort","tcpType","usernameFragment","writeCandidate","toUpperCase","parseIceOptions","parseRtpMap","parsed","clockRate","numChannels","writeRtpMap","preferredPayloadType","writeExtmap","headerExtension","preferredId","writeFmtp","parameters","param","parseRtcpFb","writeRtcpFb","rtcpFeedback","parseSsrcMedia","sp","colon","parseSsrcGroup","getMid","mediaSection","algorithm","getDtlsParameters","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","writeIceParameters","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","rtpmapline","fmtps","writeRtpDescription","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","flows","apt","encParam","codecPayloadType","rtx","fec","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","cname","rsize","reducedSize","compound","mux","parseMsid","spec","planB","msidParts","parseSctpDescription","maxMessageSize","maxSizeLine","sctpPort","writeSctpDescription","output","generateSessionId","writeSessionBoilerplate","sessId","sessVer","sessUser","writeMediaSection","transceiver","iceGatherer","getLocalParameters","dtlsTransport","rtpSender","rtpReceiver","sendEncodingParameters","getDirection","getKind","isRejected","parseOLine","sessionVersion","netType","addressType","isValidSDP","getFocusRecordingUpdate","presence","jibriStatus","initiator","recordingMode","sessionID","getHiddenDomainUpdate","liveStreamViewURLContainer","liveStreamViewURL","modeContainer","sessionIDContainer","getSessionIdFromIq","jibri","getSessionId","sessionIdContainer","isFromFocus","PERMISSION_PROMPT_IS_SHOWN","OLM_MESSAGE_TYPES","kOlmData","OlmAdapterEvents","OLM_ID_KEY_READY","PARTICIPANT_E2EE_CHANNEL_READY","PARTICIPANT_KEY_UPDATED","OlmAdapter","_conf","_init","Deferred","_key","_keyIndex","_reqs","_bootstrapOlm","_onEndpointMessageReceived","_onConferenceJoined","_onConferenceLeft","_onParticipantLeft","Olm","promises","pId","olmData","_getParticipantOlmData","uuidv4","olm","ciphertext","_encryptKeyInfo","setRejectTimeout","_sendMessage","allSettled","_olmAccount","Account","idKeys","identity_keys","_idKey","curve25519","get_library_version","keyInfo","base64js","fromByteArray","keyIndex","encrypt","_sendSessionInit","free","_sendError","Session","create_outbound","idKey","otKey","ack","pendingSessionUuid","create_inbound","remove_one_time_keys","decrypt","safeJsonParse","toByteArray","lastKey","isEqual","generate_one_time_keys","otKeys","one_time_keys","mark_keys_as_published","SIGNATURE_OPTIONS","namedCurve","_conferenceJoined","_enabled","_initialized","_signatureKeyPair","_e2eeCtx","E2EEContext","_olmAdapter","_ratchetKey","debounce","_ratchetKeyImpl","_rotateKey","_rotateKeyImpl","_onParticipantPropertyChanged","_onParticipantJoined","_onMediaSessionStarted","_onLocalTrackAdded","_setupReceiverE2EEForTrack","_trackMuteChanged","_onOlmIdKeyReady","_onParticipantE2EEChannelReady","_onParticipantKeyUpdated","supportsInsertableStreams","testing","disableE2EE","isEnabled","enabled","crypto","subtle","generateKey","setSignatureKey","privateKey","serializedSigningKey","exportKey","publicKey","setLocalParticipantProperty","_restartMediaSessions","_generateKey","updateKey","setKey","getRandomValues","_getMediaSessions","_setupSenderE2EEForTrack","cleanup","oldValue","importedKey","importKey","crv","key_ops","material","newKey","ratchet","updateCurrentKey","findReceiverForTrack","handleReceiver","sender","findSenderForTrack","handleSender","doesVideoMuteByStreamRemove","isVideoTrack","JingleSessionPC","JingleSession","jingleContents","videoContents","maxFrameHeightSel","localJid","remoteJid","mediaConstraints","isInitiator","_bridgeSessionId","_cachedOldLocalSdp","_cachedNewLocalSdp","_iceCheckingStartedTimestamp","_gatheringStartedTimestamp","localRecvMaxFrameHeight","_localVideoActive","_remoteVideoActive","_gatheringReported","lasticecandidate","closed","remoteRecvMaxFrameHeight","signalingLayer","SignalingLayerImpl","modificationQueue","AsyncQueue","wasConnected","establishmentDuration","_xmppListeners","onXmppStatusChanged","_removeSenderVideoConstraintsChangeListener","_assertNotEnded","JingleSessionState","doInitialize","isReconnect","wasstable","webrtcIceUdpDisable","webrtcIceTcpDisable","pcOptions","gatherStats","maxstats","capScreenshareBitrate","videoQuality","disableSimulcast","_abtestSuspendVideoEnabled","preferH264","disableH264","startSilent","onicecandidate","phase","sendIceCandidate","onsignalingstatechange","signalingState","connectionState","oniceconnectionstatechange","iceConnectionState","reconnect","usesTerminateForRestart","enableIceRestart","supportsRestartByTerminate","iceStarted","onnegotiationneeded","remoteDescription","usesUnifiedPlan","workFunction","finishedCallback","oldSdp","localDescription","_renegotiate","newSdp","notifyMySSRCUpdate","setChatRoom","enableLayerSuspension","MediaSessionEvents","REMOTE_VIDEO_CONSTRAINTS_CHANGED","getRemoteRecvMaxFrameHeight","idealHeight","localSDP","ice","sdpMLineIndex","jcand","errorMesssage","usedrip","dripContainer","sendIceCandidates","candidates","initiatorJid","cands","sdpMid","fingerprintLine","tmp","required","newJingleErrorHandler","sendIceFailedNotification","sessionInfo","addIceCandidates","iceCandidates","rtcCandidate","RTCIceCandidate","outerHTML","iceCandidate","addIceCandidate","readSsrcInfo","ssrcElement","setSSRCOwner","i3","ssrcInfoElement","generateRecvonlySsrc","getConfiguredVideoCodec","acceptOffer","jingleOffer","failure","setOfferAnswerCycle","sendSessionAccept","invite","addTracks","createOffer","offerSdp","setLocalDescription","sendSessionInitiate","setAnswer","jingleAnswer","jingleOfferAnswerIq","newRemoteSdp","_processNewJingleOfferIq","oldLocalSdp","bridgeSessionId","sendContentModify","newLocalSdp","setVideoCodecs","preferred","disabled","current","replaceTransport","jingleOfferElem","enableForcedReload","sendTransportAccept","originalOffer","clone","remove","newFingerprint","accept","responder","responderJid","sessionModify","transportAccept","medialines","sendTransportReject","transportReject","setSenderMaxBitrates","setMaxBitRate","setSenderVideoConstraint","videoActive","setMediaTransferActive","setSenderVideoDegradationPreference","terminate","sendSessionTerminate","sessionTerminate","reasonDescription","restart","requestRestart","onTerminated","reasonCondition","reasonText","_parseSsrcInfoFromSourceAdd","sourceAddElem","currentRemoteSdp","addSsrcInfo","i1","i2","addRemoteStream","_addOrRemoveRemoteStream","removeRemoteStream","isAdd","logPrefix","addOrRemoveSsrcInfo","_parseSsrcInfoFromSourceRemove","_processRemoteAddSource","_processRemoteRemoveSource","offerIq","remoteSdp","removeSsrcInfo","usesPlanB","findIndex","optionalRemoteSdp","RTCSessionDescription","_initiatorRenegotiate","_responderRenegotiate","setRemoteDescription","createAnswer","answer","offer","replaceTrack","oldTrack","newTrack","clearRecvonlySsrc","shouldRenegotiate","newLocalSDP","sourceRemoveElem","ssrcLines","_verifyNoSSRCChanged","operationName","oldSDP","currentLocalSDP","sdpDiff","SDPDiffer","addedMedia","getNewMedia","removedMedia","addTrackAsUnmute","_addRemoveTrackAsMuteUnmute","removeTrackAsMute","isMute","oldLocalSDP","removeTrackMute","addTrackUnmute","audioActive","logAudioStr","logVideoStr","isSessionActive","audioActiveChanged","setAudioTransferActive","pcVideoActiveChanged","setVideoTransferActive","modifyContents","newVideoSenders","parseVideoSenders","newMaxFrameHeight","parseMaxFrameHeight","_modifyRemoteVideoActive","remoteVideoSenders","isRemoteVideoActive","newSDP","sdpDiffer","request","failureCb","errResponse","errorElSel","errorReasonSel","errorMsgSel","getIceConnectionState","getConnectionState","finishCallback","shutdown","abTesting","enableSuspendVideoTest","_getInitiatorJid","integerHash","b64","lens","getLens","validLen","placeHoldersLen","Arr","_byteLength","curByte","revLookup","uint8","extraBytes","len2","encodeChunk","lookup","end","arrayEquals","array1","array2","equals","mySDP","otherSDP","myMedias","othersMedias","newMedia","othersMediaIdx","myMedia","othersMedia","otherSsrcGroup","mySsrcGroup","modify","sdpMediaSsrcs","modified","mediaSsrc","nv","cachedSetTimeout","cachedClearTimeout","defaultSetTimout","defaultClearTimeout","runTimeout","fun","currentQueue","queue","draining","queueIndex","cleanUpNextTick","drainQueue","marker","runClearTimeout","Item","noop","nextTick","title","env","argv","versions","cwd","chdir","dir","umask","AudioContext","webkitAudioContext","LocalStatsCollector","interval","intervalId","intervalMilis","suspend","isLocalStatsSupported","analyser","createAnalyser","smoothingTimeConstant","fftSize","createMediaStreamSource","frequencyBinCount","getByteTimeDomainData","samples","maxVolume","parseFloat","toFixed","timeDomainDataToAudioLevel","newLevel","lastLevel","diff","animateLevel","IDENTITY_PROPERTIES","IDENTITY_PROPERTIES_FOR_COMPARE","ERROR_FEATURE_VERSION_MISMATCH","compareIdentities","some","generateSha","sortedIdentities","accumulatedValue","sortedFeatures","feature","versionToCapabilities","jidToVersion","rooms","externalFeatures","_addChatRoom","_removeChatRoom","CAPS","_handleCaps","_onMucMemberLeft","_removeJidToVersionEntry","submit","external","_generateVersion","_updateRoomWithExternalFeatures","removeFeature","removeFromPresence","children","addToPresence","getFeatures","_getDiscoInfo","sha","receivedNode","category","_fixChatRoomPresenceMap","_notifyVersionChanged","_identities","_features","oldVersion","ON","OFF","trackHandler2Prop","JitsiTrack","streamInactiveHandler","trackMediaType","containers","disposed","_streamInactiveHandler","_setStream","_addMediaStreamInactiveHandler","onended","oninactive","_setHandler","_unregisterHandlers","isWebRTCTrackMuted","isLocalAudioTrack","getOriginalStream","getStreamId","getTrack","getTrackLabel","getTrackId","getUsageLabel","_maybeFireTrackAttached","container","_onTrackAttach","_attachTTFMTracker","detach","_onTrackDetach","isScreenSharing","active","newAudioLevel","supportsReceiverStats","getMSID","trackId","TrackVADEmitter","procNodeSampleRate","vadProcessor","jitsiLocalTrack","_procNodeSampleRate","_vadProcessor","_localTrack","_bufferResidue","Float32Array","_audioContext","createAudioContext","sampleRate","getRequiredPCMFrequency","_vadSampleSize","getSampleLength","_onAudioProcess","_initializeAudioContext","_audioSource","_audioProcessingNode","createScriptProcessor","audioEvent","inData","inputBuffer","getChannelData","completeInData","sampleTimestamp","pcmSample","vadScore","calculateAudioFrameVAD","timestamp","score","pcmData","getDeviceId","_connectAudioGraph","onaudioprocess","destination","_disconnectAudioGraph","_cleanupResources","stopStream","getDeviceLabel","_destroyed","AudioContextImpl","E2E_RTT_CHANGED","clearRejectTimeout","_timeout","ms","SIM_LAYER_RIDS","TPCUtils","videoBitrates","localStreamEncodingsConfig","high","low","scaleResolutionDownBy","standard","ensureCorrectOrderOfSsrcs","parsedSdp","reorderedSsrcs","sources","_getStreamEncodings","isSimulcastOn","insertUnifiedPlanSimulcastReceive","usesSdpMungingForSimulcast","simulcast_03","simulcast","simulcastLine","transceiverInit","sendEncodings","addTransceiver","getTransceivers","addStream","setEncodings","getLocalStreamHeightConstraints","localVideoHeightConstraints","encoding","stopped","localSSRCs","_addedStreams","_extractPrimarySSRC","getParameters","encodings","setParameters","transceivers","updateEncodingsResolution","every","displayName","isLocalStats","_userId","setDisplayName","_isLocalStats","setDominantSpeaker","totalDominantSpeakerTime","_dominantSpeakerStart","_hasLeft","getUserId","getDisplayName","newName","isDominantSpeaker","isNowDominantSpeaker","timeElapsed","getTotalDominantSpeakerTime","total","hasLeft","markAsHasLeft","IDENTITY_UPDATED","WINDOW","JS_MD5_NO_WINDOW","WEB_WORKER","NODE_JS","JS_MD5_NO_NODE_JS","COMMON_JS","JS_MD5_NO_COMMON_JS","AMD","ARRAY_BUFFER","JS_MD5_NO_ARRAY_BUFFER","HEX_CHARS","EXTRA","SHIFT","OUTPUT_TYPES","BASE64_ENCODE_CHAR","blocks","buffer8","Uint32Array","JS_MD5_NO_ARRAY_BUFFER_IS_VIEW","isView","createOutputMethod","outputType","Md5","update","createMethod","method","nodeWrap","eval","nodeMethod","createHash","digest","sharedMemory","h0","h1","h2","h3","bytes","hBytes","finalized","hashed","first","notString","lastByteIndex","finalize","bc","da","hex","base64","v1","v2","v3","base64Str","md5","reTrim","reIsBadHex","reIsBinary","reIsOctal","freeParseInt","nativeMax","nativeMin","toNumber","isSymbol","isBinary","lastArgs","lastThis","maxWait","timerId","lastCallTime","lastInvokeTime","leading","maxing","trailing","invokeFunc","time","thisArg","leadingEdge","timerExpired","shouldInvoke","timeSinceLastCall","trailingEdge","remainingWait","debounced","isInvoking","textEncoder","TextEncoder","deriveBits","encode","keyBytes","_setEffectInProgress","effect","_startStreamEffect","maxEnabledResolution","_constraints","_prevSetMuted","_facingMode","_trackEnded","_hasSentData","_testDataSent","_realDeviceId","_trackMutedTS","_onDeviceListWillChange","oldRealDeviceId","_setRealDeviceIdFromDeviceList","_onAudioOutputDeviceChanged","_initNoDataFromSourceHandlers","isEnded","_isNoDataFromSourceEventsEnabled","_fireNoDataFromSourceEvent","isReceivingData","storedMSID","_streamEffect","_originalStream","startEffect","_stopStreamEffect","stopEffect","_switchStreamEffect","setEffect","cont","removeTrack","_queueSetMuted","setMuted","_setMuted","logMuteInfo","_removeStreamFromConferenceAsMute","streamOptions","getCameraFacingMode","assign","streamsInfo","streamInfo","_addStreamToConferenceAsUnmute","_sendMuteStatus","_addLocalTrackAsUnmute","successCallback","_removeLocalTrackAsMute","_setConference","_onByteSentStatsReceived","bytesSent","trackSettings","_stopStreamInProgress","_switchCamera","_effectEnabled","VADAudioAnalyser","createVADProcessor","_createVADProcessor","_vadEmitter","_isVADEmitterRunning","_detectionServices","_vadInitTracker","_processVADScore","_trackAdded","_trackRemoved","addVADDetectionService","vadService","detector","_stopVADEmitter","_startVADEmitter","processVADScore","_changeDetectorsMuteState","changeMuteState","vadEmitter","JibriSession","_mode","_setSessionID","setStatus","getError","getID","_sessionID","getInitiator","_initiator","getLiveStreamViewURL","_liveStreamViewURL","getStatus","getTerminator","_terminator","getMode","setError","setLiveStreamViewURL","setInitiator","setTerminator","appData","broadcastId","focusMucJid","_createIQ","recordingXMLUtils","_setErrorFromIq","errorIq","_eventEmitter","_permissions","JitsiMediaDevicesEvents","_logOutputDevice","permissions","_handlePermissionsChange","_permissionsApiSupported","query","onchange","_parsePermissionState","results","supported","permissionStatus","deviceID","isDevicePermissionGranted","isMultipleAudioInputSupported","emitEvent","consoleTransport","globalTransports","transportIdx","globalOptions","getCallerInfo","callerInfo","fileLocation","column","disableCallerInfo","logPrefixes","toISOString","fullLogParts","methods","LOG","webpackPolyfill","deprecate","paths","grammar","reg","format","u","z","rate","subtype","sessionConfig","raddr","dir2","RecordingResult","TrackRecorder","recorder","startTime","startRecorder","trackRecorder","stopRecorder","determineCorrectFileType","MediaRecorder","isTypeSupported","AudioRecorder","jitsiConference","recorders","fileType","isRecording","instantiateTrackRecorder","updateNames","originalStream","mimeType","ondataavailable","dataEvent","recorderToRemove","download","Blob","URL","createObjectURL","href","click","revokeObjectURL","getRecordingResults","getFileType","JitsiConnection","appID","errType","ANALYTICS_CONNECTION_DISCONNECTED","setToken","initJitsiConference","JitsiConference","getConnectionTimes","getLogs","metadata","ua","eventManager","JitsiConferenceEventManager","componentsVersions","ComponentsVersions","jvbJingleSession","lastDominantSpeaker","dtmfManager","somebodySupportsDTMF","authEnabled","startAudioMuted","startVideoMuted","startMutedPolicy","isMutedByFocus","mutedByFocusActor","wasStopped","connectionQuality","ConnectionQuality","avgRtpStatsReporter","AvgRTPStatsReporter","avgRtpStatsN","_audioOutputProblemDetector","AudioOutputProblemDetector","isJvbConnectionInterrupted","speakerStatsCollector","SpeakerStatsCollector","deferredStartP2PTask","delay","backToP2PDelay","isP2PConnectionInterrupted","p2pJingleSession","videoSIPGWHandler","VideoSIPGW","recordingManager","RecordingManager","_conferenceJoinAnalyticsEventSent","isE2EESupported","_e2eEncryption","resourceCreator","isAuthenticatedUser","setupXMPPListeners","codecSettings","disabledCodec","enforcePreferredCodec","jvbCodec","preferredCodec","p2pCodec","codecSelection","CodecSelection","_statsCurrentId","statisticsId","Settings","callStatsUserName","statsId","_onIceConnectionInterrupted","_onIceConnectionRestored","_onIceConnectionEstablished","_updateProperties","_sendConferenceJoinAnalyticsEvent","e2eping","E2ePing","setupRTCListeners","qualityController","QualityController","_peerConnStatusRtcMuteTimeout","_peerConnStatusOutOfLastNTimeout","callStatsThreshold","statisticsDisplayName","callStatsCustomScriptUrl","setupChatRoomListeners","setupStatisticsListeners","enableTalkWhileMuted","_audioAnalyser","vadTalkMutedDetection","VADTalkMutedDetection","DetectionEvents","enableNoisyMicDetection","vadNoiseDetection","VADNoiseDetection","enableNoAudioDetection","_noAudioSignalDetection","NoAudioSignalDetection","hasAudioSignal","channelLastN","jvb121Status","Jvb121EventGenerator","p2pDominantSpeakerDetection","P2PDominantSpeakerDetection","userRegion","getPreferredCodec","_maybeSetSITimeout","authenticateAndUpgradeRole","isJoined","joined","isP2PEnabled","isP2PTestModeEnabled","p2pTestMode","leave","onLocalTrackRemoved","_sendConferenceLeftAnalyticsEvent","_delayedIceFailed","removeXMPPListeners","onMemberLeft","_getActiveMediaSession","isAuthEnabled","isLoggedIn","authIdentity","getAuthLogin","isExternalAuthEnabled","moderator","getExternalAuthUrl","urlForPopup","getPopupLoginUrl","getLoginUrl","getPerformanceStats","longTasksStats","eventId","addCommandListener","command","addPresenceListener","removeCommandListener","removePresenceListener","sendTextMessage","elementName","getFromPresence","sendPrivateTextMessage","sendPrivateMessage","sendCommand","sendCommandOnce","removeCommand","setSubject","subject","isModerator","getTranscriber","transcriber","Transcriber","localAudioTracks","getTranscriptionStatus","transcriptionStatus","_fireAudioLevelChangeEvent","activeTpc","getActivePeerConnection","_fireMuteChangeEvent","actorParticipant","muteParticipant","myroomjid","actorId","muteHandler","audioLevelHandler","_doReplaceTrack","_setupNewTrack","replaceTrackPromises","setVideoMute","addAsUnmutePromises","removeAsMutePromises","getRole","isHidden","hiddenDomain","lock","lockRoom","JitsiConferenceErrors","unlock","selectParticipant","selectParticipants","lastN","isInteger","isVideoActive","getParticipantCount","countHidden","grantOwner","setAffiliation","kickParticipant","kick","_maybeClearSITimeout","_sessionInitiateTimeout","onMemberJoined","nick","statsID","botType","fullJid","JitsiParticipant","_role","_botType","_updateFeatures","_maybeStartOrStopP2P","_onMucJoined","_supportsDTMF","updateDTMFSupport","setProperty","_onMemberBotTypeChanged","botParticipant","onMemberKicked","isSelfPresence","kickedParticipantId","kickedParticipant","onLocalRoleChanged","onUserRoleChanged","onDisplayNameChanged","_displayName","_tracks","onCallAccepted","onTransportInfo","transportInfo","removedTrack","_onIncomingCallP2P","jingleSession","rejectReason","supportsP2P","_shouldBeInP2PMode","_rejectIncomingCall","_acceptP2PIncomingCall","onIncomingCall","isFocus","_acceptJvbIncomingCall","serverRegion","_isE2EEEnabled","_setBridgeChannel","_suspendMediaTransferForJvbConnection","webSocket","forceJvb121","p2pFailed","_stopP2PSession","onSuspendDetected","supportsDTMF","isDTMFSupported","sendTones","tones","duration","peerConnection","startRecording","stopRecording","isSIPCallingSupported","hangup","startTranscriber","stopTranscriber","getPhoneNumber","getPhonePin","getMeetingUniqueId","getMeetingId","setStartMutedPolicy","policy","getStartMutedPolicy","isStartAudioMuted","isStartVideoMuted","removeLocalParticipantProperty","getLocalParticipantProperty","presMap","nodes","prop","overallFeedback","detailedFeedback","getSsrcByTrack","getLocalSSRC","getSSRC","remoteUserId","_isFocus","mucJid","_fireIncompatibleVersionsEvent","sendEndpointMessage","broadcastEndpointMessage","sendThroughVideobridge","messageType","messageToSend","isConnectionInterrupted","_onConferenceRestarted","restartInProgress","_onIceConnectionFailed","IceFailedHandling","remoteID","getStatsID","_addRemoteJVBTracks","_addRemoteTracks","_addRemoteP2PTracks","logName","p2pEstablishmentDuration","jvbEstablishmentDuration","done","forceJVB121Ratio","establishmentDurationDiff","_setP2PStatus","_removeRemoteJVBTracks","changed","getProperty","_maybeClearDeferredStartP2P","_removeRemoteTracks","_removeRemoteP2PTracks","sessionNickname","_resumeMediaTransferForJvbConnection","_startP2PSession","newP2PJingleSession","userLeftEvent","peers","peerCount","shouldBeInP2P","myId","peersId","hasBotPeer","wasP2PEstablished","getP2PConnectionState","startP2PSession","peerJid","stopP2PSession","getSpeakerStats","getStats","setPreferredReceiveMaxFrameHeight","setPreferredSendMaxFrameHeight","createVideoSIPGWSession","sipAddress","VideoSIPGWConstants","meetingId","perf","toggleE2EE","setEnabled","isLobbySupported","getLobby","isMembersOnly","membersOnlyEnabled","enableLobby","disableLobby","disable","joinLobby","email","lobbyDenyAccess","denyAccess","lobbyApproveAccess","approveAccess","xmppListeners","chatRoom","chatRoomForwarder","EventEmitterForwarder","actor","forward","setParticipantPropertyListener","recorderSession","logObject","AuthenticationEvents","txt","myJid","ts","updated","featuresChangedListener","_addConferenceXMPPListener","audioMuted","videoMuted","ignoreStartMuted","createdTimestamp","isTwa","matchMedia","_getChromiumBasedVersion","isUserInteractionRequiredForUnmute","supportsBandwidthStatistics","supportsCodecPreferences","RTCRtpTransceiver","RTCRtpSender","ondevicechange","supportsLocalCandidateRttStatistics","PerformanceObserver","supportedEntryTypes","RTCRtpReceiver","supportsRTTStatistics","createEncodedStreams","createEncodedVideoStreams","ReadableStream","postMessage","getCapabilities","chromium","default","17","getFirstMatch","getSecondMatch","matchAndReturnConst","getWindowsVersionName","getMacOSVersionName","getAndroidVersionName","getVersionPrecision","compareVersions","getBrowserAlias","BROWSER_ALIASES_MAP","getBrowserTypeByAlias","BROWSER_MAP","18","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","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","tv","WindowsPhone","Windows","MacOS","iOS","Android","WebOS","Linux","ChromeOS","PlayStation4","EdgeHTML","Blink","Trident","Presto","Gecko","WebKit","90","configurable","writable","getResult","91","_ua","parsedResult","getUA","parseBrowser","describe","getBrowser","getOS","os","parseOS","getOSName","getOSVersion","getPlatform","platform","parsePlatform","getPlatformType","getEngine","engine","parseEngine","getEngineName","isOS","isPlatform","isBrowser","compareVersion","isEngine","is","92","93","versionName","94","vendor","model","95","analyticsHandlers","permanentProperties","conferenceName","setAnalyticsHandlers","_setUserProperties","_sendEvent","setUserProperties","setConferenceName","_verifyRequiredFields","objectType","containerType","objectId","_maybeCacheEvent","statsInterval","longTasks","maxDuration","performanceStatsInterval","avgRatePerMinute","maxDurationMs","longTaskEventHandler","getEntries","task","observer","observe","buffered","longTasksIntervalId","_lastTimeStamp","KEYS_BY_BROWSER_TYPE","calculatePacketLoss","lostPackets","totalPackets","round","SsrcStats","loss","bitrate","upload","framerate","ConferenceStats","packetLoss","StatsCollector","_browserType","_usesPromiseGetStats","_getStatValue","_defineNewGetStatValueMethod","_defineGetStatValueMethod","baselineAudioLevelsReport","currentAudioLevelsReport","currentStatsReport","previousStatsReport","audioLevelReportHistory","audioLevelsIntervalId","conferenceStats","audioLevelsIntervalMilis","statsIntervalId","statsIntervalMilis","ssrc2stats","browsers","setLoss","setResolution","addBitrate","resetBitrate","setFramerate","setCodec","startAudioLevelStats","audioLevels","getAudioLevels","processNewAudioLevelReport","processAudioLevelReport","processNewStatsReport","processStatsReport","itemStatByKey","stat","keyFromName","getNonNegativeStat","getStatValue","byteSentStats","receiveBandwidth","sendBandwidth","localCandidateType","localip","remoteCandidateType","conferenceStatsTransport","selected","local","localCandidateId","remote","remoteCandidateId","ipAddress","portNumber","candidateType","before","ssrcIds","isRemote","remoteSource","ssrcStats","isDownloadStream","packetsNow","packetsBefore","packetsDiff","packetsLostNow","packetsLostBefore","packetsLostDiff","packetsTotal","packetsLost","bytesReceivedNow","bytesReceivedBefore","bytesReceived","nowBytesTransmitted","timeMs","bitrateReceivedKbps","bitrateSentKbps","_processAndEmitReport","bitrateDownload","bitrateUpload","resolutions","framerates","audioCodec","videoCodec","audioBitrateDownload","audioBitrateUpload","videoBitrateDownload","videoBitrateUpload","userResolutions","userFramerates","codecDesc","userCodecs","avgAudioLevels","localAvgAudioLevels","avgAudioLevel","sum","currentValue","getNonNegativeValue","_calculateBitrate","fieldName","bytesNow","bytesBefore","bytesProcessed","bitrateKbps","nominated","availableIncomingBitrate","availableOutgoingBitrate","remoteUsedCandidate","localUsedCandidate","localIp","networkType","currentRoundTripTime","framerateMean","codecId","codecShortType","frameHeight","frameWidth","framesPerSecond","framesSent","trackIdentifier","getSsrcByTrackId","dest","srcEvent","hidden","_jid","_id","_conference","_hidden","_statsID","_connectionStatus","_properties","_identity","getConference","jitsiTrack","isAudioMuted","_isMediaTypeMuted","queryFeatures","_getFeaturesPromise","getBotType","onLoginSuccessful","roomPassword","rejectPromise","canceled","authenticationError","connectionError","kJitsiE2EE","E2EEcontext","baseUrl","ljm","workerBlob","blobUrl","_worker","Worker","operation","receiverStreams","createEncodedAudioStreams","readableStream","readable","writableStream","senderStreams","stropheConnection","_resumeRetryN","_retryDelay","_cancelResume","_networkOnlineListener","_scheduleResume","_resumeTimeout","getJitterDelay","retryDelay","_resumeConnection","resumeToken","pattern","oldToken","retry","minDelay","LastRequestTracker","_lastSuccess","xmppConnection","originalRawInput","ConnectionPlugin","failedPings","_onPingThresholdExceeded","_getTimeSinceLastServerResponse","pingInterval","pingTimeout","pingThreshold","threshold","pingTimestampsToKeep","pingExecIntervals","_addPingExecutionTimestamp","_lastServerCheck","pingIntervals","maxInterval","previousTS","currentInterval","onPresence","onPresenceUnavailable","onPresenceError","onMessage","onMute","ChatRoom","doLeave","createNonAnonymousRoom","packet2JSON","json2packet","packet","filterNodeFromPresenceJSON","MEMBERS_AFFILIATIONS","members","presHandlers","_removeConnListeners","noBridgeAvailable","Moderator","lobby","Lobby","initPresenceMap","lastPresences","phoneNumber","phonePin","participantPropertyListener","locked","JitsiTranscriptionStatus","xns","addVideoInfoToPresence","presenceUpdateTime","disableFocus","allocateConferenceFocus","onConnStatusChanged","fromJoin","billingId","presenceSyncTime","discoRoomInfo","getInfo","meetingIdValEl","setMeetingId","membersOnly","lobbyRoomField","setLobbyRoomJid","disableDiscoInfo","getForm","form","formSubmit","member","statusEl","hasStatusUpdate","hasVersionUpdate","xElement","mucUserItem","affiliation","getFocusUserJid","isHiddenDomain","fromHiddenDomain","xEl","extractIdentityInformation","userInfo","_extractFeatures","newRole","_initFocus","memberOfThis","displayJids","restartByTerminateSupported","att","phone","pin","processNode","var","focusFeatures","tagHandlers","nickname","onParticipantLeft","skipEvents","onMucMemberLeft","destroySelect","reasonSelect","isKick","membersKeys","actorSelect","actorNick","subjectText","stamp","dateParts","passwordSelect","lobbyRoomNode","lobbyRoomJid","grantIQ","kickIQ","onError","onNotSupported","formsubmit","setMembersOnly","formToSubmit","handlerIdx","getMemberRole","sendVideoInfoPresence","sendAudioInfoPresence","addAudioInfoToPresence","getMediaPresenceInfo","mutedNode","codecTypeNode","videoTypeNode","codecType","isSipGatewayEnabled","iqToFocus","clean","onMucLeft","doReject","mainRoom","maybeJoinLobbyRoom","_maybeJoinLobbyRoom","lobbyRoom","_leaveLobbyRoom","avatar","invitePassword","memberRoomJid","msgToSend","createExpBackoffTimer","step","origin","xmppService","getNextTimeout","getNextErrorTimeout","externalAuthEnabled","sipGatewayEnabled","attachEvent","setFocusUserJid","focusJid","focusUserJid","getFocusComponent","focusComponent","createConferenceIq","machineUID","machineId","audioPacketDelay","startBitrate","minBitrate","opusMaxAverageBitrate","stereo","parseSessionId","resultIq","parseConfigOptions","authenticationEnabled","_allocateConferenceFocusSuccess","_allocateConferenceFocusError","invalidSession","reservationErr","errorCode","errorTextNode","waitMs","retrySec","urlCallback","failureCallback","_getLoginUrl","popup","urlCb","decodeURIComponent","logout","logoutUrl","jvbIceConfig","p2pIceConfig","offerToReceiveAudio","offerToReceiveVideo","onJingle","fromJid","sess","startMuted","successTime","me","v2Res","onReceiveStunAndTurnCredentials","v2Err","v1Res","v1Err","iceservers","dict","credential","temp","useTurnUdp","updateLog","_queue","_processQueueTasks","_stopped","kill","previous_async","only_once","fn","called","noConflict","_toString","_isArray","_each","_map","_keys","setImmediate","completed","eachSeries","iterate","forEachSeries","eachLimit","limit","_eachLimit","forEachLimit","started","running","replenish","doParallel","doSeries","_asyncMap","eachfn","mapSeries","mapLimit","_mapLimit","doParallelLimit","memo","inject","foldl","reduceRight","reversed","foldr","_filter","filterSeries","select","selectSeries","_reject","rejectSeries","main_callback","detect","detectSeries","any","sortBy","criteria","left","right","auto","tasks","remainingTasks","taskComplete","theCallback","taskCallback","safeResults","rkey","requires","ready","times","attempts","wrappedTask","wrappedCallback","wrappedResults","retryAttempt","finalAttempt","seriesCallback","series","waterfall","wrapIterator","_parallel","parallel","parallelLimit","makeCallback","_concat","cb","concatSeries","whilst","doWhilst","until","doUntil","worker","concurrency","_insert","drain","saturated","workers","empty","idle","priorityQueue","_compareTasks","sequence","compare","beg","_binarySearch","cargo","working","drained","_console_fn","memoize","hasher","queues","memoized","unmemoized","unmemoize","counter","timesSeries","seq","fns","newargs","nextargs","compose","_applyEach","go","applyEach","applyEachSeries","forever","char","getState","addSources","removeSources","SignalingLayer","ssrcOwners","oldChatRoom","_audioMuteHandler","_videoMuteHandler","_videoTypeHandler","SignalingEvents","getPeerMediaInfo","getSSRCOwner","StropheLogger","logIncoming","logOutgoing","onRayo","roomPass","callResource","resetLastErrorStatusRegExpr","lastErrorStatusRegExpr","trace","errStatusCapture","_getCodecMimeType","jvbPreferredCodec","_isCodecSupported","p2pPreferredCodec","_selectPreferredCodec","_onMediaSessionStared","mediaSession","selectedCodec","remoteParticipants","peerMediaInfo","senderVideoConstraintsChanged","_areRetriesEnabled","_closedFromClient","datachannel","createDataChannel","_handleChannel","_wsUrl","_initWebSocket","ws","_startConnectionRetries","timeoutS","reload","_retryTimeout","_stopConnectionRetries","_retryWebSocketConnection","closeEvent","colibriClass","msgPayload","endpointIds","selectedEndpoints","maxFrameHeightPixels","channel","dominantSpeakerEndpoint","endpoint","videoConstraints","jsonObject","dtlsRole","_initialTrackId","getCommonCapabilities","localCapabilities","remoteCapabilities","commonCapabilities","findCodecByPayloadType","rtxCapabilityMatches","lRtx","rRtx","lCodecs","rCodecs","lCodec","rCodec","lHeaderExtension","rHeaderExtension","isActionAllowedInSignalingState","maybeAddCandidate","iceTransport","alreadyAdded","getRemoteCandidates","remoteCandidate","addRemoteCandidate","makeError","NotSupportedError","InvalidStateError","InvalidAccessError","OperationError","edgeVersion","addTrackToStreamAndFireEvent","dispatchEvent","MediaStreamTrackEvent","fireAddTrack","trackEvent","Event","_dispatchEvent","_eventTarget","canTrickleIceCandidates","needNegotiation","localStreams","remoteStreams","_localDescription","_remoteDescription","iceGatheringState","usingBundle","rtcpMuxPolicy","hasTurn","isString","filterIceServers","_iceGatherers","iceCandidatePoolSize","RTCIceGatherer","gatherPolicy","_config","_sdpSessionId","_sdpSessionVersion","_dtlsRole","_isClosed","onaddstream","ontrack","onremovestream","onconnectionstatechange","onicegatheringstatechange","ondatachannel","_emitGatheringStateChange","getConfiguration","getLocalStreams","getRemoteStreams","_createTransceiver","doNotAdd","hasBundleTransport","recvEncodingParameters","associatedRemoteMediaStreams","wantReceive","_createIceAndDtlsTransports","_maybeFireNegotiationNeeded","clonedStream","clonedTrack","removeStream","getSenders","getReceivers","_createIceGatherer","bufferedCandidateEvents","bufferCandidates","_gather","onlocalcandidate","evt","serializedCandidate","toJSON","complete","RTCIceTransport","onicestatechange","_updateIceConnectionState","_updateConnectionState","RTCDtlsTransport","ondtlsstatechange","_disposeIceAndDtlsTransports","_transceive","recv","receive","isIceLite","rejected","remoteIceParameters","remoteDtlsParameters","_updateSignalingState","receiverList","iceOptions","remoteMsid","isComplete","setTransport","setRemoteCandidates","isNewTrack","nativeTrack","removeTrackFromStreamAndFireEvent","states","checking","disconnected","failed","new","connecting","numAudioTracks","numVideoTracks","offerOptions","remoteCodec","rHdrExt","getLocalCandidates","mediaSectionsInOffer","candidateString","selector","MediaStreamTrack","senderOrReceiver","allStats","ortcObjectName","nativeGetstats","nativeStats","mapStats","inboundrtp","outboundrtp","candidatepair","localcandidate","remotecandidate","nativeMethod","audioTransferActive","_dtmfSender","_dtmfTonesQueue","videoTransferActive","localUfrag","remoteUfrag","_peerVideoTypeChanged","_peerMutedChanged","safeConstraints","rtcStatsSFUP2P","standardVideoBitrates","maxBitratesVideo","tpcUtils","statsinterval","interop","Interop","Simulcast","numOfLayers","explodeRemoteSimulcast","sdpConsistency","SdpConsistency","localSdpMunger","LocalSdpMunger","rtxModifier","RtxModifier","senderVideoMaxHeight","what","_remoteStreamAdded","_remoteStreamRemoved","_remoteTrackAdded","onremovetrack","_remoteTrackRemoved","_processStat","statValue","endTime","dumpSDP","_getDesiredMediaDirection","mediaTransferActive","hasAnyTracksOfType","_setVideoType","setMute","getSynchronizationSources","endpoints","endpointTrackMap","mediaTrack","findTrackById","onaddtrack","streamAudioTracks","streamVideoTracks","remoteSDP","mediaLines","mls","trackSsrc","ownerEndpointId","_createRemoteTrack","remoteTracksMap","existingTrack","JitsiRemoteTrack","isUserStream","_removeRemoteTrackById","_getRemoteTrackById","removedAudioTrack","removedVideoTrack","_removeRemoteTrack","toBeRemoved","normalizePlanB","firstSsrcs","newSsrcLines","filteredLines","ssrcId","cnameLine","replaceDefaultUnifiedPlanMsid","resStr","_getSSRC","_injectSsrcGroupForUnifiedSimulcast","fidGroups","getters","toPlanB","maybeAddMutedLocalVideoTracksToSDP","transformer","audioMedia","videoMedia","enforceSendRecv","transformStreamIdentifiers","_isSharingScreen","_mungeCodecOrder","codecPreference","bitrates","containsTrack","webrtcStream","_addStream","generateNewStreamSSRCInfo","setPrimarySsrc","setSsrcCache","rtxSsrcMapping","rtxSsrc","promiseChain","_assertTrackBelongs","webRtcStream","_removeStream","doesBelong","defaultCodec","isMediaStreamInPc","findSenderByKind","opts","_ensureSimulcastGroupIsLast","localSdp","sdpStr","videoStartIndex","simStartIndex","otherStartIndex","simEndIndex","simStr","otherEndIndex","sdpHead","simStrTrimmed","sdpTail","_adjustLocalMediaDirection","modifiedDirection","desiredAudioDirection","desiredVideoDirection","toUnifiedPlan","localVideoTrack","videoSender","preference","degradationPreference","planBScreenSharing","presenterEnabled","scaleFactor","layer","mungeRemoteDescription","currentDescription","newHeight","encodingsEnabledState","ldStreamIndex","interToneGap","dtmf","localAudioTrack","createDTMFSender","ontonechange","_onToneChange","toneBuffer","insertDTMF","tone","clearVideoSsrcCache","peerTracks","_createOfferOrAnswer","isOffer","handleSuccess","resultSdp","resolveFn","rejectFn","hasPrimarySsrcCached","makeVideoPrimarySsrcsConsistent","mungeLocalDescription","modifyRtxSsrcs","groupsMap","groupSSRCs","extractSSRCMap","_processLocalSSRCsMap","handleFailure","oaPromise","trackMSID","newSSRCNum","oldSSRCNum","currNumSsrcs","updateAssociatedRtxStream","primarySsrcInfo","primarySsrcMsid","primarySsrcCname","previousRtxSSRC","correspondingRtxSsrcs","clearSsrcCache","ssrcMapping","sdpTransformer","modifyRtxSsrcs2","primaryVideoSsrcs","correspondingRtxSsrc","previousAssociatedRtxStream","stripRtx","cachedPrimarySsrc","injectRecvOnly","newPrimarySsrc","ttfmTrackerAudioAttached","ttfmTrackerVideoAttached","containerEvents","hasBeenMuted","_bindTrackHandlers","_containerHandlers","_containerEventHandler","_onTrackMute","_onTrackUnmute","_playCallback","gumStart","gumEnd","gumDuration","ttfm","_getStatus","_addMutedLocalVideoTracksToSDP","localVideos","isInPeerConnection","shouldFakeSdp","requiredSSRCs","ssrcCache","primaryCname","_transformMediaIdentifiers","pcId","streamAndTrackIDs","sessionDesc","audioMLine","kSimulcastFormats","layers","videoBitrateCap","getTarget","millisSinceStart","simulcastFormat","targetHeight","pixels","rampUp","_localStats","jvbRTT","_lastConnectionQualityUpdate","_remoteStats","_timeIceConnected","_timeVideoUnmuted","_timeLastBwCapRemoved","ConferenceEvents","_updateLocalConnectionQuality","ConnectionQualityEvents","_broadcastLocalStats","_updateRemoteStats","_updateLocalStats","_maybeUpdateUnmuteTime","bridgeCount","_calculateConnectionQuality","resolutionName","quality","activeTPC","newVideoBitrateCap","bandwidthLimiter","getBandwidthLimit","maxIncreasePerSecond","prevConnectionQuality","diffSeconds","updateLocalConnectionQuality","_actOnIceFailed","explicitlyDisabled","useTerminateForRestart","reloadClient","jvbConnection","jvbConnIceState","_canceled","_iceFailedTimeout","_timeoutTrigger","_hasAudioInput","_audioLevel","_clearTriggerTimeout","_handleAudioInputStateChange","_handleNoAudioInputDetection","_eventFired","_audioTrack","myUserID","_processing","_scoreArray","_audioLvlArray","_active","_calculateNoisyScore","scoreAvg","audioLevelAvg","_setActiveState","_recordValues","avgAudioLvl","_processTimeout","posAudioLevels","_calculateVADScore","ParticipantWrapper","requests","lastRequestId","clearIntervals","sendRequest","handleResponse","maybeSendAnalytics","isDataChannelOpen","pingIntervalMs","analyticsInterval","analyticsIntervalMs","requestId","requestMessage","timeSent","E2ePingEvents","Infinity","participantJoined","participantLeft","messageReceived","dataChannelOpened","participantWrapper","handleRequest","_jvb121","evaluateStatus","oldStatus","_propagateSendMaxFrameHeight","preferredReceiveMaxFrameHeight","sendMaxFrameHeight","selectSendMaxFrameHeight","activeMediaSession","preferredSendMaxFrameHeight","_sessions","_chatRoom","getSession","_handleFocusPresence","_handleJibriPresence","_addSession","_emitSessionUpdate","_createSession","_localAudioLevelCache","_reportedParticipants","_audioProblemCandidates","_numberOfRemoteAudioLevelsReceived","_onLocalAudioLevelsReport","_onRemoteAudioLevelReceived","_clearUserData","numberOfReports","localAudioLevelsString","AverageStatReport","calculate","appendReport","ConnectionAvgStats","_n","_sampleIdx","_avgRTT","_avgRemoteRTTMap","_avgRtpStatsReporter","_avgEnd2EndRTT","_onConnectionStats","_calculateAvgStats","_onRemoteStatsUpdated","_processRemoteStats","batchReport","jvbEnd2EndRTT","jvbStatsMonitor","avgRemoteRTT","_calculateAvgRemoteRTT","avgLocalRTT","_resetAvgStats","remoteAvg","avg","validData","rttAvg","_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","myID","peerID","videosResolution","peerAvgPixels","_calculatePeerAvgVideoPixels","videos","peerSsrcCount","peerSsrcPixels","peerFpsSum","videosFps","peerAvgFPS","_calculatePeerAvgVideoFps","peerSsrcFps","transportStats","dominantSpeakerId","SpeakerStats","_onDominantSpeaker","_onUserJoin","_onUserLeave","_onDisplayNameChange","_updateStats","oldDominantSpeaker","newDominantSpeaker","savedUser","newStats","speakerStatsToUpdate","newParticipant","SphinxService","audioRecorder","transcriptionService","transcription","lineLength","blobCallBack","wordArray","getUTCMilliseconds","wordObject","begin","word","maybeMerge","hasPopulatedArrays","twoDimensionalArray","callBack","recordingResult","merge","arrays","potentialWords","pushWordToSortedArray","lowestWordArray","wordToAdd","updateTranscription","foundSmaller","wordToCompare","getTranscription","processVersions","mucResource","getComponentVersion","componentName","sessionStateChangeListener","sessionStateChanged","handleJibriSIPState","Constants","sipaddress","setState","failure_reason","JitsiVideoSIPGWSession","addStateListener","removeStateListener","_sendJibriIQ","failureReason","oldState","displayname","getActiveAudioDevice","audioDevices","devicePromiseArray","micDevice","devicePromise","outcomeArray","successfulPromises","rejectedPromises","rejectReasons","stopActiveDevices","deviceLabel","ProxyConnectionService","jitsiConnection","_peerConnection","_onFatalError","_onSendMessage","_onRemoteStream","processMessage","getPeerJid","_convertStringToXML","$jingle","_createPeerConnection","receiveVideo","_selfCloseConnection","xml","xmlDom","onRemoteStream","onSendMessage","ProxyConnectionPC","jitsiRemoteTrack","isVideo","convertVideoToDesktop","jitsiLocalTracks","newCreateLocalTracks","stringifiedIq","XMLSerializer","serializeToString","onConnectionClosed","receiveAudio","_onSessionAccept","_onSessionInitiate","_onSessionTerminate","_onTransportInfo","connectionStub","iceConfigStub","roomStub","_rtc","BUSY","RESOURCE_CONSTRAINT","SERVICE_UNAVAILABLE","FILE","api","_loadScript","appId","appSecret","disablePrecalltest","_initialize","execute","makePrecallTest","AuthUtil","getTokenAuthUrl","urlPattern","roleUpgrade","AudioMixer","_started","_streamsToMix","_streamMSSArray","addMediaStream","_mixedMSD","createMediaStreamDestination","streamMSS","found","PLAN_B_MIDS","findSimGroup","grp","findFidGroup","addSimGroupSources","sourceGroups","sourceList","findSourcebyId","relatedFidGroup","relatedSsrc","addSourcesToMline","otherSsrc","simGroup2","checkIfMlineForSsrcExists","mlines","sessionMedia","bLine","bundle","mids","msidSemantic","semantic","currentDesc","newMline","mLineForData","ssrc2group","createSourceGroupMap","bundleOnly","inactiveMid","cmLine","msCrypto","rnds8","rng","byteToHex","buf","bth","rnds","getAnalyticsAttributesFromOptions","video_requested","_mergeNamespaceAndModule","JitsiMeetJS","constants","recording","recordingConstants","sipVideoGW","detection","errorTypes","logLevels","JitsiMediaDevices","enableAnalyticsLogging","enableWindowOnErrorHandler","getGlobalOnErrorHandler","aprops","isWebRtcSupported","addGlobalLogTransport","globalTransport","removeGlobalLogTransport","setGlobalLogOptions","firePermissionPromptIsShownEvent","originalOptions","promiseFulfilled","mStream","currentlyAvailableMediaDevices","setVideoTrackContentHints","oldResolution","newResolution","resName","getLowerResolution","createTrackVADEmitter","localAudioDeviceId","createAudioMixer","isCollectingLocalStats","lineno","colno","setNetworkInfo","hint","contentHint","precallTest","util","logStorage","stringifyObjects","storeInterval","maxEntryLength","logLevel","_log","storeLogsIntervalID","totalLen","outputCache","someObject","formatLogMessage","prevMessage","prevMessageText","_flush","_reschedulePublishInterval","force","reschedule","isReady","cachedQueue","storeLogs","__webpack_amd_options__","scriptReadyRegex","fullPageUrl","pageUrl","scripts","supportsScriptReadyState","isNotOpera","hasNativeCurrentScriptAccessor","stackTraceLimit","hasStackBeforeThrowing","hasStackAfterThrowing","_nearestExecutingScript","eligibleScripts","skipStackDepth","getScriptFromUrl","getScriptUrlFromStack","ignoreMessage","getSoleInlineScript","currentScript","thrownErr","near","far","_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","scope","Timeout","clearFn","_clearFn","unref","ref","enroll","msecs","_idleTimeoutId","unenroll","_unrefActive","_onTimeout","clearImmediate","registerImmediate","messagePrefix","onGlobalMessage","nextHandle","tasksByHandle","currentlyRunningATask","attachTo","handle","runIfPresent","importScripts","postMessageIsAsynchronous","oldOnMessage","canUsePostMessage","MessageChannel","port1","port2","removeChild","toIntIfInt","parseReg","needsBlank","keyLocation","rawName","attachProperties","validLine","paramReducer","acc","expr","scid","formatRegExp","formatStr","makeLine","defaultOuterOrder","defaultInnerOrder","outerOrder","innerOrder","transformUtils","parseSsrcs","writeSsrcs","processVideo","validateDescription","_parseSimLayers","_buildNewToOldSsrcMap","newSsrcList","oldSsrcList","newSsrc","oldSsrc","_fillInSourceDataFromCache","newSimSsrcs","newMsid","newCname","ssrcsToReplace","ssrcsToAdd","_generateSourceData","addAssociatedStream","simSsrcs","simSsrc","_restoreSimulcast","enableConferenceFlag","simulcastSsrcs","relatedGroup","relatedSsrcs","relatedSSRC","simulcastGroup","nuke","implodeRemoteSimulcast","invalid","assertGoogConference","xGoogleFlag","removeGoogConference","Word","TranscriptionService","sphinxURL","toReturn","getURL","audioFileBlob","DONE","formatResponse","objects","filler","verify","getWord","getBeginTime","getEndTime","audioBlob","logDisabled_","deprecationWarnings_","extractVersion","uastring","wrapPeerConnectionEvent","eventNameToWrap","nativeAddEventListener","nativeEventName","modifiedEvent","_eventMap","nativeRemoveEventListener","unwrappedCb","disableLog","bool","disableWarnings","deprecated","oldMethod","newMethod","detectBrowser","mozGetUserMedia","webkitGetUserMedia","isSecureContext","webkitRTCPeerConnection","supportsUnifiedPlan","val","compactObject","isEmptyObject","filterStats","outbound","streamStatsType","filteredResult","trackStats","trackStat","walkStats","resultSet","shimGetUserMedia","browserDetails","constraintsToChrome_","cc","exact","oldname_","oc","mix","advanced","shimConstraints_","remap","face","getSupportedFacingModeLies","getSupportedConstraints","dev","shimError_","PermissionDeniedError","PermissionDismissedError","DevicesNotFoundError","ConstraintNotSatisfiedError","TrackStartError","MediaDeviceFailedDueToShutdown","MediaDeviceKillSwitchOn","TabCaptureError","ScreenCaptureError","DeviceCaptureError","origGetUserMedia","DOMException","shimGetDisplayMedia","getSourceId","widthSpecified","heightSpecified","frameRateSpecified","shimMediaStream","webkitMediaStream","shimOnTrack","_ontrack","origSetRemoteDescription","_ontrackpoly","te","shimGetSendersWithDtmf","shimSenderWithDtmf","_dtmf","_pc","_senders","origAddTrack","origRemoveTrack","origAddStream","origRemoveStream","origGetSenders","shimGetStats","origGetStats","onSucc","onErr","fixChromeStats_","standardReport","standardStats","makeMapStats","successCallbackWrapper_","shimSenderReceiverGetStats","origGetReceivers","receivers","srcElement","shimAddTrackRemoveTrackWithNative","_shimmedLocalStreams","existingSenders","newSenders","newSender","shimAddTrackRemoveTrack","origGetLocalStreams","nativeStreams","_reverseStreams","_streams","newStream","replaceInternalStreamId","internalId","externalStream","internalStream","replaceExternalStreamId","alreadyExists","oldStream","methodObj","origSetLocalDescription","origLocalDescription","getOwnPropertyDescriptor","streamid","shimPeerConnection","addIceCandidateNullSupported","nativeAddIceCandidate","fixNegotiationNeeded","origMSTEnabled","RTCDtmfSender","RTCDTMFSender","RTCPeerConnectionShim","shimRTCPeerConnection","validTurn","shimReplaceTrack","setTrack","nativeGetUserMedia","nativeGetSettings","nativeApplyConstraints","preferredMediaSource","RTCTrackEvent","mozRTCPeerConnection","modernStatsTypes","nativeGetStats","shimSenderGetStats","shimReceiverGetStats","shimRemoveStream","shimRTCDataChannel","DataChannel","RTCDataChannel","shimAddTransceiver","origAddTransceiver","setParametersPromises","initParameters","shouldPerformCheck","encodingParam","maxFramerate","shimCreateOffer","origCreateOffer","finally","shimCreateAnswer","origCreateAnswer","shimLocalStreamsAPI","_localStreams","_addTrack","shimRemoteStreamsAPI","_remoteStreams","_onaddstream","_onaddstreampoly","shimCallbacksAPI","withCallback","shimConstraints","errcb","shimRTCIceServerUrls","OrigPeerConnection","pcConfig","newIceServers","generateCertificate","shimTrackEventTransceiver","shimCreateOfferLegacy","audioTransceiver","setDirection","videoTransceiver","shimRTCIceCandidate","NativeRTCIceCandidate","nativeCandidate","parsedCandidate","augmentedCandidate","shimMaxMessageSize","_sctp","sctpInDescription","getRemoteFirefoxVersion","getCanSendMaxMessageSize","remoteIsFirefox","canSendMaxMessageSize","getMaxMessageSize","canSendMMS","remoteMMS","POSITIVE_INFINITY","shimSendThrowTypeError","wrapDcSend","dc","origDataChannelSend","origCreateDataChannel","dataChannel","shimConnectionState","_onconnectionstatechange","origMethod","_connectionstatechangepoly","_lastConnectionState","newEvent","removeAllowExtmapMixed","nativeSRD","adapter","shimChrome","shimFirefox","shimEdge","shimSafari","commonShim","chromeShim","browserShim","firefoxShim","edgeShim","safariShim","adapterFactory"],"mappings":"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","file":"lib-jitsi-meet.min.js","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})(window, function() {\nreturn "," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 148);\n","const 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    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\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 initator\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 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 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 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    // 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:\n        '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     */\n    JSON_MESSAGE_RECEIVED: 'xmmp.json_message_received'\n};\n\nmodule.exports = XMPPEvents;\n","/**\n * The events for the conference.\n */\n\n/**\n * Event indicates that the current conference audio input switched between audio\n * input states,i.e. with or without audio input.\n */\nexport const AUDIO_INPUT_STATE_CHANGE = 'conference.audio_input_state_changed';\n\n/**\n * Indicates that authentication status changed.\n */\nexport const 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 */\nexport const BEFORE_STATISTICS_DISPOSED = 'conference.beforeStatisticsDisposed';\n\n/**\n * Indicates that an error occured.\n */\nexport const CONFERENCE_ERROR = 'conference.error';\n\n/**\n * Indicates that conference failed.\n */\nexport const CONFERENCE_FAILED = 'conference.failed';\n\n/**\n * Indicates that conference has been joined. The event does NOT provide any\n * parameters to its listeners.\n */\nexport const CONFERENCE_JOINED = 'conference.joined';\n\n/**\n * Indicates that conference has been left.\n */\nexport const CONFERENCE_LEFT = 'conference.left';\n\n/**\n * Indicates that the conference unique identifier has been set.\n */\nexport const 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 whenVthe *ICE* connection enters 'connected'\n * state for the first time.\n */\nexport const 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 */\nexport const 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 */\nexport const CONNECTION_RESTORED = 'conference.connectionRestored';\n\n/**\n * A connection to the video bridge's data channel has been established.\n */\nexport const DATA_CHANNEL_OPENED = 'conference.dataChannelOpened';\n\n/**\n * A user has changed it display name\n */\nexport const DISPLAY_NAME_CHANGED = 'conference.displayNameChanged';\n\n/**\n * The dominant speaker was changed.\n */\nexport const DOMINANT_SPEAKER_CHANGED = 'conference.dominantSpeaker';\n\n/**\n * UTC conference timestamp when first participant joined.\n */\nexport const CONFERENCE_CREATED_TIMESTAMP = 'conference.createdTimestamp';\n\n/**\n * Indicates that DTMF support changed.\n */\nexport const DTMF_SUPPORT_CHANGED = 'conference.dtmfSupportChanged';\n\n/**\n * Indicates that a message from another participant is received on data\n * channel.\n */\nexport const ENDPOINT_MESSAGE_RECEIVED = 'conference.endpoint_message_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 */\nexport const JVB121_STATUS = 'conference.jvb121Status';\n\n/**\n * You are kicked from the conference.\n * @param {JitsiParticipant} the participant that initiated the kick.\n */\nexport const 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 */\nexport const 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 */\nexport const LAST_N_ENDPOINTS_CHANGED = 'conference.lastNEndpointsChanged';\n\n/**\n * Indicates that the room has been locked or unlocked.\n */\nexport const 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 */\nexport const 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 */\nexport const _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 */\nexport const _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 */\nexport const MEMBERS_ONLY_CHANGED = 'conference.membersOnlyChanged';\n\n/**\n * New text message was received.\n */\nexport const MESSAGE_RECEIVED = 'conference.messageReceived';\n\n/**\n * Event indicates that the current selected input device has no signal\n */\nexport const NO_AUDIO_INPUT = 'conference.no_audio_input';\n\n/**\n * Event indicates that the current microphone used by the conference is noisy.\n */\nexport const NOISY_MIC = 'conference.noisy_mic';\n\n/**\n * New private text message was received.\n */\nexport const 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 */\nexport const PARTICIPANT_CONN_STATUS_CHANGED\n    = 'conference.participant_conn_status_changed';\n\n/**\n * Indicates that the features of the participant has been changed.\n */\nexport const PARTCIPANT_FEATURES_CHANGED\n    = 'conference.partcipant_features_changed';\n\n/**\n * Indicates that a the value of a specific property of a specific participant\n * has changed.\n */\nexport const PARTICIPANT_PROPERTY_CHANGED\n    = '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 */\nexport const P2P_STATUS = 'conference.p2pStatus';\n\n/**\n * Indicates that phone number changed.\n */\nexport const PHONE_NUMBER_CHANGED = 'conference.phoneNumberChanged';\n\n/**\n * The conference properties changed.\n * @type {string}\n */\nexport const PROPERTIES_CHANGED = 'conference.propertiesChanged';\n\n/**\n * Indicates that recording state changed.\n */\nexport const RECORDER_STATE_CHANGED = 'conference.recorderStateChanged';\n\n/**\n * Indicates that video SIP GW state changed.\n * @param {VideoSIPGWConstants} status.\n */\nexport const VIDEO_SIP_GW_AVAILABILITY_CHANGED\n    = '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 */\nexport const VIDEO_SIP_GW_SESSION_STATE_CHANGED\n    = 'conference.videoSIPGWSessionStateChanged';\n\n/**\n * Indicates that start muted settings changed.\n */\nexport const START_MUTED_POLICY_CHANGED\n    = 'conference.start_muted_policy_changed';\n\n/**\n * Indicates that the local user has started muted.\n */\nexport const STARTED_MUTED = 'conference.started_muted';\n\n/**\n * Indicates that subject of the conference has changed.\n */\nexport const SUBJECT_CHANGED = 'conference.subjectChanged';\n\n/**\n * Indicates that DTMF support changed.\n */\nexport const SUSPEND_DETECTED = 'conference.suspendDetected';\n\n/**\n * Event indicates that local user is talking while he muted himself\n */\nexport const 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 */\nexport const TRACK_ADDED = 'conference.trackAdded';\n\n/**\n * Audio levels of a media track ( attached to the conference) was changed.\n */\nexport const 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 */\nexport const 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 */\nexport const TRACK_REMOVED = 'conference.trackRemoved';\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 */\nexport const TRANSCRIPTION_STATUS_CHANGED\n    = 'conference.transcriptionStatusChanged';\n\n\n/**\n * A new user joined the conference.\n */\nexport const USER_JOINED = 'conference.userJoined';\n\n/**\n * A user has left the conference.\n */\nexport const USER_LEFT = 'conference.userLeft';\n\n/**\n * User role changed.\n */\nexport const USER_ROLE_CHANGED = 'conference.roleChanged';\n\n/**\n * User status changed.\n */\nexport const USER_STATUS_CHANGED = 'conference.statusChanged';\n\n/**\n * Event indicates that the bot participant type changed.\n */\nexport const BOT_TYPE_CHANGED = 'conference.bot_type_changed';\n\n/**\n * A new user joined the lobby room.\n */\nexport const LOBBY_USER_JOINED = 'conference.lobby.userJoined';\n\n/**\n * A user from the lobby room has been update.\n */\nexport const LOBBY_USER_UPDATED = 'conference.lobby.userUpdated';\n\n/**\n * A user left the lobby room.\n */\nexport const LOBBY_USER_LEFT = 'conference.lobby.userLeft';\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","import BrowserCapabilities from './BrowserCapabilities';\n\nexport default new BrowserCapabilities();\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","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-meet-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    })) {\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 */\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\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","/**\n * The audio type.\n */\nexport const AUDIO = 'audio';\n\n/**\n * The presenter type.\n */\nexport const PRESENTER = 'presenter';\n\n/**\n * The video type.\n */\nexport const VIDEO = 'video';\n","const 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\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     * 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\nmodule.exports = RTCEvents;\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\n/**\n * The constant which identifies an event of type \"operational\".\n * @type {string}\n */\nexport const TYPE_OPERATIONAL = 'operational';\n\n/**\n * The constant which identifies an event of type \"page\".\n * @type {string}\n */\nexport const TYPE_PAGE = 'page';\n\n/**\n * The constant which identifies an event of type \"track\".\n * @type {string}\n */\nexport const TYPE_TRACK = 'track';\n\n/**\n * The constant which identifies an event of type \"ui\".\n * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const ACTION_JINGLE_TERMINATE = 'terminate';\n\n/**\n * The \"action\" value for Jingle events which indicates that a transport-replace\n * was received.\n * @type {string}\n */\nexport const ACTION_JINGLE_TR_RECEIVED\n    = '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 * @type {string}\n */\nexport const ACTION_JINGLE_TR_SUCCESS\n    = '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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const ACTION_P2P_ESTABLISHED = 'established';\n\n/**\n * The \"action\" value for P2P events which indicates that something failed.\n * @type {string}\n */\nexport const ACTION_P2P_FAILED = 'failed';\n\n/**\n * The \"action\" value for P2P events which indicates that a switch to\n * jitsi-videobridge happened.\n * @type {string}\n */\nexport const 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 * @type {string}\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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const ICE_ESTABLISHMENT_DURATION_DIFF\n    = '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 */\nexport const 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 */\nexport const 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 */\nexport const TRACK_UNMUTED = '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 = function() {\n    const bridgeDown = 'bridge.down';\n\n    return {\n        action: bridgeDown,\n        actionSubject: bridgeDown,\n        type: TYPE_OPERATIONAL\n    };\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\n    = function(errorType, errorMessage, details) {\n        return {\n            type: TYPE_OPERATIONAL,\n            action: 'connection.failed',\n            attributes: {\n                'error_type': errorType,\n                'error_message': errorMessage,\n                ...details\n            }\n        };\n    };\n\n/**\n * Creates a conference event.\n *\n * @param {string} action - The action of the event.\n * @param {Object} attributes - The attributes to be added to the event.\n * @returns {{type: string, source: string, action: string, attributes: object}}\n */\nexport function createConferenceEvent(action, attributes) {\n    return {\n        action,\n        attributes,\n        source: 'conference',\n        type: TYPE_OPERATIONAL\n    };\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 = function(stage, attributes) {\n    const action = 'connection.stage.reached';\n\n    return {\n        action,\n        actionSubject: stage,\n        attributes,\n        source: action,\n        type: TYPE_OPERATIONAL\n    };\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 = function(participantId, region, rtt) {\n    const attributes = {\n        'participant_id': participantId,\n        region,\n        rtt\n    };\n\n    return {\n        attributes,\n        name: 'e2e_rtt',\n        type: TYPE_OPERATIONAL\n    };\n};\n\n/**\n * Creates an event which indicates that the focus has left the MUC.\n */\nexport const createFocusLeftEvent = function() {\n    const action = 'focus.left';\n\n    return {\n        action,\n        actionSubject: action,\n        type: TYPE_OPERATIONAL\n    };\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 * @returns {{type: string, source: string, name: string}}\n */\nexport const createGetUserMediaEvent = function(action, attributes = {}) {\n    return {\n        type: TYPE_OPERATIONAL,\n        source: 'get.user.media',\n        action,\n        attributes\n    };\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 * @returns {{type: string, source: string, name: string}}\n */\nexport const createParticipantConnectionStatusEvent = function(attributes = {}) {\n    const action = 'duration';\n\n    return {\n        type: TYPE_OPERATIONAL,\n        source: 'peer.conn.status',\n        action,\n        attributes\n    };\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 = function(action, attributes = {}) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action,\n        source: 'jingle',\n        attributes\n    };\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 {String} the media type of the local track ('audio' or\n * 'video').\n */\nexport const createNoDataFromSourceEvent = function(mediaType, value) {\n    return {\n        attributes: {\n            'media_type': mediaType,\n            value\n        },\n        action: 'track.no.data.from.source',\n        type: TYPE_OPERATIONAL\n    };\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 = function(action, attributes = {}) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action,\n        source: 'p2p',\n        attributes\n    };\n};\n\n/**\n * Indicates that we received a remote command to mute.\n */\nexport const createRemotelyMutedEvent = function() {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'remotely.muted'\n    };\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 = function(attributes) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'rtp.stats',\n        attributes\n    };\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 * @returns {{type: string, action: string, attributes: *}}\n */\nexport const createRttByRegionEvent = function(attributes) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'rtt.by.region',\n        attributes\n    };\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 * @returns {{type: string, action: string, attributes: *}}\n */\nexport const createTransportStatsEvent = function(attributes) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'transport.stats',\n        attributes\n    };\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 {string} 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 function createAudioOutputProblemEvent(userID, localAudioLevels, remoteAudioLevels) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'audio.output.problem',\n        attributes: {\n            userID,\n            localAudioLevels,\n            remoteAudioLevels\n        }\n    };\n}\n\n/**\n * Creates an event which contains an information related to the bridge channel close event.\n *\n * @param {string} code - A code from {@link https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent}\n * @param {string} reason - A string which describes the reason for closing the bridge channel.\n * @returns {{type: string, action: string, attributes: { code: string, reason: string }}}\n */\nexport const createBridgeChannelClosedEvent = function(code, reason) {\n    return {\n        type: TYPE_OPERATIONAL,\n        action: 'bridge-channel.error',\n        attributes: {\n            code,\n            reason\n        }\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 = function(attributes) {\n    return createConnectionStageReachedEvent('ttfm', attributes);\n};\n","import { getLogger } from 'jitsi-meet-logger';\nconst logger = getLogger(__filename);\n\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\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    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    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.log(\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    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.log(\n                'parseCandidate called with a line that is not a candidate'\n                    + ' line');\n            logger.log(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.log('did not find typ in the right place');\n            logger.log(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.log(`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 = '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          __filename,\n          MediaStreamTrack,\n          RTCIceCandidate: true,\n          RTCPeerConnection,\n          RTCSessionDescription: true\n*/\n\nimport EventEmitter from 'events';\nimport { getLogger } from 'jitsi-meet-logger';\nimport clonedeep from 'lodash.clonedeep';\n\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport CameraFacingMode from '../../service/RTC/CameraFacingMode';\nimport * as MediaType from '../../service/RTC/MediaType';\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 Statistics from '../statistics/statistics';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport SDPUtil from '../xmpp/SDPUtil';\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 resolution to obtain for video tracks if no resolution is specified.\n * This default is used for old gum flow only, as new gum flow uses\n * {@link DEFAULT_CONSTRAINTS}.\n */\nconst OLD_GUM_DEFAULT_RESOLUTION = 720;\n\n/**\n * Default devices to obtain when no specific devices are specified. This\n * default is used for old gum flow only.\n */\nconst OLD_GUM_DEFAULT_DEVICES = [ 'audio', 'video' ];\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: 240\n        }\n    }\n};\n\n/**\n * The default frame rate for Screen Sharing.\n */\nconst SS_DEFAULT_FRAME_RATE = 5;\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// Disables Highpass Filter\nlet disableHPF = false;\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 * Initialize wrapper function for enumerating devices.\n * TODO: remove this, it should no longer be needed.\n *\n * @returns {?Function}\n */\nfunction initEnumerateDevicesWithCallback() {\n    if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {\n        return callback => {\n            navigator.mediaDevices.enumerateDevices()\n                .then(devices => {\n                    updateKnownDevices(devices);\n                    callback(devices);\n                }, () => {\n                    updateKnownDevices([]);\n                    callback([]);\n                });\n        };\n    }\n}\n\n/**\n *\n * @param constraints\n * @param isNewStyleConstraintsSupported\n * @param resolution\n */\nfunction setResolutionConstraints(\n        constraints,\n        isNewStyleConstraintsSupported,\n        resolution) {\n    if (Resolutions[resolution]) {\n        if (isNewStyleConstraintsSupported) {\n            constraints.video.width = {\n                ideal: Resolutions[resolution].width\n            };\n            constraints.video.height = {\n                ideal: Resolutions[resolution].height\n            };\n        }\n\n        constraints.video.mandatory.minWidth = Resolutions[resolution].width;\n        constraints.video.mandatory.minHeight = Resolutions[resolution].height;\n    }\n\n    if (constraints.video.mandatory.minWidth) {\n        constraints.video.mandatory.maxWidth\n            = constraints.video.mandatory.minWidth;\n    }\n\n    if (constraints.video.mandatory.minHeight) {\n        constraints.video.mandatory.maxHeight\n            = constraints.video.mandatory.minHeight;\n    }\n}\n\n/**\n * @param {string[]} um required user media types\n *\n * @param {Object} [options={}] optional parameters\n * @param {string} options.resolution\n * @param {number} options.bandwidth\n * @param {number} options.fps\n * @param {string} options.desktopStream\n * @param {string} options.cameraDeviceId\n * @param {string} options.micDeviceId\n * @param {CameraFacingMode} options.facingMode\n * @param {bool} firefox_fake_device\n * @param {Object} options.frameRate - used only for dekstop sharing.\n * @param {Object} options.frameRate.min - Minimum fps\n * @param {Object} options.frameRate.max - Maximum fps\n * @param {bool}   options.screenShareAudio - Used by electron clients to\n * enable system audio screen sharing.\n */\nfunction getConstraints(um, options = {}) {\n    const constraints = {\n        audio: false,\n        video: false\n    };\n\n    // Don't mix new and old style settings for Chromium as this leads\n    // to TypeError in new Chromium versions. @see\n    // https://bugs.chromium.org/p/chromium/issues/detail?id=614716\n    // This is a temporary solution, in future we will fully split old and\n    // new style constraints when new versions of Chromium and Firefox will\n    // have stable support of new constraints format. For more information\n    // @see https://github.com/jitsi/lib-jitsi-meet/pull/136\n    const isNewStyleConstraintsSupported\n        = browser.isFirefox()\n            || browser.isSafari()\n            || browser.isReactNative();\n\n    if (um.indexOf('video') >= 0) {\n        // same behaviour as true\n        constraints.video = { mandatory: {},\n            optional: [] };\n\n        if (options.cameraDeviceId) {\n            if (isNewStyleConstraintsSupported) {\n                // New style of setting device id.\n                constraints.video.deviceId = options.cameraDeviceId;\n            }\n\n            // Old style.\n            constraints.video.mandatory.sourceId = options.cameraDeviceId;\n        } else {\n            // Prefer the front i.e. user-facing camera (to the back i.e.\n            // environment-facing camera, for example).\n            // TODO: Maybe use \"exact\" syntax if options.facingMode is defined,\n            // but this probably needs to be decided when updating other\n            // constraints, as we currently don't use \"exact\" syntax anywhere.\n            const facingMode = options.facingMode || CameraFacingMode.USER;\n\n            if (isNewStyleConstraintsSupported) {\n                constraints.video.facingMode = facingMode;\n            }\n            constraints.video.optional.push({\n                facingMode\n            });\n        }\n\n        if (options.minFps || options.maxFps || options.fps) {\n            // for some cameras it might be necessary to request 30fps\n            // so they choose 30fps mjpg over 10fps yuy2\n            if (options.minFps || options.fps) {\n                // Fall back to options.fps for backwards compatibility\n                options.minFps = options.minFps || options.fps;\n                constraints.video.mandatory.minFrameRate = options.minFps;\n            }\n            if (options.maxFps) {\n                constraints.video.mandatory.maxFrameRate = options.maxFps;\n            }\n        }\n\n        setResolutionConstraints(\n            constraints, isNewStyleConstraintsSupported, options.resolution);\n    }\n    if (um.indexOf('audio') >= 0) {\n        if (browser.isReactNative()) {\n            // The react-native-webrtc project that we're currently using\n            // expects the audio constraint to be a boolean.\n            constraints.audio = true;\n        } else if (browser.isFirefox()) {\n            if (options.micDeviceId) {\n                constraints.audio = {\n                    mandatory: {},\n                    deviceId: options.micDeviceId, // new style\n                    optional: [ {\n                        sourceId: options.micDeviceId // old style\n                    } ] };\n            } else {\n                constraints.audio = true;\n            }\n        } else {\n            // same behaviour as true\n            constraints.audio = { mandatory: {},\n                optional: [] };\n            if (options.micDeviceId) {\n                if (isNewStyleConstraintsSupported) {\n                    // New style of setting device id.\n                    constraints.audio.deviceId = options.micDeviceId;\n                }\n\n                // Old style.\n                constraints.audio.optional.push({\n                    sourceId: options.micDeviceId\n                });\n            }\n\n            // if it is good enough for hangouts...\n            constraints.audio.optional.push(\n                { echoCancellation: !disableAEC && !disableAP },\n                { googEchoCancellation: !disableAEC && !disableAP },\n                { googAutoGainControl: !disableAGC && !disableAP },\n                { googNoiseSuppression: !disableNS && !disableAP },\n                { googHighpassFilter: !disableHPF && !disableAP },\n                { googNoiseSuppression2: !disableNS && !disableAP },\n                { googEchoCancellation2: !disableAEC && !disableAP },\n                { googAutoGainControl2: !disableAGC && !disableAP }\n            );\n        }\n    }\n    if (um.indexOf('screen') >= 0) {\n        if (browser.isChrome()) {\n            constraints.video = {\n                mandatory: getSSConstraints({\n                    ...options,\n                    source: 'screen'\n                }),\n                optional: []\n            };\n\n        } else if (browser.isFirefox()) {\n            constraints.video = {\n                mozMediaSource: 'window',\n                mediaSource: 'window',\n                frameRate: options.frameRate || {\n                    min: SS_DEFAULT_FRAME_RATE,\n                    max: SS_DEFAULT_FRAME_RATE\n                }\n            };\n\n        } else {\n            const errmsg\n                = '\\'screen\\' WebRTC media source is supported only in Chrome'\n                    + ' and Firefox';\n\n            GlobalOnErrorHandler.callErrorHandler(new Error(errmsg));\n            logger.error(errmsg);\n        }\n    }\n    if (um.indexOf('desktop') >= 0) {\n        constraints.video = {\n            mandatory: getSSConstraints({\n                ...options,\n                source: 'desktop'\n            }),\n            optional: []\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        if (browser.isElectron() && options.screenShareAudio\n            && (options.desktopStream.indexOf('screen') >= 0)) {\n\n            // Provide constraints as described by the electron desktop capturer\n            // documentation here:\n            // https://www.electronjs.org/docs/api/desktop-capturer\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 and also takes care of the previously mentioned issue.\n            constraints.audio = { mandatory: {\n                chromeMediaSource: constraints.video.mandatory.chromeMediaSource\n            } };\n        }\n    }\n\n    if (options.bandwidth) {\n        if (!constraints.video) {\n            // same behaviour as true\n            constraints.video = { mandatory: {},\n                optional: [] };\n        }\n        constraints.video.optional.push({ bandwidth: options.bandwidth });\n    }\n\n    // we turn audio for both audio and video tracks, the fake audio & video\n    // seems to work only when enabled in one getUserMedia call, we cannot get\n    // fake audio separate by fake video this later can be a problem with some\n    // of the tests\n    if (browser.isFirefox() && options.firefox_fake_device) {\n        // seems to be fixed now, removing this experimental fix, as having\n        // multiple audio tracks brake the tests\n        // constraints.audio = true;\n        constraints.fake = true;\n    }\n\n    return constraints;\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\n * 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\n * capture device to get video from.\n * @param {Object} options.constraints - Default constraints object to use\n * as a base for the returned constraints.\n * @param {Object} options.desktopStream - The desktop source id from which\n * to capture a desktop sharing video.\n * @param {string} options.facingMode - Which direction the camera is\n * pointing to.\n * @param {string} options.micDeviceId - The device id for the audio capture\n * device to get audio from.\n * @param {Object} options.frameRate - used only for dekstop sharing.\n * @param {Object} options.frameRate.min - Minimum fps\n * @param {Object} options.frameRate.max - Maximum fps\n * @private\n * @returns {Object}\n */\nfunction newGetConstraints(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        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.isSafari()) {\n            if (constraints.video.height && constraints.video.height.ideal) {\n                constraints.video.height = { ideal: clonedeep(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: clonedeep(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        // Use the standard audio constraints on non-chromium browsers.\n        if (browser.isFirefox() || browser.isSafari()) {\n            constraints.audio = {\n                deviceId: options.micDeviceId,\n                autoGainControl: !disableAGC && !disableAP,\n                echoCancellation: !disableAEC && !disableAP,\n                noiseSuppression: !disableNS && !disableAP\n            };\n        } else {\n            // NOTE(brian): the new-style ('advanced' instead of 'optional')\n            // doesn't seem to carry through the googXXX constraints\n            // Changing back to 'optional' here (even with video using\n            // the 'advanced' style) allows them to be passed through\n            // but also requires the device id to capture to be set in optional\n            // as sourceId otherwise the constraints are considered malformed.\n            if (!constraints.audio.optional) {\n                constraints.audio.optional = [];\n            }\n            constraints.audio.optional.push(\n                { sourceId: options.micDeviceId },\n                { echoCancellation: !disableAEC && !disableAP },\n                { googEchoCancellation: !disableAEC && !disableAP },\n                { googAutoGainControl: !disableAGC && !disableAP },\n                { googNoiseSuppression: !disableNS && !disableAP },\n                { googHighpassFilter: !disableHPF && !disableAP },\n                { googNoiseSuppression2: !disableNS && !disableAP },\n                { googEchoCancellation2: !disableAEC && !disableAP },\n                { googAutoGainControl2: !disableAGC && !disableAP }\n            );\n        }\n    } else {\n        constraints.audio = false;\n    }\n\n    if (um.indexOf('desktop') >= 0) {\n        if (!constraints.video || typeof constraints.video === 'boolean') {\n            constraints.video = {};\n        }\n\n        constraints.video = {\n            mandatory: getSSConstraints({\n                ...options,\n                source: 'desktop'\n            })\n        };\n    }\n\n    return constraints;\n}\n\n/**\n * Generates GUM constraints for screen sharing.\n *\n * @param {Object} options - The options passed to\n * <tt>obtainAudioAndVideoPermissions</tt>.\n * @returns {Object} - GUM constraints.\n *\n * TODO: Currently only the new GUM flow and Chrome is using the method. We\n * should make it work for all use cases.\n */\nfunction getSSConstraints(options = {}) {\n    const {\n        desktopStream,\n        frameRate = {\n            min: SS_DEFAULT_FRAME_RATE,\n            max: SS_DEFAULT_FRAME_RATE\n        }\n    } = options;\n    const { max, min } = frameRate;\n\n    const constraints = {\n        chromeMediaSource: options.source,\n        maxWidth: window.screen.width,\n        maxHeight: window.screen.height\n    };\n\n    if (typeof min === 'number') {\n        constraints.minFrameRate = min;\n    }\n\n    if (typeof max === 'number') {\n        constraints.maxFrameRate = max;\n    }\n\n    if (typeof desktopStream !== 'undefined') {\n        constraints.chromeMediaSourceId = desktopStream;\n    }\n\n    return constraints;\n}\n\n/**\n * Generates constraints for screen sharing when using getDisplayMedia.\n * The constraints(MediaTrackConstraints) are applied to the resulting track.\n *\n * @returns {Object} - MediaTrackConstraints constraints.\n */\nfunction getTrackSSConstraints(options = {}) {\n    // we used to set height and width in the constraints, but this can lead\n    // to inconsistencies if the browser is on a lower resolution screen\n    // and we share a screen with bigger resolution, so they are now not set\n    const constraints = {\n        frameRate: SS_DEFAULT_FRAME_RATE\n    };\n    const { desktopSharingFrameRate } = options;\n\n    if (desktopSharingFrameRate && desktopSharingFrameRate.max) {\n        constraints.frameRate = desktopSharingFrameRate.max;\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 * Handles the newly created Media Streams.\n * @param streams the new Media Streams\n * @param resolution the resolution of the video streams\n * @returns {*[]} object that describes the new streams\n */\nfunction handleLocalStream(streams, resolution) {\n    let audioStream, desktopStream, videoStream;\n    const res = [];\n\n    // XXX The function obtainAudioAndVideoPermissions has examined the type of\n    // the browser, its capabilities, etc. and has taken the decision whether to\n    // invoke getUserMedia per device (e.g. Firefox) or once for both audio and\n    // video (e.g. Chrome). In order to not duplicate the logic here, examine\n    // the specified streams and figure out what we've received based on\n    // obtainAudioAndVideoPermissions' decision.\n    if (streams) {\n        // As mentioned above, certian types of browser (e.g. Chrome) support\n        // (with a result which meets our requirements expressed bellow) calling\n        // getUserMedia once for both audio and video.\n        const audioVideo = streams.audioVideo;\n\n        if (audioVideo) {\n            const audioTracks = audioVideo.getAudioTracks();\n\n            if (audioTracks.length) {\n                audioStream = new MediaStream();\n                for (let i = 0; i < audioTracks.length; i++) {\n                    audioStream.addTrack(audioTracks[i]);\n                }\n            }\n\n            const videoTracks = audioVideo.getVideoTracks();\n\n            if (videoTracks.length) {\n                videoStream = new MediaStream();\n                for (let j = 0; j < videoTracks.length; j++) {\n                    videoStream.addTrack(videoTracks[j]);\n                }\n            }\n\n            audioVideo.release && audioVideo.release(false);\n        } else {\n            // On other types of browser (e.g. Firefox) we choose (namely,\n            // obtainAudioAndVideoPermissions) to call getUserMedia per device\n            // (type).\n            audioStream = streams.audio;\n            videoStream = streams.video;\n        }\n\n        desktopStream = streams.desktop;\n    }\n\n    if (desktopStream) {\n        const { stream, sourceId, sourceType } = desktopStream;\n\n        res.push({\n            stream,\n            sourceId,\n            sourceType,\n            track: stream.getVideoTracks()[0],\n            mediaType: MediaType.VIDEO,\n            videoType: VideoType.DESKTOP\n        });\n    }\n    if (audioStream) {\n        res.push({\n            stream: audioStream,\n            track: audioStream.getAudioTracks()[0],\n            mediaType: MediaType.AUDIO,\n            videoType: null\n        });\n    }\n    if (videoStream) {\n        res.push({\n            stream: videoStream,\n            track: videoStream.getVideoTracks()[0],\n            mediaType: MediaType.VIDEO,\n            videoType: VideoType.CAMERA,\n            resolution\n        });\n    }\n\n    return res;\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.disableHPF === 'boolean') {\n            disableHPF = options.disableHPF;\n            logger.info(`Disable HPF: ${disableHPF}`);\n        }\n\n        availableDevices = [];\n        window.clearInterval(availableDevicesPollTimer);\n        availableDevicesPollTimer = undefined;\n\n        this.enumerateDevices = initEnumerateDevicesWithCallback();\n\n        if (browser.usesNewGumFlow()) {\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        } else 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            const message = 'Endpoint does not appear to be WebRTC-capable';\n\n            logger.error(message);\n            throw new Error(message);\n        }\n\n        this._initPCConstraints();\n\n        screenObtainer.init(\n            options,\n            this.getUserMediaWithConstraints.bind(this));\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     * Creates instance objects for peer connection constraints both for p2p\n     * and outside of p2p.\n     */\n    _initPCConstraints() {\n        if (browser.isFirefox()) {\n            this.pcConstraints = {};\n        } else if (browser.isChromiumBased() || browser.isReactNative()) {\n            this.pcConstraints = { optional: [\n                { googHighStartBitrate: 0 },\n                { googPayloadPadding: true },\n                { googScreencastMinBitrate: 100 },\n                { googCpuOveruseDetection: true },\n                { googCpuOveruseEncodeUsage: true },\n                { googCpuUnderuseThreshold: 55 },\n                { googCpuOveruseThreshold: 85 }\n            ] };\n\n            this.p2pPcConstraints\n                = JSON.parse(JSON.stringify(this.pcConstraints));\n        }\n\n        this.p2pPcConstraints = this.p2pPcConstraints || this.pcConstraints;\n    }\n\n    /* eslint-disable max-params */\n\n    /**\n    * @param {string[]} um required user media types\n    * @param {Object} [options] optional parameters\n    * @param {string} options.resolution\n    * @param {number} options.bandwidth\n    * @param {number} options.fps\n    * @param {string} options.desktopStream\n    * @param {string} options.cameraDeviceId\n    * @param {string} options.micDeviceId\n    * @param {Object} options.frameRate - used only for dekstop sharing.\n    * @param {Object} options.frameRate.min - Minimum fps\n    * @param {Object} options.frameRate.max - Maximum fps\n    * @param {bool}   options.screenShareAudio - Used by electron clients to\n    * enable system audio screen sharing.\n    * @param {number} options.timeout - The timeout in ms for GUM.\n    * @returns {Promise} Returns a media stream on success or a JitsiTrackError\n    * on failure.\n    **/\n    getUserMediaWithConstraints(um, options = {}) {\n        const {\n            timeout,\n            ...otherOptions\n        } = options;\n        const constraints = getConstraints(um, otherOptions);\n\n        logger.info('Get media constraints', JSON.stringify(constraints));\n\n        return this._getUserMedia(um, constraints, timeout);\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(error);\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     * @param {Object} options\n     * @param {string[]} options.desktopSharingSources\n     * @param {Object} options.desktopSharingFrameRate\n     * @param {Object} options.desktopSharingFrameRate.min - Minimum fps\n     * @param {Object} options.desktopSharingFrameRate.max - Maximum fps\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    _newGetDesktopMedia(options) {\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                this._parseDesktopSharingOptions(options),\n                stream => {\n                    resolve(stream);\n                },\n                error => {\n                    reject(error);\n                });\n        });\n    }\n\n    /* eslint-enable max-params */\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     * @param {Object} options.desktopSharingFrameRate\n     * @param {Object} options.desktopSharingFrameRate.min - Minimum fps\n     * @param {Object} options.desktopSharingFrameRate.max - Maximum fps\n     * @returns {*} Promise object that will receive the new JitsiTracks\n     */\n    obtainAudioAndVideoPermissions(options = {}) {\n        options.devices = options.devices || [ ...OLD_GUM_DEFAULT_DEVICES ];\n        options.resolution = options.resolution || OLD_GUM_DEFAULT_RESOLUTION;\n\n        const requestingDesktop = options.devices.includes('desktop');\n\n        if (requestingDesktop && !screenObtainer.isSupported()) {\n            return Promise.reject(\n                new Error('Desktop sharing is not supported!'));\n        }\n\n        return this._getAudioAndVideoStreams(options).then(streams =>\n            handleLocalStream(streams, options.resolution));\n    }\n\n    /**\n     * Performs one call to getUserMedia for audio and/or video and another call\n     * for desktop.\n     *\n     * @param {Object} options - An object describing how the gUM request should\n     * be executed. See {@link obtainAudioAndVideoPermissions} for full options.\n     * @returns {*} Promise object that will receive the new JitsiTracks on\n     * success or a JitsiTrackError on failure.\n     */\n    _getAudioAndVideoStreams(options) {\n        const requestingDesktop = options.devices.includes('desktop');\n\n        options.devices = options.devices.filter(device =>\n            device !== 'desktop');\n\n        const gumPromise = options.devices.length\n            ? this.getUserMediaWithConstraints(options.devices, options)\n            : Promise.resolve(null);\n\n        return gumPromise\n            .then(avStream => {\n                // If any requested devices are missing, call gum again in\n                // an attempt to obtain the actual error. For example, the\n                // requested video device is missing or permission was\n                // denied.\n                const missingTracks\n                    = this._getMissingTracks(options.devices, avStream);\n\n                if (missingTracks.length) {\n                    this.stopMediaStream(avStream);\n\n                    return this.getUserMediaWithConstraints(\n                        missingTracks, options)\n\n                        // GUM has already failed earlier and this success\n                        // handling should not be reached.\n                        .then(() => Promise.reject(new JitsiTrackError(\n                            { name: 'UnknownError' },\n                            getConstraints(options.devices, options),\n                            missingTracks)));\n                }\n\n                return avStream;\n            })\n            .then(audioVideo => {\n                if (!requestingDesktop) {\n                    return { audioVideo };\n                }\n\n                if (options.desktopSharingSourceDevice) {\n                    this.stopMediaStream(audioVideo);\n\n                    throw new Error('Using a camera as screenshare source is'\n                        + 'not supported on this browser.');\n                }\n\n                return new Promise((resolve, reject) => {\n                    screenObtainer.obtainStream(\n                        this._parseDesktopSharingOptions(options),\n                        desktop => resolve({\n                            audioVideo,\n                            desktop\n                        }),\n                        error => {\n                            if (audioVideo) {\n                                this.stopMediaStream(audioVideo);\n                            }\n                            reject(error);\n                        });\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     * Returns an object formatted for specifying desktop sharing parameters.\n     *\n     * @param {Object} options - Takes in the same options object as\n     * {@link obtainAudioAndVideoPermissions}.\n     * @returns {Object}\n     */\n    _parseDesktopSharingOptions(options) {\n        return {\n            desktopSharingSources: options.desktopSharingSources,\n            gumOptions: {\n                frameRate: options.desktopSharingFrameRate\n            },\n            trackOptions: getTrackSSConstraints(options)\n        };\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    newObtainAudioAndVideoPermissions(options) {\n        logger.info('Using the new gUM flow');\n\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                desktopSharingSources,\n                desktopSharingFrameRate\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\n                // Leverage the helper used by {@link _newGetDesktopMedia} to\n                // get constraints for the desktop stream.\n                const { gumOptions, trackOptions }\n                    = this._parseDesktopSharingOptions(otherOptions);\n\n                const constraints = {\n                    video: {\n                        ...gumOptions,\n                        deviceId: matchingDevice.deviceId\n                    }\n                };\n\n                return this._getUserMedia(requestedDevices, constraints, timeout)\n                    .then(stream => {\n                        const track = stream && stream.getTracks()[0];\n                        const applyConstrainsPromise\n                            = track && track.applyConstraints\n                                ? track.applyConstraints(trackOptions)\n                                : Promise.resolve();\n\n                        return applyConstrainsPromise\n                            .then(() => {\n                                return {\n                                    sourceType: 'device',\n                                    stream\n                                };\n                            });\n                    });\n            }\n\n            return this._newGetDesktopMedia({\n                desktopSharingSources,\n                desktopSharingFrameRate\n            });\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 = newGetConstraints(\n                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        return deviceType === 'output' || deviceType === 'audiooutput'\n            ? isAudioOutputDeviceChangeAvailable\n            : 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     * 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 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","/**\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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport VideoType from '../../service/RTC/VideoType';\nimport browser from '../browser';\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 *\n * @param tracksInfo\n * @param options\n */\nfunction createLocalTracks(tracksInfo, options) {\n    const newTracks = [];\n    let deviceId = null;\n\n    tracksInfo.forEach(trackInfo => {\n        if (trackInfo.mediaType === MediaType.AUDIO) {\n            deviceId = options.micDeviceId;\n        } else if (trackInfo.videoType === VideoType.CAMERA) {\n            deviceId = options.cameraDeviceId;\n        }\n        rtcTrackIdCounter = safeCounterIncrement(rtcTrackIdCounter);\n        const localTrack = new JitsiLocalTrack({\n            ...trackInfo,\n            deviceId,\n            facingMode: options.facingMode,\n            rtcId: rtcTrackIdCounter,\n            effects: options.effects\n        });\n\n        newTracks.push(localTrack);\n    });\n\n    return newTracks;\n}\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 _newCreateLocalTracks(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 = -1;\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         * Holds the sender video constraints signaled from the bridge.\n         */\n        this._senderVideoConstraints = {};\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 = [];\n\n        // The last N change listener.\n        this._lastNChangeListener = this._onLastNChanged.bind(this);\n\n        this._onDeviceListChanged = this._onDeviceListChanged.bind(this);\n        this._updateAudioOutputForAudioTracks\n            = this._updateAudioOutputForAudioTracks.bind(this);\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(\n            RTCEvents.AUDIO_OUTPUT_DEVICE_CHANGED,\n            this._updateAudioOutputForAudioTracks\n        );\n\n        RTCUtils.removeListener(\n            RTCEvents.DEVICE_LIST_CHANGED,\n            this._onDeviceListChanged\n        );\n\n        this.removeListener(\n            RTCEvents.LASTN_ENDPOINT_CHANGED,\n            this._lastNChangeListener\n        );\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 newCreateLocalTracks(tracksInfo) {\n        return _newCreateLocalTracks(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        const usesNewGumFlow = browser.usesNewGumFlow();\n        const obtainMediaPromise = usesNewGumFlow\n            ? RTCUtils.newObtainAudioAndVideoPermissions(options)\n            : RTCUtils.obtainAudioAndVideoPermissions(options);\n\n        return obtainMediaPromise.then(tracksInfo => {\n            if (usesNewGumFlow) {\n                return _newCreateLocalTracks(tracksInfo);\n            }\n\n            return createLocalTracks(tracksInfo, options);\n        });\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(\n            peerconnection, wsUrl, this.eventEmitter, this._senderVideoConstraintsChanged.bind(this));\n\n        this._channelOpenListener = () => {\n            // When the channel becomes available, tell the bridge about\n            // video selections so that it can do adaptive simulcast,\n            // we want the notification to trigger even if userJid\n            // is undefined, or null.\n            try {\n                this._channel.sendSelectedEndpointsMessage(\n                    this._selectedEndpoints);\n\n                if (typeof this._maxFrameHeight !== 'undefined') {\n                    this._channel.sendReceiverVideoConstraintMessage(\n                        this._maxFrameHeight);\n                }\n            } catch (error) {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error(\n                    `Cannot send selected(${this._selectedEndpoint})`\n                    + `frameHeight(${this._maxFrameHeight}) endpoint message`,\n                    error);\n            }\n\n            this.removeListener(RTCEvents.DATA_CHANNEL_OPEN,\n                this._channelOpenListener);\n            this._channelOpenListener = null;\n\n            // If setLastN was invoked before the bridge channel completed\n            // opening, apply the specified value now that the channel\n            // is open. NOTE that -1 is the default value assumed by both\n            // RTC module and the JVB.\n            if (this._lastN !== -1) {\n                this._channel.sendSetLastNMessage(this._lastN);\n            }\n        };\n\n        this.addListener(RTCEvents.DATA_CHANNEL_OPEN,\n            this._channelOpenListener);\n\n        // Add Last N change listener.\n        this.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,\n            this._lastNChangeListener);\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     * Notifies this instance that the sender video constraints signaled from the bridge have changed.\n     *\n     * @param {Object} senderVideoConstraints the sender video constraints from the bridge.\n     * @private\n     */\n    _senderVideoConstraintsChanged(senderVideoConstraints) {\n        logger.info('Remote max frame height received on bridge channel: ', JSON.stringify(senderVideoConstraints));\n        this._senderVideoConstraints = senderVideoConstraints;\n        this.eventEmitter.emit(RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED);\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     * 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 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     * 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\n     *      provide information about the media or participants which is not\n     *      carried over SDP.\n     * @param {object} iceConfig An object describing the ICE config like\n     *      defined in the WebRTC specification.\n     * @param {boolean} isP2P Indicates whether or not the new TPC will be used\n     *      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\n     *      the simulcast.\n     * @param {boolean} options.disableRtx If set to 'true' will disable the\n     *      RTX.\n     * @param {boolean} options.disableH264 If set to 'true' H264 will be\n     *      disabled by removing it from the SDP.\n     * @param {boolean} options.preferH264 If set to 'true' H264 will be\n     *      preferred over other video codecs.\n     * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\n     * @return {TraceablePeerConnection}\n     */\n    createPeerConnection(signaling, iceConfig, isP2P, options) {\n        const pcConstraints = RTC.getPCConstraints(isP2P);\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        // FIXME: We should rename iceConfig to pcConfig.\n\n        if (options.enableInsertableStreams) {\n            logger.debug('E2EE - setting insertable streams constraints');\n            iceConfig.encodedInsertableStreams = true;\n            iceConfig.forceEncodedAudioInsertableStreams = true; // legacy, to be removed in M88.\n            iceConfig.forceEncodedVideoInsertableStreams = true; // legacy, to be removed in M88.\n        }\n\n        if (browser.supportsSdpSemantics()) {\n            iceConfig.sdpSemantics = 'plan-b';\n        }\n\n        if (options.forceTurnRelay) {\n            iceConfig.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        iceConfig.bundlePolicy = 'max-bundle';\n\n        peerConnectionIdCounter = safeCounterIncrement(peerConnectionIdCounter);\n\n        const newConnection\n            = new TraceablePeerConnection(\n                this,\n                peerConnectionIdCounter,\n                signaling,\n                iceConfig, 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     * Returns the current value for \"lastN\" - the amount of videos are going\n     * to be delivered. When set to -1 for unlimited or all available videos.\n     * @return {number}\n     */\n    getLastN() {\n        return this._lastN;\n    }\n\n    /**\n     * @return {Object} The sender video constraints signaled from the brridge.\n     */\n    getSenderVideoConstraints() {\n        return this._senderVideoConstraints;\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     * 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 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     *\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     * Removes all JitsiRemoteTracks associated with given MUC nickname\n     * (resource part of the JID). Returns array of removed tracks.\n     *\n     * @param {string} Owner The resource part of the MUC JID.\n     * @returns {JitsiRemoteTrack[]}\n     */\n    removeRemoteTracks(owner) {\n        let removedTracks = [];\n\n        for (const tpc of this.peerConnections.values()) {\n            const pcRemovedTracks = tpc.removeRemoteTracks(owner);\n\n            removedTracks = removedTracks.concat(pcRemovedTracks);\n        }\n\n        logger.debug(\n            `Removed remote tracks for ${owner}`\n                + ` count: ${removedTracks.length}`);\n\n        return removedTracks;\n    }\n\n    /**\n     *\n     */\n    static getPCConstraints(isP2P) {\n        const pcConstraints\n            = isP2P ? RTCUtils.p2pPcConstraints : RTCUtils.pcConstraints;\n\n        if (!pcConstraints) {\n            return {};\n        }\n\n        return JSON.parse(JSON.stringify(pcConstraints));\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 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,\n                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    /* eslint-enable max-params */\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     * 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     * 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","/**\n * The errors for the JitsiTrack objects.\n */\n\n/**\n * An error which indicates that some of requested constraints in\n * getUserMedia call were not satisfied.\n */\nexport const 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 */\nexport const ELECTRON_DESKTOP_PICKER_ERROR\n    = '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 */\nexport const ELECTRON_DESKTOP_PICKER_NOT_FOUND\n    = 'gum.electron_desktop_picker_not_found';\n\n/**\n * Generic getUserMedia error.\n */\nexport const GENERAL = 'gum.general';\n\n/**\n * An error which indicates that requested device was not found.\n */\nexport const NOT_FOUND = 'gum.not_found';\n\n/**\n * An error which indicates that user denied permission to share requested\n * device.\n */\nexport const PERMISSION_DENIED = 'gum.permission_denied';\n\n/**\n * Generic error for screensharing failure.\n */\nexport const SCREENSHARING_GENERIC_ERROR\n    = 'gum.screensharing_generic_error';\n\n/**\n * An error which indicates that user canceled screen sharing window\n * selection dialog.\n */\nexport const SCREENSHARING_USER_CANCELED\n    = 'gum.screensharing_user_canceled';\n\n\n/**\n * Indicates that the timeout passed to the obtainAudioAndVideoPermissions has expired without GUM resolving.\n */\nexport const TIMEOUT = 'gum.timeout';\n\n/**\n * An error which indicates that track has been already disposed and cannot\n * be longer used.\n */\nexport const TRACK_IS_DISPOSED = 'track.track_is_disposed';\n\n/**\n * An error which indicates that track has no MediaStream associated.\n */\nexport const 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 */\nexport const UNSUPPORTED_RESOLUTION = 'gum.unsupported_resolution';\n","/* global module */\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","/**\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 * The errors for the conference.\n */\n\n/**\n * Indicates that client must be authenticated to create the conference.\n */\nexport const AUTHENTICATION_REQUIRED = 'conference.authenticationRequired';\n\n/**\n * Indicates that chat error occurred.\n */\nexport const CHAT_ERROR = 'conference.chatError';\n\n/**\n * Indicates that conference has been destroyed.\n */\nexport const CONFERENCE_DESTROYED = 'conference.destroyed';\n\n/**\n * Indicates that max users limit has been reached.\n */\nexport const CONFERENCE_MAX_USERS = 'conference.max_users';\n\n/**\n * Indicates that a connection error occurred when trying to join a conference.\n */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const CONFERENCE_ACCESS_DENIED = 'conference.connectionError.accessDenied';\n\n/**\n * Indicates that focus error happened.\n */\nexport const FOCUS_DISCONNECTED = 'conference.focusDisconnected';\n\n/**\n * Indicates that focus left the conference.\n */\nexport const FOCUS_LEFT = 'conference.focusLeft';\n\n/**\n * Indicates that graceful shutdown happened.\n */\nexport const GRACEFUL_SHUTDOWN = 'conference.gracefulShutdown';\n\n/**\n * Indicates that the media connection has failed.\n */\nexport const ICE_FAILED = 'conference.iceFailed';\n\n/**\n * Indicates that the versions of the server side components are incompatible\n * with the client side.\n */\nexport const INCOMPATIBLE_SERVER_VERSIONS\n    = 'conference.incompatible_server_versions';\n\n/**\n * Indicates that offer/answer had failed.\n */\nexport const OFFER_ANSWER_FAILED = 'conference.offerAnswerFailed';\n\n/**\n * Indicates that password cannot be set for this conference.\n */\nexport const PASSWORD_NOT_SUPPORTED = 'conference.passwordNotSupported';\n\n/**\n * Indicates that a password is required in order to join the conference.\n */\nexport const PASSWORD_REQUIRED = 'conference.passwordRequired';\n\n/**\n * Indicates that reservation system returned error.\n */\nexport const RESERVATION_ERROR = 'conference.reservationError';\n\n/**\n * Indicates that there is no available videobridge.\n */\nexport const VIDEOBRIDGE_NOT_AVAILABLE = 'conference.videobridgeNotAvailable';\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;\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","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","/* global $ */\n\nimport browser from '../browser';\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 * 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                    elem.c('source', {\n                        ssrc: availableSsrc,\n                        xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0'\n                    });\n\n                    ssrcParameters.forEach(ssrcSdpLine => {\n                        // get everything after first space\n                        const idx = ssrcSdpLine.indexOf(' ');\n                        const kv = ssrcSdpLine.substr(idx + 1);\n\n                        elem.c('parameter');\n                        if (kv.indexOf(':') === -1) {\n                            elem.attrs({ name: kv });\n                        } else {\n                            const name = kv.split(':', 2)[0];\n\n                            elem.attrs({ name });\n\n                            let v = kv.split(':', 2)[1];\n\n                            v = SDPUtil.filterSpecialChars(v);\n                            elem.attrs({ value: v });\n                        }\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 'sendonly':\n                        elem.attrs({ senders: 'responder' });\n                        break;\n                    case 'recvonly':\n                        elem.attrs({ senders: 'initiator' });\n                        break;\n                    case 'sendrecv':\n                        elem.attrs({ senders: 'both' });\n                        break;\n                    case '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=sendrecv', this.session)) {\n            elem.attrs({ senders: 'both' });\n        } else if (SDPUtil.findLine(m, 'a=sendonly', this.session)) {\n            elem.attrs({ senders: 'initiator' });\n        } else if (SDPUtil.findLine(m, 'a=recvonly', this.session)) {\n            elem.attrs({ senders: 'responder' });\n        } else if (SDPUtil.findLine(m, 'a=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 sctpmap\n        = SDPUtil.findLine(this.media[mediaindex], 'a=sctpmap:', this.session);\n\n    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        }\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 = '1';\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 ? 'DTLS/SCTP' : 'RTP/SAVPF';\n    } else {\n        media.proto = 'RTP/AVPF';\n    }\n    if (sctp.length) {\n        sdp += `m=application ${media.port} DTLS/SCTP ${\n            sctp.attr('number')}\\r\\n`;\n        sdp += `a=sctpmap:${sctp.attr('number')} ${sctp.attr('protocol')}`;\n\n        const streamCount = sctp.attr('streams');\n\n        if (streamCount) {\n            sdp += ` ${streamCount}\\r\\n`;\n        } else {\n            sdp += '\\r\\n';\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=sendonly\\r\\n';\n        break;\n    case 'responder':\n        sdp += 'a=recvonly\\r\\n';\n        break;\n    case 'none':\n        sdp += 'a=inactive\\r\\n';\n        break;\n    case 'both':\n        sdp += 'a=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    desc\n        .find('>source[xmlns=\"urn:xmpp:jingle:apps:rtp:ssma:0\"]')\n        .each((_, source) => {\n            const ssrc = source.getAttribute('ssrc');\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                    sdp += `a=ssrc:${ssrc} ${name}`;\n                    if (value && value.length) {\n                        sdp += `:${value}`;\n                    }\n                    sdp += '\\r\\n';\n                });\n        });\n\n    return sdp;\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","/**\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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const VAD_TALK_WHILE_MUTED = 'detection.vad_talk_while_muted';\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 */\nexport const 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 */\nexport const BEFORE_DISPOSED = 'statistics.before_disposed';\n\n/**\n * An event carrying all statistics by ssrc.\n */\nexport const 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 */\nexport const CONNECTION_STATS = 'statistics.connectionstats';\n\n/**\n * An event carrying performance stats.\n */\nexport const LONG_TASKS_STATS = 'statistics.long_tasks_stats';\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","/**\n * Status that video SIP GW service is available.\n * @type {string}\n */\nexport const STATUS_AVAILABLE = 'available';\n\n/**\n * Status that video SIP GW service is not available.\n * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const STATUS_BUSY = 'busy';\n\n/**\n * Video SIP GW session state, currently running.\n * @type {string}\n */\nexport const STATE_ON = 'on';\n\n/**\n * Video SIP GW session state, currently stopped and not running.\n * @type {string}\n */\nexport const STATE_OFF = 'off';\n\n/**\n * Video SIP GW session state, currently is starting.\n * @type {string}\n */\nexport const STATE_PENDING = 'pending';\n\n/**\n * Video SIP GW session state, has observed some issues and is retrying at the\n * moment.\n * @type {string}\n */\nexport const STATE_RETRYING = 'retrying';\n\n/**\n * Video SIP GW session state, tried to start but it failed.\n * @type {string}\n */\nexport const STATE_FAILED = 'failed';\n\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 * @type {string}\n */\nexport const 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 * @type {string}\n */\nexport const ERROR_SESSION_EXISTS = 'error_session_already_exists';\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';\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\\/([\\d.]+)/)[1];\n\n        return {\n            name: ELECTRON,\n            version\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","/**\n * The events for the connection.\n */\n\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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const 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 */\nexport const DISPLAY_NAME_REQUIRED = 'connection.display_name_required';\n","/* global module */\n/**\n * Enumeration of the codec mime types\n * @type {{H264: string, VP8: string, VP9: string}}\n */\nconst CodecMimeType = {\n    /**\n     * The h264 codec mime type\n     */\n    H264: 'h264',\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","/* global $ */\n\nimport { getLogger } from 'jitsi-meet-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 GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\nimport RandomUtil from '../util/RandomUtil';\n\nimport Caps 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 * 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, this.options.clientNode);\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('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\n        // Disable RTX on Firefox 83 and older versions because of\n        // https://bugzilla.mozilla.org/show_bug.cgi?id=1668028\n        if (!(this.options.disableRtx || (browser.isFirefox() && browser.isVersionLessThan(84)))) {\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        if (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        if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {\n            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            // FIXME no need to do it again on stream resume\n            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                    // check for speakerstats\n                    identities.forEach(identity => {\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                            identity.name && this.caps.getFeaturesAndIdentities(identity.name, identity.type)\n                                .then(({ features: f }) => {\n                                    f.forEach(fr => {\n                                        if (fr.endsWith('#displayname_required')) {\n                                            this.eventEmitter.emit(\n                                                JitsiConnectionEvents.DISPLAY_NAME_REQUIRED);\n                                        }\n                                    });\n                                })\n                                .catch(e => logger.warn('Error getting features from lobby.', e && e.message));\n                        }\n                    });\n\n                    if (this.speakerStatsComponentAddress\n                        || this.conferenceDurationComponentAddress) {\n                        this.connection.addHandler(\n                            this._onPrivateMessage.bind(this), null,\n                            'message', null, null);\n                    }\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\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            // wrong password or username, prompt user\n            this.eventEmitter.emit(\n                JitsiConnectionEvents.CONNECTION_FAILED,\n                JitsiConnectionErrors.PASSWORD_REQUIRED,\n                msg,\n                credentials);\n        }\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        this.connection.connect(\n            jid,\n            password,\n            this.connectionHandler.bind(this, {\n                jid,\n                password\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        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        // There are cases (when using subdomain) where muc can hold an uppercase part\n        let roomjid = `${roomName}@${options.customDomain\n            ? options.customDomain : this.options.hosts.muc.toLowerCase()}/`;\n\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 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     * 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        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(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            return true;\n        }\n\n        const jsonMessage = $(msg).find('>json-message')\n            .text();\n        const parsedJson = this.tryParseJSONAndVerify(jsonMessage);\n\n        if (parsedJson\n            && parsedJson[JITSI_MEET_MUC_TYPE] === 'speakerstats'\n            && parsedJson.users) {\n            this.eventEmitter.emit(\n                XMPPEvents.SPEAKER_STATS_RECEIVED, parsedJson.users);\n        }\n\n        if (parsedJson\n            && parsedJson[JITSI_MEET_MUC_TYPE] === 'conference_duration'\n            && parsedJson.created_timestamp) {\n            this.eventEmitter.emit(\n                XMPPEvents.CONFERENCE_TIMESTAMP_RECEIVED, parsedJson.created_timestamp);\n        }\n\n        return true;\n    }\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","import { jitsiLocalStorage } from '@jitsi/js-utils';\nimport { getLogger } from 'jitsi-meet-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            _machineId = this._storage.getItem('jitsiMeetId');\n            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","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 the first media SDP of given name.\n     * @param {string} mediaType the name of the media e.g. 'audio', 'video',\n     * 'data'.\n     * @return {MLineWrap|null} return {@link MLineWrap} instance for the media\n     * line or <tt>null</tt> if not found. The object returned references\n     * the underlying SDP state held by this <tt>SdpTransformWrap</tt> instance\n     * (it's not a copy).\n     */\n    selectMedia(mediaType) {\n        const selectedMLine\n            = this.parsedSDP.media.find(mLine => mLine.type === mediaType);\n\n        return selectedMLine ? new MLineWrap(selectedMLine) : 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","/**\n * Indicates that the local connection statistics were updated.\n */\nexport const LOCAL_STATS_UPDATED = 'cq.local_stats_updated';\n\n/**\n * Indicates that the connection statistics for a particular remote participant\n * were updated.\n */\nexport const REMOTE_STATS_UPDATED = 'cq.remote_stats_updated';\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 * @type {string}\n */\nexport const PENDING = 'pending';\n\n/**\n * The active Jingle session state as defined in XEP-0166\n * (after 'session-invite'/'session-accept').\n *\n * @type {string}\n */\nexport const ACTIVE = 'active';\n\n/**\n * The ended Jingle session state as defined in XEP-0166\n * (after 'session-terminate').\n * @type {string}\n */\nexport const ENDED = 'ended';\n","/**\n * The know jingle actions that can be sent and should be acted upon by\n * {@code ProxyConnectionService} and {@code ProxyConnectionPC}.\n */\nexport const 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","const Resolutions = {\n    '1080': {\n        width: 1920,\n        height: 1080,\n        order: 8\n    },\n    'fullhd': {\n        width: 1920,\n        height: 1080,\n        order: 8\n    },\n    '720': {\n        width: 1280,\n        height: 720,\n        order: 7\n    },\n    'hd': {\n        width: 1280,\n        height: 720,\n        order: 7\n    },\n    '960': {\n        width: 960,\n        height: 720,\n        order: 6\n    },\n    '540': {\n        width: 960,\n        height: 540,\n        order: 5\n    },\n    'qhd': {\n        width: 960,\n        height: 540,\n        order: 5\n    },\n\n    // 16:9 resolution first.\n    '360': {\n        width: 640,\n        height: 360,\n        order: 4\n    },\n    '640': {\n        width: 640,\n        height: 480,\n        order: 3\n    },\n    'vga': {\n        width: 640,\n        height: 480,\n        order: 3\n    },\n\n    // 16:9 resolution first.\n    '180': {\n        width: 320,\n        height: 180,\n        order: 2\n    },\n    '320': {\n        width: 320,\n        height: 240,\n        order: 1\n    }\n};\n\nmodule.exports = Resolutions;\n","/* global callstats */\n\nimport browser from '../browser';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\n\nconst logger = require('jitsi-meet-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     *\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            let configParams;\n\n            if (options.applicationName) {\n                configParams = {\n                    applicationVersion:\n                        `${options.applicationName} (${\n                            browser.getName()})`\n                };\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 * @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","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","/**\n * The errors for the connection.\n */\n\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 */\nexport const CONNECTION_DROPPED_ERROR = 'connection.droppedError';\n\n/**\n * Not specified errors.\n */\nexport const OTHER_ERROR = 'connection.otherError';\n\n/**\n * Indicates that a password is required in order to join the conference.\n */\nexport const 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 */\nexport const SERVER_ERROR = 'connection.serverError';\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","import { getLogger } from 'jitsi-meet-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._lastSuccessTracker = new LastSuccessTracker();\n        this._lastSuccessTracker.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     * 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._lastSuccessTracker.getTimeSinceLastSuccess();\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","/**\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 */\nexport const 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 */\nexport const PEER_VIDEO_TYPE_CHANGED = 'signaling.peerVideoType';\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 * 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","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","\nimport JitsiTrackError from '../../JitsiTrackError';\nimport * as JitsiTrackErrors from '../../JitsiTrackErrors';\nimport browser from '../browser';\n\nconst logger = require('jitsi-meet-logger').getLogger(__filename);\n\nlet gumFunction = null;\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     * @param {Function} gum GUM method\n     */\n    init(options = {}, gum) {\n        this.options = options;\n        gumFunction = gum;\n\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     * 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 {Object} [options] - Screen sharing options.\n     * @param {Array<string>} [options.desktopSharingSources] - Array with the\n     * sources that have to be displayed in the desktop picker window ('screen',\n     * 'window', etc.).\n     * @param onSuccess - Success callback.\n     * @param onFailure - Failure callback.\n     */\n    obtainScreenOnElectron(options = {}, onSuccess, onFailure) {\n        if (window.JitsiMeetScreenObtainer\n            && window.JitsiMeetScreenObtainer.openDesktopPicker) {\n            const { desktopSharingSources, gumOptions } = options;\n\n            window.JitsiMeetScreenObtainer.openDesktopPicker(\n                {\n                    desktopSharingSources: desktopSharingSources || [ 'screen', 'window' ]\n                },\n                (streamId, streamType, screenShareAudio = false) =>\n                    onGetStreamResponse(\n                        {\n                            response: {\n                                streamId,\n                                streamType,\n                                screenShareAudio\n                            },\n                            gumOptions\n                        },\n                        onSuccess,\n                        onFailure\n                    ),\n                err => onFailure(new JitsiTrackError(\n                    JitsiTrackErrors.ELECTRON_DESKTOP_PICKER_ERROR,\n                    err\n                ))\n            );\n        } else {\n            onFailure(new JitsiTrackError(\n                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(options, callback, errorCallback) {\n        logger.info('Using getDisplayMedia for screen sharing');\n\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        getDisplayMedia({\n            video: true,\n            audio: true,\n            cursor: 'always'\n        })\n            .then(stream => {\n                let applyConstraintsPromise;\n\n                if (stream\n                    && stream.getTracks()\n                    && stream.getTracks().length > 0) {\n                    const videoTrack = stream.getVideoTracks()[0];\n\n                    // Apply video track constraint.\n                    if (videoTrack) {\n                        applyConstraintsPromise = videoTrack.applyConstraints(options.trackOptions);\n                    }\n                } else {\n                    applyConstraintsPromise = Promise.resolve();\n                }\n\n                applyConstraintsPromise.then(() =>\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', 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(options, 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/**\n * Handles response from external application / extension and calls GUM to\n * receive the desktop streams or reports error.\n * @param {object} options\n * @param {object} options.response\n * @param {string} options.response.streamId - the streamId for the desktop\n * stream.\n * @param {bool}   options.response.screenShareAudio - Used by electron clients to\n * enable system audio screen sharing.\n * @param {string} options.response.error - error to be reported.\n * @param {object} options.gumOptions - options passed to GUM.\n * @param {Function} onSuccess - callback for success.\n * @param {Function} onFailure - callback for failure.\n * @param {object} gumOptions - options passed to GUM.\n */\nfunction onGetStreamResponse(\n        options = {\n            response: {},\n            gumOptions: {}\n        },\n        onSuccess,\n        onFailure) {\n    const { streamId, streamType, screenShareAudio, error } = options.response || {};\n\n    if (streamId) {\n        const gumOptions = {\n            desktopStream: streamId,\n            screenShareAudio,\n            ...options.gumOptions\n        };\n\n        gumFunction([ 'desktop' ], gumOptions)\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        if (streamId === '') {\n            onFailure(new JitsiTrackError(\n                JitsiTrackErrors.SCREENSHARING_USER_CANCELED));\n\n            return;\n        }\n\n        onFailure(new JitsiTrackError(\n            JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR,\n            error));\n    }\n}\n\nexport default ScreenObtainer;\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","/* global __filename */\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport * as 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\n * {@link ParticipantConnectionStatus.outOfLastNTimeout}.\n *\n * @type {number}\n */\nconst DEFAULT_NOT_IN_LAST_N_TIMEOUT = 500;\n\n/**\n * Default value of 2000 milliseconds for\n * {@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.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'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 : 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.rtc.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            logger.debug('Assuming connection active by JVB - no notification');\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(\n            'leaving/entering lastN', leavingLastN, enteringLastN, now);\n\n        for (const id of leavingLastN) {\n            this.enteredLastNTimestamp.delete(id);\n            this._clearRestoringTimer(id);\n            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            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","import { getLogger } from 'jitsi-meet-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","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nvar 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(function(line) {\n    return line.trim();\n  });\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n  var parts = blob.split('\\nm=');\n  return parts.map(function(part, index) {\n    return (index > 0 ? 'm=' + part : part).trim() + '\\r\\n';\n  });\n};\n\n// returns the session description.\nSDPUtils.getDescription = function(blob) {\n  var sections = SDPUtils.splitSections(blob);\n  return sections && sections[0];\n};\n\n// returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n  var 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(function(line) {\n    return line.indexOf(prefix) === 0;\n  });\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\"\nSDPUtils.parseCandidate = function(line) {\n  var 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  var candidate = {\n    foundation: parts[0],\n    component: parseInt(parts[1], 10),\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 (var 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 compability.\n        candidate.usernameFragment = parts[i + 1];\n        break;\n      default: // extension handling, in particular ufrag\n        candidate[parts[i]] = parts[i + 1];\n        break;\n    }\n  }\n  return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\nSDPUtils.writeCandidate = function(candidate) {\n  var sdp = [];\n  sdp.push(candidate.foundation);\n  sdp.push(candidate.component);\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  var 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// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n  return line.substr(14).split(' ');\n};\n\n// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n  var parts = line.substr(9).split(' ');\n  var 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// Generate an a=rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  var 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 an 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  var 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 a=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 an ftmp 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  var parsed = {};\n  var kv;\n  var parts = line.substr(line.indexOf(' ') + 1).split(';');\n  for (var 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 an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n  var line = '';\n  var pt = codec.payloadType;\n  if (codec.preferredPayloadType !== undefined) {\n    pt = codec.preferredPayloadType;\n  }\n  if (codec.parameters && Object.keys(codec.parameters).length) {\n    var params = [];\n    Object.keys(codec.parameters).forEach(function(param) {\n      if (codec.parameters[param]) {\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 an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n  var parts = line.substr(line.indexOf(' ') + 1).split(' ');\n  return {\n    type: parts.shift(),\n    parameter: parts.join(' ')\n  };\n};\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n  var lines = '';\n  var 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(function(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 an RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n  var sp = line.indexOf(' ');\n  var parts = {\n    ssrc: parseInt(line.substr(7, sp - 7), 10)\n  };\n  var 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\nSDPUtils.parseSsrcGroup = function(line) {\n  var parts = line.substr(13).split(' ');\n  return {\n    semantics: parts.shift(),\n    ssrcs: parts.map(function(ssrc) {\n      return parseInt(ssrc, 10);\n    })\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  var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n  if (mid) {\n    return mid.substr(6);\n  }\n};\n\nSDPUtils.parseFingerprint = function(line) {\n  var parts = line.substr(14).split(' ');\n  return {\n    algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n    value: parts[1]\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  var lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=fingerprint:');\n  // Note: a=setup line is ignored since we use the 'auto' role.\n  // Note2: 'algorithm' is not case sensitive except 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  var sdp = 'a=setup:' + setupType + '\\r\\n';\n  params.fingerprints.forEach(function(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  var 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  var 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 paramters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n  var 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  var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n    'a=ice-ufrag:')[0];\n  var 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  return 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n      'a=ice-pwd:' + params.password + '\\r\\n';\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n  var description = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: [],\n    rtcp: []\n  };\n  var lines = SDPUtils.splitLines(mediaSection);\n  var mline = lines[0].split(' ');\n  for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n    var pt = mline[i];\n    var rtpmapline = SDPUtils.matchPrefix(\n      mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n    if (rtpmapline) {\n      var codec = SDPUtils.parseRtpMap(rtpmapline);\n      var 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(function(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  var 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(function(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(function(codec) {\n    sdp += SDPUtils.writeRtpMap(codec);\n    sdp += SDPUtils.writeFmtp(codec);\n    sdp += SDPUtils.writeRtcpFb(codec);\n  });\n  var maxptime = 0;\n  caps.codecs.forEach(function(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  sdp += 'a=rtcp-mux\\r\\n';\n\n  if (caps.headerExtensions) {\n    caps.headerExtensions.forEach(function(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  var encodingParameters = [];\n  var description = SDPUtils.parseRtpParameters(mediaSection);\n  var hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n  var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n  // filter a=ssrc:... cname:, ignore PlanB-msid\n  var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(function(line) {\n      return SDPUtils.parseSsrcMedia(line);\n    })\n    .filter(function(parts) {\n      return parts.attribute === 'cname';\n    });\n  var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n  var secondarySsrc;\n\n  var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n    .map(function(line) {\n      var parts = line.substr(17).split(' ');\n      return parts.map(function(part) {\n        return parseInt(part, 10);\n      });\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(function(codec) {\n    if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n      var 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  var 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(function(params) {\n      params.maxBitrate = bandwidth;\n    });\n  }\n  return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n  var rtcpParameters = {};\n\n  // Gets the first SSRC. Note tha with RTX there might be multiple\n  // SSRCs.\n  var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(function(line) {\n      return SDPUtils.parseSsrcMedia(line);\n    })\n    .filter(function(obj) {\n      return obj.attribute === 'cname';\n    })[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  var 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  var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n  rtcpParameters.mux = mux.length > 0;\n\n  return rtcpParameters;\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  var parts;\n  var 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  var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n    .map(function(line) {\n      return SDPUtils.parseSsrcMedia(line);\n    })\n    .filter(function(msidParts) {\n      return msidParts.attribute === 'msid';\n    });\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  var mline = SDPUtils.parseMLine(mediaSection);\n  var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n  var maxMessageSize;\n  if (maxSizeLine.length > 0) {\n    maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n  }\n  if (isNaN(maxMessageSize)) {\n    maxMessageSize = 65536;\n  }\n  var 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: maxMessageSize\n    };\n  }\n  var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n  if (sctpMapLines.length > 0) {\n    var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0]\n      .substr(10)\n      .split(' ');\n    return {\n      port: parseInt(parts[0], 10),\n      protocol: parts[1],\n      maxMessageSize: 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  var 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 boilder 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  var sessionId;\n  var version = sessVer !== undefined ? sessVer : 2;\n  if (sessId) {\n    sessionId = sessId;\n  } else {\n    sessionId = SDPUtils.generateSessionId();\n  }\n  var 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\nSDPUtils.writeMediaSection = function(transceiver, caps, type, stream) {\n  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n  // Map ICE parameters (ufrag, pwd) to SDP.\n  sdp += SDPUtils.writeIceParameters(\n    transceiver.iceGatherer.getLocalParameters());\n\n  // Map DTLS parameters to SDP.\n  sdp += SDPUtils.writeDtlsParameters(\n    transceiver.dtlsTransport.getLocalParameters(),\n    type === 'offer' ? 'actpass' : 'active');\n\n  sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n  if (transceiver.direction) {\n    sdp += 'a=' + transceiver.direction + '\\r\\n';\n  } else if (transceiver.rtpSender && transceiver.rtpReceiver) {\n    sdp += 'a=sendrecv\\r\\n';\n  } else if (transceiver.rtpSender) {\n    sdp += 'a=sendonly\\r\\n';\n  } else if (transceiver.rtpReceiver) {\n    sdp += 'a=recvonly\\r\\n';\n  } else {\n    sdp += 'a=inactive\\r\\n';\n  }\n\n  if (transceiver.rtpSender) {\n    // spec.\n    var msid = 'msid:' + stream.id + ' ' +\n        transceiver.rtpSender.track.id + '\\r\\n';\n    sdp += 'a=' + msid;\n\n    // for Chrome.\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n        ' ' + msid;\n    if (transceiver.sendEncodingParameters[0].rtx) {\n      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n          ' ' + msid;\n      sdp += 'a=ssrc-group:FID ' +\n          transceiver.sendEncodingParameters[0].ssrc + ' ' +\n          transceiver.sendEncodingParameters[0].rtx.ssrc +\n          '\\r\\n';\n    }\n  }\n  // FIXME: this should be written by writeRtpDescription.\n  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n      ' cname:' + SDPUtils.localCName + '\\r\\n';\n  if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n        ' cname:' + SDPUtils.localCName + '\\r\\n';\n  }\n  return sdp;\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  var lines = SDPUtils.splitLines(mediaSection);\n  for (var 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  var lines = SDPUtils.splitLines(mediaSection);\n  var 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  var lines = SDPUtils.splitLines(mediaSection);\n  var 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  var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n  var 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  var lines = SDPUtils.splitLines(blob);\n  for (var 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 * 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","/**\n * The events for the media devices.\n */\n\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 */\nexport const 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 */\nexport const 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 */\nexport const PERMISSION_PROMPT_IS_SHOWN\n    = 'mediaDevices.permissionPromptIsShown';\n","/* global __filename, Olm */\n\nimport base64js from 'base64-js';\nimport { getLogger } from 'jitsi-meet-logger';\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 { 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\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_JOINED, this._onConferenceJoined.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        } else {\n            this._init.reject(new Error('Olm not supported'));\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     * @returns {number}\n     */\n    async updateCurrentKey(key) {\n        this._key = key;\n\n        return this._keyIndex;\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\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     * 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.eventEmitter.emit(OlmAdapterEvents.OLM_ID_KEY_READY, this._idKey);\n        } catch (e) {\n            logger.error('Failed to initialize Olm', e);\n            this._init.reject(e);\n        }\n\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 the conference joined event. Upon joining a conference, the participant\n     * who just joined will start new olm sessions with every other participant.\n     *\n     * @private\n     */\n    async _onConferenceJoined() {\n        logger.debug('Conference joined');\n\n        await this._init;\n\n        const promises = [];\n\n        // Establish a 1-to-1 Olm session with every participant in the conference.\n        // We are forcing the last user to join the conference to start the exchange\n        // so we can send some pre-established secrets in the ACK.\n        for (const participant of this._conf.getParticipants()) {\n            promises.push(this._sendSessionInit(participant));\n        }\n\n        await Promise.allSettled(promises);\n\n        // TODO: retry failed ones.\n        // TODO: skip participants which don't support E2EE.\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\n                this.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_E2EE_CHANNEL_READY, 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.eventEmitter.emit(OlmAdapterEvents.PARTICIPANT_E2EE_CHANNEL_READY, 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    /**\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        const olmData = this._getParticipantOlmData(participant);\n\n        if (olmData.session) {\n            olmData.session.free();\n            olmData.session = undefined;\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\nOlmAdapter.events = OlmAdapterEvents;\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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport debounce from 'lodash.debounce';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\n\nimport E2EEContext from './E2EEContext';\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// We use ECDSA with Curve P-521 for the long-term signing keys. See\n//   https://developer.mozilla.org/en-US/docs/Web/API/EcKeyGenParams\nconst SIGNATURE_OPTIONS = {\n    name: 'ECDSA',\n    namedCurve: 'P-521'\n};\n\n/**\n * This module integrates {@link E2EEContext} 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        this.conference = conference;\n\n        this._conferenceJoined = false;\n        this._enabled = false;\n        this._initialized = false;\n        this._key = undefined;\n        this._signatureKeyPair = undefined;\n\n        this._e2eeCtx = new E2EEContext();\n        this._olmAdapter = new OlmAdapter(conference);\n\n        // Debounce key rotation / ratcheting to avoid a storm of messages.\n        this._ratchetKey = debounce(this._ratchetKeyImpl, DEBOUNCE_PERIOD);\n        this._rotateKey = debounce(this._rotateKeyImpl, DEBOUNCE_PERIOD);\n\n        // Participant join / leave operations. Used for key advancement / rotation.\n        //\n\n        this.conference.on(\n            JitsiConferenceEvents.CONFERENCE_JOINED,\n            () => {\n                this._conferenceJoined = true;\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\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        // Olm signalling events.\n        this._olmAdapter.on(\n            OlmAdapter.events.OLM_ID_KEY_READY,\n            this._onOlmIdKeyReady.bind(this));\n        this._olmAdapter.on(\n            OlmAdapter.events.PARTICIPANT_E2EE_CHANNEL_READY,\n            this._onParticipantE2EEChannelReady.bind(this));\n        this._olmAdapter.on(\n            OlmAdapter.events.PARTICIPANT_KEY_UPDATED,\n            this._onParticipantKeyUpdated.bind(this));\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        return browser.supportsInsertableStreams()\n            && OlmAdapter.isSupported()\n            && !(config.testing && config.testing.disableE2EE);\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._enabled = enabled;\n\n        if (!this._initialized && enabled) {\n            // Generate a frame signing key pair. Per session currently.\n            this._signatureKeyPair = await crypto.subtle.generateKey(SIGNATURE_OPTIONS,\n                true, [ 'sign', 'verify' ]);\n            this._e2eeCtx.setSignatureKey(this.conference.myUserId(), this._signatureKeyPair.privateKey);\n\n            // Serialize the JWK of the signing key. Using JSON, might be easy to xml-ify.\n            const serializedSigningKey = await crypto.subtle.exportKey('jwk', this._signatureKeyPair.publicKey);\n\n            // TODO: sign this with the OLM account key.\n            this.conference.setLocalParticipantProperty('e2ee.signatureKey', JSON.stringify(serializedSigningKey));\n\n            // Need to re-create the peerconnections in order to apply the insertable streams constraint.\n            // TODO: this was necessary due to some audio issues when indertable streams are used\n            // even though encryption is not performed. This should be fixed in the browser eventually.\n            // https://bugs.chromium.org/p/chromium/issues/detail?id=1103280\n            this.conference._restartMediaSessions();\n\n            this._initialized = true;\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        this._olmAdapter.updateKey(this._key).then(index => {\n            // Set our key so we begin encrypting.\n            this._e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\n        });\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     * 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     * Publushes our own Olmn id key in presence.\n     * @private\n     */\n    _onOlmIdKeyReady(idKey) {\n        logger.debug(`Olm id key ready: ${idKey}`);\n\n        // Publish it in presence.\n        this.conference.setLocalParticipantProperty('e2ee.idKey', idKey);\n    }\n\n    /**\n     * Advances (using ratcheting) the current key when a new participant joins the conference.\n     * @private\n     */\n    _onParticipantJoined(id) {\n        logger.debug(`Participant ${id} joined`);\n\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        logger.debug(`Participant ${id} left`);\n\n        this._e2eeCtx.cleanup(id);\n\n        if (this._enabled) {\n            this._rotateKey();\n        }\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     * 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     * 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.signatureKey':\n            logger.debug(`Participant ${participant.getId()} updated their signature key: ${newValue}`);\n            if (newValue) {\n                const parsed = JSON.parse(newValue);\n\n                const importedKey = await crypto.subtle.importKey('jwk', parsed, { name: 'ECDSA',\n                    namedCurve: parsed.crv }, true, parsed.key_ops);\n\n                this._e2eeCtx.setSignatureKey(participant.getId(), importedKey);\n            } else {\n                logger.warn(`e2ee signatureKey for ${participant.getId()} could not be updated with empty value.`);\n            }\n            break;\n        }\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 = await this._olmAdapter.updateCurrentKey(this._key);\n\n        this._e2eeCtx.setKey(this.conference.myUserId(), this._key, index);\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     * 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","/* global __filename, $ */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport { $iq, Strophe } from 'strophe.js';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport {\n    ICE_DURATION,\n    ICE_STATE_CHANGED\n} from '../../service/statistics/AnalyticsEvents';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\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 SDP from './SDP';\nimport SDPDiffer from './SDPDiffer';\nimport SDPUtil from './SDPUtil';\nimport SignalingLayerImpl from './SignalingLayerImpl';\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 * @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\n     * 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\n     * createOffer/Answer, as defined by the WebRTC standard\n     * @param iceConfig the ICE servers config object as defined by the WebRTC\n     * standard.\n     * @param {boolean} isP2P indicates whether this instance is\n     * meant to be used in a direct, peer to peer connection or <tt>false</tt>\n     * if it's a JVB connection.\n     * @param {boolean} isInitiator indicates if it will be the side which\n     * initiates the session.\n     * @constructor\n     *\n     * @implements {SignalingLayer}\n     */\n    constructor(\n            sid,\n            localJid,\n            remoteJid,\n            connection,\n            mediaConstraints,\n            iceConfig,\n            isP2P,\n            isInitiator) {\n        super(\n            sid,\n            localJid,\n            remoteJid, connection, mediaConstraints, iceConfig, 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 signaling layer implementation.\n         * @type {SignalingLayerImpl}\n         */\n        this.signalingLayer = new SignalingLayerImpl();\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\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\n            // disable simulcast for screenshare and set the max bitrate to\n            // 500Kbps if the testing flag is present in config.js.\n            if (options.testing\n                && options.testing.capScreenshareBitrate\n                && typeof options.testing.capScreenshareBitrate === 'number') {\n                pcOptions.capScreenshareBitrate\n                    = Math.random()\n                    < options.testing.capScreenshareBitrate;\n\n                // add the capScreenshareBitrate to the permanent properties so\n                // that it's included with every event that we send to the\n                // analytics backend.\n                Statistics.analytics.addPermanentProperties({ capScreenshareBitrate: pcOptions.capScreenshareBitrate });\n            }\n        }\n\n        if (options.startSilent) {\n            pcOptions.startSilent = true;\n        }\n\n        this.peerconnection\n            = this.rtc.createPeerConnection(\n                    this.signalingLayer,\n                    this.iceConfig,\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\n            if (!this.isP2P) {\n                this.room.connectionTimes[\n                    `ice.state.${this.peerconnection.iceConnectionState}`]\n                    = now;\n            }\n            logger.log(\n                `(TIME) ICE ${this.peerconnection.iceConnectionState}`\n                    + ` P2P? ${this.isP2P}:\\t`,\n                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                    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                if (!this.wasConnected && this.wasstable) {\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         * 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 (browser.usesUnifiedPlan() && state === 'stable'\n                && remoteDescription && typeof remoteDescription.sdp === 'string') {\n                logger.debug(`onnegotiationneeded fired on ${this.peerconnection} in state: ${state}`);\n                const workFunction = finishedCallback => {\n                    const oldSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n                    this._renegotiate()\n                        .then(() => {\n                            const newSdp = new SDP(this.peerconnection.localDescription.sdp);\n\n                            this.notifyMySSRCUpdate(oldSdp, newSdp);\n                            finishedCallback();\n                        },\n                        finishedCallback /* will be called with en error */);\n                };\n\n                this.modificationQueue.push(\n                    workFunction,\n                    error => {\n                        if (error) {\n                            logger.error('onnegotiationneeded error', error);\n                        } else {\n                            logger.debug('onnegotiationneeded executed - OK');\n                        }\n                    });\n            }\n        };\n\n        // The signaling layer will bind it's listeners at this point\n        this.signalingLayer.setChatRoom(this.room);\n\n        if (!this.isP2P && options.enableLayerSuspension) {\n            // If this is the bridge session, we'll listen for\n            // SENDER_VIDEO_CONSTRAINTS_CHANGED events and notify the peer connection\n            this._removeSenderVideoConstraintsChangeListener = this.rtc.addListener(\n                RTCEvents.SENDER_VIDEO_CONSTRAINTS_CHANGED, () => {\n                    this.eventEmitter.emit(\n                        MediaSessionEvents.REMOTE_VIDEO_CONSTRAINTS_CHANGED, this);\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 this.options.enableLayerSuspension ? this.rtc.getSenderVideoConstraints().idealHeight : 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\n                = SDPUtil.iceparams(\n                    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                    // start 20ms callout\n                    setTimeout(() => {\n                        if (this.dripContainer.length === 0) {\n                            return;\n                        }\n                        this.sendIceCandidates(this.dripContainer);\n                        this.dripContainer = [];\n                    }, 20);\n                }\n                this.dripContainer.push(candidate);\n            } else {\n                this.sendIceCandidates([ candidate ]);\n            }\n        } else {\n            logger.log('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('sendIceCandidates', 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('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(\n                '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('addIceCandidate ok!'),\n                        err => logger.error('addIceCandidate failed!', err));\n            }\n\n            finishedCallback();\n        };\n\n        logger.debug(\n            `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                $(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(\n                                    `Invalid SSRC ${ssrc} value received`\n                                        + ` for ${owner}`);\n                            } else {\n                                this.signalingLayer.setSSRCOwner(\n                                    ssrc,\n                                    Strophe.getResourceFromJid(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(\n                '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(success, failure);\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        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error('invite error', error);\n                } else {\n                    logger.debug('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.info('Session-initiate: ', init);\n        this.connection.sendIQ(init,\n            () => {\n                logger.info('Got RESULT for \"session-initiate\"');\n            },\n            error => {\n                logger.error('\"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('setAnswer - succeeded');\n            },\n            error => {\n                logger.error('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        this.modificationQueue.push(\n            workFunction,\n            error => {\n                error ? failure(error) : success();\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(() => finishedCallback(), error => finishedCallback(error));\n            };\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        let 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            null);\n\n        // Calling tree() to print something useful\n        accept = accept.tree();\n        logger.info('Sending session-accept', accept);\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: '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\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        let 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        // Calling tree() to print something useful to the logger\n        transportAccept = transportAccept.tree();\n        logger.info('Sending transport-accept: ', transportAccept);\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        let 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        transportReject = transportReject.tree();\n        logger.info('Sending \\'transport-reject', transportReject);\n\n        this.connection.sendIQ(transportReject,\n            success,\n            this.newJingleErrorHandler(transportReject, failure),\n            IQ_TIMEOUT);\n    }\n\n    /**\n     * Sets the maximum bitrates on the local video track. Bitrate values from\n     * videoQuality settings in config.js will be used for configuring the sender.\n     * @returns {Promise<void>} promise that will be resolved when the operation is\n     * successful and rejected otherwise.\n     */\n    setSenderMaxBitrates() {\n        if (this._assertNotEnded()) {\n            return this.peerconnection.setMaxBitRate();\n        }\n\n        return Promise.resolve();\n    }\n\n    /**\n     * Sets the resolution constraint on the local camera track.\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     */\n    setSenderVideoConstraint(maxFrameHeight) {\n        if (this._assertNotEnded()) {\n            logger.info(`${this} setSenderVideoConstraint: ${maxFrameHeight}`);\n\n            // RN doesn't support RTCRtpSenders yet, aggresive layer suspension on RN is implemented\n            // by changing the media direction in the SDP. This is applicable to jvb sessions only.\n            if (!this.isP2P && browser.isReactNative() && typeof maxFrameHeight !== 'undefined') {\n                const videoActive = maxFrameHeight > 0;\n\n                return this.setMediaTransferActive(true, videoActive);\n            }\n\n            return this.peerconnection.setSenderVideoConstraint(maxFrameHeight);\n        }\n\n        return Promise.resolve();\n    }\n\n    /**\n     * Sets the degradation preference on the video sender. This setting determines if\n     * resolution or framerate will be preferred when bandwidth or cpu is constrained.\n     * @returns {Promise<void>} promise that will be resolved when the operation is\n     * successful and rejected otherwise.\n     */\n    setSenderVideoDegradationPreference() {\n        if (this._assertNotEnded()) {\n            return this.peerconnection.setSenderVideoDegradationPreference();\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            let 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            // Calling tree() to print something useful\n            sessionTerminate = sessionTerminate.tree();\n            logger.info('Sending session-terminate', sessionTerminate);\n            this.connection.sendIQ(\n                sessionTerminate,\n                success,\n                this.newJingleErrorHandler(sessionTerminate, failure),\n                IQ_TIMEOUT);\n        } else {\n            logger.info(`Skipped sending session-terminate for ${this}`);\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(`Session terminated ${this}`, 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('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\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\n                            += `a=ssrc-group:${semantics} ${\n                                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                    logger.warn(\n                        `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 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(`Processing ${logPrefix}`);\n            logger.log(\n                'ICE connection state: ',\n                this.peerconnection.iceConnectionState);\n\n            const oldLocalSdp\n                = 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            this._renegotiate(newRemoteSdp.raw)\n                .then(() => {\n                    const newLocalSdp\n                        = new SDP(this.peerconnection.localDescription.sdp);\n\n                    logger.log(\n                        `${logPrefix} - OK, SDPs: `, oldLocalSdp, newLocalSdp);\n                    this.notifyMySSRCUpdate(oldLocalSdp, newLocalSdp);\n                    finishedCallback();\n                }, error => {\n                    logger.error(`${logPrefix} failed:`, error);\n                    finishedCallback(error);\n                });\n        };\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 = browser.usesPlanB()\n            ? new SDP(this.peerconnection.remoteDescription.sdp)\n            : new SDP(this.peerconnection.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 (browser.usesPlanB()) {\n                lines.forEach(line => {\n                    remoteSdp.media[idx]\n                        = remoteSdp.media[idx].replace(`${line}\\r\\n`, '');\n                });\n            } else {\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\n                        // Change the direction to \"inactive\" only on Firefox. Audio fails on\n                        // Safari (possibly Chrome in unified plan mode) when we try to re-use inactive\n                        // m-lines due to a webkit bug.\n                        // https://bugs.webkit.org/show_bug.cgi?id=211181\n                        if (browser.isFirefox()) {\n                            remoteSdp.media[mid] = remoteSdp.media[mid].replace('a=sendonly', 'a=inactive');\n                        }\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        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('Renegotiate: setting remote description');\n\n        return this.peerconnection.setRemoteDescription(remoteDescription)\n            .then(() => {\n                logger.debug('Renegotiate: creating answer');\n\n                return this.peerconnection.createAnswer(this.mediaConstraints)\n                    .then(answer => {\n                        logger.debug('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('Renegotiate: creating offer');\n\n        return this.peerconnection.createOffer(this.mediaConstraints)\n            .then(offer => {\n                logger.debug('Renegotiate: setting local description');\n\n                return this.peerconnection.setLocalDescription(offer)\n                    .then(() => {\n                        logger.debug(\n                            'Renegotiate: setting remote description');\n\n                        // eslint-disable-next-line max-len\n                        return this.peerconnection.setRemoteDescription(remoteDescription);\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            const oldLocalSdp = this.peerconnection.localDescription.sdp;\n\n            if (browser.usesPlanB()) {\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                    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 && newTrack.isVideoTrack()) {\n                            // FIXME set all sender parameters in one go?\n                            // Set the degradation preference on the new video sender.\n                            return this.peerconnection.setSenderVideoDegradationPreference()\n\n                                // Apply the cached video constraints on the new video sender.\n                                .then(() => this.peerconnection.setSenderVideoConstraint())\n                                .then(() => this.peerconnection.setMaxBitRate());\n                        }\n                    });\n                })\n                .then(() => finishedCallback(), error => finishedCallback(error));\n        };\n\n        return new Promise((resolve, reject) => {\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        logger.error('Replace track error:', error);\n                        reject(error);\n                    } else {\n                        logger.info('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(\n                `${this} - some SSRC were added on ${operationName}`,\n                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(\n                `${this} - some SSRCs were removed on ${operationName}`,\n                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                // Apply the video constraints, max bitrates and degradation preference on\n                // the video sender if needed.\n                if (track.isVideoTrack() && browser.doesVideoMuteByStreamRemove()) {\n                    return this.setSenderMaxBitrates()\n                        .then(() => this.setSenderVideoDegradationPreference())\n                        .then(() => this.setSenderVideoConstraint());\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\n                                // enough to fail the whole operation. It will log\n                                // an error inside.\n                                this._verifyNoSSRCChanged(\n                                    operationName, new SDP(oldLocalSDP));\n                                finishedCallback();\n                            });\n                    } else {\n                        finishedCallback();\n                    }\n                },\n                finishedCallback /* will be called with an error */);\n        };\n\n        return new Promise((resolve, reject) => {\n            this.modificationQueue.push(\n                workFunction,\n                error => {\n                    if (error) {\n                        reject(error);\n                    } else {\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(`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                        reject(error);\n                    } else {\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(\n            `${this} queued \"content-modify\" task`\n                + `(video senders=\"${newVideoSenders}\")`);\n\n        this.modificationQueue.push(\n            workFunction,\n            error => {\n                if (error) {\n                    logger.error('\"content-modify\" failed', error);\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(\n                `${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\n        if (this.state !== JingleSessionState.ACTIVE) {\n            logger.warn(`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('Not sending SSRC update while the signaling is disconnected');\n\n            return;\n        }\n\n        this._cachedOldLocalSdp = undefined;\n        this._cachedNewLocalSdp = undefined;\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        if (removedAnySSRCs) {\n            logger.info('Sending source-remove', remove.tree());\n            this.connection.sendIQ(\n                remove, null,\n                this.newJingleErrorHandler(remove), IQ_TIMEOUT);\n        } else {\n            logger.log('removal not necessary');\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            logger.info('Sending source-add', add.tree());\n            this.connection.sendIQ(\n                add, null, this.newJingleErrorHandler(add), IQ_TIMEOUT);\n        } else {\n            logger.log('addition not necessary');\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(`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        // Remove any pending tasks from the queue\n        this.modificationQueue.clear();\n\n        this.modificationQueue.push(finishCallback => {\n            // The signaling layer will remove it's listeners\n            this.signalingLayer.setChatRoom(null);\n\n            // do not try to close if already closed.\n            this.peerconnection && this.peerconnection.close();\n            finishCallback();\n        });\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[p2p=${this.isP2P},`\n                    + `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","export default {\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","'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","import 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\n            modify.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });\n            modify.attrs({ ssrc: mediaSsrc.ssrc });\n\n            // iterate over ssrc lines\n            mediaSsrc.lines.forEach(line => {\n                const idx = line.indexOf(' ');\n                const kv = line.substr(idx + 1);\n\n                modify.c('parameter');\n                if (kv.indexOf(':') === -1) {\n                    modify.attrs({ name: kv });\n                } else {\n                    const nv = kv.split(':', 2);\n                    const name = nv[0];\n                    const value = SDPUtil.filterSpecialChars(nv[1]);\n\n                    modify.attrs({ name });\n                    modify.attrs({ value });\n                }\n                modify.up(); // end of parameter\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","// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things.  But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals.  It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n    throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n    throw new Error('clearTimeout has not been defined');\n}\n(function () {\n    try {\n        if (typeof setTimeout === 'function') {\n            cachedSetTimeout = setTimeout;\n        } else {\n            cachedSetTimeout = defaultSetTimout;\n        }\n    } catch (e) {\n        cachedSetTimeout = defaultSetTimout;\n    }\n    try {\n        if (typeof clearTimeout === 'function') {\n            cachedClearTimeout = clearTimeout;\n        } else {\n            cachedClearTimeout = defaultClearTimeout;\n        }\n    } catch (e) {\n        cachedClearTimeout = defaultClearTimeout;\n    }\n} ())\nfunction runTimeout(fun) {\n    if (cachedSetTimeout === setTimeout) {\n        //normal enviroments in sane situations\n        return setTimeout(fun, 0);\n    }\n    // if setTimeout wasn't available but was latter defined\n    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n        cachedSetTimeout = setTimeout;\n        return setTimeout(fun, 0);\n    }\n    try {\n        // when when somebody has screwed with setTimeout but no I.E. maddness\n        return cachedSetTimeout(fun, 0);\n    } catch(e){\n        try {\n            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n            return cachedSetTimeout.call(null, fun, 0);\n        } catch(e){\n            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n            return cachedSetTimeout.call(this, fun, 0);\n        }\n    }\n\n\n}\nfunction runClearTimeout(marker) {\n    if (cachedClearTimeout === clearTimeout) {\n        //normal enviroments in sane situations\n        return clearTimeout(marker);\n    }\n    // if clearTimeout wasn't available but was latter defined\n    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n        cachedClearTimeout = clearTimeout;\n        return clearTimeout(marker);\n    }\n    try {\n        // when when somebody has screwed with setTimeout but no I.E. maddness\n        return cachedClearTimeout(marker);\n    } catch (e){\n        try {\n            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally\n            return cachedClearTimeout.call(null, marker);\n        } catch (e){\n            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n            // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n            return cachedClearTimeout.call(this, marker);\n        }\n    }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n    if (!draining || !currentQueue) {\n        return;\n    }\n    draining = false;\n    if (currentQueue.length) {\n        queue = currentQueue.concat(queue);\n    } else {\n        queueIndex = -1;\n    }\n    if (queue.length) {\n        drainQueue();\n    }\n}\n\nfunction drainQueue() {\n    if (draining) {\n        return;\n    }\n    var timeout = runTimeout(cleanUpNextTick);\n    draining = true;\n\n    var len = queue.length;\n    while(len) {\n        currentQueue = queue;\n        queue = [];\n        while (++queueIndex < len) {\n            if (currentQueue) {\n                currentQueue[queueIndex].run();\n            }\n        }\n        queueIndex = -1;\n        len = queue.length;\n    }\n    currentQueue = null;\n    draining = false;\n    runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n    var args = new Array(arguments.length - 1);\n    if (arguments.length > 1) {\n        for (var i = 1; i < arguments.length; i++) {\n            args[i - 1] = arguments[i];\n        }\n    }\n    queue.push(new Item(fun, args));\n    if (queue.length === 1 && !draining) {\n        runTimeout(drainQueue);\n    }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n    this.fun = fun;\n    this.array = array;\n}\nItem.prototype.run = function () {\n    this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n    throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n    throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\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","export const CALLSTATS_SCRIPT_URL = 'https://api.callstats.io/static/callstats-ws.min.js';\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\nconst logger = require('jitsi-meet-logger').getLogger(__filename);\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\nexport const ERROR_FEATURE_VERSION_MISMATCH = 'Feature version mismatch';\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 * 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.versionToCapabilities = Object.create(null);\n        this.jidToVersion = Object.create(null);\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        connection.addHandler(this._handleCaps.bind(this), Strophe.NS.CAPS);\n\n        this._onMucMemberLeft = this._removeJidToVersionEntry.bind(this);\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.addToPresence('features', { children });\n        }\n    }\n\n    /**\n     * Returns a set with the features for a participant.\n     * @param {String} jid the jid of the participant\n     * @param {int} timeout the timeout in ms for reply from the participant.\n     * @returns {Promise<Set<String>, Error>}\n     */\n    getFeatures(jid, timeout = 5000) {\n        const user\n            = jid in this.jidToVersion ? this.jidToVersion[jid] : null;\n\n        if (!user || !(user.version in this.versionToCapabilities)) {\n            const node = user ? `${user.node}#${user.version}` : null;\n\n            return this._getDiscoInfo(jid, node, timeout)\n                .then(({ features, identities }) => {\n                    if (user) {\n                        const sha = generateSha(\n                            Array.from(identities),\n                            Array.from(features)\n                        );\n                        const receivedNode = `${user.node}#${sha}`;\n\n                        if (receivedNode === node) {\n                            this.versionToCapabilities[receivedNode] = features;\n\n                            return features;\n                        }\n\n                        // Check once if it has been cached asynchronously.\n                        if (this.versionToCapabilities[receivedNode]) {\n                            return this.versionToCapabilities[receivedNode];\n                        }\n\n                        logger.error(`Expected node ${node} but received ${\n                            receivedNode}`);\n\n                        return Promise.reject(ERROR_FEATURE_VERSION_MISMATCH);\n                    }\n\n                    return features;\n                });\n        }\n\n        return Promise.resolve(this.versionToCapabilities[user.version]);\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                const features = new Set();\n                const identities = new Set();\n\n                $(response)\n                    .find('>query>feature')\n                    .each(\n                        (_, el) => features.add(el.getAttribute('var')));\n                $(response)\n                    .find('>query>identity')\n                    .each(\n                        (_, el) => identities.add({\n                            type: el.getAttribute('type'),\n                            name: el.getAttribute('name'),\n                            category: el.getAttribute('category')\n                        }));\n                resolve({\n                    features,\n                    identities });\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        room.addListener(XMPPEvents.MUC_MEMBER_LEFT, this._onMucMemberLeft);\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        room.removeListener(XMPPEvents.MUC_MEMBER_LEFT, this._onMucMemberLeft);\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.addToPresence('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    /**\n     * Parses the \"c\" xml node from presence.\n     * @param {DOMElement} stanza the presence packet\n     */\n    _handleCaps(stanza) {\n        const from = stanza.getAttribute('from');\n        const caps = stanza.querySelector('c');\n        const version = caps.getAttribute('ver');\n        const node = caps.getAttribute('node');\n        const oldVersion = this.jidToVersion[from];\n\n        this.jidToVersion[from] = { version,\n            node };\n        if (oldVersion && oldVersion.version !== version) {\n            this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from);\n        }\n\n        // return true to not remove the handler from Strophe\n        return true;\n    }\n\n    /**\n     * Removes entry from this.jidToVersion map.\n     * @param {String} jid the jid to be removed.\n     */\n    _removeJidToVersionEntry(jid) {\n        if (jid in this.jidToVersion) {\n            delete this.jidToVersion[jid];\n        }\n    }\n}\n","/**\n * The transciption is on.\n *\n * @type {String}\n */\nexport const ON = 'on';\n\n/**\n * The transciption is off.\n *\n * @type {String}\n */\nexport const OFF = 'off';\n","/* global __filename, module */\nimport EventEmitter from 'events';\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport * as 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 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 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","/**\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","/**\n * Indicates that the end-to-end round-trip-time for a participant has changed.\n */\nexport const E2E_RTT_CHANGED = 'e2eping.e2e_rtt_changed';\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 { getLogger } from 'jitsi-meet-logger';\nimport transform from 'sdp-transform';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport browser from '../browser';\n\nconst logger = getLogger(__filename);\nconst SIM_LAYER_1_RID = '1';\nconst SIM_LAYER_2_RID = '2';\nconst SIM_LAYER_3_RID = '3';\n\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     * @param videoBitrates - the bitrates to be configured on the video senders for\n     * different resolutions both in unicast and simulcast mode.\n     */\n    constructor(peerconnection, videoBitrates) {\n        this.pc = peerconnection;\n        this.videoBitrates = videoBitrates.VP8 || 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() ? this.videoBitrates.high : this.videoBitrates.low,\n                rid: SIM_LAYER_1_RID,\n                scaleResolutionDownBy: browser.isFirefox() ? 1.0 : 4.0\n            },\n            {\n                active: true,\n                maxBitrate: this.videoBitrates.standard,\n                rid: SIM_LAYER_2_RID,\n                scaleResolutionDownBy: 2.0\n            },\n            {\n                active: true,\n                maxBitrate: browser.isFirefox() ? this.videoBitrates.low : this.videoBitrates.high,\n                rid: SIM_LAYER_3_RID,\n                scaleResolutionDownBy: browser.isFirefox() ? 4.0 : 1.0\n            }\n        ];\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 === 'audio') {\n                return;\n            }\n            if (!mLine.ssrcGroups || !mLine.ssrcGroups.length) {\n                return;\n            }\n            let reorderedSsrcs = [];\n\n            mLine.ssrcGroups[0].ssrcs.split(' ').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     * 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     * 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\n        // we munge SDP for turning on simulcast. Remove this check\n        // when we move 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 === '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 === '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\n    * in a p2p connection.\n    * @returns {void}\n    */\n    addTrack(localTrack, isInitiator) {\n        const track = localTrack.getTrack();\n\n        if (isInitiator) {\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: 'sendrecv',\n                streams: [ localTrack.getOriginalStream() ],\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     * Adds a track on the RTCRtpSender as part of the unmute operation.\n     * @param {JitsiLocalTrack} localTrack - track to be unmuted.\n     * @returns {Promise<void>} - resolved when done.\n     */\n    addTrackUnmute(localTrack) {\n        const mediaType = localTrack.getType();\n        const track = localTrack.getTrack();\n\n        // The assumption here is that the first transceiver of the specified\n        // media type is that of the local track.\n        const transceiver = this.pc.peerconnection.getTransceivers()\n            .find(t => t.receiver && t.receiver.track && t.receiver.track.kind === mediaType);\n\n        if (!transceiver) {\n            return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));\n        }\n        logger.debug(`Adding ${localTrack} on ${this.pc}`);\n\n        // If the client starts with audio/video muted setting, the transceiver direction\n        // will be set to 'recvonly'. Use addStream here so that a MSID is generated for the stream.\n        if (transceiver.direction === 'recvonly') {\n            const stream = localTrack.getOriginalStream();\n\n            if (stream) {\n                this.pc.peerconnection.addStream(localTrack.getOriginalStream());\n\n                return this.setEncodings(localTrack).then(() => {\n                    this.pc.localTracks.set(localTrack.rtcId, localTrack);\n                    transceiver.direction = 'sendrecv';\n                });\n            }\n\n            return Promise.resolve();\n        }\n\n        return transceiver.sender.replaceTrack(track);\n    }\n\n    /**\n     * Obtains the current local video track's height constraints based on the\n     * initial stream encodings configuration on the sender and the resolution\n     * of the current local track added to the peerconnection.\n     * @param {MediaStreamTrack} localTrack local video track\n     * @returns {Array[number]} an array containing the resolution heights of\n     * simulcast streams configured on the video sender.\n     */\n    getLocalStreamHeightConstraints(localTrack) {\n        // React-native hasn't implemented MediaStreamTrack getSettings yet.\n        if (browser.isReactNative()) {\n            return null;\n        }\n\n        const localVideoHeightConstraints = [];\n\n        // Firefox doesn't return the height of the desktop track, assume a min. height of 720.\n        const { height = 720 } = localTrack.getSettings();\n\n        for (const encoding of this.localStreamEncodingsConfig) {\n            localVideoHeightConstraints.push(height / encoding.scaleResolutionDownBy);\n        }\n\n        return localVideoHeightConstraints;\n    }\n\n    /**\n     * Removes the track from the RTCRtpSender as part of the mute operation.\n     * @param {JitsiLocalTrack} localTrack - track to be removed.\n     * @returns {Promise<void>} - resolved when done.\n     */\n    removeTrackMute(localTrack) {\n        const mediaType = localTrack.getType();\n        const transceiver = this.pc.peerconnection.getTransceivers()\n            .find(t => t.sender && t.sender.track && t.sender.track.id === localTrack.getTrackId());\n\n        if (!transceiver) {\n            return Promise.reject(new Error(`RTCRtpTransceiver for ${mediaType} not found`));\n        }\n\n        logger.debug(`Removing ${localTrack} on ${this.pc}`);\n\n        return transceiver.sender.replaceTrack(null);\n    }\n\n    /**\n     * Replaces the existing track on a RTCRtpSender with the given track.\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<void>} - resolved when done.\n     */\n    replaceTrack(oldTrack, newTrack) {\n        if (oldTrack && newTrack) {\n            const mediaType = newTrack.getType();\n            const stream = newTrack.getOriginalStream();\n\n            // Ignore cases when the track is replaced while the device is in a muted state,like\n            // replacing camera when video muted or replacing mic when audio muted. These JitsiLocalTracks\n            // do not have a mediastream attached. Replace track will be called again when the device is\n            // unmuted and the track will be replaced on the peerconnection then.\n            if (!stream) {\n                this.pc.localTracks.delete(oldTrack.rtcId);\n                this.pc.localTracks.set(newTrack.rtcId, newTrack);\n\n                return Promise.resolve();\n            }\n            const track = mediaType === MediaType.AUDIO\n                ? stream.getAudioTracks()[0]\n                : stream.getVideoTracks()[0];\n            const transceiver = this.pc.peerconnection.getTransceivers()\n                .find(t => t.receiver.track.kind === mediaType && !t.stopped);\n\n            if (!transceiver) {\n                return Promise.reject(new Error('replace track failed'));\n            }\n            logger.debug(`Replacing ${oldTrack} with ${newTrack} on ${this.pc}`);\n\n            return transceiver.sender.replaceTrack(track)\n                .then(() => {\n                    const ssrc = this.pc.localSSRCs.get(oldTrack.rtcId);\n\n                    this.pc.localTracks.delete(oldTrack.rtcId);\n                    this.pc.localSSRCs.delete(oldTrack.rtcId);\n                    this.pc._addedStreams = this.pc._addedStreams.filter(s => s !== stream);\n                    this.pc.localTracks.set(newTrack.rtcId, newTrack);\n\n                    this.pc._addedStreams.push(stream);\n                    this.pc.localSSRCs.set(newTrack.rtcId, ssrc);\n                    this.pc.eventEmitter.emit(RTCEvents.LOCAL_TRACK_SSRC_UPDATED,\n                        newTrack,\n                        this.pc._extractPrimarySSRC(ssrc));\n                });\n        } else if (oldTrack && !newTrack) {\n            return this.removeTrackMute(oldTrack)\n                .then(() => {\n                    this.pc.localTracks.delete(oldTrack.rtcId);\n                    this.pc.localSSRCs.delete(oldTrack.rtcId);\n                });\n        } else if (newTrack && !oldTrack) {\n            const ssrc = this.pc.localSSRCs.get(newTrack.rtcId);\n\n            return this.addTrackUnmute(newTrack)\n                .then(() => {\n                    this.pc.localTracks.set(newTrack.rtcId, newTrack);\n                    this.pc.localSSRCs.set(newTrack.rtcId, ssrc);\n                });\n        }\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 transceiver = this.pc.peerconnection.getTransceivers()\n            .find(t => t.sender && t.sender.track && t.sender.track.kind === track.getType());\n        const parameters = transceiver.sender.getParameters();\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(`${active ? 'Enabling' : 'Suspending'} ${mediaType} media transfer on ${this.pc}`);\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 = 'sendrecv';\n                } else {\n                    transceiver.direction = 'recvonly';\n                }\n            } else {\n                transceiver.direction = '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.isSafari() && 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","/**\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    }\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\nmodule.exports = SpeakerStats;\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","/**\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 `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 * 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-CTR',\n        length: 128\n    }, false, [ 'encrypt', 'decrypt' ]);\n    const authenticationKey = await crypto.subtle.deriveKey({\n        name: 'HKDF',\n        salt: textEncoder.encode('JFrameAuthenticationKey'),\n        hash: 'SHA-256',\n        info\n    }, material, {\n        name: 'HMAC',\n        hash: 'SHA-256'\n    }, false, [ 'sign' ]);\n    const saltKey = await crypto.subtle.deriveBits({\n        name: 'HKDF',\n        salt: textEncoder.encode('JFrameSaltKey'),\n        hash: 'SHA-256',\n        info\n    }, material, 128);\n\n    return {\n        material,\n        encryptionKey,\n        authenticationKey,\n        saltKey\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","/* global __filename, Promise */\n\nimport { getLogger } from 'jitsi-meet-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 * as 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 new JitsiLocalTrack instance.\n     *\n     * @constructor\n     * @param {Object} trackInfo\n     * @param {number} trackInfo.rtcId the ID assigned by the RTC module\n     * @param trackInfo.stream WebRTC MediaStream, parent of the track\n     * @param trackInfo.track underlying WebRTC MediaStreamTrack for new\n     * JitsiRemoteTrack\n     * @param trackInfo.mediaType the MediaType of the JitsiRemoteTrack\n     * @param trackInfo.videoType the VideoType of the JitsiRemoteTrack\n     * @param trackInfo.effects the effects array contains the effect instance to use\n     * @param trackInfo.resolution the video resolution if it's a video track\n     * @param trackInfo.deviceId the ID of the local device for this track\n     * @param trackInfo.facingMode the camera facing mode used in getUserMedia\n     * call\n     * @param {sourceId} trackInfo.sourceId - The id of the desktop sharing\n     * source. NOTE: defined for desktop sharing 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        /**\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        if (browser.usesNewGumFlow()) {\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        } else {\n            // FIXME Currently, Firefox is ignoring our constraints about\n            // resolutions so we do not store it, to avoid wrong reporting of\n            // local track resolution.\n            this.resolution = browser.isFirefox() ? null : resolution;\n            this.maxEnabledResolution = this.resolution;\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        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     * 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     * Sets handlers to the MediaStreamTrack object that will detect camera\n     * issues.\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     * @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     * Fires NO_DATA_FROM_SOURCE event and logs it to analytics and callstats.\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 real device ID by comparing track information with device\n     * information. This is temporary solution until getConstraints() method\n     * will be implemented in browsers.\n     *\n     * @param {MediaDeviceInfo[]} devices - list of devices obtained from\n     * enumerateDevices() call\n     */\n    _setRealDeviceIdFromDeviceList(devices) {\n        const track = this.getTrack();\n        const kind = `${track.kind}input`;\n        let device = devices.find(d => d.kind === kind && d.label === track.label);\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\n     * handlers to it.\n     *\n     * @param {MediaStream} stream the new stream.\n     * @protected\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     * @private\n     * @param {*} effect - Represents effect instance\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.getTracks()[0];\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     */\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     * 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\n            return Promise.resolve();\n        }\n\n        this._setEffectInProgress = true;\n\n        if (browser.usesUnifiedPlan()) {\n            this._switchStreamEffect(effect);\n            if (this.isVideoTrack()) {\n                this.containers.forEach(cont => RTCUtils.attachMediaStream(cont, this.stream));\n            }\n\n            return conference.replaceTrack(this, this)\n                .then(() => {\n                    this._setEffectInProgress = false;\n                })\n                .catch(error => {\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        // 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     * Asynchronously mutes this track.\n     *\n     * @returns {Promise}\n     */\n    mute() {\n        return this._queueSetMuted(true);\n    }\n\n    /**\n     * Asynchronously unmutes this track.\n     *\n     * @returns {Promise}\n     */\n    unmute() {\n        return this._queueSetMuted(false);\n    }\n\n    /**\n     * Initializes a new Promise to execute {@link #_setMuted}. May be called\n     * multiple times in a row and the invocations of {@link #_setMuted} and,\n     * 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     * @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     * Mutes / unmutes this track.\n     *\n     * @param {boolean} muted - If <tt>true</tt>, this track will be muted;\n     * 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            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                        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            if (browser.usesNewGumFlow()) {\n                promise\n                    = RTCUtils.newObtainAudioAndVideoPermissions(Object.assign(\n                        {},\n                        streamOptions,\n                        { constraints: { video: this._constraints } }));\n            } else {\n                if (this.resolution) {\n                    streamOptions.resolution = this.resolution;\n                }\n\n                promise\n                    = RTCUtils.obtainAudioAndVideoPermissions(streamOptions);\n            }\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\n                    = browser.usesNewGumFlow()\n                        ? streamsInfo.find(\n                            info => info.track.kind === mediaType)\n                        : streamsInfo.find(\n                            info => info.mediaType === 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(() => this._sendMuteStatus(muted))\n            .then(() => this.emit(TRACK_MUTE_CHANGED, this));\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\n        // process. Only TraceablePeerConnections to which the track is attached\n        // should care about this action. The TPCs to which the track is not\n        // attached can sync up when track is re-attached.\n        // A problem with that is that the \"modify sources\" queue is part of\n        // the JingleSessionPC and it would be excluded from the process. One\n        // solution would be to extract class between TPC and JingleSessionPC\n        // which would contain the queue and would notify the signaling layer\n        // when local SSRCs are changed. This would help to separate XMPP from\n        // 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     * Removes stream from conference and marks it as \"mute\" operation.\n     *\n     * @param {Function} successCallback will be called on success\n     * @param {Function} errorCallback will be called on error\n     * @private\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 {Promise}\n     */\n    _sendMuteStatus(mute) {\n        if (!this.conference || !this.conference.room) {\n            return Promise.resolve();\n        }\n\n        return new Promise(resolve => {\n            this.conference.room[\n                this.isAudioTrack()\n                    ? 'setAudioMute'\n                    : 'setVideoMute'](mute, resolve);\n        });\n    }\n\n    /**\n     * @inheritdoc\n     *\n     * Stops sending the media track. And removes it from the HTML.\n     * NOTE: Works for local tracks only.\n     *\n     * @extends JitsiTrack#dispose\n     * @returns {Promise}\n     */\n    dispose() {\n        this._switchStreamEffect();\n\n        let promise = Promise.resolve();\n\n        if (this.conference) {\n            promise = 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 <tt>true</tt> - if the stream is muted and <tt>false</tt>\n     * otherwise.\n     *\n     * @returns {boolean} <tt>true</tt> - if the stream is muted and\n     * <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        return !this.track || !this.track.enabled;\n    }\n\n    /**\n     * Sets the JitsiConference object associated with the track. This is temp\n     * solution.\n     *\n     * @param conference the JitsiConference object\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     * Returns <tt>true</tt>.\n     *\n     * @returns {boolean} <tt>true</tt>\n     */\n    isLocal() {\n        return true;\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     * 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     * Handles bytes sent statistics.\n     *\n     * @param {TraceablePeerConnection} tpc the source of the \"bytes sent\" stat\n     * @param {number} bytesSent the new value\n     * NOTE: used only for audio tracks to detect audio issues.\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     * Returns facing mode for video track from camera. For other cases (e.g.\n     * audio track or 'desktop' video track) 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            let trackSettings;\n\n            try {\n                trackSettings = this.track.getSettings();\n            } catch (e) {\n                // XXX React-native-webrtc, for example, defines\n                // MediaStreamTrack#getSettings() but the implementation throws\n                // a \"Not implemented\" Error.\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     * Stops the associated MediaStream.\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     * Switches the camera facing mode if the WebRTC implementation supports the\n     * custom MediaStreamTrack._switchCamera method. Currently, the method in\n     * question is implemented in react-native-webrtc only. When such a WebRTC\n     * implementation is executing, the method is the preferred way to switch\n     * between the front/user-facing and the back/environment-facing cameras\n     * because it will likely be (as is the case of react-native-webrtc)\n     * noticeably faster that creating a new MediaStreamTrack via a new\n     * getUserMedia call with the switched facingMode constraint value.\n     * Moreover, the approach with a new getUserMedia call may not even work:\n     * WebRTC on Android and iOS is either very slow to open the camera a second\n     * time or plainly freezes attempting to do that.\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     * Checks whether the attached MediaStream is receiving data from source or\n     * not. If the stream property is null(because of mute or another reason)\n     * this method will return false.\n     * NOTE: This method doesn't indicate problem with the streams directly.\n     * For example in case of video mute the method will return false or if the\n     * user has disposed the track.\n     *\n     * @returns {boolean} true if the stream is receiving data and false\n     * 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\n        // readyState can have value muted. In the latest versions\n        // readyState can have values \"live\" and \"ended\" and there is\n        // muted boolean property. If the stream is muted that means that\n        // we aren't receiving any data from the source. We want to notify\n        // the users for error if the stream is muted or ended on it's\n        // 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     * 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","import { EventEmitter } from 'events';\nimport { getLogger } from 'jitsi-meet-logger';\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        this._vadEmitter.on(VAD_SCORE_PUBLISHED, this._processVADScore);\n        this._vadEmitter.start();\n        this._isVADEmitterRunning = true;\n    }\n\n    /**\n     * Stop the {@link TrackVADEmitter} and detach the event listener.\n     * @returns {void}\n     */\n    _stopVADEmitter() {\n        this._vadEmitter.removeListener(VAD_SCORE_PUBLISHED, this._processVADScore);\n        this._vadEmitter.stop();\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 { $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|undefined} 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|undefined} 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 creator's jid of the session.\n     * @param {JitsiParticipant} participant - The creator of the session.\n     */\n    setInitiator(participant) {\n        this._initiator = participant;\n    }\n\n    /**\n     * Sets the jid of the participant that stopped the session.\n     * @param {JitsiParticipant} participant  - The participant's jid,\n     * that stopped the session.\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 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 * as 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        //\n        // NOTE: We don't cache the result for the query because this can potentialy lead to outdated result returned\n        // from isDevicePermissionGranted method ( for the time period before the first GUM has been resolved) if the\n        //  onchange handler is not working.\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                    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                    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     * NOTE: We don't cache the result from the query because this can potentialy lead to outdated result returned\n     * from this method ( for the time period before the first GUM has been resolved) if the onchange handler is not\n     * working.\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\n     * from more than one device.\n     *\n     * @returns {boolean}\n     */\n    isMultipleAudioInputSupported() {\n        return !browser.isFirefox();\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","/* 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","module.exports = function(module) {\n\tif (!module.webpackPolyfill) {\n\t\tmodule.deprecate = function() {};\n\t\tmodule.paths = [];\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\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","/* global MediaRecorder, MediaStream */\n\nconst RecordingResult = require('./recordingResult');\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 * 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 */\nconst TrackRecorder = function(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/**\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 */\nmodule.exports = AudioRecorder;\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","/* global __filename, $, Promise */\n\nimport EventEmitter from 'events';\nimport { getLogger } from 'jitsi-meet-logger';\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 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 { QualityController } from './modules/qualitycontrol/QualityController';\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 {\n    FEATURE_E2EE,\n    FEATURE_JIGASI,\n    JITSI_MEET_MUC_TYPE\n} from './modules/xmpp/xmpp';\nimport CodecMimeType from './service/RTC/CodecMimeType';\nimport * as MediaType from './service/RTC/MediaType';\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 * as 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) {\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.eventEmitter = new EventEmitter();\n    this.options = options;\n    this.eventManager = new JitsiConferenceEventManager(this);\n    this.participants = {};\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    // 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    this._audioOutputProblemDetector = new AudioOutputProblemDetector(this);\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 Encryprtion is supported');\n\n        this._e2eEncryption = new E2EEncryption(this);\n    }\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 (!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    // Override connection and xmpp properties (Useful if the connection\n    // reloaded)\n    if (options.connection) {\n        this.connection = options.connection;\n        this.xmpp = this.connection.xmpp;\n\n        // Setup XMPP events only if we have new connection object.\n        this.eventManager.setupXMPPListeners();\n    }\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    // 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.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    }\n\n    this.qualityController = new QualityController(this);\n\n    this.participantConnectionStatus\n        = new ParticipantConnectionStatusHandler(\n            this.rtc,\n            this,\n            {\n                // Both these options are not public API, leaving it here only\n                // as an entry point through config for tuning up purposes.\n                // Default values should be adjusted as soon as optimal values\n                // are discovered.\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        });\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.isSafari()) {\n\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.isSafari()) {\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\n/**\n * Joins the conference.\n * @param password {string} the password\n */\nJitsiConference.prototype.join = function(password) {\n    if (this.room) {\n        this.room.join(password).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 = 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    // 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\n    if (this.room) {\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\n        this.eventManager.removeXMPPListeners();\n\n        this.room = null;\n\n        return room.leave()\n            .then(() => {\n                if (this.rtc) {\n                    this.rtc.destroy();\n                }\n            })\n            .catch(error => {\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                throw error;\n            });\n    }\n\n    // If this.room == null we are calling second time leave().\n    return Promise.reject(\n        new Error('The conference is has been already left'));\n};\n\n/**\n * Returns the currently active media session if any.\n *\n * @returns {JingleSessionPC|undefined}\n * @private\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 * @private\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 * Returns name of this conference.\n */\nJitsiConference.prototype.getName = function() {\n    return this.options.name;\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 * 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(\n        message, elementName = 'body') {\n    if (this.room) {\n        const displayName = (this.room.getFromPresence('nick') || {}).value;\n\n        this.room.sendMessage(message, elementName, displayName);\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(\n        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.addToPresence(name, values);\n        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        this.room.addToPresence('nick', {\n            attributes: { xmlns: 'http://jabber.org/protocol/nick' },\n            value: name\n        });\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    }\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        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(\n        audioLevel,\n        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);\n    }\n\n    let actorParticipant;\n\n    if (this.mutedByFocusActor) {\n        const actorId = Strophe.getResourceFromJid(this.mutedByFocusActor);\n\n        actorParticipant = this.participants[actorId];\n    }\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_MUTE_CHANGED, track, actorParticipant);\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,\n        track.muteHandler);\n    track.removeEventListener(JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n        track.audioLevelHandler);\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    // First do the removal of the oldTrack at the JitsiConference level\n    if (oldTrack) {\n        if (oldTrack.disposed) {\n            return Promise.reject(\n                new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n        }\n    }\n    if (newTrack) {\n        if (newTrack.disposed) {\n            return Promise.reject(\n                new JitsiTrackError(JitsiTrackErrors.TRACK_IS_DISPOSED));\n        }\n    }\n\n    // Now replace the stream at the lower levels\n    return this._doReplaceTrack(oldTrack, newTrack)\n        .then(() => {\n            if (oldTrack) {\n                this.onLocalTrackRemoved(oldTrack);\n            }\n            if (newTrack) {\n                // Now handle the addition of the newTrack at the\n                // JitsiConference level\n                this._setupNewTrack(newTrack);\n            }\n\n            return Promise.resolve();\n        }, error => Promise.reject(new Error(error)));\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(\n            this.jvbJingleSession.replaceTrack(oldTrack, newTrack));\n    } else {\n        logger.info('_doReplaceTrack - no JVB JingleSession');\n    }\n\n    if (this.p2pJingleSession) {\n        replaceTrackPromises.push(\n            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 * Operations related to creating a new track\n * @param {JitsiLocalTrack} newTrack the new track being created\n */\nJitsiConference.prototype._setupNewTrack = function(newTrack) {\n    if (newTrack.isAudioTrack() || (newTrack.isVideoTrack()\n            && newTrack.videoType !== VideoType.DESKTOP)) {\n        // Report active device to statistics\n        const devices = RTC.getCurrentlyAvailableMediaDevices();\n        const device\n            = devices.find(\n                d =>\n                    d.kind === `${newTrack.getTrack().kind}input`\n                        && d.label === newTrack.getTrack().label);\n\n        if (device) {\n            Statistics.sendActiveDeviceListEvent(\n                RTC.getEventDataForActiveDevice(device));\n        }\n    }\n    if (newTrack.isVideoTrack()) {\n        this.removeCommand('videoType');\n        this.sendCommand('videoType', {\n            value: newTrack.videoType,\n            attributes: {\n                xmlns: 'http://jitsi.org/jitmeet/video'\n            }\n        });\n    }\n    this.rtc.addLocalTrack(newTrack);\n\n    // ensure that we're sharing proper \"is muted\" state\n    if (newTrack.isAudioTrack()) {\n        this.room.setAudioMute(newTrack.isMuted());\n    } else {\n        this.room.setVideoMute(newTrack.isMuted());\n    }\n\n    newTrack.muteHandler = this._fireMuteChangeEvent.bind(this, newTrack);\n    newTrack.audioLevelHandler = this._fireAudioLevelChangeEvent.bind(this);\n    newTrack.addEventListener(\n        JitsiTrackEvents.TRACK_MUTE_CHANGED,\n        newTrack.muteHandler);\n    newTrack.addEventListener(\n        JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n        newTrack.audioLevelHandler);\n\n    newTrack._setConference(this);\n\n    this.eventEmitter.emit(JitsiConferenceEvents.TRACK_ADDED, newTrack);\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.info(\n            'Add local MediaStream as unmute -'\n                + ' no JVB Jingle session started yet');\n    }\n\n    if (this.p2pJingleSession) {\n        addAsUnmutePromises.push(this.p2pJingleSession.addTrackAsUnmute(track));\n    } else {\n        logger.info(\n            'Add local MediaStream as unmute -'\n                + ' no P2P Jingle session started yet');\n    }\n\n    return Promise.all(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(\n            this.jvbJingleSession.removeTrackAsMute(track));\n    } else {\n        logger.info(\n            'Remove local MediaStream - no JVB JingleSession started yet');\n    }\n    if (this.p2pJingleSession) {\n        removeAsMutePromises.push(\n            this.p2pJingleSession.removeTrackAsMute(track));\n    } else {\n        logger.info(\n            'Remove local MediaStream - no P2P JingleSession started yet');\n    }\n\n    return Promise.all(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.rtc.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.rtc.getLastN();\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.rtc.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\n    = function(countHidden = false) {\n\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 * Kick participant from this conference.\n * @param {string} id id of the participant to kick\n */\nJitsiConference.prototype.kickParticipant = function(id) {\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    this.room.kick(participant.getJid());\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) {\n    const participant = this.getParticipantById(id);\n\n    if (!participant) {\n        return;\n    }\n    this.room.muteParticipant(participant.getJid(), true);\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 */\nJitsiConference.prototype.onMemberJoined = function(\n        jid, nick, role, isHidden, statsID, status, identity, botType, fullJid, features) {\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._role = role;\n    participant._botType = botType;\n    participant._features = features || new Set();\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._botType = 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._botType) {\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\n    delete this.participants[id];\n\n    const removedTracks = this.rtc.removeRemoteTracks(id);\n\n    removedTracks.forEach(\n        track =>\n            this.eventEmitter.emit(JitsiConferenceEvents.TRACK_REMOVED, track));\n\n    // there can be no participant in case the member that left is focus\n    if (participant) {\n        this.eventEmitter.emit(\n            JitsiConferenceEvents.USER_LEFT, id, participant);\n    }\n\n    this._maybeStartOrStopP2P(true /* triggered by user left event */);\n    this._maybeClearSITimeout();\n};\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 */\nJitsiConference.prototype.onMemberKicked = function(isSelfPresence, actorId, kickedParticipantId) {\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);\n\n        this.leave();\n\n        return;\n    }\n\n    const kickedParticipant = this.participants[kickedParticipantId];\n\n    this.eventEmitter.emit(\n        JitsiConferenceEvents.PARTICIPANT_KICKED, actorParticipant, kickedParticipant);\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._role = 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 into\n * the conference.\n *\n * @param {JitsiRemoteTrack} track the JitsiRemoteTrack which was added to this\n * JitsiConference\n */\nJitsiConference.prototype.onRemoteTrackAdded = function(track) {\n    if (track.isP2P && !this.isP2PActive()) {\n        logger.info(\n            'Trying to add remote P2P track, when not in P2P - IGNORED');\n\n        return;\n    } else if (!track.isP2P && this.isP2PActive()) {\n        logger.info(\n            '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.addEventListener(\n        JitsiTrackEvents.TRACK_AUDIO_LEVEL_CHANGED,\n        (audioLevel, tpc) => {\n            const activeTPC = this.getActivePeerConnection();\n\n            if (activeTPC === tpc) {\n                emitter.emit(\n                    JitsiConferenceEvents.TRACK_AUDIO_LEVEL_CHANGED,\n                    id,\n                    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(\n                    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(\n        jingleSession,\n        jingleOffer) {\n\n    let rejectReason;\n\n    if (!browser.supportsP2P()) {\n        rejectReason = {\n            reason: 'unsupported-applications',\n            reasonDescription: 'P2P not supported',\n            errorMsg: 'This client does not support P2P connections'\n        };\n    } else if (!this.isP2PEnabled() && !this.isP2PTestModeEnabled()) {\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(\n        jingleSession,\n        jingleOffer,\n        now) {\n    // Handle incoming P2P call\n    if (jingleSession.isP2P) {\n        this._onIncomingCallP2P(jingleSession, jingleOffer);\n    } else {\n        if (!this.room.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(\n        jingleSession,\n        jingleOffer,\n        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(\n            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(\n        JitsiConferenceEvents.SERVER_REGION_CHANGED,\n        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(this.room, this.rtc, {\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    // Add local tracks to the session\n    const localTracks = this.getLocalTracks();\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(\n                    JitsiConferenceEvents._MEDIA_SESSION_STARTED,\n                    jingleSession);\n                if (!this.isP2PActive()) {\n                    this.eventEmitter.emit(\n                        JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED,\n                        jingleSession);\n                }\n            },\n            error => {\n                GlobalOnErrorHandler.callErrorHandler(error);\n                logger.error(\n                    'Failed to accept incoming Jingle session', error);\n            },\n            localTracks\n        );\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(\n        jingleSession,\n        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(\n        jingleSession,\n        reasonCondition,\n        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        // 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        }\n        this._stopP2PSession();\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    if (this.isP2PActive()) {\n        return this.p2pJingleSession.peerconnection;\n    }\n\n    return this.jvbJingleSession ? this.jvbJingleSession.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        return;\n    }\n    this.startMutedPolicy = policy;\n    this.room.addToPresence('startmuted', {\n        attributes: {\n            audio: policy.audio,\n            video: policy.video,\n            xmlns: 'http://jitsi.org/jitmeet/start-muted'\n        }\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(\n        overallFeedback,\n        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\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 * <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 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(\n        message,\n        to = '',\n        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.restartInProgress = true;\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('connectivity-error', 'ICE FAILED');\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(\n        jingleSession,\n        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.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                this.p2pJingleSession);\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(\n        '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(\n        '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(\n        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('decline', 'force JVB121');\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(\n            JitsiConferenceEvents.PROPERTIES_CHANGED,\n            this.properties);\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(\n        sessionNickname,\n        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.rtc.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(\n        JitsiConferenceEvents._MEDIA_SESSION_ACTIVE_CHANGED,\n        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.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    // NOTE one may consider to start P2P with the local tracks detached,\n    // but no data will be sent until ICE succeeds anyway. And we switch\n    // immediately once the P2P ICE connects.\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 (!browser.supportsP2P()\n        || !this.isP2PEnabled()\n        || this.isP2PTestModeEnabled()) {\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._botType === 'poltergeist' || p._features.has(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 {string} [reason=\"success\"] one of the Jingle \"reason\" element\n * names as defined by https://xmpp.org/extensions/xep-0166.html#def-reason\n * @param {string} [reasonDescription=\"Turing off P2P session\"] text\n * description that will be included in the session terminate message\n * @private\n */\nJitsiConference.prototype._stopP2PSession = function(\n        reason,\n        reasonDescription) {\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) {\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 lack of 'reason' is considered as graceful session terminate\n            // where both initiator and responder terminate their sessions\n            // simultaneously.\n            if (reason) {\n                logger.error(\n                    'An error occurred while trying to terminate'\n                        + ' P2P Jingle session', error);\n            }\n        }, {\n            reason: reason ? reason : 'success',\n            reasonDescription: reasonDescription\n                ? reasonDescription : 'Turing off P2P session',\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) {\n            this._addRemoteJVBTracks();\n        } else {\n            logger.info('Not adding remote JVB tracks - no session yet');\n        }\n    }\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/**\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() {\n    this._stopP2PSession();\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 * 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.qualityController.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.qualityController.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\n    = 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    }\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 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 * 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    }\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 * 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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport { Strophe } from 'strophe.js';\n\nimport * as JitsiConferenceErrors from './JitsiConferenceErrors';\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport Statistics from './modules/statistics/statistics';\nimport EventEmitterForwarder from './modules/util/EventEmitterForwarder';\nimport * as 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(\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());\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    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.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\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.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 participant = conference.getParticipantById(\n                    Strophe.getResourceFromJid(jid));\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, displayName, txt, myJid, ts) => {\n            const id = Strophe.getResourceFromJid(jid);\n\n            conference.eventEmitter.emit(\n                JitsiConferenceEvents.MESSAGE_RECEIVED,\n                id, txt, ts, displayName);\n        });\n\n    chatRoom.addListener(\n        XMPPEvents.PRIVATE_MESSAGE_RECEIVED,\n\n        // eslint-disable-next-line max-params\n        (jid, displayName, 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                logger.warn(\n                    'Ignored XMPPEvents.JSON_MESSAGE_RECEIVED for not existing '\n                    + `participant: ${from}`,\n                    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\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        id => {\n            if (conference.lastDominantSpeaker !== id && conference.room) {\n                conference.lastDominantSpeaker = id;\n                conference.eventEmitter.emit(\n                    JitsiConferenceEvents.DOMINANT_SPEAKER_CHANGED, id);\n\n                if (conference.statistics && conference.myUserId() === id) {\n                    // We are the new dominant speaker.\n                    conference.statistics.sendDominantSpeakerEvent(\n                        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.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    conference.xmpp.caps.removeListener(\n        XMPPEvents.PARTICIPANT_FEATURES_CHANGED,\n        this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED]);\n    delete this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED];\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    const featuresChangedListener = (from, features) => {\n        const participant = conference.getParticipantById(Strophe.getResourceFromJid(from));\n\n        if (participant) {\n            participant._features = features;\n            conference.eventEmitter.emit(JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED, participant);\n        }\n    };\n\n    conference.xmpp.caps.addListener(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, featuresChangedListener);\n    this.xmppListeners[XMPPEvents.PARTICIPANT_FEATURES_CHANGED] = featuresChangedListener;\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\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","import { BrowserDetection } from '@jitsi/js-utils';\nimport { getLogger } from 'jitsi-meet-logger';\n\nconst logger = getLogger(__filename);\n\n/* Minimum required Chrome / Chromium version. This applies also to derivatives. */\nconst MIN_REQUIRED_CHROME_VERSION = 72;\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\n     * the <tt>PeerConnection</tt> and disposed on video mute (in order to turn\n     * off the camera device).\n     * @return {boolean} <tt>true</tt> if the current browser supports this\n     * strategy or <tt>false</tt> otherwise.\n     */\n    doesVideoMuteByStreamRemove() {\n        return this.isChromiumBased() || this.isSafari();\n    }\n\n    /**\n     * Check whether or not the current browser support peer to peer connections\n     * @return {boolean} <tt>true</tt> if p2p is supported or <tt>false</tt>\n     * otherwise.\n     */\n    supportsP2P() {\n        return !this.usesUnifiedPlan();\n    }\n\n    /**\n     * Checks if the current browser is Chromium based, that is, it's either\n     * Chrome / Chromium or uses it as its engine, but doesn't identify as\n     * 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,\n     *   such as Brave and Vivaldi\n     * - Browsers which are NOT Chrome but use it as their engine, and have\n     *   custom detection code: Opera, Electron and NW.JS\n     */\n    isChromiumBased() {\n        return this.isChrome()\n            || this.isElectron()\n            || this.isNWJS()\n            || this.isOpera();\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        return (this.isChromiumBased() && this._getChromiumBasedVersion() >= MIN_REQUIRED_CHROME_VERSION)\n            || this.isFirefox()\n            || this.isReactNative()\n            || (this.isSafari() && !this.isVersionLessThan('12.1'));\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() || this.isSafari();\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.isSafari();\n    }\n\n    /**\n     * Checks if the current browser supports setting codec preferences on the transceiver.\n     * @returns {boolean}\n     */\n    supportsCodecPreferences() {\n        return this.usesUnifiedPlan()\n            && typeof window.RTCRtpTransceiver !== 'undefined'\n            && Object.keys(window.RTCRtpTransceiver.prototype).indexOf('setCodecPreferences') > -1\n            && Object.keys(RTCRtpSender.prototype).indexOf('getCapabilities') > -1\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.isSafari();\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.isSafari();\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\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     * Checks if the browser uses plan B.\n     *\n     * @returns {boolean}\n     */\n    usesPlanB() {\n        return !this.usesUnifiedPlan();\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.isSafari();\n    }\n\n    /**\n     * Checks if the browser uses unified plan.\n     *\n     * @returns {boolean}\n     */\n    usesUnifiedPlan() {\n        if (this.isFirefox()) {\n            return true;\n        }\n\n        if (this.isSafari() && typeof window.RTCRtpTransceiver !== 'undefined') {\n            // https://trac.webkit.org/changeset/236144/webkit/trunk/LayoutTests/webrtc/video-addLegacyTransceiver.html\n            // eslint-disable-next-line no-undef\n            return Object.keys(RTCRtpTransceiver.prototype).indexOf('currentDirection') > -1;\n        }\n\n        return false;\n    }\n\n    /**\n     * Returns whether or not the current browser should be using the new\n     * getUserMedia flow, which utilizes the adapter shim. This method should\n     * be temporary and used while migrating all browsers to use adapter and\n     * the new getUserMedia.\n     *\n     * @returns {boolean}\n     */\n    usesNewGumFlow() {\n        if (this.isChromiumBased() || this.isFirefox() || this.isSafari()) {\n            return true;\n        }\n\n        return false;\n    }\n\n    /**\n     * Checks if the browser uses webrtc-adapter. All browsers using the new\n     * getUserMedia flow.\n     *\n     * @returns {boolean}\n     */\n    usesAdapter() {\n        return this.usesNewGumFlow();\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 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                || window.RTCRtpSender.prototype.createEncodedVideoStreams))) {\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 the \"sdpSemantics\" configuration option.\n     * https://webrtc.org/web-apis/chrome/unified-plan/\n     *\n     * @returns {boolean}\n     */\n    supportsSdpSemantics() {\n        return this.isChromiumBased();\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","!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}})}));","import { getLogger } from 'jitsi-meet-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","\nimport { getLogger } from 'jitsi-meet-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","import { browsers } from '@jitsi/js-utils';\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as 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 * The lib-jitsi-meet browser-agnostic names of the browser-specific keys\n * reported by RTCPeerConnection#getStats mapped by browser.\n */\nconst KEYS_BY_BROWSER_TYPE = {};\n\nKEYS_BY_BROWSER_TYPE[browsers.FIREFOX] = {\n    'ssrc': 'ssrc',\n    'packetsReceived': 'packetsReceived',\n    'packetsLost': 'packetsLost',\n    'packetsSent': 'packetsSent',\n    'bytesReceived': 'bytesReceived',\n    'bytesSent': 'bytesSent',\n    'framerateMean': 'framerateMean',\n    'ip': 'address',\n    'port': 'port',\n    'protocol': 'protocol'\n};\nKEYS_BY_BROWSER_TYPE[browsers.CHROME] = {\n    'receiveBandwidth': 'googAvailableReceiveBandwidth',\n    'sendBandwidth': 'googAvailableSendBandwidth',\n    'remoteAddress': 'googRemoteAddress',\n    'transportType': 'googTransportType',\n    'localAddress': 'googLocalAddress',\n    'activeConnection': 'googActiveConnection',\n    'ssrc': 'ssrc',\n    'packetsReceived': 'packetsReceived',\n    'packetsSent': 'packetsSent',\n    'packetsLost': 'packetsLost',\n    'bytesReceived': 'bytesReceived',\n    'bytesSent': 'bytesSent',\n    'googCodecName': 'googCodecName',\n    'googFrameHeightReceived': 'googFrameHeightReceived',\n    'googFrameWidthReceived': 'googFrameWidthReceived',\n    'googFrameHeightSent': 'googFrameHeightSent',\n    'googFrameWidthSent': 'googFrameWidthSent',\n    'googFrameRateReceived': 'googFrameRateReceived',\n    'googFrameRateSent': 'googFrameRateSent',\n    'audioInputLevel': 'audioInputLevel',\n    'audioOutputLevel': 'audioOutputLevel',\n    'currentRoundTripTime': 'googRtt',\n    'remoteCandidateType': 'googRemoteCandidateType',\n    'localCandidateType': 'googLocalCandidateType',\n    'ip': 'ip',\n    'port': 'port',\n    'protocol': 'protocol'\n};\nKEYS_BY_BROWSER_TYPE[browsers.REACT_NATIVE] = {\n    'packetsReceived': 'packetsReceived',\n    'packetsSent': 'packetsSent',\n    'bytesReceived': 'bytesReceived',\n    'bytesSent': 'bytesSent',\n    'frameWidth': 'frameWidth',\n    'frameHeight': 'frameHeight',\n    'framesPerSecond': 'framesPerSecond',\n    'ip': 'ip',\n    'port': 'port',\n    'protocol': 'protocol'\n};\nKEYS_BY_BROWSER_TYPE[browsers.OPERA]\n    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];\nKEYS_BY_BROWSER_TYPE[browsers.NWJS]\n    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];\nKEYS_BY_BROWSER_TYPE[browsers.ELECTRON]\n    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];\nKEYS_BY_BROWSER_TYPE[browsers.SAFARI]\n    = KEYS_BY_BROWSER_TYPE[browsers.CHROME];\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(\n        peerconnection,\n        audioLevelsInterval,\n        statsInterval,\n        eventEmitter) {\n    // StatsCollector depends entirely on the format of the reports returned by\n    // RTCPeerConnection#getStats. Given that the value of\n    // browser#getName() is very unlikely to change at runtime, it\n    // makes sense to discover whether StatsCollector supports the executing\n    // browser as soon as possible. Otherwise, (1) getStatValue would have to\n    // needlessly check a \"static\" condition multiple times very very often and\n    // (2) the lack of support for the executing browser would be discovered and\n    // reported multiple times very very often too late in the execution in some\n    // totally unrelated callback.\n    /**\n     * The browser type supported by this StatsCollector. In other words, the\n     * type of the browser which initialized this StatsCollector\n     * instance.\n     * @private\n     */\n    this._browserType = browser.getName();\n    const keys = KEYS_BY_BROWSER_TYPE[this._browserType];\n\n    if (!keys) {\n        throw new Error(`The browser type '${this._browserType}' isn't supported!`);\n    }\n\n    /**\n     * Whether to use the Promise-based getStats API or not.\n     * @type {boolean}\n     */\n    this._usesPromiseGetStats\n        = browser.isSafari() || browser.isFirefox() || browser.isReactNative();\n\n    /**\n     * The function which is to be used to retrieve the value associated in a\n     * report returned by RTCPeerConnection#getStats with a lib-jitsi-meet\n     * browser-agnostic name/key.\n     *\n     * @function\n     * @private\n     */\n    this._getStatValue\n        = this._usesPromiseGetStats\n            ? this._defineNewGetStatValueMethod(keys)\n            : this._defineGetStatValueMethod(keys);\n\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.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/* eslint-enable max-params */\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();\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                        report => {\n                            let results = null;\n\n                            if (!report || !report.result\n                                || typeof report.result !== 'function') {\n                                results = report;\n                            } else {\n                                results = report.result();\n                            }\n                            this.currentAudioLevelsReport = results;\n                            if (this._usesPromiseGetStats) {\n                                this.processNewAudioLevelReport();\n                            } else {\n                                this.processAudioLevelReport();\n                            }\n\n                            this.baselineAudioLevelsReport\n                                = this.currentAudioLevelsReport;\n                        },\n                        error => this.errorCallback(error)\n                    );\n                }\n            },\n            this.audioLevelsIntervalMilis\n        );\n    }\n\n    this.statsIntervalId = setInterval(\n        () => {\n            // Interval updates\n            this.peerconnection.getStats(\n                report => {\n                    let results = null;\n\n                    if (!report || !report.result\n                        || typeof report.result !== 'function') {\n                        // firefox\n                        results = report;\n                    } else {\n                        // chrome\n                        results = report.result();\n                    }\n\n                    this.currentStatsReport = results;\n                    try {\n                        if (this._usesPromiseGetStats) {\n                            this.processNewStatsReport();\n                        } else {\n                            this.processStatsReport();\n                        }\n                    } catch (e) {\n                        GlobalOnErrorHandler.callErrorHandler(e);\n                        logger.error(`Unsupported key:${e}`, e);\n                    }\n\n                    this.previousStatsReport = this.currentStatsReport;\n                },\n                error => this.errorCallback(error)\n            );\n        },\n        this.statsIntervalMilis\n    );\n};\n\n/**\n * Defines a function which (1) is to be used as a StatsCollector method and (2)\n * gets the value from a specific report returned by RTCPeerConnection#getStats\n * associated with a lib-jitsi-meet browser-agnostic name.\n *\n * @param {Object.<string,string>} keys the map of LibJitsi browser-agnostic\n * names to RTCPeerConnection#getStats browser-specific keys\n */\nStatsCollector.prototype._defineGetStatValueMethod = function(keys) {\n    // Define the function which converts a lib-jitsi-meet browser-asnostic name\n    // to a browser-specific key of a report returned by\n    // RTCPeerConnection#getStats.\n    const keyFromName = function(name) {\n        const key = keys[name];\n\n        if (key) {\n            return key;\n        }\n\n        throw new Error(`The property '${name}' isn't supported!`);\n    };\n\n    // Define the function which retrieves the value from a specific report\n    // returned by RTCPeerConnection#getStats associated with a given\n    // browser-specific key.\n    let itemStatByKey;\n\n    switch (this._browserType) {\n    case browsers.CHROME:\n    case browsers.OPERA:\n    case browsers.NWJS:\n    case browsers.ELECTRON:\n        // TODO What about other types of browser which are based on Chrome such\n        // as NW.js? Every time we want to support a new type browser we have to\n        // go and add more conditions (here and in multiple other places).\n        // Cannot we do a feature detection instead of a browser type check? For\n        // example, if item has a stat property of type function, then it's very\n        // likely that whoever defined it wanted you to call it in order to\n        // retrieve the value associated with a specific key.\n        itemStatByKey = (item, key) => item.stat(key);\n        break;\n    default:\n        itemStatByKey = (item, key) => item[key];\n    }\n\n    // Compose the 2 functions defined above to get a function which retrieves\n    // the value from a specific report returned by RTCPeerConnection#getStats\n    // associated with a specific lib-jitsi-meet browser-agnostic name.\n    return (item, name) => itemStatByKey(item, keyFromName(name));\n};\n\n/**\n * Obtains a stat value from given stat and converts it to a non-negative\n * number. If the value is either invalid or negative then 0 will be returned.\n * @param report\n * @param {string} name\n * @return {number}\n * @private\n */\nStatsCollector.prototype.getNonNegativeStat = function(report, name) {\n    let value = this._getStatValue(report, name);\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/* eslint-disable no-continue */\n\n/**\n * Stats processing logic.\n */\nStatsCollector.prototype.processStatsReport = function() {\n    if (!this.previousStatsReport) {\n        return;\n    }\n\n    const getStatValue = this._getStatValue;\n    const byteSentStats = {};\n\n    for (const idx in this.currentStatsReport) {\n        if (!this.currentStatsReport.hasOwnProperty(idx)) {\n            continue;\n        }\n        const now = this.currentStatsReport[idx];\n\n        // The browser API may return \"undefined\" values in the array\n        if (!now) {\n            continue;\n        }\n\n        try {\n            const receiveBandwidth = getStatValue(now, 'receiveBandwidth');\n            const sendBandwidth = getStatValue(now, 'sendBandwidth');\n\n            if (receiveBandwidth || sendBandwidth) {\n                this.conferenceStats.bandwidth = {\n                    'download': Math.round(receiveBandwidth / 1000),\n                    'upload': Math.round(sendBandwidth / 1000)\n                };\n            }\n        } catch (e) { /* not supported*/ }\n\n        if (now.type === 'googCandidatePair') {\n            let active, ip, localCandidateType, localip,\n                remoteCandidateType, rtt, type;\n\n            try {\n                active = getStatValue(now, 'activeConnection');\n                if (!active) {\n                    continue;\n                }\n\n                ip = getStatValue(now, 'remoteAddress');\n                type = getStatValue(now, 'transportType');\n                localip = getStatValue(now, 'localAddress');\n                localCandidateType = getStatValue(now, 'localCandidateType');\n                remoteCandidateType = getStatValue(now, 'remoteCandidateType');\n                rtt = this.getNonNegativeStat(now, 'currentRoundTripTime');\n            } catch (e) { /* not supported*/ }\n            if (!ip || !type || !localip || active !== 'true') {\n                continue;\n            }\n\n            // Save the address unless it has been saved already.\n            const conferenceStatsTransport = this.conferenceStats.transport;\n\n            if (!conferenceStatsTransport.some(\n                    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,\n                    remoteCandidateType,\n                    rtt\n                });\n            }\n            continue;\n        }\n\n        if (now.type === 'candidatepair') {\n            // we need succeeded and selected pairs only\n            if (now.state !== 'succeeded' || !now.selected) {\n                continue;\n            }\n\n            const local = this.currentStatsReport[now.localCandidateId];\n            const remote = this.currentStatsReport[now.remoteCandidateId];\n\n            this.conferenceStats.transport.push({\n                ip: `${remote.ipAddress}:${remote.portNumber}`,\n                type: local.transport,\n                localip: `${local.ipAddress}:${local.portNumber}`,\n                p2p: this.peerconnection.isP2P,\n                localCandidateType: local.candidateType,\n                remoteCandidateType: remote.candidateType\n            });\n        }\n\n        if (now.type !== 'ssrc' && now.type !== 'outboundrtp'\n            && now.type !== 'inboundrtp' && now.type !== 'track') {\n            continue;\n        }\n\n        const before = this.previousStatsReport[idx];\n        let ssrc = this.getNonNegativeStat(now, 'ssrc');\n\n        // If type=\"track\", take the first SSRC from ssrcIds.\n        if (now.type === 'track' && Array.isArray(now.ssrcIds)) {\n            ssrc = Number(now.ssrcIds[0]);\n        }\n\n        if (!before || !ssrc) {\n            continue;\n        }\n\n        // isRemote is available only in FF and is ignored in case of chrome\n        // according to the spec\n        // https://www.w3.org/TR/webrtc-stats/#dom-rtcrtpstreamstats-isremote\n        // when isRemote is true indicates that the measurements were done at\n        // the remote endpoint and reported in an RTCP RR/XR.\n        // Fixes a problem where we are calculating local stats wrong adding\n        // the sent bytes to the local download bitrate.\n        // In new W3 stats spec, type=\"track\" has a remoteSource boolean\n        // property.\n        if (now.isRemote === true || now.remoteSource === true) {\n            continue;\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        let packetsNow = getStatValue(now, key);\n\n        if (typeof packetsNow === 'undefined'\n            || packetsNow === null || packetsNow === '') {\n            isDownloadStream = false;\n            key = 'packetsSent';\n            packetsNow = getStatValue(now, key);\n            if (typeof packetsNow === 'undefined' || packetsNow === null) {\n                logger.warn('No packetsReceived nor packetsSent stat found');\n            }\n        }\n        if (!packetsNow || packetsNow < 0) {\n            packetsNow = 0;\n        }\n\n        const packetsBefore = this.getNonNegativeStat(before, key);\n        const packetsDiff = Math.max(0, packetsNow - packetsBefore);\n\n        const packetsLostNow\n            = this.getNonNegativeStat(now, 'packetsLost');\n        const packetsLostBefore\n            = this.getNonNegativeStat(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        const bytesReceivedNow\n            = this.getNonNegativeStat(now, 'bytesReceived');\n        const bytesReceivedBefore\n            = this.getNonNegativeStat(before, 'bytesReceived');\n        const bytesReceived\n            = Math.max(0, bytesReceivedNow - bytesReceivedBefore);\n\n        let bytesSent = 0;\n\n        // TODO: clean this mess up!\n        let nowBytesTransmitted = getStatValue(now, 'bytesSent');\n\n        if (typeof nowBytesTransmitted === 'number'\n            || typeof nowBytesTransmitted === 'string') {\n            nowBytesTransmitted = Number(nowBytesTransmitted);\n            if (!isNaN(nowBytesTransmitted)) {\n                byteSentStats[ssrc] = nowBytesTransmitted;\n                if (nowBytesTransmitted > 0) {\n                    bytesSent = nowBytesTransmitted\n                        - getStatValue(before, 'bytesSent');\n                }\n            }\n        }\n        bytesSent = Math.max(0, bytesSent);\n\n        const timeMs = now.timestamp - before.timestamp;\n        let bitrateReceivedKbps = 0, bitrateSentKbps = 0;\n\n        if (timeMs > 0) {\n            // TODO is there any reason to round here?\n            bitrateReceivedKbps = Math.round((bytesReceived * 8) / timeMs);\n            bitrateSentKbps = Math.round((bytesSent * 8) / timeMs);\n        }\n\n        ssrcStats.addBitrate({\n            'download': bitrateReceivedKbps,\n            'upload': bitrateSentKbps\n        });\n\n        const resolution = {\n            height: null,\n            width: null\n        };\n\n        try {\n            let height, width;\n\n            if ((height = getStatValue(now, 'googFrameHeightReceived'))\n                && (width = getStatValue(now, 'googFrameWidthReceived'))) {\n                resolution.height = height;\n                resolution.width = width;\n            } else if ((height = getStatValue(now, 'googFrameHeightSent'))\n                && (width = getStatValue(now, 'googFrameWidthSent'))) {\n                resolution.height = height;\n                resolution.width = width;\n            }\n        } catch (e) { /* not supported*/ }\n\n        // Tries to get frame rate\n        let frameRate;\n\n        try {\n            frameRate = getStatValue(now, 'googFrameRateReceived')\n                || getStatValue(now, 'googFrameRateSent') || 0;\n        } catch (e) {\n            // if it fails with previous properties(chrome),\n            // let's try with another one (FF)\n            try {\n                frameRate = this.getNonNegativeStat(now, 'framerateMean');\n            } catch (err) { /* not supported*/ }\n        }\n        ssrcStats.setFramerate(Math.round(frameRate || 0));\n\n        if (resolution.height && resolution.width) {\n            ssrcStats.setResolution(resolution);\n        } else {\n            ssrcStats.setResolution(null);\n        }\n\n        let codec;\n\n        // Try to get the codec for later reporting.\n        try {\n            codec = getStatValue(now, 'googCodecName') || '';\n        } catch (e) { /* not supported*/ }\n\n        ssrcStats.setCodec(codec);\n    }\n\n\n    this.eventEmitter.emit(\n        StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);\n\n    this._processAndEmitReport();\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 * Stats processing logic.\n */\nStatsCollector.prototype.processAudioLevelReport = function() {\n    if (!this.baselineAudioLevelsReport) {\n        return;\n    }\n\n    const getStatValue = this._getStatValue;\n\n    for (const idx in this.currentAudioLevelsReport) {\n        if (!this.currentAudioLevelsReport.hasOwnProperty(idx)) {\n            continue;\n        }\n\n        const now = this.currentAudioLevelsReport[idx];\n\n        if (now.type !== 'ssrc' && now.type !== 'track') {\n            continue;\n        }\n\n        const before = this.baselineAudioLevelsReport[idx];\n        let ssrc = this.getNonNegativeStat(now, 'ssrc');\n\n        if (!ssrc && Array.isArray(now.ssrcIds)) {\n            ssrc = Number(now.ssrcIds[0]);\n        }\n\n        if (!before) {\n            logger.warn(`${ssrc} not enough data`);\n            continue;\n        }\n\n        if (!ssrc) {\n            if ((Date.now() - now.timestamp) < 3000) {\n                logger.warn('No ssrc: ');\n            }\n            continue;\n        }\n\n        // Audio level\n        let audioLevel;\n\n        try {\n            audioLevel\n                = getStatValue(now, 'audioInputLevel')\n                    || getStatValue(now, 'audioOutputLevel');\n        } catch (e) { /* not supported*/\n            logger.warn('Audio Levels are not available in the statistics.');\n            clearInterval(this.audioLevelsIntervalId);\n\n            return;\n        }\n\n        if (audioLevel) {\n            let isLocal;\n\n            // If type=\"ssrc\" (legacy) check whether they are received packets.\n            if (now.type === 'ssrc') {\n                isLocal = !getStatValue(now, 'packetsReceived');\n\n            // If type=\"track\", check remoteSource boolean property.\n            } else {\n                isLocal = !now.remoteSource;\n            }\n\n            // According to the W3C WebRTC Stats spec, audioLevel should be in\n            // 0..1 range (0 == silence). However browsers don't behave that\n            // way so we must convert it to 0..1.\n            // TODO: Can't find specs about what this value really is, but it\n            // seems to vary between 0 and around 32k.\n            audioLevel = audioLevel / 32767;\n\n            if (!(ssrc in this.audioLevelReportHistory)) {\n                this.audioLevelReportHistory[ssrc] = {\n                    isLocal,\n                    data: []\n                };\n            }\n            this.audioLevelReportHistory[ssrc].data.push(audioLevel);\n\n            this.eventEmitter.emit(\n                StatisticsEvents.AUDIO_LEVEL,\n                this.peerconnection,\n                ssrc,\n                audioLevel,\n                isLocal);\n        }\n    }\n};\n\n/* eslint-enable no-continue */\n\n/**\n * New promised based getStats report processing.\n * Tested with chrome, firefox and safari. Not switching it on for chrome as\n * frameRate stat is missing and calculating it using framesSent,\n * gives values double the values seen in webrtc-internals.\n * https://w3c.github.io/webrtc-stats/\n */\n\n/**\n * Defines a function which (1) is to be used as a StatsCollector method and (2)\n * gets the value from a specific report returned by RTCPeerConnection#getStats\n * associated with a lib-jitsi-meet browser-agnostic name in case of using\n * Promised based getStats.\n *\n * @param {Object.<string,string>} keys the map of LibJitsi browser-agnostic\n * names to RTCPeerConnection#getStats browser-specific keys\n */\nStatsCollector.prototype._defineNewGetStatValueMethod = function(keys) {\n    // Define the function which converts a lib-jitsi-meet browser-asnostic name\n    // to a browser-specific key of a report returned by\n    // RTCPeerConnection#getStats.\n    const keyFromName = function(name) {\n        const key = keys[name];\n\n        if (key) {\n            return key;\n        }\n\n        throw new Error(`The property '${name}' isn't supported!`);\n    };\n\n    // Compose the 2 functions defined above to get a function which retrieves\n    // the value from a specific report returned by RTCPeerConnection#getStats\n    // associated with a specific lib-jitsi-meet browser-agnostic name.\n    return (item, name) => item[keyFromName(name)];\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 new getStats logic.\n */\nStatsCollector.prototype.processNewStatsReport = function() {\n    if (!this.previousStatsReport) {\n        return;\n    }\n\n    const getStatValue = this._getStatValue;\n    const byteSentStats = {};\n\n    this.currentStatsReport.forEach(now => {\n\n        // RTCIceCandidatePairStats\n        // https://w3c.github.io/webrtc-stats/#candidatepair-dict*\n        if (now.type === 'candidate-pair'\n            && now.nominated\n            && now.state === 'succeeded') {\n\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\n                = this.currentStatsReport.get(now.remoteCandidateId);\n            const localUsedCandidate\n                = this.currentStatsReport.get(now.localCandidateId);\n\n            // RTCIceCandidateStats\n            // https://w3c.github.io/webrtc-stats/#icecandidate-dict*\n            // safari currently does not provide ice candidates in stats\n            if (remoteUsedCandidate && localUsedCandidate) {\n                const remoteIpAddress = getStatValue(remoteUsedCandidate, 'ip');\n                const remotePort = getStatValue(remoteUsedCandidate, 'port');\n                const ip = `${remoteIpAddress}:${remotePort}`;\n\n                const localIpAddress = getStatValue(localUsedCandidate, 'ip');\n                const localPort = getStatValue(localUsedCandidate, 'port');\n\n                const localIp = `${localIpAddress}:${localPort}`;\n                const type = getStatValue(remoteUsedCandidate, 'protocol');\n\n                // Save the address unless it has been saved already.\n                const conferenceStatsTransport = this.conferenceStats.transport;\n\n                if (!conferenceStatsTransport.some(\n                        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\n                = this.getNonNegativeValue(now.packetsLost);\n            const packetsLostBefore\n                = this.getNonNegativeValue(before.packetsLost);\n            const packetsLostDiff\n                = Math.max(0, packetsLostNow - packetsLostBefore);\n\n            ssrcStats.setLoss({\n                packetsTotal: packetsDiff + packetsLostDiff,\n                packetsLost: packetsLostDiff,\n                isDownloadStream\n            });\n\n            const resolution = {\n                height: null,\n                width: null\n            };\n\n            try {\n                resolution.height = getStatValue(now, 'frameHeight');\n                resolution.width = getStatValue(now, 'frameWidth');\n            } catch (e) { /* not supported*/ }\n\n            // Tries to get frame rate\n            let frameRate;\n\n            try {\n                frameRate = getStatValue(now, 'framesPerSecond');\n            } catch (err) { /* not supported*/ }\n\n            ssrcStats.setFramerate(Math.round(frameRate || 0));\n\n            if (resolution.height && resolution.width) {\n                ssrcStats.setResolution(resolution);\n            } else {\n                ssrcStats.setResolution(null);\n            }\n\n            if (now.type === 'inbound-rtp') {\n\n                ssrcStats.addBitrate({\n                    'download': this._calculateBitrate(\n                                    now, before, 'bytesReceived'),\n                    'upload': 0\n                });\n\n                // RTCInboundRtpStreamStats\n                // https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*\n                // TODO: can we use framesDecoded for frame rate, available\n                // in chrome\n            } else {\n                byteSentStats[ssrc] = this.getNonNegativeValue(now.bytesSent);\n                ssrcStats.addBitrate({\n                    'download': 0,\n                    'upload': this._calculateBitrate(\n                                now, before, 'bytesSent')\n                });\n\n                // RTCOutboundRtpStreamStats\n                // https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*\n                // TODO: can we use framesEncoded for frame rate, available\n                // in chrome\n            }\n\n            // FF has framerateMean out of spec\n            const framerateMean = now.framerateMean;\n\n            if (framerateMean) {\n                ssrcStats.setFramerate(Math.round(framerateMean || 0));\n            }\n\n\n            let codec;\n\n            // Try to get the codec for later reporting.\n            try {\n                codec = this.currentStatsReport.get(now.codecId);\n            } catch (e) { /* not supported*/ }\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        // track for resolution\n        // RTCVideoHandlerStats\n        // https://w3c.github.io/webrtc-stats/#vststats-dict*\n        // RTCMediaHandlerStats\n        // https://w3c.github.io/webrtc-stats/#mststats-dict*\n        } else if (now.type === 'track') {\n\n            const resolution = {\n                height: now.frameHeight,\n                width: now.frameWidth\n            };\n\n            // Tries to get frame rate\n            let frameRate = now.framesPerSecond;\n\n            if (!frameRate) {\n                // we need to calculate it\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\n                            = now.framesSent - before.framesSent;\n\n                        frameRate = (numberOfFramesSinceBefore / timeMs) * 1000;\n                    }\n                }\n\n                if (!frameRate) {\n                    return;\n                }\n            }\n\n            const trackIdentifier = now.trackIdentifier;\n            const ssrc = this.peerconnection.getSsrcByTrackId(trackIdentifier);\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            ssrcStats.setFramerate(Math.round(frameRate || 0));\n\n            if (resolution.height && resolution.width) {\n                ssrcStats.setResolution(resolution);\n            } else {\n                ssrcStats.setResolution(null);\n            }\n        }\n    });\n\n    this.eventEmitter.emit(\n        StatisticsEvents.BYTE_SENT_STATS, this.peerconnection, byteSentStats);\n\n    this._processAndEmitReport();\n};\n\n/**\n * Stats processing logic.\n */\nStatsCollector.prototype.processNewAudioLevelReport = 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/**\n * End new promised based getStats processing methods.\n */\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","\nimport { getLogger } from 'jitsi-meet-logger';\nimport { Strophe } from 'strophe.js';\n\n\nimport * as JitsiConferenceEvents from './JitsiConferenceEvents';\nimport { ParticipantConnectionStatus }\n    from './modules/connectivity/ParticipantConnectionStatus';\nimport { ERROR_FEATURE_VERSION_MISMATCH } from './modules/xmpp/Caps';\nimport * as MediaType from './service/RTC/MediaType';\n\nconst logger = getLogger(__filename);\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     */\n    constructor(jid, conference, displayName, hidden, statsID, status, identity) {\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._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 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     *\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     * Returns a set with the features for the participant.\n     * @param {int} timeout the timeout in ms for reply from the participant.\n     * @returns {Promise<Set<String>, Error>}\n     */\n    queryFeatures(timeout = 5000) {\n        if (this._getFeaturesPromise) {\n            return this._getFeaturesPromise;\n        }\n\n        this._getFeaturesPromise = this._conference.xmpp.caps.getFeatures(this._jid, timeout)\n            .catch(error => {\n                // Retry on feature version mismatch\n                if (error === ERROR_FEATURE_VERSION_MISMATCH) {\n                    return this._conference.xmpp.caps.getFeatures(this._jid, timeout);\n                }\n\n                logger.warn(`Failed to discover features of ${this._jid}`, error);\n\n                return Promise.reject(error);\n            });\n\n        return this._getFeaturesPromise\n            .then(result => {\n                this._getFeaturesPromise = undefined;\n\n                return result;\n            }, error => {\n                this._getFeaturesPromise = undefined;\n\n                throw error;\n            });\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","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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-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     */\n    constructor() {\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        // Initialize the E2EE worker. In order to avoid CORS issues, start the worker and have it\n        // synchronously load the JS.\n        const workerUrl = `${baseUrl}lib-jitsi-meet.e2ee-worker.js`;\n        const workerBlob\n            = new Blob([ `importScripts(\"${workerUrl}\");` ], { type: 'application/javascript' });\n        const blobUrl = window.URL.createObjectURL(workerBlob);\n\n        this._worker = new Worker(blobUrl, { name: 'E2EE Worker' });\n        this._worker.onerror = e => logger.onerror(e);\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     * 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        let receiverStreams;\n\n        if (receiver.createEncodedStreams) {\n            receiverStreams = receiver.createEncodedStreams();\n        } else {\n            receiverStreams = kind === 'video' ? receiver.createEncodedVideoStreams()\n                : receiver.createEncodedAudioStreams();\n        }\n\n        this._worker.postMessage({\n            operation: 'decode',\n            readableStream: receiverStreams.readable || receiverStreams.readableStream,\n            writableStream: receiverStreams.writable || receiverStreams.writableStream,\n            participantId\n        }, [ receiverStreams.readable || receiverStreams.readableStream,\n            receiverStreams.writable || receiverStreams.writableStream ]);\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        let senderStreams;\n\n        if (sender.createEncodedStreams) {\n            senderStreams = sender.createEncodedStreams();\n        } else {\n            senderStreams = kind === 'video' ? sender.createEncodedVideoStreams()\n                : sender.createEncodedAudioStreams();\n        }\n\n        this._worker.postMessage({\n            operation: 'encode',\n            readableStream: senderStreams.readable || senderStreams.readableStream,\n            writableStream: senderStreams.writable || senderStreams.writableStream,\n            participantId\n        }, [ senderStreams.readable || senderStreams.readableStream,\n            senderStreams.writable || senderStreams.writableStream ]);\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            participantId,\n            key,\n            keyIndex\n        });\n    }\n\n    /**\n     * Set the E2EE signature key for the specified participant.\n     * @param {string} participantId - the ID of the participant who's key we are setting.\n     * @param {CryptoKey} key - the webcrypto key to set.\n     */\n    setSignatureKey(participantId, key) {\n        this._worker.postMessage({\n            operation: 'setSignatureKey',\n            participantId,\n            key\n        });\n    }\n}\n","import { getLogger } from 'jitsi-meet-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    }\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            // 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 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 { getLogger } from 'jitsi-meet-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","/* global $ */\n\nimport { getLogger } from 'jitsi-meet-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    }\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.rooms[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     *\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","/* global $, __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport isEqual from 'lodash.isequal';\nimport { $iq, $msg, $pres, Strophe } from 'strophe.js';\n\nimport * as JitsiTranscriptionStatus from '../../JitsiTranscriptionStatus';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport XMPPEvents from '../../service/xmpp/XMPPEvents';\nimport GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport Listenable from '../util/Listenable';\n\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 */\nfunction 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     */\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        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.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.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        // We need to broadcast 'videomuted' status from the beginning, cause\n        // Jicofo makes decisions based on that. Initialize it with 'false'\n        // here.\n        this.addVideoInfoToPresence(false);\n\n        if (options.deploymentInfo && options.deploymentInfo.userRegion) {\n            this.presMap.nodes.push({\n                'tagName': 'region',\n                'attributes': {\n                    id: options.deploymentInfo.userRegion,\n                    xmlns: 'http://jitsi.org/jitsi-meet'\n                }\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) {\n        this.password = password;\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            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            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.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                for (const tag of [ 'id', 'name', 'avatar' ]) {\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 (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\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(\n                        XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, properties);\n\n                    this.restartByTerminateSupported = properties['support-terminate-restart'] === 'true';\n                    logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);\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     * @param nickname\n     */\n    sendMessage(message, elementName, nickname) {\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).up();\n        } else {\n            msg.c(elementName, { xmlns: 'http://jitsi.org/jitmeet' }, message)\n                .up();\n        }\n        if (nickname) {\n            msg.c('nick', { xmlns: 'http://jabber.org/protocol/nick' })\n                .t(nickname)\n                .up()\n                .up();\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     * @param nickname\n     */\n    sendPrivateMessage(id, message, elementName, nickname) {\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        if (nickname) {\n            msg.c('nick', { xmlns: 'http://jabber.org/protocol/nick' })\n                .t(nickname)\n                .up()\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\n        if (isKick) {\n            const actorSelect\n                = $(pres)\n                .find('>x[xmlns=\"http://jabber.org/protocol/muc#user\"]>item>actor');\n\n            let actorNick;\n\n            if (actorSelect.length) {\n                actorNick = actorSelect.attr('nick');\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        }\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 nick\n            = $(msg).find('>nick[xmlns=\"http://jabber.org/protocol/nick\"]')\n                .text()\n            || Strophe.getResourceFromJid(from);\n\n        const type = msg.getAttribute('type');\n\n        if (type === 'error') {\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, nick, txt, this.myroomjid, stamp);\n            } else if (type === 'groupchat') {\n                this.eventEmitter.emit(XMPPEvents.MESSAGE_RECEIVED,\n                        from, nick, 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        } 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('>lobbyroom');\n            let lobbyRoomJid;\n\n            if (lobbyRoomNode.length) {\n                lobbyRoomJid = lobbyRoomNode.text();\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     */\n    kick(jid) {\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('You have been kicked.').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(formsubmit, onSuccess, 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     * @param key\n     * @param values\n     */\n    addToPresence(key, values) {\n        values.tagName = key;\n        this.removeFromPresence(key);\n        this.presMap.nodes.push(values);\n        this.presenceUpdateTime = Date.now();\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     * @param callback\n     */\n    setVideoMute(mute, callback) {\n        this.sendVideoInfoPresence(mute);\n        if (callback) {\n            callback(mute);\n        }\n    }\n\n    /**\n     *\n     * @param mute\n     * @param callback\n     */\n    setAudioMute(mute, callback) {\n        return this.sendAudioInfoPresence(mute, callback);\n    }\n\n    /**\n     *\n     * @param mute\n     */\n    addAudioInfoToPresence(mute) {\n        this.addToPresence(\n            'audiomuted',\n            {\n                attributes: { 'xmlns': 'http://jitsi.org/jitmeet/audio' },\n                value: mute.toString()\n            });\n    }\n\n    /**\n     *\n     * @param mute\n     * @param callback\n     */\n    sendAudioInfoPresence(mute, callback) {\n        this.addAudioInfoToPresence(mute);\n\n        // FIXME resend presence on CONNECTED\n        this.sendPresence();\n        if (callback) {\n            callback();\n        }\n    }\n\n    /**\n     *\n     * @param mute\n     */\n    addVideoInfoToPresence(mute) {\n        this.addToPresence(\n            'videomuted',\n            {\n                attributes: { 'xmlns': 'http://jitsi.org/jitmeet/video' },\n                value: mute.toString()\n            });\n    }\n\n    /**\n     *\n     * @param mute\n     */\n    sendVideoInfoPresence(mute) {\n        this.addVideoInfoToPresence(mute);\n        this.sendPresence();\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: false, // unmuted by default\n            videoType: undefined // no video type 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        data.muted = mutedNode.length > 0 && mutedNode[0].value === 'true';\n\n        return data;\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 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     */\n    muteParticipant(jid, mute) {\n        logger.info('set mute', mute);\n        const iqToFocus = $iq(\n            { to: this.focusMucJid,\n                type: 'set' })\n            .c('mute', {\n                xmlns: 'http://jitsi.org/jitmeet/audio',\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     * 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    }\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        return new Promise((resolve, reject) => {\n            const timeout = setTimeout(() => onMucLeft(true), 5000);\n            const eventEmitter = this.eventEmitter;\n\n            this.clean();\n\n            /**\n             *\n             * @param doReject\n             */\n            function onMucLeft(doReject = false) {\n                eventEmitter.removeListener(XMPPEvents.MUC_LEFT, onMucLeft);\n                clearTimeout(timeout);\n                if (doReject) {\n                    // the timeout expired\n                    reject(new Error('The timeout for the confirmation about '\n                        + 'leaving the room expired.'));\n                } else {\n                    resolve();\n                }\n            }\n            eventEmitter.on(XMPPEvents.MUC_LEFT, onMucLeft);\n            this.doLeave();\n        });\n    }\n}\n\n/* eslint-enable newline-per-chained-call */\n","import { getLogger } from 'jitsi-meet-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     * Leaves the lobby room.\n     * @private\n     */\n    _leaveLobbyRoom() {\n        if (this.lobbyRoom) {\n            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\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.removeFromPresence('nick');\n            this.lobbyRoom.addToPresence('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                    // 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 let's join and leave lobby\n                        this.mainRoom.join(invitePassword);\n\n                        this._leaveLobbyRoom();\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._leaveLobbyRoom();\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.removeFromPresence(EMAIL_COMMAND);\n                    this.lobbyRoom.addToPresence(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        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: this.mainRoom.roomjid })\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","/* global $, Promise */\n\nimport { getLogger } from 'jitsi-meet-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    logger.info(`Someone left is it focus ? ${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 (config.opusMaxAverageBitrate) {\n        elem.c(\n            'property', {\n                name: 'opusMaxAverageBitrate',\n                value: config.opusMaxAverageBitrate\n            }).up();\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    if (this.options.conference.stereo !== undefined) {\n        elem.c(\n            'property', {\n                name: 'stereo',\n                value: this.options.conference.stereo\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","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","/* global $, __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport { $iq, Strophe } from 'strophe.js';\n\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 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 *\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        logger.log(`on jingle ${action} from ${fromJid}`, iq);\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', 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        switch (action) {\n        case 'session-initiate': {\n            logger.log('(TIME) received session-initiate:\\t', now);\n            const startMuted = $(iq).find('jingle>startmuted');\n\n            if (startMuted && startMuted.length > 0) {\n                const audioMuted = startMuted.attr('audio');\n                const videoMuted = startMuted.attr('video');\n\n                this.eventEmitter.emit(\n                    XMPPEvents.START_MUTED_FROM_FOCUS,\n                    audioMuted === 'true',\n                    videoMuted === 'true');\n            }\n\n            logger.info(\n                `Marking session from ${fromJid\n                } as ${isP2P ? '' : '*not*'} P2P`);\n\n            const iceConfig = 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(iceConfig)),\n                    isP2P,\n                    /* initiator */ false);\n\n            this.sessions[sess.sid] = sess;\n\n            this.eventEmitter.emit(XMPPEvents.CALL_INCOMING,\n                sess, $(iq).find('>jingle'), now);\n            break;\n        }\n        case 'session-accept': {\n            this.eventEmitter.emit(\n                XMPPEvents.CALL_ACCEPTED, sess, $(iq).find('>jingle'));\n            break;\n        }\n        case 'content-modify': {\n            sess.modifyContents($(iq).find('>jingle'));\n            break;\n        }\n        case 'transport-info': {\n            this.eventEmitter.emit(\n                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            this.terminate(sess.sid, reasonCondition, reasonText);\n            this.eventEmitter.emit(XMPPEvents.CALL_ENDED,\n                sess, reasonCondition, reasonText);\n            break;\n        }\n        case 'transport-replace':\n            logger.info('(TIME) Start transport replace:\\t', now);\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        case 'addsource': // FIXME: proprietary, un-jingleish\n        case 'source-add': // FIXME: proprietary\n            sess.addRemoteStream($(iq).find('>jingle>content'));\n            break;\n        case 'removesource': // FIXME: proprietary, un-jingleish\n        case 'source-remove': // FIXME: proprietary\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            v2Err => {\n                logger.warn('getting turn credentials with extdisco:2 failed, trying extdisco:1', v2Err);\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                    v1Err => {\n                        logger.warn('getting turn credentials failed', v1Err);\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     */\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\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 async from 'async';\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        task(finishedCallback);\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","/*!\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","/**\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","/* global __filename */\nimport { getLogger } from 'jitsi-meet-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\n     * the PeerConnection onCreateAnswer/Offer as defined by the WebRTC.\n     * @param {Object} iceConfig the ICE servers config object as defined by\n     * the WebRTC. Passed to the PeerConnection's constructor.\n     * @param {boolean} isInitiator indicates if it will be the side which\n     * initiates the session.\n     */\n    constructor(\n            sid,\n            localJid,\n            remoteJid,\n            connection,\n            mediaConstraints,\n            iceConfig,\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.iceConfig = iceConfig;\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         * 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 {object} options - the options, see implementing class's\n     * {@link #doInitialize} description for more details.\n     */\n    initialize(room, rtc, 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        this.room = room;\n        this.rtc = rtc;\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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport SignalingLayer from '../../service/RTC/SignalingLayer';\n\nconst logger = getLogger(__filename);\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    /**\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        }\n        if (room) {\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     * @inheritDoc\n     */\n    getPeerMediaInfo(owner, mediaType) {\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    /**\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        this.ssrcOwners.set(ssrc, endpointId);\n    }\n}\n","\nimport Listenable from '../../modules/util/Listenable';\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    getPeerMediaInfo(owner, mediaType) { // eslint-disable-line no-unused-vars\n        throw new Error('not implemented');\n    }\n}\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-meet-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","/* global __filename */\n/**\n * Strophe logger implementation. Logs from level WARN and above.\n */\nimport { getLogger } from 'jitsi-meet-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","\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport CodecMimeType from '../../service/RTC/CodecMimeType';\nimport * as 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        // Do not prefer VP9 on Firefox because of the following bug.\n        // https://bugzilla.mozilla.org/show_bug.cgi?id=1633876\n        if (browser.isFirefox() && this.jvbPreferredCodec === CodecMimeType.VP9) {\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._onMediaSessionStared(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('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    _onMediaSessionStared(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 codec = preferredCodec ? preferredCodec : this.jvbPreferredCodec;\n        let selectedCodec = codec;\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\n                if (peerMediaInfo && peerMediaInfo.codecType && peerMediaInfo.codecType !== codec) {\n                    selectedCodec = peerMediaInfo.codecType;\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-meet-logger';\n\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport { createBridgeChannelClosedEvent } from '../../service/statistics/AnalyticsEvents';\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     * @param {function} senderVideoConstraintsChanged callback to call when the sender video constraints change.\n     */\n    constructor(peerconnection, wsUrl, emitter, senderVideoConstraintsChanged) {\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        this._senderVideoConstraintsChanged = senderVideoConstraintsChanged;\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 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     * 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                // Endpoint ID from the Videobridge.\n                const dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint;\n\n                logger.info(`New dominant speaker: ${dominantSpeakerEndpoint}.`);\n                emitter.emit(RTCEvents.DOMINANT_SPEAKER_CHANGED, dominantSpeakerEndpoint);\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 'LastNEndpointsChangeEvent': {\n                // The new/latest list of last-n endpoint IDs (i.e. endpoints for which the bridge is sending video).\n                const lastNEndpoints = obj.lastNEndpoints;\n\n                logger.info(`New forwarded endpoints: ${lastNEndpoints}`);\n                emitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED, lastNEndpoints);\n\n                break;\n            }\n            case 'SenderVideoConstraints': {\n                const videoConstraints = obj.videoConstraints;\n\n                if (videoConstraints) {\n                    logger.info(`SenderVideoConstraints: ${JSON.stringify(videoConstraints)}`);\n                    this._senderVideoConstraintsChanged(videoConstraints);\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","/*\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\nvar SDPUtils = require('sdp');\n\nfunction fixStatsType(stat) {\n  return {\n    inboundrtp: 'inbound-rtp',\n    outboundrtp: 'outbound-rtp',\n    candidatepair: 'candidate-pair',\n    localcandidate: 'local-candidate',\n    remotecandidate: 'remote-candidate'\n  }[stat.type] || stat.type;\n}\n\nfunction writeMediaSection(transceiver, caps, type, stream, dtlsRole) {\n  var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps);\n\n  // Map ICE parameters (ufrag, pwd) to SDP.\n  sdp += SDPUtils.writeIceParameters(\n      transceiver.iceGatherer.getLocalParameters());\n\n  // Map DTLS parameters to SDP.\n  sdp += SDPUtils.writeDtlsParameters(\n      transceiver.dtlsTransport.getLocalParameters(),\n      type === 'offer' ? 'actpass' : dtlsRole || 'active');\n\n  sdp += 'a=mid:' + transceiver.mid + '\\r\\n';\n\n  if (transceiver.rtpSender && transceiver.rtpReceiver) {\n    sdp += 'a=sendrecv\\r\\n';\n  } else if (transceiver.rtpSender) {\n    sdp += 'a=sendonly\\r\\n';\n  } else if (transceiver.rtpReceiver) {\n    sdp += 'a=recvonly\\r\\n';\n  } else {\n    sdp += 'a=inactive\\r\\n';\n  }\n\n  if (transceiver.rtpSender) {\n    var trackId = transceiver.rtpSender._initialTrackId ||\n        transceiver.rtpSender.track.id;\n    transceiver.rtpSender._initialTrackId = trackId;\n    // spec.\n    var msid = 'msid:' + (stream ? stream.id : '-') + ' ' +\n        trackId + '\\r\\n';\n    sdp += 'a=' + msid;\n    // for Chrome. Legacy should no longer be required.\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n        ' ' + msid;\n\n    // RTX\n    if (transceiver.sendEncodingParameters[0].rtx) {\n      sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n          ' ' + msid;\n      sdp += 'a=ssrc-group:FID ' +\n          transceiver.sendEncodingParameters[0].ssrc + ' ' +\n          transceiver.sendEncodingParameters[0].rtx.ssrc +\n          '\\r\\n';\n    }\n  }\n  // FIXME: this should be written by writeRtpDescription.\n  sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc +\n      ' cname:' + SDPUtils.localCName + '\\r\\n';\n  if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) {\n    sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc +\n        ' cname:' + SDPUtils.localCName + '\\r\\n';\n  }\n  return sdp;\n}\n\n// Edge does not like\n// 1) stun: filtered after 14393 unless ?transport=udp is present\n// 2) turn: that does not have all of turn:host:port?transport=udp\n// 3) turn: with ipv6 addresses\n// 4) turn: occurring muliple times\nfunction filterIceServers(iceServers, edgeVersion) {\n  var hasTurn = false;\n  iceServers = JSON.parse(JSON.stringify(iceServers));\n  return iceServers.filter(function(server) {\n    if (server && (server.urls || server.url)) {\n      var urls = server.urls || server.url;\n      if (server.url && !server.urls) {\n        console.warn('RTCIceServer.url is deprecated! Use urls instead.');\n      }\n      var isString = typeof urls === 'string';\n      if (isString) {\n        urls = [urls];\n      }\n      urls = urls.filter(function(url) {\n        var validTurn = url.indexOf('turn:') === 0 &&\n            url.indexOf('transport=udp') !== -1 &&\n            url.indexOf('turn:[') === -1 &&\n            !hasTurn;\n\n        if (validTurn) {\n          hasTurn = true;\n          return true;\n        }\n        return url.indexOf('stun:') === 0 && edgeVersion >= 14393 &&\n            url.indexOf('?transport=udp') === -1;\n      });\n\n      delete server.url;\n      server.urls = isString ? urls[0] : urls;\n      return !!urls.length;\n    }\n  });\n}\n\n// Determines the intersection of local and remote capabilities.\nfunction getCommonCapabilities(localCapabilities, remoteCapabilities) {\n  var commonCapabilities = {\n    codecs: [],\n    headerExtensions: [],\n    fecMechanisms: []\n  };\n\n  var findCodecByPayloadType = function(pt, codecs) {\n    pt = parseInt(pt, 10);\n    for (var i = 0; i < codecs.length; i++) {\n      if (codecs[i].payloadType === pt ||\n          codecs[i].preferredPayloadType === pt) {\n        return codecs[i];\n      }\n    }\n  };\n\n  var rtxCapabilityMatches = function(lRtx, rRtx, lCodecs, rCodecs) {\n    var lCodec = findCodecByPayloadType(lRtx.parameters.apt, lCodecs);\n    var rCodec = findCodecByPayloadType(rRtx.parameters.apt, rCodecs);\n    return lCodec && rCodec &&\n        lCodec.name.toLowerCase() === rCodec.name.toLowerCase();\n  };\n\n  localCapabilities.codecs.forEach(function(lCodec) {\n    for (var i = 0; i < remoteCapabilities.codecs.length; i++) {\n      var rCodec = remoteCapabilities.codecs[i];\n      if (lCodec.name.toLowerCase() === rCodec.name.toLowerCase() &&\n          lCodec.clockRate === rCodec.clockRate) {\n        if (lCodec.name.toLowerCase() === 'rtx' &&\n            lCodec.parameters && rCodec.parameters.apt) {\n          // for RTX we need to find the local rtx that has a apt\n          // which points to the same local codec as the remote one.\n          if (!rtxCapabilityMatches(lCodec, rCodec,\n              localCapabilities.codecs, remoteCapabilities.codecs)) {\n            continue;\n          }\n        }\n        rCodec = JSON.parse(JSON.stringify(rCodec)); // deepcopy\n        // number of channels is the highest common number of channels\n        rCodec.numChannels = Math.min(lCodec.numChannels,\n            rCodec.numChannels);\n        // push rCodec so we reply with offerer payload type\n        commonCapabilities.codecs.push(rCodec);\n\n        // determine common feedback mechanisms\n        rCodec.rtcpFeedback = rCodec.rtcpFeedback.filter(function(fb) {\n          for (var j = 0; j < lCodec.rtcpFeedback.length; j++) {\n            if (lCodec.rtcpFeedback[j].type === fb.type &&\n                lCodec.rtcpFeedback[j].parameter === fb.parameter) {\n              return true;\n            }\n          }\n          return false;\n        });\n        // FIXME: also need to determine .parameters\n        //  see https://github.com/openpeer/ortc/issues/569\n        break;\n      }\n    }\n  });\n\n  localCapabilities.headerExtensions.forEach(function(lHeaderExtension) {\n    for (var i = 0; i < remoteCapabilities.headerExtensions.length;\n         i++) {\n      var rHeaderExtension = remoteCapabilities.headerExtensions[i];\n      if (lHeaderExtension.uri === rHeaderExtension.uri) {\n        commonCapabilities.headerExtensions.push(rHeaderExtension);\n        break;\n      }\n    }\n  });\n\n  // FIXME: fecMechanisms\n  return commonCapabilities;\n}\n\n// is action=setLocalDescription with type allowed in signalingState\nfunction isActionAllowedInSignalingState(action, type, signalingState) {\n  return {\n    offer: {\n      setLocalDescription: ['stable', 'have-local-offer'],\n      setRemoteDescription: ['stable', 'have-remote-offer']\n    },\n    answer: {\n      setLocalDescription: ['have-remote-offer', 'have-local-pranswer'],\n      setRemoteDescription: ['have-local-offer', 'have-remote-pranswer']\n    }\n  }[type][action].indexOf(signalingState) !== -1;\n}\n\nfunction maybeAddCandidate(iceTransport, candidate) {\n  // Edge's internal representation adds some fields therefore\n  // not all fieldѕ are taken into account.\n  var alreadyAdded = iceTransport.getRemoteCandidates()\n      .find(function(remoteCandidate) {\n        return candidate.foundation === remoteCandidate.foundation &&\n            candidate.ip === remoteCandidate.ip &&\n            candidate.port === remoteCandidate.port &&\n            candidate.priority === remoteCandidate.priority &&\n            candidate.protocol === remoteCandidate.protocol &&\n            candidate.type === remoteCandidate.type;\n      });\n  if (!alreadyAdded) {\n    iceTransport.addRemoteCandidate(candidate);\n  }\n  return !alreadyAdded;\n}\n\n\nfunction makeError(name, description) {\n  var e = new Error(description);\n  e.name = name;\n  // legacy error codes from https://heycam.github.io/webidl/#idl-DOMException-error-names\n  e.code = {\n    NotSupportedError: 9,\n    InvalidStateError: 11,\n    InvalidAccessError: 15,\n    TypeError: undefined,\n    OperationError: undefined\n  }[name];\n  return e;\n}\n\nmodule.exports = function(window, edgeVersion) {\n  // https://w3c.github.io/mediacapture-main/#mediastream\n  // Helper function to add the track to the stream and\n  // dispatch the event ourselves.\n  function addTrackToStreamAndFireEvent(track, stream) {\n    stream.addTrack(track);\n    stream.dispatchEvent(new window.MediaStreamTrackEvent('addtrack',\n        {track: track}));\n  }\n\n  function removeTrackFromStreamAndFireEvent(track, stream) {\n    stream.removeTrack(track);\n    stream.dispatchEvent(new window.MediaStreamTrackEvent('removetrack',\n        {track: track}));\n  }\n\n  function fireAddTrack(pc, track, receiver, streams) {\n    var trackEvent = new Event('track');\n    trackEvent.track = track;\n    trackEvent.receiver = receiver;\n    trackEvent.transceiver = {receiver: receiver};\n    trackEvent.streams = streams;\n    window.setTimeout(function() {\n      pc._dispatchEvent('track', trackEvent);\n    });\n  }\n\n  var RTCPeerConnection = function(config) {\n    var pc = this;\n\n    var _eventTarget = document.createDocumentFragment();\n    ['addEventListener', 'removeEventListener', 'dispatchEvent']\n        .forEach(function(method) {\n          pc[method] = _eventTarget[method].bind(_eventTarget);\n        });\n\n    this.canTrickleIceCandidates = null;\n\n    this.needNegotiation = false;\n\n    this.localStreams = [];\n    this.remoteStreams = [];\n\n    this._localDescription = null;\n    this._remoteDescription = null;\n\n    this.signalingState = 'stable';\n    this.iceConnectionState = 'new';\n    this.connectionState = 'new';\n    this.iceGatheringState = 'new';\n\n    config = JSON.parse(JSON.stringify(config || {}));\n\n    this.usingBundle = config.bundlePolicy === 'max-bundle';\n    if (config.rtcpMuxPolicy === 'negotiate') {\n      throw(makeError('NotSupportedError',\n          'rtcpMuxPolicy \\'negotiate\\' is not supported'));\n    } else if (!config.rtcpMuxPolicy) {\n      config.rtcpMuxPolicy = 'require';\n    }\n\n    switch (config.iceTransportPolicy) {\n      case 'all':\n      case 'relay':\n        break;\n      default:\n        config.iceTransportPolicy = 'all';\n        break;\n    }\n\n    switch (config.bundlePolicy) {\n      case 'balanced':\n      case 'max-compat':\n      case 'max-bundle':\n        break;\n      default:\n        config.bundlePolicy = 'balanced';\n        break;\n    }\n\n    config.iceServers = filterIceServers(config.iceServers || [], edgeVersion);\n\n    this._iceGatherers = [];\n    if (config.iceCandidatePoolSize) {\n      for (var i = config.iceCandidatePoolSize; i > 0; i--) {\n        this._iceGatherers.push(new window.RTCIceGatherer({\n          iceServers: config.iceServers,\n          gatherPolicy: config.iceTransportPolicy\n        }));\n      }\n    } else {\n      config.iceCandidatePoolSize = 0;\n    }\n\n    this._config = config;\n\n    // per-track iceGathers, iceTransports, dtlsTransports, rtpSenders, ...\n    // everything that is needed to describe a SDP m-line.\n    this.transceivers = [];\n\n    this._sdpSessionId = SDPUtils.generateSessionId();\n    this._sdpSessionVersion = 0;\n\n    this._dtlsRole = undefined; // role for a=setup to use in answers.\n\n    this._isClosed = false;\n  };\n\n  Object.defineProperty(RTCPeerConnection.prototype, 'localDescription', {\n    configurable: true,\n    get: function() {\n      return this._localDescription;\n    }\n  });\n  Object.defineProperty(RTCPeerConnection.prototype, 'remoteDescription', {\n    configurable: true,\n    get: function() {\n      return this._remoteDescription;\n    }\n  });\n\n  // set up event handlers on prototype\n  RTCPeerConnection.prototype.onicecandidate = null;\n  RTCPeerConnection.prototype.onaddstream = null;\n  RTCPeerConnection.prototype.ontrack = null;\n  RTCPeerConnection.prototype.onremovestream = null;\n  RTCPeerConnection.prototype.onsignalingstatechange = null;\n  RTCPeerConnection.prototype.oniceconnectionstatechange = null;\n  RTCPeerConnection.prototype.onconnectionstatechange = null;\n  RTCPeerConnection.prototype.onicegatheringstatechange = null;\n  RTCPeerConnection.prototype.onnegotiationneeded = null;\n  RTCPeerConnection.prototype.ondatachannel = null;\n\n  RTCPeerConnection.prototype._dispatchEvent = function(name, event) {\n    if (this._isClosed) {\n      return;\n    }\n    this.dispatchEvent(event);\n    if (typeof this['on' + name] === 'function') {\n      this['on' + name](event);\n    }\n  };\n\n  RTCPeerConnection.prototype._emitGatheringStateChange = function() {\n    var event = new Event('icegatheringstatechange');\n    this._dispatchEvent('icegatheringstatechange', event);\n  };\n\n  RTCPeerConnection.prototype.getConfiguration = function() {\n    return this._config;\n  };\n\n  RTCPeerConnection.prototype.getLocalStreams = function() {\n    return this.localStreams;\n  };\n\n  RTCPeerConnection.prototype.getRemoteStreams = function() {\n    return this.remoteStreams;\n  };\n\n  // internal helper to create a transceiver object.\n  // (which is not yet the same as the WebRTC 1.0 transceiver)\n  RTCPeerConnection.prototype._createTransceiver = function(kind, doNotAdd) {\n    var hasBundleTransport = this.transceivers.length > 0;\n    var transceiver = {\n      track: null,\n      iceGatherer: null,\n      iceTransport: null,\n      dtlsTransport: null,\n      localCapabilities: null,\n      remoteCapabilities: null,\n      rtpSender: null,\n      rtpReceiver: null,\n      kind: kind,\n      mid: null,\n      sendEncodingParameters: null,\n      recvEncodingParameters: null,\n      stream: null,\n      associatedRemoteMediaStreams: [],\n      wantReceive: true\n    };\n    if (this.usingBundle && hasBundleTransport) {\n      transceiver.iceTransport = this.transceivers[0].iceTransport;\n      transceiver.dtlsTransport = this.transceivers[0].dtlsTransport;\n    } else {\n      var transports = this._createIceAndDtlsTransports();\n      transceiver.iceTransport = transports.iceTransport;\n      transceiver.dtlsTransport = transports.dtlsTransport;\n    }\n    if (!doNotAdd) {\n      this.transceivers.push(transceiver);\n    }\n    return transceiver;\n  };\n\n  RTCPeerConnection.prototype.addTrack = function(track, stream) {\n    if (this._isClosed) {\n      throw makeError('InvalidStateError',\n          'Attempted to call addTrack on a closed peerconnection.');\n    }\n\n    var alreadyExists = this.transceivers.find(function(s) {\n      return s.track === track;\n    });\n\n    if (alreadyExists) {\n      throw makeError('InvalidAccessError', 'Track already exists.');\n    }\n\n    var transceiver;\n    for (var i = 0; i < this.transceivers.length; i++) {\n      if (!this.transceivers[i].track &&\n          this.transceivers[i].kind === track.kind) {\n        transceiver = this.transceivers[i];\n      }\n    }\n    if (!transceiver) {\n      transceiver = this._createTransceiver(track.kind);\n    }\n\n    this._maybeFireNegotiationNeeded();\n\n    if (this.localStreams.indexOf(stream) === -1) {\n      this.localStreams.push(stream);\n    }\n\n    transceiver.track = track;\n    transceiver.stream = stream;\n    transceiver.rtpSender = new window.RTCRtpSender(track,\n        transceiver.dtlsTransport);\n    return transceiver.rtpSender;\n  };\n\n  RTCPeerConnection.prototype.addStream = function(stream) {\n    var pc = this;\n    if (edgeVersion >= 15025) {\n      stream.getTracks().forEach(function(track) {\n        pc.addTrack(track, stream);\n      });\n    } else {\n      // Clone is necessary for local demos mostly, attaching directly\n      // to two different senders does not work (build 10547).\n      // Fixed in 15025 (or earlier)\n      var clonedStream = stream.clone();\n      stream.getTracks().forEach(function(track, idx) {\n        var clonedTrack = clonedStream.getTracks()[idx];\n        track.addEventListener('enabled', function(event) {\n          clonedTrack.enabled = event.enabled;\n        });\n      });\n      clonedStream.getTracks().forEach(function(track) {\n        pc.addTrack(track, clonedStream);\n      });\n    }\n  };\n\n  RTCPeerConnection.prototype.removeTrack = function(sender) {\n    if (this._isClosed) {\n      throw makeError('InvalidStateError',\n          'Attempted to call removeTrack on a closed peerconnection.');\n    }\n\n    if (!(sender instanceof window.RTCRtpSender)) {\n      throw new TypeError('Argument 1 of RTCPeerConnection.removeTrack ' +\n          'does not implement interface RTCRtpSender.');\n    }\n\n    var transceiver = this.transceivers.find(function(t) {\n      return t.rtpSender === sender;\n    });\n\n    if (!transceiver) {\n      throw makeError('InvalidAccessError',\n          'Sender was not created by this connection.');\n    }\n    var stream = transceiver.stream;\n\n    transceiver.rtpSender.stop();\n    transceiver.rtpSender = null;\n    transceiver.track = null;\n    transceiver.stream = null;\n\n    // remove the stream from the set of local streams\n    var localStreams = this.transceivers.map(function(t) {\n      return t.stream;\n    });\n    if (localStreams.indexOf(stream) === -1 &&\n        this.localStreams.indexOf(stream) > -1) {\n      this.localStreams.splice(this.localStreams.indexOf(stream), 1);\n    }\n\n    this._maybeFireNegotiationNeeded();\n  };\n\n  RTCPeerConnection.prototype.removeStream = function(stream) {\n    var pc = this;\n    stream.getTracks().forEach(function(track) {\n      var sender = pc.getSenders().find(function(s) {\n        return s.track === track;\n      });\n      if (sender) {\n        pc.removeTrack(sender);\n      }\n    });\n  };\n\n  RTCPeerConnection.prototype.getSenders = function() {\n    return this.transceivers.filter(function(transceiver) {\n      return !!transceiver.rtpSender;\n    })\n    .map(function(transceiver) {\n      return transceiver.rtpSender;\n    });\n  };\n\n  RTCPeerConnection.prototype.getReceivers = function() {\n    return this.transceivers.filter(function(transceiver) {\n      return !!transceiver.rtpReceiver;\n    })\n    .map(function(transceiver) {\n      return transceiver.rtpReceiver;\n    });\n  };\n\n\n  RTCPeerConnection.prototype._createIceGatherer = function(sdpMLineIndex,\n      usingBundle) {\n    var pc = this;\n    if (usingBundle && sdpMLineIndex > 0) {\n      return this.transceivers[0].iceGatherer;\n    } else if (this._iceGatherers.length) {\n      return this._iceGatherers.shift();\n    }\n    var iceGatherer = new window.RTCIceGatherer({\n      iceServers: this._config.iceServers,\n      gatherPolicy: this._config.iceTransportPolicy\n    });\n    Object.defineProperty(iceGatherer, 'state',\n        {value: 'new', writable: true}\n    );\n\n    this.transceivers[sdpMLineIndex].bufferedCandidateEvents = [];\n    this.transceivers[sdpMLineIndex].bufferCandidates = function(event) {\n      var end = !event.candidate || Object.keys(event.candidate).length === 0;\n      // polyfill since RTCIceGatherer.state is not implemented in\n      // Edge 10547 yet.\n      iceGatherer.state = end ? 'completed' : 'gathering';\n      if (pc.transceivers[sdpMLineIndex].bufferedCandidateEvents !== null) {\n        pc.transceivers[sdpMLineIndex].bufferedCandidateEvents.push(event);\n      }\n    };\n    iceGatherer.addEventListener('localcandidate',\n      this.transceivers[sdpMLineIndex].bufferCandidates);\n    return iceGatherer;\n  };\n\n  // start gathering from an RTCIceGatherer.\n  RTCPeerConnection.prototype._gather = function(mid, sdpMLineIndex) {\n    var pc = this;\n    var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;\n    if (iceGatherer.onlocalcandidate) {\n      return;\n    }\n    var bufferedCandidateEvents =\n      this.transceivers[sdpMLineIndex].bufferedCandidateEvents;\n    this.transceivers[sdpMLineIndex].bufferedCandidateEvents = null;\n    iceGatherer.removeEventListener('localcandidate',\n      this.transceivers[sdpMLineIndex].bufferCandidates);\n    iceGatherer.onlocalcandidate = function(evt) {\n      if (pc.usingBundle && sdpMLineIndex > 0) {\n        // if we know that we use bundle we can drop candidates with\n        // ѕdpMLineIndex > 0. If we don't do this then our state gets\n        // confused since we dispose the extra ice gatherer.\n        return;\n      }\n      var event = new Event('icecandidate');\n      event.candidate = {sdpMid: mid, sdpMLineIndex: sdpMLineIndex};\n\n      var cand = evt.candidate;\n      // Edge emits an empty object for RTCIceCandidateComplete‥\n      var end = !cand || Object.keys(cand).length === 0;\n      if (end) {\n        // polyfill since RTCIceGatherer.state is not implemented in\n        // Edge 10547 yet.\n        if (iceGatherer.state === 'new' || iceGatherer.state === 'gathering') {\n          iceGatherer.state = 'completed';\n        }\n      } else {\n        if (iceGatherer.state === 'new') {\n          iceGatherer.state = 'gathering';\n        }\n        // RTCIceCandidate doesn't have a component, needs to be added\n        cand.component = 1;\n        // also the usernameFragment. TODO: update SDP to take both variants.\n        cand.ufrag = iceGatherer.getLocalParameters().usernameFragment;\n\n        var serializedCandidate = SDPUtils.writeCandidate(cand);\n        event.candidate = Object.assign(event.candidate,\n            SDPUtils.parseCandidate(serializedCandidate));\n\n        event.candidate.candidate = serializedCandidate;\n        event.candidate.toJSON = function() {\n          return {\n            candidate: event.candidate.candidate,\n            sdpMid: event.candidate.sdpMid,\n            sdpMLineIndex: event.candidate.sdpMLineIndex,\n            usernameFragment: event.candidate.usernameFragment\n          };\n        };\n      }\n\n      // update local description.\n      var sections = SDPUtils.getMediaSections(pc._localDescription.sdp);\n      if (!end) {\n        sections[event.candidate.sdpMLineIndex] +=\n            'a=' + event.candidate.candidate + '\\r\\n';\n      } else {\n        sections[event.candidate.sdpMLineIndex] +=\n            'a=end-of-candidates\\r\\n';\n      }\n      pc._localDescription.sdp =\n          SDPUtils.getDescription(pc._localDescription.sdp) +\n          sections.join('');\n      var complete = pc.transceivers.every(function(transceiver) {\n        return transceiver.iceGatherer &&\n            transceiver.iceGatherer.state === 'completed';\n      });\n\n      if (pc.iceGatheringState !== 'gathering') {\n        pc.iceGatheringState = 'gathering';\n        pc._emitGatheringStateChange();\n      }\n\n      // Emit candidate. Also emit null candidate when all gatherers are\n      // complete.\n      if (!end) {\n        pc._dispatchEvent('icecandidate', event);\n      }\n      if (complete) {\n        pc._dispatchEvent('icecandidate', new Event('icecandidate'));\n        pc.iceGatheringState = 'complete';\n        pc._emitGatheringStateChange();\n      }\n    };\n\n    // emit already gathered candidates.\n    window.setTimeout(function() {\n      bufferedCandidateEvents.forEach(function(e) {\n        iceGatherer.onlocalcandidate(e);\n      });\n    }, 0);\n  };\n\n  // Create ICE transport and DTLS transport.\n  RTCPeerConnection.prototype._createIceAndDtlsTransports = function() {\n    var pc = this;\n    var iceTransport = new window.RTCIceTransport(null);\n    iceTransport.onicestatechange = function() {\n      pc._updateIceConnectionState();\n      pc._updateConnectionState();\n    };\n\n    var dtlsTransport = new window.RTCDtlsTransport(iceTransport);\n    dtlsTransport.ondtlsstatechange = function() {\n      pc._updateConnectionState();\n    };\n    dtlsTransport.onerror = function() {\n      // onerror does not set state to failed by itself.\n      Object.defineProperty(dtlsTransport, 'state',\n          {value: 'failed', writable: true});\n      pc._updateConnectionState();\n    };\n\n    return {\n      iceTransport: iceTransport,\n      dtlsTransport: dtlsTransport\n    };\n  };\n\n  // Destroy ICE gatherer, ICE transport and DTLS transport.\n  // Without triggering the callbacks.\n  RTCPeerConnection.prototype._disposeIceAndDtlsTransports = function(\n      sdpMLineIndex) {\n    var iceGatherer = this.transceivers[sdpMLineIndex].iceGatherer;\n    if (iceGatherer) {\n      delete iceGatherer.onlocalcandidate;\n      delete this.transceivers[sdpMLineIndex].iceGatherer;\n    }\n    var iceTransport = this.transceivers[sdpMLineIndex].iceTransport;\n    if (iceTransport) {\n      delete iceTransport.onicestatechange;\n      delete this.transceivers[sdpMLineIndex].iceTransport;\n    }\n    var dtlsTransport = this.transceivers[sdpMLineIndex].dtlsTransport;\n    if (dtlsTransport) {\n      delete dtlsTransport.ondtlsstatechange;\n      delete dtlsTransport.onerror;\n      delete this.transceivers[sdpMLineIndex].dtlsTransport;\n    }\n  };\n\n  // Start the RTP Sender and Receiver for a transceiver.\n  RTCPeerConnection.prototype._transceive = function(transceiver,\n      send, recv) {\n    var params = getCommonCapabilities(transceiver.localCapabilities,\n        transceiver.remoteCapabilities);\n    if (send && transceiver.rtpSender) {\n      params.encodings = transceiver.sendEncodingParameters;\n      params.rtcp = {\n        cname: SDPUtils.localCName,\n        compound: transceiver.rtcpParameters.compound\n      };\n      if (transceiver.recvEncodingParameters.length) {\n        params.rtcp.ssrc = transceiver.recvEncodingParameters[0].ssrc;\n      }\n      transceiver.rtpSender.send(params);\n    }\n    if (recv && transceiver.rtpReceiver && params.codecs.length > 0) {\n      // remove RTX field in Edge 14942\n      if (transceiver.kind === 'video'\n          && transceiver.recvEncodingParameters\n          && edgeVersion < 15019) {\n        transceiver.recvEncodingParameters.forEach(function(p) {\n          delete p.rtx;\n        });\n      }\n      if (transceiver.recvEncodingParameters.length) {\n        params.encodings = transceiver.recvEncodingParameters;\n      } else {\n        params.encodings = [{}];\n      }\n      params.rtcp = {\n        compound: transceiver.rtcpParameters.compound\n      };\n      if (transceiver.rtcpParameters.cname) {\n        params.rtcp.cname = transceiver.rtcpParameters.cname;\n      }\n      if (transceiver.sendEncodingParameters.length) {\n        params.rtcp.ssrc = transceiver.sendEncodingParameters[0].ssrc;\n      }\n      transceiver.rtpReceiver.receive(params);\n    }\n  };\n\n  RTCPeerConnection.prototype.setLocalDescription = function(description) {\n    var pc = this;\n\n    // Note: pranswer is not supported.\n    if (['offer', 'answer'].indexOf(description.type) === -1) {\n      return Promise.reject(makeError('TypeError',\n          'Unsupported type \"' + description.type + '\"'));\n    }\n\n    if (!isActionAllowedInSignalingState('setLocalDescription',\n        description.type, pc.signalingState) || pc._isClosed) {\n      return Promise.reject(makeError('InvalidStateError',\n          'Can not set local ' + description.type +\n          ' in state ' + pc.signalingState));\n    }\n\n    var sections;\n    var sessionpart;\n    if (description.type === 'offer') {\n      // VERY limited support for SDP munging. Limited to:\n      // * changing the order of codecs\n      sections = SDPUtils.splitSections(description.sdp);\n      sessionpart = sections.shift();\n      sections.forEach(function(mediaSection, sdpMLineIndex) {\n        var caps = SDPUtils.parseRtpParameters(mediaSection);\n        pc.transceivers[sdpMLineIndex].localCapabilities = caps;\n      });\n\n      pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n        pc._gather(transceiver.mid, sdpMLineIndex);\n      });\n    } else if (description.type === 'answer') {\n      sections = SDPUtils.splitSections(pc._remoteDescription.sdp);\n      sessionpart = sections.shift();\n      var isIceLite = SDPUtils.matchPrefix(sessionpart,\n          'a=ice-lite').length > 0;\n      sections.forEach(function(mediaSection, sdpMLineIndex) {\n        var transceiver = pc.transceivers[sdpMLineIndex];\n        var iceGatherer = transceiver.iceGatherer;\n        var iceTransport = transceiver.iceTransport;\n        var dtlsTransport = transceiver.dtlsTransport;\n        var localCapabilities = transceiver.localCapabilities;\n        var remoteCapabilities = transceiver.remoteCapabilities;\n\n        // treat bundle-only as not-rejected.\n        var rejected = SDPUtils.isRejected(mediaSection) &&\n            SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;\n\n        if (!rejected && !transceiver.rejected) {\n          var remoteIceParameters = SDPUtils.getIceParameters(\n              mediaSection, sessionpart);\n          var remoteDtlsParameters = SDPUtils.getDtlsParameters(\n              mediaSection, sessionpart);\n          if (isIceLite) {\n            remoteDtlsParameters.role = 'server';\n          }\n\n          if (!pc.usingBundle || sdpMLineIndex === 0) {\n            pc._gather(transceiver.mid, sdpMLineIndex);\n            if (iceTransport.state === 'new') {\n              iceTransport.start(iceGatherer, remoteIceParameters,\n                  isIceLite ? 'controlling' : 'controlled');\n            }\n            if (dtlsTransport.state === 'new') {\n              dtlsTransport.start(remoteDtlsParameters);\n            }\n          }\n\n          // Calculate intersection of capabilities.\n          var params = getCommonCapabilities(localCapabilities,\n              remoteCapabilities);\n\n          // Start the RTCRtpSender. The RTCRtpReceiver for this\n          // transceiver has already been started in setRemoteDescription.\n          pc._transceive(transceiver,\n              params.codecs.length > 0,\n              false);\n        }\n      });\n    }\n\n    pc._localDescription = {\n      type: description.type,\n      sdp: description.sdp\n    };\n    if (description.type === 'offer') {\n      pc._updateSignalingState('have-local-offer');\n    } else {\n      pc._updateSignalingState('stable');\n    }\n\n    return Promise.resolve();\n  };\n\n  RTCPeerConnection.prototype.setRemoteDescription = function(description) {\n    var pc = this;\n\n    // Note: pranswer is not supported.\n    if (['offer', 'answer'].indexOf(description.type) === -1) {\n      return Promise.reject(makeError('TypeError',\n          'Unsupported type \"' + description.type + '\"'));\n    }\n\n    if (!isActionAllowedInSignalingState('setRemoteDescription',\n        description.type, pc.signalingState) || pc._isClosed) {\n      return Promise.reject(makeError('InvalidStateError',\n          'Can not set remote ' + description.type +\n          ' in state ' + pc.signalingState));\n    }\n\n    var streams = {};\n    pc.remoteStreams.forEach(function(stream) {\n      streams[stream.id] = stream;\n    });\n    var receiverList = [];\n    var sections = SDPUtils.splitSections(description.sdp);\n    var sessionpart = sections.shift();\n    var isIceLite = SDPUtils.matchPrefix(sessionpart,\n        'a=ice-lite').length > 0;\n    var usingBundle = SDPUtils.matchPrefix(sessionpart,\n        'a=group:BUNDLE ').length > 0;\n    pc.usingBundle = usingBundle;\n    var iceOptions = SDPUtils.matchPrefix(sessionpart,\n        'a=ice-options:')[0];\n    if (iceOptions) {\n      pc.canTrickleIceCandidates = iceOptions.substr(14).split(' ')\n          .indexOf('trickle') >= 0;\n    } else {\n      pc.canTrickleIceCandidates = false;\n    }\n\n    sections.forEach(function(mediaSection, sdpMLineIndex) {\n      var lines = SDPUtils.splitLines(mediaSection);\n      var kind = SDPUtils.getKind(mediaSection);\n      // treat bundle-only as not-rejected.\n      var rejected = SDPUtils.isRejected(mediaSection) &&\n          SDPUtils.matchPrefix(mediaSection, 'a=bundle-only').length === 0;\n      var protocol = lines[0].substr(2).split(' ')[2];\n\n      var direction = SDPUtils.getDirection(mediaSection, sessionpart);\n      var remoteMsid = SDPUtils.parseMsid(mediaSection);\n\n      var mid = SDPUtils.getMid(mediaSection) || SDPUtils.generateIdentifier();\n\n      // Reject datachannels which are not implemented yet.\n      if (rejected || (kind === 'application' && (protocol === 'DTLS/SCTP' ||\n          protocol === 'UDP/DTLS/SCTP'))) {\n        // TODO: this is dangerous in the case where a non-rejected m-line\n        //     becomes rejected.\n        pc.transceivers[sdpMLineIndex] = {\n          mid: mid,\n          kind: kind,\n          protocol: protocol,\n          rejected: true\n        };\n        return;\n      }\n\n      if (!rejected && pc.transceivers[sdpMLineIndex] &&\n          pc.transceivers[sdpMLineIndex].rejected) {\n        // recycle a rejected transceiver.\n        pc.transceivers[sdpMLineIndex] = pc._createTransceiver(kind, true);\n      }\n\n      var transceiver;\n      var iceGatherer;\n      var iceTransport;\n      var dtlsTransport;\n      var rtpReceiver;\n      var sendEncodingParameters;\n      var recvEncodingParameters;\n      var localCapabilities;\n\n      var track;\n      // FIXME: ensure the mediaSection has rtcp-mux set.\n      var remoteCapabilities = SDPUtils.parseRtpParameters(mediaSection);\n      var remoteIceParameters;\n      var remoteDtlsParameters;\n      if (!rejected) {\n        remoteIceParameters = SDPUtils.getIceParameters(mediaSection,\n            sessionpart);\n        remoteDtlsParameters = SDPUtils.getDtlsParameters(mediaSection,\n            sessionpart);\n        remoteDtlsParameters.role = 'client';\n      }\n      recvEncodingParameters =\n          SDPUtils.parseRtpEncodingParameters(mediaSection);\n\n      var rtcpParameters = SDPUtils.parseRtcpParameters(mediaSection);\n\n      var isComplete = SDPUtils.matchPrefix(mediaSection,\n          'a=end-of-candidates', sessionpart).length > 0;\n      var cands = SDPUtils.matchPrefix(mediaSection, 'a=candidate:')\n          .map(function(cand) {\n            return SDPUtils.parseCandidate(cand);\n          })\n          .filter(function(cand) {\n            return cand.component === 1;\n          });\n\n      // Check if we can use BUNDLE and dispose transports.\n      if ((description.type === 'offer' || description.type === 'answer') &&\n          !rejected && usingBundle && sdpMLineIndex > 0 &&\n          pc.transceivers[sdpMLineIndex]) {\n        pc._disposeIceAndDtlsTransports(sdpMLineIndex);\n        pc.transceivers[sdpMLineIndex].iceGatherer =\n            pc.transceivers[0].iceGatherer;\n        pc.transceivers[sdpMLineIndex].iceTransport =\n            pc.transceivers[0].iceTransport;\n        pc.transceivers[sdpMLineIndex].dtlsTransport =\n            pc.transceivers[0].dtlsTransport;\n        if (pc.transceivers[sdpMLineIndex].rtpSender) {\n          pc.transceivers[sdpMLineIndex].rtpSender.setTransport(\n              pc.transceivers[0].dtlsTransport);\n        }\n        if (pc.transceivers[sdpMLineIndex].rtpReceiver) {\n          pc.transceivers[sdpMLineIndex].rtpReceiver.setTransport(\n              pc.transceivers[0].dtlsTransport);\n        }\n      }\n      if (description.type === 'offer' && !rejected) {\n        transceiver = pc.transceivers[sdpMLineIndex] ||\n            pc._createTransceiver(kind);\n        transceiver.mid = mid;\n\n        if (!transceiver.iceGatherer) {\n          transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,\n              usingBundle);\n        }\n\n        if (cands.length && transceiver.iceTransport.state === 'new') {\n          if (isComplete && (!usingBundle || sdpMLineIndex === 0)) {\n            transceiver.iceTransport.setRemoteCandidates(cands);\n          } else {\n            cands.forEach(function(candidate) {\n              maybeAddCandidate(transceiver.iceTransport, candidate);\n            });\n          }\n        }\n\n        localCapabilities = window.RTCRtpReceiver.getCapabilities(kind);\n\n        // filter RTX until additional stuff needed for RTX is implemented\n        // in adapter.js\n        if (edgeVersion < 15019) {\n          localCapabilities.codecs = localCapabilities.codecs.filter(\n              function(codec) {\n                return codec.name !== 'rtx';\n              });\n        }\n\n        sendEncodingParameters = transceiver.sendEncodingParameters || [{\n          ssrc: (2 * sdpMLineIndex + 2) * 1001\n        }];\n\n        // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams\n        var isNewTrack = false;\n        if (direction === 'sendrecv' || direction === 'sendonly') {\n          isNewTrack = !transceiver.rtpReceiver;\n          rtpReceiver = transceiver.rtpReceiver ||\n              new window.RTCRtpReceiver(transceiver.dtlsTransport, kind);\n\n          if (isNewTrack) {\n            var stream;\n            track = rtpReceiver.track;\n            // FIXME: does not work with Plan B.\n            if (remoteMsid && remoteMsid.stream === '-') {\n              // no-op. a stream id of '-' means: no associated stream.\n            } else if (remoteMsid) {\n              if (!streams[remoteMsid.stream]) {\n                streams[remoteMsid.stream] = new window.MediaStream();\n                Object.defineProperty(streams[remoteMsid.stream], 'id', {\n                  get: function() {\n                    return remoteMsid.stream;\n                  }\n                });\n              }\n              Object.defineProperty(track, 'id', {\n                get: function() {\n                  return remoteMsid.track;\n                }\n              });\n              stream = streams[remoteMsid.stream];\n            } else {\n              if (!streams.default) {\n                streams.default = new window.MediaStream();\n              }\n              stream = streams.default;\n            }\n            if (stream) {\n              addTrackToStreamAndFireEvent(track, stream);\n              transceiver.associatedRemoteMediaStreams.push(stream);\n            }\n            receiverList.push([track, rtpReceiver, stream]);\n          }\n        } else if (transceiver.rtpReceiver && transceiver.rtpReceiver.track) {\n          transceiver.associatedRemoteMediaStreams.forEach(function(s) {\n            var nativeTrack = s.getTracks().find(function(t) {\n              return t.id === transceiver.rtpReceiver.track.id;\n            });\n            if (nativeTrack) {\n              removeTrackFromStreamAndFireEvent(nativeTrack, s);\n            }\n          });\n          transceiver.associatedRemoteMediaStreams = [];\n        }\n\n        transceiver.localCapabilities = localCapabilities;\n        transceiver.remoteCapabilities = remoteCapabilities;\n        transceiver.rtpReceiver = rtpReceiver;\n        transceiver.rtcpParameters = rtcpParameters;\n        transceiver.sendEncodingParameters = sendEncodingParameters;\n        transceiver.recvEncodingParameters = recvEncodingParameters;\n\n        // Start the RTCRtpReceiver now. The RTPSender is started in\n        // setLocalDescription.\n        pc._transceive(pc.transceivers[sdpMLineIndex],\n            false,\n            isNewTrack);\n      } else if (description.type === 'answer' && !rejected) {\n        transceiver = pc.transceivers[sdpMLineIndex];\n        iceGatherer = transceiver.iceGatherer;\n        iceTransport = transceiver.iceTransport;\n        dtlsTransport = transceiver.dtlsTransport;\n        rtpReceiver = transceiver.rtpReceiver;\n        sendEncodingParameters = transceiver.sendEncodingParameters;\n        localCapabilities = transceiver.localCapabilities;\n\n        pc.transceivers[sdpMLineIndex].recvEncodingParameters =\n            recvEncodingParameters;\n        pc.transceivers[sdpMLineIndex].remoteCapabilities =\n            remoteCapabilities;\n        pc.transceivers[sdpMLineIndex].rtcpParameters = rtcpParameters;\n\n        if (cands.length && iceTransport.state === 'new') {\n          if ((isIceLite || isComplete) &&\n              (!usingBundle || sdpMLineIndex === 0)) {\n            iceTransport.setRemoteCandidates(cands);\n          } else {\n            cands.forEach(function(candidate) {\n              maybeAddCandidate(transceiver.iceTransport, candidate);\n            });\n          }\n        }\n\n        if (!usingBundle || sdpMLineIndex === 0) {\n          if (iceTransport.state === 'new') {\n            iceTransport.start(iceGatherer, remoteIceParameters,\n                'controlling');\n          }\n          if (dtlsTransport.state === 'new') {\n            dtlsTransport.start(remoteDtlsParameters);\n          }\n        }\n\n        // If the offer contained RTX but the answer did not,\n        // remove RTX from sendEncodingParameters.\n        var commonCapabilities = getCommonCapabilities(\n          transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n\n        var hasRtx = commonCapabilities.codecs.filter(function(c) {\n          return c.name.toLowerCase() === 'rtx';\n        }).length;\n        if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {\n          delete transceiver.sendEncodingParameters[0].rtx;\n        }\n\n        pc._transceive(transceiver,\n            direction === 'sendrecv' || direction === 'recvonly',\n            direction === 'sendrecv' || direction === 'sendonly');\n\n        // TODO: rewrite to use http://w3c.github.io/webrtc-pc/#set-associated-remote-streams\n        if (rtpReceiver &&\n            (direction === 'sendrecv' || direction === 'sendonly')) {\n          track = rtpReceiver.track;\n          if (remoteMsid) {\n            if (!streams[remoteMsid.stream]) {\n              streams[remoteMsid.stream] = new window.MediaStream();\n            }\n            addTrackToStreamAndFireEvent(track, streams[remoteMsid.stream]);\n            receiverList.push([track, rtpReceiver, streams[remoteMsid.stream]]);\n          } else {\n            if (!streams.default) {\n              streams.default = new window.MediaStream();\n            }\n            addTrackToStreamAndFireEvent(track, streams.default);\n            receiverList.push([track, rtpReceiver, streams.default]);\n          }\n        } else {\n          // FIXME: actually the receiver should be created later.\n          delete transceiver.rtpReceiver;\n        }\n      }\n    });\n\n    if (pc._dtlsRole === undefined) {\n      pc._dtlsRole = description.type === 'offer' ? 'active' : 'passive';\n    }\n\n    pc._remoteDescription = {\n      type: description.type,\n      sdp: description.sdp\n    };\n    if (description.type === 'offer') {\n      pc._updateSignalingState('have-remote-offer');\n    } else {\n      pc._updateSignalingState('stable');\n    }\n    Object.keys(streams).forEach(function(sid) {\n      var stream = streams[sid];\n      if (stream.getTracks().length) {\n        if (pc.remoteStreams.indexOf(stream) === -1) {\n          pc.remoteStreams.push(stream);\n          var event = new Event('addstream');\n          event.stream = stream;\n          window.setTimeout(function() {\n            pc._dispatchEvent('addstream', event);\n          });\n        }\n\n        receiverList.forEach(function(item) {\n          var track = item[0];\n          var receiver = item[1];\n          if (stream.id !== item[2].id) {\n            return;\n          }\n          fireAddTrack(pc, track, receiver, [stream]);\n        });\n      }\n    });\n    receiverList.forEach(function(item) {\n      if (item[2]) {\n        return;\n      }\n      fireAddTrack(pc, item[0], item[1], []);\n    });\n\n    // check whether addIceCandidate({}) was called within four seconds after\n    // setRemoteDescription.\n    window.setTimeout(function() {\n      if (!(pc && pc.transceivers)) {\n        return;\n      }\n      pc.transceivers.forEach(function(transceiver) {\n        if (transceiver.iceTransport &&\n            transceiver.iceTransport.state === 'new' &&\n            transceiver.iceTransport.getRemoteCandidates().length > 0) {\n          console.warn('Timeout for addRemoteCandidate. Consider sending ' +\n              'an end-of-candidates notification');\n          transceiver.iceTransport.addRemoteCandidate({});\n        }\n      });\n    }, 4000);\n\n    return Promise.resolve();\n  };\n\n  RTCPeerConnection.prototype.close = function() {\n    this.transceivers.forEach(function(transceiver) {\n      /* not yet\n      if (transceiver.iceGatherer) {\n        transceiver.iceGatherer.close();\n      }\n      */\n      if (transceiver.iceTransport) {\n        transceiver.iceTransport.stop();\n      }\n      if (transceiver.dtlsTransport) {\n        transceiver.dtlsTransport.stop();\n      }\n      if (transceiver.rtpSender) {\n        transceiver.rtpSender.stop();\n      }\n      if (transceiver.rtpReceiver) {\n        transceiver.rtpReceiver.stop();\n      }\n    });\n    // FIXME: clean up tracks, local streams, remote streams, etc\n    this._isClosed = true;\n    this._updateSignalingState('closed');\n  };\n\n  // Update the signaling state.\n  RTCPeerConnection.prototype._updateSignalingState = function(newState) {\n    this.signalingState = newState;\n    var event = new Event('signalingstatechange');\n    this._dispatchEvent('signalingstatechange', event);\n  };\n\n  // Determine whether to fire the negotiationneeded event.\n  RTCPeerConnection.prototype._maybeFireNegotiationNeeded = function() {\n    var pc = this;\n    if (this.signalingState !== 'stable' || this.needNegotiation === true) {\n      return;\n    }\n    this.needNegotiation = true;\n    window.setTimeout(function() {\n      if (pc.needNegotiation) {\n        pc.needNegotiation = false;\n        var event = new Event('negotiationneeded');\n        pc._dispatchEvent('negotiationneeded', event);\n      }\n    }, 0);\n  };\n\n  // Update the ice connection state.\n  RTCPeerConnection.prototype._updateIceConnectionState = function() {\n    var newState;\n    var states = {\n      'new': 0,\n      closed: 0,\n      checking: 0,\n      connected: 0,\n      completed: 0,\n      disconnected: 0,\n      failed: 0\n    };\n    this.transceivers.forEach(function(transceiver) {\n      if (transceiver.iceTransport && !transceiver.rejected) {\n        states[transceiver.iceTransport.state]++;\n      }\n    });\n\n    newState = 'new';\n    if (states.failed > 0) {\n      newState = 'failed';\n    } else if (states.checking > 0) {\n      newState = 'checking';\n    } else if (states.disconnected > 0) {\n      newState = 'disconnected';\n    } else if (states.new > 0) {\n      newState = 'new';\n    } else if (states.connected > 0) {\n      newState = 'connected';\n    } else if (states.completed > 0) {\n      newState = 'completed';\n    }\n\n    if (newState !== this.iceConnectionState) {\n      this.iceConnectionState = newState;\n      var event = new Event('iceconnectionstatechange');\n      this._dispatchEvent('iceconnectionstatechange', event);\n    }\n  };\n\n  // Update the connection state.\n  RTCPeerConnection.prototype._updateConnectionState = function() {\n    var newState;\n    var states = {\n      'new': 0,\n      closed: 0,\n      connecting: 0,\n      connected: 0,\n      completed: 0,\n      disconnected: 0,\n      failed: 0\n    };\n    this.transceivers.forEach(function(transceiver) {\n      if (transceiver.iceTransport && transceiver.dtlsTransport &&\n          !transceiver.rejected) {\n        states[transceiver.iceTransport.state]++;\n        states[transceiver.dtlsTransport.state]++;\n      }\n    });\n    // ICETransport.completed and connected are the same for this purpose.\n    states.connected += states.completed;\n\n    newState = 'new';\n    if (states.failed > 0) {\n      newState = 'failed';\n    } else if (states.connecting > 0) {\n      newState = 'connecting';\n    } else if (states.disconnected > 0) {\n      newState = 'disconnected';\n    } else if (states.new > 0) {\n      newState = 'new';\n    } else if (states.connected > 0) {\n      newState = 'connected';\n    }\n\n    if (newState !== this.connectionState) {\n      this.connectionState = newState;\n      var event = new Event('connectionstatechange');\n      this._dispatchEvent('connectionstatechange', event);\n    }\n  };\n\n  RTCPeerConnection.prototype.createOffer = function() {\n    var pc = this;\n\n    if (pc._isClosed) {\n      return Promise.reject(makeError('InvalidStateError',\n          'Can not call createOffer after close'));\n    }\n\n    var numAudioTracks = pc.transceivers.filter(function(t) {\n      return t.kind === 'audio';\n    }).length;\n    var numVideoTracks = pc.transceivers.filter(function(t) {\n      return t.kind === 'video';\n    }).length;\n\n    // Determine number of audio and video tracks we need to send/recv.\n    var offerOptions = arguments[0];\n    if (offerOptions) {\n      // Reject Chrome legacy constraints.\n      if (offerOptions.mandatory || offerOptions.optional) {\n        throw new TypeError(\n            'Legacy mandatory/optional constraints not supported.');\n      }\n      if (offerOptions.offerToReceiveAudio !== undefined) {\n        if (offerOptions.offerToReceiveAudio === true) {\n          numAudioTracks = 1;\n        } else if (offerOptions.offerToReceiveAudio === false) {\n          numAudioTracks = 0;\n        } else {\n          numAudioTracks = offerOptions.offerToReceiveAudio;\n        }\n      }\n      if (offerOptions.offerToReceiveVideo !== undefined) {\n        if (offerOptions.offerToReceiveVideo === true) {\n          numVideoTracks = 1;\n        } else if (offerOptions.offerToReceiveVideo === false) {\n          numVideoTracks = 0;\n        } else {\n          numVideoTracks = offerOptions.offerToReceiveVideo;\n        }\n      }\n    }\n\n    pc.transceivers.forEach(function(transceiver) {\n      if (transceiver.kind === 'audio') {\n        numAudioTracks--;\n        if (numAudioTracks < 0) {\n          transceiver.wantReceive = false;\n        }\n      } else if (transceiver.kind === 'video') {\n        numVideoTracks--;\n        if (numVideoTracks < 0) {\n          transceiver.wantReceive = false;\n        }\n      }\n    });\n\n    // Create M-lines for recvonly streams.\n    while (numAudioTracks > 0 || numVideoTracks > 0) {\n      if (numAudioTracks > 0) {\n        pc._createTransceiver('audio');\n        numAudioTracks--;\n      }\n      if (numVideoTracks > 0) {\n        pc._createTransceiver('video');\n        numVideoTracks--;\n      }\n    }\n\n    var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,\n        pc._sdpSessionVersion++);\n    pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n      // For each track, create an ice gatherer, ice transport,\n      // dtls transport, potentially rtpsender and rtpreceiver.\n      var track = transceiver.track;\n      var kind = transceiver.kind;\n      var mid = transceiver.mid || SDPUtils.generateIdentifier();\n      transceiver.mid = mid;\n\n      if (!transceiver.iceGatherer) {\n        transceiver.iceGatherer = pc._createIceGatherer(sdpMLineIndex,\n            pc.usingBundle);\n      }\n\n      var localCapabilities = window.RTCRtpSender.getCapabilities(kind);\n      // filter RTX until additional stuff needed for RTX is implemented\n      // in adapter.js\n      if (edgeVersion < 15019) {\n        localCapabilities.codecs = localCapabilities.codecs.filter(\n            function(codec) {\n              return codec.name !== 'rtx';\n            });\n      }\n      localCapabilities.codecs.forEach(function(codec) {\n        // work around https://bugs.chromium.org/p/webrtc/issues/detail?id=6552\n        // by adding level-asymmetry-allowed=1\n        if (codec.name === 'H264' &&\n            codec.parameters['level-asymmetry-allowed'] === undefined) {\n          codec.parameters['level-asymmetry-allowed'] = '1';\n        }\n\n        // for subsequent offers, we might have to re-use the payload\n        // type of the last offer.\n        if (transceiver.remoteCapabilities &&\n            transceiver.remoteCapabilities.codecs) {\n          transceiver.remoteCapabilities.codecs.forEach(function(remoteCodec) {\n            if (codec.name.toLowerCase() === remoteCodec.name.toLowerCase() &&\n                codec.clockRate === remoteCodec.clockRate) {\n              codec.preferredPayloadType = remoteCodec.payloadType;\n            }\n          });\n        }\n      });\n      localCapabilities.headerExtensions.forEach(function(hdrExt) {\n        var remoteExtensions = transceiver.remoteCapabilities &&\n            transceiver.remoteCapabilities.headerExtensions || [];\n        remoteExtensions.forEach(function(rHdrExt) {\n          if (hdrExt.uri === rHdrExt.uri) {\n            hdrExt.id = rHdrExt.id;\n          }\n        });\n      });\n\n      // generate an ssrc now, to be used later in rtpSender.send\n      var sendEncodingParameters = transceiver.sendEncodingParameters || [{\n        ssrc: (2 * sdpMLineIndex + 1) * 1001\n      }];\n      if (track) {\n        // add RTX\n        if (edgeVersion >= 15019 && kind === 'video' &&\n            !sendEncodingParameters[0].rtx) {\n          sendEncodingParameters[0].rtx = {\n            ssrc: sendEncodingParameters[0].ssrc + 1\n          };\n        }\n      }\n\n      if (transceiver.wantReceive) {\n        transceiver.rtpReceiver = new window.RTCRtpReceiver(\n            transceiver.dtlsTransport, kind);\n      }\n\n      transceiver.localCapabilities = localCapabilities;\n      transceiver.sendEncodingParameters = sendEncodingParameters;\n    });\n\n    // always offer BUNDLE and dispose on return if not supported.\n    if (pc._config.bundlePolicy !== 'max-compat') {\n      sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {\n        return t.mid;\n      }).join(' ') + '\\r\\n';\n    }\n    sdp += 'a=ice-options:trickle\\r\\n';\n\n    pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n      sdp += writeMediaSection(transceiver, transceiver.localCapabilities,\n          'offer', transceiver.stream, pc._dtlsRole);\n      sdp += 'a=rtcp-rsize\\r\\n';\n\n      if (transceiver.iceGatherer && pc.iceGatheringState !== 'new' &&\n          (sdpMLineIndex === 0 || !pc.usingBundle)) {\n        transceiver.iceGatherer.getLocalCandidates().forEach(function(cand) {\n          cand.component = 1;\n          sdp += 'a=' + SDPUtils.writeCandidate(cand) + '\\r\\n';\n        });\n\n        if (transceiver.iceGatherer.state === 'completed') {\n          sdp += 'a=end-of-candidates\\r\\n';\n        }\n      }\n    });\n\n    var desc = new window.RTCSessionDescription({\n      type: 'offer',\n      sdp: sdp\n    });\n    return Promise.resolve(desc);\n  };\n\n  RTCPeerConnection.prototype.createAnswer = function() {\n    var pc = this;\n\n    if (pc._isClosed) {\n      return Promise.reject(makeError('InvalidStateError',\n          'Can not call createAnswer after close'));\n    }\n\n    if (!(pc.signalingState === 'have-remote-offer' ||\n        pc.signalingState === 'have-local-pranswer')) {\n      return Promise.reject(makeError('InvalidStateError',\n          'Can not call createAnswer in signalingState ' + pc.signalingState));\n    }\n\n    var sdp = SDPUtils.writeSessionBoilerplate(pc._sdpSessionId,\n        pc._sdpSessionVersion++);\n    if (pc.usingBundle) {\n      sdp += 'a=group:BUNDLE ' + pc.transceivers.map(function(t) {\n        return t.mid;\n      }).join(' ') + '\\r\\n';\n    }\n    sdp += 'a=ice-options:trickle\\r\\n';\n\n    var mediaSectionsInOffer = SDPUtils.getMediaSections(\n        pc._remoteDescription.sdp).length;\n    pc.transceivers.forEach(function(transceiver, sdpMLineIndex) {\n      if (sdpMLineIndex + 1 > mediaSectionsInOffer) {\n        return;\n      }\n      if (transceiver.rejected) {\n        if (transceiver.kind === 'application') {\n          if (transceiver.protocol === 'DTLS/SCTP') { // legacy fmt\n            sdp += 'm=application 0 DTLS/SCTP 5000\\r\\n';\n          } else {\n            sdp += 'm=application 0 ' + transceiver.protocol +\n                ' webrtc-datachannel\\r\\n';\n          }\n        } else if (transceiver.kind === 'audio') {\n          sdp += 'm=audio 0 UDP/TLS/RTP/SAVPF 0\\r\\n' +\n              'a=rtpmap:0 PCMU/8000\\r\\n';\n        } else if (transceiver.kind === 'video') {\n          sdp += 'm=video 0 UDP/TLS/RTP/SAVPF 120\\r\\n' +\n              'a=rtpmap:120 VP8/90000\\r\\n';\n        }\n        sdp += 'c=IN IP4 0.0.0.0\\r\\n' +\n            'a=inactive\\r\\n' +\n            'a=mid:' + transceiver.mid + '\\r\\n';\n        return;\n      }\n\n      // FIXME: look at direction.\n      if (transceiver.stream) {\n        var localTrack;\n        if (transceiver.kind === 'audio') {\n          localTrack = transceiver.stream.getAudioTracks()[0];\n        } else if (transceiver.kind === 'video') {\n          localTrack = transceiver.stream.getVideoTracks()[0];\n        }\n        if (localTrack) {\n          // add RTX\n          if (edgeVersion >= 15019 && transceiver.kind === 'video' &&\n              !transceiver.sendEncodingParameters[0].rtx) {\n            transceiver.sendEncodingParameters[0].rtx = {\n              ssrc: transceiver.sendEncodingParameters[0].ssrc + 1\n            };\n          }\n        }\n      }\n\n      // Calculate intersection of capabilities.\n      var commonCapabilities = getCommonCapabilities(\n          transceiver.localCapabilities,\n          transceiver.remoteCapabilities);\n\n      var hasRtx = commonCapabilities.codecs.filter(function(c) {\n        return c.name.toLowerCase() === 'rtx';\n      }).length;\n      if (!hasRtx && transceiver.sendEncodingParameters[0].rtx) {\n        delete transceiver.sendEncodingParameters[0].rtx;\n      }\n\n      sdp += writeMediaSection(transceiver, commonCapabilities,\n          'answer', transceiver.stream, pc._dtlsRole);\n      if (transceiver.rtcpParameters &&\n          transceiver.rtcpParameters.reducedSize) {\n        sdp += 'a=rtcp-rsize\\r\\n';\n      }\n    });\n\n    var desc = new window.RTCSessionDescription({\n      type: 'answer',\n      sdp: sdp\n    });\n    return Promise.resolve(desc);\n  };\n\n  RTCPeerConnection.prototype.addIceCandidate = function(candidate) {\n    var pc = this;\n    var sections;\n    if (candidate && !(candidate.sdpMLineIndex !== undefined ||\n        candidate.sdpMid)) {\n      return Promise.reject(new TypeError('sdpMLineIndex or sdpMid required'));\n    }\n\n    // TODO: needs to go into ops queue.\n    return new Promise(function(resolve, reject) {\n      if (!pc._remoteDescription) {\n        return reject(makeError('InvalidStateError',\n            'Can not add ICE candidate without a remote description'));\n      } else if (!candidate || candidate.candidate === '') {\n        for (var j = 0; j < pc.transceivers.length; j++) {\n          if (pc.transceivers[j].rejected) {\n            continue;\n          }\n          pc.transceivers[j].iceTransport.addRemoteCandidate({});\n          sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);\n          sections[j] += 'a=end-of-candidates\\r\\n';\n          pc._remoteDescription.sdp =\n              SDPUtils.getDescription(pc._remoteDescription.sdp) +\n              sections.join('');\n          if (pc.usingBundle) {\n            break;\n          }\n        }\n      } else {\n        var sdpMLineIndex = candidate.sdpMLineIndex;\n        if (candidate.sdpMid) {\n          for (var i = 0; i < pc.transceivers.length; i++) {\n            if (pc.transceivers[i].mid === candidate.sdpMid) {\n              sdpMLineIndex = i;\n              break;\n            }\n          }\n        }\n        var transceiver = pc.transceivers[sdpMLineIndex];\n        if (transceiver) {\n          if (transceiver.rejected) {\n            return resolve();\n          }\n          var cand = Object.keys(candidate.candidate).length > 0 ?\n              SDPUtils.parseCandidate(candidate.candidate) : {};\n          // Ignore Chrome's invalid candidates since Edge does not like them.\n          if (cand.protocol === 'tcp' && (cand.port === 0 || cand.port === 9)) {\n            return resolve();\n          }\n          // Ignore RTCP candidates, we assume RTCP-MUX.\n          if (cand.component && cand.component !== 1) {\n            return resolve();\n          }\n          // when using bundle, avoid adding candidates to the wrong\n          // ice transport. And avoid adding candidates added in the SDP.\n          if (sdpMLineIndex === 0 || (sdpMLineIndex > 0 &&\n              transceiver.iceTransport !== pc.transceivers[0].iceTransport)) {\n            if (!maybeAddCandidate(transceiver.iceTransport, cand)) {\n              return reject(makeError('OperationError',\n                  'Can not add ICE candidate'));\n            }\n          }\n\n          // update the remoteDescription.\n          var candidateString = candidate.candidate.trim();\n          if (candidateString.indexOf('a=') === 0) {\n            candidateString = candidateString.substr(2);\n          }\n          sections = SDPUtils.getMediaSections(pc._remoteDescription.sdp);\n          sections[sdpMLineIndex] += 'a=' +\n              (cand.type ? candidateString : 'end-of-candidates')\n              + '\\r\\n';\n          pc._remoteDescription.sdp =\n              SDPUtils.getDescription(pc._remoteDescription.sdp) +\n              sections.join('');\n        } else {\n          return reject(makeError('OperationError',\n              'Can not add ICE candidate'));\n        }\n      }\n      resolve();\n    });\n  };\n\n  RTCPeerConnection.prototype.getStats = function(selector) {\n    if (selector && selector instanceof window.MediaStreamTrack) {\n      var senderOrReceiver = null;\n      this.transceivers.forEach(function(transceiver) {\n        if (transceiver.rtpSender &&\n            transceiver.rtpSender.track === selector) {\n          senderOrReceiver = transceiver.rtpSender;\n        } else if (transceiver.rtpReceiver &&\n            transceiver.rtpReceiver.track === selector) {\n          senderOrReceiver = transceiver.rtpReceiver;\n        }\n      });\n      if (!senderOrReceiver) {\n        throw makeError('InvalidAccessError', 'Invalid selector.');\n      }\n      return senderOrReceiver.getStats();\n    }\n\n    var promises = [];\n    this.transceivers.forEach(function(transceiver) {\n      ['rtpSender', 'rtpReceiver', 'iceGatherer', 'iceTransport',\n          'dtlsTransport'].forEach(function(method) {\n            if (transceiver[method]) {\n              promises.push(transceiver[method].getStats());\n            }\n          });\n    });\n    return Promise.all(promises).then(function(allStats) {\n      var results = new Map();\n      allStats.forEach(function(stats) {\n        stats.forEach(function(stat) {\n          results.set(stat.id, stat);\n        });\n      });\n      return results;\n    });\n  };\n\n  // fix low-level stat names and return Map instead of object.\n  var ortcObjects = ['RTCRtpSender', 'RTCRtpReceiver', 'RTCIceGatherer',\n    'RTCIceTransport', 'RTCDtlsTransport'];\n  ortcObjects.forEach(function(ortcObjectName) {\n    var obj = window[ortcObjectName];\n    if (obj && obj.prototype && obj.prototype.getStats) {\n      var nativeGetstats = obj.prototype.getStats;\n      obj.prototype.getStats = function() {\n        return nativeGetstats.apply(this)\n        .then(function(nativeStats) {\n          var mapStats = new Map();\n          Object.keys(nativeStats).forEach(function(id) {\n            nativeStats[id].type = fixStatsType(nativeStats[id]);\n            mapStats.set(id, nativeStats[id]);\n          });\n          return mapStats;\n        });\n      };\n    }\n  });\n\n  // legacy callback shims. Should be moved to adapter.js some days.\n  var methods = ['createOffer', 'createAnswer'];\n  methods.forEach(function(method) {\n    var nativeMethod = RTCPeerConnection.prototype[method];\n    RTCPeerConnection.prototype[method] = function() {\n      var args = arguments;\n      if (typeof args[0] === 'function' ||\n          typeof args[1] === 'function') { // legacy\n        return nativeMethod.apply(this, [arguments[2]])\n        .then(function(description) {\n          if (typeof args[0] === 'function') {\n            args[0].apply(null, [description]);\n          }\n        }, function(error) {\n          if (typeof args[1] === 'function') {\n            args[1].apply(null, [error]);\n          }\n        });\n      }\n      return nativeMethod.apply(this, arguments);\n    };\n  });\n\n  methods = ['setLocalDescription', 'setRemoteDescription', 'addIceCandidate'];\n  methods.forEach(function(method) {\n    var nativeMethod = RTCPeerConnection.prototype[method];\n    RTCPeerConnection.prototype[method] = function() {\n      var args = arguments;\n      if (typeof args[1] === 'function' ||\n          typeof args[2] === 'function') { // legacy\n        return nativeMethod.apply(this, arguments)\n        .then(function() {\n          if (typeof args[1] === 'function') {\n            args[1].apply(null);\n          }\n        }, function(error) {\n          if (typeof args[2] === 'function') {\n            args[2].apply(null, [error]);\n          }\n        });\n      }\n      return nativeMethod.apply(this, arguments);\n    };\n  });\n\n  // getStats is special. It doesn't have a spec legacy method yet we support\n  // getStats(something, cb) without error callbacks.\n  ['getStats'].forEach(function(method) {\n    var nativeMethod = RTCPeerConnection.prototype[method];\n    RTCPeerConnection.prototype[method] = function() {\n      var args = arguments;\n      if (typeof args[1] === 'function') {\n        return nativeMethod.apply(this, arguments)\n        .then(function() {\n          if (typeof args[1] === 'function') {\n            args[1].apply(null);\n          }\n        });\n      }\n      return nativeMethod.apply(this, arguments);\n    };\n  });\n\n  return RTCPeerConnection;\n};\n","/* global __filename, RTCSessionDescription */\n\nimport { Interop } from '@jitsi/sdp-interop';\nimport { getLogger } from 'jitsi-meet-logger';\nimport transform from 'sdp-transform';\n\nimport * as CodecMimeType from '../../service/RTC/CodecMimeType';\nimport * as MediaType from '../../service/RTC/MediaType';\nimport RTCEvents from '../../service/RTC/RTCEvents';\nimport * as SignalingEvents from '../../service/RTC/SignalingEvents';\nimport * as VideoType from '../../service/RTC/VideoType';\nimport browser from '../browser';\nimport * as GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';\nimport RtxModifier from '../xmpp/RtxModifier';\nimport SDP from '../xmpp/SDP';\nimport SDPUtil from '../xmpp/SDPUtil';\nimport SdpConsistency from '../xmpp/SdpConsistency';\nimport { SdpTransformWrap } from '../xmpp/SdpTransformUtil';\n\nimport JitsiRemoteTrack from './JitsiRemoteTrack';\nimport LocalSdpMunger from './LocalSdpMunger';\nimport RTC from './RTC';\nimport RTCUtils from './RTCUtils';\nimport { SIM_LAYER_RIDS, TPCUtils } 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';\nconst DESKTOP_SHARE_RATE = 500000;\nconst HD_BITRATE = 2500000;\nconst LD_BITRATE = 200000;\nconst SD_BITRATE = 700000;\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} iceConfig WebRTC 'PeerConnection' ICE config\n * @param {object} constraints WebRTC 'PeerConnection' constraints\n * @param {boolean} isP2P indicates whether or not the new instance will be used\n * 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\n * the simulcast.\n * @param {boolean} options.disableRtx if set to 'true' will disable the RTX\n * @param {boolean} options.capScreenshareBitrate if set to 'true' simulcast will\n * be disabled for screenshare and a max bitrate of 500Kbps will applied on the\n * stream.\n * @param {string} options.disabledCodec the mime type of the code that should\n * not be negotiated on the peerconnection.\n * @param {boolean} options.disableH264 If set to 'true' H264 will be\n *      disabled by removing it from the SDP (deprecated)\n * @param {boolean} options.preferH264 if set to 'true' H264 will be preferred\n * over other video codecs. (deprecated)\n * @param {string} options.preferredCodec the mime type of the codec that needs\n * to be made the preferred codec for the connection.\n * @param {boolean} options.startSilent If set to 'true' no audio will be sent or received.\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        iceConfig,\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    // FIXME: We should support multiple streams per jid.\n    /**\n     * The map holds remote tracks associated with this peer connection.\n     * It maps user's JID to media type and remote track\n     * (one track per media type per user's JID).\n     * @type {Map<string, Map<MediaType, 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 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\n        = new RTCUtils.RTCPeerConnectionType(iceConfig, safeConstraints);\n\n    // The standard video bitrates are used in Unified plan when switching\n    // between camera/desktop tracks on the same sender.\n    const standardVideoBitrates = {\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    // These bitrates will be applied on all browsers for camera sources in both simulcast and p2p mode.\n    this.videoBitrates = this.options.videoQuality && this.options.videoQuality.maxBitratesVideo\n        ? this.options.videoQuality.maxBitratesVideo\n        : standardVideoBitrates;\n\n    this.tpcUtils = new TPCUtils(this, this.videoBitrates);\n    this.updateLog = [];\n    this.stats = {};\n    this.statsinterval = null;\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    const Simulcast = require('@jitsi/sdp-simulcast');\n\n    this.simulcast = new Simulcast(\n        {\n            numOfLayers: SIM_LAYER_RIDS.length,\n            explodeRemoteSimulcast: false,\n            usesUnifiedPlan: browser.usesUnifiedPlan()\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);\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.\n     */\n    this.senderVideoMaxHeight = null;\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 stream events in plan-b and track events in unified plan.\n    if (browser.usesPlanB()) {\n        this.peerconnection.onaddstream\n            = event => this._remoteStreamAdded(event.stream);\n        this.peerconnection.onremovestream\n            = event => this._remoteStreamRemoved(event.stream);\n    } else {\n        this.peerconnection.ontrack = event => {\n            const stream = event.streams[0];\n\n            this._remoteTrackAdded(stream, event.track, event.transceiver);\n            stream.onremovetrack = evt => {\n                this._remoteTrackRemoved(stream, evt.track);\n            };\n        };\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.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(stats => {\n                if (stats.result\n                    && 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\n                // empty error callback\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 * @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(\n        mediaType) {\n    let mediaTransferActive = true;\n\n    if (mediaType === MediaType.AUDIO) {\n        mediaTransferActive = this.audioTransferActive;\n    } else if (mediaType === MediaType.VIDEO) {\n        mediaTransferActive = this.videoTransferActive;\n    }\n    if (mediaTransferActive) {\n        return this.hasAnyTracksOfType(mediaType) ? 'sendrecv' : 'recvonly';\n    }\n\n    return 'inactive';\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(`No endpointID on peerVideoTypeChanged ${this}`);\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('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\n * information on the RTCRtpReceivers. The information relevant to the ssrc\n * is updated each time a RTP packet constaining the ssrc is received.\n * @returns {Object} containing ssrc and audio level information as a\n * key-value pair.\n */\nTraceablePeerConnection.prototype.getAudioLevels = function() {\n    const audioLevels = {};\n    const audioReceivers = this.peerconnection.getReceivers()\n        .filter(receiver => receiver.track && receiver.track.kind === MediaType.AUDIO);\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 track.\n *\n * @returns {JitsiLocalTrack|undefined} - local video track.\n */\nTraceablePeerConnection.prototype.getLocalVideoTrack = function() {\n    return this.getLocalTracks(MediaType.VIDEO)[0];\n};\n\n/**\n * Checks whether or not this {@link TraceablePeerConnection} instance contains\n * any local tracks for given <tt>mediaType</tt>.\n * @param {MediaType} mediaType\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 * @param {string} [endpointId] the track owner's identifier (MUC nickname)\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 */\nTraceablePeerConnection.prototype.getRemoteTracks = function(\n        endpointId,\n        mediaType) {\n    const remoteTracks = [];\n    const endpoints\n        = endpointId ? [ endpointId ] : this.remoteTracks.keys();\n\n    for (const endpoint of endpoints) {\n        const endpointTrackMap = this.remoteTracks.get(endpoint);\n\n        if (!endpointTrackMap) {\n\n            // Otherwise an empty Map() would have to be allocated above\n            // eslint-disable-next-line no-continue\n            continue;\n        }\n\n        for (const trackMediaType of endpointTrackMap.keys()) {\n            // per media type filtering\n            if (!mediaType || mediaType === trackMediaType) {\n                const mediaTrack = endpointTrackMap.get(trackMediaType);\n\n                if (mediaTrack) {\n                    remoteTracks.push(mediaTrack);\n                }\n            }\n        }\n    }\n\n    return remoteTracks;\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(\n            `${this} ignored remote 'stream added' event for non-user stream`\n             + `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(\n            `${this} ignored remote 'stream added' event for non-user stream`\n             + `id: ${streamId}`);\n\n        return;\n    }\n    logger.info(`${this} remote track added:`, streamId, 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 = browser.usesPlanB()\n        ? new SDP(this.remoteDescription.sdp)\n        : new SDP(this.peerconnection.remoteDescription.sdp);\n    let mediaLines;\n\n    if (browser.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(\n                `No media lines for type ${\n                    mediaType} found in remote SDP for remote track: ${\n                    streamId}`));\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(\n                `No SSRC lines for streamId ${\n                    streamId} for remote track, media 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: ${ssrcStr} for remote track, msid: ${\n                    streamId} media type: ${mediaType}`));\n\n        // Abort\n        return;\n    } else if (!ownerEndpointId) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(\n                `No SSRC owner known for: ${\n                    trackSsrc} for remote track, msid: ${\n                    streamId} media type: ${mediaType}`));\n\n        // Abort\n        return;\n    }\n\n    logger.log(`${this} associated ssrc`, ownerEndpointId, trackSsrc);\n\n    const peerMediaInfo\n        = this.signalingLayer.getPeerMediaInfo(ownerEndpointId, mediaType);\n\n    if (!peerMediaInfo) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(\n                `${this}: no peer media info available for ${\n                    ownerEndpointId}`));\n\n        return;\n    }\n\n    const muted = peerMediaInfo.muted;\n    const videoType = peerMediaInfo.videoType; // can be undefined\n\n    this._createRemoteTrack(\n        ownerEndpointId, stream, track, mediaType, videoType, trackSsrc, muted);\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 */\nTraceablePeerConnection.prototype._createRemoteTrack = function(\n        ownerEndpointId,\n        stream,\n        track,\n        mediaType,\n        videoType,\n        ssrc,\n        muted) {\n    let remoteTracksMap = this.remoteTracks.get(ownerEndpointId);\n\n    if (!remoteTracksMap) {\n        remoteTracksMap = new Map();\n        this.remoteTracks.set(ownerEndpointId, remoteTracksMap);\n    }\n\n    const existingTrack = remoteTracksMap.get(mediaType);\n\n    // Delete the existing track and create the new one because of a known bug on Safari.\n    // RTCPeerConnection.ontrack fires when a new remote track is added but MediaStream.onremovetrack doesn't so\n    // it needs to be removed whenever a new track is received for the same endpoint id.\n    if (existingTrack && browser.isSafari()) {\n        this._remoteTrackRemoved(existingTrack.getOriginalStream(), existingTrack.getTrack());\n    }\n\n    if (existingTrack && existingTrack.getTrack() === track) {\n        // Ignore duplicated event which can originate either from\n        // 'onStreamAdded' or 'onTrackAdded'.\n        logger.info(\n            `${this} ignored duplicated remote track added event for: `\n                + `${ownerEndpointId}, ${mediaType}`);\n\n        return;\n    } else if (existingTrack) {\n        logger.error(`${this} overwriting remote track for ${ownerEndpointId} ${mediaType}`);\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\n    remoteTracksMap.set(mediaType, remoteTrack);\n\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(\n            `Ignored remote 'stream removed' event for non-user stream ${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 * @param {MediaStream} stream WebRTC MediaStream instance which is the parent\n * of the track.\n * @param {MediaStreamTrack} track the WebRTC MediaStreamTrack which has been\n * removed from the PeerConnection.\n */\nTraceablePeerConnection.prototype._remoteTrackRemoved = function(\n        stream,\n        track) {\n    const streamId = RTC.getStreamID(stream);\n    const trackId = track && RTC.getTrackID(track);\n\n    logger.info(`${this} - remote track removed: ${streamId}, ${trackId}`);\n\n    if (!streamId) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(`${this} remote track removal failed - no stream ID`));\n\n        return;\n    }\n\n    if (!trackId) {\n        GlobalOnErrorHandler.callErrorHandler(\n            new Error(`${this} remote track removal failed - no track ID`));\n\n        return;\n    }\n\n    if (!this._removeRemoteTrackById(streamId, trackId)) {\n        // NOTE this warning is always printed when user leaves the room,\n        // because we remove remote tracks manually on MUC member left event,\n        // before the SSRCs are removed by Jicofo. In most cases it is fine to\n        // ignore this warning, but still it's better to keep it printed for\n        // debugging purposes.\n        //\n        // We could change the behaviour to emit track removed only from here,\n        // but the order of the events will change and consuming apps could\n        // behave unexpectedly (the \"user left\" event would come before \"track\n        // removed\" events).\n        logger.warn(\n            `${this} Removed track not found for msid: ${streamId},\n             track id: ${trackId}`);\n    }\n};\n\n/**\n * Finds remote track by it's stream and track ids.\n * @param {string} streamId the media stream id as defined by the WebRTC\n * @param {string} trackId the media track id as defined by the WebRTC\n * @return {JitsiRemoteTrack|undefined} the track's instance or\n * <tt>undefined</tt> if not found.\n * @private\n */\nTraceablePeerConnection.prototype._getRemoteTrackById = function(\n        streamId,\n        trackId) {\n    // .find will break the loop once the first match is found\n    for (const endpointTrackMap of this.remoteTracks.values()) {\n        for (const mediaTrack of endpointTrackMap.values()) {\n            // FIXME verify and try to use ===\n            /* eslint-disable eqeqeq */\n            if (mediaTrack.getStreamId() == streamId\n                && mediaTrack.getTrackId() == trackId) {\n                return mediaTrack;\n            }\n\n            /* eslint-enable eqeqeq */\n        }\n    }\n\n    return undefined;\n};\n\n/**\n * Removes all JitsiRemoteTracks associated with given MUC nickname\n * (resource part of the JID). Returns array of removed tracks.\n *\n * @param {string} owner - The resource part of the MUC JID.\n * @returns {JitsiRemoteTrack[]}\n */\nTraceablePeerConnection.prototype.removeRemoteTracks = function(owner) {\n    const removedTracks = [];\n    const remoteTracksMap = this.remoteTracks.get(owner);\n\n    if (remoteTracksMap) {\n        const removedAudioTrack = remoteTracksMap.get(MediaType.AUDIO);\n        const removedVideoTrack = remoteTracksMap.get(MediaType.VIDEO);\n\n        removedAudioTrack && removedTracks.push(removedAudioTrack);\n        removedVideoTrack && removedTracks.push(removedVideoTrack);\n\n        this.remoteTracks.delete(owner);\n    }\n\n    logger.debug(\n        `${this} removed remote tracks for ${owner} count: ${\n            removedTracks.length}`);\n\n    return removedTracks;\n};\n\n/**\n * Removes and disposes given <tt>JitsiRemoteTrack</tt> instance. Emits\n * {@link RTCEvents.REMOTE_TRACK_REMOVED}.\n * @param {JitsiRemoteTrack} toBeRemoved\n */\nTraceablePeerConnection.prototype._removeRemoteTrack = function(toBeRemoved) {\n    toBeRemoved.dispose();\n    const participantId = toBeRemoved.getParticipantId();\n    const remoteTracksMap = this.remoteTracks.get(participantId);\n\n    if (!remoteTracksMap) {\n        logger.error(\n            `removeRemoteTrack: no remote tracks map for ${participantId}`);\n    } else if (!remoteTracksMap.delete(toBeRemoved.getType())) {\n        logger.error(\n            `Failed to remove ${toBeRemoved} - type mapping messed up ?`);\n    }\n    this.eventEmitter.emit(RTCEvents.REMOTE_TRACK_REMOVED, toBeRemoved);\n};\n\n/**\n * Removes and disposes <tt>JitsiRemoteTrack</tt> identified by given stream and\n * track ids.\n *\n * @param {string} streamId the media stream id as defined by the WebRTC\n * @param {string} trackId the media track id as defined by the WebRTC\n * @returns {JitsiRemoteTrack|undefined} the track which has been removed or\n * <tt>undefined</tt> if no track matching given stream and track ids was\n * found.\n */\nTraceablePeerConnection.prototype._removeRemoteTrackById = function(\n        streamId,\n        trackId) {\n    const toBeRemoved = this._getRemoteTrackById(streamId, trackId);\n\n    if (toBeRemoved) {\n        this._removeRemoteTrack(toBeRemoved);\n    }\n\n    return toBeRemoved;\n};\n\n/**\n * @typedef {Object} SSRCGroupInfo\n * @property {Array<number>} ssrcs group's SSRCs\n * @property {string} semantics\n */\n/**\n * @typedef {Object} TrackSSRCInfo\n * @property {Array<number>} ssrcs track's SSRCs\n * @property {Array<SSRCGroupInfo>} groups track's SSRC groups\n */\n/**\n * Returns map with keys msid and <tt>TrackSSRCInfo</tt> values.\n * @param {Object} desc the WebRTC SDP instance.\n * @return {Map<string,TrackSSRCInfo>}\n */\nfunction extractSSRCMap(desc) {\n    /**\n     * Track SSRC infos mapped by stream ID (msid)\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    for (const mLine of session.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\n                        = group.ssrcs.split(' ').map(\n                            ssrcStr => parseInt(ssrcStr, 10));\n                    const primarySSRC = groupSSRCs[0];\n\n                    // Note that group.semantics is already present\n\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        for (const ssrc of mLine.ssrcs) {\n            if (ssrc.attribute !== 'msid') {\n                continue; // eslint-disable-line no-continue\n            }\n\n            const msid = ssrc.value;\n            let ssrcInfo = ssrcMap.get(msid);\n\n            if (!ssrcInfo) {\n                ssrcInfo = {\n                    ssrcs: [],\n                    groups: [],\n                    msid\n                };\n                ssrcMap.set(msid, ssrcInfo);\n            }\n\n            const ssrcNumber = ssrc.id;\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 = `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('audio');\n    let changed = false;\n\n    if (audioMedia && audioMedia.direction !== 'sendrecv') {\n        if (options.startSilent) {\n            audioMedia.direction = 'inactive';\n        } else {\n            audioMedia.direction = 'sendrecv';\n        }\n\n        changed = true;\n    }\n\n    const videoMedia = transformer.selectMedia('video');\n\n    if (videoMedia && videoMedia.direction !== 'sendrecv') {\n        videoMedia.direction = '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\n        // 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            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    localDescription() {\n        let desc = this.peerconnection.localDescription;\n\n        if (!desc) {\n            logger.debug('getLocalDescription no localDescription found');\n\n            return {};\n        }\n\n        this.trace('getLocalDescription::preTransform', dumpSDP(desc));\n\n        // if we're running on FF, transform to Plan B first.\n        if (browser.usesUnifiedPlan()) {\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 {\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('getRemoteDescription no remoteDescription found');\n\n            return {};\n        }\n        this.trace('getRemoteDescription::preTransform', dumpSDP(desc));\n\n        // if we're running on FF, transform to Plan B first.\n        if (browser.usesUnifiedPlan()) {\n            desc = this.interop.toPlanB(desc);\n            this.trace(\n                'getRemoteDescription::postTransform (Plan B)', dumpSDP(desc));\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 screensharing is in progress.\n *\n * @returns {boolean}  Returns true if a desktop track has been added to the\n * peerconnection, false otherwise.\n */\nTraceablePeerConnection.prototype._isSharingScreen = function() {\n    const track = this.getLocalVideoTrack();\n\n    return 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 || browser.supportsCodecPreferences()) {\n        return description;\n    }\n\n    const parsedSdp = transform.parse(description.sdp);\n    const mLine = parsedSdp.media.find(m => m.type === this.codecPreference.mediaType);\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            const bitrates = Object.values(this.videoBitrates.VP9 || this.videoBitrates);\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: this._isSharingScreen() ? HD_BITRATE : Math.floor(bitrates[2] / 1000)\n            } ];\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 * Checks if given track belongs to this peerconnection instance.\n *\n * @param {JitsiLocalTrack|JitsiRemoteTrack} track - The track to be checked.\n * @returns {boolean}\n */\nTraceablePeerConnection.prototype.containsTrack = function(track) {\n    if (track.isLocal()) {\n        return this.localTracks.has(track.rtcId);\n    }\n\n    const participantId = track.getParticipantId();\n    const remoteTracksMap = this.remoteTracks.get(participantId);\n\n    return Boolean(remoteTracksMap && remoteTracksMap.get(track.getType()) === track);\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(`add ${track} to: ${this}`);\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\n    // For p2p unified case, use addTransceiver API to add the tracks on the peerconnection.\n    if (browser.usesUnifiedPlan() && this.isP2P) {\n        this.tpcUtils.addTrack(track, isInitiator);\n    } else {\n        // In all other cases, i.e., plan-b and unified plan bridge case, use addStream API to\n        // add the track to the peerconnection.\n        // TODO - addTransceiver doesn't generate a MSID for the stream, which is needed for signaling\n        // the ssrc to Jicofo. Switch to using UUID as MSID when addTransceiver is used in Unified plan\n        // JVB connection case as well.\n        const webrtcStream = track.getOriginalStream();\n\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}`));\n        }\n\n        // Muted video tracks do not have WebRTC stream\n        if (browser.usesPlanB() && browser.doesVideoMuteByStreamRemove()\n                && 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\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 = 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\n * 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.addTrackUnmute = function(track) {\n    if (!this._assertTrackBelongs('addTrackUnmute', track)) {\n        // Abort\n        return Promise.reject('Track not found on the peerconnection');\n    }\n\n    logger.info(`Adding ${track} as unmute to ${this}`);\n    const webRtcStream = track.getOriginalStream();\n\n    if (!webRtcStream) {\n        logger.error(\n            `Unable to add ${track} as unmute to ${this} - no WebRTC stream`);\n\n        return Promise.reject('Stream not found');\n    }\n\n    if (browser.usesUnifiedPlan()) {\n        return this.tpcUtils.addTrackUnmute(track);\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(\n            `${methodName}: ${localTrack} does not belong to ${this}`);\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.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 * 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(`Invalid codec settings: preferred ${preferredCodec}, disabled ${disabledCodec},\n            atleast one value is needed`);\n    }\n\n    if (browser.supportsCodecPreferences()) {\n        // TODO implement codec preference using RTCRtpTransceiver.setCodecPreferences()\n        // We are using SDP munging for now until all browsers support this.\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    return this.peerconnection.getSenders().find(s => s.track && s.track.kind === mediaType);\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    return this.peerconnection.getSenders().find(s => s.track === track);\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\n * replaced\n * @param {JitsiLocalTrack|null} newTrack - The new track to use\n * @returns {Promise<boolean>} - If the promise resolves with true,\n * renegotiation will be needed. Otherwise no renegotiation is needed.\n */\nTraceablePeerConnection.prototype.replaceTrack = function(oldTrack, newTrack) {\n    if (browser.usesUnifiedPlan()) {\n        return this.tpcUtils.replaceTrack(oldTrack, newTrack)\n\n            // renegotiate when SDP is used for simulcast munging\n            .then(() => this.isSimulcastOn() && browser.usesSdpMungingForSimulcast());\n    }\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 (browser.usesUnifiedPlan()) {\n        return this.tpcUtils.removeTrackMute(localTrack);\n    }\n\n    if (webRtcStream) {\n        logger.info(\n            `Removing ${localTrack} as mute from ${this}`);\n        this._removeStream(webRtcStream);\n\n        return Promise.resolve(true);\n    }\n\n    logger.error(`removeStreamMute - no WebRTC stream for ${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(\n        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 {Object} localDescription the WebRTC session description instance for\n * the local description.\n * @private\n */\nTraceablePeerConnection.prototype._adjustLocalMediaDirection = function(\n        localDescription) {\n    const transformer = new SdpTransformWrap(localDescription.sdp);\n    let modifiedDirection = false;\n    const audioMedia = transformer.selectMedia('audio');\n\n    if (audioMedia) {\n        const desiredAudioDirection\n            = this._getDesiredMediaDirection(MediaType.AUDIO);\n\n        if (audioMedia.direction !== desiredAudioDirection) {\n            audioMedia.direction = desiredAudioDirection;\n            logger.info(\n                `Adjusted local audio direction to ${desiredAudioDirection}`);\n            modifiedDirection = true;\n        }\n    } else {\n        logger.warn('No \"audio\" media found int the local description');\n    }\n\n    const videoMedia = transformer.selectMedia('video');\n\n    if (videoMedia) {\n        const desiredVideoDirection\n            = this._getDesiredMediaDirection(MediaType.VIDEO);\n\n        if (videoMedia.direction !== desiredVideoDirection) {\n            videoMedia.direction = desiredVideoDirection;\n            logger.info(\n                `Adjusted local video direction to ${desiredVideoDirection}`);\n            modifiedDirection = true;\n        }\n    } else {\n        logger.warn('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\nTraceablePeerConnection.prototype.setLocalDescription = function(description) {\n    let localSdp = description;\n\n    this.trace('setLocalDescription::preTransform', dumpSDP(localSdp));\n\n    // Munge the order of the codecs based on the preferences set through config.js\n    localSdp = this._mungeCodecOrder(localSdp);\n\n    if (browser.usesPlanB()) {\n        localSdp = this._adjustLocalMediaDirection(localSdp);\n        localSdp = this._ensureSimulcastGroupIsLast(localSdp);\n    } else {\n\n        // if we're using unified plan, transform to it first.\n        localSdp = this.interop.toUnifiedPlan(localSdp);\n        this.trace(\n            'setLocalDescription::postTransform (Unified Plan)',\n            dumpSDP(localSdp));\n    }\n\n    return new Promise((resolve, reject) => {\n        this.peerconnection.setLocalDescription(localSdp)\n            .then(() => {\n                this.trace('setLocalDescriptionOnSuccess');\n                const localUfrag = SDPUtil.getUfrag(localSdp.sdp);\n\n                if (localUfrag !== this.localUfrag) {\n                    this.localUfrag = localUfrag;\n                    this.eventEmitter.emit(\n                        RTCEvents.LOCAL_UFRAG_CHANGED, this, localUfrag);\n                }\n                resolve();\n            }, err => {\n                this.trace('setLocalDescriptionOnFailure', err);\n                this.eventEmitter.emit(\n                    RTCEvents.SET_LOCAL_DESCRIPTION_FAILED,\n                    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 (browser.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\n/**\n * Sets the degradation preference on the video sender. This setting determines if\n * resolution or framerate will be preferred when bandwidth or cpu is constrained.\n * Sets it to 'maintain-framerate' when a camera track is added to the pc, sets it\n * to 'maintain-resolution' when a desktop track is being shared instead.\n * @returns {Promise<void>}\n */\nTraceablePeerConnection.prototype.setSenderVideoDegradationPreference = function() {\n    if (!this.peerconnection.getSenders) {\n        logger.debug('Browser does not support RTCRtpSender');\n\n        return Promise.resolve();\n    }\n    const localVideoTrack = this.getLocalVideoTrack();\n    const videoSender = this.findSenderByKind(MediaType.VIDEO);\n\n    if (!videoSender) {\n        return Promise.resolve();\n    }\n    const parameters = videoSender.getParameters();\n\n    if (!parameters.encodings || !parameters.encodings.length) {\n        return Promise.resolve();\n    }\n    for (const encoding in parameters.encodings) {\n        if (parameters.encodings.hasOwnProperty(encoding)) {\n            const preference = localVideoTrack.videoType === VideoType.CAMERA\n                ? DEGRADATION_PREFERENCE_CAMERA\n                : DEGRADATION_PREFERENCE_DESKTOP;\n\n            logger.info(`Setting video sender degradation preference on ${this} to ${preference}`);\n            parameters.encodings[encoding].degradationPreference = preference;\n        }\n    }\n    this.tpcUtils.updateEncodingsResolution(parameters);\n\n    return videoSender.setParameters(parameters);\n};\n\n/**\n * Sets the max bitrate on the RTCRtpSender so that the\n * bitrate of the enocder doesn't exceed the configured value.\n * This is needed for the desktop share until spec-complaint\n * simulcast is implemented.\n * @param {JitsiLocalTrack} localTrack - the local track whose\n * max bitrate is to be configured.\n * @returns {Promise<void>}\n */\nTraceablePeerConnection.prototype.setMaxBitRate = function() {\n    // For VP9, max bitrate is configured by setting b=AS value in SDP. Browsers do\n    // not yet support setting max bitrates for individual VP9 SVC layers.\n    if (this.getConfiguredVideoCodec() === CodecMimeType.VP9 || !window.RTCRtpSender) {\n        return Promise.resolve();\n    }\n    const localVideoTrack = this.getLocalVideoTrack();\n\n    if (!localVideoTrack) {\n        return Promise.resolve();\n    }\n\n    const videoType = localVideoTrack.videoType;\n    const planBScreenSharing = browser.usesPlanB() && videoType === VideoType.DESKTOP;\n\n    // Apply the maxbitrates on the video track when one of the conditions is met.\n    // 1. Max. bitrates for video are specified through videoQuality settings in config.js\n    // 2. Track is a desktop track and bitrate is capped using capScreenshareBitrate option in plan-b mode.\n    // 3. The client is running in Unified plan mode.\n    if (!((this.options.videoQuality && this.options.videoQuality.maxBitratesVideo)\n        || (planBScreenSharing && this.options.capScreenshareBitrate)\n        || browser.usesUnifiedPlan())) {\n        return Promise.resolve();\n    }\n\n    const presenterEnabled = localVideoTrack._originalStream\n        && localVideoTrack._originalStream.id !== localVideoTrack.getStreamId();\n    const videoSender = this.findSenderByKind(MediaType.VIDEO);\n\n    if (!videoSender) {\n        return Promise.resolve();\n    }\n    const parameters = videoSender.getParameters();\n\n    if (!(parameters.encodings && parameters.encodings.length)) {\n        return Promise.resolve();\n    }\n\n    if (this.isSimulcastOn()) {\n        for (const encoding in parameters.encodings) {\n            if (parameters.encodings.hasOwnProperty(encoding)) {\n                let bitrate;\n\n                if (planBScreenSharing) {\n                    // On chromium, set a max bitrate of 500 Kbps for screenshare when capScreenshareBitrate\n                    // is enabled through config.js and presenter is not turned on.\n                    // FIXME the top 'isSimulcastOn' condition is confusing for screensharing, because\n                    // if capScreenshareBitrate option is enabled then the simulcast is turned off\n                    bitrate = this.options.capScreenshareBitrate\n                        ? presenterEnabled ? HD_BITRATE : DESKTOP_SHARE_RATE\n\n                        // Remove the bitrate config if not capScreenshareBitrate:\n                        // When switching from camera to desktop and videoQuality.maxBitratesVideo were set,\n                        // then the 'maxBitrate' setting must be cleared, because if simulcast is enabled for screen\n                        // and maxBitrates are set then Chrome will not send the screen stream (plan B).\n                        : undefined;\n                } else {\n                    bitrate = this.tpcUtils.localStreamEncodingsConfig[encoding].maxBitrate;\n                }\n\n                logger.info(`${this} Setting a max bitrate of ${bitrate} bps on layer `\n                    + `${this.tpcUtils.localStreamEncodingsConfig[encoding].rid}`);\n                parameters.encodings[encoding].maxBitrate = bitrate;\n            }\n        }\n    } else {\n        // Do not change the max bitrate for desktop tracks in non-simulcast mode.\n        let bitrate = this.videoBitrates.high;\n\n        if (videoType === VideoType.CAMERA) {\n            // Determine the bitrates based on the sender constraint applied for unicast tracks.\n            const scaleFactor = this.senderVideoMaxHeight\n                ? Math.floor(localVideoTrack.resolution / this.senderVideoMaxHeight)\n                : 1;\n            const encoding = this.tpcUtils.localStreamEncodingsConfig\n                .find(layer => layer.scaleResolutionDownBy === scaleFactor);\n\n            if (encoding) {\n                logger.info(`${this} Setting a max bitrate of ${encoding.maxBitrate} bps on local video track`);\n                bitrate = encoding.maxBitrate;\n            }\n        }\n        parameters.encodings[0].maxBitrate = bitrate;\n    }\n    this.tpcUtils.updateEncodingsResolution(parameters);\n\n    return videoSender.setParameters(parameters);\n};\n\nTraceablePeerConnection.prototype.setRemoteDescription = function(description) {\n    this.trace('setRemoteDescription::preTransform', dumpSDP(description));\n\n    // Munge the order of the codecs based on the preferences set through config.js\n    // eslint-disable-next-line no-param-reassign\n    description = this._mungeCodecOrder(description);\n\n    if (browser.usesPlanB()) {\n        // TODO the focus should squeze or explode the remote simulcast\n        if (this.isSimulcastOn()) {\n            // eslint-disable-next-line no-param-reassign\n            description = this.simulcast.mungeRemoteDescription(description, true /* add x-google-conference flag */);\n            this.trace(\n                'setRemoteDescription::postTransform (simulcast)',\n                dumpSDP(description));\n        }\n\n        // eslint-disable-next-line no-param-reassign\n        description = normalizePlanB(description);\n    } else {\n        const currentDescription = this.peerconnection.remoteDescription;\n\n        // eslint-disable-next-line no-param-reassign\n        description = this.interop.toUnifiedPlan(description, currentDescription);\n        this.trace(\n            'setRemoteDescription::postTransform (Unified)',\n            dumpSDP(description));\n\n        if (this.isSimulcastOn()) {\n            // eslint-disable-next-line no-param-reassign\n            description = this.simulcast.mungeRemoteDescription(description);\n\n            // eslint-disable-next-line no-param-reassign\n            description = this.tpcUtils.insertUnifiedPlanSimulcastReceive(description);\n            this.trace(\n                'setRemoteDescription::postTransform (sim receive)',\n                dumpSDP(description));\n\n            // eslint-disable-next-line no-param-reassign\n            description = this.tpcUtils.ensureCorrectOrderOfSsrcs(description);\n        }\n    }\n\n    return new Promise((resolve, reject) => {\n        this.peerconnection.setRemoteDescription(description)\n            .then(() => {\n                this.trace('setRemoteDescriptionOnSuccess');\n                const remoteUfrag = SDPUtil.getUfrag(description.sdp);\n\n                if (remoteUfrag !== this.remoteUfrag) {\n                    this.remoteUfrag = remoteUfrag;\n                    this.eventEmitter.emit(\n                        RTCEvents.REMOTE_UFRAG_CHANGED, this, remoteUfrag);\n                }\n                resolve();\n            }, err => {\n                this.trace('setRemoteDescriptionOnFailure', err);\n                this.eventEmitter.emit(\n                    RTCEvents.SET_REMOTE_DESCRIPTION_FAILED,\n                    err,\n                    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\n * the user preferred value. If simulcast is enabled on the peerconection, all the\n * simulcast encodings that have a resolution height lower or equal to the value\n * provided will remain active. For the non-simulcast case, video constraint is\n * applied on the track.\n * @param {number} frameHeight - The user preferred max frame height.\n * @returns {Promise} promise that will be resolved when the operation is\n * successful and rejected otherwise.\n */\nTraceablePeerConnection.prototype.setSenderVideoConstraint = function(frameHeight = null) {\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    // Need to explicitly check for null as 0 is falsy, but a valid value\n    const newHeight = frameHeight === null ? this.senderVideoMaxHeight : frameHeight;\n\n    this.senderVideoMaxHeight = newHeight;\n\n    // If layer suspension is disabled and sender constraint is not configured for the conference,\n    // resolve here so that the encodings stay enabled. This can happen in custom apps built using\n    // lib-jitsi-meet.\n    if (newHeight === null) {\n        return Promise.resolve();\n    }\n\n    logger.log(`${this} senderVideoMaxHeight: ${newHeight}`);\n\n    const localVideoTrack = this.getLocalVideoTrack();\n\n    if (!localVideoTrack || localVideoTrack.isMuted()) {\n        return Promise.resolve();\n    }\n    const videoSender = this.findSenderByKind(MediaType.VIDEO);\n\n    if (!videoSender) {\n        return Promise.resolve();\n    }\n    const parameters = videoSender.getParameters();\n\n    if (!parameters || !parameters.encodings || !parameters.encodings.length) {\n        return Promise.resolve();\n    }\n\n    if (this.isSimulcastOn()) {\n        // Determine the encodings that need to stay enabled based on the new frameHeight provided.\n        const encodingsEnabledState = this.tpcUtils.getLocalStreamHeightConstraints(localVideoTrack.track)\n            .map(height => height <= newHeight);\n\n        // Always keep the LD stream enabled, specifically when the LD stream's resolution is higher than of the\n        // requested 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        const ldStreamIndex = this.tpcUtils.localStreamEncodingsConfig\n            .findIndex(layer => layer.scaleResolutionDownBy === 4.0);\n\n        if (newHeight > 0 && ldStreamIndex !== -1) {\n            encodingsEnabledState[ldStreamIndex] = true;\n        }\n        for (const encoding in parameters.encodings) {\n            if (parameters.encodings.hasOwnProperty(encoding)) {\n                parameters.encodings[encoding].active = encodingsEnabledState[encoding];\n            }\n        }\n        this.tpcUtils.updateEncodingsResolution(parameters);\n    } else if (newHeight > 0) {\n        // Do not scale down the desktop tracks until QualityController is able to propagate the sender constraints\n        // only on the active media connection. Right now, the sender constraints received on the bridge channel\n        // are propagated on both the jvb and p2p connections in cases where they both are active at the same time.\n        parameters.encodings[0].scaleResolutionDownBy\n            = localVideoTrack.videoType === VideoType.DESKTOP || localVideoTrack.resolution <= newHeight\n                ? 1\n                : Math.floor(localVideoTrack.resolution / newHeight);\n        parameters.encodings[0].active = true;\n    } else {\n        parameters.encodings[0].scaleResolutionDownBy = undefined;\n        parameters.encodings[0].active = false;\n    }\n\n    logger.info(`${this} setting max height of ${newHeight}, encodings: ${JSON.stringify(parameters.encodings)}`);\n\n    return videoSender.setParameters(parameters).then(() => {\n        localVideoTrack.maxEnabledResolution = newHeight;\n        this.eventEmitter.emit(RTCEvents.LOCAL_TRACK_MAX_ENABLED_RESOLUTION_CHANGED, localVideoTrack);\n\n        // Max bitrate needs to be reconfigured on the sender in p2p/non-simulcast case if needed when\n        // the send resolution changes.\n        if (this.isP2P || !this.isSimulcastOn()) {\n            return this.setMaxBitRate();\n        }\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 (browser.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('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(\n        SignalingEvents.PEER_MUTED_CHANGED, this._peerMutedChanged);\n    this.signalingLayer.off(\n        SignalingEvents.PEER_VIDEO_TYPE_CHANGED, this._peerVideoTypeChanged);\n\n    for (const peerTracks of this.remoteTracks.values()) {\n        for (const remoteTrack of peerTracks.values()) {\n            this._removeRemoteTrack(remoteTrack);\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('RTC._removePeerConnection returned false');\n    }\n    if (this.statsinterval !== null) {\n        window.clearInterval(this.statsinterval);\n        this.statsinterval = null;\n    }\n    logger.info(`Closing ${this}...`);\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 (browser.usesPlanB()) {\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            // Configure simulcast for camera tracks always and for desktop tracks only when\n            // the \"capScreenshareBitrate\" flag in config.js is disabled.\n            if (this.isSimulcastOn() && browser.usesSdpMungingForSimulcast()\n                && (!this.options.capScreenshareBitrate\n                || (this.options.capScreenshareBitrate && !this._isSharingScreen()))) {\n                // eslint-disable-next-line no-param-reassign\n                resultSdp = this.simulcast.mungeLocalDescription(resultSdp);\n                this.trace(\n                    `create${logName}`\n                        + 'OnSuccess::postTransform (simulcast)',\n                    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 = extractSSRCMap(resultSdp);\n\n            logger.debug('Got local SSRCs MAP: ', ssrcMap);\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(`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    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 trackMSID = track.storedMSID;\n\n        if (ssrcMap.has(trackMSID)) {\n            const newSSRC = ssrcMap.get(trackMSID);\n\n            if (!newSSRC) {\n                logger.error(`No SSRC found for: ${trackMSID} in ${this}`);\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                if (oldSSRCNum === null) {\n                    logger.info(\n                        `Storing new local SSRC for ${track} in ${this}`,\n                        newSSRC);\n                } else {\n                    logger.error(\n                        `Overwriting SSRC for ${track} ${trackMSID} in ${this\n                        } with: `, newSSRC);\n                }\n                this.localSSRCs.set(track.rtcId, newSSRC);\n\n                this.eventEmitter.emit(\n                    RTCEvents.LOCAL_TRACK_SSRC_UPDATED, track, newSSRCNum);\n            } else {\n                logger.debug(\n                    `The local SSRC(${newSSRCNum}) for ${track} ${trackMSID}`\n                     + `is still up to date in ${this}`);\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(`No SSRCs found in the local SDP for ${track} MSID: ${trackMSID} in ${this}`);\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 * Obtains call-related stats from the peer connection.\n *\n * @param {Function} callback - The function to invoke after successfully\n * obtaining stats.\n * @param {Function} errback - The function to invoke after failing to obtain\n * stats.\n * @returns {void}\n */\nTraceablePeerConnection.prototype.getStats = function(callback, errback) {\n    // TODO (brian): After moving all browsers to adapter, check if adapter is\n    // accounting for different getStats apis, making the browser-checking-if\n    // unnecessary.\n    if (browser.isSafari() || browser.isFirefox() || browser.isReactNative()) {\n        // uses the new Promise based getStats\n        this.peerconnection.getStats()\n            .then(callback)\n            .catch(errback || (() => {\n\n                // Making sure that getStats won't fail if error callback is\n                // not passed.\n            }));\n    } else {\n        this.peerconnection.getStats(callback);\n    }\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(`Will overwrite local SSRCs for track ID: ${rtcId}`);\n    }\n\n    // Configure simulcast for camera tracks always and for desktop tracks only when\n    // the \"capScreenshareBitrate\" flag in config.js is disabled.\n    if (this.isSimulcastOn()\n        && (!this.options.capScreenshareBitrate\n        || (this.options.capScreenshareBitrate && !this._isSharingScreen()))) {\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 * Creates a text representation of this <tt>TraceablePeerConnection</tt>\n * instance.\n * @return {string}\n */\nTraceablePeerConnection.prototype.toString = function() {\n    return `TPC[${this.id},p2p:${this.isP2P}]`;\n};\n","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\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    logger.debug(\n        `Updating mline to associate ${rtxSsrc}`\n        + `rtx ssrc with primary stream, ${primarySsrcInfo.id}`);\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        logger.debug(`${rtxSsrc} was already associated with ${primarySsrc}`);\n\n        return;\n    }\n    if (previousRtxSSRC) {\n        logger.debug(\n            `${primarySsrc} was previously associated with rtx`\n            + `${previousRtxSSRC}, removing all references to it`);\n\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\n        logger.debug(`groups before filtering for ${previousRtxSSRC}`);\n        logger.debug(mLine.dumpSSRCGroups());\n\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\n     *  already have them.  If the video ssrc has been\n     *  seen before, and already had an RTX ssrc generated,\n     *  the same RTX ssrc will be used again.\n     * @param {string} sdpStr sdp in raw string format\n     */\n    modifyRtxSsrcs(sdpStr) {\n        const sdpTransformer = new SdpTransformWrap(sdpStr);\n        const videoMLine = sdpTransformer.selectMedia('video');\n\n        if (!videoMLine) {\n            logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n            return sdpStr;\n        }\n\n        return this.modifyRtxSsrcs2(videoMLine)\n            ? sdpTransformer.toRawSDP() : sdpStr;\n    }\n\n    /**\n     * Does the same thing as {@link modifyRtxSsrcs}, but takes the\n     *  {@link MLineWrap} instance wrapping video media as an argument.\n     * @param {MLineWrap} videoMLine\n     * @return {boolean} <tt>true</tt> if the SDP wrapped by\n     *  {@link SdpTransformWrap} has been modified or <tt>false</tt> otherwise.\n     */\n    modifyRtxSsrcs2(videoMLine) {\n        if (videoMLine.direction === 'recvonly') {\n            logger.debug('RtxModifier doing nothing, video m line is recvonly');\n\n            return false;\n        }\n        if (videoMLine.getSSRCCount() < 1) {\n            logger.debug('RtxModifier doing nothing, no video ssrcs present');\n\n            return false;\n        }\n        logger.debug('Current ssrc mapping: ', this.correspondingRtxSsrcs);\n        const primaryVideoSsrcs = videoMLine.getPrimaryVideoSSRCs();\n\n        logger.debug('Parsed primary video ssrcs ', primaryVideoSsrcs,\n            ' making sure all have rtx streams');\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                logger.debug(\n                    'Already have an associated rtx ssrc for'\n                    + `video ssrc ${ssrc}: ${correspondingRtxSsrc}`);\n            } else {\n                logger.debug(\n                    `No previously associated rtx ssrc for video ssrc ${ssrc}`);\n\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                    logger.debug(\n                        `Rtx stream ${previousAssociatedRtxStream} `\n                        + 'already existed in the sdp as an rtx stream for '\n                        + `${ssrc}`);\n                    correspondingRtxSsrc = previousAssociatedRtxStream;\n                } else {\n                    correspondingRtxSsrc = SDPUtil.generateSsrc();\n                    logger.debug(`Generated rtx ssrc ${correspondingRtxSsrc} `\n                                 + `for ssrc ${ssrc}`);\n                }\n                logger.debug(`Caching rtx ssrc ${correspondingRtxSsrc} `\n                             + `for video ssrc ${ssrc}`);\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     * @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 videoMLine = sdpTransformer.selectMedia('video');\n\n        if (!videoMLine) {\n            logger.debug(`No 'video' media found in the sdp: ${sdpStr}`);\n\n            return sdpStr;\n        }\n        if (videoMLine.direction === 'recvonly') {\n            logger.debug('RtxModifier doing nothing, video m line is recvonly');\n\n            return sdpStr;\n        }\n        if (videoMLine.getSSRCCount() < 1) {\n            logger.debug('RtxModifier doing nothing, no video ssrcs present');\n\n            return sdpStr;\n        }\n        if (!videoMLine.containsAnySSRCGroups()) {\n            logger.debug('RtxModifier doing nothing, '\n              + 'no video ssrcGroups present');\n\n            return sdpStr;\n        }\n        const fidGroups = videoMLine.findGroups('FID');\n\n        // Remove the fid groups from the mline\n\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        return sdpTransformer.toRawSDP();\n    }\n}\n","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\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('video');\n\n        if (!videoMLine) {\n            logger.debug(\n                `${this.logPrefix} no 'video' media found in the sdp: `\n                    + `${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(\n                    `${this.logPrefix} no SSRC found for the recvonly video`\n                        + 'stream!');\n            }\n        } else {\n            const newPrimarySsrc = videoMLine.getPrimaryVideoSsrc();\n\n            if (!newPrimarySsrc) {\n                logger.info(\n                    `${this.logPrefix} sdp-consistency couldn't`\n                        + ' parse new primary ssrc');\n\n                return sdpStr;\n            }\n            if (this.cachedPrimarySsrc) {\n                logger.info(\n                    `${this.logPrefix} sdp-consistency replacing new ssrc`\n                        + `${newPrimarySsrc} with cached `\n                        + `${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                logger.info(\n                    `${this.logPrefix} sdp-consistency caching primary ssrc`\n                        + `${this.cachedPrimarySsrc}`);\n            }\n\n            this.injectRecvOnly = true;\n        }\n\n        return sdpTransformer.toRawSDP();\n    }\n}\n","import * as JitsiTrackEvents from '../../JitsiTrackEvents';\nimport { createTtfmEvent } from '../../service/statistics/AnalyticsEvents';\nimport Statistics from '../statistics/statistics';\n\nimport JitsiTrack from './JitsiTrack';\n\nconst logger = require('jitsi-meet-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 = [\n    'abort', 'canplay', 'canplaythrough', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart',\n    'pause', 'play', 'playing', 'ratechange', 'stalled', 'suspend', 'waiting'\n];\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     * @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        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\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     * 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     * 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     * 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        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\n     * 'audio' element.\n     * @private\n     */\n    _onTrackAttach(container) {\n        logger.debug(`Track has been attached to a container: ${this}`);\n\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\n     * 'audio' element.\n     * @private\n     */\n    _onTrackDetach(container) {\n        logger.debug(`Track has been detached from a container: ${this}`);\n\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     * 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}, status: ${this._getStatus()}]`;\n    }\n}\n","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-logger';\n\nimport * as MediaType from '../../service/RTC/MediaType';\nimport { SdpTransformWrap } from '../xmpp/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     */\n    constructor(tpc) {\n        this.tpc = tpc;\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('video');\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\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 = muted || !isInPeerConnection;\n\n            logger.debug(\n                `${this.tpc} ${videoTrack} muted: ${\n                    muted}, is in PeerConnection: ${\n                    isInPeerConnection} => should fake sdp ? : ${\n                    shouldFakeSdp}`);\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(\n                    `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 = '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                logger.debug(\n                    `${this.tpc} injecting video SSRC: ${ssrcNum} for ${\n                        videoTrack}`);\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                    logger.debug(\n                        `${this.tpc} injecting SIM group for ${videoTrack}`,\n                        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     * Modifies 'cname', 'msid', 'label' and 'mslabel' by appending\n     * the id of {@link LocalSdpMunger#tpc} at the end, preceding by a dash\n     * sign.\n     *\n     * @param {MLineWrap} mediaSection - The media part (audio or video) of the\n     * session description which will be modified in place.\n     * @returns {void}\n     * @private\n     */\n    _transformMediaIdentifiers(mediaSection) {\n        const pcId = this.tpc.id;\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                    if (streamAndTrackIDs.length === 2) {\n                        const streamId = streamAndTrackIDs[0];\n                        const trackId = streamAndTrackIDs[1];\n\n                        ssrcLine.value\n                            = `${streamId}-${pcId} ${trackId}-${pcId}`;\n                    } else {\n                        logger.warn(\n                            'Unable to munge local MSID'\n                                + `- weird format detected: ${ssrcLine.value}`);\n                    }\n                }\n                break;\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('audio');\n\n        if (audioMLine) {\n            this._transformMediaIdentifiers(audioMLine);\n        }\n\n        const videoMLine = transformer.selectMedia('video');\n\n        if (videoMLine) {\n            this._transformMediaIdentifiers(videoMLine);\n        }\n\n        return new RTCSessionDescription({\n            type: sessionDesc.type,\n            sdp: transformer.toRawSDP()\n        });\n    }\n}\n","import { getLogger } from 'jitsi-meet-logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport * as RTCEvents from '../../service/RTC/RTCEvents';\nimport * as ConnectionQualityEvents from '../../service/connectivity/ConnectionQualityEvents';\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 * See media/engine/simulcast.ss from webrtc.org\n */\nconst kSimulcastFormats = [\n    { width: 1920,\n        height: 1080,\n        layers: 3,\n        max: 5000,\n        target: 4000,\n        min: 800 },\n    { width: 1280,\n        height: 720,\n        layers: 3,\n        max: 2500,\n        target: 2500,\n        min: 600 },\n    { width: 960,\n        height: 540,\n        layers: 3,\n        max: 900,\n        target: 900,\n        min: 450 },\n    { width: 640,\n        height: 360,\n        layers: 2,\n        max: 700,\n        target: 500,\n        min: 150 },\n    { width: 480,\n        height: 270,\n        layers: 2,\n        max: 450,\n        target: 350,\n        min: 150 },\n    { width: 320,\n        height: 180,\n        layers: 1,\n        max: 200,\n        target: 150,\n        min: 30 }\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/**\n * The current cap (in kbps) put on the video stream (or null if there isn't\n * a cap).  If there is a cap, we'll take it into account when calculating\n * the current quality.\n */\nlet videoBitrateCap = null;\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\n * video started.\n */\nfunction getTarget(simulcast, resolution, millisSinceStart) {\n    // Completely ignore the bitrate in the first 5 seconds, as the first\n    // event seems to fire very early and the value is suspicious and causes\n    // false positives.\n    if (millisSinceStart < 15000) {\n        return 1;\n    }\n\n    let target = 0;\n    let height = Math.min(resolution.height, resolution.width);\n\n    if (simulcast) {\n        // Find the first format with height no bigger than ours.\n        let simulcastFormat = kSimulcastFormats.find(f => f.height <= height);\n\n        if (simulcastFormat) {\n            // Sum the target fields from all simulcast layers for the given\n            // resolution (e.g. 720p + 360p + 180p).\n            for (height = simulcastFormat.height; height >= 180; height /= 2) {\n                const targetHeight = height;\n\n                simulcastFormat\n                    = kSimulcastFormats.find(f => f.height === targetHeight);\n                if (simulcastFormat) {\n                    target += simulcastFormat.target;\n                } else {\n                    break;\n                }\n            }\n        }\n    } else {\n        // See GetMaxDefaultVideoBitrateKbps in\n        // media/engine/webrtcvideoengine2.cc from webrtc.org\n        const pixels = resolution.width * resolution.height;\n\n        if (pixels <= 320 * 240) {\n            target = 600;\n        } else if (pixels <= 640 * 480) {\n            target = 1700;\n        } else if (pixels <= 960 * 540) {\n            target = 2000;\n        } else {\n            target = 2500;\n        }\n    }\n\n    // Allow for an additional 1 second for ramp up -- delay any initial drop\n    // of connection quality by 1 second.\n    return Math.min(target, 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         * 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        /**\n         * The time at which a video bitrate cap was last removed.  We use\n         * this to calculate how much time we, as a sender, have had to\n         * ramp-up\n         */\n        this._timeLastBwCapRemoved = -1;\n\n        // We assume a global startBitrate value for the sake of simplicity.\n        if (options.config.startBitrate && options.config.startBitrate > 0) {\n            startBitrate = 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        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        // Listen to local statistics events originating from the RTC module\n        // and update the _localStats field.\n        // Oh, and by the way, the resolutions of all remote participants are\n        // also piggy-backed in these \"local\" statistics. It's obvious, really,\n        // if one carefully reads the *code* (but not the docs) in\n        // UI/VideoLayout/VideoLayout.js#updateLocalConnectionStats in\n        // jitsi-meet\n        // TODO: We should keep track of the remote resolution in _remoteStats,\n        // and notify about changes via separate events.\n        conference.statistics.addConnectionStatsListener(\n            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\n     * a screen capture).\n     * @param isMuted {boolean} whether the local video is muted.\n     * @param resolutionName {Resolution} the input resolution used by the\n     * 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 sending bitrate.\n\n            // Figure out if simulcast is in use\n            const activeTPC = this._conference.getActivePeerConnection();\n            const isSimulcastOn\n                = Boolean(activeTPC && activeTPC.isSimulcastOn());\n\n            const newVideoBitrateCap\n                = activeTPC && activeTPC.bandwidthLimiter\n                && activeTPC.bandwidthLimiter.getBandwidthLimit('video');\n\n            // If we had a cap set but there isn't one now, then it has\n            // just been 'lifted', so we should treat this like a new\n            // ramp up.\n            if (!newVideoBitrateCap && videoBitrateCap) {\n                this._timeLastBwCapRemoved = window.performance.now();\n\n                // Set the start bitrate to whatever we were just capped to\n                startBitrate = videoBitrateCap;\n            }\n            videoBitrateCap = newVideoBitrateCap;\n\n            // time since sending of video was enabled.\n            const millisSinceStart = window.performance.now()\n                - Math.max(this._timeVideoUnmuted,\n                    this._timeIceConnected,\n                    this._timeLastBwCapRemoved);\n\n            // expected sending bitrate in perfect conditions\n            let target\n                = getTarget(isSimulcastOn, resolution, millisSinceStart);\n\n            target = Math.min(0.9 * target, MAX_TARGET_BITRATE);\n\n            if (videoBitrateCap) {\n                target = Math.min(target, videoBitrateCap);\n            }\n\n            quality = 100 * this._localStats.bitrate.upload / target;\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\n                = (window.performance.now() - this._lastConnectionQualityUpdate)\n                    / 1000;\n\n            quality\n                = Math.min(\n                    quality,\n                    prevConnectionQuality\n                        + (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.broadcastEndpointMessage({\n                type: STATS_MESSAGE_TYPE,\n                values: data });\n        } catch (e) {\n            // We often hit this in the beginning of a call, before the data\n            // channel is ready. It is not a big problem, because we will\n            // send the statistics again after a few seconds, and the error is\n            // already logged elsewhere. So just ignore it.\n\n            // let errorMsg = \"Failed to broadcast local stats\";\n            // logger.error(errorMsg, e);\n            // GlobalOnErrorHandler.callErrorHandler(\n            //    new Error(errorMsg + \": \" + e));\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","/* global __filename */\nimport { getLogger } from 'jitsi-meet-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        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        const reloadClient = this._conference.restartInProgress && enableForcedReload;\n\n        logger.info('ICE failed,'\n            + ` enableForcedReload: ${enableForcedReload},`\n            + ` enableIceRestart: ${enableIceRestart},`\n            + ` restartInProgress: ${this._conference.restartInProgress},`\n            + ` supports restart by terminate: ${supportsRestartByTerminate}`);\n\n        if (explicitlyDisabled || (!enableIceRestart && !supportsRestartByTerminate) || reloadClient) {\n            logger.info('ICE failed, but ICE restarts are disabled');\n            this._conference.eventEmitter.emit(\n                JitsiConferenceEvents.CONFERENCE_FAILED,\n                JitsiConferenceErrors.ICE_FAILED);\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","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","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","/* global __filename */\nimport { getLogger } from 'jitsi-meet-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","/* global __filename */\n\nimport { getLogger } from 'jitsi-meet-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 * as JitsiConferenceEvents from '../../JitsiConferenceEvents';\nimport MediaSessionEvents from '../xmpp/MediaSessionEvents';\n\n/**\n * The class manages send and receive 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 class QualityController {\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 quality constraints.\n     */\n    constructor(conference) {\n        this.conference = conference;\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._propagateSendMaxFrameHeight());\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._propagateSendMaxFrameHeight();\n                }\n            });\n        this.preferredReceiveMaxFrameHeight\n            && mediaSession.setReceiverVideoConstraint(this.preferredReceiveMaxFrameHeight);\n\n        // Set the degradation preference on the local video track.\n        mediaSession.setSenderVideoDegradationPreference();\n\n        // Set the max bitrates on video sender if they are specified in config.js videoQuality settings.\n        mediaSession.setSenderMaxBitrates();\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     * @returns {Promise<void[]>}\n     * @private\n     */\n    _propagateSendMaxFrameHeight() {\n        const sendMaxFrameHeight = this.selectSendMaxFrameHeight();\n        const promises = [];\n\n        if (sendMaxFrameHeight >= 0) {\n            for (const session of this.conference._getMediaSessions()) {\n                promises.push(session.setSenderVideoConstraint(sendMaxFrameHeight));\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     * @returns {number|undefined}\n     */\n    selectSendMaxFrameHeight() {\n        const activeMediaSession = this.conference._getActiveMediaSession();\n        const remoteRecvMaxFrameHeight = activeMediaSession && activeMediaSession.getRemoteRecvMaxFrameHeight();\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 receive video frame height.\n     * @param {number|undefined} maxFrameHeight - the new value.\n     */\n    setPreferredReceiveMaxFrameHeight(maxFrameHeight) {\n        this.preferredReceiveMaxFrameHeight = maxFrameHeight;\n\n        for (const session of this.conference._getMediaSessions()) {\n            maxFrameHeight && session.setReceiverVideoConstraint(maxFrameHeight);\n        }\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        return this._propagateSendMaxFrameHeight();\n    }\n}\n","import { getLogger } from 'jitsi-meet-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-meet-logger';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport * as 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","/* global __filename */\nimport { getLogger } from 'jitsi-meet-logger';\nimport isEqual from 'lodash.isequal';\n\nimport * as ConferenceEvents from '../../JitsiConferenceEvents';\nimport * as 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        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     * 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     * @private\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    }\n}\n","const AudioRecorder = require('./audioRecorder');\nconst SphinxService = require(\n    './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\nmodule.exports = Transcriber;\n","import Statistics from '../statistics/statistics';\n\nconst logger = require('jitsi-meet-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","import { getLogger } from 'jitsi-meet-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-meet-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-meet-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","/* globals $ */\n\nimport { getLogger } from 'jitsi-meet-logger';\nimport { $iq } from 'strophe.js';\n\nimport * as 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\n     * video should be returned as a desktop stream. Defaults to false.\n     * @param {Object} [options.iceConfig] - The {@code RTCConfiguration} to use\n     * for the peer connection.\n     * @param {JitsiConnection} [options.jitsiConnection] - The\n     * {@code JitsiConnection} which will be used to fetch TURN credentials for\n     * the P2P connection.\n     * @param {Function} options.onRemoteStream - Callback to invoke when a\n     * remote video stream has been received and converted to a\n     * {@code JitsiLocakTrack}. The {@code JitsiLocakTrack} will be passed in.\n     * @param {Function} options.onSendMessage - Callback to invoke when a\n     * message has to be sent (signaled) out. The arguments passed in are the\n     * 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            iceConfig: jitsiConnection\n                && 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            iceConfig: this._options.iceConfig,\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.newCreateLocalTracks(\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 { getLogger } from 'jitsi-meet-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 { 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.iceConfig] - The {@code RTCConfiguration} to use\n     * for the peer connection.\n     * @param {boolean} [options.isInitiator] - If true, the local client should\n     * send offers. If false, the local client should send answers. Defaults to\n     * false.\n     * @param {Function} options.onRemoteStream - Callback to invoke when a\n     * remote media stream has been received through the peer connection.\n     * @param {string} options.peerJid - The jid of the remote client with which\n     * the peer connection is being establish and which should receive direct\n     * messages regarding peer connection updates.\n     * @param {boolean} [options.receiveVideo] - Whether or not the peer\n     * connection should accept incoming video streams. Defaults to false.\n     * @param {Function} options.onSendMessage - Callback to invoke when a\n     * message has to be sent (signaled) out.\n     */\n    constructor(options = {}) {\n        this._options = {\n            iceConfig: {},\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 iceConfigStub = {\n            iceServers: DEFAULT_STUN_SERVERS,\n            ...this._options.iceConfig\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        };\n\n        /**\n         * A {@code JitsiConference} stub passed to the {@link RTC} module.\n         * @type {Object}\n         */\n        const conferenceStub = {};\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            iceConfigStub, // iceConfig\n            true, // isP2P\n            this._options.isInitiator // isInitiator\n        );\n\n        /**\n         * An additional initialize call is necessary to properly set instance\n         * variable for calling.\n         */\n        peerConnection.initialize(roomStub, this._rtc, 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","export default {\n    error: {\n        BUSY: 'busy',\n        ERROR: 'error',\n        RESOURCE_CONSTRAINT: 'resource-constraint',\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","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() {\n    if (browser.isReactNative()) {\n        return;\n    }\n\n    return new Promise(resolve => {\n        ScriptUtil.loadScript(\n            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();\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","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","/* global\n    __filename\n*/\n\nimport { getLogger } from 'jitsi-meet-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","/* 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';\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 * 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 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 (!mLine.sources) {\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 and append the m-lines to the existing description.\n                if (checkIfMlineForSsrcExists(ssrc, ssrc2group, currentDesc.media)) {\n                    return;\n                }\n\n                // check if there is a m-line that is inactive and is of the same media type\n                const inactiveMid = currentDesc.media\n                    .findIndex(cmLine => cmLine.direction\n                        && cmLine.direction === 'inactive'\n                        && cmLine.type === type);\n\n                if (inactiveMid > -1) {\n                    currentDesc.media[inactiveMid].direction = 'sendonly';\n                    addSourcesToMline(currentDesc.media[inactiveMid], ssrc, ssrc2group, mLine.sources);\n                } else {\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        });\n        session.media = currentDesc ? currentDesc.media : Object.values(media);\n        const mids = [];\n\n        session.media.forEach(mLine => {\n            mids.push(mLine.mid);\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","// 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.\nvar getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\nvar rnds8 = new Uint8Array(16);\nexport default function 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}","/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\nvar byteToHex = [];\n\nfor (var i = 0; i < 256; ++i) {\n  byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction bytesToUuid(buf, offset) {\n  var i = offset || 0;\n  var 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\nexport default bytesToUuid;","import rng from './rng.js';\nimport bytesToUuid from './bytesToUuid.js';\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  var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\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    var start = offset || 0;\n\n    for (var i = 0; i < 16; ++i) {\n      buf[start + i] = rnds[i];\n    }\n\n    return buf;\n  }\n\n  return bytesToUuid(rnds);\n}\n\nexport default v4;","// 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","/* global __filename */\n\nimport Logger from 'jitsi-meet-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 getActiveAudioDevice from './modules/detection/ActiveDeviceDetector';\nimport * as DetectionEvents from './modules/detection/DetectionEvents';\nimport TrackVADEmitter from './modules/detection/TrackVADEmitter';\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 * as MediaType from './service/RTC/MediaType';\nimport Resolutions from './service/RTC/Resolutions';\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_PERMISSION_PROMPT_TIMEOUT = 1000;\n\n/**\n * Gets the next lowest desirable resolution to try for a camera. If the given\n * resolution is already the lowest acceptable resolution, returns {@code null}.\n *\n * @param resolution the current resolution\n * @return the next lowest resolution from the given one, or {@code null} if it\n * is already the lowest acceptable resolution.\n */\nfunction getLowerResolution(resolution) {\n    if (!Resolutions[resolution]) {\n        return null;\n    }\n    const order = Resolutions[resolution].order;\n    let res = null;\n    let resName = null;\n\n    Object.keys(Resolutions).forEach(r => {\n        const value = Resolutions[r];\n\n        if (!res || (res.order < value.order && value.order < order)) {\n            resName = r;\n            res = value;\n        }\n    });\n\n    if (resName === resolution) {\n        resName = null;\n    }\n\n    return resName;\n}\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    },\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        // 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        // Log deployment-specific information, if available. Defined outside\n        // the application by individual deployments\n        const aprops = options.deploymentInfo;\n\n        if (aprops && Object.keys(aprops).length > 0) {\n            const logObject = {};\n\n            for (const attr in aprops) {\n                if (aprops.hasOwnProperty(attr)) {\n                    logObject[attr] = aprops[attr];\n                }\n            }\n\n            logObject.id = 'deployment_info';\n            Statistics.sendLog(JSON.stringify(logObject));\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 {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     * @param {boolean} (firePermissionPromptIsShownEvent) - if event\n     * JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN should be fired\n     * @param originalOptions - internal use only, to be able to store the\n     * originally requested options.\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(\n            options = {}, firePermissionPromptIsShownEvent, originalOptions) {\n        let promiseFulfilled = false;\n\n        if (firePermissionPromptIsShownEvent === true) {\n            window.setTimeout(() => {\n                if (!promiseFulfilled) {\n                    JitsiMediaDevices.emitEvent(\n                        JitsiMediaDevicesEvents.PERMISSION_PROMPT_IS_SHOWN,\n                        browser.getName());\n                }\n            }, USER_MEDIA_PERMISSION_PROMPT_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(options)\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(options)));\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.UNSUPPORTED_RESOLUTION\n                    && !browser.usesNewGumFlow()) {\n                    const oldResolution = options.resolution || '720';\n                    const newResolution = getLowerResolution(oldResolution);\n\n                    if (newResolution !== null) {\n                        options.resolution = newResolution;\n\n                        logger.debug(\n                            'Retry createLocalTracks with resolution',\n                            newResolution);\n\n                        Statistics.sendAnalytics(createGetUserMediaEvent(\n                            'warning',\n                            {\n                                'old_resolution': oldResolution,\n                                'new_resolution': newResolution,\n                                reason: 'unsupported resolution'\n                            }));\n\n                        return this.createLocalTracks(\n                            options,\n                            undefined,\n                            originalOptions || Object.assign({}, options));\n                    }\n\n                    // We tried everything. If there is a mandatory device id,\n                    // remove it and let gum find a device to use.\n                    if (originalOptions\n                        && error.gum.constraints\n                        && error.gum.constraints.video\n                        && error.gum.constraints.video.mandatory\n                        && error.gum.constraints.video.mandatory.sourceId) {\n                        originalOptions.cameraDeviceId = undefined;\n\n                        return this.createLocalTracks(originalOptions);\n                    }\n                }\n\n                if (error.name\n                        === 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","/* 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","/* globals __webpack_amd_options__ */\nmodule.exports = __webpack_amd_options__;\n","/*!\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","const Constants = {\n    LOCAL_JID: 'local'\n};\n\nmodule.exports = Constants;\n","import { $iq, Strophe } from 'strophe.js';\n\nStrophe.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.NS.DISCO_INFO, 'iq', 'get', null, null);\n        // disco items\n        conn.addHandler(this._onDiscoItems.bind(this), 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.NS.DISCO_INFO};\n        if (node)\n            attrs.node = node;\n\n        var info = $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.NS.DISCO_ITEMS};\n        if (node)\n            attrs.node = node;\n\n        var items = $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 = $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.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.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","import { $build, Strophe } from 'strophe.js';\n\n/**\n* StropheJS - Stream Management XEP-0198\n*\n* This plugin implements stream management ACK capabilities of the specs XEP-0198.\n* Note: Resumption is not supported in this current implementation.\n*\n* Reference: http://xmpp.org/extensions/xep-0198.html\n*\n* @class streamManagement\n*/\nStrophe.addConnectionPlugin('streamManagement', {\n\n\t/**\n\t* @property {Boolean} logging: Set to true to enable logging regarding out of sync stanzas.\n\t*/\n\tlogging: false,\n\n\t/**\n\t* @property {Boolean} autoSendCountOnEveryIncomingStanza: Set to true to send an 'a' response after every stanza.\n\t* @default false\n\t* @public\n\t*/\n\tautoSendCountOnEveryIncomingStanza: false,\n\n\t/**\n\t* @property {Integer} requestResponseInterval: Set this value to send a request for counter on very interval\n\t* number of stanzas sent. Set to 0 to disable.\n\t* @default 5\n\t* @public\n\t*/\n\trequestResponseInterval: 5,\n\n\t/**\n\t* @property {Pointer} _c: Strophe connection instance.\n\t* @private\n\t*/\n\t_c: null,\n\n\t/**\n\t* @property {String} _NS XMPP Namespace.\n\t* @private\n\t*/\n\t_NS: 'urn:xmpp:sm:3',\n\n\t/**\n\t* @property {Boolean} _isStreamManagementEnabled\n\t* @private\n\t*/\n\t_isStreamManagementEnabled: false,\n\n\t/**\n\t* @property {Integer} _serverProcesssedStanzasCounter: Keeps count of stanzas confirmed processed by the server.\n\t* The server is the source of truth of this value. It is the 'h' attribute on the latest 'a' element received\n\t* from the server.\n\t* @private\n\t*/\n\t_serverProcesssedStanzasCounter: null,\n\n\t/**\n\t* @property {Integer} _clientProcessedStanzasCounter: Counter of stanzas received by the client from the server.\n\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* the server.\n\t* @private\n\t*/\n\t_clientProcessedStanzasCounter: null,\n\n\t/**\n\t* @property {Integer} _clientSentStanzasCounter\n\t* @private\n\t*/\n\t_clientSentStanzasCounter: null,\n\n\t/**\n\t* Stores a reference to Strophe connection xmlOutput function to wrap counting functionality.\n\t* @method _originalXMLOutput\n\t* @type {Handler}\n\t* @private\n\t*/\n\t_originalXMLOutput: null,\n\n\t/**\n\t* @property {Handler} _requestHandler: Stores reference to handler that process count request from server.\n\t* @private\n\t*/\n\t_requestHandler: null,\n\n\t/**\n\t* @property {Handler} _incomingHandler: Stores reference to handler that processes incoming stanzas count.\n\t* @private\n\t*/\n\t_incomingHandler: null,\n\n\t/**\n\t* @property {Integer} _requestResponseIntervalCount: Counts sent stanzas since last response request.\n\t*/\n\t_requestResponseIntervalCount: 0,\n\n\t/**\n\t * @property {boolean} _isSupported: indicates whether or not the server has advertised support for the stream\n\t * management namespace.\n\t */\n\t_isSupported: false,\n\n\t/**\n\t* @property {Queue} _unacknowledgedStanzas: Maintains a list of packet ids for stanzas which have yet to be acknowledged.\n\t*/\n\t_unacknowledgedStanzas: [],\n\n\t/**\n\t* @property {Array} _acknowledgedStanzaListeners: Stores callbacks for each stanza acknowledged by the server.\n\t* Provides the packet id of the stanza as a parameter.\n\t* @private\n\t*/\n\t_acknowledgedStanzaListeners: [],\n\n\taddAcknowledgedStanzaListener: function(listener) {\n\t\tthis._acknowledgedStanzaListeners.push(listener);\n\t},\n\n\tenable: function(resume) {\n\t\tif (!this._isSupported) {\n\t\t\tthrow new Error('The server doesn\\'t support urn:xmpp:sm:3 namespace');\n\t\t} else if (this._connectionStatus !== Strophe.Status.CONNECTED) {\n\t\t\tthrow new Error('enable() can only be called in the CONNECTED state');\n\t\t}\n\t\tthis._c.send($build('enable', { xmlns: this._NS, resume }));\n\t\tthis._c.flush();\n\t\tthis._c.pause();\n\t},\n\n\tgetResumeToken: function() {\n\t\treturn this._resumeToken;\n\t},\n\n\tisSupported() {\n\t\treturn this._isSupported;\n\t},\n\n\tresume: function() {\n\t\tif (!this.getResumeToken()) {\n\t\t\tthrow new Error('No resume token');\n\t\t}\n\t\tif (this._connectionStatus !== Strophe.Status.DISCONNECTED) {\n\t\t\tthrow new Error('resume() can only be called in the DISCONNECTED state');\n\t\t}\n\n\t\tthis._c.options.explicitResourceBinding = true;\n\t\tthis._resuming = true;\n\n\t\tthis._originalConnect.apply(this._c, this._connectArgs);\n\t},\n\n\trequestAcknowledgement: function() {\n\t\tif (this._connectionStatus !== Strophe.Status.CONNECTED) {\n\t\t\tthrow new Error('requestAcknowledgement() can only be called in the CONNECTED state');\n\t\t}\n\t\tthis._requestResponseIntervalCount = 0;\n\t\tthis._c.send($build('r', { xmlns: this._NS }));\n\t},\n\n\tgetOutgoingCounter: function() {\n\t\treturn this._clientSentStanzasCounter;\n\t},\n\n\tgetIncomingCounter: function() {\n\t\treturn this._clientProcessedStanzasCounter;\n\t},\n\n\tinit: function(conn) {\n\t\tthis._c = conn;\n\t\tStrophe.addNamespace('SM', this._NS);\n\n\t\t// Storing original xmlOutput function to use additional logic\n\t\tthis._originalXMLOutput = this._c.xmlOutput;\n\t\tthis._c.xmlOutput = this.xmlOutput.bind(this);\n\n\t\tthis._originalConnect = this._c.connect;\n\t\tthis._c.connect = this._interceptConnectArgs.bind(this);\n\n\t\tthis._originalOnStreamFeaturesAfterSASL = this._c._onStreamFeaturesAfterSASL;\n\t\tthis._c._onStreamFeaturesAfterSASL = this._onStreamFeaturesAfterSASL.bind(this);\n\n\t\tthis._originalDoDisconnect = this._c._doDisconnect;\n\t\tthis._c._doDisconnect = this._interceptDoDisconnect.bind(this);\n\n\t\tthis._originalDisconnect = this._c.disconnect;\n\t\tthis._c.disconnect = this._interceptDisconnect.bind(this);\n\t},\n\n\t_interceptDisconnect: function() {\n\t\tthis._resumeToken = undefined;\n\t\tthis._originalDisconnect.apply(this._c, arguments);\n\t},\n\n\t_interceptDoDisconnect: function() {\n\t\tif (this.getResumeToken()\n\t\t\t\t&& !this._resuming\n\t\t\t\t&& this._c.connected && !this._c.disconnecting) {\n\t\t\tthis._resumeState = {\n\t\t\t\thandlers: this._c.handlers,\n\t\t\t\ttimedHandlers: this._c.timedHandlers,\n\t\t\t\tremoveTimeds: this._c.removeTimeds,\n\t\t\t\tremoveHandlers: this._c.removeHandlers,\n\t\t\t\taddTimeds: this._c.addTimeds,\n\t\t\t\taddHandlers: this._c.addHandlers\n\t\t\t};\n\t\t\tthis._storedJid = this._c.jid;\n\n\t\t\tthis.logging && Strophe.debug('SM stored resume state, handler count: ' + this._resumeState.handlers.length);\n\t\t}\n\n\t\t// Remove any queued stanzas from the buffer that have failed to send while the socket was closed,\n\t\t// as they would interfere with the resume flow. They will be resent anyway.\n\t\tthis._c._data = [];\n\n\t\tthis._originalDoDisconnect.apply(this._c, arguments);\n\t},\n\n\t_interceptConnectArgs: function() {\n\t\tthis._connectArgs = arguments;\n\n\t\tthis._originalConnect.apply(this._c, arguments);\n\t},\n\n\t_onStreamFeaturesAfterSASL: function(elem) {\n\t\tthis._isSupported = elem.getElementsByTagNameNS(this._NS, \"sm\").length > 0;\n\n\t\treturn this._originalOnStreamFeaturesAfterSASL.apply(this._c, arguments);\n\t},\n\n\tstatusChanged: function (status) {\n\t\tthis._connectionStatus = status;\n\t\tif (!this.getResumeToken()\n\t\t\t&& (status === Strophe.Status.CONNECTED || status === Strophe.Status.DISCONNECTED)) {\n\t\t\tthis.logging && Strophe.debug('SM reset state');\n\n\t\t\tthis._serverProcesssedStanzasCounter = 0;\n\t\t\tthis._clientProcessedStanzasCounter = 0;\n\n\t\t\tthis._clientSentStanzasCounter = 0;\n\n\t\t\tthis._isStreamManagementEnabled = false;\n\t\t\tthis._requestResponseIntervalCount = 0;\n\n\t\t\t// FIXME not described in JSDocs\n\t\t\tthis._resuming = false;\n\n\t\t\tif (status === Strophe.Status.DISCONNECTED) {\n\t\t\t\tthis._isSupported = false;\n\t\t\t}\n\n\t\t\tthis._unacknowledgedStanzas = [];\n\n\t\t\tif (this._requestHandler) {\n\t\t\t\tthis._c.deleteHandler(this._requestHandler);\n\t\t\t}\n\n\t\t\tif (this._incomingHandler) {\n\t\t\t\tthis._c.deleteHandler(this._incomingHandler);\n\t\t\t}\n\n\t\t\tthis._requestHandler = this._c.addHandler(this._handleServerRequestHandler.bind(this), this._NS, 'r');\n\t\t\tthis._ackHandler = this._c.addHandler(this._handleServerAck.bind(this), this._NS, 'a');\n\t\t\tthis._incomingHandler = this._c.addHandler(this._incomingStanzaHandler.bind(this));\n\n\t\t\t// FIXME handler instances stored, but never used\n\t\t\tthis._enabledHandler = this._c._addSysHandler(this._handleEnabled.bind(this), this._NS, 'enabled');\n\t\t\tthis._resumeFailedHandler = this._c._addSysHandler(this._handleResumeFailed.bind(this), this._NS, 'failed');\n\t\t\tthis._resumedHandler =  this._c._addSysHandler(this._handleResumed.bind(this), this._NS,'resumed');\n\n\t\t} else if (status === Strophe.Status.BINDREQUIRED)  {\n\t\t\tthis._c.jid = this._storedJid;\n\n\t\t\t// Restore Strophe handlers\n\t\t\tfor (const property in this._resumeState) {\n\t\t\t\tthis._c[property] = this._resumeState[property];\n\t\t\t}\n\n\t\t\t// FIXME check conditions if there's session ID and if enabled\n\t\t\tthis._c.send($build('resume', {\n\t\t\t\txmlns: this._NS,\n\t\t\t\th: this._clientProcessedStanzasCounter,\n\t\t\t\tprevid: this._resumeToken\n\t\t\t}));\n\t\t\tthis._c.flush();\n\t\t} else if (status === Strophe.Status.ERROR) {\n\t\t\tthis.logging && Strophe.debug('SM cleared resume token on error');\n\t\t\tthis._resumeToken = undefined;\n\t\t}\n\t},\n\n\t/**\n\t* This method overrides the send method implemented by Strophe.Connection\n\t* to count outgoing stanzas\n\t*\n\t* @method Send\n\t* @public\n\t*/\n\txmlOutput: function(elem) {\n\t\tif (Strophe.isTagEqual(elem, 'iq') ||\n\t\t\tStrophe.isTagEqual(elem, 'presence') ||\n\t\t\tStrophe.isTagEqual(elem, 'message')) {\n\t\t\tthis._increaseSentStanzasCounter(elem);\n\t\t}\n\n\t\treturn this._originalXMLOutput.call(this._c, elem);\n\t},\n\n\t_handleEnabled: function(elem) {\n\t\tthis._isStreamManagementEnabled = true;\n\t\t// FIXME fail if requested, but not enabled\n\t\tthis._resumeToken = elem.getAttribute('resume') === 'true' && elem.getAttribute('id');\n\n\t\tthis._c.resume();\n\n\t\treturn true;\n\t},\n\n\t_handleResumeFailed: function(elem) {\n\t\tconst error = elem && elem.firstElementChild && elem.firstElementChild.tagName;\n\n\t\tthis._c._changeConnectStatus(Strophe.Status.ERROR, error, elem);\n\t\tthis._c._doDisconnect();\n\n\t\treturn true;\n\t},\n\n\t_handleResumed: function(elem) {\n\t\t// FIXME check if in the correct state\n\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\tthis._resuming = false;\n\t\tthis._c.do_bind = false; // No need to bind our resource anymore\n\t\tthis._c.authenticated = true;\n\t\tthis._c.restored = true;\n\n\t\tif (this._unacknowledgedStanzas.length > 0) {\n\t\t\tthis.logging && Strophe.debug('SM Sending unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t\tfor(const stanza of this._unacknowledgedStanzas) {\n\t\t\t\tthis._c.send(stanza);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.logging && Strophe.debug('SM No unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t}\n\n\t\tthis._c._changeConnectStatus(Strophe.Status.CONNECTED, null);\n\n\t\treturn true;\n\t},\n\n\t_incomingStanzaHandler: function(elem) {\n\t\tif (Strophe.isTagEqual(elem, 'iq') || Strophe.isTagEqual(elem, 'presence') || Strophe.isTagEqual(elem, 'message'))  {\n\t\t\tthis._increaseReceivedStanzasCounter();\n\n\t\t\tif (this.autoSendCountOnEveryIncomingStanza) {\n\t\t\t\tthis._answerProcessedStanzas();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t},\n\n\t_handleAcknowledgedStanzas: function(reportedHandledCount, lastKnownHandledCount) {\n\t\tvar delta = reportedHandledCount - lastKnownHandledCount;\n\n\t\tif (delta < 0) {\n\t\t\tthis._throwError('New reported stanza count lower than previous. New: ' + reportedHandledCount + ' - Previous: ' + lastKnownHandledCount);\n\t\t}\n\n\t\tif (delta > this._unacknowledgedStanzas.length) {\n\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}\n\n\t\tfor(var i = 0; i < delta; i++) {\n\t\t\tvar stanza = this._unacknowledgedStanzas.shift();\n\t\t\tfor (var j = 0; j < this._acknowledgedStanzaListeners.length; j++) {\n\t\t\t\tthis._acknowledgedStanzaListeners[j](stanza);\n\t\t\t}\n\t\t}\n\n\t\tif (this.logging && this._unacknowledgedStanzas.length > 0) {\n\t\t\tStrophe.warn('SM Unacknowledged stanzas', this._unacknowledgedStanzas);\n\t\t}\n\n\t\tthis._serverProcesssedStanzasCounter = reportedHandledCount;\n\n\t\tif (this.requestResponseInterval > 0) {\n\t\t\tthis._requestResponseIntervalCount = 0;\n\t\t}\n\t},\n\n\t_handleServerRequestHandler: function() {\n\t\tthis._answerProcessedStanzas();\n\n\t\treturn true;\n\t},\n\n\t_handleServerAck: function(elem){\n\t\tvar handledCount = parseInt(elem.getAttribute('h'));\n\t\tthis._handleAcknowledgedStanzas(handledCount, this._serverProcesssedStanzasCounter);\n\n\t\treturn true;\n\t},\n\n\t_answerProcessedStanzas: function() {\n\t\tif (this._isStreamManagementEnabled) {\n\t\t\tthis._c.send($build('a', { xmlns: this._NS, h: this._clientProcessedStanzasCounter }));\n\t\t}\n\t},\n\n\t_increaseSentStanzasCounter: function(elem) {\n\t\tif (this._isStreamManagementEnabled) {\n\t\t\tif (this._unacknowledgedStanzas.indexOf(elem) !== -1) {\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis._unacknowledgedStanzas.push(elem);\n\t\t\tthis._clientSentStanzasCounter++;\n\n\t\t\tif (this.requestResponseInterval > 0) {\n\t\t\t\tthis._requestResponseIntervalCount++;\n\n\t\t\t\tif (this._requestResponseIntervalCount === this.requestResponseInterval) {\n\t\t\t\t\t// FIXME Can not call send from onIdle.\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tif (this._connectionStatus === Strophe.Status.CONNECTED) {\n\t\t\t\t\t\t\tthis.requestAcknowledgement();\n\t\t\t\t\t\t}\n\t\t\t\t\t}, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t_increaseReceivedStanzasCounter: function() {\n\t\tif (this._isStreamManagementEnabled) {\n\t\t\tthis._clientProcessedStanzasCounter++;\n\t\t}\n\t},\n\n\t_throwError: function(msg) {\n\t\tStrophe.error(msg);\n\t\tthrow new Error(msg);\n\t}\n\n});\n","var scope = (typeof global !== \"undefined\" && global) ||\n            (typeof self !== \"undefined\" && self) ||\n            window;\nvar apply = Function.prototype.apply;\n\n// DOM APIs, for completeness\n\nexports.setTimeout = function() {\n  return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);\n};\nexports.setInterval = function() {\n  return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);\n};\nexports.clearTimeout =\nexports.clearInterval = function(timeout) {\n  if (timeout) {\n    timeout.close();\n  }\n};\n\nfunction Timeout(id, clearFn) {\n  this._id = id;\n  this._clearFn = clearFn;\n}\nTimeout.prototype.unref = Timeout.prototype.ref = function() {};\nTimeout.prototype.close = function() {\n  this._clearFn.call(scope, this._id);\n};\n\n// Does not start the time, just sets up the members needed.\nexports.enroll = function(item, msecs) {\n  clearTimeout(item._idleTimeoutId);\n  item._idleTimeout = msecs;\n};\n\nexports.unenroll = function(item) {\n  clearTimeout(item._idleTimeoutId);\n  item._idleTimeout = -1;\n};\n\nexports._unrefActive = exports.active = function(item) {\n  clearTimeout(item._idleTimeoutId);\n\n  var msecs = item._idleTimeout;\n  if (msecs >= 0) {\n    item._idleTimeoutId = setTimeout(function onTimeout() {\n      if (item._onTimeout)\n        item._onTimeout();\n    }, msecs);\n  }\n};\n\n// setimmediate attaches itself to the global object\nrequire(\"setimmediate\");\n// On some exotic environments, it's not clear which object `setimmediate` was\n// able to install onto.  Search each possibility in the same order as the\n// `setimmediate` library.\nexports.setImmediate = (typeof self !== \"undefined\" && self.setImmediate) ||\n                       (typeof global !== \"undefined\" && global.setImmediate) ||\n                       (this && this.setImmediate);\nexports.clearImmediate = (typeof self !== \"undefined\" && self.clearImmediate) ||\n                         (typeof global !== \"undefined\" && global.clearImmediate) ||\n                         (this && this.clearImmediate);\n","(function (global, undefined) {\n    \"use strict\";\n\n    if (global.setImmediate) {\n        return;\n    }\n\n    var nextHandle = 1; // Spec says greater than zero\n    var tasksByHandle = {};\n    var currentlyRunningATask = false;\n    var doc = global.document;\n    var registerImmediate;\n\n    function setImmediate(callback) {\n      // Callback can either be a function or a string\n      if (typeof callback !== \"function\") {\n        callback = new Function(\"\" + callback);\n      }\n      // Copy function arguments\n      var args = new Array(arguments.length - 1);\n      for (var i = 0; i < args.length; i++) {\n          args[i] = arguments[i + 1];\n      }\n      // Store and register the task\n      var task = { callback: callback, args: args };\n      tasksByHandle[nextHandle] = task;\n      registerImmediate(nextHandle);\n      return nextHandle++;\n    }\n\n    function clearImmediate(handle) {\n        delete tasksByHandle[handle];\n    }\n\n    function run(task) {\n        var callback = task.callback;\n        var args = task.args;\n        switch (args.length) {\n        case 0:\n            callback();\n            break;\n        case 1:\n            callback(args[0]);\n            break;\n        case 2:\n            callback(args[0], args[1]);\n            break;\n        case 3:\n            callback(args[0], args[1], args[2]);\n            break;\n        default:\n            callback.apply(undefined, args);\n            break;\n        }\n    }\n\n    function runIfPresent(handle) {\n        // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n        // So if we're currently running a task, we'll need to delay this invocation.\n        if (currentlyRunningATask) {\n            // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n            // \"too much recursion\" error.\n            setTimeout(runIfPresent, 0, handle);\n        } else {\n            var task = tasksByHandle[handle];\n            if (task) {\n                currentlyRunningATask = true;\n                try {\n                    run(task);\n                } finally {\n                    clearImmediate(handle);\n                    currentlyRunningATask = false;\n                }\n            }\n        }\n    }\n\n    function installNextTickImplementation() {\n        registerImmediate = function(handle) {\n            process.nextTick(function () { runIfPresent(handle); });\n        };\n    }\n\n    function canUsePostMessage() {\n        // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n        // where `global.postMessage` means something completely different and can't be used for this purpose.\n        if (global.postMessage && !global.importScripts) {\n            var postMessageIsAsynchronous = true;\n            var oldOnMessage = global.onmessage;\n            global.onmessage = function() {\n                postMessageIsAsynchronous = false;\n            };\n            global.postMessage(\"\", \"*\");\n            global.onmessage = oldOnMessage;\n            return postMessageIsAsynchronous;\n        }\n    }\n\n    function installPostMessageImplementation() {\n        // Installs an event handler on `global` for the `message` event: see\n        // * https://developer.mozilla.org/en/DOM/window.postMessage\n        // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n        var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n        var onGlobalMessage = function(event) {\n            if (event.source === global &&\n                typeof event.data === \"string\" &&\n                event.data.indexOf(messagePrefix) === 0) {\n                runIfPresent(+event.data.slice(messagePrefix.length));\n            }\n        };\n\n        if (global.addEventListener) {\n            global.addEventListener(\"message\", onGlobalMessage, false);\n        } else {\n            global.attachEvent(\"onmessage\", onGlobalMessage);\n        }\n\n        registerImmediate = function(handle) {\n            global.postMessage(messagePrefix + handle, \"*\");\n        };\n    }\n\n    function installMessageChannelImplementation() {\n        var channel = new MessageChannel();\n        channel.port1.onmessage = function(event) {\n            var handle = event.data;\n            runIfPresent(handle);\n        };\n\n        registerImmediate = function(handle) {\n            channel.port2.postMessage(handle);\n        };\n    }\n\n    function installReadyStateChangeImplementation() {\n        var html = doc.documentElement;\n        registerImmediate = function(handle) {\n            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted\n            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.\n            var script = doc.createElement(\"script\");\n            script.onreadystatechange = function () {\n                runIfPresent(handle);\n                script.onreadystatechange = null;\n                html.removeChild(script);\n                script = null;\n            };\n            html.appendChild(script);\n        };\n    }\n\n    function installSetTimeoutImplementation() {\n        registerImmediate = function(handle) {\n            setTimeout(runIfPresent, 0, handle);\n        };\n    }\n\n    // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.\n    var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);\n    attachTo = attachTo && attachTo.setTimeout ? attachTo : global;\n\n    // Don't get fooled by e.g. browserify environments.\n    if ({}.toString.call(global.process) === \"[object process]\") {\n        // For Node.js before 0.9\n        installNextTickImplementation();\n\n    } else if (canUsePostMessage()) {\n        // For non-IE10 modern browsers\n        installPostMessageImplementation();\n\n    } else if (global.MessageChannel) {\n        // For web workers, where supported\n        installMessageChannelImplementation();\n\n    } else if (doc && \"onreadystatechange\" in doc.createElement(\"script\")) {\n        // For IE 6–8\n        installReadyStateChangeImplementation();\n\n    } else {\n        // For older browsers\n        installSetTimeoutImplementation();\n    }\n\n    attachTo.setImmediate = setImmediate;\n    attachTo.clearImmediate = clearImmediate;\n}(typeof self === \"undefined\" ? typeof global === \"undefined\" ? this : global : self));\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","/* 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","/* 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 * @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\n *                  objects\n */\nconst RecordingResult = function(blob, name, startTime, wordArray) {\n    this.blob = blob;\n    this.name = name;\n    this.startTime = startTime;\n    this.wordArray = wordArray;\n};\n\n/* eslint-enable max-params */\n\nmodule.exports = RecordingResult;\n","/* global config */\n\nconst Word = require('../word');\n\nconst audioRecorder = require('./../audioRecorder');\nconst TranscriptionService = require('./AbstractTranscriptionService');\n\n/**\n * Implements a TranscriptionService for a Sphinx4 http server\n */\nconst SphinxService = function() {\n    // set the correct url\n    this.url = getURL();\n};\n\n/**\n * Subclass of AbstractTranscriptionService\n */\nSphinxService.prototype = Object.create(TranscriptionService.prototype);\n\n/**\n * Set the right constructor\n */\nSphinxService.constructor = SphinxService;\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 */\nSphinxService.prototype.sendRequest = function(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: ${\n                    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 */\nSphinxService.prototype.formatResponse = function(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\n    result.shift();\n    const array = [];\n\n    result.forEach(\n        word =>\n            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 */\nSphinxService.prototype.verify = function(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 * 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\nmodule.exports = SphinxService;\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 */\nconst Word = function(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 */\nWord.prototype.getWord = function() {\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 */\nWord.prototype.getBeginTime = function() {\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 */\nWord.prototype.getEndTime = function() {\n    return this.end;\n};\n\nmodule.exports = Word;\n","/**\n * Abstract class representing an interface to implement a speech-to-text\n * service on.\n */\nconst TranscriptionService = function() {\n    throw new Error('TranscriptionService is abstract and cannot be'\n        + '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 */\nTranscriptionService.prototype.send = function 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// eslint-disable-next-line no-unused-vars\nTranscriptionService.prototype.sendRequest = function(audioBlob, callback) {\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// eslint-disable-next-line no-unused-vars\nTranscriptionService.prototype.formatResponse = function(response) {\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// eslint-disable-next-line no-unused-vars\nTranscriptionService.prototype.verify = function(response) {\n    throw new Error('TranscriptionService.verify is abstract');\n};\n\nmodule.exports = TranscriptionService;\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\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        cb(modifiedEvent);\n      }\n    };\n    this._eventMap = this._eventMap || {};\n    this._eventMap[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[cb]) {\n      return nativeRemoveEventListener.apply(this, arguments);\n    }\n    const unwrappedCb = this._eventMap[cb];\n    delete this._eventMap[cb];\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  const {navigator} = window;\n\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  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 (navigator.mediaDevices &&\n      navigator.userAgent.match(/Edge\\/(\\d+).(\\d+)$/)) { // Edge.\n    result.browser = 'edge';\n    result.version = extractVersion(navigator.userAgent,\n        /Edge\\/(\\d+).(\\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) {\n  const navigator = window && window.navigator;\n\n  if (!navigator.mediaDevices) {\n    return;\n  }\n\n  const browserDetails = utils.detectBrowser(window);\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/*\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) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  const browserDetails = utils.detectBrowser(window);\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) {\n  const browserDetails = utils.detectBrowser(window);\n\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  const addIceCandidateNullSupported =\n    window.RTCPeerConnection.prototype.addIceCandidate.length === 0;\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  // support for addIceCandidate(null or undefined)\n  const nativeAddIceCandidate =\n      window.RTCPeerConnection.prototype.addIceCandidate;\n  window.RTCPeerConnection.prototype.addIceCandidate =\n    function addIceCandidate() {\n      if (!addIceCandidateNullSupported && !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. Native support planned for Chrome M77.\n      if (browserDetails.version < 78 &&\n        arguments[0] && arguments[0].candidate === '') {\n        return Promise.resolve();\n      }\n      return nativeAddIceCandidate.apply(this, arguments);\n    };\n}\n\nexport function fixNegotiationNeeded(window) {\n  utils.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {\n    const pc = e.target;\n    if (pc.signalingState !== 'stable') {\n      return;\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\nexport function shimGetUserMedia(window) {\n  const navigator = window && window.navigator;\n\n  const shimError_ = function(e) {\n    return {\n      name: {PermissionDeniedError: 'NotAllowedError'}[e.name] || e.name,\n      message: e.message,\n      constraint: e.constraint,\n      toString() {\n        return this.name;\n      }\n    };\n  };\n\n  // getUserMedia error shim.\n  const origGetUserMedia = navigator.mediaDevices.getUserMedia.\n      bind(navigator.mediaDevices);\n  navigator.mediaDevices.getUserMedia = function(c) {\n    return origGetUserMedia(c).catch(e => Promise.reject(shimError_(e)));\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) {\n  if (!('getDisplayMedia' in window.navigator)) {\n    return;\n  }\n  if (!(window.navigator.mediaDevices)) {\n    return;\n  }\n  if (window.navigator.mediaDevices &&\n    'getDisplayMedia' in window.navigator.mediaDevices) {\n    return;\n  }\n  window.navigator.mediaDevices.getDisplayMedia =\n    window.navigator.getDisplayMedia.bind(window.navigator);\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';\nimport {filterIceServers} from './filtericeservers';\nimport shimRTCPeerConnection from 'rtcpeerconnection-shim';\n\nexport {shimGetUserMedia} from './getusermedia';\nexport {shimGetDisplayMedia} from './getdisplaymedia';\n\nexport function shimPeerConnection(window) {\n  const browserDetails = utils.detectBrowser(window);\n\n  if (window.RTCIceGatherer) {\n    if (!window.RTCIceCandidate) {\n      window.RTCIceCandidate = function RTCIceCandidate(args) {\n        return args;\n      };\n    }\n    if (!window.RTCSessionDescription) {\n      window.RTCSessionDescription = function RTCSessionDescription(args) {\n        return args;\n      };\n    }\n    // this adds an additional event listener to MediaStrackTrack that signals\n    // when a tracks enabled property was changed. Workaround for a bug in\n    // addStream, see below. No longer required in 15025+\n    if (browserDetails.version < 15025) {\n      const origMSTEnabled = Object.getOwnPropertyDescriptor(\n          window.MediaStreamTrack.prototype, 'enabled');\n      Object.defineProperty(window.MediaStreamTrack.prototype, 'enabled', {\n        set(value) {\n          origMSTEnabled.set.call(this, value);\n          const ev = new Event('enabled');\n          ev.enabled = value;\n          this.dispatchEvent(ev);\n        }\n      });\n    }\n  }\n\n  // ORTC defines the DTMF sender a bit different.\n  // https://github.com/w3c/ortc/issues/714\n  if (window.RTCRtpSender && !('dtmf' in window.RTCRtpSender.prototype)) {\n    Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {\n      get() {\n        if (this._dtmf === undefined) {\n          if (this.track.kind === 'audio') {\n            this._dtmf = new window.RTCDtmfSender(this);\n          } else if (this.track.kind === 'video') {\n            this._dtmf = null;\n          }\n        }\n        return this._dtmf;\n      }\n    });\n  }\n  // Edge currently only implements the RTCDtmfSender, not the\n  // RTCDTMFSender alias. See http://draft.ortc.org/#rtcdtmfsender2*\n  if (window.RTCDtmfSender && !window.RTCDTMFSender) {\n    window.RTCDTMFSender = window.RTCDtmfSender;\n  }\n\n  const RTCPeerConnectionShim = shimRTCPeerConnection(window,\n      browserDetails.version);\n  window.RTCPeerConnection = function RTCPeerConnection(config) {\n    if (config && config.iceServers) {\n      config.iceServers = filterIceServers(config.iceServers,\n        browserDetails.version);\n      utils.log('ICE servers after filtering:', config.iceServers);\n    }\n    return new RTCPeerConnectionShim(config);\n  };\n  window.RTCPeerConnection.prototype = RTCPeerConnectionShim.prototype;\n}\n\nexport function shimReplaceTrack(window) {\n  // ORTC has replaceTrack -- https://github.com/w3c/ortc/issues/614\n  if (window.RTCRtpSender &&\n      !('replaceTrack' in window.RTCRtpSender.prototype)) {\n    window.RTCRtpSender.prototype.replaceTrack =\n        window.RTCRtpSender.prototype.setTrack;\n  }\n}\n","/*\n *  Copyright (c) 2018 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// Edge does not like\n// 1) stun: filtered after 14393 unless ?transport=udp is present\n// 2) turn: that does not have all of turn:host:port?transport=udp\n// 3) turn: with ipv6 addresses\n// 4) turn: occurring muliple times\nexport function filterIceServers(iceServers, edgeVersion) {\n  let hasTurn = false;\n  iceServers = JSON.parse(JSON.stringify(iceServers));\n  return iceServers.filter(server => {\n    if (server && (server.urls || server.url)) {\n      var urls = server.urls || server.url;\n      if (server.url && !server.urls) {\n        utils.deprecated('RTCIceServer.url', 'RTCIceServer.urls');\n      }\n      const isString = typeof urls === 'string';\n      if (isString) {\n        urls = [urls];\n      }\n      urls = urls.filter(url => {\n        // filter STUN unconditionally.\n        if (url.indexOf('stun:') === 0) {\n          return false;\n        }\n\n        const validTurn = url.startsWith('turn') &&\n            !url.startsWith('turn:[') &&\n            url.includes('transport=udp');\n        if (validTurn && !hasTurn) {\n          hasTurn = true;\n          return true;\n        }\n        return validTurn && !hasTurn;\n      });\n\n      delete server.url;\n      server.urls = isString ? urls[0] : urls;\n      return !!urls.length;\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';\n\nimport * as utils from '../utils';\n\nexport function shimGetUserMedia(window) {\n  const browserDetails = utils.detectBrowser(window);\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) {\n  const browserDetails = utils.detectBrowser(window);\n\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  // support for addIceCandidate(null or undefined)\n  // as well as ignoring {sdpMid, candidate: \"\"}\n  if (browserDetails.version < 68) {\n    const nativeAddIceCandidate =\n        window.RTCPeerConnection.prototype.addIceCandidate;\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      if (arguments[0] && arguments[0].candidate === '') {\n        return Promise.resolve();\n      }\n      return nativeAddIceCandidate.apply(this, arguments);\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            params.encodings = initParameters.sendEncodings;\n            this.setParametersPromises.push(\n              sender.setParameters(params)\n              .catch(() => {})\n            );\n          }\n        }\n        return transceiver;\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 *  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) {\n        const stream = arguments[1];\n        if (stream) {\n          if (!this._localStreams) {\n            this._localStreams = [stream];\n          } else if (!this._localStreams.includes(stream)) {\n            this._localStreams.push(stream);\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  // 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 window.RTCPeerConnection) {\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","/*\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) {\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  const browserDetails = utils.detectBrowser(window);\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 removeAllowExtmapMixed(window) {\n  /* remove a=extmap-allow-mixed for Chrome < M71 */\n  if (!window.RTCPeerConnection) {\n    return;\n  }\n  const browserDetails = utils.detectBrowser(window);\n  if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {\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      desc.sdp = desc.sdp.split('\\n').filter((line) => {\n        return line.trim() !== 'a=extmap-allow-mixed';\n      }).join('\\n');\n    }\n    return nativeSRD.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 = adapterFactory({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 edgeShim from './edge/edge_shim';\nimport * as firefoxShim from './firefox/firefox_shim';\nimport * as safariShim from './safari/safari_shim';\nimport * as commonShim from './common_shim';\n\n// Shimming starts here.\nexport function adapterFactory({window} = {}, options = {\n  shimChrome: true,\n  shimFirefox: true,\n  shimEdge: 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  };\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      logging('adapter.js shimming chrome.');\n      // Export to the adapter global object visible in the browser.\n      adapter.browserShim = chromeShim;\n\n      chromeShim.shimGetUserMedia(window);\n      chromeShim.shimMediaStream(window);\n      chromeShim.shimPeerConnection(window);\n      chromeShim.shimOnTrack(window);\n      chromeShim.shimAddTrackRemoveTrack(window);\n      chromeShim.shimGetSendersWithDtmf(window);\n      chromeShim.shimGetStats(window);\n      chromeShim.shimSenderReceiverGetStats(window);\n      chromeShim.fixNegotiationNeeded(window);\n\n      commonShim.shimRTCIceCandidate(window);\n      commonShim.shimConnectionState(window);\n      commonShim.shimMaxMessageSize(window);\n      commonShim.shimSendThrowTypeError(window);\n      commonShim.removeAllowExtmapMixed(window);\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      firefoxShim.shimGetUserMedia(window);\n      firefoxShim.shimPeerConnection(window);\n      firefoxShim.shimOnTrack(window);\n      firefoxShim.shimRemoveStream(window);\n      firefoxShim.shimSenderGetStats(window);\n      firefoxShim.shimReceiverGetStats(window);\n      firefoxShim.shimRTCDataChannel(window);\n      firefoxShim.shimAddTransceiver(window);\n      firefoxShim.shimCreateOffer(window);\n      firefoxShim.shimCreateAnswer(window);\n\n      commonShim.shimRTCIceCandidate(window);\n      commonShim.shimConnectionState(window);\n      commonShim.shimMaxMessageSize(window);\n      commonShim.shimSendThrowTypeError(window);\n      break;\n    case 'edge':\n      if (!edgeShim || !edgeShim.shimPeerConnection || !options.shimEdge) {\n        logging('MS edge shim is not included in this adapter release.');\n        return adapter;\n      }\n      logging('adapter.js shimming edge.');\n      // Export to the adapter global object visible in the browser.\n      adapter.browserShim = edgeShim;\n\n      edgeShim.shimGetUserMedia(window);\n      edgeShim.shimGetDisplayMedia(window);\n      edgeShim.shimPeerConnection(window);\n      edgeShim.shimReplaceTrack(window);\n\n      // the edge shim implements the full RTCIceCandidate object.\n\n      commonShim.shimMaxMessageSize(window);\n      commonShim.shimSendThrowTypeError(window);\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      safariShim.shimRTCIceServerUrls(window);\n      safariShim.shimCreateOfferLegacy(window);\n      safariShim.shimCallbacksAPI(window);\n      safariShim.shimLocalStreamsAPI(window);\n      safariShim.shimRemoteStreamsAPI(window);\n      safariShim.shimTrackEventTransceiver(window);\n      safariShim.shimGetUserMedia(window);\n\n      commonShim.shimRTCIceCandidate(window);\n      commonShim.shimMaxMessageSize(window);\n      commonShim.shimSendThrowTypeError(window);\n      commonShim.removeAllowExtmapMixed(window);\n      break;\n    default:\n      logging('Unsupported browser!');\n      break;\n  }\n\n  return adapter;\n}\n"],"sourceRoot":""}