1 line
2.9 MiB
Plaintext
1 line
2.9 MiB
Plaintext
{"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/RTCEvents.js","webpack://JitsiMeetJS/./service/statistics/AnalyticsEvents.js","webpack://JitsiMeetJS/./service/RTC/MediaType.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/./service/RTC/VideoType.js","webpack://JitsiMeetJS/./JitsiTrackErrors.js","webpack://JitsiMeetJS/./JitsiTrackEvents.js","webpack://JitsiMeetJS/./JitsiConferenceErrors.js","webpack://JitsiMeetJS/../events/events.js","webpack://JitsiMeetJS/./JitsiTrackError.js","webpack://JitsiMeetJS/./modules/util/Listenable.js","webpack://JitsiMeetJS/./modules/xmpp/SDP.js","webpack://JitsiMeetJS/../@jitsi/js-utils/browser-detection/browsers.js","webpack://JitsiMeetJS/../@jitsi/js-utils/browser-detection/BrowserDetection.js","webpack://JitsiMeetJS/../@jitsi/js-utils/jitsi-local-storage/index.js","webpack://JitsiMeetJS/./modules/detection/DetectionEvents.js","webpack://JitsiMeetJS/./service/statistics/Events.js","webpack://JitsiMeetJS/./modules/videosipgw/VideoSIPGWConstants.js","webpack://JitsiMeetJS/../sdp-transform/lib/index.js","webpack://JitsiMeetJS/./JitsiConnectionEvents.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/./service/RTC/CodecMimeType.js","webpack://JitsiMeetJS/./modules/util/RandomUtil.js","webpack://JitsiMeetJS/./modules/xmpp/ConnectionPlugin.js","webpack://JitsiMeetJS/./JitsiConnectionErrors.js","webpack://JitsiMeetJS/./service/RTC/SignalingEvents.js","webpack://JitsiMeetJS/../lodash.clonedeep/index.js","webpack://JitsiMeetJS/./modules/xmpp/XmppConnection.js","webpack://JitsiMeetJS/./service/RTC/CameraFacingMode.js","webpack://JitsiMeetJS/../webpack/buildin/global.js","webpack://JitsiMeetJS/../lodash.isequal/index.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/./modules/e2ee/E2EEncryption.js","webpack://JitsiMeetJS/./modules/xmpp/JingleSessionPC.js","webpack://JitsiMeetJS/./modules/xmpp/MediaSessionEvents.js","webpack://JitsiMeetJS/./JitsiMediaDevicesEvents.js","webpack://JitsiMeetJS/./modules/e2ee/OlmAdapter.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/../@jitsi/js-utils/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/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_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","PARTCIPANT_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","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","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","CREATE_ANSWER_FAILED","CREATE_OFFER_FAILED","DATA_CHANNEL_OPEN","ENDPOINT_CONN_STATUS_CHANGED","LASTN_ENDPOINT_CHANGED","GRANTED_PERMISSIONS","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","AUDIO","PRESENTER","VIDEO","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","devicesReceived","newDevices","mediaDeviceInfoToJSON","facing","compareAvailableMediaDevices","slice","RTCUtils","Listenable","super","clearInterval","enumerateDevices","navigator","mediaDevices","then","devices","initEnumerateDevicesWithCallback","usesNewGumFlow","RTCPeerConnectionType","RTCPeerConnection","attachMediaStream","wrapAttachMediaStream","srcObject","getStreamID","getTrackID","isChromiumBased","src","srcObjectPropertyName","jitsiObjectURL","URL","createObjectURL","defaultSetVideoSrc","MediaStream","videoTracks","audioTracks","_initPCConstraints","screenObtainer","getUserMediaWithConstraints","isDeviceListAvailable","ds","supportsDeviceChangeEvent","addEventListener","setInterval","pcConstraints","googHighStartBitrate","googPayloadPadding","googScreencastMinBitrate","googCpuOveruseDetection","googCpuOveruseEncodeUsage","googCpuUnderuseThreshold","googCpuOveruseThreshold","p2pPcConstraints","Promise","resolve","reject","getUserMedia","catch","_newGetUserMediaWithConstraints","umDevices","_newGetDesktopMedia","isSupported","supportsVideo","obtainStream","_parseDesktopSharingOptions","obtainAudioAndVideoPermissions","_getAudioAndVideoStreams","streams","audioStream","videoStream","res","audioVideo","addTrack","desktop","sourceType","track","MediaType","videoType","VideoType","DESKTOP","CAMERA","handleLocalStream","requestingDesktop","avStream","missingTracks","_getMissingTracks","stopMediaStream","desktopSharingSourceDevice","requestedDevices","missingDevices","audioDeviceRequested","videoDeviceRequested","desktopSharingSources","gumOptions","trackOptions","newObtainAudioAndVideoPermissions","mediaStreamsMetaData","maybeRequestDesktopDevice","matchingDevice","getTracks","applyConstraints","maybeRequestCaptureDevices","requestedCaptureDevices","clonedeep","autoGainControl","noiseSuppression","newGetConstraints","desktopAudioTracks","desktopAudioStream","desktopVideoTracks","desktopVideoStream","effects","isDeviceChangeAvailable","deviceType","mediaStream","release","url","revokeObjectURL","isDesktopSharingEnabled","setAudioOutputDevice","getAudioOutputDevice","getCurrentlyAvailableMediaDevices","getEventDataForActiveDevice","deviceData","setSuspendVideo","enable","googSuspendBelowMinBitrate","rtcUtils","origAttachMediaStream","apply","ex","callUnhandledRejectionHandler","promise","oldOnErrorHandler","oldOnUnhandledRejection","onunhandledrejection","args","errHandler","peerConnectionIdCounter","rtcTrackIdCounter","_newCreateLocalTracks","mediaStreamMetaData","metaData","getSettings","safeCounterIncrement","JitsiLocalTrack","rtcId","RTC","peerConnections","localTracks","_channel","_lastN","_lastNEndpoints","_senderVideoConstraints","_maxFrameHeight","_pinnedEndpoint","_selectedEndpoints","_lastNChangeListener","_onLastNChanged","_onDeviceListChanged","_updateAudioOutputForAudioTracks","addListener","destroy","_channelOpenListener","tracksInfo","newTracks","trackInfo","localTrack","createLocalTracks","initializeBridgeChannel","wsUrl","BridgeChannel","_senderVideoConstraintsChanged","sendPinnedEndpointMessage","sendSelectedEndpointsMessage","sendReceiverVideoConstraintMessage","_selectedEndpoint","sendSetLastNMessage","senderVideoConstraints","lastNEndpoints","oldLastNEndpoints","leavingLastNEndpoints","enteringLastNEndpoints","isInLastN","onCallEnded","setReceiverVideoConstraint","maxFrameHeight","isOpen","selectEndpoints","ids","pinEndpoint","eventType","createPeerConnection","signaling","iceConfig","isP2P","getPCConstraints","abtestSuspendVideo","addPermanentProperties","enableInsertableStreams","encodedInsertableStreams","forceEncodedAudioInsertableStreams","forceEncodedVideoInsertableStreams","supportsSdpSemantics","sdpSemantics","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","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","once","emitter","eventListener","errorListener","_events","_eventsCount","_maxListeners","defaultMaxListeners","checkListener","_getMaxListeners","that","_addListener","prepend","events","existing","warning","newListener","warned","count","onceWrapper","fired","wrapFn","_onceWrap","state","wrapped","_listeners","unwrap","evlistener","ret","unwrapListeners","arrayClone","listenerCount","copy","RangeError","getPrototypeOf","setMaxListeners","getMaxListeners","doError","er","context","listeners","prependListener","prependOnceListener","list","position","originalListener","spliceOne","off","rawListeners","eventNames","TRACK_ERROR_TO_MESSAGE_MAP","JitsiTrackErrors","constraint","failedConstraintName","getResolutionFromFailedConstraint","removeEventListener","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","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","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","STATUS_AVAILABLE","STATUS_UNDEFINED","STATUS_BUSY","STATE_ON","STATE_OFF","STATE_PENDING","STATE_RETRYING","STATE_FAILED","ERROR_NO_CONNECTION","ERROR_SESSION_EXISTS","parser","writer","write","parseFmtpConfig","parseParams","parsePayloads","parseRemoteCandidates","parseImageAttributes","parseSimulcastStreamList","WRONG_STATE","DISPLAY_NAME_REQUIRED","DEFAULT_STUN_SERVERS","urls","JITSI_MEET_MUC_TYPE","XMPP","token","disconnectInProgress","connectionTimes","authenticatedUser","initStropheUtil","initStropheLogger","enableWebsocketResume","serviceUrl","websocketKeepAlive","XmppConnection","createConnection","bosh","_initStrophePlugins","caps","Caps","clientNode","initFeaturesList","ev","addFeature","disableRtx","enableOpusRed","supportsAudioRed","enableLipSync","rayo","E2EEncryption","getConnection","connectionHandler","credentials","performance","statusStr","getStatusString","XMPPEvents","getStunAndTurnCredentials","_resetState","pingJid","getFeaturesAndIdentities","identities","PING","identity","speakerStatsComponentAddress","conferenceDurationComponentAddress","lobbySupported","f","fr","endsWith","JitsiConnectionEvents","_onPrivateMessage","password","anonymousConnectionFailed","connectionFailed","lastErrorMsg","JitsiConnectionErrors","ping","stopInterval","wasIntentionalDisconnect","errMsg","_getConnectionFailedReasonDetails","lastErrorStatus","getLastErrorStatus","attaching","anonymousdomain","hosts","configDomain","hostname","createRoom","roomName","onCreateResource","roomjid","customDomain","muc","mucNickname","randomHexString","emuc","getJid","getJingleLog","getLog","getXmppLog","dial","getSessions","sessions","disconnectListener","_cleanupXmppConnection","isUsingWebSocket","evType","sendUnavailableBeacon","jvb","iceServers","p2p","p2pStunServers","stunServers","iceTransportPolicy","MucConnectionPlugin","JingleConnectionPlugin","RayoConnectionPlugin","deploymentInfo","shard","headersArr","trim","shard_changed","suspend_time","getPingSuspendTime","time_since_last_success","getTimeSinceLastSuccess","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","VP8","VP9","randomElement","randomHexDigit","randomAlphanumStr","getConnectionPluginDefinition","base","ConnectionPluginListenable","CONNECTION_DROPPED_ERROR","OTHER_ERROR","SERVER_ERROR","PEER_MUTED_CHANGED","PEER_VIDEO_TYPE_CHANGED","argsTag","funcTag","genTag","mapTag","setTag","reFlags","reIsHostCtor","reIsUint","cloneableTags","freeGlobal","freeSelf","freeExports","freeModule","moduleExports","addMapEntry","pair","addSetEntry","arrayReduce","iteratee","accumulator","initAccum","isHostObject","mapToArray","overArg","setToArray","uid","arrayProto","funcProto","objectProto","coreJsData","maskSrcKey","exec","IE_PROTO","funcToString","objectToString","reIsNative","RegExp","Buffer","Uint8Array","getPrototype","objectCreate","propertyIsEnumerable","nativeGetSymbols","nativeIsBuffer","isBuffer","nativeKeys","DataView","getNative","WeakMap","nativeCreate","dataViewCtorString","toSource","mapCtorString","promiseCtorString","setCtorString","weakMapCtorString","symbolProto","symbolValueOf","valueOf","Hash","entries","entry","ListCache","MapCache","Stack","__data__","arrayLikeKeys","inherited","isObjectLike","isArrayLike","isArrayLikeObject","isArguments","baseTimes","skipIndexes","isIndex","assignValue","objValue","eq","assocIndexOf","baseClone","isDeep","isFull","customizer","isObject","isArr","input","initCloneArray","copyArray","getTag","isFunc","buffer","cloneBuffer","isPrototype","initCloneObject","copyObject","getSymbols","copySymbols","baseAssign","cloneFunc","Ctor","cloneArrayBuffer","dataView","byteOffset","byteLength","cloneDataView","typedArray","cloneTypedArray","cloneMap","regexp","lastIndex","cloneRegExp","cloneSet","symbol","initCloneByTag","stacked","props","keysFunc","symbolsFunc","offset","arrayPush","baseGetAllKeys","getAllKeys","arrayEach","subValue","baseIsNative","isFunction","arrayBuffer","newValue","getMapData","getValue","cache","pairs","LARGE_ARRAY_SIZE","other","ArrayBuffer","ctorString","isLength","baseKeys","CONN_STATUS_CHANGED","_options","_stropheConn","_usesWebsocket","_lastSuccessTracker","LastSuccessTracker","startTracking","_resumeTask","ResumeTask","_deferredIQs","PingConnectionPlugin","onPingThresholdExceeded","_onPingErrorThresholdExceeded","websocket","_status","OPEN","disco","_stropheConnectionCb","targetCallback","blockCallback","_maybeEnableStreamResume","_maybeStartWSKeepAlive","_processDeferredIQs","cancel","startInterval","_tryResumingConnection","_wsKeepAlive","Events","_clearDeferredIQs","deferred","closeWebsocket","streamManagement","getResumeToken","intervalWithJitter","fetch","timeLeft","sendIQ2","sendBeacon","schedule","ENVIRONMENT","g","objectTag","typedArrayTags","freeProcess","process","nodeUtil","binding","nodeIsTypedArray","isTypedArray","arraySome","predicate","nativeObjectToString","symToStringTag","SetCache","isArg","isBuff","isType","baseGetTag","isOwn","unmasked","getRawTag","baseIsArguments","baseIsEqual","bitmask","equalFunc","objIsArr","othIsArr","objTag","othTag","objIsObj","othIsObj","isSameTag","equalArrays","convert","isPartial","equalByTag","objIsWrapped","othIsWrapped","objUnwrapped","othUnwrapped","objProps","objLength","othLength","skipCtor","othValue","compared","objCtor","othCtor","equalObjects","baseIsEqualDeep","isMasked","arrLength","seen","arrValue","othIndex","resIndex","arrayFilter","baseUnary","gumFunction","ScreenObtainer","_createObtainStreamMethod","JitsiMeetNW","obtainDesktopStream","jitsiError","obtainScreenOnElectron","supportsGetDisplayMedia","obtainScreenFromGetDisplayMedia","JitsiMeetScreenObtainer","openDesktopPicker","streamType","onGetStreamResponse","errorCallback","getDisplayMedia","cursor","applyConstraintsPromise","videoTrack","errorDetails","errorName","errorMsg","errorStack","currentExecutingScript","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","_conferenceJoined","_enabled","_initialized","_key","_e2eeCtx","E2EEContext","_olmAdapter","OlmAdapter","_ratchetKey","debounce","_ratchetKeyImpl","_rotateKey","_rotateKeyImpl","_onParticipantJoined","_onParticipantLeft","_onMediaSessionStarted","_onLocalTrackAdded","_setupReceiverE2EEForTrack","_trackMuteChanged","PARTICIPANT_E2EE_CHANNEL_READY","_onParticipantE2EEChannelReady","PARTICIPANT_KEY_UPDATED","_onParticipantKeyUpdated","supportsInsertableStreams","testing","disableE2EE","isEnabled","setEnabled","enabled","_restartMediaSessions","_generateKey","updateKey","setKey","crypto","getRandomValues","_getMediaSessions","_setupSenderE2EEForTrack","cleanup","material","importKey","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","disabledCodec","preferredCodec","disableSimulcast","disableH264","preferH264","_abtestSuspendVideoEnabled","startSilent","onicecandidate","phase","sendIceCandidate","onsignalingstatechange","signalingState","connectionState","oniceconnectionstatechange","iceConnectionState","reconnect","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","acceptOffer","jingleOffer","failure","setOfferAnswerCycle","sendSessionAccept","invite","addTracks","createOffer","offerSdp","setLocalDescription","sendSessionInitiate","setAnswer","jingleAnswer","jingleOfferAnswerIq","newRemoteSdp","_processNewJingleOfferIq","oldLocalSdp","bridgeSessionId","sendContentModify","newLocalSdp","replaceTransport","jingleOfferElem","originalOffer","clone","remove","newFingerprint","sendTransportAccept","accept","responder","responderJid","sessionModify","transportAccept","medialines","sendTransportReject","transportReject","setSenderMaxBitrates","setMaxBitRate","setSenderVideoConstraint","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","setMediaTransferActive","audioActive","videoActive","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","PERMISSION_PROMPT_IS_SHOWN","OLM_MESSAGE_TYPES","kOlmData","OlmAdapterEvents","_conf","_init","Deferred","_keyIndex","_reqs","_bootstrapOlm","_onEndpointMessageReceived","_onConferenceJoined","_onConferenceLeft","Olm","promises","pId","olmData","_getParticipantOlmData","uuidv4","olm","ciphertext","_encryptKeyInfo","setRejectTimeout","_sendMessage","allSettled","_olmAccount","Account","idKeys","identity_keys","_idKey","curve25519","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","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","_addChatRoom","_removeChatRoom","CAPS","_handleCaps","_onMucMemberLeft","_removeJidToVersionEntry","submit","_generateVersion","removeFeature","getFeatures","_getDiscoInfo","sha","receivedNode","category","_fixChatRoomPresenceMap","addToPresence","_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","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","subtle","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","children","_eventEmitter","_grantedPermissions","JitsiMediaDevicesEvents","_logOutputDevice","_handleGrantedPermissions","_permissionsApiSupported","permissions","query","deviceID","isDevicePermissionGranted","supported","results","every","permissionStatus","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","createEmptyStream","mimeType","ondataavailable","dataEvent","recorderToRemove","download","Blob","href","click","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","_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","setLocalParticipantProperty","_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","removeFromPresence","setSubject","subject","isModerator","getTranscriber","transcriber","Transcriber","localAudioTracks","getTranscriptionStatus","transcriptionStatus","localVideoTrack","_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","pinParticipant","lastN","isInteger","isVideoActive","getParticipantCount","countHidden","grantOwner","setAffiliation","kickParticipant","kick","_maybeClearSITimeout","_sessionInitiateTimeout","onMemberJoined","nick","statsID","botType","JitsiParticipant","_role","_botType","_updateFeatures","_maybeStartOrStopP2P","_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","bridgeChannelType","openBridgeChannel","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","_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","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","isUserInteractionRequiredForUnmute","supportsBandwidthStatistics","supportsCodecPreferences","RTCRtpTransceiver","RTCRtpSender","ondevicechange","supportsLocalCandidateRttStatistics","PerformanceObserver","supportedEntryTypes","RTCRtpReceiver","supportsRTTStatistics","_getChromiumBasedVersion","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","audioBitrateDownload","audioBitrateUpload","audioCodec","videoBitrateDownload","videoBitrateUpload","videoCodec","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","frameHeight","frameWidth","framesPerSecond","framesSent","trackIdentifier","getSsrcByTrackId","dest","srcEvent","hidden","_jid","_id","_conference","_hidden","_statsID","_connectionStatus","_properties","_identity","getConference","jitsiTrack","oldValue","isAudioMuted","_isMediaTypeMuted","_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","retry","minDelay","LastRequestTracker","_lastSuccess","xmppConnection","originalRawInput","ConnectionPlugin","failedPings","pingExecIntervals","_onPingThresholdExceeded","_addPingExecutionTimestamp","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","customJoinPresenceExtensions","disableFocus","allocateConferenceFocus","onConnStatusChanged","fromJoin","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","newRole","_initFocus","memberOfThis","displayJids","att","phone","pin","processNode","focusFeatures","supportsRestartByTerminate","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","videoTypeNode","isSipGatewayEnabled","iqToFocus","clean","onMucLeft","doReject","mainRoom","maybeJoinLobbyRoom","_maybeJoinLobbyRoom","lobbyRoom","_leaveLobbyRoom","avatar","invitePassword","memberRoomJid","msgToSend","createExpBackoffTimer","step","origin","xmppService","getNextTimeout","getNextErrorTimeout","externalAuthEnabled","sipGatewayEnabled","call_control","attachEvent","setFocusUserJid","focusJid","focusUserJid","getFocusComponent","focusComponent","createConferenceIq","machineUID","machineId","openSctp","enforcedBridge","enableTcc","enableRemb","minParticipants","audioPacketDelay","startBitrate","minBitrate","octo","probability","opusMaxAverageBitrate","stereo","useRoomAsSharedDocumentName","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","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","senderVideoConstraintsChanged","_areRetriesEnabled","_closedFromClient","datachannel","createDataChannel","_handleChannel","_wsUrl","_initWebSocket","ws","_startConnectionRetries","timeoutS","reload","_retryTimeout","_stopConnectionRetries","_retryWebSocketConnection","closeEvent","colibriClass","msgPayload","jsonObject","pinnedEndpoint","endpointIds","selectedEndpoints","maxFrameHeightPixels","channel","dominantSpeakerEndpoint","endpoint","videoConstraints","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","getCodecMimeType","codecPreference","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","peerMediaInfo","_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","hasCameraTrack","_mungeCodecOrder","containsTrack","webrtcStream","_addStream","generateNewStreamSSRCInfo","setPrimarySsrc","setSsrcCache","rtxSsrcMapping","rtxSsrc","promiseChain","_assertTrackBelongs","webRtcStream","_removeStream","doesBelong","isMediaStreamInPc","findSenderByKind","opts","_ensureSimulcastGroupIsLast","localSdp","sdpStr","videoStartIndex","simStartIndex","otherStartIndex","simEndIndex","simStr","otherEndIndex","sdpHead","simStrTrimmed","sdpTail","_adjustLocalMediaDirection","modifiedDirection","desiredAudioDirection","desiredVideoDirection","toUnifiedPlan","videoSender","preference","degradationPreference","presenterEnabled","scaleFactor","layer","enableConferenceFlag","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","enableIceRestart","explicitlyDisabled","useTerminateForRestart","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","pingInterval","pingIntervalMs","analyticsInterval","analyticsIntervalMs","requestId","requestMessage","timeSent","E2ePingEvents","Infinity","participantJoined","participantLeft","messageReceived","dataChannelOpened","participantWrapper","handleRequest","_jvb121","evaluateStatus","oldStatus","_propagateSendMaxFrameHeight","mediaSession","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","otherOptions","_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","current","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","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","_getUserMedia","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":"CAAA,SAA2CA,EAAMC,GAC1B,iBAAZC,SAA0C,iBAAXC,OACxCA,OAAOD,QAAUD,IACQ,mBAAXG,QAAyBA,OAAOC,IAC9CD,OAAO,GAAIH,GACe,iBAAZC,QACdA,QAAqB,YAAID,IAEzBD,EAAkB,YAAIC,IARxB,CASGK,QAAQ,WACX,O,YCTE,IAAIC,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUP,QAGnC,IAAIC,EAASI,EAAiBE,GAAY,CACzCC,EAAGD,EACHE,GAAG,EACHT,QAAS,IAUV,OANAU,EAAQH,GAAUI,KAAKV,EAAOD,QAASC,EAAQA,EAAOD,QAASM,GAG/DL,EAAOQ,GAAI,EAGJR,EAAOD,QA0Df,OArDAM,EAAoBM,EAAIF,EAGxBJ,EAAoBO,EAAIR,EAGxBC,EAAoBQ,EAAI,SAASd,EAASe,EAAMC,GAC3CV,EAAoBW,EAAEjB,EAASe,IAClCG,OAAOC,eAAenB,EAASe,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEV,EAAoBgB,EAAI,SAAStB,GACX,oBAAXuB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAenB,EAASuB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAenB,EAAS,aAAc,CAAEyB,OAAO,KAQvDnB,EAAoBoB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQnB,EAAoBmB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFAxB,EAAoBgB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOnB,EAAoBQ,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRvB,EAAoB2B,EAAI,SAAShC,GAChC,IAAIe,EAASf,GAAUA,EAAO2B,WAC7B,WAAwB,OAAO3B,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAK,EAAoBQ,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRV,EAAoBW,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG7B,EAAoBgC,EAAI,GAIjBhC,EAAoBA,EAAoBiC,EAAI,K,gBCuNrDtC,EAAOD,QAzSY,CAIfwC,yBAA0B,gCAI1BC,qBAAsB,4BACtBC,wBAAyB,+BACzBC,YAAa,mBAKbC,cAAe,2BAIfC,cAAe,2BAMfC,WAAY,wBACZC,oBAAqB,2BAGrBC,8BAA+B,qCAK/BC,uBAAwB,4BAIxBC,kBAAmB,yBAInBC,uBAAwB,8BAIxBC,oBAAqB,2BAIrBC,sBAAuB,6BAKvBC,0BAA2B,iCAI3BC,qBAAsB,4BAKtBC,gBAAiB,uBAKjBC,kBAAmB,yBACnBC,SAAU,gBACVC,mBAAoB,0BACpBC,WAAY,kBACZC,kBAAmB,yBAMnBC,eAAgB,qBAMhBC,oBAAqB,0BAWrBC,OAAQ,cAGRC,mBAAoB,yBAKpBC,eAAgB,sBAIhBC,iBAAkB,wBAIlBC,wBAAyB,+BAIzBC,yBAA0B,gCAG1BC,4BAA6B,mCAG7BC,cAAe,qBAGfC,WAAY,kBAGZC,kBAAmB,yBAGnBC,gBAAiB,uBAGjBC,wBAAyB,+BAGzBC,yBAA0B,gCAG1BC,sBAAuB,6BAGvBC,kBAAmB,yBAGnBC,SAAU,gBAIVC,iBAAkB,wBAGlBC,iBAAkB,wBAGlBC,yBAA0B,gCAI1BC,wBAAyB,mBAIzBC,wBAAyB,mBAMzBC,+BAAgC,kBAKhCC,4BAA6B,mCAC7BC,kBAAmB,yBAKnBC,qBAAsB,gCACtBC,kBAAmB,yBACnBC,gBAAiB,uBACjBC,iBAAkB,wBAGlBC,cAAe,qBAKfC,uBAAwB,4BAIxBC,aAAc,oBAKdC,qBAAsB,4BACtBC,kBAAmB,8BACnBC,mBAAoB,0BACpBC,+BAAgC,sCAChCC,gBAAiB,uBACjBC,gCAAiC,uCAKjCC,qBAAsB,4BAGtBC,qBAAsB,4BAItBC,6BAA8B,oCAY9BC,uBAAwB,8BAKxBC,uBAAwB,8BAKxBC,8BAA+B,qCAI/BC,uBAAwB,8BAIxBC,gBAAiB,uBAIjBC,iBAAkB,wBAQlBC,6BAA8B,oCAK9BC,eAAgB,4BAQhBC,kCAAmC,qCAUnCC,mCACI,qCAIJC,6BAA8B,oCAM9BC,sBAAuB,+B,6BCtS3B,y4FAQO,MAAMC,EAA2B,uCAK3BC,EAAsB,iCAOtBC,EAA6B,sCAK7BC,EAAmB,mBAKnBC,EAAoB,oBAMpBC,EAAoB,oBAKpBC,EAAkB,kBAOlBzE,EAAyB,mCAOzBE,EAAyB,mCAMzBC,EAAsB,gCAKtBuE,EAAsB,+BAKtBpE,EAAuB,gCAKvBqE,EAA2B,6BAK3BC,EAA+B,8BAK/BC,EAAuB,gCAMvBC,EAA4B,uCAgB5BC,EAAgB,0BAMhBhE,EAAS,oBAOTiE,EAAqB,gCAUrBC,EAA2B,mCAK3BC,EAAqB,gCAOrBC,EAAwB,mCAOxBC,EAAyB,mCAOzBC,EAAgC,0CAQhCC,EAAuB,gCAKvBpE,EAAmB,6BAKnBqE,EAAiB,4BAKjBC,EAAY,uBAKZpE,EAA2B,oCAgB3BqE,EACP,6CAKOpD,EACP,yCAMOqD,EACP,0CAOOC,EAAa,uBAKbpD,EAAuB,gCAMvBqD,EAAqB,+BAKrBhD,EAAyB,kCAMzBmB,EACP,2CAWOC,EACP,2CAKO6B,EACP,wCAKOC,EAAgB,2BAKhBnC,EAAkB,4BAKlBC,EAAmB,6BAKnBmC,EAAmB,8BAQnBC,EAAc,wBAKdC,EAA4B,gCAO5BC,EAAqB,8BAQrBC,EAAgB,0BAQhBtC,EACP,wCAMOuC,EAAc,wBAKdC,EAAY,sBAKZC,EAAoB,yBAKpBC,GAAsB,2BAKtBC,GAAmB,8BAKnBC,GAAoB,8BAKpBC,GAAqB,+BAKrBC,GAAkB,6B,iBCzW9B,gBAAkB7J,IAIX,WAAc,aAEV,IAAI8J,OAA8B,IAAXC,EAAyBA,EACpB,oBAATC,KAAuBA,KACZ,oBAAX3J,OAAyBA,OAAS,GAErD,SAAS4J,EAAQC,GAWf,OATED,EADoB,mBAAXzI,QAAoD,iBAApBA,OAAO2I,SACtC,SAAUD,GAClB,cAAcA,GAGN,SAAUA,GAClB,OAAOA,GAAyB,mBAAX1I,QAAyB0I,EAAIE,cAAgB5I,QAAU0I,IAAQ1I,OAAOa,UAAY,gBAAkB6H,IAI9GA,GAGjB,SAASG,EAAmBC,GAC1B,OAGF,SAA4BA,GAC1B,GAAIC,MAAMC,QAAQF,GAAM,CACtB,IAAK,IAAI7J,EAAI,EAAGgK,EAAO,IAAIF,MAAMD,EAAII,QAASjK,EAAI6J,EAAII,OAAQjK,IAAKgK,EAAKhK,GAAK6J,EAAI7J,GAEjF,OAAOgK,GAPFE,CAAmBL,IAW5B,SAA0BM,GACxB,GAAIpJ,OAAO2I,YAAYhJ,OAAOyJ,IAAkD,uBAAzCzJ,OAAOkB,UAAUwI,SAASjK,KAAKgK,GAAgC,OAAOL,MAAMO,KAAKF,GAZtFG,CAAiBT,IAerD,WACE,MAAM,IAAIU,UAAU,mDAhBuCC,GAoC7D,IAAIC,EAAW,SAAkBC,EAAGC,GAClC,IAAIC,GAAW,MAAJF,IAAmB,MAAJC,GAE1B,OADWD,GAAK,KAAOC,GAAK,KAAOC,GAAO,KAC5B,GAAW,MAANA,GAejBC,EAAW,SAAkBC,GAC/B,GAAmB,iBAARA,EACT,MAAM,IAAIC,MAAM,oCAKlB,IAFA,IAAIC,EAAM,GAEDhL,EAAI,EAAGA,EAAiB,EAAb8K,EAAIb,OAAYjK,GAAK,EACvCgL,EAAIhL,GAAK,KAA+B,IAAxB8K,EAAIG,WAAWjL,EAAI,KAAaA,EAAI,GAGtD,OAAOgL,GAoCLE,EAAU,SAAiBC,EAAGC,EAAGC,EAAGX,EAAG3I,EAAGb,GAC5C,OAAOuJ,GAxDsBa,EAwDLb,EAASA,EAASW,EAAGD,GAAIV,EAASC,EAAGxJ,OAxD3BqK,EAwDgCxJ,GAvD9CuJ,IAAQ,GAAKC,EAuDqCF,GAxD1D,IAAiBC,EAAKC,GA2DhCC,EAAS,SAAgBJ,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAG3I,EAAGb,GAC7C,OAAOgK,EAAQG,EAAIhL,GAAKgL,EAAI/K,EAAG8K,EAAGC,EAAGX,EAAG3I,EAAGb,IAGzCuK,EAAS,SAAgBL,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAG3I,EAAGb,GAC7C,OAAOgK,EAAQG,EAAI/K,EAAID,GAAKC,EAAG8K,EAAGC,EAAGX,EAAG3I,EAAGb,IAGzCwK,EAAS,SAAgBN,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAG3I,EAAGb,GAC7C,OAAOgK,EAAQG,EAAIhL,EAAIC,EAAG8K,EAAGC,EAAGX,EAAG3I,EAAGb,IAGpCyK,EAAS,SAAgBP,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAG3I,EAAGb,GAC7C,OAAOgK,EAAQ7K,GAAKgL,GAAK/K,GAAI8K,EAAGC,EAAGX,EAAG3I,EAAGb,IAOvC0K,EAAW,SAAkBlB,EAAGmB,GAElCnB,EAAEmB,GAAO,IAAM,KAAQA,EAAM,GAC7BnB,EAA0B,IAAvBmB,EAAM,KAAO,GAAK,IAAWA,EAOhC,IANA,IAIIC,EAAMC,EAAMC,EAAMC,EAJlBb,EAAI,WACJC,GAAK,UACLhL,GAAK,WACLC,EAAI,UAGCN,EAAI,EAAGA,EAAI0K,EAAET,OAAQjK,GAAK,GACjC8L,EAAOV,EACPW,EAAOV,EACPW,EAAO3L,EACP4L,EAAO3L,EACP8K,EAAII,EAAOJ,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAIkL,EAAOlL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,IAAK,WACtCK,EAAImL,EAAOnL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,GAAI,WACrCqL,EAAIG,EAAOH,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,YACtCoL,EAAII,EAAOJ,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAIkL,EAAOlL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,GAAI,YACrCK,EAAImL,EAAOnL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,IAAK,YACtCqL,EAAIG,EAAOH,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,UACtCoL,EAAII,EAAOJ,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,EAAG,YACpCM,EAAIkL,EAAOlL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,IAAK,YACtCK,EAAImL,EAAOnL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,IAAK,OACvCqL,EAAIG,EAAOH,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,IAAK,YACvCoL,EAAII,EAAOJ,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,IAAK,EAAG,YACrCM,EAAIkL,EAAOlL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,IAAK,UACvCK,EAAImL,EAAOnL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,IAAK,YACvCqL,EAAIG,EAAOH,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,GAAI,YACtCoL,EAAIK,EAAOL,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAImL,EAAOnL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,GAAI,YACrCK,EAAIoL,EAAOpL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,GAAI,WACtCqL,EAAII,EAAOJ,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,WACtCoL,EAAIK,EAAOL,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAImL,EAAOnL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,EAAG,UACrCK,EAAIoL,EAAOpL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,IAAK,WACvCqL,EAAII,EAAOJ,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,WACtCoL,EAAIK,EAAOL,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,EAAG,WACpCM,EAAImL,EAAOnL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,GAAI,YACtCK,EAAIoL,EAAOpL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,IAAK,WACtCqL,EAAII,EAAOJ,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,GAAI,YACrCoL,EAAIK,EAAOL,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,IAAK,GAAI,YACtCM,EAAImL,EAAOnL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,GAAI,UACrCK,EAAIoL,EAAOpL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,GAAI,YACrCqL,EAAII,EAAOJ,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,IAAK,YACvCoL,EAAIM,EAAON,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,QACrCM,EAAIoL,EAAOpL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,IAAK,YACtCK,EAAIqL,EAAOrL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,GAAI,YACtCqL,EAAIK,EAAOL,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,IAAK,UACvCoL,EAAIM,EAAON,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,YACrCM,EAAIoL,EAAOpL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,GAAI,YACrCK,EAAIqL,EAAOrL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,IAAK,WACtCqL,EAAIK,EAAOL,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,IAAK,YACvCoL,EAAIM,EAAON,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,IAAK,EAAG,WACrCM,EAAIoL,EAAOpL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,IAAK,WACtCK,EAAIqL,EAAOrL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,IAAK,WACtCqL,EAAIK,EAAOL,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,GAAI,UACrCoL,EAAIM,EAAON,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAIoL,EAAOpL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,IAAK,WACvCK,EAAIqL,EAAOrL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,GAAI,WACtCqL,EAAIK,EAAOL,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,WACtCoL,EAAIO,EAAOP,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAIqL,EAAOrL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,GAAI,YACrCK,EAAIsL,EAAOtL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,IAAK,YACvCqL,EAAIM,EAAON,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,UACtCoL,EAAIO,EAAOP,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,IAAK,EAAG,YACrCM,EAAIqL,EAAOrL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,GAAI,IAAK,YACtCK,EAAIsL,EAAOtL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,IAAK,IAAK,SACvCqL,EAAIM,EAAON,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,YACtCoL,EAAIO,EAAOP,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,EAAG,YACpCM,EAAIqL,EAAOrL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,IAAK,UACvCK,EAAIsL,EAAOtL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,IAAK,YACtCqL,EAAIM,EAAON,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,IAAK,GAAI,YACtCoL,EAAIO,EAAOP,EAAGC,EAAGhL,EAAGC,EAAGoK,EAAE1K,EAAI,GAAI,GAAI,WACrCM,EAAIqL,EAAOrL,EAAG8K,EAAGC,EAAGhL,EAAGqK,EAAE1K,EAAI,IAAK,IAAK,YACvCK,EAAIsL,EAAOtL,EAAGC,EAAG8K,EAAGC,EAAGX,EAAE1K,EAAI,GAAI,GAAI,WACrCqL,EAAIM,EAAON,EAAGhL,EAAGC,EAAG8K,EAAGV,EAAE1K,EAAI,GAAI,IAAK,WACtCoL,EAAIX,EAASW,EAAGU,GAChBT,EAAIZ,EAASY,EAAGU,GAChB1L,EAAIoK,EAASpK,EAAG2L,GAChB1L,EAAImK,EAASnK,EAAG2L,GAGlB,MAAO,CAACb,EAAGC,EAAGhL,EAAGC,IASf4L,EAAM,CACRC,UAAW,SAAmBpK,GAC5B,OAvIW,SAAkBqK,GAI/B,IAHA,IACItB,EAAM,GAED9K,EAAI,EAAGA,EAAsB,EAAlBoM,EAASnC,OAAYjK,IACvC8K,GAJY,mBAIGuB,OAAOD,EAASpM,GAAK,IAAMA,EAAI,EAAI,EAAI,EAAI,IAJ9C,mBAI6DqM,OAAOD,EAASpM,GAAK,IAAMA,EAAI,EAAI,EAAI,IAGlH,OAAO8K,EA+HEwB,CAASV,EAASf,EAAS9I,GAAe,EAAXA,EAAEkI,UAE1CsC,KAAM,SAAcxK,GAClB,OAxJW,SAAkBiJ,GAG/B,IAFA,IAAIF,EAAM,GAED9K,EAAI,EAAGA,EAAiB,GAAbgL,EAAIf,OAAajK,GAAK,EACxC8K,GAAO0B,OAAOC,aAAazB,EAAIhL,GAAK,KAAOA,EAAI,GAAK,KAGtD,OAAO8K,EAiJE4B,CAASd,EAASf,EAAS9I,GAAe,EAAXA,EAAEkI,WAoB5C,SAAS0C,EAAUjC,EAAGmB,GAEpBnB,EAAEmB,GAAO,IAAM,KAAQ,GAAKA,EAAM,GAClCnB,EAAyB,IAAtBmB,EAAM,IAAM,GAAK,IAAWA,EAC/B,IAMI7L,EAAG4M,EAAG1L,EAAG4K,EAAMC,EAAMC,EAAMC,EAAMY,EANjCC,EAAI,IAAIhD,MAAM,IACdsB,EAAI,WACJC,GAAK,UACLhL,GAAK,WACLC,EAAI,UACJyM,GAAK,WAGT,IAAK/M,EAAI,EAAGA,EAAI0K,EAAET,OAAQjK,GAAK,GAAI,CAOjC,IANA8L,EAAOV,EACPW,EAAOV,EACPW,EAAO3L,EACP4L,EAAO3L,EACPuM,EAAOE,EAEFH,EAAI,EAAGA,EAAI,GAAIA,IAEhBE,EAAEF,GADAA,EAAI,GACClC,EAAE1K,EAAI4M,GAENI,EAAIF,EAAEF,EAAI,GAAKE,EAAEF,EAAI,GAAKE,EAAEF,EAAI,IAAME,EAAEF,EAAI,IAAK,GAG1D1L,EAAI+L,EAAWA,EAAWD,EAAI5B,EAAG,GAAI8B,EAAQN,EAAGvB,EAAGhL,EAAGC,IAAK2M,EAAWA,EAAWF,EAAGD,EAAEF,IAAKO,EAAQP,KACnGG,EAAIzM,EACJA,EAAID,EACJA,EAAI2M,EAAI3B,EAAG,IACXA,EAAID,EACJA,EAAIlK,EAGNkK,EAAI6B,EAAW7B,EAAGU,GAClBT,EAAI4B,EAAW5B,EAAGU,GAClB1L,EAAI4M,EAAW5M,EAAG2L,GAClB1L,EAAI2M,EAAW3M,EAAG2L,GAClBc,EAAIE,EAAWF,EAAGF,GAGpB,MAAO,CAACzB,EAAGC,EAAGhL,EAAGC,EAAGyM,GAQtB,SAASG,EAAQhM,EAAGmK,EAAGhL,EAAGC,GACxB,OAAIY,EAAI,GACCmK,EAAIhL,GAAKgL,EAAI/K,EAGlBY,EAAI,GACCmK,EAAIhL,EAAIC,EAGbY,EAAI,GACCmK,EAAIhL,EAAIgL,EAAI/K,EAAID,EAAIC,EAGtB+K,EAAIhL,EAAIC,EAOjB,SAAS6M,EAAQjM,GACf,OAAOA,EAAI,GAAK,WAAaA,EAAI,GAAK,WAAaA,EAAI,IAAM,YAAc,UAO7E,SAASkM,EAAe7L,EAAK8L,GAC3B,IAAIC,EAAOC,EAAShM,GAEhB+L,EAAKrD,OAAS,KAChBqD,EAAOX,EAAUW,EAAmB,EAAb/L,EAAI0I,SAM7B,IAHA,IAAIuD,EAAO,IAAI1D,MAAM,IACjB2D,EAAO,IAAI3D,MAAM,IAEZ9J,EAAI,EAAGA,EAAI,GAAIA,IACtBwN,EAAKxN,GAAe,UAAVsN,EAAKtN,GACfyN,EAAKzN,GAAe,WAAVsN,EAAKtN,GAGjB,IAAIuM,EAAOI,EAAUa,EAAKE,OAAOH,EAASF,IAAQ,IAAoB,EAAdA,EAAKpD,QAC7D,OAAO0C,EAAUc,EAAKC,OAAOnB,GAAO,KAQtC,SAASU,EAAWvC,EAAGC,GACrB,IAAIC,GAAW,MAAJF,IAAmB,MAAJC,GAE1B,OADWD,GAAK,KAAOC,GAAK,KAAOC,GAAO,KAC5B,GAAW,MAANA,EAOrB,SAASoC,EAAI1B,EAAKC,GAChB,OAAOD,GAAOC,EAAMD,IAAQ,GAAKC,EAQnC,SAASgC,EAASzC,GAIhB,IAHA,IAAIE,EAAM,GAGDhL,EAAI,EAAGA,EAAiB,EAAb8K,EAAIb,OAAYjK,GAAK,EACvCgL,EAAIhL,GAAK,KAHA,IAGO8K,EAAIG,WAAWjL,EAAI,KAAc,GAAKA,EAAI,GAG5D,OAAOgL,EAOT,SAAS2C,EAASvB,GAKhB,IAJA,IAEIwB,EAAShB,EADT9B,EAAM,GAGD9K,EAAI,EAAGA,EAAsB,EAAlBoM,EAASnC,OAAYjK,GAAK,EAG5C,IAFA4N,GAAWxB,EAASpM,GAAK,IAAM,GAAK,EAAIA,EAAI,GAAK,MAAS,IAAMoM,EAASpM,EAAI,GAAK,IAAM,GAAK,GAAKA,EAAI,GAAK,GAAK,MAAS,EAAIoM,EAASpM,EAAI,GAAK,IAAM,GAAK,GAAKA,EAAI,GAAK,GAAK,IAExK4M,EAAI,EAAGA,EAAI,EAAGA,IACT,EAAJ5M,EAAY,EAAJ4M,EAA0B,GAAlBR,EAASnC,OAC3Ba,GAAO,IAEPA,GAXI,mEAWOuB,OAAOuB,GAAW,GAAK,EAAIhB,GAAK,IAKjD,OAAO9B,EAOT,SAAS+C,EAAS7C,GAIhB,IAHA,IAAIF,EAAM,GAGD9K,EAAI,EAAGA,EAAiB,GAAbgL,EAAIf,OAAajK,GAAK,EACxC8K,GAAO0B,OAAOC,aAAazB,EAAIhL,GAAK,KAAO,GAAKA,EAAI,GAH3C,KAMX,OAAO8K,EAQT,IAAIgD,EAAO,CACTC,cAAe,SAAuBxM,EAAK8L,GACzC,OAAOM,EAASP,EAAe7L,EAAK8L,KAEtCW,SAAU,SAAkBjM,GAC1B,OAAO4L,EAAShB,EAAUY,EAASxL,GAAe,EAAXA,EAAEkI,UAE3C4D,SAAUA,EACVT,eAAgBA,EAChBa,cAAe,SAAuB1M,EAAK8L,GACzC,OAAOQ,EAAST,EAAe7L,EAAK8L,KAEtCa,SAAU,SAAkBnM,GAC1B,OAAO8L,EAASlB,EAAUY,EAASxL,GAAe,EAAXA,EAAEkI,WAIzCkE,EACQ,SAAkBrD,GAC1B,IAAI9K,EAAGK,EACH+N,EAAM,GACNvC,EAAMf,EAAIb,OAEd,IAAKjK,EAAI,EAAGA,EAAI6L,EAAK7L,KACnBK,EAAIyK,EAAIG,WAAWjL,KAEV,GAAUK,GAAK,IACtB+N,GAAOtD,EAAIuB,OAAOrM,GACTK,EAAI,MACb+N,GAAO5B,OAAOC,aAAa,IAAOpM,GAAK,GAAK,IAC5C+N,GAAO5B,OAAOC,aAAa,IAAOpM,GAAK,EAAI,IAC3C+N,GAAO5B,OAAOC,aAAa,IAAOpM,GAAK,EAAI,MAE3C+N,GAAO5B,OAAOC,aAAa,IAAOpM,GAAK,EAAI,IAC3C+N,GAAO5B,OAAOC,aAAa,IAAOpM,GAAK,EAAI,KAI/C,OAAO+N,GArBPD,EAuBU,SAAoBE,GAsB9B,IAAK,IAAIC,KAFTD,EAAUA,GAAW,GAGnB,GAAI3N,OAAOkB,UAAUC,eAAe1B,KAAKkO,EAASC,GAAa,CAC7D,IAAIC,EAAU,GACVC,EAAS,GACTC,EAAO,GACPC,EAAYL,EAAQC,GACpBK,EAA+B,WAAvBnF,EAAQkF,GAChBE,EAAcC,OAAOC,SAASH,EAAQD,EAAUzN,MAAQyN,IAExDC,IACFJ,EAAUG,EAAUH,QAAU,YAAcG,EAAUH,QAAU,GAChEC,EAASE,EAAUF,OAAS,WAAaE,EAAUF,OAAS,GAC5DC,EAAOC,EAAUD,KAAO,SAAWC,EAAUD,KAAO,IAGtDM,SAASC,OAASV,EAAa,IAAMM,EAAcL,EAAUC,EAASC,IAkB9E,SAASQ,EAAO1O,EAAM2O,GACpB,OAAO,IAAIC,EAAQC,QAAQ7O,EAAM2O,GA2BnC,SAASG,EAAIH,GACX,OAAO,IAAIC,EAAQC,QAAQ,KAAMF,GAanC,SAASI,EAAMJ,GACb,OAAO,IAAIC,EAAQC,QAAQ,WAAYF,GAWzC,IAAIC,EAAU,CAEZI,QAAS,YAqBTC,GAAI,CACFC,SAAU,sCACVC,KAAM,iBACNC,OAAQ,gBACRC,KAAM,iBACNC,OAAQ,mBACRC,QAAS,oBACTC,WAAY,wCACZC,YAAa,yCACbC,IAAK,iCACLC,KAAM,mCACNC,OAAQ,mCACRC,QAAS,sCACTC,KAAM,mCACNC,QAAS,sCACTf,QAAS,oBACTgB,QAAS,sCACTC,SAAU,sCACVC,MAAO,gCASTA,MAAO,CACLC,KAAM,CAAC,IAAK,aAAc,KAAM,OAAQ,KAAM,MAAO,KAAM,KAAM,IAAK,OAAQ,SAAU,KAAM,QAC9FC,WAAY,CACV,EAAK,CAAC,QACN,WAAc,CAAC,SACf,GAAM,GACN,KAAQ,CAAC,SACT,GAAM,GACN,IAAO,CAAC,MAAO,MAAO,QAAS,SAAU,SACzC,GAAM,CAAC,SACP,GAAM,CAAC,SACP,EAAK,CAAC,SACN,KAAQ,CAAC,SACT,OAAU,GACV,GAAM,CAAC,SACP,KAAQ,IAEVC,IAAK,CAAC,mBAAoB,QAAS,cAAe,YAAa,aAAc,cAAe,cAAe,eAAgB,aAAc,mBASzIC,SAAU,SAAkBC,GAC1B,IAAK,IAAI9Q,EAAI,EAAGA,EAAImP,EAAQsB,MAAMC,KAAKzG,OAAQjK,IAC7C,GAAI8Q,IAAQ3B,EAAQsB,MAAMC,KAAK1Q,GAC7B,OAAO,EAIX,OAAO,GAUT+Q,eAAgB,SAAwBD,EAAKE,GAC3C,QAA6C,IAAlC7B,EAAQsB,MAAME,WAAWG,IAAwB3B,EAAQsB,MAAME,WAAWG,GAAK7G,OAAS,EACjG,IAAK,IAAIjK,EAAI,EAAGA,EAAImP,EAAQsB,MAAME,WAAWG,GAAK7G,OAAQjK,IACxD,GAAIgR,IAAc7B,EAAQsB,MAAME,WAAWG,GAAK9Q,GAC9C,OAAO,EAKb,OAAO,GAETiR,SAAU,SAAkBC,GAC1B,IAAK,IAAIlR,EAAI,EAAGA,EAAImP,EAAQsB,MAAMG,IAAI3G,OAAQjK,IAC5C,GAAIkR,IAAU/B,EAAQsB,MAAMG,IAAI5Q,GAC9B,OAAO,EAIX,OAAO,IAoBXmR,OAAQ,CACNC,MAAO,EACPC,WAAY,EACZC,SAAU,EACVC,eAAgB,EAChBC,SAAU,EACVC,UAAW,EACXC,aAAc,EACdC,cAAe,EACfC,SAAU,EACVC,SAAU,EACVC,YAAa,GACbC,aAAc,IAEhBC,eAAgB,CACdC,WAAY,aACZC,SAAU,WACVC,iBAAkB,6BAClBC,aAAc,eACdC,eAAgB,WAYlBC,SAAU,CACRC,MAAO,EACPC,KAAM,EACNC,KAAM,EACNrB,MAAO,EACPsB,MAAO,GAUTC,YAAa,CACXC,OAAQ,EACRC,KAAM,EACNC,MAAO,EACPC,SAAU,IAiBZC,QAAS,IACTC,kBAAmB,GAcnBC,aAAc,SAAsB3S,EAAMU,GACxCkO,EAAQK,GAAGjP,GAAQU,GAiBrBkS,aAAc,SAAsBC,EAAMC,EAAUC,GAClD,IAAK,IAAItT,EAAI,EAAGA,EAAIoT,EAAKG,WAAWtJ,OAAQjK,IAAK,CAC/C,IAAIwT,EAAYJ,EAAKG,WAAWvT,GAE5BwT,EAAUC,WAAatE,EAAQwD,YAAYC,QAAYS,IAAYK,KAAKC,WAAWH,EAAWH,IAChGC,EAAKE,KAkBXG,WAAY,SAAoBC,EAAIrT,GAClC,OAAOqT,EAAGC,UAAYtT,GAOxBuT,cAAe,KAMfC,eAAgB,WACd,IAAIC,EAWJ,YAP+CC,IAA3ClF,SAASmF,eAAeC,gBAAgCpF,SAASmF,eAAeC,gBAAkBpF,SAASqF,cAAgBrF,SAASqF,aAAe,IACrJJ,EAAMN,KAAKW,gBACPC,YAAYN,EAAIO,cAAc,YAElCP,EAAMjF,SAASmF,eAAeC,eAAe,gBAAiB,UAAW,MAGpEH,GASTQ,aAAc,WAKZ,OAJKrF,EAAQ2E,gBACX3E,EAAQ2E,cAAgB3E,EAAQ4E,kBAG3B5E,EAAQ2E,eAWjBO,aAAc,WAIZ,IAHA,IAAIL,EAAM,KACNS,EAAa,CAAC,yBAA0B,yBAA0B,yBAA0B,yBAA0B,qBAAsB,oBAAqB,oBAE5JnU,EAAI,EAAGA,EAAImU,EAAWxK,QACjB,OAAR+J,EADiC1T,IAEnC,IACE0T,EAAM,IAAIU,cAAcD,EAAWnU,IACnC,MAAOyM,GACPiH,EAAM,KAOZ,OAAOA,GAqBTW,WAAY,SAAoBpU,GAC9B,IAAKA,EACH,OAAO,KAMT,IAHA,IAAIqU,EAAOzF,EAAQqF,eAAeD,cAAchU,GAGvC6K,EAAI,EAAGA,EAAIyJ,UAAU5K,OAAQmB,IAAK,CACzC,IAAI0J,EAAMD,UAAUzJ,GAEpB,GAAK0J,EAIL,GAAmB,iBAARA,GAAmC,iBAARA,EACpCF,EAAKN,YAAYnF,EAAQ4F,YAAYD,SAChC,GAAqB,WAAjBtL,EAAQsL,IAAyC,mBAAbA,EAAIE,KACjD,IAAK,IAAIhV,EAAI,EAAGA,EAAI8U,EAAI7K,OAAQjK,IAAK,CACnC,IAAIiV,EAAOH,EAAI9U,GAEO,WAAlBwJ,EAAQyL,IAA2C,mBAAdA,EAAKD,WAAmCf,IAAZgB,EAAK,IAAgC,OAAZA,EAAK,IACjGL,EAAKM,aAAaD,EAAK,GAAIA,EAAK,SAG/B,GAAqB,WAAjBzL,EAAQsL,GACjB,IAAK,IAAIK,KAAKL,EACRpU,OAAOkB,UAAUC,eAAe1B,KAAK2U,EAAKK,SAAiBlB,IAAXa,EAAIK,IAA+B,OAAXL,EAAIK,IAC9EP,EAAKM,aAAaC,EAAGL,EAAIK,IAMjC,OAAOP,GAYTQ,UAAW,SAAmBC,GAM5B,OADAA,GADAA,GADAA,GADAA,GADAA,EAAOA,EAAKC,QAAQ,MAAO,UACfA,QAAQ,KAAM,SACdA,QAAQ,KAAM,SACdA,QAAQ,KAAM,WACdA,QAAQ,KAAM,WAa5BC,YAAa,SAAqBF,GAMhC,OADAA,GADAA,GADAA,GADAA,GADAA,EAAOA,EAAKC,QAAQ,UAAW,MACnBA,QAAQ,QAAS,MACjBA,QAAQ,QAAS,MACjBA,QAAQ,UAAW,MACnBA,QAAQ,UAAW,MAejCP,YAAa,SAAqBM,GAChC,OAAOlG,EAAQqF,eAAegB,eAAeH,IAY/CI,YAAa,SAAqBC,GAChC,IAAId,EAWJ,OATIe,UAEFf,GADa,IAAIe,WACHC,gBAAgBF,EAAM,cAEpCd,EAAO,IAAIF,cAAc,qBACpBmB,MAAQ,QACbjB,EAAKkB,QAAQJ,IAGRd,GAYTmB,QAAS,SAAiB3C,GACxB,IAAKA,EACH,OAAO,KAGT,IAAItI,EAAM,GAEqB,IAA3BsI,EAAKG,WAAWtJ,QAAgBmJ,EAAKK,WAAatE,EAAQwD,YAAYE,OACxE/H,GAAOsI,EAAK4C,WAGd,IAAK,IAAIhW,EAAI,EAAGA,EAAIoT,EAAKG,WAAWtJ,OAAQjK,IACtCoT,EAAKG,WAAWvT,GAAGyT,WAAatE,EAAQwD,YAAYE,OACtD/H,GAAOsI,EAAKG,WAAWvT,GAAGgW,WAI9B,OAAO7G,EAAQiG,UAAUtK,IAe3BmL,YAAa,SAAqB7C,GAChC,IAAIQ,EAEJ,GAAIR,EAAKK,WAAatE,EAAQwD,YAAYC,OAAQ,CAChDgB,EAAKzE,EAAQwF,WAAWvB,EAAKS,SAE7B,IAAK,IAAI7T,EAAI,EAAGA,EAAIoT,EAAKzC,WAAW1G,OAAQjK,IAC1C4T,EAAGsB,aAAa9B,EAAKzC,WAAW3Q,GAAGkW,SAAU9C,EAAKzC,WAAW3Q,GAAGiB,OAGlE,IAAK,IAAIkV,EAAK,EAAGA,EAAK/C,EAAKG,WAAWtJ,OAAQkM,IAC5CvC,EAAGU,YAAYnF,EAAQ8G,YAAY7C,EAAKG,WAAW4C,UAE5C/C,EAAKK,WAAatE,EAAQwD,YAAYE,OAC/Ce,EAAKzE,EAAQqF,eAAegB,eAAepC,EAAK4C,YAGlD,OAAOpC,GAeTwC,WAAY,SAAoBhD,GAC9B,IAAIQ,EAEJ,GAAIR,EAAKK,WAAatE,EAAQwD,YAAYC,OAAQ,CAChD,IAAI9B,EAAMsC,EAAK8C,SAASG,cAExB,GAAIlH,EAAQsB,MAAMI,SAASC,GACzB,IACE8C,EAAKzE,EAAQwF,WAAW7D,GAExB,IAAK,IAAI9Q,EAAI,EAAGA,EAAImP,EAAQsB,MAAME,WAAWG,GAAK7G,OAAQjK,IAAK,CAC7D,IAAIgR,EAAY7B,EAAQsB,MAAME,WAAWG,GAAK9Q,GAC1CiB,EAAQmS,EAAKkD,aAAatF,GAE9B,GAAI,MAAO/P,GAAqD,KAAVA,IAA0B,IAAVA,GAA6B,IAAVA,EASzF,GALkB,UAAd+P,GAA4C,WAAnBxH,EAAQvI,SAAgD,IAAlBA,EAAMsV,UACvEtV,EAAQA,EAAMsV,SAIE,UAAdvF,EAAuB,CAIzB,IAHA,IAAIJ,EAAM,GACN4F,EAAWvV,EAAMwV,MAAM,KAElB7J,EAAI,EAAGA,EAAI4J,EAASvM,OAAQ2C,IAAK,CACxC,IAAIqI,EAAOuB,EAAS5J,GAAG6J,MAAM,KACzBC,EAAUzB,EAAK,GAAGK,QAAQ,OAAQ,IAAIA,QAAQ,OAAQ,IAAIe,cAE9D,GAAIlH,EAAQsB,MAAMQ,SAASyF,GAAU,CACnC,IAAIC,EAAW1B,EAAK,GAAGK,QAAQ,OAAQ,IAAIA,QAAQ,OAAQ,IAC3D1E,EAAIgG,KAAKF,EAAU,KAAOC,IAI1B/F,EAAI3G,OAAS,IACfhJ,EAAQ2P,EAAIiG,KAAK,MACjBjD,EAAGsB,aAAalE,EAAW/P,SAG7B2S,EAAGsB,aAAalE,EAAW/P,GAI/B,IAAK,IAAI6V,EAAM,EAAGA,EAAM1D,EAAKG,WAAWtJ,OAAQ6M,IAC9ClD,EAAGU,YAAYnF,EAAQiH,WAAWhD,EAAKG,WAAWuD,KAEpD,MAAO/J,GAEP6G,EAAKzE,EAAQ4F,YAAY,QAEtB,CACLnB,EAAKzE,EAAQqF,eAAeuC,yBAE5B,IAAK,IAAIC,EAAM,EAAGA,EAAM5D,EAAKG,WAAWtJ,OAAQ+M,IAC9CpD,EAAGU,YAAYnF,EAAQiH,WAAWhD,EAAKG,WAAWyD,WAGjD,GAAI5D,EAAKK,WAAatE,EAAQwD,YAAYI,SAAU,CACzDa,EAAKzE,EAAQqF,eAAeuC,yBAE5B,IAAK,IAAIE,EAAM,EAAGA,EAAM7D,EAAKG,WAAWtJ,OAAQgN,IAC9CrD,EAAGU,YAAYnF,EAAQiH,WAAWhD,EAAKG,WAAW0D,UAE3C7D,EAAKK,WAAatE,EAAQwD,YAAYE,OAC/Ce,EAAKzE,EAAQ4F,YAAY3B,EAAK4C,YAGhC,OAAOpC,GAYTsD,WAAY,SAAoBtC,GAC9B,MAAoB,iBAATA,EACFA,EAGFA,EAAKU,QAAQ,aAAc,IAAIA,QAAQ,MAAO,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,MAAO,QAAQA,QAAQ,MAAO,QAAQA,QAAQ,MAAO,QAAQA,QAAQ,MAAO,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,QAAQA,QAAQ,KAAM,SAYjQ6B,aAAc,SAAsBvC,GAClC,MAAoB,iBAATA,EACFA,EAGFA,EAAKU,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,KAAKA,QAAQ,QAAS,OAYrO8B,eAAgB,SAAwBC,GACtC,OAAIA,EAAIC,QAAQ,KAAO,EACd,KAGFD,EAAIZ,MAAM,KAAK,IAYxBc,iBAAkB,SAA0BF,GAC1C,IAAIG,EAAOrI,EAAQsI,kBAAkBJ,GAErC,GAAIG,EAAKF,QAAQ,KAAO,EACtB,OAAOE,EAEP,IAAIE,EAAQF,EAAKf,MAAM,KAEvB,OADAiB,EAAMC,OAAO,EAAG,GACTD,EAAMb,KAAK,MAatBe,mBAAoB,SAA4BP,GAC9C,IAAKA,EACH,OAAO,KAGT,IAAItV,EAAIsV,EAAIZ,MAAM,KAElB,OAAI1U,EAAEkI,OAAS,EACN,MAGTlI,EAAE4V,OAAO,EAAG,GACL5V,EAAE8U,KAAK,OAYhBY,kBAAmB,SAA2BJ,GAC5C,OAAOA,EAAMA,EAAIZ,MAAM,KAAK,GAAK,MAMnCoB,aAAc,SAAsB9K,QACX,IAAZA,EAAE+K,OACX3I,EAAQ4I,MAAMhL,EAAE+K,OAGd/K,EAAEiL,UACJ7I,EAAQ4I,MAAM,UAAYrE,KAAKuE,QAAU,IAAMlL,EAAEiL,UAAY,IAAMjL,EAAEmL,KAAO,MAAQnL,EAAExM,KAAO,KAAOwM,EAAEoL,SAC7FpL,EAAEqL,SACXjJ,EAAQ4I,MAAM,UAAYrE,KAAKuE,QAAU,IAAMlL,EAAEqL,SAAW,IAAMrL,EAAEsL,WAAa,MAAQtL,EAAExM,KAAO,KAAOwM,EAAEoL,SAE3GhJ,EAAQ4I,MAAM,UAAYhL,EAAEoL,UAiChCG,IAAK,SAAaC,EAAOC,GACnBD,IAAU7E,KAAKpB,SAASI,OAAqC,WAA5BlJ,EAAQ5J,OAAO6Y,UAAyD,mBAAzB7Y,OAAO6Y,QAAQC,OACjG9Y,OAAO6Y,QAAQC,MAAMF,IAUzBG,MAAO,SAAeH,GACpB9E,KAAK4E,IAAI5E,KAAKpB,SAASC,MAAOiG,IAShCI,KAAM,SAAcJ,GAClB9E,KAAK4E,IAAI5E,KAAKpB,SAASE,KAAMgG,IAS/BK,KAAM,SAAcL,GAClB9E,KAAK4E,IAAI5E,KAAKpB,SAASG,KAAM+F,IAS/BE,MAAO,SAAeF,GACpB9E,KAAK4E,IAAI5E,KAAKpB,SAASlB,MAAOoH,IAShCT,MAAO,SAAeS,GACpB9E,KAAK4E,IAAI5E,KAAKpB,SAASI,MAAO8F,IAYhCM,UAAW,SAAmB1F,GAC5B,IAAKA,EACH,OAAO,KAGgB,mBAAdA,EAAK2F,OACd3F,EAAOA,EAAK2F,QAGd,IAAIC,EAAQpP,EAAmBE,MAAMsJ,EAAKzC,WAAW1G,QAAQgP,QAAQC,KAAI,SAAUlZ,GACjF,OAAOoT,EAAKzC,WAAW3Q,GAAGkW,YAG5B8C,EAAMhE,OACN,IAAImE,EAASH,EAAMI,QAAO,SAAUhO,EAAG3J,GACrC,MAAO,GAAGiM,OAAOtC,EAAG,KAAKsC,OAAOjM,EAAG,MAAOiM,OAAOyB,EAAQiG,UAAUhC,EAAKzC,WAAW0I,aAAa5X,GAAGR,OAAQ,OAC1G,IAAIyM,OAAO0F,EAAK8C,WAEnB,GAAI9C,EAAKG,WAAWtJ,OAAS,EAAG,CAC9BkP,GAAU,IAEV,IAAK,IAAInZ,EAAI,EAAGA,EAAIoT,EAAKG,WAAWtJ,OAAQjK,IAAK,CAC/C,IAAIsZ,EAAQlG,EAAKG,WAAWvT,GAE5B,OAAQsZ,EAAM7F,UACZ,KAAKtE,EAAQwD,YAAYC,OAEvBuG,GAAUhK,EAAQ2J,UAAUQ,GAC5B,MAEF,KAAKnK,EAAQwD,YAAYE,KAEvBsG,GAAUhK,EAAQiG,UAAUkE,EAAMtD,WAClC,MAEF,KAAK7G,EAAQwD,YAAYG,MAEvBqG,GAAU,YAAcG,EAAMtD,UAAY,OAIhDmD,GAAU,KAAO/F,EAAK8C,SAAW,SAEjCiD,GAAU,KAGZ,OAAOA,GAOTI,WAAY,EAMZC,mBAAoB,GASpBC,oBAAqB,SAA6BlZ,EAAMmZ,GACtDvK,EAAQqK,mBAAmBjZ,GAAQmZ,GA8CvCvK,QAAkB,SAAU5O,EAAM2O,GAEnB,aAAT3O,GAAgC,YAATA,GAA+B,OAATA,IAC3C2O,IAAUA,EAAMyK,MAClBzK,EAAMyK,MAAQxK,EAAQK,GAAGG,OACfT,IACVA,EAAQ,CACNyK,MAAOxK,EAAQK,GAAGG,UAMxB+D,KAAKkG,SAAWzK,EAAQwF,WAAWpU,EAAM2O,GAEzCwE,KAAKkB,KAAOlB,KAAKkG,WAGnBzK,EAAQC,QAAQxN,UAAY,CAU1BmX,KAAM,WACJ,OAAOrF,KAAKkG,UAadxP,SAAU,WACR,OAAO+E,EAAQ2J,UAAUpF,KAAKkG,WAahCC,GAAI,WAEF,OADAnG,KAAKkB,KAAOlB,KAAKkB,KAAKkF,WACfpG,MAaTpU,KAAM,WAEJ,OADAoU,KAAKkB,KAAOlB,KAAKkG,SACVlG,MAeTxE,MAAO,SAAe6K,GACpB,IAAK,IAAI5E,KAAK4E,EACRrZ,OAAOkB,UAAUC,eAAe1B,KAAK4Z,EAAW5E,UAC7BlB,IAAjB8F,EAAU5E,GACZzB,KAAKkB,KAAKoF,gBAAgB7E,GAE1BzB,KAAKkB,KAAKM,aAAaC,EAAG4E,EAAU5E,KAK1C,OAAOzB,MAmBTrT,EAAG,SAAWE,EAAM2O,EAAOmG,GACzB,IAAIiE,EAAQnK,EAAQwF,WAAWpU,EAAM2O,EAAOmG,GAO5C,OANA3B,KAAKkB,KAAKN,YAAYgF,GAEF,iBAATjE,GAAqC,iBAATA,IACrC3B,KAAKkB,KAAO0E,GAGP5F,MAiBTuG,MAAO,SAAe7G,GACpB,IAAI8G,EACAC,EAAShL,EAAQqF,eAErB,IACE0F,OAAgCjG,IAAtBkG,EAAOC,WACjB,MAAOrN,GACPmN,GAAU,EAGZ,IAAIG,EAAUH,EAAUC,EAAOC,WAAWhH,GAAM,GAAQjE,EAAQ8G,YAAY7C,GAG5E,OAFAM,KAAKkB,KAAKN,YAAY+F,GACtB3G,KAAKkB,KAAOyF,EACL3G,MAeTxS,EAAG,SAAWmU,GACZ,IAAIiE,EAAQnK,EAAQ4F,YAAYM,GAEhC,OADA3B,KAAKkB,KAAKN,YAAYgF,GACf5F,MAcT4G,EAAG,SAAW5E,GACZ,IAAI6E,EAAWxL,SAASwF,cAAc,QAEtCgG,EAASC,UAAY9E,EAIrB,IAFA,IAAI+E,EAAQtL,EAAQiH,WAAWmE,GAExBE,EAAMlH,WAAWtJ,OAAS,GAC/ByJ,KAAKkB,KAAKN,YAAYmG,EAAMlH,WAAW,IAGzC,OAAOG,OAiCXvE,EAAQuL,QAAU,SAAUzC,EAAS5W,EAAId,EAAMoa,EAAMC,EAAIvQ,EAAMwQ,GAC7DnH,KAAKuE,QAAUA,EACfvE,KAAKrS,GAAKA,EACVqS,KAAKnT,KAAOA,EACZmT,KAAKiH,KAAOA,EACZjH,KAAKkH,GAAKA,EACVlH,KAAKmH,QAAUA,GAAW,CACxB,kBAAoB,EACpB,yBAA2B,GAGzBnH,KAAKmH,QAAQC,YACf3L,EAAQ0J,KAAK,yEACbnF,KAAKmH,QAAQE,iBAAmBrH,KAAKmH,QAAQC,iBACtCpH,KAAKmH,QAAQC,WAGlBpH,KAAKmH,QAAQE,iBACfrH,KAAKrJ,KAAOA,EAAO8E,EAAQsI,kBAAkBpN,GAAQ,KAErDqJ,KAAKrJ,KAAOA,EAIdqJ,KAAKsH,MAAO,GAGd7L,EAAQuL,QAAQ9Y,UAAY,CAY1BqZ,aAAc,SAAsB7H,GAClC,IAAI8H,EAAc9H,EAAKkD,aAAa,SAMpC,OAJI4E,GAAexH,KAAKmH,QAAQM,0BAC9BD,EAAcA,EAAYzE,MAAM,KAAK,IAGhCyE,GAYTE,eAAgB,SAAwBhI,GACtC,IAAIiI,EAAQ3H,KAER4H,GAAU,EAEd,OAAK5H,KAAKrS,KAGR8N,EAAQgE,aAAaC,EAAM,MAAM,SAAUA,GACrCiI,EAAMJ,aAAa7H,KAAUiI,EAAMha,KACrCia,GAAU,MAGPA,GAAW5H,KAAKuH,aAAa7H,KAAUM,KAAKrS,KAavDka,QAAS,SAAiBnI,GACxB,IAAI/I,EAAO+I,EAAKkD,aAAa,QAEzB5C,KAAKmH,QAAQE,mBACf1Q,EAAO8E,EAAQsI,kBAAkBpN,IAGnC,IAAImR,EAAYpI,EAAKkD,aAAa,QAElC,SAAI5C,KAAK0H,eAAehI,IAAWM,KAAKnT,OAAQ4O,EAAQwE,WAAWP,EAAMM,KAAKnT,OAAYmT,KAAKiH,OAAS7Q,MAAMC,QAAQ2J,KAAKiH,OAA0C,IAAlCjH,KAAKiH,KAAKrD,QAAQkE,GAAoBA,IAAc9H,KAAKiH,OAAYjH,KAAKkH,IAAMxH,EAAKkD,aAAa,QAAU5C,KAAKkH,IAASlH,KAAKrJ,MAAQA,IAASqJ,KAAKrJ,OAiB1RoR,IAAK,SAAarI,GAChB,IAAI+F,EAAS,KAEb,IACEA,EAASzF,KAAKuE,QAAQ7E,GACtB,MAAOrG,GAGP,MAFAoC,EAAQ0I,aAAa9K,GAEfA,EAGR,OAAOoM,GAST/O,SAAU,WACR,MAAO,aAAesJ,KAAKuE,QAAU,IAAMvE,KAAKnT,KAAO,IAAMmT,KAAKkH,GAAK,IAAMlH,KAAKrS,GAAK,OA6B3F8N,EAAQuM,aAAe,SAAUC,EAAQ1D,GACvCvE,KAAKiI,OAASA,EACdjI,KAAKuE,QAAUA,EACfvE,KAAKkI,YAAa,IAAIC,MAAOC,UAC7BpI,KAAKsH,MAAO,GAGd7L,EAAQuM,aAAa9Z,UAAY,CAQ/B6Z,IAAK,WAEH,OADA/H,KAAKkI,YAAa,IAAIC,MAAOC,UACtBpI,KAAKuE,WAMd8D,MAAO,WACLrI,KAAKkI,YAAa,IAAIC,MAAOC,WAS/B1R,SAAU,WACR,MAAO,kBAAoBsJ,KAAKuE,QAAU,IAAMvE,KAAKiI,OAAS,OA4JlExM,EAAQ6M,WAAa,SAAUC,EAASpB,GACtC,IAAIqB,EAASxI,KAGbA,KAAKuI,QAAUA,EAEfvI,KAAKmH,QAAUA,GAAW,GAC1B,IAAIsB,EAAQzI,KAAKmH,QAAQuB,UAAY,GAsDrC,IAAK,IAAIjH,KApDsB,IAA3B8G,EAAQ3E,QAAQ,QAA4C,IAA5B2E,EAAQ3E,QAAQ,SAAyC,IAAxB6E,EAAM7E,QAAQ,MACjF5D,KAAK2I,OAAS,IAAIlN,EAAQmN,UAAU5I,MAEpCA,KAAK2I,OAAS,IAAIlN,EAAQoN,KAAK7I,MAKjCA,KAAK2D,IAAM,GAGX3D,KAAKlF,OAAS,KAGdkF,KAAK8I,SAAW,KAEhB9I,KAAK+I,WAAa,GAClB/I,KAAKgJ,YAAa,EAClBhJ,KAAKiJ,SAAU,EAEfjJ,KAAKkJ,cAAgB,GACrBlJ,KAAKmJ,SAAW,GAChBnJ,KAAKoJ,aAAe,GACpBpJ,KAAKqJ,eAAiB,GACtBrJ,KAAKsJ,UAAY,GACjBtJ,KAAKuJ,YAAc,GACnBvJ,KAAKwJ,sBAAwB,CAC3B,KAAQ,GACR,UAAa,IAEfxJ,KAAKyJ,aAAe,KACpBzJ,KAAK0J,mBAAqB,KAC1B1J,KAAK2J,eAAgB,EACrB3J,KAAK4J,WAAY,EACjB5J,KAAK6J,eAAgB,EACrB7J,KAAK8J,mBAAoB,EACzB9J,KAAK+J,QAAS,EACd/J,KAAKgK,UAAW,EAChBhK,KAAKiK,MAAQ,GACbjK,KAAKkK,UAAY,EACjBlK,KAAKmK,sBAAwB,KAC7BnK,KAAKoK,sBAAwB,KAC7BpK,KAAKqK,wBAA0B,KAE/BrK,KAAKsK,WAAa,EAElBtK,KAAKyJ,aAAec,YAAW,WAC7B,OAAO/B,EAAOgC,YACb,KACH/P,EAAiBuF,KAAKmH,QAAQxM,SAC9BqF,KAAKyK,uBAAuBzK,KAAKmH,QAAQuD,YAE3BjP,EAAQqK,mBACpB,GAAI9Y,OAAOkB,UAAUC,eAAe1B,KAAKgP,EAAQqK,mBAAoBrE,GAAI,CACvE,IAAIkJ,EAAI,aAERA,EAAEzc,UAAYuN,EAAQqK,mBAAmBrE,GACzCzB,KAAKyB,GAAK,IAAIkJ,EACd3K,KAAKyB,GAAGmJ,KAAK5K,QAKnBvE,EAAQ6M,WAAWpa,UAAY,CAO7Bma,MAAO,WACLrI,KAAK2I,OAAOkC,SAGZ7K,KAAKgJ,YAAa,EAClBhJ,KAAKiJ,SAAU,EAEfjJ,KAAKkJ,cAAgB,GACrBlJ,KAAKmJ,SAAW,GAChBnJ,KAAKoJ,aAAe,GACpBpJ,KAAKqJ,eAAiB,GACtBrJ,KAAKsJ,UAAY,GACjBtJ,KAAKuJ,YAAc,GACnBvJ,KAAK2J,eAAgB,EACrB3J,KAAK4J,WAAY,EACjB5J,KAAK6J,eAAgB,EACrB7J,KAAKgK,UAAW,EAChBhK,KAAKiK,MAAQ,GACbjK,KAAK8K,UAAY,GACjB9K,KAAKkK,UAAY,GAYnBa,MAAO,WACL/K,KAAK+J,QAAS,GAQhBiB,OAAQ,WACNhL,KAAK+J,QAAS,GAyBhBkB,YAAa,SAAqBC,GAChC,IAAIC,EAAO,uCAAuCvJ,QAAQ,SAAS,SAAUjV,GAC3E,IAAIS,EAAoB,GAAhBge,KAAKC,SAAgB,EAE7B,OADc,MAAN1e,EAAYS,EAAQ,EAAJA,EAAU,GACzBsJ,SAAS,OAGpB,MAAsB,iBAAXwU,GAAyC,iBAAXA,EAChCC,EAAO,IAAMD,EAEbC,EAAO,IA0BlBG,wBAAyB,SAAiC5C,EAAU6C,EAAaC,GAC/ExL,KAAKwJ,sBAAsBd,GAAU6C,GAAeC,GA2CtDC,QAAS,SAAiB9H,EAAK+H,EAAMF,EAAUG,EAAMC,EAAMC,EAAOC,GAChE9L,KAAK2D,IAAMA,EAKX3D,KAAK+L,QAAUtQ,EAAQsI,kBAAkB/D,KAAK2D,KAK9C3D,KAAK8L,QAAUA,GAAWrQ,EAAQiI,eAAe1D,KAAK2D,KAKtD3D,KAAK0L,KAAOA,EAKZ1L,KAAKgM,SAAW,OAChBhM,KAAKiM,iBAAmBT,EACxBxL,KAAK6J,eAAgB,EACrB7J,KAAK4J,WAAY,EACjB5J,KAAK2J,eAAgB,EACrB3J,KAAKgK,UAAW,EAEhBhK,KAAKlF,OAASW,EAAQoI,iBAAiB7D,KAAK2D,KAE5C3D,KAAKkM,qBAAqBzQ,EAAQgC,OAAOE,WAAY,MAErDqC,KAAK2I,OAAOwD,SAASR,EAAMC,EAAMC,IA2BnCO,OAAQ,SAAgBzI,EAAK0I,EAAKC,EAAKd,EAAUG,EAAMC,EAAMW,GAC3D,KAAIvM,KAAK2I,kBAAkBlN,EAAQoN,MAE5B,CACL,IAAI7D,EAAQ,IAAI3N,MAAM,gEAEtB,MADA2N,EAAMnY,KAAO,sBACPmY,EAJNhF,KAAK2I,OAAO6D,QAAQ7I,EAAK0I,EAAKC,EAAKd,EAAUG,EAAMC,EAAMW,IAmC7DE,QAAS,SAAiB9I,EAAK6H,EAAUG,EAAMC,EAAMW,GACnD,IAAIvM,KAAK0M,2BAEF,CACL,IAAI1H,EAAQ,IAAI3N,MAAM,iEAEtB,MADA2N,EAAMnY,KAAO,sBACPmY,EAJNhF,KAAK2I,OAAOgE,SAAShJ,EAAK6H,EAAUG,EAAMC,EAAMW,IAYpDG,yBAA0B,WACxB,GAAI1M,KAAK2I,kBAAkBlN,EAAQoN,KAAM,CACvC,IAAK+D,KACH,OAAO,EAGT,IACEC,eAAeC,QAAQ,YAAa,aACpCD,eAAeE,WAAW,aAC1B,MAAO1T,GACP,OAAO,EAGT,OAAO,EAGT,OAAO,GAqBT2T,SAAU,SAAkBtN,KAsB5BuN,UAAW,SAAmBvN,KAgB9BwN,SAAU,SAAkBvT,KAgB5BwT,UAAW,SAAmBxT,KAe9ByT,aAAc,SAAsBd,KAgBpCe,KAAM,SAAc3N,GAClB,GAAa,OAATA,EAAJ,CAIA,GAAyB,mBAAdA,EAAK4B,KACd,IAAK,IAAIhV,EAAI,EAAGA,EAAIoT,EAAKnJ,OAAQjK,IAC/B0T,KAAKsN,WAAW5N,EAAKpT,QAEO,mBAAdoT,EAAK2F,KACrBrF,KAAKsN,WAAW5N,EAAK2F,QAErBrF,KAAKsN,WAAW5N,GAGlBM,KAAK2I,OAAO4E,UAWdC,MAAO,WAGLC,aAAazN,KAAKyJ,cAElBzJ,KAAKwK,WAmBPkD,aAAc,SAAsBhO,EAAM8L,EAAUmC,EAASC,GAC3D,IAAIC,EAAS7N,KAET8N,EAAiB,KAEI,mBAAdpO,EAAK2F,OACd3F,EAAOA,EAAK2F,QAGd,IAAI6B,EAAKxH,EAAKkD,aAAa,MAQ3B,GANKsE,IAEHA,EAAKlH,KAAKiL,YAAY,gBACtBvL,EAAK8B,aAAa,KAAM0F,IAGF,mBAAbsE,GAA8C,mBAAZmC,EAAwB,CACnE,IAAIpJ,EAAUvE,KAAK+N,YAAW,SAAUC,GAElCF,GACFD,EAAOI,mBAAmBH,GAGQ,UAAhCE,EAAOpL,aAAa,QAClB+K,GACFA,EAAQK,GAEDxC,GACTA,EAASwC,KAEV,KAAM,WAAY,KAAM9G,GAEvB0G,IACFE,EAAiB9N,KAAKkO,gBAAgBN,GAAS,WAS7C,OAPAC,EAAOM,cAAc5J,GAGjBoJ,GACFA,EAAQ,OAGH,MAMb,OADA3N,KAAKqN,KAAK3N,GACHwH,GAiBTkH,OAAQ,SAAgB1O,EAAM8L,EAAUmC,EAASC,GAC/C,IAAIS,EAASrO,KAET8N,EAAiB,KAEI,mBAAdpO,EAAK2F,OACd3F,EAAOA,EAAK2F,QAGd,IAAI6B,EAAKxH,EAAKkD,aAAa,MAQ3B,GANKsE,IAEHA,EAAKlH,KAAKiL,YAAY,UACtBvL,EAAK8B,aAAa,KAAM0F,IAGF,mBAAbsE,GAA8C,mBAAZmC,EAAwB,CACnE,IAAIpJ,EAAUvE,KAAK+N,YAAW,SAAUC,GAElCF,GACFO,EAAOJ,mBAAmBH,GAG5B,IAAIQ,EAASN,EAAOpL,aAAa,QAEjC,GAAe,WAAX0L,EACE9C,GACFA,EAASwC,OAEN,IAAe,UAAXM,EAIJ,CACL,IAAItJ,EAAQ,IAAI3N,MAAM,sBAAsB2C,OAAOsU,IAEnD,MADAtJ,EAAMnY,KAAO,eACPmY,EANF2I,GACFA,EAAQK,MAOX,KAAM,KAAM,CAAC,QAAS,UAAW9G,GAEhC0G,IACFE,EAAiB9N,KAAKkO,gBAAgBN,GAAS,WAS7C,OAPAS,EAAOF,cAAc5J,GAGjBoJ,GACFA,EAAQ,OAGH,MAMb,OADA3N,KAAKqN,KAAK3N,GACHwH,GAOToG,WAAY,SAAoBiB,GAC9B,GAAgB,OAAZA,IAAqBA,EAAQpO,UAAYoO,EAAQ1O,WAAY,CAC/D,IAAImF,EAAQ,IAAI3N,MAAM,gCAEtB,MADA2N,EAAMnY,KAAO,eACPmY,EAGRhF,KAAKiK,MAAM/G,KAAKqL,IAMlBC,aAAc,WACZ,IAAIC,EAASzO,KAEbA,KAAKiK,MAAM/G,KAAK,WAEhBlD,KAAK2I,OAAO6F,eAEZxO,KAAKyJ,aAAec,YAAW,WAC7B,OAAOkE,EAAOjE,YACb,MAyBL0D,gBAAiB,SAAyBjG,EAAQ1D,GAChD,IAAImK,EAAQ,IAAIjT,EAAQuM,aAAaC,EAAQ1D,GAE7C,OADAvE,KAAKsJ,UAAUpG,KAAKwL,GACbA,GAaTT,mBAAoB,SAA4BU,GAG9C3O,KAAKoJ,aAAalG,KAAKyL,IAmEzBZ,WAAY,SAAoBxJ,EAAS5W,EAAId,EAAMoa,EAAMC,EAAIvQ,EAAMwQ,GACjE,IAAIyH,EAAO,IAAInT,EAAQuL,QAAQzC,EAAS5W,EAAId,EAAMoa,EAAMC,EAAIvQ,EAAMwQ,GAElE,OADAnH,KAAKuJ,YAAYrG,KAAK0L,GACfA,GAaTT,cAAe,SAAuBQ,GAGpC3O,KAAKqJ,eAAenG,KAAKyL,GAGzB,IAAIriB,EAAI0T,KAAKuJ,YAAY3F,QAAQ+K,GAE7BriB,GAAK,GACP0T,KAAKuJ,YAAYtF,OAAO3X,EAAG,IAa/Bme,uBAAwB,SAAgCC,GACtD1K,KAAK0K,WAAa,IAClBA,EAAaA,GAAc,CAACjP,EAAQoT,cAAepT,EAAQqT,aAAcrT,EAAQsT,QAAStT,EAAQuT,gBAAiBvT,EAAQwT,YAAaxT,EAAQyT,UAAWzT,EAAQ0T,WACxJC,QAAQpP,KAAKqP,sBAAsBvhB,KAAKkS,QAWrDqP,sBAAuB,SAA+BC,GACpDtP,KAAK0K,WAAW4E,EAAUphB,UAAUrB,MAAQyiB,GAmB9CC,WAAY,SAAoBC,GAK9B,GAJAxP,KAAKkM,qBAAqBzQ,EAAQgC,OAAOQ,cAAeuR,GAExD/T,EAAQ0J,KAAK,kCAAoCqK,GAE7CxP,KAAK4J,UAAW,CAClB,IAAI6F,GAAO,EACXzP,KAAK6J,eAAgB,EAEjB7J,KAAK2J,gBACP8F,EAAO7T,EAAM,CACX,MAASH,EAAQK,GAAGG,OACpB,KAAQ,iBAKZ+D,KAAK0J,mBAAqB1J,KAAK0P,oBAAoB,IAAM1P,KAAK2P,qBAAqB7hB,KAAKkS,OAExFA,KAAK2I,OAAOiH,YAAYH,QAExBhU,EAAQ0J,KAAK,gEAEbnF,KAAK2I,OAAOkH,oBAEZ7P,KAAK8P,iBAcT5D,qBAAsB,SAA8B6D,EAAQC,EAAWtQ,GAErE,IAAK,IAAI+B,KAAKhG,EAAQqK,mBACpB,GAAI9Y,OAAOkB,UAAUC,eAAe1B,KAAKgP,EAAQqK,mBAAoBrE,GAAI,CACvE,IAAIwO,EAASjQ,KAAKyB,GAElB,GAAIwO,EAAOC,cACT,IACED,EAAOC,cAAcH,EAAQC,GAC7B,MAAOG,GACP1U,EAAQuJ,MAAM,GAAGhL,OAAOyH,EAAG,iDAAiDzH,OAAOmW,KAO3F,GAAInQ,KAAKiM,iBACP,IACEjM,KAAKiM,iBAAiB8D,EAAQC,EAAWtQ,GACzC,MAAOrG,GACPoC,EAAQ0I,aAAa9K,GAErBoC,EAAQuJ,MAAM,iDAAiDhL,OAAOX,MAW5EyW,cAAe,SAAuBE,GACH,iBAAtBhQ,KAAKyJ,cACdgE,aAAazN,KAAKyJ,cAIY,OAA5BzJ,KAAK0J,qBACP1J,KAAKiO,mBAAmBjO,KAAK0J,oBAC7B1J,KAAK0J,mBAAqB,MAG5BjO,EAAQwJ,MAAM,4BAEdjF,KAAK2I,OAAOmH,gBAEZ9P,KAAK2J,eAAgB,EACrB3J,KAAK6J,eAAgB,EACrB7J,KAAKgK,UAAW,EAEhBhK,KAAKmJ,SAAW,GAChBnJ,KAAKkJ,cAAgB,GACrBlJ,KAAKoJ,aAAe,GACpBpJ,KAAKqJ,eAAiB,GACtBrJ,KAAKsJ,UAAY,GACjBtJ,KAAKuJ,YAAc,GAEnBvJ,KAAKkM,qBAAqBzQ,EAAQgC,OAAOO,aAAcgS,GAEvDhQ,KAAK4J,WAAY,GAenBwG,UAAW,SAAmBC,EAAKC,GACjC,IAAIC,EAASvQ,KAEbvE,EAAQwJ,MAAM,oBAEd,IAAIvF,EAAOM,KAAK2I,OAAO6H,WAAWH,GAElC,GAAa,OAAT3Q,EAAJ,CAqBA,IAjBIM,KAAKgN,WAAavR,EAAQ6M,WAAWpa,UAAU8e,WAC7CtN,EAAK8C,WAAaxC,KAAK2I,OAAO8H,OAAS/Q,EAAKG,WAAWtJ,OACzDyJ,KAAKgN,SAAStN,EAAKG,WAAW,IAE9BG,KAAKgN,SAAStN,IAIdM,KAAKkN,WAAazR,EAAQ6M,WAAWpa,UAAUgf,WAC7CoD,EACFtQ,KAAKkN,SAASoD,GAEdtQ,KAAKkN,SAASzR,EAAQ2J,UAAU1F,KAK7BM,KAAKqJ,eAAe9S,OAAS,GAAG,CACrC,IAAIqY,EAAO5O,KAAKqJ,eAAeqH,MAC3BpkB,EAAI0T,KAAKmJ,SAASvF,QAAQgL,GAE1BtiB,GAAK,GACP0T,KAAKmJ,SAASlF,OAAO3X,EAAG,GAK5B,KAAO0T,KAAKuJ,YAAYhT,OAAS,GAC/ByJ,KAAKmJ,SAASjG,KAAKlD,KAAKuJ,YAAYmH,OAItC,GAAI1Q,KAAK6J,eAAiB7J,KAAK2I,OAAOgI,cACpC3Q,KAAK8P,oBADP,CAMA,IAAI7I,EAAOvH,EAAKkD,aAAa,QAE7B,GAAa,OAATqE,GAA0B,cAATA,EAAsB,CAEzC,GAAIjH,KAAK6J,cACP,OAIF,IAAI+G,EAAOlR,EAAKkD,aAAa,aACzBiO,EAAWnR,EAAKoR,qBAAqB,YAczC,OAZa,OAATF,GACW,wBAATA,GAAkCC,EAASta,OAAS,IACtDqa,EAAO,YAGT5Q,KAAKkM,qBAAqBzQ,EAAQgC,OAAOG,SAAUgT,IAEnD5Q,KAAKkM,qBAAqBzQ,EAAQgC,OAAOG,SAAUnC,EAAQ6C,eAAeyS,oBAG5E/Q,KAAK8P,cAAcc,GAMrBnV,EAAQgE,aAAaC,EAAM,MAAM,SAAUkG,GAEzC,IAAIoL,EAAUT,EAAOpH,SACrBoH,EAAOpH,SAAW,GAElB,IAAK,IAAI8H,EAAM,EAAGA,EAAMD,EAAQza,OAAQ0a,IAAO,CAC7C,IAAIC,EAAQF,EAAQC,GAGpB,MACMC,EAAMrJ,QAAQjC,KAAW2K,EAAO5G,eAAkBuH,EAAM5J,MACtD4J,EAAMnJ,IAAInC,KAId2K,EAAOpH,SAASjG,KAAKgO,GAEvB,MAAO7X,GAEPoC,EAAQ0J,KAAK,wDAA0D9L,EAAEoL,iBASjFiG,WAAY,GAkBZyG,YAAa,SAAqBd,EAAKe,EAAWd,GAGhD,IAAIe,EAFJ5V,EAAQwJ,MAAM,0BACdjF,KAAK4J,WAAY,EAGjB,IACEyH,EAAWrR,KAAK2I,OAAO6H,WAAWH,GAClC,MAAOhX,GACP,GAAIA,EAAExM,OAAS4O,EAAQ6C,eAAeC,WACpC,MAAMlF,EAGR2G,KAAKkM,qBAAqBzQ,EAAQgC,OAAOG,SAAUnC,EAAQ6C,eAAeC,YAE1EyB,KAAK8P,cAAcrU,EAAQ6C,eAAeC,YAG5C,GAAK8S,IAIDrR,KAAKgN,WAAavR,EAAQ6M,WAAWpa,UAAU8e,WAC7CqE,EAAS7O,WAAaxC,KAAK2I,OAAO8H,OAASY,EAASxR,WAAWtJ,OACjEyJ,KAAKgN,SAASqE,EAASxR,WAAW,IAElCG,KAAKgN,SAASqE,IAIdrR,KAAKkN,WAAazR,EAAQ6M,WAAWpa,UAAUgf,WAC7CoD,EACFtQ,KAAKkN,SAASoD,GAEdtQ,KAAKkN,SAASzR,EAAQ2J,UAAUiM,KAIpBrR,KAAK2I,OAAOwI,YAAYE,KAEtB5V,EAAQgC,OAAOG,UAajC,GANIyT,EAASC,uBACGD,EAASC,uBAAuB7V,EAAQK,GAAGW,OAAQ,YAAYlG,OAAS,EAExE8a,EAASP,qBAAqB,mBAAmBva,OAAS,GAAK8a,EAASP,qBAAqB,YAAYva,OAAS,EAGlI,CAMA,IAAIgb,EAAU,GACV7G,EAAa2G,EAASP,qBAAqB,aAE/C,GAAIpG,EAAWnU,OAAS,EACtB,IAAK,IAAIjK,EAAI,EAAGA,EAAIoe,EAAWnU,OAAQjK,IAAK,CAC1C,IAAIklB,EAAO/V,EAAQ4G,QAAQqI,EAAWpe,IAClC0T,KAAK0K,WAAW8G,IAAOD,EAAQrO,KAAKlD,KAAK0K,WAAW8G,IAIrC,IAAnBD,EAAQhb,QAC2C,IAAjD8a,EAASP,qBAAqB,QAAQva,QASb,IAA3ByJ,KAAK8J,mBACP9J,KAAKyR,aAAaF,GAPhBvR,KAAK2I,OAAO+I,kBAAkBN,QAnBhCpR,KAAK2I,OAAO+I,kBAAkBN,IAuClCO,yBAA0B,SAAkCjH,GAE1D,IAAK,IAAIpe,EAAI,EAAGA,EAAIoe,EAAWnU,OAAS,IAAKjK,EAAG,CAG9C,IAFA,IAAIslB,EAAStlB,EAEJ4M,EAAI5M,EAAI,EAAG4M,EAAIwR,EAAWnU,SAAU2C,EACvCwR,EAAWxR,GAAGhL,UAAU2jB,SAAWnH,EAAWkH,GAAQ1jB,UAAU2jB,WAClED,EAAS1Y,GAIb,GAAI0Y,IAAWtlB,EAAG,CAChB,IAAIwlB,EAAOpH,EAAWpe,GACtBoe,EAAWpe,GAAKoe,EAAWkH,GAC3BlH,EAAWkH,GAAUE,GAIzB,OAAOpH,GAgBT+G,aAAc,SAAsBF,GAC7BvR,KAAK+R,iBAAiBR,IACzBvR,KAAKgS,sBAiBTD,iBAAkB,SAA0BrH,GAC1CA,EAAa1K,KAAK2R,yBAAyBjH,GAAc,IAGzD,IAFA,IAAIuH,GAAkB,EAEb3lB,EAAI,EAAGA,EAAIoe,EAAWnU,SAAUjK,EACvC,GAAKoe,EAAWpe,GAAG4B,UAAUgkB,KAAKlS,MAAlC,CAIAA,KAAKmK,sBAAwBnK,KAAKmS,eAAenS,KAAKoS,iBAAiBtkB,KAAKkS,MAAO,KAAM,UAAW,KAAM,MAC1GA,KAAKoK,sBAAwBpK,KAAKmS,eAAenS,KAAKqS,iBAAiBvkB,KAAKkS,MAAO,KAAM,UAAW,KAAM,MAC1GA,KAAKqK,wBAA0BrK,KAAKmS,eAAenS,KAAKsS,mBAAmBxkB,KAAKkS,MAAO,KAAM,YAAa,KAAM,MAChHA,KAAKuS,gBAAkB,IAAI7H,EAAWpe,GAEtC0T,KAAKuS,gBAAgBC,QAAQxS,MAE7B,IAAIyS,EAAwBlX,EAAO,OAAQ,CACzC,MAASE,EAAQK,GAAGU,KACpB,UAAawD,KAAKuS,gBAAgB1lB,OAGpC,GAAImT,KAAKuS,gBAAgBG,cAAe,CACtC,IAAIC,EAAW3S,KAAKuS,gBAAgBK,YAAY5S,KAAM,MAEtDyS,EAAsBjlB,EAAEqlB,KAAKF,IAG/B3S,KAAKqN,KAAKoF,EAAsBpN,QAChC4M,GAAkB,EAClB,MAGF,OAAOA,GAOTK,mBAAoB,SAA4B5S,GAC9C,IAAIoT,EAAYC,KAAKtX,EAAQ4G,QAAQ3C,IAEjCiT,EAAW3S,KAAKuS,gBAAgBK,YAAY5S,KAAM8S,GAElD9E,EAASzS,EAAO,WAAY,CAC9B,MAASE,EAAQK,GAAGU,OAQtB,MALiB,KAAbmW,GACF3E,EAAOxgB,EAAEqlB,KAAKF,IAGhB3S,KAAKqN,KAAKW,EAAO3I,SACV,GAOT2M,mBAAoB,WACuB,OAArCvW,EAAQiI,eAAe1D,KAAK2D,MAG9B3D,KAAKkM,qBAAqBzQ,EAAQgC,OAAOG,SAAUnC,EAAQ6C,eAAeG,kBAE1EuB,KAAKuP,WAAW9T,EAAQ6C,eAAeG,oBAGvCuB,KAAKkM,qBAAqBzQ,EAAQgC,OAAOI,eAAgB,MAEzDmC,KAAKmS,eAAenS,KAAKgT,sBAAsBllB,KAAKkS,MAAO,KAAM,KAAM,KAAM,WAE7EA,KAAKqN,KAAK1R,EAAI,CACZ,KAAQ,MACR,GAAMqE,KAAKlF,OACX,GAAM,YACLnO,EAAE,QAAS,CACZsZ,MAAOxK,EAAQK,GAAGI,OACjBvP,EAAE,WAAY,IAAIa,EAAEiO,EAAQiI,eAAe1D,KAAK2D,MAAM0B,UAkB7D2N,sBAAuB,SAA+BtT,GAEpD,IAAIuT,EAAKtX,EAAI,CACXsL,KAAM,MACNC,GAAI,YACHva,EAAE,QAAS,CACZsZ,MAAOxK,EAAQK,GAAGI,OACjBvP,EAAE,WAAY,IAAIa,EAAEiO,EAAQiI,eAAe1D,KAAK2D,MAAMwC,KAAKxZ,EAAE,YAAYa,EAAEwS,KAAK0L,MAcnF,OAZKjQ,EAAQyI,mBAAmBlE,KAAK2D,OAInC3D,KAAK2D,IAAMlI,EAAQsI,kBAAkB/D,KAAK2D,KAAO,YAGnDsP,EAAG9M,KAAKxZ,EAAE,WAAY,IAAIa,EAAEiO,EAAQyI,mBAAmBlE,KAAK2D,MAE5D3D,KAAKmS,eAAenS,KAAKkT,UAAUplB,KAAKkS,MAAO,KAAM,KAAM,KAAM,WAEjEA,KAAKqN,KAAK4F,EAAG5N,SACN,GAYT+M,iBAAkB,SAA0B1S,GAC1C,IAAIyT,EAASnT,KAEb,GAAIA,KAAK+I,WAAW,oBAAqB,CACvC,IAAIqK,EAGAC,EAFUN,KAAKtX,EAAQ4G,QAAQ3C,IAEb4T,MADJ,yBAOlB,GAJmB,MAAfD,EAAQ,KACVD,EAAkBC,EAAQ,IAGxBD,IAAoBpT,KAAK+I,WAAW,oBAWtC,OATA/I,KAAKmO,cAAcnO,KAAKoK,uBACxBpK,KAAKoK,sBAAwB,KAEzBpK,KAAKqK,0BACPrK,KAAKmO,cAAcnO,KAAKqK,yBACxBrK,KAAKqK,wBAA0B,MAGjCrK,KAAK+I,WAAa,GACX/I,KAAKqS,iBAAiB,MAIjC5W,EAAQyJ,KAAK,kCAETlF,KAAKuS,iBACPvS,KAAKuS,gBAAgBgB,YAIvBvT,KAAKmO,cAAcnO,KAAKoK,uBACxBpK,KAAKoK,sBAAwB,KAEzBpK,KAAKqK,0BACPrK,KAAKmO,cAAcnO,KAAKqK,yBACxBrK,KAAKqK,wBAA0B,MAGjC,IAAImJ,EAAyB,GAEzBC,EAAU,SAAiBtK,EAAUzJ,GACvC,KAAOyJ,EAAS5S,QACd4c,EAAOhF,cAAchF,EAASuH,OAKhC,OAFAyC,EAAOO,2BAA2BhU,IAE3B,GAYT,OATA8T,EAAuBtQ,KAAKlD,KAAKmS,gBAAe,SAAUzS,GACxD,OAAO+T,EAAQD,EAAwB9T,KACtC,KAAM,kBAAmB,KAAM,OAClC8T,EAAuBtQ,KAAKlD,KAAKmS,gBAAe,SAAUzS,GACxD,OAAO+T,EAAQD,EAAwB9T,KACtCjE,EAAQK,GAAGW,OAAQ,WAAY,KAAM,OAExCuD,KAAKwO,gBAEE,GAUTkF,2BAA4B,SAAoChU,GAE9DM,KAAK8I,SAAWpJ,EAEhB,IAAK,IAAIpT,EAAI,EAAGA,EAAIoT,EAAKG,WAAWtJ,OAAQjK,IAAK,CAC/C,IAAIsZ,EAAQlG,EAAKG,WAAWvT,GAEL,SAAnBsZ,EAAMpD,WACRxC,KAAKiJ,SAAU,GAGM,YAAnBrD,EAAMpD,WACRxC,KAAKgJ,YAAa,GAItB,OAAKhJ,KAAKiJ,SAIEjJ,KAAKmH,QAAQwM,wBAGvB3T,KAAKkM,qBAAqBzQ,EAAQgC,OAAOY,aAAc,MAFvD2B,KAAKlS,QAKA,IATLkS,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAU,OAE5C,IAuBXhQ,KAAM,WACJ,GAAKkS,KAAKiJ,QAAV,CAKAjJ,KAAKmS,eAAenS,KAAK4T,wBAAwB9lB,KAAKkS,MAAO,KAAM,KAAM,KAAM,gBAE/E,IAAI6T,EAAWpY,EAAQyI,mBAAmBlE,KAAK2D,KAE3CkQ,EACF7T,KAAKqN,KAAK1R,EAAI,CACZsL,KAAM,MACNC,GAAI,iBACHva,EAAE,OAAQ,CACXsZ,MAAOxK,EAAQK,GAAGa,OACjBhQ,EAAE,WAAY,IAAIa,EAAEqmB,GAAUxO,QAEjCrF,KAAKqN,KAAK1R,EAAI,CACZsL,KAAM,MACNC,GAAI,iBACHva,EAAE,OAAQ,CACXsZ,MAAOxK,EAAQK,GAAGa,OACjB0I,aArBH5J,EAAQmJ,IAAInJ,EAAQmD,SAASE,KAAM,oEAkCvC8U,wBAAyB,SAAiClU,GAGtD,IACIsQ,EAHN,GAAkC,UAA9BtQ,EAAKkD,aAAa,QAWpB,OAVAnH,EAAQ0J,KAAK,4BACEzF,EAAKoR,qBAAqB,YAG5Bva,OAAS,IACpByZ,EAAYvU,EAAQ6C,eAAeE,UAGrCwB,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAUkS,EAAWtQ,IAEvD,EAIT,IAAI5R,EAAO4R,EAAKoR,qBAAqB,QAErC,KAAIhjB,EAAKyI,OAAS,GAmBhB,OAJAkF,EAAQ0J,KAAK,4BAEbnF,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAU,KAAM4B,IAElD,EAlBP,IAAIoU,EAAUhmB,EAAK,GAAGgjB,qBAAqB,OAEvCgD,EAAQvd,OAAS,IACnByJ,KAAK2D,IAAMlI,EAAQ4G,QAAQyR,EAAQ,IAE/B9T,KAAKgJ,WACPhJ,KAAK+T,qBAEL/T,KAAK2J,eAAgB,EAErB3J,KAAKkM,qBAAqBzQ,EAAQgC,OAAOM,UAAW,SAoB5DgW,kBAAmB,WACjB,IAAK/T,KAAKgJ,WACR,MAAM,IAAI3R,MAAM,kDAAoD,yBAAyB2C,OAAOyB,EAAQK,GAAGc,QAAS,qCAG1HoD,KAAKmS,eAAenS,KAAKgU,mBAAmBlmB,KAAKkS,MAAO,KAAM,KAAM,KAAM,mBAE1EA,KAAKqN,KAAK1R,EAAI,CACZsL,KAAM,MACNC,GAAI,oBACHva,EAAE,UAAW,CACdsZ,MAAOxK,EAAQK,GAAGc,UACjByI,SAqBL2O,mBAAoB,SAA4BtU,GAC9C,GAAkC,WAA9BA,EAAKkD,aAAa,QACpB5C,KAAK2J,eAAgB,EAErB3J,KAAKkM,qBAAqBzQ,EAAQgC,OAAOM,UAAW,WAC/C,GAAkC,UAA9B2B,EAAKkD,aAAa,QAK3B,OAJAnH,EAAQ0J,KAAK,4BAEbnF,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAU,KAAM4B,IAElD,EAGT,OAAO,GAYT2S,iBAAkB,SAA0B3S,GAgB1C,OAdIM,KAAKmK,wBACPnK,KAAKmO,cAAcnO,KAAKmK,uBACxBnK,KAAKmK,sBAAwB,MAG3BnK,KAAKqK,0BACPrK,KAAKmO,cAAcnO,KAAKqK,yBACxBrK,KAAKqK,wBAA0B,MAG7BrK,KAAKuS,iBAAiBvS,KAAKuS,gBAAgB0B,YAE/CjU,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAU,KAAM4B,IAElD,GAeTwT,UAAW,SAAmBxT,GAW5B,MAVkC,WAA9BA,EAAKkD,aAAa,SACpB5C,KAAK2J,eAAgB,EAErB3J,KAAKkM,qBAAqBzQ,EAAQgC,OAAOM,UAAW,OACb,UAA9B2B,EAAKkD,aAAa,UAC3B5C,KAAKkM,qBAAqBzQ,EAAQgC,OAAOK,SAAU,KAAM4B,GAEzDM,KAAKuP,WAAW,2BAGX,GAcTG,oBAAqB,SAA6BzH,EAAQ1D,GACxD,IAAImK,EAAQ,IAAIjT,EAAQuM,aAAaC,EAAQ1D,GAG7C,OAFAmK,EAAMpH,MAAO,EACbtH,KAAKsJ,UAAUpG,KAAKwL,GACbA,GAiBTyD,eAAgB,SAAwB5N,EAAS5W,EAAId,EAAMoa,EAAMC,GAC/D,IAAI0H,EAAO,IAAInT,EAAQuL,QAAQzC,EAAS5W,EAAId,EAAMoa,EAAMC,GAGxD,OAFA0H,EAAKtH,MAAO,EACZtH,KAAKuJ,YAAYrG,KAAK0L,GACfA,GAYTe,qBAAsB,WAUpB,OATAlU,EAAQwJ,MAAM,mCAEdjF,KAAKkM,qBAAqBzQ,EAAQgC,OAAOW,YAAa,MAEtD4B,KAAK2I,OAAOgH,uBAGZ3P,KAAK8P,iBAEE,GASTtF,QAAS,WAMP,IALA,IAAI0J,EAASlU,KAKNA,KAAKsJ,UAAU/S,OAAS,GAC7ByJ,KAAKkJ,cAAchG,KAAKlD,KAAKsJ,UAAUoH,OAIzC,KAAO1Q,KAAKoJ,aAAa7S,OAAS,GAAG,CACnC,IAAImY,EAAQ1O,KAAKoJ,aAAasH,MAC1BpkB,EAAI0T,KAAKkJ,cAActF,QAAQ8K,GAE/BpiB,GAAK,GACP0T,KAAKkJ,cAAcjF,OAAO3X,EAAG,GAQjC,IAHA,IAAI6nB,GAAM,IAAIhM,MAAOC,UACjB4I,EAAU,GAELoD,EAAM,EAAGA,EAAMpU,KAAKkJ,cAAc3S,OAAQ6d,IAAO,CACxD,IAAIC,EAASrU,KAAKkJ,cAAckL,IAE5BpU,KAAK2J,eAAkB0K,EAAO/M,OACpB+M,EAAOnM,WAAamM,EAAOpM,OAE3BkM,GAAO,EACbE,EAAOtM,OACTiJ,EAAQ9N,KAAKmR,GAGfrD,EAAQ9N,KAAKmR,IAKnBrU,KAAKkJ,cAAgB8H,EACrBvD,aAAazN,KAAKyJ,cAElBzJ,KAAK2I,OAAO6B,UAGRxK,KAAK4J,YACP5J,KAAKyJ,aAAec,YAAW,WAC7B,OAAO2J,EAAO1J,YACb,QAqCT/O,EAAQ6Y,cAAgB,SAAUznB,EAAM6lB,EAAeb,GAIrD7R,KAAKnT,KAAOA,EAKZmT,KAAK0S,cAAgBA,EAmBrB1S,KAAK6R,SAAWA,GAGlBpW,EAAQ6Y,cAAcpmB,UAAY,CAmBhCgkB,KAAM,SAAcqC,GAClB,OAAO,GAST/B,QAAS,SAAiB+B,GACxBvU,KAAKwU,YAAcD,GAcrB3B,YAAa,SAAqB2B,EAAYzB,GAC5C,MAAM,IAAIzb,MAAM,6CAMlB4c,UAAW,WACTjU,KAAKwU,YAAc,MAMrBjB,UAAW,WACTvT,KAAKwU,YAAc,OAoBvB/Y,EAAQoT,cAAgB,aAExBpT,EAAQoT,cAAc3gB,UAAY,IAAIuN,EAAQ6Y,cAAc,aAAa,EAAO,IAEhF7Y,EAAQoT,cAAc3gB,UAAUgkB,KAAO,SAAUqC,GAC/C,OAA8B,OAAvBA,EAAWzI,SAOpBrQ,EAAQyT,UAAY,aAEpBzT,EAAQyT,UAAUhhB,UAAY,IAAIuN,EAAQ6Y,cAAc,SAAS,EAAM,IAEvE7Y,EAAQyT,UAAUhhB,UAAUgkB,KAAO,SAAUqC,GAC3C,OAA8B,OAAvBA,EAAWzI,SAGpBrQ,EAAQyT,UAAUhhB,UAAU0kB,YAAc,SAAU2B,GAClD,IAAIE,EAAWF,EAAWxI,QAK1B,OAJA0I,GAAsB,KACtBA,GAAsBF,EAAWzI,QACjC2I,GAAsB,KACtBA,GAAsBF,EAAW7I,KAC1BjR,EAAega,IAOxBhZ,EAAQ0T,SAAW,aAEnB1T,EAAQ0T,SAASjhB,UAAY,IAAIuN,EAAQ6Y,cAAc,eAAe,EAAM,IAE5E7Y,EAAQ0T,SAASjhB,UAAUgkB,KAAO,SAAUqC,GAC1C,OAA8B,OAAvBA,EAAWzI,SAGpBrQ,EAAQ0T,SAASjhB,UAAU0kB,YAAc,SAAU2B,EAAYzB,EAAW4B,GACxE,IAAIC,EAASD,GAAelc,EAAIC,UAAU,GAAqB,WAAhB2S,KAAKC,UAChDoJ,EAAW,KAAOha,EAAe8Z,EAAWzI,SAqEhD,OApEA2I,GAAY,MACZA,GAAYE,EACZJ,EAAWxL,WAAW4L,OAASA,EAC/BJ,EAAWxL,WAAW,6BAA+B0L,EACrDA,EAAW,MAAQA,EAEnBzU,KAAK4S,YAAc,SAAU2B,EAAYzB,GAOvC,IANA,IAAI8B,EAAOC,EAAMpe,EAAMqe,EAAIC,EAAGC,EAAO1oB,EAAGmV,EACpCwT,EAAe,UACfC,EAAc,GAAGlb,OAAOua,EAAWxL,WAAW,6BAA8B,KAAK/O,OAAO8Y,EAAW,KACnG6B,EAASJ,EAAWxL,WAAW4L,OAC/BQ,EAAc,wBAEXrC,EAAUQ,MAAM6B,IAAc,CACnC,IAAI9B,EAAUP,EAAUQ,MAAM6B,GAG9B,OAFArC,EAAYA,EAAUlR,QAAQyR,EAAQ,GAAI,IAElCA,EAAQ,IACd,IAAK,IACHuB,EAAQvB,EAAQ,GAChB,MAEF,IAAK,IACHwB,EAAOxB,EAAQ,GACf,MAEF,IAAK,IACH5c,EAAO4c,EAAQ,IAKrB,GAAIuB,EAAMQ,OAAO,EAAGT,EAAOpe,UAAYoe,EAErC,OADAJ,EAAWxL,WAAa,GACjBwL,EAAWlC,mBAIpB6C,GADAD,GAAgB,KAAOL,EAEvBC,EAAO9B,KAAK8B,GACZA,GAAQ,UACR,IAAInJ,EAAOjR,EAAe8Z,EAAW7I,MAGrC,IAFAoJ,EAAKE,EAAQ5a,EAAKV,eAAegS,EAAMmJ,GAElCvoB,EAAI,EAAGA,EAAImK,EAAMnK,IAAK,CAGzB,IAFAyoB,EAAI3a,EAAKV,eAAegS,EAAMtR,EAAKD,SAAS6a,IAEvCvT,EAAI,EAAGA,EAAI,EAAGA,IACjBqT,EAAGrT,IAAMsT,EAAEtT,GAGbuT,EAAQD,EAGVD,EAAK1a,EAAKD,SAAS2a,GACnB,IAAIO,EAAYjb,EAAKV,eAAeob,EAAI,cACpCQ,EAAYlb,EAAKG,cAAcua,EAAI,cACnCS,EAAkBnb,EAAKV,eAAeU,EAAKI,SAASJ,EAAKD,SAASkb,IAAaH,GAGnF,IAFAX,EAAWxL,WAAW,oBAAsB3O,EAAKC,cAAcib,EAAWJ,GAErEzT,EAAI,EAAGA,EAAI,EAAGA,IACjB4T,EAAU5T,IAAM8T,EAAgB9T,GAIlC,OADAwT,GAAgB,MAAQpC,KAAKzY,EAAKD,SAASkb,KAItCZ,GAOThZ,EAAQsT,QAAU,aAElBtT,EAAQsT,QAAQ7gB,UAAY,IAAIuN,EAAQ6Y,cAAc,cAAc,EAAO,IAE3E7Y,EAAQsT,QAAQ7gB,UAAUgkB,KAAO,SAAUqC,GACzC,OAA8B,OAAvBA,EAAWzI,SAapBrQ,EAAQsT,QAAQ7gB,UAAUsnB,OAAS,SAAUpe,GAC3C,MAAO,IAAMA,EAAIwK,QAAQ,MAAO,QAAQA,QAAQ,KAAM,OAAS,KAGjEnG,EAAQsT,QAAQ7gB,UAAU0kB,YAAc,SAAU2B,EAAYzB,EAAW4B,GAQvE,IAPA,IAAIS,EAAc,mCACdR,EAASD,GAAelc,EAAIC,UAAU,GAAqB,WAAhB2S,KAAKC,UAChDoK,EAAQ,GACRC,EAAO,KACPd,EAAQ,GAGL9B,EAAUQ,MAAM6B,IAAc,CACnC,IAAI9B,EAAUP,EAAUQ,MAAM6B,GAI9B,OAHArC,EAAYA,EAAUlR,QAAQyR,EAAQ,GAAI,IAC1CA,EAAQ,GAAKA,EAAQ,GAAGzR,QAAQ,WAAY,MAEpCyR,EAAQ,IACd,IAAK,QACHoC,EAAQpC,EAAQ,GAChB,MAEF,IAAK,QACHuB,EAAQvB,EAAQ,GAChB,MAEF,IAAK,MACGA,EAAQ,GACd,MAEF,IAAK,OACHqC,EAAOrC,EAAQ,IAKrB,IAAIsC,EAAapB,EAAWvI,SAAW,IAAMuI,EAAWzZ,OAE3C,OAAT4a,IACFC,EAAaA,EAAa,IAAMD,GAGlC,IAAIE,EAAOnb,EAAe8Z,EAAWzI,QAAU,IAAM2J,EAAQ,IAAMzV,KAAKwU,YAAY9I,MAChFmK,EAAKrd,EAAIK,KAAK+c,GAAQ,IAAMhB,EAAQ,IAAMD,EAC1CmB,EAAK,gBAAkBH,EACvBV,EAAe,GAenB,OAdAA,GAAgB,iBAChBA,GAAgB,YAAcjV,KAAKwV,OAAO/a,EAAe8Z,EAAWzI,UAAY,IAChFmJ,GAAgB,SAAWjV,KAAKwV,OAAOC,GAAS,IAChDR,GAAgB,SAAWjV,KAAKwV,OAAOZ,GAAS,IAChDK,GAAgB,eAChBA,GAAgB,UAAYjV,KAAKwV,OAAOb,GAAU,IAClDM,GAAgB,cAAgBjV,KAAKwV,OAAOG,GAAc,IAC1DV,GAAgB,YAAczc,EAAIC,UAAUD,EAAIC,UAAUod,GAAM,IAAMjB,EAAQ,aAAeD,EAAS,SAAWnc,EAAIC,UAAUqd,IAAO,IACtIb,GAAgB,WAEhBjV,KAAK4S,YAAc,WACjB,MAAO,IAGFqC,GAOTxZ,EAAQuT,gBAAkB,aAE1BvT,EAAQuT,gBAAgB9gB,UAAY,IAAIuN,EAAQ6Y,cAAc,eAAe,EAAM,IAEnF7Y,EAAQuT,gBAAgB9gB,UAAUgkB,KAAO,SAAUqC,GACjD,OAA2B,OAApBA,EAAW7I,MAGpBjQ,EAAQuT,gBAAgB9gB,UAAU0kB,YAAc,SAAU2B,GACxD,IAAIE,EAAW,KAYf,OAV2B,OAAvBF,EAAWzI,UACb2I,EAAWA,EAAW,KAAOF,EAAWxI,SAG1C0I,GAAsB,IACtBA,GAAsB,IACtBA,GAAsB,eACtBA,GAAsBF,EAAW7I,KACjC+I,GAAsB,IAEfha,EADPga,GAAsB,MAaxBhZ,EAAQqT,aAAe,aAEvBrT,EAAQqT,aAAa5gB,UAAY,IAAIuN,EAAQ6Y,cAAc,YAAY,EAAM,IAE7E7Y,EAAQqT,aAAa5gB,UAAU0kB,YAAc,SAAU2B,GAQrD,OAAOA,EAAWzI,UAAYyI,EAAWxI,QAAU,GAAKwI,EAAWxI,SAOrEtQ,EAAQwT,YAAc,aAEtBxT,EAAQwT,YAAY/gB,UAAY,IAAIuN,EAAQ6Y,cAAc,YAAY,EAAM,IAE5E7Y,EAAQwT,YAAY/gB,UAAUgkB,KAAO,SAAUqC,GAC7C,OAA2B,OAApBA,EAAW7I,MAGpBjQ,EAAQwT,YAAY/gB,UAAU0kB,YAAc,SAAU2B,GACpD,IAAIE,EAAW,KAQf,OAN2B,OAAvBF,EAAWzI,UACb2I,GAAsBF,EAAWxI,SAGnC0I,GAAsB,KACtBA,GAAsBF,EAAW7I,KAC1BjR,EAAega,IAExB,IAAIsB,EAAO,CACT,QAAWta,EACX,OAAUF,EACV,IAAOI,EACP,KA/qHF,SAAcH,GACZ,OAAO,IAAIC,EAAQC,QAAQ,UAAWF,IA+qHtC,MAASI,EACT,KAAQxB,EACR,IAAO5B,EACP,cAAiB4B,EAAKC,cACtB,SAAYD,EAAKE,SACjB,cAAiBF,EAAKG,cACtB,SAAYH,EAAKI,UASfwb,EAAYD,EAAKta,QACjBwa,EAAWF,EAAKxa,OAoBpBya,EAAUE,QAAU,SAAUxW,EAAME,EAAM0M,EAAK6J,GAC7CnW,KAAKkH,KAAO8O,EAAUnQ,WACtB7F,KAAKoW,QAAU1W,EACfM,KAAKrG,KAAOqc,EAAU5Q,UAAU1F,GAGhCM,KAAKqW,SAAWzW,EAChBI,KAAKJ,KAAOA,EACZI,KAAKsM,IAAMA,EACXtM,KAAKsW,KAAOC,IACZvW,KAAKmW,MAAQA,GAAS,EACtBnW,KAAKwW,OAAQ,EACbxW,KAAKyW,KAAO,KAEZzW,KAAK0W,IAAM,WACT,OAAK1W,KAAKsW,MAIA,IAAInO,KACAnI,KAAKsW,MAAQ,IAJlB,GAOXtW,KAAK2W,SAAW,WACd,OAAK3W,KAAKyW,MAIA,IAAItO,KACAnI,KAAKyW,MAAQ,IAJlB,GAOXzW,KAAK4W,IAAM5W,KAAK6W,WAGlBb,EAAUE,QAAQhoB,UAAY,CAc5B4oB,YAAa,WACX,IAAI5V,EAAO,KAEX,GAAIlB,KAAK4W,IAAIG,aAAe/W,KAAK4W,IAAIG,YAAYC,iBAG/C,GAAqB,iBAFrB9V,EAAOlB,KAAK4W,IAAIG,YAAYC,iBAEnB7W,QAIP,MAHA6V,EAAUhR,MAAM,6BAChBgR,EAAUhR,MAAM,iBAAmBhF,KAAK4W,IAAI3B,cAC5Ce,EAAUhR,MAAM,gBAAkBgR,EAAU5Q,UAAUpF,KAAK4W,IAAIG,cACzD,IAAI1f,MAAM,oBAEb,GAAI2I,KAAK4W,IAAI3B,aAAc,CAKhC,GAHAe,EAAU/Q,MAAM,mFAChB/D,GAAO,IAAIe,WAAYC,gBAAgBlC,KAAK4W,IAAI3B,aAAc,mBAAmB+B,iBAG/E,MAAM,IAAI3f,MAAM,8BACX,GAAI6J,EAAK+V,cAAc,eAAgB,CAC5CjB,EAAUhR,MAAM,8BAAgC9D,EAAK+V,cAAc,eAAeC,aAClFlB,EAAUhR,MAAM,iBAAmBhF,KAAK4W,IAAI3B,cAC5C,IAAIjQ,EAAQ,IAAI3N,MAEhB,MADA2N,EAAMnY,KAAOmpB,EAAU1X,eAAeC,WAChCyG,GAIV,OAAO9D,GAWT2V,QAAS,WACP,IAAID,EAAM,KAcV,OAZI1qB,OAAOirB,gBACTP,EAAM,IAAIO,gBAEFC,kBACNR,EAAIQ,iBAAiB,2BAEdlrB,OAAO8U,gBAChB4V,EAAM,IAAI5V,cAAc,sBAI1B4V,EAAIS,mBAAqBrX,KAAKJ,KAAK9R,KAAK,KAAMkS,MACvC4W,IA4BXZ,EAAUnN,KAAO,SAAU0L,GACzBvU,KAAKsX,MAAQ/C,EAGbvU,KAAKsM,IAAMlB,KAAKmM,MAAsB,WAAhBnM,KAAKC,UAG3BrL,KAAKqM,IAAM,KAEXrM,KAAK4L,KAAO,EACZ5L,KAAK2L,KAAO,GACZ3L,KAAK9T,OAAS,EACd8T,KAAKwX,OAAS,EACdxX,KAAKyX,WAAa,KAClBzX,KAAK0X,oBAAsB,KAC3B1X,KAAK8K,UAAY,IAGnBkL,EAAUnN,KAAK3a,UAAY,CAYzBuiB,MAAO,KAQPkH,WAAY,WACV,IAAItG,EAAW4E,EAAS,OAAQ,CAC9B,IAAOjW,KAAKsM,MACZ,MAAS0J,EAAUla,GAAGC,WAaxB,OAViB,OAAbiE,KAAKqM,KACPgF,EAAS7V,MAAM,CACb,IAAOwE,KAAKqM,MAIZrM,KAAKsX,MAAMnQ,QAAQyQ,WAAa5X,KAAKsX,MAAM5K,4BAC7C1M,KAAK6X,gBAGAxG,GAQTxG,OAAQ,WACN7K,KAAKsM,IAAMlB,KAAKmM,MAAsB,WAAhBnM,KAAKC,UAC3BrL,KAAKqM,IAAM,KACXrM,KAAKwX,OAAS,EAEVxX,KAAKsX,MAAM5K,4BACbxgB,OAAO2gB,eAAeE,WAAW,wBAGnC/M,KAAKsX,MAAMlK,aAAapN,KAAKsM,MAQ/BH,SAAU,SAAkBR,EAAMC,EAAMC,GACtC7L,KAAK2L,KAAOA,GAAQ3L,KAAK2L,KACzB3L,KAAK4L,KAAOA,GAAQ5L,KAAK4L,KACzB5L,KAAKwX,OAAS,EAEd,IAAIM,EAAO9X,KAAK2X,aAAanc,MAAM,CACjC,GAAMwE,KAAKsX,MAAMxc,OACjB,WAAY,KACZ,KAAQkF,KAAK2L,KACb,KAAQ3L,KAAK4L,KACb,QAAW,0BACX,IAAO,MACP,eAAgB,MAChB,aAAcoK,EAAUla,GAAGE,OAGzB6P,GACFiM,EAAKtc,MAAM,CACT,MAASqQ,IAIb,IAAIsF,EAAcnR,KAAKsX,MAAMnG,YAE7BnR,KAAK8K,UAAU5H,KAAK,IAAI8S,EAAUE,QAAQ4B,EAAKzS,OAAQrF,KAAK+X,sBAAsBjqB,KAAKkS,KAAMmR,EAAYrjB,KAAKkS,KAAKsX,QAASQ,EAAKzS,OAAOzC,aAAa,SAErJ5C,KAAKgY,4BA2BPxL,QAAS,SAAiB7I,EAAK0I,EAAKC,EAAKd,EAAUG,EAAMC,EAAMW,GAC7DvM,KAAKsX,MAAM3T,IAAMA,EACjB3D,KAAKqM,IAAMA,EACXrM,KAAKsM,IAAMA,EACXtM,KAAKsX,MAAMrL,iBAAmBT,EAC9BxL,KAAKsX,MAAMxc,OAASkb,EAAUnS,iBAAiB7D,KAAKsX,MAAM3T,KAC1D3D,KAAKsX,MAAM3N,eAAgB,EAC3B3J,KAAKsX,MAAM1N,WAAY,EACvB5J,KAAK2L,KAAOA,GAAQ3L,KAAK2L,KACzB3L,KAAK4L,KAAOA,GAAQ5L,KAAK4L,KACzB5L,KAAK9T,OAASqgB,GAAQvM,KAAK9T,OAE3B8T,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOS,SAAU,OAsB7DyO,SAAU,SAAkBhJ,EAAK6H,EAAUG,EAAMC,EAAMW,GACrD,IAAI0L,EAAUrL,KAAKsL,MAAMhsB,OAAO2gB,eAAesL,QAAQ,yBAEvD,KAAI,MAAOF,GAA+CA,EAAQ3L,KAAO2L,EAAQ5L,KAAO4L,EAAQtU,MAAQ,MAAOA,GAAuCqS,EAAUjS,kBAAkBkU,EAAQtU,OAASqS,EAAUjS,kBAAkBJ,IAE7L,OAAlCqS,EAAUtS,eAAeC,IAAiBqS,EAAUnS,iBAAiBoU,EAAQtU,OAASA,IAI/E,CACL,IAAIqB,EAAQ,IAAI3N,MAAM,qCAEtB,MADA2N,EAAMnY,KAAO,sBACPmY,EANNhF,KAAKsX,MAAMtN,UAAW,EAEtBhK,KAAKwM,QAAQyL,EAAQtU,IAAKsU,EAAQ5L,IAAK4L,EAAQ3L,IAAKd,EAAUG,EAAMC,EAAMW,IAe9EsL,cAAe,WACT7X,KAAKsX,MAAM3N,cACT3J,KAAKsX,MAAM3T,KAAO3D,KAAKsM,KAAOtM,KAAKqM,KACrCngB,OAAO2gB,eAAeC,QAAQ,uBAAwBF,KAAKwL,UAAU,CACnE,IAAOpY,KAAKsX,MAAM3T,IAClB,IAAO3D,KAAKsM,IACZ,IAAOtM,KAAKqM,OAIhBngB,OAAO2gB,eAAeE,WAAW,yBAWrCoE,YAAa,SAAqBE,GAChC,IAAIgH,EAAMhH,EAASzO,aAAa,QAEhC,GAAY,OAARyV,GAAwB,cAARA,EAAqB,CAEvC,IAAIzH,EAAOS,EAASzO,aAAa,aACjCoT,EAAUhR,MAAM,2BAA6B4L,GAC7C,IAAIC,EAAWQ,EAASP,qBAAqB,YAc7C,OAZa,OAATF,GACW,wBAATA,GAAkCC,EAASta,OAAS,IACtDqa,EAAO,YAGT5Q,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOG,SAAUgT,IAE3D5Q,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOG,SAAU,WAG7DoC,KAAKsX,MAAMxH,cAAcc,GAElBoF,EAAUvY,OAAOG,SAKrBoC,KAAKqM,MACRrM,KAAKqM,IAAMgF,EAASzO,aAAa,QAGnC,IAAI2J,EAAO8E,EAASzO,aAAa,YAE7B2J,IACFvM,KAAK9T,OAASosB,SAAS/L,EAAM,KAG/B,IAAIX,EAAOyF,EAASzO,aAAa,QAE7BgJ,IACF5L,KAAK4L,KAAO0M,SAAS1M,EAAM,KAG7B,IAAID,EAAO0F,EAASzO,aAAa,QAE7B+I,IACF3L,KAAK2L,KAAO2M,SAAS3M,EAAM,KAG7B,IAAI8L,EAAapG,EAASzO,aAAa,cAEnC6U,IACFzX,KAAKyX,WAAaa,SAASb,EAAY,MAU3C7H,YAAa,SAAqBH,GAChCzP,KAAKuY,eAAe9I,IAQtBK,cAAe,WACb9P,KAAKqM,IAAM,KACXrM,KAAKsM,IAAMlB,KAAKmM,MAAsB,WAAhBnM,KAAKC,UAEvBrL,KAAKsX,MAAM5K,4BACbxgB,OAAO2gB,eAAeE,WAAW,wBAGnC/M,KAAKsX,MAAMlK,aAAapN,KAAKsM,MAS/BqE,YAAa,WACX,OAAiC,IAA1B3Q,KAAK8K,UAAUvU,QASxBiiB,2BAA4B,SAAoCnI,GAC9D,IAAIoI,EAAYzY,KAAK0Y,kBAAkBrI,GAEnCsI,EAAe3Y,KAAKsX,MAAM9N,sBAAsBoP,KAAKH,GAErDE,GACFA,EAAalsB,KAAKuT,KAAMyY,IAc5BI,UAAW,SAAmBJ,GAC5BzY,KAAKwX,SACLxB,EAAU7Q,KAAK,4BAA8BsT,EAAY,uBAAyBzY,KAAKwX,QAEnFxX,KAAKwX,OAAS,GAChBxX,KAAKsX,MAAM3H,wBASf+B,kBAAmB,SAA2BlG,GAC5CwK,EAAU7Q,KAAK,gGAGbqG,EADEA,EACSA,EAAS1d,KAAKkS,KAAKsX,OAEnBtX,KAAKsX,MAAMnG,YAAYrjB,KAAKkS,KAAKsX,OAG9C,IAAIQ,EAAO9X,KAAK2X,aAEhB3X,KAAK8K,UAAU5H,KAAK,IAAI8S,EAAUE,QAAQ4B,EAAKzS,OAAQrF,KAAK+X,sBAAsBjqB,KAAKkS,KAAMwL,GAAWsM,EAAKzS,OAAOzC,aAAa,SAEjI5C,KAAKgY,4BAQPrI,qBAAsB,WACpB3P,KAAK6P,qBAMPA,kBAAmB,WACjB,KAAO7P,KAAK8K,UAAUvU,OAAS,GAAG,CAChC,IAAI8Z,EAAMrQ,KAAK8K,UAAU4F,MAEzBL,EAAImG,OAAQ,EACZnG,EAAIuG,IAAIJ,QAERnG,EAAIuG,IAAIS,mBAAqB,eASjC7M,QAAS,WACP,IAAI7Q,EAAOqG,KAAKsX,MAAMrN,MAOtB,GALIjK,KAAKsX,MAAM3N,eAA2C,IAA1B3J,KAAK8K,UAAUvU,QAAgC,IAAhBoD,EAAKpD,SAAiByJ,KAAKsX,MAAMzN,gBAC9FmM,EAAU/Q,MAAM,wDAChBtL,EAAKuJ,KAAK,QAGRlD,KAAKsX,MAAMvN,OAAf,CAIA,GAAI/J,KAAK8K,UAAUvU,OAAS,GAAKoD,EAAKpD,OAAS,EAAG,CAGhD,IAFA,IAAIuhB,EAAO9X,KAAK2X,aAEPrrB,EAAI,EAAGA,EAAIqN,EAAKpD,OAAQjK,IACf,OAAZqN,EAAKrN,KACS,YAAZqN,EAAKrN,GACPwrB,EAAKtc,MAAM,CACT,GAAMwE,KAAKsX,MAAMxc,OACjB,WAAY,KACZ,eAAgB,OAChB,aAAckb,EAAUla,GAAGE,OAG7B8b,EAAKvR,MAAM5M,EAAKrN,IAAI6Z,aAKnBnG,KAAKsX,MAAMrN,MAClBjK,KAAKsX,MAAMrN,MAAQ,GAEnBjK,KAAK8K,UAAU5H,KAAK,IAAI8S,EAAUE,QAAQ4B,EAAKzS,OAAQrF,KAAK+X,sBAAsBjqB,KAAKkS,KAAMA,KAAKsX,MAAMlH,UAAUtiB,KAAKkS,KAAKsX,QAASQ,EAAKzS,OAAOzC,aAAa,SAE9J5C,KAAKgY,2BAGP,GAAIhY,KAAK8K,UAAUvU,OAAS,EAAG,CAC7B,IAAIuiB,EAAe9Y,KAAK8K,UAAU,GAAG4L,MAEN,OAA3B1W,KAAK8K,UAAU,GAAG2L,MAChBzW,KAAK8K,UAAU,GAAG6L,WAAavL,KAAKmM,MAAMvB,EAAUzW,kBAAoBS,KAAK2L,OAC/E3L,KAAKgY,2BAILc,EAAe1N,KAAKmM,MAAMvB,EAAU1W,QAAUU,KAAK2L,QACrDqK,EAAU7Q,KAAK,WAAanF,KAAK8K,UAAU,GAAG5D,GAAK,oBAAsBkE,KAAKmM,MAAMvB,EAAU1W,QAAUU,KAAK2L,MAAQ,gCAErH3L,KAAKgY,+BAcXU,kBAAmB,SAA2BrI,EAAK0I,GACjD,IAAIN,EAEJ,GAA2B,IAAvBpI,EAAIuG,IAAIoC,WACV,IACEP,EAAYpI,EAAIuG,IAAI7G,OACpB,MAAO1W,GAGP2c,EAAUhR,MAAM,mEAA0EyT,GAQ9F,YAJyB,IAAdA,IACTA,EAA2B,iBAARM,EAAmBA,EAAM,GAGvCN,GAeTV,sBAAuB,SAA+BnY,EAAMyQ,GAG1D,GAFA2F,EAAU/Q,MAAM,cAAgBoL,EAAInJ,GAAK,IAAMmJ,EAAI8F,MAAQ,qBAAuB9F,EAAIuG,IAAIoC,YAEtF3I,EAAImG,MACNnG,EAAImG,OAAQ,OAId,GAA2B,IAAvBnG,EAAIuG,IAAIoC,WAAZ,CAKA,IAAIP,EAAYzY,KAAK0Y,kBAAkBrI,GAIvC,GAFArQ,KAAK0X,oBAAsBrH,EAAIuG,IAAIqC,wBAE/BjZ,KAAK6J,eAAiB4O,GAAa,IAKrC,OAJAzY,KAAK6Y,UAAUJ,QAEfzY,KAAKwY,2BAA2BnI,GAKlC,IAAI6I,EAAgBT,EAAY,GAAKA,EAAY,IAC7CU,EAAmB9I,EAAI8F,MAAQnW,KAAKsX,MAAMhN,WAS9C,IAPI4O,GAAiBC,KAEnBnZ,KAAKoZ,eAAe/I,GAEpB2F,EAAU/Q,MAAM,cAAgBoL,EAAInJ,GAAK,2BAGzB,MAAduR,EAAmB,CAErB,IAAIY,EAASrZ,KAAK8K,UAAU,KAAOuF,GACtBrQ,KAAK8K,UAAU,KAAOuF,GAKrBgJ,GAAUrZ,KAAK8K,UAAUvU,OAAS,GAAKyJ,KAAK8K,UAAU,GAAG4L,MAAQtL,KAAKmM,MAAMvB,EAAUzW,kBAAoBS,KAAK2L,QAC3H3L,KAAKsZ,gBAAgB,GAGvBtZ,KAAKsX,MAAMlK,aAAamM,OAAOlJ,EAAI/D,KAAO,GAE1C0J,EAAU/Q,MAAM,cAAgBoL,EAAInJ,GAAK,IAAMmJ,EAAI8F,MAAQ,YAC3DvW,EAAKyQ,GAELrQ,KAAKwX,OAAS,OACS,IAAdiB,GAAmBA,GAAa,KAAOA,EAAY,KAAOA,GAAa,MAEhFzC,EAAUhR,MAAM,cAAgBqL,EAAInJ,GAAK,IAAMmJ,EAAI8F,MAAQ,UAAYsC,EAAY,aAEnFzY,KAAK6Y,UAAUJ,GAEfzY,KAAKwY,2BAA2BnI,GAE5BoI,GAAa,KAAOA,EAAY,MAClCzY,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOQ,cAAe,MAEhE+B,KAAKsX,MAAMxH,kBAGbkG,EAAUhR,MAAM,cAAgBqL,EAAInJ,GAAK,IAAMmJ,EAAI8F,MAAQ,UAAYsC,EAAY,aAGhFS,GAAkBC,EAEZA,IAAqBnZ,KAAKsX,MAAM1N,WACzC5J,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOG,SAAU,aAF3DoC,KAAKgY,6BAeTwB,gBAAiB,SAAyBltB,GACxC,IAAIqb,EAAQ3H,KAERqQ,EAAMrQ,KAAK8K,UAAUxe,GAErBmsB,EAAYzY,KAAK0Y,kBAAkBrI,GAAM,GAG7C,GAAIA,EAAI8F,MAAQnW,KAAKsX,MAAMhN,WACzBtK,KAAKsX,MAAM3H,2BADb,CAMA,IAAImJ,EAAezI,EAAIqG,MACnB+C,GAAmBC,MAAMZ,IAAiBA,EAAe1N,KAAKmM,MAAMvB,EAAU1W,QAAUU,KAAK2L,MAC7FgO,EAAiC,OAAbtJ,EAAIoG,MAAiBpG,EAAIsG,WAAavL,KAAKmM,MAAMvB,EAAUzW,kBAAoBS,KAAK2L,MACxGiO,EAAsC,IAAvBvJ,EAAIuG,IAAIoC,aAAqBP,EAAY,GAAKA,GAAa,KAgB9E,IAdIgB,GAAmBE,GAAqBC,KACtCD,GACF3D,EAAUhR,MAAM,WAAWhL,OAAOgG,KAAK8K,UAAUxe,GAAG4a,GAAI,uCAG1DmJ,EAAImG,OAAQ,EACZnG,EAAIuG,IAAIJ,QAERnG,EAAIuG,IAAIS,mBAAqB,aAE7BrX,KAAK8K,UAAUxe,GAAK,IAAI0pB,EAAUE,QAAQ7F,EAAI+F,QAAS/F,EAAIgG,SAAUhG,EAAI/D,IAAK+D,EAAI8F,OAClF9F,EAAMrQ,KAAK8K,UAAUxe,IAGI,IAAvB+jB,EAAIuG,IAAIoC,WAAkB,CAC5BhD,EAAU/Q,MAAM,cAAgBoL,EAAInJ,GAAK,IAAMmJ,EAAI8F,MAAQ,YAE3D,IACE,IAAI0D,EAAe7Z,KAAKsX,MAAMnQ,QAAQ2S,aAAe,0BACrDzJ,EAAIuG,IAAImD,KAAK,OAAQ/Z,KAAKsX,MAAM/O,SAASvI,KAAKsX,MAAMnQ,QAAQ6S,WAEpB,IAA7B3J,EAAIuG,IAAIqD,kBAEjB5J,EAAIuG,IAAIqD,iBAAiB,eAAgBJ,GAGvC7Z,KAAKsX,MAAMnQ,QAAQ+S,kBACrB7J,EAAIuG,IAAIsD,iBAAkB,GAE5B,MAAOC,GASP,OARAnE,EAAUhR,MAAM,oBAAsBmV,EAAGzjB,YAEpCsJ,KAAKsX,MAAM1N,WACd5J,KAAKsX,MAAMpL,qBAAqB8J,EAAUvY,OAAOG,SAAU,oBAG7DoC,KAAKsX,MAAM/H,aAOb,IAAI6K,EAAW,WAGb,GAFA/J,EAAIiG,KAAO,IAAInO,KAEXR,EAAM2P,MAAMnQ,QAAQkT,cAAe,CACrC,IAAIC,EAAU3S,EAAM2P,MAAMnQ,QAAQkT,cAElC,IAAK,IAAIE,KAAUD,EACbttB,OAAOkB,UAAUC,eAAe1B,KAAK6tB,EAASC,IAChDlK,EAAIuG,IAAIqD,iBAAiBM,EAAQD,EAAQC,IAK/ClK,EAAIuG,IAAIvJ,KAAKgD,EAAI1W,OAKnB,GAAI0W,EAAI8F,MAAQ,EAAG,CAGjB,IAAIqE,EAAwF,IAA9EpP,KAAKqP,IAAIrP,KAAKmM,MAAMvB,EAAU1W,QAAUU,KAAK2L,MAAOP,KAAKsP,IAAIrK,EAAI8F,MAAO,IACtF5L,YAAW,WAET6P,MACCI,QAEHJ,IAGF/J,EAAI8F,QAEAnW,KAAKsX,MAAMrK,YAAc+I,EAAU1N,WAAWpa,UAAU+e,YACtDoD,EAAI+F,QAAQ5T,WAAaxC,KAAKyQ,OAASJ,EAAI+F,QAAQvW,WAAWtJ,OAChEyJ,KAAKsX,MAAMrK,UAAUoD,EAAI+F,QAAQvW,WAAW,IAE5CG,KAAKsX,MAAMrK,UAAUoD,EAAI+F,UAIzBpW,KAAKsX,MAAMnK,YAAc6I,EAAU1N,WAAWpa,UAAUif,WAC1DnN,KAAKsX,MAAMnK,UAAUkD,EAAI1W,WAG3Bqc,EAAU/Q,MAAM,qBAA6B,IAAN3Y,EAAU,QAAU,UAAY,8BAAgC+jB,EAAIuG,IAAIoC,cAUnHI,eAAgB,SAAwB/I,GACtC2F,EAAU/Q,MAAM,oBAEhB,IAAK,IAAI3Y,EAAI0T,KAAK8K,UAAUvU,OAAS,EAAGjK,GAAK,EAAGA,IAC1C+jB,IAAQrQ,KAAK8K,UAAUxe,IACzB0T,KAAK8K,UAAU7G,OAAO3X,EAAG,GAK7B+jB,EAAIuG,IAAIS,mBAAqB,aAE7BrX,KAAKgY,4BASPsB,gBAAiB,SAAyBhtB,GACxC,IAAI+jB,EAAMrQ,KAAK8K,UAAUxe,GAER,OAAb+jB,EAAIoG,OACNpG,EAAIoG,KAAO,IAAItO,MAGjBnI,KAAKwZ,gBAAgBltB,IAevBkkB,WAAY,SAAoBH,GAC9B,IACE,OAAOA,EAAIyG,cACX,MAAOzd,GACP,GAAkB,gBAAdA,EAAEoL,QACJ,MAAMpL,EAGR2G,KAAKsX,MAAM/H,WAAW,yBAW1BgJ,eAAgB,SAAwB9I,GACtCuG,EAAU/Q,MAAM,6BAEhB,IAAI6S,EAAO9X,KAAK2X,aAAanc,MAAM,CACjCyL,KAAM,cAGJwI,GACFqI,EAAKvR,MAAMkJ,EAAKpK,QAGlB,IAAIgL,EAAM,IAAI2F,EAAUE,QAAQ4B,EAAKzS,OAAQrF,KAAK+X,sBAAsBjqB,KAAKkS,KAAMA,KAAKsX,MAAMlH,UAAUtiB,KAAKkS,KAAKsX,QAASQ,EAAKzS,OAAOzC,aAAa,QAEpJ5C,KAAK8K,UAAU5H,KAAKmN,GAEpBrQ,KAAKgY,4BAQPzK,MAAO,WACL,IAAI/E,EAASxI,KAEbyN,aAAazN,KAAKsX,MAAM7N,cAExBzJ,KAAKgY,2BAELhY,KAAKsX,MAAM7N,aAAec,YAAW,WACnC,OAAO/B,EAAO8O,MAAM9M,YACnB,MAOLgE,aAAc,WACZxO,KAAKgY,2BAELvK,aAAazN,KAAKsX,MAAM7N,eAU1BuO,yBAA0B,WACnBhY,KAAK8K,UAGRkL,EAAU/Q,MAAM,wCAA0CjF,KAAK8K,UAAUvU,OAAS,aAFlFyf,EAAU/Q,MAAM,2DAKbjF,KAAK8K,WAAuC,IAA1B9K,KAAK8K,UAAUvU,SAIlCyJ,KAAK8K,UAAUvU,OAAS,GAC1ByJ,KAAKwZ,gBAAgB,GAGnBxZ,KAAK8K,UAAUvU,OAAS,GAAK6U,KAAKuP,IAAI3a,KAAK8K,UAAU,GAAGwB,IAAMtM,KAAK8K,UAAU,GAAGwB,KAAOtM,KAAK9T,QAC9F8T,KAAKwZ,gBAAgB,MAW3B,IAAIoB,EAAY7E,EAAKta,QACjBof,EAAW9E,EAAKxa,OAilBpB,OAljBAqf,EAAUhS,UAAY,SAAU2L,GAC9BvU,KAAKsX,MAAQ/C,EACbvU,KAAKyQ,MAAQ,UACb,IAAIlI,EAAUgM,EAAWhM,QAEzB,GAA+B,IAA3BA,EAAQ3E,QAAQ,QAA4C,IAA5B2E,EAAQ3E,QAAQ,QAAe,CAGjE,IAAIkX,EAAc,GAEkB,OAAhCvG,EAAWpN,QAAQuB,UAAkD,WAA7Bxc,OAAO6uB,SAASrS,SAC1DoS,GAAe,KAEfA,GAAe,MAGjBA,GAAe,MAAQ5uB,OAAO6uB,SAASrF,KAEV,IAAzBnN,EAAQ3E,QAAQ,KAClBkX,GAAe5uB,OAAO6uB,SAASC,SAAWzS,EAE1CuS,GAAevS,EAGjBgM,EAAWhM,QAAUuS,IAIzBF,EAAUhS,UAAU1a,UAAY,CAO9B+sB,aAAc,WACZ,OAAOJ,EAAS,OAAQ,CACtB,MAASD,EAAU9e,GAAGY,QACtB,GAAMsD,KAAKsX,MAAMxc,OACjB,QAAW,SAafogB,mBAAoB,SAA4B7J,EAAU8J,GACxD,IAAI3D,EAQJ,GAAsB,KALpBA,EADEnG,EAASC,uBACFD,EAASC,uBAAuBsJ,EAAU9e,GAAGW,OAAQ,SAErD4U,EAASP,qBAAqB,iBAG9Bva,OACT,OAAO,EAQT,IALA,IAAIyO,EAAQwS,EAAO,GACfxH,EAAY,GACZrO,EAAO,GAGFrV,EAAI,EAAGA,EAAI0Y,EAAMnF,WAAWtJ,OAAQjK,IAAK,CAChD,IAAI+M,EAAI2L,EAAMnF,WAAWvT,GAEzB,GALO,wCAKH+M,EAAEuJ,aAAa,SACjB,MAGiB,SAAfvJ,EAAEmJ,SACJb,EAAOtI,EAAE6d,YAETlH,EAAY3W,EAAEmJ,SAIlB,IAAI4Y,EAAc,2BAkBlB,OAfEA,GADEpL,GAGa,UAGbrO,IACFyZ,GAAe,MAAQzZ,GAGzBiZ,EAAU5V,MAAMoW,GAEhBpb,KAAKsX,MAAMpL,qBAAqBiP,EAAenL,GAE/ChQ,KAAKsX,MAAMxH,iBAEJ,GASTjF,OAAQ,aAURsB,SAAU,WAERnM,KAAKqb,eAGLrb,KAAKsb,OAAS,IAAIC,UAAUvb,KAAKsX,MAAM/O,QAAS,QAChDvI,KAAKsb,OAAOE,OAASxb,KAAKyb,QAAQ3tB,KAAKkS,MACvCA,KAAKsb,OAAOI,QAAU1b,KAAK2b,SAAS7tB,KAAKkS,MACzCA,KAAKsb,OAAOM,QAAU5b,KAAK6b,SAAS/tB,KAAKkS,MACzCA,KAAKsb,OAAOQ,UAAY9b,KAAK+b,oBAAoBjuB,KAAKkS,OAWxDmR,YAAa,SAAqBE,GAGhC,GAFYrR,KAAKkb,mBAAmB7J,EAAUuJ,EAAUnd,OAAOG,UAG7D,OAAOgd,EAAUnd,OAAOG,UAY5Boe,mBAAoB,SAA4BvX,GAC9C,IAAIO,GAAQ,EAERrX,EAAK8W,EAAQ7B,aAAa,SAEZ,iBAAPjV,EACTqX,EAAQ,4BACCrX,IAAOitB,EAAU9e,GAAGY,UAC7BsI,EAAQ,4BAA8BrX,GAGxC,IAAIsuB,EAAMxX,EAAQ7B,aAAa,WAQ/B,MANmB,iBAARqZ,EACTjX,EAAQ,8BACS,QAARiX,IACTjX,EAAQ,8BAAgCiX,IAGtCjX,IACFhF,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOG,SAAUoH,GAE3DhF,KAAKsX,MAAMxH,iBAEJ,IAYXiM,oBAAqB,SAA6BtX,GAChD,GAAuC,IAAnCA,EAAQ9K,KAAKiK,QAAQ,WAAqD,IAAlCa,EAAQ9K,KAAKiK,QAAQ,SAAgB,CAE/E,IAAIjK,EAAO8K,EAAQ9K,KAAKiI,QAAQ,mBAAoB,IACpD,GAAa,KAATjI,EAAa,OACjB,IAAIuiB,GAAc,IAAIja,WAAYC,gBAAgBvI,EAAM,YAAYqd,gBAEpEhX,KAAKsX,MAAMtK,SAASkP,GAEpBlc,KAAKsX,MAAMpK,SAASzI,EAAQ9K,MAGxBqG,KAAKgc,mBAAmBE,IAE1Blc,KAAKmR,YAAY+K,QAEd,GAAwC,IAApCzX,EAAQ9K,KAAKiK,QAAQ,WAAkB,CAGhD,IAAIuY,GAAgB,IAAIla,WAAYC,gBAAgBuC,EAAQ9K,KAAM,YAAYqd,gBAE9EhX,KAAKsX,MAAMtK,SAASmP,GAEpBnc,KAAKsX,MAAMpK,SAASzI,EAAQ9K,MAE5B,IAAIyiB,EAAUD,EAAcvZ,aAAa,iBAEzC,GAAIwZ,EAAS,CACX,IAAI7T,EAAUvI,KAAKsX,MAAM/O,SAEFA,EAAQ3E,QAAQ,SAAW,GAAKwY,EAAQxY,QAAQ,SAAW,GAAK2E,EAAQ3E,QAAQ,QAAU,KAG/G5D,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOU,SAAU,gDAE3D6B,KAAKsX,MAAMjP,QAEXrI,KAAKsX,MAAM/O,QAAU6T,EAErBpc,KAAKmM,iBAGPnM,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOG,SAAU,2BAE3DoC,KAAKsX,MAAMxH,oBAER,CACL,IAAIuM,EAASrc,KAAKsc,YAAY7X,EAAQ9K,MAElC+F,GAAO,IAAIuC,WAAYC,gBAAgBma,EAAQ,YAAYrF,gBAC/DhX,KAAKsb,OAAOQ,UAAY9b,KAAKuc,WAAWzuB,KAAKkS,MAE7CA,KAAKsX,MAAMnG,YAAYzR,EAAM,KAAM+E,EAAQ9K,QAY/CiW,YAAa,SAAqBH,GAChC,GAAIzP,KAAKsb,QAAUtb,KAAKsb,OAAOtC,aAAeuC,UAAUiB,OAAQ,CAC1D/M,GACFzP,KAAKsX,MAAMjK,KAAKoC,GAGlB,IAAIgN,EAAQ5B,EAAS,QAAS,CAC5B,MAASD,EAAU9e,GAAGY,UAGxBsD,KAAKsX,MAAMrK,UAAUwP,EAAMpX,QAE3B,IAAIqX,EAAc9B,EAAUxV,UAAUqX,GAEtCzc,KAAKsX,MAAMnK,UAAUuP,GAErB,IACE1c,KAAKsb,OAAOjO,KAAKqP,GACjB,MAAOrjB,GACPuhB,EAAUzV,KAAK,iCAInBnF,KAAKsX,MAAMxH,iBAQbA,cAAe,WACb8K,EAAU3V,MAAM,uCAEhBjF,KAAKqb,gBAOPiB,YAAa,SAAqBtO,GAChC,MAAO,YAAcA,EAAS,cAQhCqN,aAAc,WACZ,GAAIrb,KAAKsb,OACP,IACEtb,KAAKsb,OAAOM,QAAU,KACtB5b,KAAKsb,OAAOI,QAAU,KACtB1b,KAAKsb,OAAOQ,UAAY,KACxB9b,KAAKsb,OAAOmB,QACZ,MAAOpjB,GACPuhB,EAAU3V,MAAM5L,EAAEoL,SAItBzE,KAAKsb,OAAS,MAShB3K,YAAa,WACX,OAAO,GAQTkL,SAAU,SAAkBxiB,GACtB2G,KAAKsX,MAAM1N,YAAc5J,KAAKsX,MAAMzN,eACtC+Q,EAAU5V,MAAM,iCAEhBhF,KAAKsX,MAAMxH,iBACFzW,GAAgB,OAAXA,EAAEsjB,OAAkB3c,KAAKsX,MAAM1N,WAAa5J,KAAKsb,QAK/DV,EAAU5V,MAAM,iCAEhBhF,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOG,SAAU,0EAE3DoC,KAAKsX,MAAMxH,iBAEX8K,EAAU3V,MAAM,qBASpByM,kBAAmB,SAA2BlG,GAC5CoP,EAAU5V,MAAM,6DAEhBhF,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOG,SAAUgd,EAAUtc,eAAeI,cAEhF8M,GACFA,EAAS/e,KAAKuT,KAAKsX,OAGrBtX,KAAKsX,MAAMxH,iBAQbH,qBAAsB,aAKtBE,kBAAmB,aAQnB8L,SAAU,SAAkB3W,GAC1B4V,EAAU5V,MAAM,mBAAqBA,GAErChF,KAAKsX,MAAMpL,qBAAqB0O,EAAUnd,OAAOG,SAAU,0EAE3DoC,KAAK4P,eAQPpF,QAAS,WACP,IAAI7Q,EAAOqG,KAAKsX,MAAMrN,MAEtB,GAAItQ,EAAKpD,OAAS,IAAMyJ,KAAKsX,MAAMvN,OAAQ,CACzC,IAAK,IAAIzd,EAAI,EAAGA,EAAIqN,EAAKpD,OAAQjK,IAC/B,GAAgB,OAAZqN,EAAKrN,GAAa,CACpB,IAAI0hB,OAAS,EAGXA,EADc,YAAZrU,EAAKrN,GACE0T,KAAKib,eAAe5V,OAEpB1L,EAAKrN,GAGhB,IAAIswB,EAAYhC,EAAUxV,UAAU4I,GAEpChO,KAAKsX,MAAMrK,UAAUe,GAErBhO,KAAKsX,MAAMnK,UAAUyP,GAErB5c,KAAKsb,OAAOjO,KAAKuP,GAIrB5c,KAAKsX,MAAMrN,MAAQ,KA2BvBsS,WAAY,SAAoB9X,GAC9B,IAAI/E,EAEA+c,EAAQ,wDAEZ,GAAIhY,EAAQ9K,OAAS8iB,EASnB,OARAzc,KAAKsX,MAAMpK,SAASuP,GAEpBzc,KAAKsX,MAAMtK,SAASvI,QAEfzE,KAAKsX,MAAMzN,eACd7J,KAAKsX,MAAMxH,iBAIR,GAAsC,IAAlCrL,EAAQ9K,KAAKkjB,OAAO,WAI7B,GAFAnd,GAAO,IAAIuC,WAAYC,gBAAgBuC,EAAQ9K,KAAM,YAAYqd,iBAE5DhX,KAAKgc,mBAAmBtc,GAC3B,WAEG,CACL,IAAI/F,EAAOqG,KAAKsc,YAAY7X,EAAQ9K,MAEpC+F,GAAO,IAAIuC,WAAYC,gBAAgBvI,EAAM,YAAYqd,gBAG3D,OAAIhX,KAAKkb,mBAAmBxb,EAAMkb,EAAUnd,OAAOC,YAAnD,EAKIsC,KAAKsX,MAAMzN,eAA8C,aAA7BnK,EAAKod,WAAWta,UAAoE,gBAAzC9C,EAAKod,WAAWla,aAAa,SACtG5C,KAAKsX,MAAMtK,SAAStN,QAEpBM,KAAKsX,MAAMpK,SAAS0N,EAAUxV,UAAU1F,UAO1CM,KAAKsX,MAAMlH,UAAU1Q,EAAM+E,EAAQ9K,OAQrC8hB,QAAS,WACPb,EAAU3V,MAAM,kBAEhB,IAAI8X,EAAQ/c,KAAKib,eAEjBjb,KAAKsX,MAAMrK,UAAU8P,EAAM1X,QAE3B,IAAI2X,EAAcpC,EAAUxV,UAAU2X,GAEtC/c,KAAKsX,MAAMnK,UAAU6P,GAErBhd,KAAKsb,OAAOjO,KAAK2P,IAcnBxM,WAAY,SAAoBxC,GAC9B,OAAOA,GAQTT,MAAO,WACLvN,KAAKsX,MAAM9J,SAObgB,aAAc,WACZf,aAAazN,KAAKsX,MAAM7N,cAExBzJ,KAAKsX,MAAM9M,QAAQ1c,KAAKkS,KAAKsX,MAA7BtX,KAIJrK,EAAS8F,QAAUsa,EAAKta,QACxB9F,EAAS4F,OAASwa,EAAKxa,OACvB5F,EAASgG,IAAMoa,EAAKpa,IACpBhG,EAASsnB,KAAOlH,EAAKkH,KACrBtnB,EAASiG,MAAQma,EAAKna,MAEfma,GAzwLwDhqB,EAAOD,QAAUD,M,+CCD5F,YAEe,QAAIqxB,K,gBCYnB,IAAIC,EAASC,EAAQ,IACjBC,EAAeD,EAAQ,KAwBvBE,EAAY,GAKZC,EAAU,GAKVC,EAAWL,EAAOM,OAAOC,MAG7B3xB,EAAOD,QAAU,CAMb6xB,mBAAoB,SAASC,GACzBT,EAAOQ,mBAAmBC,IAO9BC,sBAAuB,SAASD,GAC5BT,EAAOU,sBAAsBD,IAMjCE,iBAAkB,SAAS3W,GACvBgW,EAAOW,iBAAiB3W,IAM5B4W,UAAW,SAAS7W,EAAI8W,EAAY7W,GAChC,IAAI8W,EAAS,IAAId,EAAOK,EAAUtW,EAAI8W,EAAY7W,GAOlD,OANGD,GACCoW,EAAUpW,GAAMoW,EAAUpW,IAAO,GACjCoW,EAAUpW,GAAIhE,KAAK+a,IAEnBV,EAAQra,KAAK+a,GAEVA,GASXC,gBAAiB,SAASrZ,EAAOqC,GAE7B,IADA,IAAI3a,EAAI2a,EAAKoW,EAAUpW,IAAO,GAAMqW,EAC5BjxB,EAAI,EAAGA,EAAIC,EAAEgK,OAAQjK,IACzBC,EAAED,GAAG6xB,SAAStZ,IAOtBuZ,YAAa,SAAUvZ,GACnB2Y,EAAW3Y,EAEX,IADA,IAAIvY,EAAI,EACFA,EAAIixB,EAAQhnB,OAAQjK,IACtBixB,EAAQjxB,GAAG6xB,SAAStZ,GAGxB,IAAI,IAAIqC,KAAMoW,EAAW,CACrB,IAAI/wB,EAAI+wB,EAAUpW,IAAO,GACzB,IAAI5a,EAAI,EAAGA,EAAIC,EAAEgK,OAAQjK,IACrBC,EAAED,GAAG6xB,SAAStZ,KAO1B4Y,OAAQN,EAAOM,OAIfJ,aAAcA,I,8BChIlB,yKAgBA,MAAMY,EAASb,EAAQ,GAAqBW,UAAUM,GAMtD,IAAIC,EAKAC,GAAoB,EAkCxB,SAASC,EAAsBrX,GACvBsX,IAAUC,wBAITD,IAAUE,YAAY,CACvBC,YAAazX,EAAQyX,YACrBC,gBAAiB1X,EAAQ0X,gBACzBC,SAAU3X,EAAQ2X,SAClBC,UAAW5X,EAAQ4X,UACnBC,gBAAiB7X,EAAQ6X,gBACzBC,mBAAoB9X,EAAQ8X,mBAC5BC,OAAQ/X,EAAQ+X,OAChBC,OAAQhY,EAAQgY,UAEhBlB,EAAOjZ,MAAM,+CA0EN,SAASoa,EAAWC,EAAMlY,GAMrCnH,KAAKsf,YAAc,IAAIC,IACvBvf,KAAKwf,aAAe,IAAIC,IACxBzf,KAAKqf,KAAOA,EACZrf,KAAKmH,QAAUA,GAAW,GAE1BnH,KAAK0f,4BACC1f,KAAKmH,QAAQyX,aAAe5e,KAAKmH,QAAQ0X,iBAAmB7e,KAAKmH,QAAQwY,kBAK1B,IAAzCP,EAAWQ,0BACnB5f,KAAK0f,8BACL1f,KAAK6f,iCACC7f,KAAKmH,QAAQ0Y,iCACfC,IAAQC,gBACRvB,EAAsBxe,KAAKmH,SAnIvC,SAA0BA,GACjBoX,IACDyB,IAAWC,WACP9Y,EAAQ+Y,iBAAmBC,KACf,GACE,OACI5f,EACC,IAAMie,EAAsBrX,IAEnDoX,GAAoB,GA4HhB6B,CAAiBpgB,KAAKmH,SAGrBnH,KAAKmH,QAAQ+X,QACdjB,EAAO9Y,KAAK,4BAUpBnF,KAAKqgB,oBAAsB,IAAId,IAE/BH,EAAWkB,UAAUC,IAAIvgB,MA7E7Bof,EAAWxU,KAAO,SAASzD,GACvBiY,EAAWoB,oBAAsBrZ,EAAQsZ,mBACF,iBAA5BtZ,EAAQuZ,kBACftB,EAAWsB,gBAAkBvZ,EAAQuZ,iBAGE,iBAAhCvZ,EAAQwZ,sBACfvB,EAAWuB,oBAAsBxZ,EAAQwZ,qBAGC,iBAAnCxZ,EAAQyZ,yBACfxB,EAAWwB,uBAAyBzZ,EAAQyZ,wBAGhDxB,EAAWQ,0BAA4BzY,EAAQyY,2BAiEnDR,EAAWoB,oBAAqB,EAChCpB,EAAWuB,oBAAsB,IACjCvB,EAAWsB,gBAAkB,IAC7BtB,EAAWQ,2BAA4B,EACvCR,EAAWyB,UAAYA,IAEvB7zB,OAAOC,eAAemyB,EAAY,YAAa,CAM3CjyB,IAAG,KACMmxB,IACDA,EAAa,IAAIwC,KAGdxC,KAQfc,EAAWlxB,UAAU6yB,iBAAmB,SAASC,GAC7ChhB,KAAKihB,gBAAgBD,GAErB,IACI,MAAME,EACA,IAAIC,IACFH,EACA5B,EAAWuB,oBACXvB,EAAWsB,gBACX1gB,KAAKwf,cAEb0B,EAASnE,MAAMqC,EAAWoB,oBAC1BxgB,KAAKsf,YAAY8B,IAAIJ,EAAe9Z,GAAIga,GAC1C,MAAO7nB,GACL4kB,EAAOjZ,MAAO,iDAAgD3L,KAItE+lB,EAAWiC,WAAa,GAExBjC,EAAWkC,gBAAkB,SAASC,EAAQ/V,GAC1C,IAAK4T,EAAWoB,mBACZ,OAEJ,MAAMa,EAAa,IAAIG,IAAWD,EAAQnC,EAAWuB,oBACjDnV,GAEJxL,KAAKqhB,WAAWne,KAAKme,GACrBA,EAAWtE,SAGfqC,EAAWlxB,UAAUuzB,sBAAwB,SAASC,GAC7CtC,EAAWoB,oBAGhBxgB,KAAKwf,aAAamC,GAAGC,IAA8BF,IAGvDtC,EAAWlxB,UAAU2zB,yBAA2B,SAASH,GAChDtC,EAAWoB,oBAGhBxgB,KAAKwf,aAAasC,eAAeF,IAA8BF,IAGnEtC,EAAWlxB,UAAU6zB,0BAA4B,SAASL,GACtD1hB,KAAKwf,aAAamC,GAAGC,IAAkCF,IAG3DtC,EAAWlxB,UAAU8zB,6BAA+B,SAASN,GACzD1hB,KAAKwf,aAAasC,eACdF,IAAkCF,IAG1CtC,EAAWlxB,UAAU+zB,2BAA6B,SAASP,GACvD1hB,KAAKwf,aAAamC,GAAGC,IAAmCF,IAG5DtC,EAAWlxB,UAAUg0B,8BAAgC,SAASR,GAC1D1hB,KAAKwf,aAAasC,eACdF,IACAF,IAGRtC,EAAWlxB,UAAUi0B,yBAA2B,SAAST,GACrD1hB,KAAKwf,aAAamC,GAAGC,IAAkCF,IAG3DtC,EAAWlxB,UAAUk0B,4BAA8B,SAASV,GACxD1hB,KAAKwf,aAAasC,eAAeF,IAC7BF,IASRtC,EAAWlxB,UAAUm0B,0BAA4B,SAASX,GACtD1hB,KAAKwf,aAAamC,GAAGC,IAAmCF,IAS5DtC,EAAWlxB,UAAUo0B,qBAAuB,SAASC,GAC5CzC,IAAQ0C,+BAMbxiB,KAAKyiB,yBAA2B,IAAIC,IAChC1iB,KAAKwf,aACLJ,EAAWwB,wBAEf2B,EAAWZ,GACPgB,oBACA,IAAM3iB,KAAKyiB,yBAAyBG,iBACxCL,EAAWZ,GACPgB,kBACA,IAAM3iB,KAAKyiB,yBAAyBI,iBAdpC5E,EAAO9Y,KAAK,kEAuBpBia,EAAWlxB,UAAU40B,kBAAoB,WACrC,OAAO9iB,KAAKyiB,yBACNziB,KAAKyiB,yBAAyBK,oBAC9B,MASV1D,EAAWlxB,UAAU60B,6BAA+B,SAASrB,GACzD1hB,KAAKwf,aAAasC,eAAeF,IAAmCF,IAGxEtC,EAAWlxB,UAAU80B,QAAU,WAC3B,IAQShjB,KAAKqgB,oBAAoB4C,MAC1BjjB,KAAKwf,aAAa0D,KAAKtB,KAE3B,IAAK,MAAMuB,KAAanjB,KAAKqgB,oBAAoB+C,SAC7CpjB,KAAKqjB,cAAcF,EAAUG,KAEjC,IAAK,MAAMC,KAASvjB,KAAKsf,YAAY/Z,OACjCvF,KAAKwjB,iBAAiBD,GAEtBvjB,KAAKwf,cACLxf,KAAKwf,aAAaiE,qBAlB1B,QAqBIrE,EAAWkB,UAAUoD,OAAO1jB,QAIpCof,EAAWuE,eAAiB,SAASpC,GACjC,GAAKnC,EAAWoB,mBAIhB,IAAK,IAAIl0B,EAAI,EAAGA,EAAI8yB,EAAWiC,WAAW9qB,OAAQjK,IAC9C,GAAI8yB,EAAWiC,WAAW/0B,GAAGi1B,SAAWA,EAAQ,CACzBnC,EAAWiC,WAAWpd,OAAO3X,EAAG,GAExC,GAAGs3B,OACd,QAUZxE,EAAWlxB,UAAUs1B,iBAAmB,SAASD,GAC7C,MAAMrC,EAAWlhB,KAAKsf,YAAYnyB,IAAIo2B,GAElCrC,IACAA,EAAS0C,OACT5jB,KAAKsf,YAAYoE,OAAOH,KAQhCnE,EAAWlxB,UAAU+yB,gBAAkB,SAASqC,GAC5CtjB,KAAKwjB,iBAAiBF,EAAIpc,KAW9BkY,EAAWlxB,UAAU21B,eAAiB,SAASP,EAAKQ,GAChD,IAAK9jB,KAAK0f,4BACN,OACG,GAAI1f,KAAKqgB,oBAAoB0D,IAAIT,EAAIpc,IAGxC,YAFA+W,EAAOjZ,MAAM,gDAKjBiZ,EAAO/Y,KAAM,0BAAyBoe,QAEtC,MAAMU,EACA,IAAIvF,IACF6E,EACA,CACIpE,OAAQlf,KAAKmH,QAAQ+X,OACrB4E,iBAGZ9jB,KAAKqgB,oBAAoBe,IAAIkC,EAAIpc,GAAI8c,IASzC5E,EAAW6E,0BAA4B,WACnC,MAAMC,EAAc,IAAIpD,IAExB,IAAK,MAAMqD,KAAc/E,EAAWkB,UAChC,IAAK,MAAM8D,KAAMD,EAAW9D,oBAAoB+C,SAC5Cc,EAAY3D,IAAI6D,GAIxB,OAAOF,GAMX9E,EAAWlxB,UAAUm1B,cAAgB,SAASC,GAC1C,MAAMe,EAAoBrkB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAEvDmd,IAUsC,IAAlCrkB,KAAKqgB,oBAAoB4C,MACzBjjB,KAAKwf,aAAa0D,KAAKtB,KAE3B5hB,KAAKqgB,oBAAoBqD,OAAOJ,EAAIpc,IAGpCmd,EAAkBC,uBAW1BlF,EAAWlxB,UAAUq2B,mBAAqB,WACtC,OAAOvkB,KAAK0f,6BAShBN,EAAWlxB,UAAUs2B,gCAAkC,SAASlB,EAAKmB,GACjE,MAAMC,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASC,sBAAsBF,IAQvCrF,EAAWlxB,UAAU02B,6BAA+B,SAAStB,GACzD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASE,gCAUjBxF,EAAWlxB,UAAU22B,cAAgB,SAASvB,EAAKwB,EAAO7d,GACtD,MAAMyd,EAAWpB,GAAOtjB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAEzDuX,IAAUoG,cAAcC,EAAO7d,EAAMyd,IAUzCtF,EAAWlxB,UAAU62B,uBACf,SAAShI,EAAOiI,GACd,IAAK,MAAMZ,KAAMpkB,KAAKqgB,oBAAoB+C,SACtCgB,EAAGW,uBAAuBhI,EAAOiI,IAS7C5F,EAAWlxB,UAAU+2B,yBAA2B,SAASC,GACrD,IAAK,MAAMd,KAAMpkB,KAAKqgB,oBAAoB+C,SACtCgB,EAAGa,2BAIPjlB,KAAKqf,KAAK4F,yBAAyBC,IAQvC9F,EAAW+F,0BAA4B,SAASC,GAC5C,MAAMC,EAAYjG,EAAW6E,4BAE7B,GAAIoB,EAAUpC,KACV,IAAK,MAAMmB,KAAMiB,EACb5G,IAAU0G,0BAA0BC,EAAahB,QAGrD3F,IAAU0G,0BAA0BC,EAAa,OAmBzDhG,EAAWlxB,UAAUo3B,4BAA8B,SAC3ChC,EACA0B,EACAO,EACAC,EACAC,EACAC,GACJ,MAAMhB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASY,4BACLN,EACAO,EACAC,EACAC,EACAC,IAWZtG,EAAWuG,uBAAyB,SAAStsB,GACzC,MAAM2L,EACA3L,aAAausB,IAthBvB,SAA2C5gB,GACvC,MAAMmL,EAAM,IAAI9Y,MAkBhB,OAfA8Y,EAAI/L,MAAQY,EAAMZ,MAGlB+L,EAAItjB,MAAQmY,EAAMnY,MAAQ,kBAAoBmY,EAAM6gB,KAAO7gB,EAAM6gB,IAAI7gB,OAC9DA,EAAM6gB,IAAI7gB,MAAMnY,KAAQ,MAAKmY,EAAM6gB,IAAI7gB,MAAMnY,KAAS,IAK7DsjB,EAAI2V,eAAiB9gB,EAAM6gB,KAAO7gB,EAAM6gB,IAAIE,YACtCnZ,KAAKwL,UAAUpT,EAAM6gB,IAAIE,aAAe,GAG9C5V,EAAI1L,QAAUO,EAAMP,QAEb0L,EAogBG6V,CAAkC3sB,GAAKA,EAC3CgsB,EAAYjG,EAAW6E,4BAE7B,GAAIoB,EAAUpC,KACV,IAAK,MAAMmB,KAAMiB,EACb5G,IAAUkH,uBAAuB3gB,EAAOof,QAG5C3F,IAAUkH,uBAAuB3gB,EAAO,OAUhDoa,EAAWlxB,UAAU+3B,sBAAwB,SAAS5sB,EAAGiqB,GACrD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASuB,sBAAsB5sB,IAUvC+lB,EAAWlxB,UAAUg4B,uBAAyB,SAAS7sB,EAAGiqB,GACtD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASwB,uBAAuB7sB,IAUxC+lB,EAAWlxB,UAAUi4B,uBAAyB,SAAS9sB,EAAGiqB,GACtD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAASyB,uBAAuB9sB,IAUxC+lB,EAAWlxB,UAAUk4B,wBAA0B,SAAS/sB,EAAGiqB,GACvD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAAS0B,wBAAwB/sB,IAUzC+lB,EAAWlxB,UAAUm4B,0BAA4B,SAAShtB,EAAGiqB,GACzD,MAAMoB,EAAW1kB,KAAKqgB,oBAAoBlzB,IAAIm2B,EAAIpc,IAE9Cwd,GACAA,EAAS2B,0BAA0BhtB,IAS3C+lB,EAAWkH,QAAU,SAAS55B,GAC1B,MAAM65B,EAAe,IAAIzF,IAMzB,IAAK,MAAM0F,KAASpH,EAAWkB,UAAW,CACtC,GAAIkG,EAAM3G,iCACN,OAGA2G,EAAMnG,oBAAoB4C,MAC1BsD,EAAahG,IAAIiG,EAAMnG,oBAAoB+C,SAASqD,OAAOl5B,OAInE,GAAIg5B,EAAatD,KACb,IAAK,MAAMyD,KAAcH,EACrB9H,IAAUkI,mBAAmBj6B,EAAGg6B,QAGpCjI,IAAUkI,mBAAmBj6B,EAAG,OAYxC0yB,EAAWlxB,UAAU04B,aAAe,SAASC,EAASC,GAUlD,OAPA1H,EAAWyB,UAAUkG,UACjBC,IACA,CACIC,OAAQJ,EACRC,YAGDrI,IAAUmI,aAAa5mB,KAAKmH,QAAQ+X,OAAQ2H,EAASC,IAGhE1H,EAAW8H,UAAY9J,EAAQ,KAAsC8J,UAOrE9H,EAAW+H,kBAAoB,SAASniB,GAChCA,aAAiB4gB,KAAmB5gB,EAAM6gB,IAC1CzG,EAAWuG,uBAAuB3gB,GAElCoa,EAAWkH,QAAQthB,IAa3Boa,EAAWgI,oBAAsB,SAASC,EAAOC,EAAa,IAC1D,IAAKD,EAGD,YAFApJ,EAAO9Y,KAAK,iCAKhB,IAAIoiB,EAIAA,EADiB,iBAAVF,EACMA,EAEA,CACTx6B,KAAMw6B,EACNC,cAIRrJ,EAAOrZ,IAAIgI,KAAKwL,UAAUmP,IAG1BvnB,KAAK6gB,UAAUkG,UAAUM,EAAOC,IAUpClI,EAAWoI,cAAgB,SAASC,EAAWH,EAAa,IACxDtnB,KAAK6gB,UAAUkG,UAAUU,EAAWH,M,6DChsBxCv7B,EAAOD,QA7GW,CAId47B,qBAAsB,2BAKtBC,oBAAqB,0BACrBC,kBAAmB,wBACnBC,6BAA8B,mCAC9Bn0B,yBAA0B,+BAC1Bo0B,uBAAwB,6BAOxBC,oBAAqB,0BAErBC,iCAAkC,uCAOlCC,oBAAqB,0BAQrBC,yBAA0B,+BAK1BC,2CAA4C,iDAE5CC,eAAgB,qBAMhBC,mBAAoB,yBAIpBC,kBAAmB,wBAMnBC,qBAAsB,2BAItBC,oBAAqB,0BAKrBC,6BAA8B,mCAK9BC,8BAA+B,oCAC/BC,4BAA6B,kCAC7BC,oBAAqB,0BAKrBC,wBAAyB,8BACzBC,sBAAuB,4BAMvBj1B,0BAA2B,gCAS3Bk1B,oBAAqB,0BASrBC,qBAAsB,6B,oiEC9EnB,MAAMC,EAAmB,cAMnBC,EAAY,OAMZC,EAAa,QAMbC,EAAU,KAOVC,EAAwB,UAOxBC,EAA2B,yBAO3BC,EAA4B,4BAQ5BC,EAA2B,2BAO3BC,EAA0B,YAO1BC,EACP,6BAOOC,EACP,4BAOOC,EAAsB,UAOtBC,EAAyB,cAMzBC,EAAoB,SAOpBC,EAA2B,gBAuB3BC,EAAmB,mBAYnBC,EAA0B,0BAS1BjD,EAAW,WAgBXkD,EAAe,eAWfC,EACP,kCAgBOC,EAAoB,oBAQpBC,EAAgB,sBAUhBC,EAAgB,gBAMhBC,EAAwB,WAGjC,MAAO,CACHC,OAHe,cAIfC,cAJe,cAKfxjB,KAAMgiB,IAUDyB,EACP,SAASC,EAAWC,EAAcC,GAChC,MAAO,CACH5jB,KAAMgiB,EACNuB,OAAQ,oBACRvtB,WAAY,EAAF,CACN,WAAc0tB,EACd,cAAiBC,GACdC,KAYZ,SAASC,EAAsBN,EAAQvtB,GAC1C,MAAO,CACHutB,SACAvtB,aACA8tB,OAAQ,aACR9jB,KAAMgiB,GAeP,MAAM+B,EAAoC,SAASC,EAAOhuB,GAC7D,MAAMutB,EAAS,2BAEf,MAAO,CACHA,SACAC,cAAeQ,EACfhuB,aACA8tB,OAAQP,EACRvjB,KAAMgiB,IAWDiC,EAAoB,SAASC,EAAeC,EAAQC,GAO7D,MAAO,CACHpuB,WAPe,CACf,eAAkBkuB,EAClBC,SACAC,OAKAx+B,KAAM,UACNoa,KAAMgiB,IAODqC,EAAuB,WAGhC,MAAO,CACHd,OAHW,aAIXC,cAJW,aAKXxjB,KAAMgiB,IAYDsC,EAA0B,SAASf,EAAQvtB,EAAa,IACjE,MAAO,CACHgK,KAAMgiB,EACN8B,OAAQ,iBACRP,SACAvtB,eAUKuuB,EAAyC,SAASvuB,EAAa,IAGxE,MAAO,CACHgK,KAAMgiB,EACN8B,OAAQ,mBACRP,OALW,WAMXvtB,eASKwuB,EAAoB,SAASjB,EAAQvtB,EAAa,IAC3D,MAAO,CACHgK,KAAMgiB,EACNuB,SACAO,OAAQ,SACR9tB,eAWKyuB,EAA8B,SAASC,EAAWp+B,GAC3D,MAAO,CACH0P,WAAY,CACR,WAAc0uB,EACdp+B,SAEJi9B,OAAQ,4BACRvjB,KAAMgiB,IASD2C,EAAiB,SAASpB,EAAQvtB,EAAa,IACxD,MAAO,CACHgK,KAAMgiB,EACNuB,SACAO,OAAQ,MACR9tB,eAOK4uB,EAA2B,WACpC,MAAO,CACH5kB,KAAMgiB,EACNuB,OAAQ,mBA+CHsB,EAAsB,SAAS7uB,GACxC,MAAO,CACHgK,KAAMgiB,EACNuB,OAAQ,YACRvtB,eA0BK8uB,EAA4B,SAAS9uB,GAC9C,MAAO,CACHgK,KAAMgiB,EACNuB,OAAQ,kBACRvtB,eAYD,SAAS+uB,EAA8BC,EAAQC,EAAkBC,GACpE,MAAO,CACHllB,KAAMgiB,EACNuB,OAAQ,uBACRvtB,WAAY,CACRgvB,SACAC,mBACAC,sBAYL,MAAMC,EAAiC,SAASzP,EAAMnN,GACzD,MAAO,CACHvI,KAAMgiB,EACNuB,OAAQ,uBACRvtB,WAAY,CACR0f,OACAnN,YAoBC6c,EAAkB,SAASpvB,GACpC,OAAO+tB,EAAkC,OAAQ/tB,K,6BCpjBrD,sGAGO,MAAMqvB,EAAQ,QAKRC,EAAY,YAKZC,EAAQ,S,8BCbrB,gEACA,MAAMvO,EAASF,oBAAUM,GAMnBoO,EAAU,CACZC,mBAAmB/qB,GAIRA,EAAOA,EAAKC,QAAQ,iBAAkB,IAAMD,EAEvDgrB,UAAUC,EAAWC,GACjB,IACIC,EAAKC,EADLpzB,EAAO,KAeX,OAZKozB,EAAQN,EAAQO,SAASJ,EAAW,eAAgBC,MAC7CC,EACEL,EAAQO,SACNJ,EACA,aACAC,MACZlzB,EAAO,CACHozB,MAAON,EAAQQ,cAAcF,GAC7BD,IAAKL,EAAQS,YAAYJ,KAI1BnzB,GAEXszB,cAAczoB,GACHA,EAAK2oB,UAAU,IAE1BC,cAAcC,GACF,eAAcA,EAE1BH,YAAY1oB,GACDA,EAAK2oB,UAAU,IAE1BG,YAAYR,GACA,aAAYA,EAExBS,SAAS/oB,GACEA,EAAK2oB,UAAU,GAE1BK,WAAWhpB,GACP,MAAM7K,EAAO,GACPqK,EAAQQ,EAAK2oB,UAAU,GAAGpqB,MAAM,KAUtC,OARApJ,EAAK8zB,MAAQzpB,EAAM0pB,QACnB/zB,EAAKg0B,KAAO3pB,EAAM0pB,QAClB/zB,EAAK8O,MAAQzE,EAAM0pB,QACa,KAA5B1pB,EAAMA,EAAMzN,OAAS,IACrByN,EAAM0M,MAEV/W,EAAKi0B,IAAM5pB,EAEJrK,GAEXk0B,WAAWC,GAEF,KAAIA,EAAML,SAASK,EAAMH,QAAQG,EAAMrlB,SACpCqlB,EAAMF,IAAIzqB,KAAK,OAE3B4qB,YAAYvpB,GACR,MAAM7K,EAAO,GACb,IAAIqK,EAAQQ,EAAK2oB,UAAU,GAAGpqB,MAAM,KAQpC,OANApJ,EAAKuN,GAAKlD,EAAM0pB,QAChB1pB,EAAQA,EAAM,GAAGjB,MAAM,KACvBpJ,EAAK9M,KAAOmX,EAAM0pB,QAClB/zB,EAAKq0B,UAAYhqB,EAAM0pB,QACvB/zB,EAAKs0B,SAAWjqB,EAAMzN,OAASyN,EAAM0pB,QAAU,IAExC/zB,GAQXu0B,aAAa1pB,GACT,MAAMR,EAAQQ,EAAK2oB,UAAU,IAAIpqB,MAAM,KAQvC,MAAO,CAPUiB,EAAM,GACNA,EAAM,GAGHA,EAAMzN,OAAS,EAAIyN,EAAM,GAAK,OAKtDmqB,YAAYjuB,GACR,IAAIsE,EACG,YAAWtE,EAAG0C,aAAa,SAAS1C,EAAG0C,aAAa,WACnD1C,EAAG0C,aAAa,eAOxB,OALI1C,EAAG0C,aAAa,aACmB,MAAhC1C,EAAG0C,aAAa,cACnB4B,GAAS,IAAGtE,EAAG0C,aAAa,aAGzB4B,GAEX4pB,YAAY5pB,GACR,MAAM7K,EAAO,GACPqK,EAAQQ,EAAK2oB,UAAU,GAAGpqB,MAAM,KAStC,OAPApJ,EAAKyD,IAAM4G,EAAM0pB,QACjB/zB,EAAK,gBAAkBqK,EAAM0pB,QAC7B/zB,EAAK,cAAgBqK,EAAM0pB,QACvB1pB,EAAMzN,SACNoD,EAAK,kBAAoBqK,EAAMb,KAAK,MAGjCxJ,GAEX00B,iBAAiB7pB,GACb,MAAM7K,EAAO,GACPqK,EAAQQ,EAAK2oB,UAAU,IAAIpqB,MAAM,KAMvC,OAJApJ,EAAKd,KAAOmL,EAAM0pB,QAClB/zB,EAAK20B,YAActqB,EAAM0pB,QAGlB/zB,GAEX40B,UAAU/pB,GACN,MAAM7K,EAAO,GACb,IAAIqK,EAAQQ,EAAKzB,MAAM,KAEvBiB,EAAM0pB,QACN1pB,EAAQA,EAAMb,KAAK,KAAKJ,MAAM,KAC9B,IAAK,IAAIzW,EAAI,EAAGA,EAAI0X,EAAMzN,OAAQjK,IAAK,CACnC,IAAIuB,EAAMmW,EAAM1X,GAAGyW,MAAM,KAAK,GAE9B,KAAOlV,EAAI0I,QAAqB,MAAX1I,EAAI,IACrBA,EAAMA,EAAIs/B,UAAU,GAExB,MAAM5/B,EAAQyW,EAAM1X,GAAGyW,MAAM,KAAK,GAE9BlV,GAAON,EACPoM,EAAKuJ,KAAK,CAAErW,KAAMgB,EACdN,UACGM,GAEP8L,EAAKuJ,KAAK,CAAErW,KAAM,GACdU,MAAOM,IAInB,OAAO8L,GAEX60B,kBAAkBhqB,GACd,MAAMiqB,EAAY,GACZC,EAAQlqB,EAAKzB,MAAM,KAEzB0rB,EAAUE,WAAaD,EAAM,GAAGvB,UAAU,IAC1CsB,EAAUG,UAAYF,EAAM,GAC5BD,EAAU/lB,SAAWgmB,EAAM,GAAG/rB,cAC9B8rB,EAAU5c,SAAW6c,EAAM,GAC3BD,EAAUI,GAAKH,EAAM,GACrBD,EAAUd,KAAOe,EAAM,GAGvBD,EAAUxnB,KAAOynB,EAAM,GACvBD,EAAUK,WAAa,EACvB,IAAK,IAAIxiC,EAAI,EAAGA,EAAIoiC,EAAMn4B,OAAQjK,GAAK,EACnC,OAAQoiC,EAAMpiC,IACd,IAAK,QACDmiC,EAAU,YAAcC,EAAMpiC,EAAI,GAClC,MACJ,IAAK,QACDmiC,EAAU,YAAcC,EAAMpiC,EAAI,GAClC,MACJ,IAAK,aACDmiC,EAAUK,WAAaJ,EAAMpiC,EAAI,GACjC,MACJ,IAAK,UACDmiC,EAAUM,QAAUL,EAAMpiC,EAAI,GAC9B,MACJ,QACI2xB,EAAOrZ,IACF,sCACG8pB,EAAMpiC,UAAUoiC,EAAMpiC,EAAI,OAS1C,OANAmiC,EAAUO,QAAU,IAIpBP,EAAUvnB,GAAKkE,KAAKC,SAAS3U,SAAS,IAAI0e,OAAO,EAAG,IAE7CqZ,GAEXQ,kBAAkBC,GACd,IAAI1qB,EAAO,CACN,eAAc0qB,EAAKP,WACpBO,EAAKN,UACLM,EAAKxmB,SACLwmB,EAAKrd,SACLqd,EAAKL,GACLK,EAAKvB,KACL,MACAuB,EAAKjoB,MACP9D,KAAK,KAGP,OADAqB,GAAQ,IACA0qB,EAAKjoB,MACb,IAAK,QACL,IAAK,QACL,IAAK,QACGioB,EAAKC,gBAAgB,aACdD,EAAKC,gBAAgB,cAC5B3qB,GAAQ,QACRA,GAAQ,IACRA,GAAQ0qB,EAAK,YACb1qB,GAAQ,IACRA,GAAQ,QACRA,GAAQ,IACRA,GAAQ0qB,EAAK,YACb1qB,GAAQ,KAchB,OAVI0qB,EAAKC,gBAAgB,aACrB3qB,GAAQ,UACRA,GAAQ,IACRA,GAAQ0qB,EAAKH,QACbvqB,GAAQ,KAEZA,GAAQ,aACRA,GAAQ,IACRA,GAAQ0qB,EAAKC,gBAAgB,cAAgBD,EAAKJ,WAAa,IAExDtqB,GAEX4qB,UAAUC,GAIN,MAAM11B,EAAO,IAAI4lB,IACX+P,EAAQD,EAAKtsB,MAAM,QAEzB,IAAK,IAAIzW,EAAI,EAAGA,EAAIgjC,EAAM/4B,OAAQjK,IAC9B,GAAiC,YAA7BgjC,EAAMhjC,GAAG6gC,UAAU,EAAG,GAAkB,CAExC,MAAMnI,EAAOsK,EAAMhjC,GAAGyW,MAAM,WAAW,GAAGA,MAAM,KAAK,GAEhDpJ,EAAKxM,IAAI63B,IACVrrB,EAAKynB,IAAI4D,EAAM,IAGnBrrB,EAAKxM,IAAI63B,GAAM9hB,KAAKosB,EAAMhjC,IAIlC,OAAOqN,GAEX41B,YAAY/qB,GACR,MAAMR,EAAQQ,EAAK4Q,OAAO,IAAIrS,MAAM,KAC9BpJ,EAAO,GAMb,OAJAA,EAAK61B,GAAKxrB,EAAM0pB,QAChB/zB,EAAKsN,KAAOjD,EAAM0pB,QAClB/zB,EAAK81B,OAASzrB,EAEPrK,GAEX+1B,YAAYlrB,GACR,MAAMR,EAAQQ,EAAK4Q,OAAO,GAAGrS,MAAM,KAC7BpJ,EAAO,GAYb,OAVAA,EAAKpM,MAAQyW,EAAM0pB,SACc,IAA7B/zB,EAAKpM,MAAMqW,QAAQ,KACnBjK,EAAKg2B,UAAY,QAEjBh2B,EAAKg2B,UAAYh2B,EAAKpM,MAAM6nB,OAAOzb,EAAKpM,MAAMqW,QAAQ,KAAO,GAC7DjK,EAAKpM,MAAQoM,EAAKpM,MAAM6nB,OAAO,EAAGzb,EAAKpM,MAAMqW,QAAQ,OAEzDjK,EAAKi2B,IAAM5rB,EAAM0pB,QACjB/zB,EAAK81B,OAASzrB,EAEPrK,GAEXqzB,SAAS6C,EAAUC,EAAQC,GACvB,IAAIT,EAAQO,EAAS9sB,MAAM,QAE3B,IAAK,IAAIzW,EAAI,EAAGA,EAAIgjC,EAAM/4B,OAAQjK,IAC9B,GAAIgjC,EAAMhjC,GAAG6gC,UAAU,EAAG2C,EAAOv5B,UAAYu5B,EACzC,OAAOR,EAAMhjC,GAGrB,IAAKyjC,EACD,OAAO,EAIXT,EAAQS,EAAYhtB,MAAM,QAC1B,IAAK,IAAI7J,EAAI,EAAGA,EAAIo2B,EAAM/4B,OAAQ2C,IAC9B,GAAIo2B,EAAMp2B,GAAGi0B,UAAU,EAAG2C,EAAOv5B,UAAYu5B,EACzC,OAAOR,EAAMp2B,GAIrB,OAAO,GAEX82B,UAAUH,EAAUC,EAAQC,GACxB,IAAIT,EAAQO,EAAS9sB,MAAM,QAC3B,MAAMktB,EAAU,GAEhB,IAAK,IAAI3jC,EAAI,EAAGA,EAAIgjC,EAAM/4B,OAAQjK,IAC1BgjC,EAAMhjC,GAAG6gC,UAAU,EAAG2C,EAAOv5B,UAAYu5B,GACzCG,EAAQ/sB,KAAKosB,EAAMhjC,IAG3B,GAAI2jC,EAAQ15B,SAAWw5B,EACnB,OAAOE,EAIXX,EAAQS,EAAYhtB,MAAM,QAC1B,IAAK,IAAI7J,EAAI,EAAGA,EAAIo2B,EAAM/4B,OAAQ2C,IAC1Bo2B,EAAMp2B,GAAGi0B,UAAU,EAAG2C,EAAOv5B,UAAYu5B,GACzCG,EAAQ/sB,KAAKosB,EAAMp2B,IAI3B,OAAO+2B,GAEXC,kBAAkB1rB,GAKd,GAAmC,IAA/BA,EAAKZ,QAAQ,cAEbY,EAAQ,KAAIA,OACT,GAA8B,iBAA1BA,EAAK2oB,UAAU,EAAG,IAMzB,OALAlP,EAAOrZ,IACH,kEAEJqZ,EAAOrZ,IAAIJ,GAEJ,KAE6B,SAApCA,EAAK2oB,UAAU3oB,EAAKjO,OAAS,KAE7BiO,EAAOA,EAAK2oB,UAAU,EAAG3oB,EAAKjO,OAAS,IAE3C,MAAMk4B,EAAY,GACZC,EAAQlqB,EAAKzB,MAAM,KAEzB,GAAiB,QAAb2rB,EAAM,GAIN,OAHAzQ,EAAOrZ,IAAI,uCACXqZ,EAAOrZ,IAAIJ,GAEJ,KAEXiqB,EAAUE,WAAaD,EAAM,GAAGvB,UAAU,IAC1CsB,EAAUG,UAAYF,EAAM,GAC5BD,EAAU/lB,SAAWgmB,EAAM,GAAG/rB,cAC9B8rB,EAAU5c,SAAW6c,EAAM,GAC3BD,EAAUI,GAAKH,EAAM,GACrBD,EAAUd,KAAOe,EAAM,GAGvBD,EAAUxnB,KAAOynB,EAAM,GAEvBD,EAAUK,WAAa,IACvB,IAAK,IAAIxiC,EAAI,EAAGA,EAAIoiC,EAAMn4B,OAAQjK,GAAK,EACnC,OAAQoiC,EAAMpiC,IACd,IAAK,QACDmiC,EAAU,YAAcC,EAAMpiC,EAAI,GAClC,MACJ,IAAK,QACDmiC,EAAU,YAAcC,EAAMpiC,EAAI,GAClC,MACJ,IAAK,aACDmiC,EAAUK,WAAaJ,EAAMpiC,EAAI,GACjC,MACJ,IAAK,UACDmiC,EAAUM,QAAUL,EAAMpiC,EAAI,GAC9B,MACJ,QACI2xB,EAAOrZ,IAAK,oBAAmB8pB,EAAMpiC,UAAUoiC,EAAMpiC,EAAI,OASjE,OANAmiC,EAAUO,QAAU,IAIpBP,EAAUvnB,GAAKkE,KAAKC,SAAS3U,SAAS,IAAI0e,OAAO,EAAG,IAE7CqZ,GAEX0B,oBAAoBjB,GAChB,IAAI1qB,EAAO,eAEXA,GAAQ0qB,EAAKtsB,aAAa,cAC1B4B,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,aAC1B4B,GAAQ,IAER,IAAIkE,EAAWwmB,EAAKtsB,aAAa,YAmBjC,OAfIkd,IAAQsQ,aAA0C,WAA3B1nB,EAAS/F,gBAChC+F,EAAW,OAGflE,GAAQkE,EACRlE,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,YAC1B4B,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,MAC1B4B,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,QAC1B4B,GAAQ,IACRA,GAAQ,MACRA,GAAS,IAAG0qB,EAAKtsB,aAAa,QAC9B4B,GAAQ,IACA0qB,EAAKtsB,aAAa,SAC1B,IAAK,QACL,IAAK,QACL,IAAK,QACGssB,EAAKtsB,aAAa,aACXssB,EAAKtsB,aAAa,cACzB4B,GAAQ,QACRA,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,YAC1B4B,GAAQ,IACRA,GAAQ,QACRA,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,YAC1B4B,GAAQ,KAchB,MAV+B,QAA3BkE,EAAS/F,gBACT6B,GAAQ,UACRA,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,WAC1B4B,GAAQ,KAEZA,GAAQ,aACRA,GAAQ,IACRA,GAAQ0qB,EAAKtsB,aAAa,eAAiB,IAEjC4B,EAAF,QAQZ6rB,sBAAsBC,GAClB,MAAMC,EAAWD,EAAWE,MACvBhrB,IAAIirB,GAAYA,EAASvpB,IACzBwpB,OAAO,CAAC1L,EAAM2L,EAAOC,IAAUA,EAAMhtB,QAAQohB,KAAU2L,GACvDp6B,OACCs6B,EACCP,EAAWQ,YAAcR,EAAWQ,WAAWv6B,QAAW,EAEjE,GAAIg6B,EAAW,GAAmB,IAAdM,EAEhB,OAEJ,IAAIE,EAAc,KAElB,GAAiB,IAAbR,EACAQ,EAAcT,EAAWE,MAAM,GAAGtpB,QAC/B,GAAiB,IAAbqpB,EAAgB,CAEvB,MAAMS,EACAV,EAAWQ,WAAWG,KACpBC,GAA6B,QAApBA,EAAMC,WAEnBH,IACAD,EAAcC,EAASR,MAAMztB,MAAM,KAAK,SAEzC,GAAIwtB,GAAY,EAAG,CAEtB,MAAMa,EACAd,EAAWQ,WAAWG,KACpBC,GAA6B,QAApBA,EAAMC,WAEnBC,IACAL,EAAcK,EAASZ,MAAMztB,MAAM,KAAK,IAIhD,OAAOguB,GAOXM,aAAY,IACDC,IAAWC,UAAU,EAAG,YAYnCC,iBAAiBC,EAAOzM,EAAM0M,GAC1B,IAAK,IAAIplC,EAAI,EAAGA,EAAImlC,EAAMjB,MAAMj6B,SAAUjK,EAAG,CACzC,MAAMqlC,EAAWF,EAAMjB,MAAMlkC,GAE7B,GAAIqlC,EAASzqB,KAAO8d,GACb2M,EAASr0B,YAAco0B,EAC1B,OAAOC,EAASpkC,QAa5BqkC,gBAAgBC,GACLA,EACFrB,MACAztB,MAAM,KACNyC,IAAIssB,GAAWxZ,SAASwZ,EAAS,KAS1CC,SAAQ,CAACC,EAAK/qB,IACH+qB,EAAIvE,MAAMwD,KAAKvkC,GAAKA,EAAEua,OAASA,GAO1CgrB,SAASD,GACL,MAAME,EACAF,EAAIjvB,MAAM,MAAM2tB,OAAOlsB,GAAQA,EAAK2tB,WAAW,iBAErD,GAAID,EAAW37B,OAAS,EACpB,OAAO27B,EAAW,GAAG9c,OAAO,eAAe7e,SAWnD67B,YAAYtE,EAAOuE,GACf,IAAKvE,IAAUuE,EACX,OAGJ,MAAMC,EAAuBxE,EAAMyE,IAC9B7B,OAAO6B,GAAOA,EAAIC,OAASD,EAAIC,MAAM7vB,gBAAkB0vB,EAAU1vB,eACjE6C,IAAI+sB,GAAOA,EAAIE,SAEpB,GAAIH,EAAsB,CAGtB,MAAMI,EACA5E,EAAM6E,SACPj8B,WACAqM,MAAM,KACNyC,IAAIpX,GAAKkqB,SAASlqB,EAAG,KAE1B,IAAK,MAAMohC,KAAM8C,EAAqBM,UAAW,CAC7C,MAAMC,EAAeH,EAAa9uB,QAAQ4rB,GAE1CkD,EAAazuB,OAAO4uB,EAAc,GAClCH,EAAaI,QAAQtD,GAEzB1B,EAAM6E,SAAWD,EAAavvB,KAAK,OAc3C4vB,WAAWtB,EAAOY,EAAWW,GAAc,GACvC,IAAKvB,IAAUY,EACX,OAGJ,MAAMY,EAAU,GAChB,IAAIC,EAAY,GAChB,MAAMC,EAAqBd,EAAU1vB,gBAAkBywB,IAAcC,MAAQL,EAE7E,IAAK,MAAMT,KAAOd,EAAMc,IAChBA,EAAIC,OACDD,EAAIC,MAAM7vB,gBAAkB0vB,EAAU1vB,gBACrCwwB,EACAF,EAAQ/vB,KAAKqvB,EAAIE,SAEjBS,EAAUhwB,KAAKqvB,EAAIE,UAY/B,GANIU,IACAD,EAAYzB,EAAM6B,KACb5C,OAAO6C,GAAQN,EAAQrvB,QAAQ2vB,EAAKd,UAAY,GAAKc,EAAKC,OAAOC,SAAS,wBAC1EjuB,IAAI+tB,GAAQA,EAAKd,UAGtBS,EAAU38B,OAAS,EAAG,CAGtB,MAAMm9B,EAAUR,EAAU1tB,IAAI+tB,GAAS,OAAMA,GACvCI,EAASlC,EAAM6B,KAAK5C,OACtB6C,IAA0C,IAAlCG,EAAQ9vB,QAAQ2vB,EAAKC,SAEjCN,EAAUhwB,QAAQywB,EAAOnuB,IAAI+tB,GAAQA,EAAKd,UAK1C,MAIMmB,EAJSnC,EAAMkB,SAChBj8B,WACAqM,MAAM,KACNyC,IAAI+T,QACcmX,OAAOlB,IAAiC,IAA3B0D,EAAUtvB,QAAQ4rB,IAE/B,IAAnBoE,EAAQr9B,QAERk7B,EAAM9D,KAAO,EACb8D,EAAM9B,UAAY,WAClB8B,EAAMkB,SAAW,KAEjBlB,EAAMkB,SAAWiB,EAAQzwB,KAAK,KAGlCsuB,EAAMc,IAAMd,EAAMc,IAAI7B,OAClB6C,IAA2C,IAAnCK,EAAQhwB,QAAQ2vB,EAAKd,UACjChB,EAAM6B,KAAO7B,EAAM6B,KAAK5C,OACpB6C,IAA2C,IAAnCK,EAAQhwB,QAAQ2vB,EAAKd,UAC7BhB,EAAMoC,SACNpC,EAAMoC,OAASpC,EAAMoC,OAAOnD,OACxB6C,IAA2C,IAAnCK,EAAQhwB,QAAQ2vB,EAAKd,cAMlChG,Q,m8BC1oBf,MAAMxO,EAASF,oBAAUM,GAKrByB,IAAQgU,eACR1W,EAAQ,KAGZ,MAAMoC,EAAe,IAAIC,IAenBsU,EAA0B,CAAE,QAAS,SAOrCC,EAAsB,CACxBC,MAAO,CACHC,OAAQ,CACJC,MAAO,IACPC,IAAK,IACL3Z,IAAK,OAQX4Z,EAAwB,EAI9B,IAAIC,EAAsB,UAEtBC,GAAqB,EAGrBC,GAAY,EAGZC,GAAa,EAGbC,GAAY,EAGZC,GAAa,EAGbC,GAAa,EAEjB,MAAMC,EAA0Bx5B,SAASwF,cAAc,SACjDi0B,OAC6C,IAAtCD,EAAwBE,UAErC,IAAIC,EACAC,EAKJ,SAASC,KA8ET,SAASC,EAAeC,EAAIjuB,EAAU,IAClC,MAAM4e,EAAc,CAChBsP,OAAO,EACPpB,OAAO,GAULqB,EACAxV,IAAQsQ,aACHtQ,IAAQyV,YACRzV,IAAQC,gBAEnB,GAAIqV,EAAGxxB,QAAQ,UAAY,EAAG,CAK1B,GAHAmiB,EAAYkO,MAAQ,CAAEuB,UAAW,GAC7BC,SAAU,IAEVtuB,EAAQuuB,eACJJ,IAEAvP,EAAYkO,MAAM0B,SAAWxuB,EAAQuuB,gBAIzC3P,EAAYkO,MAAMuB,UAAUI,SAAWzuB,EAAQuuB,mBAC5C,CAMH,MAAMG,EAAa1uB,EAAQ0uB,YAAcC,IAAiBC,KAEtDT,IACAvP,EAAYkO,MAAM4B,WAAaA,GAEnC9P,EAAYkO,MAAMwB,SAASvyB,KAAK,CAC5B2yB,gBAIJ1uB,EAAQ6uB,QAAU7uB,EAAQ8uB,QAAU9uB,EAAQ+uB,QAGxC/uB,EAAQ6uB,QAAU7uB,EAAQ+uB,OAE1B/uB,EAAQ6uB,OAAS7uB,EAAQ6uB,QAAU7uB,EAAQ+uB,IAC3CnQ,EAAYkO,MAAMuB,UAAUW,aAAehvB,EAAQ6uB,QAEnD7uB,EAAQ8uB,SACRlQ,EAAYkO,MAAMuB,UAAUY,aAAejvB,EAAQ8uB,SAvGnE,SACQlQ,EACAuP,EACAe,GACAC,IAAYD,KACRf,IACAvP,EAAYkO,MAAMsC,MAAQ,CACtBpC,MAAOmC,IAAYD,GAAYE,OAEnCxQ,EAAYkO,MAAMC,OAAS,CACvBC,MAAOmC,IAAYD,GAAYnC,SAIvCnO,EAAYkO,MAAMuB,UAAUgB,SAAWF,IAAYD,GAAYE,MAC/DxQ,EAAYkO,MAAMuB,UAAUiB,UAAYH,IAAYD,GAAYnC,QAGhEnO,EAAYkO,MAAMuB,UAAUgB,WAC5BzQ,EAAYkO,MAAMuB,UAAUkB,SACtB3Q,EAAYkO,MAAMuB,UAAUgB,UAGlCzQ,EAAYkO,MAAMuB,UAAUiB,YAC5B1Q,EAAYkO,MAAMuB,UAAUmB,UACtB5Q,EAAYkO,MAAMuB,UAAUiB,WAkFlCG,CACI7Q,EAAauP,EAAgCnuB,EAAQkvB,YA+C7D,GA7CIjB,EAAGxxB,QAAQ,UAAY,IACnBkc,IAAQC,gBAGRgG,EAAYsP,OAAQ,EACbvV,IAAQsQ,YACXjpB,EAAQ0vB,YACR9Q,EAAYsP,MAAQ,CAChBG,UAAW,GACXG,SAAUxuB,EAAQ0vB,YAClBpB,SAAU,CAAE,CACRG,SAAUzuB,EAAQ0vB,eAG1B9Q,EAAYsP,OAAQ,GAIxBtP,EAAYsP,MAAQ,CAAEG,UAAW,GAC7BC,SAAU,IACVtuB,EAAQ0vB,cACJvB,IAEAvP,EAAYsP,MAAMM,SAAWxuB,EAAQ0vB,aAIzC9Q,EAAYsP,MAAMI,SAASvyB,KAAK,CAC5B0yB,SAAUzuB,EAAQ0vB,eAK1B9Q,EAAYsP,MAAMI,SAASvyB,KACvB,CAAE4zB,kBAAmBrC,IAAeD,GACpC,CAAEuC,sBAAuBtC,IAAeD,GACxC,CAAEwC,qBAAsBrC,IAAeH,GACvC,CAAEyC,sBAAuBvC,IAAcF,GACvC,CAAE0C,oBAAqBtC,IAAeJ,GACtC,CAAE2C,uBAAwBzC,IAAcF,GACxC,CAAE4C,uBAAwB3C,IAAeD,GACzC,CAAE6C,sBAAuB1C,IAAeH,MAIhDY,EAAGxxB,QAAQ,WAAa,EACxB,GAAIkc,IAAQwX,WACRvR,EAAYkO,MAAQ,CAChBuB,UAAW+B,EAAiB,EAAD,KACpBpwB,GADoB,IAEvB4jB,OAAQ,YAEZ0K,SAAU,SAGX,GAAI3V,IAAQsQ,YACfrK,EAAYkO,MAAQ,CAChBuD,eAAgB,SAChBC,YAAa,SACbC,UAAWvwB,EAAQuwB,WAAa,CAC5Bjd,IAAK4Z,EACLD,IAAKC,QAIV,CACH,MAAMsD,EACA,uEAGNC,IAAqBC,iBAAiB,IAAIxgC,MAAMsgC,IAChD1Z,EAAOjZ,MAAM2yB,GAkDrB,OA/CIvC,EAAGxxB,QAAQ,YAAc,IACzBmiB,EAAYkO,MAAQ,CAChBuB,UAAW+B,EAAiB,EAAD,KACpBpwB,GADoB,IAEvB4jB,OAAQ,aAEZ0K,SAAU,IAKV3V,IAAQgY,cAAgB3wB,EAAQ4wB,kBAC5B5wB,EAAQ6wB,cAAcp0B,QAAQ,WAAa,IAS/CmiB,EAAYsP,MAAQ,CAAEG,UAAW,CAC7ByC,kBAAmBlS,EAAYkO,MAAMuB,UAAUyC,sBAKvD9wB,EAAQ+wB,YACHnS,EAAYkO,QAEblO,EAAYkO,MAAQ,CAAEuB,UAAW,GAC7BC,SAAU,KAElB1P,EAAYkO,MAAMwB,SAASvyB,KAAK,CAAEg1B,UAAW/wB,EAAQ+wB,aAOrDpY,IAAQsQ,aAAejpB,EAAQgxB,sBAI/BpS,EAAYqS,MAAO,GAGhBrS,EA+HX,SAASwR,EAAiBpwB,EAAU,IAChC,MAAM,cACF6wB,EADE,UAEFN,EAAY,CACRjd,IAAK4Z,EACLD,IAAKC,IAETltB,GACE,IAAEitB,EAAF,IAAO3Z,GAAQid,EAEf3R,EAAc,CAChBkS,kBAAmB9wB,EAAQ4jB,OAC3B2L,SAAUxqC,OAAOmsC,OAAO9B,MACxBI,UAAWzqC,OAAOmsC,OAAOnE,QAe7B,MAZmB,iBAARzZ,IACPsL,EAAYoQ,aAAe1b,GAGZ,iBAAR2Z,IACPrO,EAAYqQ,aAAehC,QAGF,IAAlB4D,IACPjS,EAAYuS,oBAAsBN,GAG/BjS,EASX,SAASwS,EAAsBpxB,EAAU,IAIrC,MAAM4e,EAAc,CAChB2R,UAAWrD,IAET,wBAAEmE,GAA4BrxB,EAMpC,OAJIqxB,GAA2BA,EAAwBpE,MACnDrO,EAAY2R,UAAYc,EAAwBpE,KAG7CrO,EASX,SAAS0S,EAAyBrD,EAAI7T,GAClC,MAAMmX,EACAC,QAAQpX,IAAWA,EAAOqX,iBAAiBriC,OAAS,EACpDsiC,EACAF,QAAQpX,IAAWA,EAAOuX,iBAAiBviC,OAAS,EACpDwiC,EAAqB,IAEE,IAAzB3D,EAAGxxB,QAAQ,WACXm1B,EAAmB9E,MAAQ4E,IAEF,IAAzBzD,EAAGxxB,QAAQ,WACXm1B,EAAmB1D,MAAQqD,GAG/BlZ,EAAa0D,KAAK8V,IAAUjR,oBAAqBgR,GA4CrD,SAASE,EAA0BC,GAC/B,MAAMC,EACAD,EAAWxI,OAAO9jC,GAAgB,eAAXA,EAAEwsC,MAAuB7iC,OAChD8iC,EACAH,EAAWxI,OAAO9jC,GAAgB,gBAAXA,EAAEwsC,MAAwB7iC,OACjD+iC,EACAJ,EAAWxI,OAAO9jC,GAAgB,eAAXA,EAAEwsC,MAAuB7iC,OAChDgjC,EACAL,EAAWxI,OAAO9jC,GAAgB,gBAAXA,EAAEwsC,MAAwB7iC,OAEvD2iC,EAAW9pB,QAAQoqB,IACf,MAAMv8B,EAAa,CACf,yBAA4Bk8B,EAC5B,0BAA6BE,EAC7B,yBAA4BC,EAC5B,0BAA6BC,EAC7B,UAAaC,EAAO7D,SACpB,gBAAmB6D,EAAOC,QAC1B,YAAeD,EAAOJ,KACtB,aAAgBI,EAAOE,OAG3Bta,IAAWoI,cAAcwC,IAAkB/sB,KAenD,SAAS08B,EAAmBC,GAY5B,IAAmCC,GArFnC,SAAsCC,GAClC,OAAIA,EAAWvjC,SAAWy+B,EAAiBz+B,QAOvCujC,EAAWt0B,IAAIu0B,GAAuBz4B,OAAO6B,KAAK,MAC1C6xB,EACCxvB,IAAIu0B,GAAuBz4B,OAAO6B,KAAK,IAQpD,SAAS42B,EAAsB70B,GAC3B,OAAO0H,KAAKwL,UAAU,CAClBghB,KAAMl0B,EAAKk0B,KACXzD,SAAUzwB,EAAKywB,SACf8D,QAASv0B,EAAKu0B,QACdC,MAAOx0B,EAAKw0B,MACZM,OAAQ90B,EAAK80B,WAkDjBC,CAA6BL,KAYjC5E,GAD+B6E,EAVDD,GAWKM,MAAM,GACzCjc,EAAO/Y,KACH,qCACA8vB,GAEJiE,EAA0BjE,GAG1BxV,EAAa0D,KAAK8V,IAAUnQ,wBAAyBgR,GAErDra,EAAa0D,KAAK8V,IAAUpQ,oBAAqBiR,IA4IrD,MAAMM,UAAiBC,IAInBnkC,cACIokC,MAAM7a,GAYV5U,KAAKzD,EAAU,IA4BX,GA3BkC,kBAAvBA,EAAQstB,aACfA,EAAattB,EAAQstB,WACrBxW,EAAO/Y,KAAM,gBAAeuvB,IAEC,kBAAtBttB,EAAQutB,YACfA,EAAYvtB,EAAQutB,UACpBzW,EAAO/Y,KAAM,eAAcwvB,IAEE,kBAAtBvtB,EAAQqtB,YACfA,EAAYrtB,EAAQqtB,UACpBvW,EAAO/Y,KAAM,eAAcsvB,IAEG,kBAAvBrtB,EAAQwtB,aACfA,EAAaxtB,EAAQwtB,WACrB1W,EAAO/Y,KAAM,gBAAeyvB,IAEE,kBAAvBxtB,EAAQytB,aACfA,EAAaztB,EAAQytB,WACrB3W,EAAO/Y,KAAM,gBAAe0vB,IAGhCI,EAAmB,GACnB9oC,OAAOouC,cAAcrF,GACrBA,OAA4B10B,EAE5BP,KAAKu6B,iBAnuBb,WACI,GAAIC,UAAUC,cAAgBD,UAAUC,aAAaF,iBACjD,OAAO/uB,IACHgvB,UAAUC,aAAaF,mBAClBG,KAAKC,IACFhB,EAAmBgB,GACnBnvB,EAASmvB,IACV,KACChB,EAAmB,IACnBnuB,EAAS,OA0tBGovB,GAEpB9a,IAAQ+a,iBACR76B,KAAK86B,sBAAwBC,kBAE7B/6B,KAAKg7B,kBACCC,GAAsB,CAAC1sB,EAASgT,KAC1BhT,IACAA,EAAQ2sB,UAAY3Z,KAIhCvhB,KAAKm7B,YAAc,EAAGj0B,QAASA,EAC/BlH,KAAKo7B,WAAa,EAAGl0B,QAASA,MAC3B,KAAI4Y,IAAQub,oBACRvb,IAAQC,gBAmCZ,CACH,MAAMtb,EAAU,gDAGhB,MADAwZ,EAAOjZ,MAAMP,GACP,IAAIpN,MAAMoN,GArChBzE,KAAK86B,sBAAwBC,kBAE7B/6B,KAAKg7B,kBACCC,GAAsB,CAAC1sB,EAASgT,KAjGlD,SAA4BhT,EAASgT,GAEjC,IAeI+Z,EAfAC,EAAwB,YAEtBA,KAAyBhtB,IAC3BgtB,EAAwB,eAClBA,KAAyBhtB,IAC3BgtB,EAAwB,OAG5BA,EACAhtB,EAAQgtB,GAAyBha,GAQjCA,IACA+Z,EAAM/Z,EAAOia,eAIRF,IACD/Z,EAAOia,eAAiBF,EAAMG,IAAIC,gBAAgBna,KAG1DhT,EAAQ+sB,IAAMA,GAAO,IAsELK,CAAmBptB,EAASgT,GAErBhT,IAGfvO,KAAKm7B,YAAc,UAAS,GAAEj0B,IAO1B,MACkB,iBAAPA,EACDA,EACAulB,IAAQC,mBAAmBxlB,IAEzClH,KAAKo7B,WAAa,EAAGl0B,QAASA,EAEzB00B,YAAY1tC,UAAU4qC,iBACvB8C,YAAY1tC,UAAU4qC,eAAiB,WACnC,OAAO94B,KAAK67B,cAGfD,YAAY1tC,UAAU0qC,iBACvBgD,YAAY1tC,UAAU0qC,eAAiB,WACnC,OAAO54B,KAAK87B,cAUxB97B,KAAK+7B,qBAELC,IAAepxB,KACXzD,EACAnH,KAAKi8B,4BAA4BnuC,KAAKkS,OAEtCA,KAAKk8B,yBACLl8B,KAAKu6B,iBAAiB4B,IAClBnH,EAAmBmH,EAAGl4B,OAAO,GAE7Bga,EAAOhZ,MAAM,sBAAuB+vB,GACpCiE,EAA0BjE,GAE1BxV,EAAa0D,KACT8V,IAAUlQ,sBACVkM,GAEAlV,IAAQsc,4BACR5B,UAAUC,aAAa4B,iBACnB,eACA,IAAMr8B,KAAKu6B,iBAAiBrF,IAIhCD,EAA4B/oC,OAAOowC,YAC/B,IAAMt8B,KAAKu6B,iBAAiBrF,GAj4BP,OA44BzC6G,qBACQjc,IAAQsQ,YACRpwB,KAAKu8B,cAAgB,IACdzc,IAAQub,mBAAqBvb,IAAQC,mBAC5C/f,KAAKu8B,cAAgB,CAAE9G,SAAU,CAC7B,CAAE+G,qBAAsB,GACxB,CAAEC,oBAAoB,GACtB,CAAEC,yBAA0B,KAC5B,CAAEC,yBAAyB,GAC3B,CAAEC,2BAA2B,GAC7B,CAAEC,yBAA0B,IAC5B,CAAEC,wBAAyB,MAG/B98B,KAAK+8B,iBACCnwB,KAAKsL,MAAMtL,KAAKwL,UAAUpY,KAAKu8B,iBAGzCv8B,KAAK+8B,iBAAmB/8B,KAAK+8B,kBAAoB/8B,KAAKu8B,cAsB1DN,4BAA4B7G,EAAIjuB,EAAU,IACtC,MAAM4e,EAAcoP,EAAeC,EAAIjuB,GAIvC,OAFA8W,EAAO/Y,KAAK,wBAAyB6gB,GAE9B,IAAIiX,QAAQ,CAACC,EAASC,KACzB1C,UAAUC,aAAa0C,aAAapX,GACnC2U,KAAKnZ,IACFtD,EAAOrZ,IAAI,sBACX6zB,EAAyBrD,EAAI7T,GAC7B0b,EAAQ1b,KAEX6b,MAAMp4B,IACHiZ,EAAO9Y,KACA,yCAAGH,KAAS+gB,MACnB0S,EAAyBrD,OAAI70B,GAC7B28B,EAAO,IAAItX,IAAgB5gB,EAAO+gB,EAAaqP,QAa3DiI,gCAAgCC,EAAWvX,EAAc,IACrD,OAAO,IAAIiX,QAAQ,CAACC,EAASC,KACzB1C,UAAUC,aAAa0C,aAAapX,GAC/B2U,KAAKnZ,IACFtD,EAAOrZ,IAAI,sBACX6zB,EAAyB6E,EAAW/b,GACpC0b,EAAQ1b,KAEX6b,MAAMp4B,IACHiZ,EAAO9Y,KACA,yCAAGH,KAAS+gB,MACnB0S,EAAyB6E,OAAW/8B,GACpC28B,EAAO,IAAItX,IAAgB5gB,EAAO+gB,EAAauX,QAmB/DC,oBAAoBp2B,GAChB,OAAK60B,IAAewB,eAAkB1d,IAAQ2d,gBAKvC,IAAIT,QAAQ,CAACC,EAASC,KACzBlB,IAAe0B,aACX19B,KAAK29B,4BAA4Bx2B,GACjCoa,IACI0b,EAAQ1b,IAEZvc,IACIk4B,EAAOl4B,OAXRg4B,QAAQE,OACX,IAAI7lC,MAAM,sCA6BtBumC,+BAA+Bz2B,EAAU,IACrCA,EAAQwzB,QAAUxzB,EAAQwzB,SAAW,IAAK5G,GAC1C5sB,EAAQkvB,WAAalvB,EAAQkvB,YA3gCF,IA+gC3B,OAF0BlvB,EAAQwzB,QAAQlH,SAAS,aAEzBuI,IAAewB,cAC9BR,QAAQE,OACX,IAAI7lC,MAAM,sCAGX2I,KAAK69B,yBAAyB12B,GAASuzB,KAAKoD,GA1Z3D,SAA2BA,EAASzH,GAChC,IAAI0H,EAAa/F,EAAegG,EAChC,MAAMC,EAAM,GAQZ,GAAIH,EAAS,CAIT,MAAMI,EAAaJ,EAAQI,WAE3B,GAAIA,EAAY,CACZ,MAAMpC,EAAcoC,EAAWtF,iBAE/B,GAAIkD,EAAYvlC,OAAQ,CACpBwnC,EAAc,IAAInC,YAClB,IAAK,IAAItvC,EAAI,EAAGA,EAAIwvC,EAAYvlC,OAAQjK,IACpCyxC,EAAYI,SAASrC,EAAYxvC,IAIzC,MAAMuvC,EAAcqC,EAAWpF,iBAE/B,GAAI+C,EAAYtlC,OAAQ,CACpBynC,EAAc,IAAIpC,YAClB,IAAK,IAAI1iC,EAAI,EAAGA,EAAI2iC,EAAYtlC,OAAQ2C,IACpC8kC,EAAYG,SAAStC,EAAY3iC,UAOzC6kC,EAAcD,EAAQzI,MACtB2I,EAAcF,EAAQ7J,MAG1B+D,EAAgB8F,EAAQM,QAG5B,GAAIpG,EAAe,CACf,MAAM,OAAEzW,EAAF,SAAUqU,EAAV,WAAoByI,GAAerG,EAEzCiG,EAAI/6B,KAAK,CACLqe,SACAqU,WACAyI,aACAC,MAAO/c,EAAOuX,iBAAiB,GAC/BnN,UAAW4S,IACXC,UAAWC,IAAUC,UAqB7B,OAlBIX,GACAE,EAAI/6B,KAAK,CACLqe,OAAQwc,EACRO,MAAOP,EAAYnF,iBAAiB,GACpCjN,UAAW4S,IACXC,UAAW,OAGfR,GACAC,EAAI/6B,KAAK,CACLqe,OAAQyc,EACRM,MAAON,EAAYlF,iBAAiB,GACpCnN,UAAW4S,IACXC,UAAWC,IAAUE,OACrBtI,eAID4H,EAgVCW,CAAkBd,EAAS32B,EAAQkvB,aAY3CwH,yBAAyB12B,GACrB,MAAM03B,EAAoB13B,EAAQwzB,QAAQlH,SAAS,WAEnDtsB,EAAQwzB,QAAUxzB,EAAQwzB,QAAQjK,OAAO8I,GAC1B,YAAXA,GAMJ,OAJmBryB,EAAQwzB,QAAQpkC,OAC7ByJ,KAAKi8B,4BAA4B90B,EAAQwzB,QAASxzB,GAClD61B,QAAQC,QAAQ,OAGjBvC,KAAKoE,IAKF,MAAMC,EACA/+B,KAAKg/B,kBAAkB73B,EAAQwzB,QAASmE,GAE9C,OAAIC,EAAcxoC,QACdyJ,KAAKi/B,gBAAgBH,GAEd9+B,KAAKi8B,4BACR8C,EAAe53B,GAIduzB,KAAK,IAAMsC,QAAQE,OAAO,IAAItX,IAC3B,CAAE/4B,KAAM,gBACRsoC,EAAehuB,EAAQwzB,QAASxzB,GAChC43B,MAGLD,IAEVpE,KAAKwD,IACF,IAAKW,EACD,MAAO,CAAEX,cAGb,GAAI/2B,EAAQ+3B,2BAGR,MAFAl/B,KAAKi/B,gBAAgBf,GAEf,IAAI7mC,MAAM,yEAIpB,OAAO,IAAI2lC,QAAQ,CAACC,EAASC,KACzBlB,IAAe0B,aACX19B,KAAK29B,4BAA4Bx2B,GACjCi3B,GAAWnB,EAAQ,CACfiB,aACAE,YAEJp5B,IACQk5B,GACAl+B,KAAKi/B,gBAAgBf,GAEzBhB,EAAOl4B,SAiB/Bg6B,kBAAkBG,EAAmB,GAAI5d,GACrC,MAAM6d,EAAiB,GAEjBC,EAAuBF,EAAiB1L,SAAS,SACjDiF,EACAnX,GAAUA,EAAOqX,iBAAiBriC,OAAS,EAE7C8oC,IAAyB3G,GACzB0G,EAAel8B,KAAK,SAGxB,MAAMo8B,EAAuBH,EAAiB1L,SAAS,SACjDoF,EACAtX,GAAUA,EAAOuX,iBAAiBviC,OAAS,EAMjD,OAJI+oC,IAAyBzG,GACzBuG,EAAel8B,KAAK,SAGjBk8B,EAUXzB,4BAA4Bx2B,GACxB,MAAO,CACHo4B,sBAAuBp4B,EAAQo4B,sBAC/BC,WAAY,CACR9H,UAAWvwB,EAAQqxB,yBAEvBiH,aAAclH,EAAsBpxB,IAsB5Cu4B,kCAAkCv4B,GAC9B8W,EAAO/Y,KAAK,0BAEZ,MAAMy6B,EAAuB,GAYvBC,EAA4B,WAK9B,MAFwC,KAFtBz4B,EAAQwzB,SAAW,IAErB/2B,QAAQ,YAGpB,OAAOo5B,QAAQC,UAGnB,MAAM,2BACFiC,EADE,sBAEFK,EAFE,wBAGF/G,GACArxB,EAIJ,GAAI+3B,EAA4B,CAC5B,MAAMW,EACA7K,GAAoBA,EAAiB/D,KAAKuI,GACxB,eAAhBA,EAAOJ,OACCI,EAAO7D,WAAauJ,GACrB1F,EAAOE,QAAUwF,IAEhC,IAAKW,EACD,OAAO7C,QAAQE,OAAO,IAAItX,IACtB,CAAE/4B,KAAM,+BACR,GACA,CAAEqyC,KAIV,MAAMC,EAAmB,CAAE,UAIrB,WAAEK,EAAF,aAAcC,GACdz/B,KAAK29B,4BAA4Bx2B,GAEjC4e,EAAc,CAChBkO,MAAO,EAAF,KACEuL,GADF,IAED7J,SAAUkK,EAAelK,YAIjC,OAAO31B,KAAKq9B,gCACR8B,EAAkBpZ,GACjB2U,KAAKnZ,IACF,MAAM+c,EAAQ/c,GAAUA,EAAOue,YAAY,GAM3C,OAJMxB,GAASA,EAAMyB,iBACXzB,EAAMyB,iBAAiBN,GACvBzC,QAAQC,WAGbvC,KAAK,KACK,CACH2D,WAAY,SACZ9c,cAMxB,OAAOvhB,KAAKu9B,oBAAoB,CAC5BgC,wBACA/G,6BAEN1qC,KAAKkS,MAqDDggC,EAA6B,WAC/B,MACMC,GADY94B,EAAQwzB,SAAW,CAAE,QAAS,UACNjK,OAAO8I,GAClC,UAAXA,GACe,UAAXA,GAAsB1Z,IAAQ2d,iBAEtC,IAAKwC,EAAwB1pC,OACzB,OAAOymC,QAAQC,UAGnB,MAAMlX,EA99BlB,SAA2BqP,EAAK,GAAIjuB,EAAU,IAG1C,MAAM4e,EAAcma,IAAU/4B,EAAQ4e,aAAeiO,GAErD,GAAIoB,EAAGxxB,QAAQ,UAAY,EAqBvB,GApBKmiB,EAAYkO,QACblO,EAAYkO,MAAQ,IAOpBnU,IAAQyV,aACJxP,EAAYkO,MAAMC,QAAUnO,EAAYkO,MAAMC,OAAOC,MACrDpO,EAAYkO,MAAMC,OAAS,CAAEC,MAAO+L,IAAUna,EAAYkO,MAAMC,OAAOC,QAEvElW,EAAO9Y,KAAK,8DAEZ4gB,EAAYkO,MAAMsC,OAASxQ,EAAYkO,MAAMsC,MAAMpC,MACnDpO,EAAYkO,MAAMsC,MAAQ,CAAEpC,MAAO+L,IAAUna,EAAYkO,MAAMsC,MAAMpC,QAErElW,EAAO9Y,KAAK,8DAGhBgC,EAAQuuB,eACR3P,EAAYkO,MAAM0B,SAAWxuB,EAAQuuB,mBAClC,CACH,MAAMG,EAAa1uB,EAAQ0uB,YAAcC,IAAiBC,KAE1DhQ,EAAYkO,MAAM4B,WAAaA,OAGnC9P,EAAYkO,OAAQ,EAuDxB,OApDImB,EAAGxxB,QAAQ,UAAY,GAClBmiB,EAAYsP,OAAsC,kBAAtBtP,EAAYsP,QACzCtP,EAAYsP,MAAQ,IAIpBvV,IAAQsQ,aAAetQ,IAAQyV,WAC/BxP,EAAYsP,MAAQ,CAChBM,SAAUxuB,EAAQ0vB,YAClBsJ,iBAAkBxL,IAAeH,EACjCsC,kBAAmBrC,IAAeD,EAClC4L,kBAAmB1L,IAAcF,IAShCzO,EAAYsP,MAAMI,WACnB1P,EAAYsP,MAAMI,SAAW,IAEjC1P,EAAYsP,MAAMI,SAASvyB,KACvB,CAAE0yB,SAAUzuB,EAAQ0vB,aACpB,CAAEC,kBAAmBrC,IAAeD,GACpC,CAAEuC,sBAAuBtC,IAAeD,GACxC,CAAEwC,qBAAsBrC,IAAeH,GACvC,CAAEyC,sBAAuBvC,IAAcF,GACvC,CAAE0C,oBAAqBtC,IAAeJ,GACtC,CAAE2C,uBAAwBzC,IAAcF,GACxC,CAAE4C,uBAAwB3C,IAAeD,GACzC,CAAE6C,sBAAuB1C,IAAeH,MAIhDzO,EAAYsP,OAAQ,EAGpBD,EAAGxxB,QAAQ,YAAc,IACpBmiB,EAAYkO,OAAsC,kBAAtBlO,EAAYkO,QACzClO,EAAYkO,MAAQ,IAGxBlO,EAAYkO,MAAQ,CAChBuB,UAAW+B,EAAiB,EAAD,KACpBpwB,GADoB,IAEvB4jB,OAAQ,eAKbhF,EAq4BqBsa,CAChBJ,EAAyB94B,GAI7B,OAFA8W,EAAO/Y,KAAK,0BAA2B6gB,GAEhC/lB,KAAKq9B,gCACR4C,EAAyBla,IAC/Bj4B,KAAKkS,MA0CP,OAAO4/B,IACFlF,MAtGiC,SAAS1C,GAC3C,IAAKA,EACD,OAGJ,MAAM,OAAEzW,EAAF,SAAUqU,EAAV,WAAoByI,GAAerG,EAEnCsI,EAAqB/e,EAAOqX,iBAElC,GAAI0H,EAAmB/pC,OAAQ,CAC3B,MAAMgqC,EAAqB,IAAI3E,YAAY0E,GAE3CX,EAAqBz8B,KAAK,CACtBqe,OAAQgf,EACR3K,WACAyI,aACAC,MAAOiC,EAAmB3H,iBAAiB,KAInD,MAAM4H,EAAqBjf,EAAOuX,iBAElC,GAAI0H,EAAmBjqC,OAAQ,CAC3B,MAAMkqC,EAAqB,IAAI7E,YAAY4E,GAE3Cb,EAAqBz8B,KAAK,CACtBqe,OAAQkf,EACR7K,WACAyI,aACAC,MAAOmC,EAAmB3H,iBAAiB,GAC3C0F,UAAWC,IAAUC,cAyE5BhE,KAAKsF,GACLtF,MAlC6B,SAASoE,GACvC,IAAKA,EACD,OAGJ,MAAMhD,EAAcgD,EAASlG,iBAE7B,GAAIkD,EAAYvlC,OAAQ,CACpB,MAAMwnC,EAAc,IAAInC,YAAYE,GAEpC6D,EAAqBz8B,KAAK,CACtBqe,OAAQwc,EACRO,MAAOP,EAAYnF,iBAAiB,GACpC8H,QAASv5B,EAAQu5B,UAIzB,MAAM7E,EAAciD,EAAShG,iBAE7B,GAAI+C,EAAYtlC,OAAQ,CACpB,MAAMynC,EAAc,IAAIpC,YAAYC,GAEpC8D,EAAqBz8B,KAAK,CACtBqe,OAAQyc,EACRM,MAAON,EAAYlF,iBAAiB,GACpC0F,UAAWC,IAAUE,OACrB+B,QAASv5B,EAAQu5B,cASxBhG,KAAK,IAAMiF,GACXvC,MAAMp4B,IACH26B,EAAqBvwB,QAAQ,EAAGmS,aAC5BvhB,KAAKi/B,gBAAgB1d,KAGlByb,QAAQE,OAAOl4B,KAUlCk3B,wBACI,OAAOvD,QACH6B,UAAUC,cACHD,UAAUC,aAAaF,kBAUtCoG,wBAAwBC,GACpB,MAAsB,WAAfA,GAA0C,gBAAfA,GAC5B9L,EASVmK,gBAAgB4B,GACZ,IAAKA,EACD,OAGJA,EAAYf,YAAY1wB,QAAQkvB,IACxBA,EAAM1a,MACN0a,EAAM1a,SAKVid,EAAYjd,MACZid,EAAYjd,OAMZid,EAAYC,SACZD,EAAYC,UAIhB,MAAMC,EAAMF,EAAYrF,eAEpBuF,WACOF,EAAYrF,eACnBC,IAAIuF,gBAAgBD,IAQ5BE,0BACI,OAAOjF,IAAewB,cAW1B0D,qBAAqBvL,GACjB,OAAK31B,KAAK2gC,wBAAwB,UAK3B9L,EAAwBE,UAAUY,GACpC+E,KAAK,KACFpG,EAAsBqB,EACtBpB,GAAqB,EAErBtW,EAAOrZ,IAAK,8BAA6B+wB,GAEzCnW,EAAa0D,KAAK8V,IAAUrQ,4BACxBgN,KAZDqH,QAAQE,OACX,IAAI7lC,MAAM,gDAoBtB8pC,uBACI,OAAO7M,EAQX8M,oCACI,OAAOpM,EAOXqM,4BAA4B7H,GACxB,MAAMN,EAAa,GACboI,EAAa,CACf,SAAY9H,EAAO7D,SACnB,KAAQ6D,EAAOJ,KACf,MAASI,EAAOE,MAChB,QAAWF,EAAOC,SAKtB,OAFAP,EAAWh2B,KAAKo+B,GAET,CAAEpI,cAWbqI,gBAAgBxb,EAAayb,GACpBzb,EAAY0P,WACb1P,EAAY0P,SAAW,IAK3B1P,EAAY0P,SACN1P,EAAY0P,SAAS/E,OACnB/jC,IAAMA,EAAEwB,eAAe,+BAE3BqzC,GACAzb,EAAY0P,SAASvyB,KAAK,CAAEu+B,2BAA4B,UAKpE,MAAMC,EAAW,IAAIvH,EAQrB,SAASc,GAAsB0G,GAC3B,OAAO,SAASpzB,EAASgT,GAErB,MAAM0c,EAAM0D,EAAsBC,MAAMF,EAAUvgC,WA4BlD,OA1BIogB,GACOmgB,EAASf,wBAAwB,WACjCpf,EAAOqX,gBACPrX,EAAOqX,iBAAiBriC,QAGxBg+B,GACPhmB,EAAQwmB,UAAU2M,EAASP,wBACtB/D,OAAM,SAASyE,GACZ,MAAM1xB,EACA,IAAIyV,IAAgBic,EAAI,KAAM,CAAE,gBAEtCjK,IAAqBkK,8BAA8B,CAC/CC,QAAS/hC,KACTwP,OAAQW,IAGZ8N,EAAO9Y,KACH,sGAGAoJ,EACA4B,MAIT8tB,GAIAyD,Q,oDCjmDf,MAAMv4B,EAAW,GAGX64B,EAAoB91C,OAAOwvB,QAYjC,MAAMumB,EAA0B/1C,OAAOg2C,qBAYvCh2C,OAAOwvB,QAlBP,YAAoCymB,GAChCh5B,EAASiG,QAAQ7K,GAAWA,KAAW49B,IACvCH,GAAqBA,KAAqBG,IAiB9Cj2C,OAAOg2C,qBAPP,SAAuC7a,GACnCle,EAASiG,QAAQ7K,GAAWA,EAAQ,KAAM,KAAM,KAAM,KAAM8iB,EAAM7X,SAClEyyB,GAA2BA,EAAwB5a,IAOvD,MAAMuQ,EAAuB,CAKzB7pB,WAAWxJ,GACP4E,EAASjG,KAAKqB,IAOlBszB,iBAAiB7yB,GACb,MAAMo9B,EAAal2C,OAAOwvB,QAErB0mB,GAGLA,EAAW,KAAM,KAAM,KAAM,KAAMp9B,IAOvC88B,8BAA8B98B,GAC1B,MAAMo9B,EAAal2C,OAAOg2C,qBAErBE,GAGLA,EAAWp9B,KAKnBjZ,EAAOD,QAAU8rC,G,84BC1DjB,MAAM3Z,EAASF,oBAAUM,GAMzB,IAAIgkB,EAA0B,EAO1BC,EAAoB,EA+CxB,SAASC,EAAsBC,EAAsB,IACjD,OAAOA,EAAoBh9B,IAAIi9B,IAC3B,MAAM,SACF7M,EADE,WAEFyI,EAFE,OAGF9c,EAHE,MAIF+c,EAJE,UAKFE,EALE,QAMFkC,GACA+B,GAEE,SAAE9M,EAAF,WAAYE,GAAeyI,EAAMoE,cAOvC,OAFAJ,EAAoBK,YAAqBL,GAElC,IAAIM,IAAgB,CACvBjN,WACAE,aACAlK,UAAW2S,EAAMlF,KACjByJ,MAAOP,EACP1M,WACAyI,aACA9c,SACA+c,QACAE,UAAWA,GAAa,KACxBkC,cAQG,MAAMoC,UAAY1I,IAM7BnkC,YAAYssB,EAAYpb,EAAU,IAC9BkzB,QACAr6B,KAAKuiB,WAAaA,EAMlBviB,KAAK+iC,gBAAkB,IAAIxjB,IAE3Bvf,KAAKgjC,YAAc,GAEnBhjC,KAAKmH,QAAUA,EAKfnH,KAAKijC,SAAW,KAUhBjjC,KAAKkjC,QAAU,EAQfljC,KAAKmjC,gBAAkB,KAKvBnjC,KAAKojC,wBAA0B,GAS/BpjC,KAAKqjC,qBAAkB9iC,EAQvBP,KAAKsjC,gBAAkB,KAQvBtjC,KAAKujC,mBAAqB,GAG1BvjC,KAAKwjC,qBAAuBxjC,KAAKyjC,gBAAgB31C,KAAKkS,MAEtDA,KAAK0jC,qBAAuB1jC,KAAK0jC,qBAAqB51C,KAAKkS,MAC3DA,KAAK2jC,iCACC3jC,KAAK2jC,iCAAiC71C,KAAKkS,MAI7Cm6B,IAASwG,wBAAwB,YACjCxG,IAASyJ,YACL5K,IAAUrQ,4BACV3oB,KAAK2jC,kCAGTxJ,IAASyJ,YACL5K,IAAUpQ,oBACV5oB,KAAK0jC,uBAUjBG,UACI1J,IAASrY,eACLkX,IAAUrQ,4BACV3oB,KAAK2jC,kCAGTxJ,IAASrY,eACLkX,IAAUpQ,oBACV5oB,KAAK0jC,sBAGT1jC,KAAK8hB,eACDkX,IAAUlR,uBACV9nB,KAAKwjC,sBAGLxjC,KAAK8jC,sBACL9jC,KAAK8hB,eACDkX,IAAUpR,kBACV5nB,KAAK8jC,sBAYjB,4BAA4BC,GACxB,OAAOxB,EAAsBwB,GAYjC,sCAAsC58B,GAClC,MAAM0zB,EAAiB/a,IAAQ+a,iBAK/B,OAJ2BA,EACrBV,IAASuF,kCAAkCv4B,GAC3CgzB,IAASyD,+BAA+Bz2B,IAEpBuzB,KAAKqJ,GACvBlJ,EACO0H,EAAsBwB,GApO7C,SAA2BA,EAAY58B,GACnC,MAAM68B,EAAY,GAClB,IAAIrO,EAAW,KAoBf,OAlBAoO,EAAW30B,QAAQ60B,IACXA,EAAUtY,YAAc4S,IACxB5I,EAAWxuB,EAAQ0vB,YACZoN,EAAUzF,YAAcC,IAAUE,SACzChJ,EAAWxuB,EAAQuuB,gBAEvB4M,EAAoBK,YAAqBL,GACzC,MAAM4B,EAAa,IAAItB,IAAJ,OACZqB,GADY,IAEftO,WACAE,WAAY1uB,EAAQ0uB,WACpBgN,MAAOP,EACP5B,QAASv5B,EAAQu5B,WAGrBsD,EAAU9gC,KAAKghC,KAGZF,EAiNQG,CAAkBJ,EAAY58B,IAY7Ci9B,wBAAwBpjB,EAAgBqjB,GACpCrkC,KAAKijC,SAAW,IAAIqB,IAChBtjB,EAAgBqjB,EAAOrkC,KAAKwf,aAAcxf,KAAKukC,+BAA+Bz2C,KAAKkS,OAEvFA,KAAK8jC,qBAAuB,KAKxB,IACI9jC,KAAKijC,SAASuB,0BACVxkC,KAAKsjC,iBACTtjC,KAAKijC,SAASwB,6BACVzkC,KAAKujC,yBAE2B,IAAzBvjC,KAAKqjC,iBACZrjC,KAAKijC,SAASyB,mCACV1kC,KAAKqjC,iBAEf,MAAOr+B,GACL4yB,IAAqBC,iBAAiB7yB,GACtCiZ,EAAOjZ,MACF,wBAAuBhF,KAAK2kC,4BACjB3kC,KAAKsjC,+BACAtjC,KAAKqjC,oCACtBr+B,GAGRhF,KAAK8hB,eAAekX,IAAUpR,kBAC1B5nB,KAAK8jC,sBACT9jC,KAAK8jC,qBAAuB,MAMP,IAAjB9jC,KAAKkjC,QACLljC,KAAKijC,SAAS2B,oBAAoB5kC,KAAKkjC,SAI/CljC,KAAK4jC,YAAY5K,IAAUpR,kBACvB5nB,KAAK8jC,sBAGT9jC,KAAK4jC,YAAY5K,IAAUlR,uBACvB9nB,KAAKwjC,sBAWbE,uBACI1jC,KAAK2jC,iCAAiCxJ,IAASgH,wBASnDoD,+BAA+BM,GAC3B5mB,EAAO/Y,KAAM,uCAAsC2/B,2BACnD7kC,KAAKojC,wBAA0ByB,EAC/B7kC,KAAKwf,aAAa0D,KAAK8V,IAAUhR,kCAQrCyb,gBAAgBqB,EAAiB,IAC7B,MAAMC,EAAoB/kC,KAAKmjC,iBAAmB,GAClD,IAAI6B,EAAwB,GACxBC,EAAyB,GAE7BjlC,KAAKmjC,gBAAkB2B,EAEvBE,EAAwBD,EAAkBrU,OACtCxpB,IAAOlH,KAAKklC,UAAUh+B,IAE1B+9B,EAAyBH,EAAepU,OACpCxpB,IAAyC,IAAnC69B,EAAkBnhC,QAAQsD,IAEpClH,KAAKuiB,WAAW/C,aAAa0D,KACzBP,2BACAqiB,EACAC,GAORE,cACQnlC,KAAKijC,WAODjjC,KAAKijC,UAAmC,cAAvBjjC,KAAKijC,SAASx1C,MAC/BuS,KAAKijC,SAASxmB,QAGlBzc,KAAKijC,SAAW,MAaxBmC,2BAA2BC,GACvBrlC,KAAKqjC,gBAAkBgC,EAEnBrlC,KAAKijC,UAAYjjC,KAAKijC,SAASqC,UAC/BtlC,KAAKijC,SAASyB,mCAAmCW,GAezDE,gBAAgBC,GACZxlC,KAAKujC,mBAAqBiC,EAEtBxlC,KAAKijC,UAAYjjC,KAAKijC,SAASqC,UAC/BtlC,KAAKijC,SAASwB,6BAA6Be,GAYnDC,YAAYv+B,GAERlH,KAAKsjC,gBAAkBp8B,EACnBlH,KAAKijC,UAAYjjC,KAAKijC,SAASqC,UAC/BtlC,KAAKijC,SAASuB,0BAA0Bt9B,GAShD,mBAAmBw+B,EAAWhkB,GAC1ByY,IAASyJ,YAAY8B,EAAWhkB,GAQpC,sBAAsBgkB,EAAWhkB,GAC7ByY,IAASrY,eAAe4jB,EAAWhkB,GAOvC,YAAYva,EAAU,IAGlB,OAFAnH,KAAKmH,QAAUA,EAERgzB,IAASvvB,KAAK5K,KAAKmH,SA4B9Bw+B,qBAAqBC,EAAWC,EAAWC,EAAO3+B,GAC9C,MAAMo1B,EAAgBuG,EAAIiD,iBAAiBD,QAED,IAA/B3+B,EAAQ6+B,qBACf7L,IAASoH,gBAAgBhF,EAAep1B,EAAQ6+B,oBAEhD5mB,IAAWyB,UAAUolB,uBACjB,CAAED,mBAAoB7+B,EAAQ6+B,sBAKlC7+B,EAAQ++B,0BACRjoB,EAAOhZ,MAAM,iDACb4gC,EAAUM,0BAA2B,EACrCN,EAAUO,oCAAqC,EAC/CP,EAAUQ,oCAAqC,GAG/CvmB,IAAQwmB,yBACRT,EAAUU,aAAe,UAM7BV,EAAUW,aAAe,aAEzBnE,EAA0BM,YAAqBN,GAE/C,MAAMoE,EACA,IAAIC,IACF1mC,KACAqiC,EACAuD,EACAC,EAAWtJ,EACXuJ,EAAO3+B,GAIf,OAFAnH,KAAK+iC,gBAAgB3hB,IAAIqlB,EAAcv/B,GAAIu/B,GAEpCA,EAYXE,sBAAsBC,GAClB,MAAM1/B,EAAK0/B,EAAwB1/B,GAEnC,QAAIlH,KAAK+iC,gBAAgBhf,IAAI7c,KAEzBlH,KAAK+iC,gBAAgBrf,OAAOxc,IAErB,GAWf2/B,cAAcvI,GACV,IAAKA,EACD,MAAM,IAAIjnC,MAAM,wCAGpB2I,KAAKgjC,YAAY9/B,KAAKo7B,GAEtBA,EAAM/b,WAAaviB,KAAKuiB,WAQ5BukB,WACI,OAAO9mC,KAAKkjC,OAMhB6D,4BACI,OAAO/mC,KAAKojC,wBAOhB4D,qBACI,MAAMC,EAAajnC,KAAKknC,eAAe3I,KAGvC,OAAO0I,EAAW1wC,OAAS0wC,EAAW,QAAK1mC,EAO/C4mC,qBACI,MAAMC,EAAapnC,KAAKknC,eAAe3I,KAGvC,OAAO6I,EAAW7wC,OAAS6wC,EAAW,QAAK7mC,EAS/C2mC,eAAevb,GACX,IAAI0b,EAASrnC,KAAKgjC,YAAY9I,QAO9B,YALkB35B,IAAdorB,IACA0b,EAASA,EAAO3W,OACZ4N,GAASA,EAAMgJ,YAAc3b,IAG9B0b,EASXE,gBAAgB5b,GACZ,IAAI6b,EAAe,GAEnB,IAAK,MAAMlkB,KAAOtjB,KAAK+iC,gBAAgB3f,SAAU,CAC7C,MAAMqkB,EAAiBnkB,EAAIikB,qBAAgBhnC,EAAWorB,GAElD8b,IACAD,EAAeA,EAAaxtC,OAAOytC,IAI3C,OAAOD,EAQXE,aAAan6C,GACT,MAAMo6C,EAAe,GASrB,OAPA3nC,KAAKknC,eAAe3I,KAAiBnvB,QAAQw4B,IAEzCD,EAAazkC,KAAK3V,EAAQq6C,EAAWC,OAASD,EAAWE,YAKtD9K,QAAQ+K,IAAIJ,GAOvBK,iBAAiB1J,GACb,MAAM2J,EAAMjoC,KAAKgjC,YAAYp/B,QAAQ06B,IAExB,IAAT2J,GAIJjoC,KAAKgjC,YAAY/+B,OAAOgkC,EAAK,GAUjCC,mBAAmBC,GACf,IAAIC,EAAgB,GAEpB,IAAK,MAAM9kB,KAAOtjB,KAAK+iC,gBAAgB3f,SAAU,CAC7C,MAAMilB,EAAkB/kB,EAAI4kB,mBAAmBC,GAE/CC,EAAgBA,EAAcpuC,OAAOquC,GAOzC,OAJApqB,EAAOhZ,MACF,6BAA4BkjC,EACtB,WAAUC,EAAc7xC,QAE5B6xC,EAMX,wBAAwBtC,GACpB,MAAMvJ,EACAuJ,EAAQ3L,IAAS4C,iBAAmB5C,IAASoC,cAEnD,OAAKA,EAIE3vB,KAAKsL,MAAMtL,KAAKwL,UAAUmkB,IAHtB,GAWf,yBAAyB+L,EAAY/mB,GACjC,OAAO4Y,IAASa,kBAAkBsN,EAAY/mB,GAOlD,mBAAmBA,GACf,OAAO4Y,IAASgB,YAAY5Z,GAOhC,kBAAkB+c,GACd,OAAOnE,IAASiB,WAAWkD,GAO/B,+BACI,OAAOnE,IAAS+B,wBAUpB,+BAA+B0E,GAC3B,OAAOzG,IAASwG,wBAAwBC,GAW5C,2BACI,OAAO9gB,IAAQ0d,cAQnB,8BACI,OAAOrD,IAASgH,uBAQpB,2CACI,OAAOhH,IAASiH,oCAOpB,mCAAmC5H,GAC/B,OAAOW,IAASkH,4BAA4B7H,GAUhD,4BAA4B7D,GACxB,OAAOwE,IAAS+G,qBAAqBvL,GAezC,oBAAoBpU,GAChB,OAAOuhB,EAAIyF,iBAAiBpO,IAASgB,YAAY5Z,IAerD,wBAAwBinB,GACpB,OAAOA,GAAyB,iBAAbA,GACC,YAAbA,EAQX,wBAAwBh9B,GACpB2uB,IAASI,iBAAiB/uB,GAQ9B,uBAAuBq1B,GACnB1G,IAAS8E,gBAAgB4B,GAO7B,iCACI,OAAO1G,IAAS8G,0BAMpBwH,qBACQzoC,KAAKijC,WACLjjC,KAAKijC,SAASxmB,QACdzc,KAAKijC,SAAW,KAEhBjjC,KAAK8hB,eAAekX,IAAUlR,uBAC1B9nB,KAAKwjC,uBAYjBkF,cAAcplB,EAAK0B,EAAM2jB,EAAYpjB,GACjC,MAAM+Y,EAAQhb,EAAIslB,eAAe5jB,GAE5BsZ,IAEOA,EAAMuK,gBAIPvK,EAAM/Y,YAAcA,GAC3BtH,EAAOjZ,MACF,GAAEs5B,qBAAyB/Y,EAAU,KAAO,kBAGrD+Y,EAAMoK,cAAcC,EAAYrlB,IAR5BrF,EAAO9Y,KAAM,6CAA4C6f,IAqBjE8jB,mBAAmBC,EAAItW,GACnB,IAAIzyB,KAAKijC,SAGL,MAAM,IAAI5rC,MAAM,gCAFhB2I,KAAKijC,SAAS+F,YAAYD,EAAItW,GAYtCwW,SAAS17C,GACDyS,KAAKkjC,SAAW31C,IAChByS,KAAKkjC,OAAS31C,EACVyS,KAAKijC,UAAYjjC,KAAKijC,SAASqC,UAC/BtlC,KAAKijC,SAAS2B,oBAAoBr3C,GAEtCyS,KAAKwf,aAAa0D,KAAK8V,IAAU/Q,oBAAqB16B,IAU9D23C,UAAUh+B,GACN,OAAQlH,KAAKmjC,iBACNnjC,KAAKmjC,gBAAgBv/B,QAAQsD,IAAO,EAW/Cy8B,iCAAiChO,GAC7B,MAAMuT,EAAoBlpC,KAAKunC,gBAAgBhJ,KAE/C,IAAK,MAAMD,KAAS4K,EAChB5K,EAAM6K,eAAexT,O,+CC/7BjC5pC,EAAOD,QAZW,CAId6yC,OAAQ,SAKRD,QAAS,Y,6BCdb,olBAQO,MAAM0K,EAAoB,wBAMpBC,EACP,oCAMOC,EACP,wCAKOC,EAAU,cAKVC,EAAY,gBAMZC,EAAoB,wBAKpBC,EACP,kCAMOC,EACP,kCAMOC,EAAoB,0BAKpBC,EAAwB,wBAMxBC,EAAyB,8B,6BCpEtC,8XAGO,MAAMC,EAAsB,gBActB/0C,EAA4B,2BAK5Bg1C,EAA6B,2BAK7B/0C,EAAqB,yBAKrBg1C,EAA0B,yBAM1BC,EAAsB,4BAMtB51C,EAAiB,wB,6BC5C9B,o6BAOO,MAAM9F,EAA0B,oCAK1B27C,EAAa,uBAKbC,EAAuB,uBAKvBC,EAAuB,uBAKvBC,EAAmB,6BAMnBC,EAAoB,wCAMpBC,EAAqB,yCAMrBC,EAA2B,0CAK3Bh7C,EAAqB,+BAKrBC,EAAa,uBAKbC,EAAoB,8BAKpB+6C,EAAa,uBAMbC,EACP,0CAKOC,EAAsB,+BAKtBC,EAAyB,kCAKzBx5C,EAAoB,8BAKpBS,EAAoB,8BAKpBg5C,EAA4B,sC,6BC1EzC,IAOIC,EAPAC,EAAuB,iBAAZC,QAAuBA,QAAU,KAC5CC,EAAeF,GAAwB,mBAAZA,EAAEpJ,MAC7BoJ,EAAEpJ,MACF,SAAsBuJ,EAAQC,EAAUjJ,GACxC,OAAOkJ,SAASn9C,UAAU0zC,MAAMn1C,KAAK0+C,EAAQC,EAAUjJ,IAKzD4I,EADEC,GAA0B,mBAAdA,EAAEM,QACCN,EAAEM,QACVt+C,OAAOu+C,sBACC,SAAwBJ,GACvC,OAAOn+C,OAAOw+C,oBAAoBL,GAC/BnxC,OAAOhN,OAAOu+C,sBAAsBJ,KAGxB,SAAwBA,GACvC,OAAOn+C,OAAOw+C,oBAAoBL,IAQtC,IAAIM,EAAclyB,OAAOG,OAAS,SAAqBnsB,GACrD,OAAOA,GAAUA,GAGnB,SAASkyB,IACPA,EAAa7U,KAAKne,KAAKuT,MAEzBjU,EAAOD,QAAU2zB,EACjB1zB,EAAOD,QAAQ4/C,KAwYf,SAAcC,EAAS9+C,GACrB,OAAO,IAAImwC,SAAQ,SAAUC,EAASC,GACpC,SAAS0O,SACerrC,IAAlBsrC,GACFF,EAAQ7pB,eAAe,QAAS+pB,GAElC5O,EAAQ,GAAG/C,MAAMztC,KAAK0U,YAExB,IAAI0qC,EAQS,UAATh/C,IACFg/C,EAAgB,SAAuB17B,GACrCw7B,EAAQ7pB,eAAej1B,EAAM++C,GAC7B1O,EAAO/sB,IAGTw7B,EAAQD,KAAK,QAASG,IAGxBF,EAAQD,KAAK7+C,EAAM++C,OA9ZvBnsB,EAAaA,aAAeA,EAE5BA,EAAavxB,UAAU49C,aAAUvrC,EACjCkf,EAAavxB,UAAU69C,aAAe,EACtCtsB,EAAavxB,UAAU89C,mBAAgBzrC,EAIvC,IAAI0rC,EAAsB,GAE1B,SAASC,EAAcxqB,GACrB,GAAwB,mBAAbA,EACT,MAAM,IAAI7qB,UAAU,0EAA4E6qB,GAsCpG,SAASyqB,EAAiBC,GACxB,YAA2B7rC,IAAvB6rC,EAAKJ,cACAvsB,EAAawsB,oBACfG,EAAKJ,cAmDd,SAASK,EAAalB,EAAQlkC,EAAMya,EAAU4qB,GAC5C,IAAI5/C,EACA6/C,EACAC,EA1HsBC,EAgJ1B,GApBAP,EAAcxqB,QAGCnhB,KADfgsC,EAASpB,EAAOW,UAEdS,EAASpB,EAAOW,QAAU9+C,OAAOY,OAAO,MACxCu9C,EAAOY,aAAe,SAIKxrC,IAAvBgsC,EAAOG,cACTvB,EAAOjoB,KAAK,cAAejc,EACfya,EAASA,SAAWA,EAASA,SAAWA,GAIpD6qB,EAASpB,EAAOW,SAElBU,EAAWD,EAAOtlC,SAGH1G,IAAbisC,EAEFA,EAAWD,EAAOtlC,GAAQya,IACxBypB,EAAOY,kBAeT,GAbwB,mBAAbS,EAETA,EAAWD,EAAOtlC,GAChBqlC,EAAU,CAAC5qB,EAAU8qB,GAAY,CAACA,EAAU9qB,GAErC4qB,EACTE,EAAS1Z,QAAQpR,GAEjB8qB,EAAStpC,KAAKwe,IAIhBh1B,EAAIy/C,EAAiBhB,IACb,GAAKqB,EAASj2C,OAAS7J,IAAM8/C,EAASG,OAAQ,CACpDH,EAASG,QAAS,EAGlB,IAAIvzC,EAAI,IAAI/B,MAAM,+CACEm1C,EAASj2C,OAAS,IAAMuC,OAAOmO,GADjC,qEAIlB7N,EAAEvM,KAAO,8BACTuM,EAAEuyC,QAAUR,EACZ/xC,EAAE6N,KAAOA,EACT7N,EAAEwzC,MAAQJ,EAASj2C,OA7KGk2C,EA8KHrzC,EA7KnB2L,SAAWA,QAAQI,MAAMJ,QAAQI,KAAKsnC,GAiL1C,OAAOtB,EAcT,SAAS0B,IACP,IAAK7sC,KAAK8sC,MAGR,OAFA9sC,KAAKmrC,OAAOrpB,eAAe9hB,KAAKiH,KAAMjH,KAAK+sC,QAC3C/sC,KAAK8sC,OAAQ,EACY,IAArB3rC,UAAU5K,OACLyJ,KAAK0hB,SAASj1B,KAAKuT,KAAKmrC,QAC1BnrC,KAAK0hB,SAASkgB,MAAM5hC,KAAKmrC,OAAQhqC,WAI5C,SAAS6rC,EAAU7B,EAAQlkC,EAAMya,GAC/B,IAAIurB,EAAQ,CAAEH,OAAO,EAAOC,YAAQxsC,EAAW4qC,OAAQA,EAAQlkC,KAAMA,EAAMya,SAAUA,GACjFwrB,EAAUL,EAAY/+C,KAAKm/C,GAG/B,OAFAC,EAAQxrB,SAAWA,EACnBurB,EAAMF,OAASG,EACRA,EA0HT,SAASC,EAAWhC,EAAQlkC,EAAMmmC,GAChC,IAAIb,EAASpB,EAAOW,QAEpB,QAAevrC,IAAXgsC,EACF,MAAO,GAET,IAAIc,EAAad,EAAOtlC,GACxB,YAAmB1G,IAAf8sC,EACK,GAEiB,mBAAfA,EACFD,EAAS,CAACC,EAAW3rB,UAAY2rB,GAAc,CAACA,GAElDD,EAsDT,SAAyBj3C,GAEvB,IADA,IAAIm3C,EAAM,IAAIl3C,MAAMD,EAAII,QACfjK,EAAI,EAAGA,EAAIghD,EAAI/2C,SAAUjK,EAChCghD,EAAIhhD,GAAK6J,EAAI7J,GAAGo1B,UAAYvrB,EAAI7J,GAElC,OAAOghD,EA1DLC,CAAgBF,GAAcG,EAAWH,EAAYA,EAAW92C,QAoBpE,SAASk3C,EAAcxmC,GACrB,IAAIslC,EAASvsC,KAAK8rC,QAElB,QAAevrC,IAAXgsC,EAAsB,CACxB,IAAIc,EAAad,EAAOtlC,GAExB,GAA0B,mBAAfomC,EACT,OAAO,EACF,QAAmB9sC,IAAf8sC,EACT,OAAOA,EAAW92C,OAItB,OAAO,EAOT,SAASi3C,EAAWr3C,EAAKpI,GAEvB,IADA,IAAI2/C,EAAO,IAAIt3C,MAAMrI,GACZzB,EAAI,EAAGA,EAAIyB,IAAKzB,EACvBohD,EAAKphD,GAAK6J,EAAI7J,GAChB,OAAOohD,EApWT1gD,OAAOC,eAAewyB,EAAc,sBAAuB,CACzDvyB,YAAY,EACZC,IAAK,WACH,OAAO8+C,GAET7qB,IAAK,SAAShgB,GACZ,GAAmB,iBAARA,GAAoBA,EAAM,GAAKqqC,EAAYrqC,GACpD,MAAM,IAAIusC,WAAW,kGAAoGvsC,EAAM,KAEjI6qC,EAAsB7qC,KAI1Bqe,EAAa7U,KAAO,gBAEGrK,IAAjBP,KAAK8rC,SACL9rC,KAAK8rC,UAAY9+C,OAAO4gD,eAAe5tC,MAAM8rC,UAC/C9rC,KAAK8rC,QAAU9+C,OAAOY,OAAO,MAC7BoS,KAAK+rC,aAAe,GAGtB/rC,KAAKgsC,cAAgBhsC,KAAKgsC,oBAAiBzrC,GAK7Ckf,EAAavxB,UAAU2/C,gBAAkB,SAAyB9/C,GAChE,GAAiB,iBAANA,GAAkBA,EAAI,GAAK09C,EAAY19C,GAChD,MAAM,IAAI4/C,WAAW,gFAAkF5/C,EAAI,KAG7G,OADAiS,KAAKgsC,cAAgBj+C,EACdiS,MASTyf,EAAavxB,UAAU4/C,gBAAkB,WACvC,OAAO3B,EAAiBnsC,OAG1Byf,EAAavxB,UAAUg1B,KAAO,SAAcjc,GAE1C,IADA,IAAIk7B,EAAO,GACF71C,EAAI,EAAGA,EAAI6U,UAAU5K,OAAQjK,IAAK61C,EAAKj/B,KAAK/B,UAAU7U,IAC/D,IAAIyhD,EAAoB,UAAT9mC,EAEXslC,EAASvsC,KAAK8rC,QAClB,QAAevrC,IAAXgsC,EACFwB,EAAWA,QAA4BxtC,IAAjBgsC,EAAOvnC,WAC1B,IAAK+oC,EACR,OAAO,EAGT,GAAIA,EAAS,CACX,IAAIC,EAGJ,GAFI7L,EAAK5rC,OAAS,IAChBy3C,EAAK7L,EAAK,IACR6L,aAAc32C,MAGhB,MAAM22C,EAGR,IAAI79B,EAAM,IAAI9Y,MAAM,oBAAsB22C,EAAK,KAAOA,EAAGvpC,QAAU,IAAM,KAEzE,MADA0L,EAAI89B,QAAUD,EACR79B,EAGR,IAAI5L,EAAUgoC,EAAOtlC,GAErB,QAAgB1G,IAAZgE,EACF,OAAO,EAET,GAAuB,mBAAZA,EACT2mC,EAAa3mC,EAASvE,KAAMmiC,OAE5B,KAAIhqC,EAAMoM,EAAQhO,OACd23C,EAAYV,EAAWjpC,EAASpM,GACpC,IAAS7L,EAAI,EAAGA,EAAI6L,IAAO7L,EACzB4+C,EAAagD,EAAU5hD,GAAI0T,KAAMmiC,GAGrC,OAAO,GAiET1iB,EAAavxB,UAAU01C,YAAc,SAAqB38B,EAAMya,GAC9D,OAAO2qB,EAAarsC,KAAMiH,EAAMya,GAAU,IAG5CjC,EAAavxB,UAAUyzB,GAAKlC,EAAavxB,UAAU01C,YAEnDnkB,EAAavxB,UAAUigD,gBACnB,SAAyBlnC,EAAMya,GAC7B,OAAO2qB,EAAarsC,KAAMiH,EAAMya,GAAU,IAqBhDjC,EAAavxB,UAAUw9C,KAAO,SAAczkC,EAAMya,GAGhD,OAFAwqB,EAAcxqB,GACd1hB,KAAK2hB,GAAG1a,EAAM+lC,EAAUhtC,KAAMiH,EAAMya,IAC7B1hB,MAGTyf,EAAavxB,UAAUkgD,oBACnB,SAA6BnnC,EAAMya,GAGjC,OAFAwqB,EAAcxqB,GACd1hB,KAAKmuC,gBAAgBlnC,EAAM+lC,EAAUhtC,KAAMiH,EAAMya,IAC1C1hB,MAIbyf,EAAavxB,UAAU4zB,eACnB,SAAwB7a,EAAMya,GAC5B,IAAI2sB,EAAM9B,EAAQ+B,EAAUhiD,EAAGiiD,EAK/B,GAHArC,EAAcxqB,QAGCnhB,KADfgsC,EAASvsC,KAAK8rC,SAEZ,OAAO9rC,KAGT,QAAaO,KADb8tC,EAAO9B,EAAOtlC,IAEZ,OAAOjH,KAET,GAAIquC,IAAS3sB,GAAY2sB,EAAK3sB,WAAaA,EACb,KAAtB1hB,KAAK+rC,aACT/rC,KAAK8rC,QAAU9+C,OAAOY,OAAO,cAEtB2+C,EAAOtlC,GACVslC,EAAOzqB,gBACT9hB,KAAKkjB,KAAK,iBAAkBjc,EAAMonC,EAAK3sB,UAAYA,SAElD,GAAoB,mBAAT2sB,EAAqB,CAGrC,IAFAC,GAAY,EAEPhiD,EAAI+hD,EAAK93C,OAAS,EAAGjK,GAAK,EAAGA,IAChC,GAAI+hD,EAAK/hD,KAAOo1B,GAAY2sB,EAAK/hD,GAAGo1B,WAAaA,EAAU,CACzD6sB,EAAmBF,EAAK/hD,GAAGo1B,SAC3B4sB,EAAWhiD,EACX,MAIJ,GAAIgiD,EAAW,EACb,OAAOtuC,KAEQ,IAAbsuC,EACFD,EAAK3gB,QAiIf,SAAmB2gB,EAAM1d,GACvB,KAAOA,EAAQ,EAAI0d,EAAK93C,OAAQo6B,IAC9B0d,EAAK1d,GAAS0d,EAAK1d,EAAQ,GAC7B0d,EAAK39B,MAlIG89B,CAAUH,EAAMC,GAGE,IAAhBD,EAAK93C,SACPg2C,EAAOtlC,GAAQonC,EAAK,SAEQ9tC,IAA1BgsC,EAAOzqB,gBACT9hB,KAAKkjB,KAAK,iBAAkBjc,EAAMsnC,GAAoB7sB,GAG1D,OAAO1hB,MAGbyf,EAAavxB,UAAUugD,IAAMhvB,EAAavxB,UAAU4zB,eAEpDrC,EAAavxB,UAAUu1B,mBACnB,SAA4Bxc,GAC1B,IAAIinC,EAAW3B,EAAQjgD,EAGvB,QAAeiU,KADfgsC,EAASvsC,KAAK8rC,SAEZ,OAAO9rC,KAGT,QAA8BO,IAA1BgsC,EAAOzqB,eAUT,OATyB,IAArB3gB,UAAU5K,QACZyJ,KAAK8rC,QAAU9+C,OAAOY,OAAO,MAC7BoS,KAAK+rC,aAAe,QACMxrC,IAAjBgsC,EAAOtlC,KACY,KAAtBjH,KAAK+rC,aACT/rC,KAAK8rC,QAAU9+C,OAAOY,OAAO,aAEtB2+C,EAAOtlC,IAEXjH,KAIT,GAAyB,IAArBmB,UAAU5K,OAAc,CAC1B,IACI1I,EADA0X,EAAOvY,OAAOuY,KAAKgnC,GAEvB,IAAKjgD,EAAI,EAAGA,EAAIiZ,EAAKhP,SAAUjK,EAEjB,oBADZuB,EAAM0X,EAAKjZ,KAEX0T,KAAKyjB,mBAAmB51B,GAK1B,OAHAmS,KAAKyjB,mBAAmB,kBACxBzjB,KAAK8rC,QAAU9+C,OAAOY,OAAO,MAC7BoS,KAAK+rC,aAAe,EACb/rC,KAKT,GAAyB,mBAFzBkuC,EAAY3B,EAAOtlC,IAGjBjH,KAAK8hB,eAAe7a,EAAMinC,QACrB,QAAkB3tC,IAAd2tC,EAET,IAAK5hD,EAAI4hD,EAAU33C,OAAS,EAAGjK,GAAK,EAAGA,IACrC0T,KAAK8hB,eAAe7a,EAAMinC,EAAU5hD,IAIxC,OAAO0T,MAoBbyf,EAAavxB,UAAUggD,UAAY,SAAmBjnC,GACpD,OAAOkmC,EAAWntC,KAAMiH,GAAM,IAGhCwY,EAAavxB,UAAUwgD,aAAe,SAAsBznC,GAC1D,OAAOkmC,EAAWntC,KAAMiH,GAAM,IAGhCwY,EAAaguB,cAAgB,SAAS9B,EAAS1kC,GAC7C,MAAqC,mBAA1B0kC,EAAQ8B,cACV9B,EAAQ8B,cAAcxmC,GAEtBwmC,EAAchhD,KAAKk/C,EAAS1kC,IAIvCwY,EAAavxB,UAAUu/C,cAAgBA,EAiBvChuB,EAAavxB,UAAUygD,WAAa,WAClC,OAAO3uC,KAAK+rC,aAAe,EAAIhB,EAAe/qC,KAAK8rC,SAAW,K,6BCxahE,YAEA,MAAM8C,EAA6B,GA2CnC,SAAShpB,EAAgB5gB,EAAOmC,EAASwzB,GACrC,GAAqB,iBAAV31B,QAA4C,IAAfA,EAAMnY,KAkB1C,OARAmT,KAAK6lB,IAAM,CACP7gB,QACA+gB,YAAa5e,EACbwzB,QAASA,GAAWvkC,MAAMC,QAAQskC,GAC5BA,EAAQT,MAAM,QACd35B,GAGFyE,EAAMnY,MACd,IAAK,kBACL,IAAK,wBACL,IAAK,gBACDmT,KAAKnT,KAAOgiD,oBACZ7uC,KAAKyE,QACCmqC,EAA2B5uC,KAAKnT,OAC3BmT,KAAK6lB,IAAI8U,SAAW,IAAIx3B,KAAK,MACxC,MACJ,IAAK,uBACL,IAAK,gBACDnD,KAAKnT,KAAOgiD,YACZ7uC,KAAKyE,QACCmqC,EAA2B5uC,KAAKnT,OAC3BmT,KAAK6lB,IAAI8U,SAAW,IAAIx3B,KAAK,MACxC,MACJ,IAAK,8BACL,IAAK,uBAAwB,CACzB,MAAM2iB,EAAiB9gB,EAAM8gB,gBAAkB9gB,EAAM8pC,WAKjD3nC,GACOA,EAAQ8sB,SACN0G,GAAWA,EAAQ/2B,QAAQ,UAAY,KACrB,aAAnBkiB,GACsB,aAAnBA,GACmB,cAAnBA,GACmB,cAAnBA,GACmB,UAAnBA,GACmB,WAAnBA,GACmB,aAAnBA,IACX9lB,KAAKnT,KAAOgiD,yBACZ7uC,KAAKyE,QACCmqC,EAA2B5uC,KAAKnT,MA6CtD,SAA2CkiD,EAAsBhpB,GAC7D,GAAIA,GAAeA,EAAYkO,OAASlO,EAAYkO,MAAMuB,UACtD,OAAQuZ,GACR,IAAK,QACD,OAAOhpB,EAAYkO,MAAMuB,UAAUgB,SACvC,IAAK,SACD,OAAOzQ,EAAYkO,MAAMuB,UAAUiB,UACvC,QACI,OAAO1Q,EAAYkO,MAAMuB,UAAUuZ,IAAyB,GAIpE,MAAO,GAxDeC,CACElpB,EACA3e,KAEZnH,KAAKnT,KAAOgiD,oBACZ7uC,KAAKyE,QACCmqC,EAA2B5uC,KAAKnT,MAC5BmY,EAAM8gB,gBAEpB,MAGJ,QACI9lB,KAAKnT,KAAOgiD,UACZ7uC,KAAKyE,QACCO,EAAMP,SAAWmqC,EAA2B5uC,KAAKnT,UAGxD,IAAqB,iBAAVmY,EAWd,MAAM,IAAI3N,MAAM,qBAVZu3C,EAA2B5pC,IAC3BhF,KAAKnT,KAAOmY,EACZhF,KAAKyE,QAAU0C,GAAWynC,EAA2B5pC,IAKrDhF,KAAKyE,QAAUO,EAMvBhF,KAAKoE,MAAQY,EAAMZ,QAAU,IAAI/M,OAAS+M,MAhI9CwqC,EAA2BC,0BACrB,sCACND,EAA2BC,+BACrB,sCACND,EAA2BC,+BACrB,mCACND,EAA2BC,iCACrB,mCACND,EAA2BC,qCACrB,kCACND,EAA2BC,WACrB,6BACND,EAA2BC,qBACrB,4CACND,EAA2BC,aACrB,2CACND,EAA2BC,qBACrB,sCACND,EAA2BC,qBACrB,kCACND,EAA2BC,yBACrB,iDA8GNjpB,EAAgB13B,UAAYlB,OAAOY,OAAOyJ,MAAMnJ,WAChD03B,EAAgB13B,UAAU+H,YAAc2vB,EAuBzBA,O,6BC/Jf,uDAOe,MAAMwU,EAMjBnkC,YAAYupB,EAAe,IAAIC,KAC3Bzf,KAAKwf,aAAeA,EAGpBxf,KAAKq8B,iBAAmBr8B,KAAK2hB,GAAK3hB,KAAK4jC,YACvC5jC,KAAKivC,oBAAsBjvC,KAAKyuC,IAAMzuC,KAAK8hB,eAS/C8hB,YAAYnc,EAAW/F,GAGnB,OAFA1hB,KAAKwf,aAAaokB,YAAYnc,EAAW/F,GAElC,IAAM1hB,KAAKivC,oBAAoBxnB,EAAW/F,GASrDI,eAAe2F,EAAW/F,GACtB1hB,KAAKwf,aAAasC,eAAe2F,EAAW/F,M,6BCxCpD,oDAUe,SAASwtB,EAAIld,GACxB,MAAMvE,EAAQuE,EAAIjvB,MAAM,UAExB,IAAK,IAAIzW,EAAI,EAAGiK,EAASk3B,EAAMl3B,OAAQjK,EAAIiK,EAAQjK,IAAK,CACpD,IAAI6iD,EAAU,KAAI1hB,EAAMnhC,GAEpBA,IAAMiK,EAAS,IACf44C,GAAU,QAEd1hB,EAAMnhC,GAAK6iD,EAEf,MAAMl3B,EAAawV,EAAMC,QAAR,OAEjB1tB,KAAKytB,MAAQA,EACbztB,KAAKsQ,IAAM2H,EAAUwV,EAAMtqB,KAAK,IAChCnD,KAAKiY,QAAUA,EASnBi3B,EAAIhhD,UAAUkhD,SAAU,EAMxBF,EAAIhhD,UAAUmhD,qBAAsB,EAMpCH,EAAIhhD,UAAUohD,qBAAsB,EAKpCJ,EAAIhhD,UAAUqhD,gBAAkB,WAC5B,MAAMC,EAAa,GAEnB,IAAK,IAAIC,EAAa,EAAGA,EAAazvC,KAAKytB,MAAMl3B,OAAQk5C,IAAc,CACnE,MAGMhiB,EAAQ,CACVgiB,aACAC,IAJEjjB,IAAQc,SACNd,IAAQO,SAAShtB,KAAKytB,MAAMgiB,GAAa,WAI7Cjf,MAAO,GACPM,WAAY,IAGhB0e,EAAWC,GAAchiB,EAEzBhB,IAAQuD,UAAUhwB,KAAKytB,MAAMgiB,GAAa,WAAWrgC,QAAQ5K,IACzD,MAAMmrC,EAAWnrC,EAAK2oB,UAAU,GAAGpqB,MAAM,KAAK,GAIzC0qB,EAAM+C,MAAMmf,KACbliB,EAAM+C,MAAMmf,GAAY,CACpB3qB,KAAM2qB,EACNrgB,MAAO,KAGf7B,EAAM+C,MAAMmf,GAAUrgB,MAAMpsB,KAAKsB,KAErCioB,IAAQuD,UAAUhwB,KAAKytB,MAAMgiB,GAAa,iBAAiBrgC,QAAQ5K,IAC/D,MAAMorC,EAAMprC,EAAKZ,QAAQ,KACnButB,EAAY3sB,EAAK4Q,OAAO,EAAGw6B,GAAKx6B,OAAO,IACvCob,EAAQhsB,EAAK4Q,OAAO,GAAK+b,EAAU56B,QAAQwM,MAAM,KAEnDytB,EAAMj6B,QACNk3B,EAAMqD,WAAW5tB,KAAK,CAClBiuB,YACAX,YAMhB,OAAOgf,GAQXN,EAAIhhD,UAAU2hD,aAAe,SAAS7qB,GAElC,MAAM8qB,EAAS9vC,KAAKuvC,kBACpB,IAAI9pC,GAAS,EAWb,OATAzY,OAAOuY,KAAKuqC,GAAQ1gC,QAAQqgC,IACpBhqC,GAGAqqC,EAAOL,GAAYjf,MAAMxL,KACzBvf,GAAS,KAIVA,GAIXypC,EAAIhhD,UAAU6hD,SAAW,SAASrwC,EAAMswC,GAEpCvjB,IAAQuD,UAAUhwB,KAAKiY,QAAS,YAAY7I,QAAQ5K,IAChD,MAAMR,EAAQQ,EAAKzB,MAAM,KACnBouB,EAAYntB,EAAM0pB,QAAQtY,OAAO,GAEvC1V,EAAK/S,EAAE,QAAS,CAAEsZ,MAAO,kCACrBkrB,cACJ,IAAK,IAAIj4B,EAAI,EAAGA,EAAI8K,EAAMzN,OAAQ2C,IAC9BwG,EAAK/S,EAAE,UAAW,CAAEE,KAAMmX,EAAM9K,KAAMiN,KAE1CzG,EAAKyG,OAGT,IAAK,IAAI7Z,EAAI,EAAGA,EAAI0T,KAAKytB,MAAMl3B,OAAQjK,IAAK,CACxC,MAAMwhC,EAAQrB,IAAQe,WAAWxtB,KAAKytB,MAAMnhC,GAAGyW,MAAM,QAAQ,IAE7D,GAAsB,UAAhB+qB,EAAML,OACa,UAAhBK,EAAML,OACU,gBAAhBK,EAAML,MACX,SAGJ,IAAIzI,EACJ,MAAMirB,EAAYxjB,IAAQO,SAAShtB,KAAKytB,MAAMnhC,GAAI,WAG9C04B,IADAirB,GACOA,EAAU9iB,UAAU,GAAGpqB,MAAM,KAAK,GAK7CrD,EAAK/S,EAAE,UAAW,CAAEujD,QAASF,EACzBnjD,KAAMihC,EAAML,QAChB,MAAM0iB,EAAW1jB,IAAQO,SAAShtB,KAAKytB,MAAMnhC,GAAI,UAEjD,GAAI6jD,EAAU,CAEV,MAAMT,EAAMjjB,IAAQc,SAAS4iB,GAE7BzwC,EAAKlE,MAAM,CAAE3O,KAAM6iD,IAGvB,GAAoB,UAAhB5hB,EAAML,OAAqC,UAAhBK,EAAML,MAAmB,CACpD/tB,EAAK/S,EAAE,cACH,CAAEsZ,MAAO,6BACLwnB,MAAOK,EAAML,QACjBzI,GACAtlB,EAAKlE,MAAM,CAAEwpB,SAEjB,IAAK,IAAI9rB,EAAI,EAAGA,EAAI40B,EAAMF,IAAIr3B,OAAQ2C,IAAK,CACvC,MAAMk3C,EACA3jB,IAAQO,SACNhtB,KAAKytB,MAAMnhC,GACV,YAAWwhC,EAAMF,IAAI10B,IAE9BwG,EAAK/S,EAAE,eAAgB8/B,IAAQsB,YAAYqiB,IAI3C,MAAMC,EACA5jB,IAAQO,SACNhtB,KAAKytB,MAAMnhC,GACV,UAASwhC,EAAMF,IAAI10B,IAE5B,GAAIm3C,EAAW,CACX,MAAMC,EAAiB7jB,IAAQ8B,UAAU8hB,GAGzC,IAAK,IAAI5uC,EAAI,EAAGA,EAAI6uC,EAAe/5C,OAAQkL,IACvC/B,EAAK/S,EAAE,YAAa2jD,EAAe7uC,IAAI0E,KAK/CnG,KAAKuwC,eAAejkD,EAAGoT,EAAMouB,EAAMF,IAAI10B,IAEvCwG,EAAKyG,KAGT,GAAI6e,EAAM,CACN,MAAMwrB,EAAU/jB,IAAQ2C,UAAUpvB,KAAKytB,MAAMnhC,IAE7C,IAAK,MAAQmkD,EAAeC,KAAoBF,EAC5C9wC,EAAK/S,EAAE,SAAU,CACbq4B,KAAMyrB,EACNxqC,MAAO,oCAGXyqC,EAAethC,QAAQuhC,IAEnB,MAAMf,EAAMe,EAAY/sC,QAAQ,KAC1BgtC,EAAKD,EAAYv7B,OAAOw6B,EAAM,GAGpC,GADAlwC,EAAK/S,EAAE,cACkB,IAArBikD,EAAGhtC,QAAQ,KACXlE,EAAKlE,MAAM,CAAE3O,KAAM+jD,QAChB,CACH,MAAM/jD,EAAO+jD,EAAG7tC,MAAM,IAAK,GAAG,GAE9BrD,EAAKlE,MAAM,CAAE3O,SAEb,IAAIgkD,EAAID,EAAG7tC,MAAM,IAAK,GAAG,GAEzB8tC,EAAIpkB,IAAQC,mBAAmBmkB,GAC/BnxC,EAAKlE,MAAM,CAAEjO,MAAOsjD,IAExBnxC,EAAKyG,OAGTzG,EAAKyG,KAKHsmB,IAAQuD,UAAUhwB,KAAKytB,MAAMnhC,GAAI,iBAExB8iB,QAAQ5K,IACnB,MAAMorC,EAAMprC,EAAKZ,QAAQ,KACnButB,EAAY3sB,EAAK4Q,OAAO,EAAGw6B,GAAKx6B,OAAO,IACvCob,EAAQhsB,EAAK4Q,OAAO,GAAK+b,EAAU56B,QAAQwM,MAAM,KAEnDytB,EAAMj6B,SACNmJ,EAAK/S,EAAE,aAAc,CAAEwkC,YACnBlrB,MAAO,oCACXuqB,EAAMphB,QAAQ/gB,GAAKqR,EAAK/S,EAAE,SAAU,CAAEq4B,KAAM32B,IAAK8X,MACjDzG,EAAKyG,QAKjB,MAAM2qC,EAAWrkB,IAAQuD,UAAUhwB,KAAKytB,MAAMnhC,GAAI,SAElD,GAAIwkD,EAASv6C,QAAUupB,IAAQixB,uBAAwB,CAGnD,MAAMC,EAAOF,EACRtrC,IAAIyrC,GAAWA,EAAQluC,MAAM,KAAK,IAClCyC,IAAI0rC,GAAWA,EAAQnuC,MAAM,KAAK,IAEvCiuC,EAAK5hC,QAAQ9C,IACT5M,EAAK/S,EAAE,SAAU,CACb2f,MACArG,MAAO,oCAEXvG,EAAKyG,OAGHsmB,IAAQO,SAAShtB,KAAKytB,MAAMnhC,GAAI,iBAGlCoT,EAAK/S,EAAE,YAAa,CAChBwkC,UAAW,MACXlrB,MAAO,oCAEX+qC,EAAK5hC,QAAQ9C,IACT5M,EAAK/S,EAAE,SAAU,CAAE2f,QAAOnG,OAE9BzG,EAAKyG,MAITsmB,IAAQO,SAAShtB,KAAKytB,MAAMnhC,GAAI,eAChCoT,EAAK/S,EAAE,YAAYwZ,KAIvBnG,KAAKuwC,eAAejkD,EAAGoT,EAAM,KAG7B,MAAMyxC,EAAc1kB,IAAQuD,UAAUhwB,KAAKytB,MAAMnhC,GAAI,aAErD,IAAK,IAAI4M,EAAI,EAAGA,EAAIi4C,EAAY56C,OAAQ2C,IAAK,CACzC,MAAMk4C,EAAS3kB,IAAQiD,YAAYyhB,EAAYj4C,IAS/C,GAPAwG,EAAK/S,EAAE,aAAc,CACjBsZ,MAAO,wCACP2pB,IAAKwhB,EAAOxhB,IACZ1oB,GAAIkqC,EAAO7jD,QAIX6jD,EAAOjjD,eAAe,aAGtB,OAAQijD,EAAOzhB,WACf,IAAK,WACDjwB,EAAKlE,MAAM,CAAE61C,QAAS,cACtB,MACJ,IAAK,WACD3xC,EAAKlE,MAAM,CAAE61C,QAAS,cACtB,MACJ,IAAK,WACD3xC,EAAKlE,MAAM,CAAE61C,QAAS,SACtB,MACJ,IAAK,WACD3xC,EAAKlE,MAAM,CAAE61C,QAAS,SAM9B3xC,EAAKyG,KAETzG,EAAKyG,KAITnG,KAAKsxC,kBAAkBhlD,EAAGoT,GAE1B,MAAMhT,EAAIsT,KAAKytB,MAAMnhC,GAEjBmgC,IAAQO,SAAStgC,EAAG,aAAcsT,KAAKiY,SACvCvY,EAAKlE,MAAM,CAAE61C,QAAS,SACf5kB,IAAQO,SAAStgC,EAAG,aAAcsT,KAAKiY,SAC9CvY,EAAKlE,MAAM,CAAE61C,QAAS,cACf5kB,IAAQO,SAAStgC,EAAG,aAAcsT,KAAKiY,SAC9CvY,EAAKlE,MAAM,CAAE61C,QAAS,cACf5kB,IAAQO,SAAStgC,EAAG,aAAcsT,KAAKiY,UAC9CvY,EAAKlE,MAAM,CAAE61C,QAAS,SAKP,MAAfvjB,EAAMH,MAAiBlB,IAAQO,SAAStgC,EAAG,gBAAiBsT,KAAKiY,UAEjEvY,EAAKlE,MAAM,CAAE61C,QAAS,aAE1B3xC,EAAKyG,KAIT,OAFAzG,EAAKyG,KAEEzG,GAGXwvC,EAAIhhD,UAAUojD,kBAAoB,SAAS7B,EAAY/vC,GACnDA,EAAK/S,EAAE,aAGP,MAAM4kD,EACA9kB,IAAQO,SAAShtB,KAAKytB,MAAMgiB,GAAa,aAAczvC,KAAKiY,SAElE,GAAIs5B,EAAS,CACT,MAAMC,EAAY/kB,IAAQyB,aAAaqjB,GAEvC7xC,EAAK/S,EAAE,UAAW,CACdsZ,MAAO,yCACPwrC,OAAQD,EAAU,GAClB9oC,SAAU8oC,EAAU,KAIpBA,EAAUj7C,OAAS,GACnBmJ,EAAKlE,MAAM,CAAEsiC,QAAS0T,EAAU,KAEpC9xC,EAAKyG,KAKHsmB,IAAQuD,UACNhwB,KAAKytB,MAAMgiB,GACX,iBACAzvC,KAAKiY,SAEA7I,QAAQ5K,IACjB,MAAM8pB,EAAc7B,IAAQ4B,iBAAiB7pB,GAE7C8pB,EAAYroB,MAAQ,8BACpBvG,EAAK/S,EAAE,eAAea,EAAE8gC,EAAYA,oBAC7BA,EAAYA,YAEnB,MAAMojB,EACAjlB,IAAQO,SACNhtB,KAAKytB,MAAMgiB,GACX,WACAzvC,KAAKiY,SAETy5B,IACApjB,EAAYqjB,MAAQD,EAAUt8B,OAAO,IAEzC1V,EAAKlE,MAAM8yB,GACX5uB,EAAKyG,OAET,MAAMyrC,EAAgBnlB,IAAQE,UAAU3sB,KAAKytB,MAAMgiB,GAAazvC,KAAKiY,SAErE,GAAI25B,EAAe,CACfA,EAAc3rC,MAAQ,uCACtBvG,EAAKlE,MAAMo2C,GAILnlB,IAAQuD,UACNhwB,KAAKytB,MAAMgiB,GACX,eACAzvC,KAAKiY,SAEE7I,QAAQ5K,IACnB,MAAMiqB,EAAYhC,IAAQyD,kBAAkB1rB,GAExCxE,KAAKovC,UACL3gB,EAAUI,GAAK,WAEnB,MAAMnmB,EACA+lB,GAA2C,iBAAvBA,EAAU/lB,SAC1B+lB,EAAU/lB,SAAS/F,cACnB,GAEL3C,KAAKqvC,sBACe,QAAb3mC,GAAmC,WAAbA,IAC1B1I,KAAKsvC,qBAAoC,QAAb5mC,GAGpChJ,EAAK/S,EAAE,YAAa8hC,GAAWtoB,OAGvCzG,EAAKyG,MAIT+oC,EAAIhhD,UAAUqiD,eAAiB,SAASd,EAAY/vC,EAAMmyC,GAEhDplB,IAAQuD,UACNhwB,KAAKytB,MAAMgiB,GACV,aAAYoC,GAEfziC,QAAQ5K,IACV,MAAMstC,EAAWrlB,IAAQ8C,YAAY/qB,GAEf,YAAlBstC,EAAS7qC,MACTvH,EAAK/S,EAAE,kBAAmB,CACtBsZ,MAAO,qCACP1Y,MAAOukD,EAASriB,OAAO,KAE3B/vB,EAAKyG,OAELzG,EAAK/S,EAAE,UAAW,CACdsZ,MAAO,qCACPgB,KAAM6qC,EAAS7qC,OAEf6qC,EAASriB,OAAOl5B,OAAS,GACzBmJ,EAAKlE,MAAM,CAAE,QAAWs2C,EAASriB,OAAO,KAE5C/vB,EAAKyG,SAKjB+oC,EAAIhhD,UAAU6jD,iBAAmB,SAASryC,EAAMmyC,GAC5C,IAAI7f,EAAM,GACV,MAAMggB,EACAtyC,EAAKuxB,KACH,gEAEJ+gB,EAAsBz7C,SACtBy7B,GAAO,uBACHggB,EAAsBzwC,KAAK,SAC3BywB,GAAOggB,EAAsBzwC,KAAK,SAElCywB,GAAO,IAEXA,GAAO,QAaX,OAVyBtyB,EAAKuxB,KAAK,wDAElBghB,KAAK,CAACC,EAAGC,KACtBngB,GAAQ,aAAY6f,KAAeM,EAAGvvC,aAAa,UAC/CuvC,EAAGC,aAAa,aAChBpgB,GAAQ,IAAGmgB,EAAGvvC,aAAa,YAE/BovB,GAAO,SAGJA,GAIXkd,EAAIhhD,UAAUmkD,WAAa,SAASC,GAChC,MAAMC,EAAYpqC,KAAKgM,MAGvBnU,KAAKsQ,IACE,cAAMiiC,yCAMb,MAAMC,EACAC,EAAEH,GAAQrhB,KAAK,mDAEjBuhB,EAAOj8C,QACPi8C,EAAOP,KAAK,CAACrC,EAAK1e,KACd,MAAMwhB,EACAD,EAAEvhB,GACCD,KAAK,YACLzrB,IAAI,CAAC0sC,EAAGS,IAAYA,EAAQ/vC,aAAa,SACzCzV,MAELulD,EAASn8C,OAAS,IAClByJ,KAAKsQ,KACG,WACA4gB,EAAMtuB,aAAa,cACZsuB,EAAMtuB,aAAa,WAC1B8vC,EAASvvC,KAAK,cAKlCnD,KAAKiY,QAAUjY,KAAKsQ,IACpBgiC,EAAOrhB,KAAK,YAAYghB,KAAK,CAACC,EAAGS,KAC7B,MAAMjmD,EAAIsT,KAAK4yC,aAAaH,EAAEE,IAE9B3yC,KAAKytB,MAAMvqB,KAAKxW,KAWpBsT,KAAKsQ,IAAMtQ,KAAKiY,QAAUjY,KAAKytB,MAAMtqB,KAAK,KAI9C+rC,EAAIhhD,UAAU0kD,aAAe,SAASD,GAClC,MAAMtjB,EAAOsjB,EAAQ1hB,KAAK,gBACpBrT,EAAY+0B,EAAQ1hB,KAAK,4DAC/B,IAAIe,EAAM,GACV,MAAM6gB,EAAOj1B,EAAUqT,KACnB,4DAEExD,EAAQ,CAAEA,MAAO4B,EAAK9tB,KAAK,SAEjCksB,KAAa,KAUb,GATgC,aAA5BklB,EAAQpxC,KAAK,aAEbksB,EAAME,KAAO,KAEb/P,EAAUqT,KAAK,qDAAqD16B,OACpEk3B,EAAMhlB,MAAQoqC,EAAKt8C,OAAS,YAAc,YAE1Ck3B,EAAMhlB,MAAQ,WAEdoqC,EAAKt8C,OAAQ,CACby7B,GAAQ,iBAAgBvE,EAAME,kBAC1BklB,EAAKtxC,KAAK,gBACdywB,GAAQ,aAAY6gB,EAAKtxC,KAAK,aAAasxC,EAAKtxC,KAAK,cAErD,MAAMuxC,EAAcD,EAAKtxC,KAAK,WAG1BywB,GADA8gB,EACQ,IAAGA,QAEJ,YAGXrlB,EAAMG,IACAyB,EACG4B,KAAK,iBACLzrB,IAAI,CAAC0sC,EAAGa,IAAgBA,EAAYnwC,aAAa,OACjDzV,MACT6kC,GAAUvF,IAAQoB,WAAWJ,GAArB,OA6CZ,OA1CAuE,GAAO,uBACF6gB,EAAKt8C,SACNy7B,GAAO,+BAIPpU,EAAUrnB,SACNqnB,EAAUrc,KAAK,WACfywB,GAAUvF,IAAQW,cAAcxP,EAAUrc,KAAK,UAAvC,QAERqc,EAAUrc,KAAK,SACfywB,GAAUvF,IAAQa,YAAY1P,EAAUrc,KAAK,QAArC,QAEZqc,EAAUqT,KAAK,qDAAqDghB,KAAK,CAACC,EAAG5jB,KACzE0D,GAAQ,iBAAgB1D,EAAY1rB,aAAa,QACjDovB,GAAQ,IAAGygB,EAAEnkB,GAAa3sB,OAC1BqwB,GAAO,OACH1D,EAAY8jB,aAAa,WACzBpgB,GAAQ,WAAU1D,EAAY1rB,aAAa,mBAMvDgb,EAAUqT,KAAK,cACVghB,KAAK,CAACC,EAAGzjB,KACN,IAAI/lB,EAAW+lB,EAAU7rB,aAAa,YAEtC8F,EAC0B,iBAAbA,EAAwBA,EAAS/F,cAAgB,GAEzD3C,KAAKqvC,sBACe,QAAb3mC,GAAmC,WAAbA,IAC1B1I,KAAKsvC,qBAAoC,QAAb5mC,IAEzB1I,KAAKovC,SACZ3gB,EAAUjtB,aAAa,KAAM,WAGjCwwB,GAAOvF,IAAQ0D,oBAAoB1B,MAGnCkkB,EAAQpxC,KAAK,YACrB,IAAK,YACDywB,GAAO,iBACP,MACJ,IAAK,YACDA,GAAO,iBACP,MACJ,IAAK,OACDA,GAAO,iBACP,MACJ,IAAK,OACDA,GAAO,iBAqFX,OAlFAA,GAAQ,SAAQ2gB,EAAQpxC,KAAK,cAMzB8tB,EAAK4B,KAAK,aAAa16B,SACvBy7B,GAAO,kBAGX3C,EAAK4B,KAAK,iBAAiBghB,KAAK,CAACC,EAAGa,KAChC/gB,GAAUvF,IAAQ0B,YAAY4kB,GAAtB,OACJN,EAAEM,GAAa9hB,KAAK,cAAc16B,SAClCy7B,GAAQ,UAAS+gB,EAAYnwC,aAAa,SAC1CovB,GACOygB,EAAEM,GACA9hB,KAAK,cACLzrB,IAAI,CAACwtC,EAAIC,KACN,MAAMpmD,EAAOomD,EAAUrwC,aAAa,QAEpC,OACK/V,EAAUA,EAAF,IAAY,IACfomD,EAAUrwC,aAAa,WAEpCzV,MACAgW,KAAK,MACd6uB,GAAO,QAIXA,GAAOhyB,KAAK+xC,iBAAiBU,EAAEM,GAAcA,EAAYnwC,aAAa,SAI1EovB,GAAOhyB,KAAK+xC,iBAAiB1iB,EAAM,KAGnCA,EACK4B,KAAK,8DACLghB,KAAK,CAACC,EAAGgB,KACNlhB,GACQ,YAAWkhB,EAAOtwC,aAAa,SAC/BswC,EAAOtwC,aAAa,eAIpCysB,EACK4B,KAAK,wDACLghB,KAAK,CAACC,EAAGrgB,KACN,MAAMV,EAAYU,EAAUjvB,aAAa,aACnC4tB,EACAiiB,EAAE5gB,GACCZ,KAAK,WACLzrB,IAAI,CAACwtC,EAAIjoB,IAAWA,EAAOnoB,aAAa,SACxCzV,MAELqjC,EAAMj6B,SACNy7B,GAAQ,gBAAeb,KAAaX,EAAMrtB,KAAK,cAK3DksB,EACK4B,KAAK,oDACLghB,KAAK,CAACC,EAAGnnB,KACN,MAAM/F,EAAO+F,EAAOnoB,aAAa,QAEjC6vC,EAAE1nB,GACGkG,KAAK,cACLghB,KAAK,CAACe,EAAIC,KACP,MAAMpmD,EAAOomD,EAAUrwC,aAAa,QACpC,IAAIrV,EAAQ0lD,EAAUrwC,aAAa,SAEnCrV,EAAQk/B,IAAQC,mBAAmBn/B,GACnCykC,GAAQ,UAAShN,KAAQn4B,IACrBU,GAASA,EAAMgJ,SACfy7B,GAAQ,IAAGzkC,GAEfykC,GAAO,WAIhBA,I,+hBCntBJ,MAAMmhB,EAAS,SAETC,EAAQ,QAERC,EAAU,UAEVC,EAAoB,YAEpBC,EAAS,SAETC,EAAO,OAEPC,EAAW,WAEXC,EAAe,eAEfC,EAAU,UCFjBC,EAAwB,CAC1B,OAAUT,EACV,SAAYA,EACZ,MAASC,EACT,QAAWC,EACX,oBAAqBC,EACrB,OAAUC,GA2Cd,SAASM,IACL,MAAMC,EAAYtZ,UAAUsZ,UAE5B,GAAIA,EAAUxgC,MAAM,YAAa,CAC7B,MAAMygC,EAAUD,EAAUxgC,MAAM,sBAAsB,GAEtD,MAAO,CACHzmB,KAAM4mD,EACNM,YAUZ,SAASC,IACL,MAAMF,EAAYtZ,UAAUsZ,UAE5B,GAAIA,EAAUxgC,MAAM,eAAgB,CAChC,MAAMygC,EAAUD,EAAUxgC,MAAM,yBAAyB,GAEzD,MAAO,CACHzmB,KAAM2mD,EACNO,YASZ,SAASE,IACL,MAAM3gC,EACAknB,UAAUsZ,UAAUxgC,MAAM,wCAChC,IAAIygC,EAMJ,GAAIzgC,GAA+B,gBAAtBknB,UAAU0Z,QAA2B,CAC9C,IAAIrnD,EASJ,OAPIymB,GAASA,EAAM/c,OAAS,IACxB1J,EAAOymB,EAAM,GACbygC,EAAUzgC,EAAM,IAEpBzmB,IAASA,EAAO,gBAChBknD,IAAYA,EAAU,WAEf,CACHlnD,KAAM6mD,EACNK,YAUZ,SAASI,EAAQC,GACb,IAAIC,EACJ,MAAMC,EAAY,CACdL,EACAJ,EACAG,GAIJ,IAAK,IAAI1nD,EAAI,EAAGA,EAAIgoD,EAAU/9C,OAAQjK,IAElC,GADA+nD,EAAcC,EAAUhoD,KACpB+nD,EACA,OAAOA,EAIf,MAAMxnD,EAAOunD,EAAOG,iBAEpB,OAAI1nD,KAAQ+mD,EACD,CACH/mD,KAAM+mD,EAAsB/mD,GAC5BknD,QAASK,EAAOI,sBAKxBH,EA3HJ,WACI,MAAMP,EAAYtZ,UAAUsZ,UACtBO,EAAc,CAChBxnD,KAAM8mD,EACNI,aAASxzC,GAGb,GAAIuzC,EAAUxgC,MAAM,YAAcwgC,EAAUxgC,MAAM,QAE9C,GAAIwgC,EAAUxgC,MAAM,WAAY,CAE5B,MAAMygC,EAAUD,EAAUxgC,MAAM,oBAAoB,GAEhDiG,OAAOjB,SAASy7B,EAAS,IAAM,KAC/BM,EAAYxnD,KAAOsmD,EACnBkB,EAAYN,QAAUA,QAG1BM,EAAYxnD,KAAOsmD,EACnBkB,EAAYN,QAAUD,EAAUxgC,MAAM,oBAAoB,GAIlE,OAAO+gC,EAoGOI,GACVJ,GAIG,CACHxnD,KAAM8mD,EACNI,aAASxzC,IAOF,MAAMm0C,EAQjBz+C,YAAYo+C,GACR,IAAIxnD,EAAMknD,EAGV,GADA/zC,KAAK20C,QAAUC,IAAOC,UAAUra,UAAUsZ,gBACf,IAAhBO,EAA6B,CACpC,MAAMS,EAAsBX,EAAQn0C,KAAK20C,SAEzC9nD,EAAOioD,EAAoBjoD,KAC3BknD,EAAUe,EAAoBf,aACvBM,EAAYxnD,QAAQ+mD,GAC3B/mD,EAAO+mD,EAAsBS,EAAYxnD,MACzCknD,EAAUM,EAAYN,UAEtBlnD,EAAO8mD,EACPI,OAAUxzC,GAGdP,KAAK+0C,MAAQloD,EACbmT,KAAKg1C,SAAWjB,EAOpBkB,UACI,OAAOj1C,KAAK+0C,MAOhBzd,WACI,OAAOt3B,KAAK+0C,QAAU5B,EAO1B+B,UACI,OAAOl1C,KAAK+0C,QAAU3B,EAO1BhjB,YACI,OAAOpwB,KAAK+0C,QAAU1B,EAO1B8B,cACI,OAAOn1C,KAAK+0C,QAAUzB,EAO1B/d,WACI,OAAOv1B,KAAK+0C,QAAUxB,EAO1B6B,SACI,OAAOp1C,KAAK+0C,QAAUvB,EAO1B1b,aACI,OAAO93B,KAAK+0C,QAAUtB,EAO1B1zB,gBACI,OAAO/f,KAAK+0C,QAAUrB,EAO1B2B,aACI,OAAOr1C,KAAKg1C,SAgBhBM,gBAAgBC,GACZ,GAAIv1C,KAAKg1C,SACL,OAAOh1C,KAAK20C,QAAQa,UAAUD,GAatCE,qBAAqB1B,GACjB,OAAO/zC,KAAKs1C,gBAAgB,CAAE,CAACt1C,KAAK+0C,OAAS,IAAGhB,IAYpD2B,kBAAkB3B,GACd,OAAO/zC,KAAKs1C,gBAAgB,CAAE,CAACt1C,KAAK+0C,OAAS,IAAGhB,IAapD4B,iBAAiB5B,GACb,OAAO/zC,KAAKs1C,gBAAgB,CAAE,CAACt1C,KAAK+0C,OAAS,IAAGhB,K,qBC7UxD,MAAM6B,UAA0Bn2B,IAAa,kB,UAAA,Y,EAK9B,I,EAL8B,c,EAAA,M,sFAYzCo2B,QACI71C,KAAK81C,SAAW,GAQpB,aACI,OAAO9oD,OAAOuY,KAAKvF,KAAK81C,UAAUv/C,OAStC4hB,QAAQ49B,GACJ,OAAO/1C,KAAK81C,SAASC,GAWzBjpC,QAAQipC,EAASC,GACbh2C,KAAK81C,SAASC,GAAWC,EAS7BjpC,WAAWgpC,UACA/1C,KAAK81C,SAASC,GASzBloD,IAAIE,GACA,MAAMwX,EAAOvY,OAAOuY,KAAKvF,KAAK81C,UAE9B,KAAIvwC,EAAKhP,QAAUxI,GAInB,OAAOwX,EAAKxX,GAQhBqX,YACI,OAAOwH,KAAKwL,UAAUpY,KAAK81C,WAOnC,MAAMG,UAA0Bx2B,IAK5BxpB,cACIokC,QAEA,IACIr6B,KAAK81C,SAAW5pD,OAAOgqD,aACvBl2C,KAAKm2C,uBAAwB,EAC/B,MAAOC,IAIJp2C,KAAK81C,WACN/wC,QAAQI,KAAK,8BACbnF,KAAK81C,SAAW,IAAIF,EACpB51C,KAAKm2C,uBAAwB,GASrCE,yBACI,OAAOr2C,KAAKm2C,sBAQhBN,QACI71C,KAAK81C,SAASD,QACd71C,KAAKkjB,KAAK,WAQd,aACI,OAAOljB,KAAK81C,SAASv/C,OAUzB4hB,QAAQ49B,GACJ,OAAO/1C,KAAK81C,SAAS39B,QAAQ49B,GAUjCjpC,QAAQipC,EAASC,EAAUM,GAAuB,GAC9Ct2C,KAAK81C,SAAShpC,QAAQipC,EAASC,GAE1BM,GACDt2C,KAAKkjB,KAAK,WAQlBnW,WAAWgpC,GACP/1C,KAAK81C,SAAS/oC,WAAWgpC,GACzB/1C,KAAKkjB,KAAK,WAUdr1B,IAAIvB,GACA,OAAO0T,KAAK81C,SAASjoD,IAAIvB,GAQ7B8Y,YACI,GAAIpF,KAAKq2C,uBACL,OAAOr2C,KAAK81C,SAAS1wC,YAGzB,MAAM7O,EAASyJ,KAAK81C,SAASv/C,OACvBggD,EAAsB,GAE5B,IAAK,IAAIjqD,EAAI,EAAGA,EAAIiK,EAAQjK,IAAK,CAC7B,MAAMuB,EAAMmS,KAAK81C,SAASjoD,IAAIvB,GAE9BiqD,EAAoB1oD,GAAOmS,KAAK81C,SAAS39B,QAAQtqB,GAGrD,OAAO+e,KAAKwL,UAAUm+B,IAIvB,MAAMC,EAAoB,IAAIP,G,6BClNrC,oXAMO,MAAMQ,EAAwB,wBAQxBvjD,EAA2B,4BAO3BoB,EAAiB,0BAOjBoiD,EAAmB,6BAWnBC,EAAuB,uBAYvBC,EAAsB,gCAQtBC,EAAuB,kC,6BC3DpC,0KAUO,MAAMC,EAAc,wBAOdC,EAAkB,6BAKlBC,EAAkB,6BASlBC,EAAmB,6BAKnBC,EAAmB,+B,6BCpChC,2dAIO,MAAMC,EAAmB,YAMnBC,EAAmB,YAOnBC,EAAc,OAMdC,EAAW,KAMXC,EAAY,MAMZC,EAAgB,UAOhBC,EAAiB,WAMjBC,EAAe,SAOfC,EAAsB,sBAOtBC,EAAuB,gC,gBC9DpC,IAAIC,EAASz6B,EAAQ,KACjB06B,EAAS16B,EAAQ,KAErBtxB,EAAQisD,MAAQD,EAChBhsD,EAAQosB,MAAQ2/B,EAAO3/B,MACvBpsB,EAAQksD,gBAAkBH,EAAOG,gBACjClsD,EAAQmsD,YAAcJ,EAAOI,YAC7BnsD,EAAQosD,cAAgBL,EAAOK,cAC/BpsD,EAAQqsD,sBAAwBN,EAAOM,sBACvCrsD,EAAQssD,qBAAuBP,EAAOO,qBACtCtsD,EAAQusD,yBAA2BR,EAAOQ,0B,6BCV1C,0QAWO,MAAMpuB,EAA0B,oCAS1Bl7B,EAAyB,mCAazBC,EAAoB,8BAMpBspD,EAAc,wBAOdC,EAAwB,oC,8BC9CrC,kSAuBA,MAAMt6B,EAASF,oBAAUM,GA0ClB,MAAMm6B,EAAuB,CAChC,CAAEC,KAAM,gCACR,CAAEA,KAAM,iCACR,CAAEA,KAAM,kCASCC,EAAsB,OAKpB,MAAMC,UAAave,IAc9BnkC,YAAYkR,EAASyxC,GACjBve,QACAr6B,KAAKuU,WAAa,KAClBvU,KAAK64C,sBAAuB,EAC5B74C,KAAK84C,gBAAkB,GACvB94C,KAAKmH,QAAUA,EACfnH,KAAK44C,MAAQA,EACb54C,KAAK+4C,mBAAoB,EA9C7BC,cACAC,cAiDIj5C,KAAKuU,WAvEb,UAA0B,sBAAE2kC,EAAF,WAAyBC,EAAa,aAAtC,MAAoDP,EAApD,mBAA2DQ,IAOjF,OALIR,IAEAO,GAAe,IAA+B,IAA7BA,EAAWv1C,QAAQ,KAAc,IAAM,YAAYg1C,KAGjE,IAAIS,IAAe,CACtBH,wBACAC,aACAC,uBA6DkBE,CAAiB,CAC/BJ,sBAAuB/xC,EAAQ+xC,sBAG/BC,WAAYhyC,EAAQgyC,YAAchyC,EAAQoyC,KAC1CX,QACAQ,mBAAoBjyC,EAAQiyC,qBAGhCp5C,KAAKw5C,sBAELx5C,KAAKy5C,KAAO,IAAIC,IAAK15C,KAAKuU,WAAYvU,KAAKmH,QAAQwyC,YAGnD35C,KAAK45C,mBAOLnH,EAAEvmD,QAAQy1B,GAAG,sBAAuBk4B,IAChC75C,KAAKuP,WAAWsqC,GAAIzc,MAAM,UAUlCwc,mBAGI55C,KAAKy5C,KAAKK,WAAW,qBACrB95C,KAAKy5C,KAAKK,WAAW,8BACrB95C,KAAKy5C,KAAKK,WAAW,wCACrB95C,KAAKy5C,KAAKK,WAAW,+BACrB95C,KAAKy5C,KAAKK,WAAW,0CACrB95C,KAAKy5C,KAAKK,WAAW,kCACrB95C,KAAKy5C,KAAKK,WAAW,kCAGf95C,KAAKmH,QAAQ4yC,YAAcj6B,IAAQsQ,aACrCpwB,KAAKy5C,KAAKK,WAAW,sBAEU,IAA/B95C,KAAKmH,QAAQ6yC,eAA0Bl6B,IAAQm6B,oBAC/Cj6C,KAAKy5C,KAAKK,WAAW,6BASzB95C,KAAKy5C,KAAKK,WAAW,qBACrB95C,KAAKy5C,KAAKK,WAAW,qBAKjBh6B,IAAQub,oBAAoD,IAA/Br7B,KAAKmH,QAAQ+yC,gBAC1Cj8B,EAAO/Y,KAAK,sBACZlF,KAAKy5C,KAAKK,WAAW,kCAGrB95C,KAAKuU,WAAW4lC,MAChBn6C,KAAKy5C,KAAKK,WAAW,0BAGrBM,IAAc5c,YAAYx9B,KAAKmH,UAC/BnH,KAAKy5C,KAAKK,WAAW,+BAO7BO,gBACI,OAAOr6C,KAAKuU,WAchB+lC,kBAAkBC,EAAc,GAAIxqC,EAAQjL,GACxC,MAAMqP,EAAMjoB,OAAOsuD,YAAYrmC,MACzBsmC,EAAYh/C,UAAQi/C,gBAAgB3qC,GAAQpN,cAQlD,GANA3C,KAAK84C,gBAAgB2B,GAAatmC,EAClC8J,EAAOrZ,IACF,kBAAiB61C,IAAY31C,EAAO,IAAGA,KAAS,QACjDqP,GAEJnU,KAAKwf,aAAa0D,KAAKy3B,IAAWvrD,0BAA2BmrD,EAAaxqC,EAAQjL,GAC9EiL,IAAWtU,UAAQgC,OAAOM,WAAagS,IAAWtU,UAAQgC,OAAOS,SAAU,CAC3E8B,KAAKuU,WAAW+9B,OAAOsI,4BAEvB38B,EAAO/Y,KAAM,iBAAgBlF,KAAKuU,WAAW5Q,KAG7C3D,KAAK66C,cAGL,MAAMC,EAAU96C,KAAKuU,WAAWzZ,OAGhCkF,KAAKy5C,KAAKsB,yBAAyBD,GAC9BpgB,KAAK,EAAG5xB,WAAUkyC,iBACVlyC,EAASib,IAAItoB,UAAQK,GAAGm/C,OACzBh9B,EAAOjZ,MACF,yBAAwB81C,qDAIjCE,EAAW5rC,QAAQ8rC,IACO,iBAAlBA,EAASj0C,OACTjH,KAAKm7C,6BAA+BD,EAASruD,MAG3B,wBAAlBquD,EAASj0C,OACTjH,KAAKo7C,mCAAqCF,EAASruD,MAGjC,eAAlBquD,EAASj0C,OACTjH,KAAKq7C,gBAAiB,EACtBH,EAASruD,MAAQmT,KAAKy5C,KAAKsB,yBAAyBG,EAASruD,KAAMquD,EAASj0C,MACvEyzB,KAAK,EAAG5xB,SAAUwyC,MACfA,EAAElsC,QAAQmsC,IACFA,EAAGC,SAAS,0BACZx7C,KAAKwf,aAAa0D,KACdu4B,6BAIfre,MAAM/jC,GAAK4kB,EAAO9Y,KAAK,qCAAsC9L,GAAKA,EAAEoL,cAI7EzE,KAAKm7C,8BACFn7C,KAAKo7C,qCACRp7C,KAAKuU,WAAWxG,WACZ/N,KAAK07C,kBAAkB5tD,KAAKkS,MAAO,KACnC,UAAW,KAAM,QAG5Bo9B,MAAMp4B,IACH,MAAM2yB,EAAS,0BAEfC,IAAqBC,iBACjB,IAAIxgC,MAAO,GAAEsgC,MAAW3yB,MAC5BiZ,EAAOjZ,MAAM2yB,EAAQ3yB,KAGzBu1C,EAAYoB,WACZ37C,KAAK+4C,mBAAoB,GAEzB/4C,KAAKuU,YAAcvU,KAAKuU,WAAW3K,WAChCnO,UAAQyI,mBAAmBlE,KAAKuU,WAAW5Q,MAG9C3D,KAAKwf,aAAa0D,KACdu4B,yBACAhgD,UAAQyI,mBAAmBlE,KAAKuU,WAAW5Q,WAEhD,GAAIoM,IAAWtU,UAAQgC,OAAOG,SACrB,+BAARkH,EACA9E,KAAK47C,2BAA4B,EAEjC57C,KAAK67C,kBAAmB,EAE5B77C,KAAK87C,aAAeh3C,EACR,cAARA,GACA9E,KAAKwf,aAAa0D,KACdu4B,oBACAM,cAAmCj3C,QAExC,GAAIiL,IAAWtU,UAAQgC,OAAOC,MACjCsC,KAAK87C,aAAeh3C,OACjB,GAAIiL,IAAWtU,UAAQgC,OAAOO,aAAc,CAE/CgC,KAAKuU,WAAWynC,KAAKC,eACrB,MAAMC,EAA2BvjB,QAAQ34B,KAAK64C,sBACxCsD,EAASr3C,GAAO9E,KAAK87C,aAE3B,GAAI97C,KAAK47C,0BAEL57C,KAAKwf,aAAa0D,KACdu4B,oBACAM,0BACD,GAAI/7C,KAAK67C,iBACZ77C,KAAKwf,aAAa0D,KACdu4B,oBACAM,cACAI,OACA57C,EACAP,KAAKo8C,0CACN,GAAIF,EACPl8C,KAAKwf,aAAa0D,KACdu4B,0BAA+CU,OAChD,CAMHl+B,EAAOjZ,MAAM,4BAIb,MAAMq3C,EAAkB5gD,UAAQ6gD,qBAE5BD,GAAmB,KAAOA,EAAkB,IAC5Cr8C,KAAKwf,aAAa0D,KACdu4B,oBACAM,eACAI,GAAU,oBACQ57C,EAClBP,KAAKo8C,qCAETp8C,KAAKwf,aAAa0D,KACdu4B,oBACAM,2BACAI,GAAU,gCACQ57C,EAClBP,KAAKo8C,2CAGVrsC,IAAWtU,UAAQgC,OAAOK,UAEjCkC,KAAKwf,aAAa0D,KACdu4B,oBACAM,oBACAj3C,EACAy1C,GASZpuC,SAASxI,EAAKg4C,GA4BV37C,KAAK66C,cACL76C,KAAKuU,WAAW9I,QACZ9H,EACAg4C,EACA37C,KAAKs6C,kBAAkBxsD,KAAKkS,KAAM,CAC9B2D,MACAg4C,cAWZvvC,OAAOjF,GACHnH,KAAK66C,cACL,MAAM1mC,EAAMnU,KAAK84C,gBAAgByD,UAAYrwD,OAAOsuD,YAAYrmC,MAEhE8J,EAAOrZ,IAAI,8BAA+BuP,GAC1CnU,KAAKuU,WAAWnI,OAAOjF,EAAQxD,IAAKwD,EAAQkF,IACxCiM,SAASnR,EAAQmF,IAAK,IAAM,EAC5BtM,KAAKs6C,kBAAkBxsD,KAAKkS,KAAM,CAC9B2D,IAAKwD,EAAQxD,IACbg4C,SAAUx0C,EAAQw0C,YAQ9Bd,cACI76C,KAAK47C,2BAA4B,EACjC57C,KAAK67C,kBAAmB,EACxB77C,KAAK87C,kBAAev7C,EACpBP,KAAK64C,0BAAuBt4C,EAQhCkL,QAAQ9H,EAAKg4C,GACT,IAAKh4C,EAAK,CACN,MAAM,gBAAE64C,EAAF,OAAmB1hD,GAAWkF,KAAKmH,QAAQs1C,MACjD,IAAIC,EAAeF,GAAmB1hD,EAStC,MAAM,SAAEigB,GAAa7uB,OAErB,GAAIswD,EAAiB,CACjB,MAAM3/B,EAAS9B,GAAYA,EAAS8B,QAE/BA,IAA4C,IAAlCA,EAAOjZ,QAAQ,eACnB5D,KAAK44C,SACZ8D,EAAe5hD,GAKvB6I,EAAM+4C,GAAiB3hC,GAAYA,EAAS4hC,SAGhD,OAAO38C,KAAKmM,SAASxI,EAAKg4C,GAa9BiB,WAAWC,EAAU11C,EAAS21C,GAE1B,IAAIC,EAAW,GAAEF,KAAY11C,EAAQ61C,aAC/B71C,EAAQ61C,aAAeh9C,KAAKmH,QAAQs1C,MAAMQ,IAAIt6C,iBAEpD,MAAMu6C,EAAcJ,EACdA,EAAiB98C,KAAKuU,WAAW5Q,IAAK3D,KAAK+4C,mBAC3CznB,IAAW6rB,gBAAgB,GAAGx6C,cAKpC,OAHAsb,EAAO/Y,KAAM,OAAMlF,KAAKuU,WAAW5Q,0BAA0Bu5C,KAC7DH,GAAWG,EAEJl9C,KAAKuU,WAAW6oC,KAAKR,WAAWG,EAAS,KAAM51C,GAQ1Dk2C,SACI,OAAOr9C,KAAKuU,WAAW5Q,IAO3B25C,eACI,MAAMhL,EAAStyC,KAAKuU,WAAW+9B,OAG/B,OAAOA,EAASA,EAAOiL,SAAW,GAMtCC,aACI,OAAQx9C,KAAKuU,WAAW0J,QAAU,IAAIrZ,KAAO,KAMjD64C,QAAQtb,GACJniC,KAAKuU,WAAW4lC,KAAKsD,QAAQtb,GASjC6Z,KAAKpuC,GACD,OAAO,IAAIovB,QAAQ,CAACC,EAASC,KACzBl9B,KAAKuU,WAAWynC,KACPA,KAAKh8C,KAAKuU,WAAWzZ,OAAQmiC,EAASC,EAAQtvB,KAO/D8vC,cACI,OAAO19C,KAAKuU,WAAW+9B,OAAOqL,SAUlCpuC,WAAWsqC,GACP,OAAI75C,KAAK64C,qBACE74C,KAAK64C,qBACJ74C,KAAKuU,YAIjBvU,KAAK64C,qBAAuB,IAAI7b,QAAQC,IACpC,MAAM2gB,EAAqB,CAACrD,EAAaxqC,KACjCA,IAAWtU,UAAQgC,OAAOO,eAC1Bi/B,IACAj9B,KAAKwf,aAAasC,eAAe64B,IAAWvrD,0BAA2BwuD,KAI/E59C,KAAKwf,aAAamC,GAAGg5B,IAAWvrD,0BAA2BwuD,KAG/D59C,KAAK69C,uBAAuBhE,GAErB75C,KAAK64C,sBAhBD7b,QAAQC,UA4BvB4gB,uBAAuBhE,GAWnB,IAFC75C,KAAKuU,WAAWupC,kBAAoB99C,KAAKuU,WAAW/G,SAEhDxN,KAAKuU,WAAWupC,kBAAjB,MAAqCjE,EAA0C,CAC/E,MAAMkE,EAASlE,EAAG5yC,KAElB,IAAe,iBAAX82C,GAAwC,WAAXA,KAI7B/9C,KAAKuU,WAAWpN,QAAQ6S,MAAO,EAG3Bha,KAAKuU,WAAWypC,yBAEhB,OAKZh+C,KAAKuU,WAAWhF,cAEqB,IAAjCvP,KAAKuU,WAAWpN,QAAQ6S,MACxBha,KAAKuU,WAAW/G,QAOxBgsC,sBACI,MAAM3T,EAAY,CACdoY,IAAK,CAAEC,WAAY,IACnBC,IAAK,CAAED,WAAY,KAGjBE,EAAkBp+C,KAAKmH,QAAQg3C,KAC9Bn+C,KAAKmH,QAAQg3C,IAAIE,aAAgB7F,EAEpCpiD,MAAMC,QAAQ+nD,KACdngC,EAAO/Y,KAAK,qBAAsBk5C,GAClCvY,EAAUsY,IAAID,WAAaE,GAG3Bp+C,KAAKmH,QAAQg3C,KAAOn+C,KAAKmH,QAAQg3C,IAAIG,qBACrCrgC,EAAO/Y,KAAK,6BACRlF,KAAKmH,QAAQg3C,IAAIG,oBAErBzY,EAAUsY,IAAIG,mBACRt+C,KAAKmH,QAAQg3C,IAAIG,oBAG3Bt+C,KAAKuU,WAAWxO,oBAAoB,OAAQ,IAAIw4C,IAAoBv+C,OACpEA,KAAKuU,WAAWxO,oBAAoB,SAAU,IAAIy4C,IAAuBx+C,KAAMA,KAAKwf,aAAcqmB,IAClG7lC,KAAKuU,WAAWxO,oBAAoB,OAAQ,IAAI04C,KASpDrC,oCACI,MAAMvxB,EAAU,GAGhB,GAAI7qB,KAAKmH,QAAQu3C,gBACV1+C,KAAKmH,QAAQu3C,eAAeC,OAC5B3+C,KAAKuU,WAAWmD,oBAAqB,CAGxC,MAAMknC,EAAa5+C,KAAKuU,WAAWmD,oBAC9BmnC,OAAO97C,MAAM,WACZuX,EAAU,GAEhBskC,EAAWxvC,QAAQ5K,IACf,MAAMR,EAAQQ,EAAKzB,MAAM,MACnBwX,EAASvW,EAAM0pB,QACfngC,EAAQyW,EAAMb,KAAK,MAEzBmX,EAAQC,GAAUhtB,IAItBs9B,EAAQi0B,cACF9+C,KAAKmH,QAAQu3C,eAAeC,QACtBrkC,EAAQ,iBAUxB,OAJAuQ,EAAQk0B,aAAe/+C,KAAKuU,WAAWynC,KAAKgD,qBAC5Cn0B,EAAQo0B,wBAA0Bj/C,KAAKuU,WAAW2qC,0BAG3Cr0B,EAQX5F,yBAAyBC,GAErB,IAAKllB,KAAKm7C,+BAAiCj2B,EACvC,OAGJ,MAAMpgB,EAAMmY,eAAK,CAAE8rB,GAAI/oC,KAAKm7C,+BAE5Br2C,EAAInY,EAAE,eAAgB,CAClBsZ,MAAO,2BACPk5C,KAAMj6B,IACL/e,KAELnG,KAAKuU,WAAWlH,KAAKvI,GAYzBs6C,sBAAsBC,GAClB,IACI,MAAMC,EAAO1yC,KAAKsL,MAAMmnC,GASxB,GAAIC,GAAwB,iBAATA,EAAmB,CAClC,MAAMr4C,EAAOq4C,EAAK5G,GAElB,QAAoB,IAATzxC,EACP,OAAOq4C,EAGXrhC,EAAOhZ,MAAM,yDACM,UAAWgC,IAEpC,MAAO5N,GAGL,OAFA4kB,EAAOjZ,MAAM3L,IAEN,EAGX,OAAO,EAUXqiD,kBAAkB52C,GACd,MAAMnO,EAAOmO,EAAIlC,aAAa,QAE9B,GAAMjM,IAASqJ,KAAKm7C,8BACbxkD,IAASqJ,KAAKo7C,mCACjB,OAAO,EAGX,MAAMmE,EAAc9M,EAAE3tC,GAAKmsB,KAAK,iBAC3BtvB,OACC69C,EAAax/C,KAAKo/C,sBAAsBG,GAgB9C,OAdIC,GACuC,iBAApCA,EAAW9G,IACX8G,EAAWC,OACdz/C,KAAKwf,aAAa0D,KACdy3B,IAAWpoD,uBAAwBitD,EAAWC,OAGlDD,GACuC,wBAApCA,EAAW9G,IACX8G,EAAWE,mBACd1/C,KAAKwf,aAAa0D,KACdy3B,IAAWnoD,8BAA+BgtD,EAAWE,oBAGtD,M,gECtwBR,SAAS/c,EAAqB8O,GACjC,IAAIkO,EAAYlO,EAMhB,OAJIA,GAAUl4B,OAAOqmC,mBACjBD,EAAY,GAGTA,EAAY,EAShB,SAASE,EAAiBC,GAC7B,OAAOA,EAAWvpD,OAAS,EAAIupD,EAAWp6C,OAAO,CAAChO,EAAGC,IAAMD,EAAIC,GAAKmoD,EAAWvpD,OAAS,EA6BrF,SAASwpD,EAAqBD,GACjC,OAAOA,EAAWpvB,OAAOnjC,GAASA,GAAS,GAvD/C,wIA8DO,MAAMyyD,EAIT/pD,cACI+J,KAAKigD,QAAU,EACfjgD,KAAKjS,EAAI,EASbmyD,QAAQ3yD,GACiB,iBAAVA,IAGXyS,KAAKjS,GAAK,EACViS,KAAKigD,QAAUjgD,KAAKigD,SAAY1yD,EAAQyS,KAAKigD,SAAWjgD,KAAKjS,GAOjEoyD,aACI,OAAOngD,KAAKigD,W,8BC3FpB,iDAGA,MAAMhiC,EAASF,oBAAUM,GAIzB,IAAI+hC,EAEAC,EA+GJ,SAASC,IACL,OAAUl1C,KAAKC,SAAS3U,SAAS,IAAzB,aAAwC0e,OAAO,EAAG,GA3G/C,KAKX0gC,SAAUU,IAQV5rC,KAAK21C,GACDvgD,KAAK81C,SAAWyK,GAAmB/J,KAOvC,wBASI,OARK4J,IACDA,EAAqBpgD,KAAK81C,SAAS39B,QAAQ,qBACtCioC,IACDA,EAmDhB,WACI,MAAMI,EAAWC,IAAkBC,mBAInC,OAFAziC,EAAOrZ,IAAI,0BAA2B47C,GAE/BA,EAxD0BG,GACrB3gD,KAAK81C,SAAShpC,QAAQ,oBAAqBszC,KAI5CA,GAOX,gBASI,OARKC,IACDA,EAAargD,KAAK81C,SAAS39B,QAAQ,eAC9BkoC,IACDA,EA+ChB,WACI,MAAMO,EAWCN,IAAQA,IAAQA,IAAQA,IAP/B,OAFAriC,EAAOrZ,IAAI,eAAgBg8C,GAEpBA,EApDkBC,GACb7gD,KAAK81C,SAAShpC,QAAQ,cAAeuzC,KAItCA,GAOX,gBAGI,OAAOrgD,KAAK81C,SAAS39B,QAAQ,cAOjC,cAAco6B,GACNA,EACAvyC,KAAK81C,SAAShpC,QAAQ,YAAaylC,GAEnCvyC,KAAK81C,SAAS/oC,WAAW,iB,wECjFrC,kHAOO,SAAS+zC,EAAiB5vB,GAC7B,OAAO5Y,SAAS4Y,EAAMV,MAAMztB,MAAM,KAAK,GAAI,IAQxC,SAASg+C,EAAmB7vB,GAC/B,OAAO5Y,SAAS4Y,EAAMV,MAAMztB,MAAM,KAAK,GAAI,IAQ/C,SAASi+C,EAAcvvB,GACnB,OAAKA,EAAMjB,MAIJiB,EAAMjB,MACRhrB,IAAIirB,GAAYA,EAASvpB,IACzBwpB,OAAO,CAAC1L,EAAM2L,EAAOC,IAAUA,EAAMhtB,QAAQohB,KAAU2L,GACvDp6B,OANM,EAaf,MAAM0qD,EAOFhrD,YAAYw7B,GACR,IAAKA,EACD,MAAM,IAAIp6B,MAAM,sBAGpB2I,KAAKyxB,MAAQA,EAUjB,YAKI,OAJKzxB,KAAKyxB,MAAMjB,QACZxwB,KAAKyxB,MAAMjB,MAAQ,IAGhBxwB,KAAKyxB,MAAMjB,MAStB,UAAUA,GACNxwB,KAAKyxB,MAAMjB,MAAQA,EAOvB,gBACI,OAAOxwB,KAAKyxB,MAAM9B,UAOtB,cAAcA,GACV3vB,KAAKyxB,MAAM9B,UAAYA,EAO3B,iBAKI,OAJK3vB,KAAKyxB,MAAMX,aACZ9wB,KAAKyxB,MAAMX,WAAa,IAGrB9wB,KAAKyxB,MAAMX,WAQtB,eAAeA,GACX9wB,KAAKyxB,MAAMX,WAAaA,EAW5BowB,iBAAiBC,EAAYC,GACzB,MAAM9jD,EAAY0C,KAAKwwB,MAAMS,KACzBowB,GAAWA,EAAQn6C,KAAOi6C,GACvBE,EAAQ/jD,YAAc8jD,GAG7B,OAAO9jD,GAAaA,EAAU/P,MAQlC+zD,WAAWC,GACFvhD,KAAKyxB,MAAMjB,OAAUxwB,KAAKyxB,MAAMjB,MAAMj6B,SAI3CyJ,KAAKyxB,MAAMjB,MACLxwB,KAAKyxB,MAAMjB,MAAME,OAAO2wB,GAAWA,EAAQn6C,KAAOq6C,IAQ5DC,iBAAiBH,GACbrhD,KAAKwwB,MAAMttB,KAAKm+C,GAWpBI,UAAUtwB,EAAWX,GACjB,OAAOxwB,KAAK8wB,WAAWG,KACnBC,GACIA,EAAMC,YAAcA,KACXX,GAASA,IAAUU,EAAMV,QAS9CkxB,WAAWvwB,GACP,OAAOnxB,KAAK8wB,WAAWJ,OACnBQ,GAASA,EAAMC,YAAcA,GASrCwwB,uBAAuBxwB,EAAWywB,GAC9B,OAAO5hD,KAAK8wB,WAAWG,KACnBC,GAASA,EAAMC,YAAcA,GACtB2vB,EAAiB5vB,KAAW0wB,GAS3CC,eAAeC,GACX,OAAO9hD,KAAKwwB,MAAMS,KACdowB,GAAiC,SAAtBA,EAAQ/jD,YACF,OAATwkD,GAAiBT,EAAQ9zD,QAAUu0D,IAOnDC,eACI,OAAOf,EAAchhD,KAAKyxB,OAQ9BuwB,wBACI,YAAiCzhD,IAA1BP,KAAKyxB,MAAMX,WAQtBmxB,sBACI,MAAMt2B,EAAY3rB,KAAKyxB,MAAMxqB,KAE7B,GAAkB,UAAd0kB,EACA,MAAM,IAAIt0B,MACL,qCAAoCs0B,MAK7C,GAAiB,IAFAq1B,EAAchhD,KAAKyxB,OAIhC,OAAOzxB,KAAKyxB,MAAMjB,MAAM,GAAGtpB,GAI/B,GAAIlH,KAAKyxB,MAAMX,WAAY,CACvB,MAAMM,EAAWpxB,KAAKyhD,UAAU,OAEhC,GAAIrwB,EACA,OAAO0vB,EAAiB1vB,GAE5B,MAAMJ,EAAWhxB,KAAKyhD,UAAU,OAEhC,GAAIzwB,EACA,OAAO8vB,EAAiB9vB,GAE5B,MAAMkxB,EAAWliD,KAAKyhD,UAAU,UAEhC,GAAIS,EACA,OAAOpB,EAAiBoB,IAcpCC,WAAWpxB,GACP,MAAMC,EAAWhxB,KAAK2hD,uBAAuB,MAAO5wB,GAGpD,OAAOC,GAAY+vB,EAAmB/vB,GAO1CoxB,WACI,OAAOpiD,KAAKwwB,MACPhrB,IAAIirB,GAAYA,EAASvpB,IACzBwpB,OAAO,CAAC1L,EAAM2L,EAAOC,IAAUA,EAAMhtB,QAAQohB,KAAU2L,GAQhE0xB,uBACI,MAAM12B,EAAY3rB,KAAKyxB,MAAMxqB,KAE7B,GAAkB,UAAd0kB,EACA,MAAM,IAAIt0B,MACL,0CAAyCs0B,GAGlD,MAAM22B,EAAatiD,KAAKoiD,WAExB,IAAK,MAAMG,KAAiBviD,KAAK8wB,WAI7B,GAAgC,QAA5ByxB,EAAcpxB,WACqB,WAA5BoxB,EAAcpxB,UAAwB,CAE7C,MAAMqxB,EAAgBzB,EAAmBwB,GAEzCD,EAAWr+C,OACPq+C,EAAW1+C,QAAQ4+C,GAAgB,GAI/C,OAAOF,EAMXG,iBACI,OAAO71C,KAAKwL,UAAUpY,KAAKyxB,MAAMX,YAQrC4xB,qBAAqB19B,GACZhlB,KAAKyxB,MAAMX,aAIhB9wB,KAAKyxB,MAAMX,WAAa9wB,KAAKyxB,MAAMX,WAC9BJ,OAAOiyB,IAAqD,IAAxCA,EAAUnyB,MAAM5sB,QAAS,GAAEohB,KAOxD49B,wBAAwBzxB,GACfnxB,KAAKyxB,MAAMX,aAIhB9wB,KAAKyxB,MAAMX,WACL9wB,KAAKyxB,MAAMX,WACRJ,OAAOiyB,GAAaA,EAAUxxB,YAAcA,IAQzD0xB,YAAYC,EAASC,GACb/iD,KAAKyxB,MAAMjB,OACXxwB,KAAKyxB,MAAMjB,MAAMphB,QAAQqhB,IACjBA,EAASvpB,KAAO47C,IAChBryB,EAASvpB,GAAK67C,KAW9BC,aAAa9xB,GACTlxB,KAAK8wB,WAAW5tB,KAAKguB,IAoBtB,MAAM+xB,EAOThtD,YAAYitD,GACRljD,KAAKmjD,UAAYC,QAAgBF,GAYrCG,YAAY13B,GACR,MAAM23B,EACAtjD,KAAKmjD,UAAU11B,MAAMwD,KAAKQ,GAASA,EAAMxqB,OAAS0kB,GAExD,OAAO23B,EAAgB,IAAIrC,EAAUqC,GAAiB,KAQ1DC,WACI,OAAOH,QAAgBpjD,KAAKmjD,c,6BC/apC,gHAGO,MAAMK,EAAsB,yBAMtBC,EAAuB,2B,6BCTpC,sGAMO,MAAMC,EAAU,UAQVC,EAAS,SAOTC,EAAQ,S,6BCrBrB,kCAIO,MAAMC,EAAU,CACnBC,OAAQ,iBACRxZ,iBAAkB,+BAClByZ,SAAU,mBACVC,UAAW,oBACXnxD,eAAgB,iBAChBoxD,YAAa,gB,cCyDjBl4D,EAAOD,QAnEa,CAChB,KAAQ,CACJyqC,MAAO,KACPrC,OAAQ,KACRgwB,MAAO,GAEX,OAAU,CACN3tB,MAAO,KACPrC,OAAQ,KACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,KACPrC,OAAQ,IACRgwB,MAAO,GAEX,GAAM,CACF3tB,MAAO,KACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAIX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAIX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,GAEX,IAAO,CACH3tB,MAAO,IACPrC,OAAQ,IACRgwB,MAAO,K,8BC/Df,0EAKA,MAAMjmC,EAASb,EAAQ,GAAqBW,UAAUM,GAQhD8lC,EACW,cADXA,EAEY,eAFZA,EAGmB,sBAHnBA,EAIoB,uBAJpBA,EAKe,kBALfA,EAMY,eANZA,EAOoB,uBAPpBA,EASc,iBASdC,EACU,aADVA,EAEY,eAFZA,EAGS,YAHTA,EAIW,cAJXA,EAKU,aALVA,EAMW,cANXA,EAUgB,mBAVhBA,EAWe,kBAXfA,EAYe,kBAZfA,EAagB,mBAahBC,EACK,QADLA,EAEK,QAFLA,EAGe,gBAOrB,IAAIC,EAcW,MAAM7lC,EAOjB,6BAA6BzZ,EAAOF,GAC5B2Z,EAAU8lC,SAAqB,YAAVv/C,GACrBiZ,EAAOjZ,MAAO,sBAAqBA,UAAcF,KAUzD,qBAAqBE,EAAOF,GAIxB,GAHAmZ,EAAOrZ,IAAK,yBAAwBI,SAAaF,KAGnC,YAAVE,EACA,OAGJyZ,EAAU+lC,oBAAqB,EAG/B,IAAIC,GAAmB,EACnBC,EAAkB,KAEtB,IAAK,MAAMrgC,KAAqB5F,EAAUkmC,QAAQvhC,SACzCiB,EAAkBugC,YACnB3mC,EAAOhZ,MAAM,+BACTof,EAAkBwgC,kBAClBJ,GAAmB,EACdC,IACDA,EAAkBrgC,KAM7BogC,GAILhmC,EAAUqmC,kBAAkBJ,GAShC,yBAAyBK,GAIrB,MAAMC,EAAgBD,EAAW7lC,OAC3B+lC,EAAYF,EAAW/jC,eAG7B,IAAK,MAAMkkC,KAAUzmC,EAAU0mC,aAC3B,GAAID,EAAOj+C,OAASo9C,EAAkB,CAClC,MAAMe,EAAYF,EAAOvrD,KAEzB8kB,EAAU4mC,aACNN,EACAK,EAAUn+C,KACVm+C,EAAUpgD,MACVogD,EAAUE,IAAML,QACjB,GAAIC,EAAOj+C,OAASo9C,EAAkB,CAIzC,MAAMkB,EAAYL,EAAOvrD,KAEzB8kB,EAAU8lC,QAAQiB,gBACdN,EAAOI,IAAML,EACbM,EAAUl+B,MACV29B,EACAO,EAAUA,gBACX,GAAIL,EAAOj+C,OAASo9C,EAA4B,CACnD,MAAM1qD,EAAOurD,EAAOvrD,KAEpB8kB,EAAU8lC,QAAQkB,uBACdP,EAAOI,IAAML,EACbtrD,EAAK+rD,YACLV,EACArrD,EAAKqrB,KACLrrB,EAAK8rB,WACL9rB,EAAK+rB,aAIjBjH,EAAU0mC,aAAa5uD,OAAS,EAapC,oBAAoB6tB,EAAInd,EAAMjC,EAAOsgD,GACjC,IAAIK,EAAS3gD,EAER2gD,IACD1nC,EAAO9Y,KAAK,uBACZwgD,EAAS,IAAItuD,MAAM,kBAEnBonB,EAAU+lC,oBAAsBpgC,EAChC3F,EAAU8lC,QAAQqB,YAAYN,EAAIlhC,EAAGlF,OAAQjY,EAAM0+C,GAEnDlnC,EAAU0mC,aAAajiD,KAAK,CACxB+D,KAAMo9C,EACN1qD,KAAM,CACFqL,MAAO2gD,EACPL,KACAr+C,UAkBhB,oBAAoBmd,EAAIiD,EAAOk+B,GAC3B,MAAMD,EAAKlhC,GAAMA,EAAGpD,eACd9B,EAASkF,GAAMA,EAAGlF,OAEpBT,EAAU+lC,oBAAsBpgC,EAChC3F,EAAU8lC,QAAQiB,gBAAgBF,EAAIj+B,EAAOnI,EAAQqmC,GAErD9mC,EAAU0mC,aAAajiD,KAAK,CACxBgc,SACAomC,KACAr+C,KAAMo9C,EACN1qD,KAAM,CAAE0tB,QACJk+B,eAahB,kCAAkCM,GAC9B,MAAMC,EAAkB,CACpB,yBACA,kBACA,oBAKJ,IAAK,MAAMC,KAAcD,EAAiB,CACtC,MAAME,EAAiBH,EAAWE,GAElCF,EAAWE,GAAc,YAAYE,GACjC,IACI,OAAOD,EAAepkB,MAAMikB,EAAYI,GAC1C,MAAO5sD,GACLu+B,IAAqBC,iBAAiBx+B,KAIlD,MAAM6sD,EAAe,CACjB,yBACA,kBACA,oBAKJ,IAAK,MAAMH,KAAcG,EAAc,CACnC,MAAMF,EAAiBH,EAAWE,GAElCF,EAAWE,GAAc,YAAYE,GACjChoC,EAAOhZ,MAAM8gD,EAAYE,GACzBD,EAAepkB,MAAMikB,EAAYI,IAGzC,MAAME,EAAsBN,EAAWD,YAGvCC,EAAWD,YAAc,SAASN,EAAIlhC,EAAInd,KAASk7B,GAI3Cl7B,IAASk9C,EAQJrkC,IAAQC,iBACThb,SAAWA,QAAQE,MAAM,cAAeqgD,EAAIlhC,EAAInd,GAGpDgX,EAAOhZ,MAAM,cAAeqgD,EAAIlhC,EAAInd,KAASk7B,GAEjD,IACIgkB,EAAoB15D,KAAKo5D,EAAYP,EAAIlhC,EAAInd,KAASk7B,GACxD,MAAOikB,GACDn/C,IAASk9C,EACTp/C,SAAWA,QAAQC,MAAM,cAAeohD,GAExCxuB,IAAqBC,iBAAiBuuB,KAatD,qBAKI,OAJK9B,IACDA,EAAW,IAAIxjC,KAGZwjC,EAeX,mBAAmBn9C,GACf,GAAIsX,EAAU8lC,QACV,MAAM,IAAIltD,MAAM,mDAEpB,IACI,MAAMgvD,EAAmBC,UAWzB,IAAIC,EAUJ,GAnBA9nC,EAAU8lC,QAAU,IAAI8B,EACxB5nC,EAAU+nC,2BAA2B/nC,EAAU8lC,SAC/C9lC,EAAUwN,OAAS,CACflN,UAAW5X,EAAQ4X,UACnBD,SAAU3X,EAAQ2X,UAEtBL,EAAUG,YAAczX,EAAQyX,YAChCH,EAAUI,gBAAkB1X,EAAQ0X,gBAIhC1X,EAAQ6X,kBACRunC,EAAe,CACXE,mBACK,GAAEt/C,EAAQ6X,oBACPc,IAAQm1B,eAIpB9tC,EAAQ+X,OAAQ,CAEhB,MAAM5L,EAAQnM,EAAQ+X,OAAO5L,MAAM,gBAGnCizC,EAAapnC,OAAShY,EAAQgY,QAAW7L,GAASA,EAAM,IAAO,IAInEmL,EAAU8lC,QAAQmC,WACdjoC,EAAUG,YACVH,EAAUI,gBACVJ,EAAUwN,OACVxN,EAAUkoC,mBACVpmD,EACAgmD,GAEJ,MAAMtnC,EAAqB9X,EAAQ8X,mBAcnC,OAZIA,IACAR,EAAU8lC,QAAQqC,uBAAuB3nC,GAEzCA,IAAqByb,KAAKj1B,IAClBA,GACAwY,EAAO/Y,KAAK,2BACN0H,KAAKsL,MAAMzS,GAAQohD,aAGhCzpB,MAAM,UAGJ,EACT,MAAO/jC,GAQL,OAJAu+B,IAAqBC,iBAAiBx+B,GACtColB,EAAU8lC,QAAU,KACpBtmC,EAAOjZ,MAAM3L,IAEN,GAWf,8BACI,OAAOs/B,QAAQla,EAAU8lC,SAS7B,iCAAiCn/B,EAAahB,GAC1C3F,EAAUqoC,aAAa1iC,EAAIggC,EAA8Bh/B,GAS7D,0BAA0B/rB,EAAG+qB,GACzB,IACI3F,EAAU4mC,aACNjhC,EACA+/B,EACA9qD,EACA+qB,GAAMA,EAAGpD,gBACf,MAAOhc,GAIDD,SAAqC,mBAAlBA,QAAQC,OAE3BD,QAAQC,MAAM,4BAA6BA,IAcvD,oBAAoB+hD,EAAclgC,EAASC,GACvC,OAAO,IAAIkW,QAAQ,CAACC,EAASC,KACzB,GAAIze,EAAU8lC,QACV9lC,EAAU8lC,QAAQyC,iBACdD,EACA,CACI96B,OAAQxN,EAAUwN,OAClBpF,UACAC,WAEJ,CAAC/W,EAAQtL,KACU,YAAXsL,EACAktB,EAAQx4B,GAERy4B,EAAOz4B,SAGhB,CACH,MAAM+K,EAAS,sDAEfyO,EAAOjZ,MAAMwK,GACb0tB,EAAO1tB,MAWnB,8BAA8BnW,EAAG+qB,GAC7B3F,EAAU4mC,aAAajhC,EAAI+/B,EAA4B9qD,EAAG,MAS9D,qBAAqBwuC,EAAM5gC,EAAMmd,GAC7B,IAAIiD,EAGAA,EADS,UAATpgB,EACQ4gC,EAAOuc,EAAyBA,EAEhCvc,EAAOuc,EAAwBA,EAG3C3lC,EAAUqoC,aAAa1iC,EAAIiD,GAe/BpxB,YAAYqtB,EAAKnc,GACbnH,KAAKkf,OAAS/X,EAAQ+X,OACtBlf,KAAKsjB,IAAMA,EACXtjB,KAAKghB,eAAiBsC,EAAItC,eAC1BhhB,KAAK8jB,aAAe3c,EAAQ2c,cA1eR,QA2epB9jB,KAAK4kD,WAAY,EAEjBnmC,EAAUkmC,QAAQpkC,IAAIvgB,MAElBye,EAAU+lC,qBACVxkD,KAAK6kD,gBAK0B,IAA3BpmC,EAAUkmC,QAAQ1hC,MAClBxE,EAAUqmC,kBAAkB9kD,OAUxC6kD,gBACI5mC,EAAO/Y,KAAK,eAAgBlF,KAAK8jB,cACjC,IACI,MAAMmjC,EAAmB,CACrBC,mBACIlnD,KAAKsjB,IAAIwiB,MACHrnB,EAAU8lC,QAAQ4C,aAAaC,KAC/B3oC,EAAU8lC,QAAQ4C,aAAaE,QAEvC/Z,EACA7uB,EAAU8lC,QAAQ+C,aAChBtnD,KAAKghB,eACLhhB,KAAK8jB,aACLrF,EAAU8lC,QAAQgD,YAAYC,UAC9BxnD,KAAKkf,OACL+nC,EACAxoC,EAAUgpC,uBAElBznD,KAAK4kD,WAAY,EAEjB,MAAM8C,EAAyB,YAAfpa,EAAIv9B,OAMpB,OAJK23C,GACDzpC,EAAOjZ,MAAM,kCAAmCsoC,EAAI7oC,SAGjDijD,EAET,MAAO1iD,GAGL,OAFA4yB,IAAqBC,iBAAiB7yB,IAE/B,GAoBfsgB,4BACQN,EACAO,EACAoiC,EACAliC,EACAC,GACJ,IAAKjH,EAAU8lC,QACX,OAGJ,MAAMmB,EAAcngC,EAAU9G,EAAUwN,OAAS07B,EAE7ClpC,EAAU+lC,mBACV/lC,EAAU8lC,QAAQkB,uBACdzlD,KAAKghB,eACL0kC,EACA1lD,KAAKkf,OACL8F,EACAS,EACAC,GAEJjH,EAAU0mC,aAAajiD,KAAK,CACxB+D,KAAMo9C,EACNiB,GAAItlD,KAAKghB,eACTrnB,KAAM,CACF+rD,cACAhgC,cACAV,OACAS,gBAYhBR,2BACIxG,EAAUqoC,aAAa9mD,KAAMokD,GAOjC9/B,qBACQ7F,EAAU+lC,oBACV/lC,EAAU8lC,QAAQiB,gBACdxlD,KAAKghB,eACLvC,EAAU8lC,QAAQH,YAAYwD,iBAC9B5nD,KAAKkf,QAEbT,EAAUkmC,QAAQjhC,OAAO1jB,MAM7B4kB,+BACInG,EAAU4mC,aACNrlD,KACAmkD,EACA,KACAnkD,KAAKghB,gBAQbiF,sBAAsB5sB,GAClBolB,EAAU4mC,aACNrlD,KAAMmkD,EAA2B9qD,EAAG2G,KAAKghB,gBAQjDkF,uBAAuB7sB,GACnBolB,EAAU4mC,aACNrlD,KAAMmkD,EAA4B9qD,EAAG2G,KAAKghB,gBAQlD2D,sBAAsBF,GAClBhG,EAAUqoC,aACN9mD,KACAykB,EAAW2/B,EAA2BA,GAU9Cr/B,uBAAuBhI,EAAOiI,GAC1B,IAAIugC,EAEAvgC,IACAugC,EAAY,CAAEvgC,SAGlBvG,EAAUqoC,aACN9mD,KACA+c,EAAQqnC,EAA+BA,EACvCmB,GAQRp/B,uBAAuB9sB,GACnBolB,EAAU4mC,aACNrlD,KAAMmkD,EAAmC9qD,EAAG2G,KAAKghB,gBAQzDoF,wBAAwB/sB,GACpBolB,EAAU4mC,aACNrlD,KAAMmkD,EAAoC9qD,EAAG2G,KAAKghB,gBAQ1DqF,0BAA0BhtB,GACtBolB,EAAU4mC,aACNrlD,KAAMmkD,EAA+B9qD,EAAG2G,KAAKghB,iBAQzDvC,EAAU8lC,QAAU,KAKpB9lC,EAAU0mC,aAAe,GAOzB1mC,EAAU+lC,oBAAqB,EAM/B/lC,EAAUG,YAAc,KAMxBH,EAAUI,gBAAkB,KAS5BJ,EAAUwN,OAAS,O,4DC3wBnBlgC,EAAOD,QAlBe,CAIlBunC,KAAM,OAKNw0B,IAAK,MAKLC,IAAK,Q,cCDT,SAASv2B,EAAU9W,EAAK2Z,GACpB,OAAOhpB,KAAKmM,MAAMnM,KAAKC,UAAY+oB,EAAM3Z,EAAM,IAAMA,EAQzD,SAASstC,EAAc5xD,GACnB,OAAOA,EAAIo7B,EAAU,EAAGp7B,EAAII,OAAS,IAqBzC,MAAM+6B,EAAa,CAKf02B,eAAc,IACHD,EA7CI,oBAoDf5K,gBAAgBhlD,GACZ,IAAIm1C,EAAM,GAEV,KAAOn1C,KACHm1C,GAAOttC,KAAKgoD,iBAGhB,OAAO1a,GAEXya,gBACAE,kBApCJ,SAA2B1xD,GACvB,IAAIkP,EAAS,GAEb,IAAK,IAAInZ,EAAI,EAAGA,EAAIiK,EAAQjK,GAAK,EAC7BmZ,GAAUsiD,EApCZ,kEAuCF,OAAOtiD,GA8BP8rB,aAGJxlC,EAAOD,QAAUwlC,G,6BC5EjB,8CAOA,SAAS42B,EAA8BC,EAAO,SAI1C,OAAO,cAAcA,EAIjBlyD,eAAeksC,GACX9H,SAAS8H,GACTniC,KAAKuU,WAAa,KAOtB3J,KAAK2J,GACDvU,KAAKuU,WAAaA,IAQf2zC,QAKR,MAAME,EACPF,EAA8B9tB,M,6BCvCpC,2MAeO,MAAMiuB,EAA2B,0BAK3BC,EAAc,wBAKdj3D,EAAoB,8BAMpBk3D,EAAe,0B,6BC/B5B,oEAMO,MAAMC,EAAqB,sBAOrBC,EAA0B,2B,iBCbvC,cAUA,IASIC,EAAU,qBAKVC,EAAU,oBACVC,EAAS,6BACTC,EAAS,eAKTC,EAAS,eAwBTC,EAAU,OAGVC,EAAe,8BAGfC,EAAW,mBAGXC,EAAgB,GACpBA,EAAcR,GAAWQ,EA7CV,kBA8CfA,EA9BqB,wBA8BWA,EA7Bd,qBA8BlBA,EA9Cc,oBA8CWA,EA7CX,iBA8CdA,EA9BiB,yBA8BWA,EA7BX,yBA8BjBA,EA7Bc,sBA6BWA,EA5BV,uBA6BfA,EA5Be,uBA4BWA,EAAcL,GACxCK,EA5CgB,mBA4CWA,EA3CX,mBA4ChBA,EA1CgB,mBA0CWA,EAAcJ,GACzCI,EAzCgB,mBAyCWA,EAxCX,mBAyChBA,EA/Be,uBA+BWA,EA9BJ,8BA+BtBA,EA9BgB,wBA8BWA,EA7BX,yBA6BsC,EACtDA,EArDe,kBAqDWA,EAAcP,GACxCO,EA3CiB,qBA2CW,EAG5B,IAAIC,EAA8B,iBAAVvzD,GAAsBA,GAAUA,EAAO5I,SAAWA,QAAU4I,EAGhFwzD,EAA0B,iBAARvzD,MAAoBA,MAAQA,KAAK7I,SAAWA,QAAU6I,KAGxEjK,EAAOu9D,GAAcC,GAAY/d,SAAS,cAATA,GAGjCge,EAA4Cv9D,IAAYA,EAAQiU,UAAYjU,EAG5Ew9D,EAAaD,GAAgC,iBAAVt9D,GAAsBA,IAAWA,EAAOgU,UAAYhU,EAGvFw9D,EAAgBD,GAAcA,EAAWx9D,UAAYu9D,EAUzD,SAASG,EAAYhkD,EAAKikD,GAGxB,OADAjkD,EAAI4b,IAAIqoC,EAAK,GAAIA,EAAK,IACfjkD,EAWT,SAASkkD,EAAYtoC,EAAK7zB,GAGxB,OADA6zB,EAAIb,IAAIhzB,GACD6zB,EAuDT,SAASuoC,EAAY/4B,EAAOg5B,EAAUC,EAAaC,GACjD,IAAIn5B,GAAS,EACTp6B,EAASq6B,EAAQA,EAAMr6B,OAAS,EAKpC,IAHIuzD,GAAavzD,IACfszD,EAAcj5B,IAAQD,MAEfA,EAAQp6B,GACfszD,EAAcD,EAASC,EAAaj5B,EAAMD,GAAQA,EAAOC,GAE3D,OAAOi5B,EAyCT,SAASE,EAAax8D,GAGpB,IAAIkY,GAAS,EACb,GAAa,MAATlY,GAA0C,mBAAlBA,EAAMmJ,SAChC,IACE+O,KAAYlY,EAAQ,IACpB,MAAO8L,IAEX,OAAOoM,EAUT,SAASukD,EAAWxkD,GAClB,IAAImrB,GAAS,EACTlrB,EAASrP,MAAMoP,EAAIyd,MAKvB,OAHAzd,EAAI4J,SAAQ,SAAS7hB,EAAOM,GAC1B4X,IAASkrB,GAAS,CAAC9iC,EAAKN,MAEnBkY,EAWT,SAASwkD,EAAQrqD,EAAMwjD,GACrB,OAAO,SAAShiD,GACd,OAAOxB,EAAKwjD,EAAUhiD,KAW1B,SAAS8oD,EAAW9oC,GAClB,IAAIuP,GAAS,EACTlrB,EAASrP,MAAMgrB,EAAI6B,MAKvB,OAHA7B,EAAIhS,SAAQ,SAAS7hB,GACnBkY,IAASkrB,GAASpjC,KAEbkY,EAIT,IASM0kD,EATFC,EAAah0D,MAAMlI,UACnBm8D,EAAYhf,SAASn9C,UACrBo8D,EAAct9D,OAAOkB,UAGrBq8D,EAAa3+D,EAAK,sBAGlB4+D,GACEL,EAAM,SAASM,KAAKF,GAAcA,EAAWhlD,MAAQglD,EAAWhlD,KAAKmlD,UAAY,KACvE,iBAAmBP,EAAO,GAItCQ,EAAeN,EAAU3zD,SAGzBvI,EAAiBm8D,EAAYn8D,eAO7By8D,EAAiBN,EAAY5zD,SAG7Bm0D,EAAaC,OAAO,IACtBH,EAAal+D,KAAK0B,GAAgByT,QAzQjB,sBAyQuC,QACvDA,QAAQ,yDAA0D,SAAW,KAI5EmpD,EAASxB,EAAgB39D,EAAKm/D,YAASxqD,EACvClT,EAASzB,EAAKyB,OACd29D,EAAap/D,EAAKo/D,WAClBC,EAAehB,EAAQj9D,OAAO4gD,eAAgB5gD,QAC9Ck+D,EAAel+D,OAAOY,OACtBu9D,EAAuBb,EAAYa,qBACnClnD,EAASmmD,EAAWnmD,OAGpBmnD,EAAmBp+D,OAAOu+C,sBAC1B8f,EAAiBN,EAASA,EAAOO,cAAW/qD,EAC5CgrD,EAAatB,EAAQj9D,OAAOuY,KAAMvY,QAGlCw+D,EAAWC,GAAU7/D,EAAM,YAC3B2zB,EAAMksC,GAAU7/D,EAAM,OACtBoxC,EAAUyuB,GAAU7/D,EAAM,WAC1Bk1B,EAAM2qC,GAAU7/D,EAAM,OACtB8/D,EAAUD,GAAU7/D,EAAM,WAC1B+/D,EAAeF,GAAUz+D,OAAQ,UAGjC4+D,EAAqBC,GAASL,GAC9BM,EAAgBD,GAAStsC,GACzBwsC,EAAoBF,GAAS7uB,GAC7BgvB,GAAgBH,GAAS/qC,GACzBmrC,GAAoBJ,GAASH,GAG7BQ,GAAc7+D,EAASA,EAAOa,eAAYqS,EAC1C4rD,GAAgBD,GAAcA,GAAYE,aAAU7rD,EASxD,SAAS8rD,GAAKC,GACZ,IAAI37B,GAAS,EACTp6B,EAAS+1D,EAAUA,EAAQ/1D,OAAS,EAGxC,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KA2F7B,SAASC,GAAUF,GACjB,IAAI37B,GAAS,EACTp6B,EAAS+1D,EAAUA,EAAQ/1D,OAAS,EAGxC,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KAyG7B,SAASE,GAASH,GAChB,IAAI37B,GAAS,EACTp6B,EAAS+1D,EAAUA,EAAQ/1D,OAAS,EAGxC,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KAuF7B,SAASG,GAAMJ,GACbtsD,KAAK2sD,SAAW,IAAIH,GAAUF,GA4FhC,SAASM,GAAcr/D,EAAOs/D,GAG5B,IAAIpnD,EAAUpP,GAAQ9I,IAsrBxB,SAAqBA,GAEnB,OAmFF,SAA2BA,GACzB,OAmIF,SAAsBA,GACpB,QAASA,GAAyB,iBAATA,EApIlBu/D,CAAav/D,IAAUw/D,GAAYx/D,GApFnCy/D,CAAkBz/D,IAAUY,EAAe1B,KAAKc,EAAO,aAC1D49D,EAAqB1+D,KAAKc,EAAO,WAAaq9D,EAAen+D,KAAKc,IAAUm7D,GAzrBhDuE,CAAY1/D,GAljB9C,SAAmBQ,EAAG67D,GAIpB,IAHA,IAAIj5B,GAAS,EACTlrB,EAASrP,MAAMrI,KAEV4iC,EAAQ5iC,GACf0X,EAAOkrB,GAASi5B,EAASj5B,GAE3B,OAAOlrB,EA4iBHynD,CAAU3/D,EAAMgJ,OAAQuC,QACxB,GAEAvC,EAASkP,EAAOlP,OAChB42D,IAAgB52D,EAEpB,IAAK,IAAI1I,KAAON,GACTs/D,IAAa1+D,EAAe1B,KAAKc,EAAOM,IACvCs/D,IAAuB,UAAPt/D,GAAmBu/D,GAAQv/D,EAAK0I,KACpDkP,EAAOvC,KAAKrV,GAGhB,OAAO4X,EAaT,SAAS4nD,GAAYr/D,EAAQH,EAAKN,GAChC,IAAI+/D,EAAWt/D,EAAOH,GAChBM,EAAe1B,KAAKuB,EAAQH,IAAQ0/D,GAAGD,EAAU//D,UACxCgT,IAAVhT,GAAyBM,KAAOG,KACnCA,EAAOH,GAAON,GAYlB,SAASigE,GAAa58B,EAAO/iC,GAE3B,IADA,IAAI0I,EAASq6B,EAAMr6B,OACZA,KACL,GAAIg3D,GAAG38B,EAAMr6B,GAAQ,GAAI1I,GACvB,OAAO0I,EAGX,OAAQ,EA8BV,SAASk3D,GAAUlgE,EAAOmgE,EAAQC,EAAQC,EAAY//D,EAAKG,EAAQoW,GACjE,IAAIqB,EAIJ,GAHImoD,IACFnoD,EAASzX,EAAS4/D,EAAWrgE,EAAOM,EAAKG,EAAQoW,GAASwpD,EAAWrgE,SAExDgT,IAAXkF,EACF,OAAOA,EAET,IAAKooD,GAAStgE,GACZ,OAAOA,EAET,IAAIugE,EAAQz3D,GAAQ9I,GACpB,GAAIugE,GAEF,GADAroD,EA2XJ,SAAwBmrB,GACtB,IAAIr6B,EAASq6B,EAAMr6B,OACfkP,EAASmrB,EAAM36B,YAAYM,GAG3BA,GAA6B,iBAAZq6B,EAAM,IAAkBziC,EAAe1B,KAAKmkC,EAAO,WACtEnrB,EAAOkrB,MAAQC,EAAMD,MACrBlrB,EAAOsoD,MAAQn9B,EAAMm9B,OAEvB,OAAOtoD,EApYIuoD,CAAezgE,IACnBmgE,EACH,OA6ON,SAAmB3iC,EAAQ6F,GACzB,IAAID,GAAS,EACTp6B,EAASw0B,EAAOx0B,OAEpBq6B,IAAUA,EAAQx6B,MAAMG,IACxB,OAASo6B,EAAQp6B,GACfq6B,EAAMD,GAAS5F,EAAO4F,GAExB,OAAOC,EArPIq9B,CAAU1gE,EAAOkY,OAErB,CACL,IAAIrI,EAAM8wD,GAAO3gE,GACb4gE,EAAS/wD,GAAOurD,GAAWvrD,GAAOwrD,EAEtC,GAAI0C,GAAS/9D,GACX,OA0HN,SAAqB6gE,EAAQV,GAC3B,GAAIA,EACF,OAAOU,EAAOl0B,QAEhB,IAAIz0B,EAAS,IAAI2oD,EAAOn4D,YAAYm4D,EAAO73D,QAE3C,OADA63D,EAAO1gB,KAAKjoC,GACLA,EAhII4oD,CAAY9gE,EAAOmgE,GAE5B,GAp0BY,mBAo0BRtwD,GAAoBA,GAAOsrD,GAAYyF,IAAWngE,EAAS,CAC7D,GAAI+7D,EAAax8D,GACf,OAAOS,EAAST,EAAQ,GAG1B,GADAkY,EA+XN,SAAyBzX,GACvB,MAAqC,mBAAtBA,EAAOiI,aAA8Bq4D,GAAYtgE,GAE5D,IAzVcya,EAwVHwiD,EAAaj9D,GAvVrB6/D,GAASplD,GAASyiD,EAAaziD,GAAS,IADjD,IAAoBA,EAzCL8lD,CAAgBJ,EAAS,GAAK5gE,IAClCmgE,EACH,OA6QR,SAAqB3iC,EAAQ/8B,GAC3B,OAAOwgE,GAAWzjC,EAAQ0jC,GAAW1jC,GAAS/8B,GA9QjC0gE,CAAYnhE,EAhD3B,SAAoBS,EAAQ+8B,GAC1B,OAAO/8B,GAAUwgE,GAAWzjC,EAAQxlB,GAAKwlB,GAAS/8B,GA+ClB2gE,CAAWlpD,EAAQlY,QAE1C,CACL,IAAK27D,EAAc9rD,GACjB,OAAOpP,EAAST,EAAQ,GAE1BkY,EA0YN,SAAwBzX,EAAQoP,EAAKwxD,EAAWlB,GAC9C,IAAImB,EAAO7gE,EAAOiI,YAClB,OAAQmH,GACN,IArtCiB,uBAstCf,OAAO0xD,GAAiB9gE,GAE1B,IAvuCU,mBAwuCV,IAvuCU,gBAwuCR,OAAO,IAAI6gE,GAAM7gE,GAEnB,IA3tCc,oBA4tCZ,OA3QN,SAAuB+gE,EAAUrB,GAC/B,IAAIU,EAASV,EAASoB,GAAiBC,EAASX,QAAUW,EAASX,OACnE,OAAO,IAAIW,EAAS94D,YAAYm4D,EAAQW,EAASC,WAAYD,EAASE,YAyQ3DC,CAAclhE,EAAQ0/D,GAE/B,IA7tCa,wBA6tCI,IA5tCJ,wBA6tCb,IA5tCU,qBA4tCI,IA3tCH,sBA2tCkB,IA1tClB,sBA2tCX,IA1tCW,sBA0tCI,IAztCG,6BAytCmB,IAxtCzB,uBAwtCyC,IAvtCzC,uBAwtCV,OA/MN,SAAyByB,EAAYzB,GACnC,IAAIU,EAASV,EAASoB,GAAiBK,EAAWf,QAAUe,EAAWf,OACvE,OAAO,IAAIe,EAAWl5D,YAAYm4D,EAAQe,EAAWH,WAAYG,EAAW54D,QA6MjE64D,CAAgBphE,EAAQ0/D,GAEjC,KAAK7E,EACH,OArQN,SAAkBrjD,EAAKkoD,EAAQkB,GAE7B,OAAOjF,EADK+D,EAASkB,EAAU5E,EAAWxkD,IAAM,GAAQwkD,EAAWxkD,GACzCgkD,EAAa,IAAIhkD,EAAIvP,aAmQpCo5D,CAASrhE,EAAQ0/D,EAAQkB,GAElC,IAhvCY,kBAivCZ,IA5uCY,kBA6uCV,OAAO,IAAIC,EAAK7gE,GAElB,IAjvCY,kBAkvCV,OAhQN,SAAqBshE,GACnB,IAAI7pD,EAAS,IAAI6pD,EAAOr5D,YAAYq5D,EAAOvkC,OAAQg+B,EAAQ0B,KAAK6E,IAEhE,OADA7pD,EAAO8pD,UAAYD,EAAOC,UACnB9pD,EA6PI+pD,CAAYxhE,GAErB,KAAK86D,EACH,OApPN,SAAkB1nC,EAAKssC,EAAQkB,GAE7B,OAAOjF,EADK+D,EAASkB,EAAU1E,EAAW9oC,IAAM,GAAQ8oC,EAAW9oC,GACzCsoC,EAAa,IAAItoC,EAAInrB,aAkPpCw5D,CAASzhE,EAAQ0/D,EAAQkB,GAElC,IApvCY,kBAqvCV,OA3Oec,EA2OI1hE,EA1OhBm+D,GAAgBn/D,OAAOm/D,GAAc1/D,KAAKijE,IAAW,GAD9D,IAAqBA,EA/LNC,CAAepiE,EAAO6P,EAAKqwD,GAAWC,IAInDtpD,IAAUA,EAAQ,IAAIsoD,IACtB,IAAIkD,EAAUxrD,EAAMjX,IAAII,GACxB,GAAIqiE,EACF,OAAOA,EAIT,GAFAxrD,EAAMgd,IAAI7zB,EAAOkY,IAEZqoD,EACH,IAAI+B,EAAQlC,EAsQhB,SAAoB3/D,GAClB,OAnOF,SAAwBA,EAAQ8hE,EAAUC,GACxC,IAAItqD,EAASqqD,EAAS9hE,GACtB,OAAOqI,GAAQrI,GAAUyX,EApwB3B,SAAmBmrB,EAAOxN,GAKxB,IAJA,IAAIuN,GAAS,EACTp6B,EAAS6sB,EAAO7sB,OAChBy5D,EAASp/B,EAAMr6B,SAEVo6B,EAAQp6B,GACfq6B,EAAMo/B,EAASr/B,GAASvN,EAAOuN,GAEjC,OAAOC,EA4vB2Bq/B,CAAUxqD,EAAQsqD,EAAY/hE,IAiOzDkiE,CAAeliE,EAAQuX,GAAMkpD,IAvQb0B,CAAW5iE,GAASgY,GAAKhY,GAUhD,OA5vBF,SAAmBqjC,EAAOg5B,GAIxB,IAHA,IAAIj5B,GAAS,EACTp6B,EAASq6B,EAAQA,EAAMr6B,OAAS,IAE3Bo6B,EAAQp6B,IAC8B,IAAzCqzD,EAASh5B,EAAMD,GAAQA,EAAOC,MA+uBpCw/B,CAAUP,GAAStiE,GAAO,SAAS8iE,EAAUxiE,GACvCgiE,IAEFQ,EAAW9iE,EADXM,EAAMwiE,IAIRhD,GAAY5nD,EAAQ5X,EAAK4/D,GAAU4C,EAAU3C,EAAQC,EAAQC,EAAY//D,EAAKN,EAAO6W,OAEhFqB,EAkDT,SAAS6qD,GAAa/iE,GACpB,SAAKsgE,GAAStgE,KAyYEqS,EAzYiBrS,EA0YxBi9D,GAAeA,KAAc5qD,MAvYvB2wD,GAAWhjE,IAAUw8D,EAAax8D,GAAUs9D,EAAa7B,GACzD92C,KAAK25C,GAASt+D,IAqY/B,IAAkBqS,EAtVlB,SAASkvD,GAAiB0B,GACxB,IAAI/qD,EAAS,IAAI+qD,EAAYv6D,YAAYu6D,EAAYvB,YAErD,OADA,IAAIjE,EAAWvlD,GAAQ2b,IAAI,IAAI4pC,EAAWwF,IACnC/qD,EA8GT,SAAS+oD,GAAWzjC,EAAQ8kC,EAAO7hE,EAAQ4/D,GACzC5/D,IAAWA,EAAS,IAKpB,IAHA,IAAI2iC,GAAS,EACTp6B,EAASs5D,EAAMt5D,SAEVo6B,EAAQp6B,GAAQ,CACvB,IAAI1I,EAAMgiE,EAAMl/B,GAEZ8/B,EAAW7C,EACXA,EAAW5/D,EAAOH,GAAMk9B,EAAOl9B,GAAMA,EAAKG,EAAQ+8B,QAClDxqB,EAEJ8sD,GAAYr/D,EAAQH,OAAkB0S,IAAbkwD,EAAyB1lC,EAAOl9B,GAAO4iE,GAElE,OAAOziE,EAkCT,SAAS0iE,GAAWlrD,EAAK3X,GACvB,IAqKiBN,EACb0Z,EAtKAtN,EAAO6L,EAAImnD,SACf,OAsKgB,WADZ1lD,SADa1Z,EApKAM,KAsKmB,UAARoZ,GAA4B,UAARA,GAA4B,WAARA,EACrD,cAAV1Z,EACU,OAAVA,GAvKDoM,EAAmB,iBAAP9L,EAAkB,SAAW,QACzC8L,EAAK6L,IAWX,SAASimD,GAAUz9D,EAAQH,GACzB,IAAIN,EAj8BN,SAAkBS,EAAQH,GACxB,OAAiB,MAAVG,OAAiBuS,EAAYvS,EAAOH,GAg8B/B8iE,CAAS3iE,EAAQH,GAC7B,OAAOyiE,GAAa/iE,GAASA,OAAQgT,EA7tBvC8rD,GAAKn+D,UAAU2nD,MAnEf,WACE71C,KAAK2sD,SAAWhB,EAAeA,EAAa,MAAQ,IAmEtDU,GAAKn+D,UAAL,OAtDA,SAAoBL,GAClB,OAAOmS,KAAK+jB,IAAIl2B,WAAemS,KAAK2sD,SAAS9+D,IAsD/Cw+D,GAAKn+D,UAAUf,IA1Cf,SAAiBU,GACf,IAAI8L,EAAOqG,KAAK2sD,SAChB,GAAIhB,EAAc,CAChB,IAAIlmD,EAAS9L,EAAK9L,GAClB,MA7YiB,8BA6YV4X,OAA4BlF,EAAYkF,EAEjD,OAAOtX,EAAe1B,KAAKkN,EAAM9L,GAAO8L,EAAK9L,QAAO0S,GAqCtD8rD,GAAKn+D,UAAU61B,IAzBf,SAAiBl2B,GACf,IAAI8L,EAAOqG,KAAK2sD,SAChB,OAAOhB,OAA6BprD,IAAd5G,EAAK9L,GAAqBM,EAAe1B,KAAKkN,EAAM9L,IAwB5Ew+D,GAAKn+D,UAAUkzB,IAXf,SAAiBvzB,EAAKN,GAGpB,OAFWyS,KAAK2sD,SACX9+D,GAAQ89D,QAA0BprD,IAAVhT,EA5aV,4BA4akDA,EAC9DyS,MAoHTwsD,GAAUt+D,UAAU2nD,MAjFpB,WACE71C,KAAK2sD,SAAW,IAiFlBH,GAAUt+D,UAAV,OArEA,SAAyBL,GACvB,IAAI8L,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAE/B,QAAI8iC,EAAQ,KAIRA,GADYh3B,EAAKpD,OAAS,EAE5BoD,EAAK+W,MAELzM,EAAOxX,KAAKkN,EAAMg3B,EAAO,IAEpB,IAyDT67B,GAAUt+D,UAAUf,IA7CpB,SAAsBU,GACpB,IAAI8L,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAE/B,OAAO8iC,EAAQ,OAAIpwB,EAAY5G,EAAKg3B,GAAO,IA0C7C67B,GAAUt+D,UAAU61B,IA9BpB,SAAsBl2B,GACpB,OAAO2/D,GAAaxtD,KAAK2sD,SAAU9+D,IAAQ,GA8B7C2+D,GAAUt+D,UAAUkzB,IAjBpB,SAAsBvzB,EAAKN,GACzB,IAAIoM,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAO/B,OALI8iC,EAAQ,EACVh3B,EAAKuJ,KAAK,CAACrV,EAAKN,IAEhBoM,EAAKg3B,GAAO,GAAKpjC,EAEZyS,MAkGTysD,GAASv+D,UAAU2nD,MA/DnB,WACE71C,KAAK2sD,SAAW,CACd,KAAQ,IAAIN,GACZ,IAAO,IAAK9sC,GAAOitC,IACnB,OAAU,IAAIH,KA4DlBI,GAASv+D,UAAT,OA/CA,SAAwBL,GACtB,OAAO6iE,GAAW1wD,KAAMnS,GAAjB,OAAgCA,IA+CzC4+D,GAASv+D,UAAUf,IAnCnB,SAAqBU,GACnB,OAAO6iE,GAAW1wD,KAAMnS,GAAKV,IAAIU,IAmCnC4+D,GAASv+D,UAAU61B,IAvBnB,SAAqBl2B,GACnB,OAAO6iE,GAAW1wD,KAAMnS,GAAKk2B,IAAIl2B,IAuBnC4+D,GAASv+D,UAAUkzB,IAVnB,SAAqBvzB,EAAKN,GAExB,OADAmjE,GAAW1wD,KAAMnS,GAAKuzB,IAAIvzB,EAAKN,GACxByS,MAgGT0sD,GAAMx+D,UAAU2nD,MApEhB,WACE71C,KAAK2sD,SAAW,IAAIH,IAoEtBE,GAAMx+D,UAAN,OAxDA,SAAqBL,GACnB,OAAOmS,KAAK2sD,SAAL,OAAwB9+D,IAwDjC6+D,GAAMx+D,UAAUf,IA5ChB,SAAkBU,GAChB,OAAOmS,KAAK2sD,SAASx/D,IAAIU,IA4C3B6+D,GAAMx+D,UAAU61B,IAhChB,SAAkBl2B,GAChB,OAAOmS,KAAK2sD,SAAS5oC,IAAIl2B,IAgC3B6+D,GAAMx+D,UAAUkzB,IAnBhB,SAAkBvzB,EAAKN,GACrB,IAAIqjE,EAAQ5wD,KAAK2sD,SACjB,GAAIiE,aAAiBpE,GAAW,CAC9B,IAAIqE,EAAQD,EAAMjE,SAClB,IAAKptC,GAAQsxC,EAAMt6D,OAASu6D,IAE1B,OADAD,EAAM3tD,KAAK,CAACrV,EAAKN,IACVyS,KAET4wD,EAAQ5wD,KAAK2sD,SAAW,IAAIF,GAASoE,GAGvC,OADAD,EAAMxvC,IAAIvzB,EAAKN,GACRyS,MAicT,IAAIyuD,GAAarD,EAAmBnB,EAAQmB,EAAkBp+D,QAyhB9D,WACE,MAAO,IAjhBLkhE,GAtQJ,SAAoB3gE,GAClB,OAAOq9D,EAAen+D,KAAKc,IAyX7B,SAAS6/D,GAAQ7/D,EAAOgJ,GAEtB,SADAA,EAAmB,MAAVA,EAnxCY,iBAmxCwBA,KAE1B,iBAAThJ,GAAqB07D,EAAS/2C,KAAK3kB,KAC1CA,GAAS,GAAKA,EAAQ,GAAK,GAAKA,EAAQgJ,EAmC7C,SAAS+3D,GAAY/gE,GACnB,IAAIshE,EAAOthE,GAASA,EAAM0I,YAG1B,OAAO1I,KAFqB,mBAARshE,GAAsBA,EAAK3gE,WAAco8D,GAY/D,SAASuB,GAASjsD,GAChB,GAAY,MAARA,EAAc,CAChB,IACE,OAAO+qD,EAAal+D,KAAKmT,GACzB,MAAOvG,IACT,IACE,OAAQuG,EAAO,GACf,MAAOvG,KAEX,MAAO,GAyDT,SAASk0D,GAAGhgE,EAAOwjE,GACjB,OAAOxjE,IAAUwjE,GAAUxjE,GAAUA,GAASwjE,GAAUA,GAxOrDvF,GA7oCa,qBA6oCD0C,GAAO,IAAI1C,EAAS,IAAIwF,YAAY,MAChDzxC,GAAO2uC,GAAO,IAAI3uC,IAAQspC,GAC1B7rB,GAvpCY,oBAupCDkxB,GAAOlxB,EAAQC,YAC1Bnc,GAAOotC,GAAO,IAAIptC,IAAQgoC,GAC1B4C,GAppCY,oBAopCDwC,GAAO,IAAIxC,MACzBwC,GAAS,SAAS3gE,GAChB,IAAIkY,EAASmlD,EAAen+D,KAAKc,GAC7BshE,EA7pCQ,mBA6pCDppD,EAAsBlY,EAAM0I,iBAAcsK,EACjD0wD,EAAapC,EAAOhD,GAASgD,QAAQtuD,EAEzC,GAAI0wD,EACF,OAAQA,GACN,KAAKrF,EAAoB,MAzpCf,oBA0pCV,KAAKE,EAAe,OAAOjD,EAC3B,KAAKkD,EAAmB,MAnqCf,mBAoqCT,KAAKC,GAAe,OAAOlD,EAC3B,KAAKmD,GAAmB,MAhqCf,mBAmqCb,OAAOxmD,IAuQX,IAAIpP,GAAUD,MAAMC,QA2BpB,SAAS02D,GAAYx/D,GACnB,OAAgB,MAATA,GAqGT,SAAkBA,GAChB,MAAuB,iBAATA,GACZA,GAAS,GAAKA,EAAQ,GAAK,GAAKA,GA/jDb,iBAw9CG2jE,CAAS3jE,EAAMgJ,UAAYg6D,GAAWhjE,GAiDhE,IAAI+9D,GAAWD,GAsLf,WACE,OAAO,GApKT,SAASkF,GAAWhjE,GAGlB,IAAI6P,EAAMywD,GAAStgE,GAASq9D,EAAen+D,KAAKc,GAAS,GACzD,OAAO6P,GAAOurD,GAAWvrD,GAAOwrD,EA2DlC,SAASiF,GAAStgE,GAChB,IAAI0Z,SAAc1Z,EAClB,QAASA,IAAkB,UAAR0Z,GAA4B,YAARA,GA2DzC,SAAS1B,GAAKvX,GACZ,OAAO++D,GAAY/+D,GAAU4+D,GAAc5+D,GAtuB7C,SAAkBA,GAChB,IAAKsgE,GAAYtgE,GACf,OAAOu9D,EAAWv9D,GAEpB,IAAIyX,EAAS,GACb,IAAK,IAAI5X,KAAOb,OAAOgB,GACjBG,EAAe1B,KAAKuB,EAAQH,IAAe,eAAPA,GACtC4X,EAAOvC,KAAKrV,GAGhB,OAAO4X,EA4tB8C0rD,CAASnjE,GA0ChEjC,EAAOD,QA9VP,SAAmByB,GACjB,OAAOkgE,GAAUlgE,GAAO,GAAM,M,yDCt3ChC,yGAUA,MAAM0wB,EAASF,oBAAUM,GAKV,MAAMg7B,UAAuBjf,IAMxC,oBACI,MAAO,CACHg3B,oBAAqB,uBAS7B,oBACI,OAAO31D,UAAQgC,OAenBxH,aAAY,sBAAEijD,EAAF,mBAAyBE,EAAzB,WAA6CD,IACrD9e,QACAr6B,KAAKqxD,SAAW,CACZnY,2BAAwD,IAA1BA,GAA+CA,EAC7EE,wBAAkD,IAAvBA,EAAqC,KAAgB7/B,OAAO6/B,IAG3Fp5C,KAAKsxD,aAAe,IAAI71D,UAAQ6M,WAAW6wC,GAC3Cn5C,KAAKuxD,eAAiBpY,EAAWhnB,WAAW,QAAUgnB,EAAWhnB,WAAW,QAG5EnyB,KAAKsxD,aAAahnD,WAAa,EAE/BtK,KAAKwxD,oBAAsB,IAAIC,IAC/BzxD,KAAKwxD,oBAAoBE,cAAc1xD,KAAMA,KAAKsxD,cAElDtxD,KAAK2xD,YAAc,IAAIC,IAAW5xD,KAAKsxD,cAcvCtxD,KAAK6xD,aAAe,GAIpB7xD,KAAK+F,oBACD,OACA,IAAI+rD,IAAqB,CACrBC,wBAAyB,IAAM/xD,KAAKgyD,mCAShD,gBACI,MAAMC,EAAYjyD,KAAKsxD,cAAgBtxD,KAAKsxD,aAAa3oD,QAAU3I,KAAKsxD,aAAa3oD,OAAO2S,OAE5F,OAAQtb,KAAKkyD,UAAYz2D,UAAQgC,OAAOM,WAAaiC,KAAKkyD,UAAYz2D,UAAQgC,OAAOS,aAC5E8B,KAAK89C,kBAAqBmU,GAAaA,EAAUj5C,aAAeuC,UAAU42C,MAQvF,YACI,OAAOnyD,KAAKsxD,aAAac,MAQ7B,oBACI,OAA2C,IAApCpyD,KAAKsxD,aAAaznD,cAQ7B,aACI,OAAO7J,KAAKsxD,aAAax2D,OAQ7B,uBACI,OAAOkF,KAAKuxD,eAQhB,UACI,OAAOvxD,KAAKsxD,aAAa3tD,IAQ7B,0BACI,OAAO3D,KAAKsxD,aAAa3oD,QAAU3I,KAAKsxD,aAAa3oD,OAAO+O,oBAQhE,aACI,OAAO1X,KAAKsxD,aAAarzC,OAQ7B,cACI,OAAOje,KAAKsxD,aAAanqD,QAQ7B,cACI,OAAOnH,KAAKsxD,aAAa/oD,QAQ7B,aACI,OAAOvI,KAAKkyD,QAUhBnsD,oBAAoBlZ,EAAMojB,GACtBjQ,KAAKnT,GAAQojB,EACbA,EAAOrF,KAAK5K,MAQhB+N,cAAco0B,GACVniC,KAAKsxD,aAAavjD,cAAco0B,GAUpC/1B,OAAOzI,EAAK0I,EAAKC,EAAKd,KAAa22B,GAC/BniC,KAAKsxD,aAAallD,OAAOzI,EAAK0I,EAAKC,EAAKtM,KAAKqyD,qBAAqBvkE,KAAKkS,KAAMwL,MAAc22B,GAS/F12B,QAAQ9H,EAAK+H,EAAMF,KAAa22B,GAC5BniC,KAAKsxD,aAAa7lD,QAAQ9H,EAAK+H,EAAM1L,KAAKqyD,qBAAqBvkE,KAAKkS,KAAMwL,MAAc22B,GAc5FkwB,qBAAqBC,EAAgBviD,KAAWoyB,GAC5CniC,KAAKkyD,QAAUniD,EAEf,IAAIwiD,GAAgB,EAEhBxiD,IAAWtU,UAAQgC,OAAOM,WAAagS,IAAWtU,UAAQgC,OAAOS,UACjE8B,KAAKwyD,2BACLxyD,KAAKyyD,yBACLzyD,KAAK0yD,sBACL1yD,KAAK2xD,YAAYgB,SACjB3yD,KAAKg8C,KAAK4W,cAAc5yD,KAAKlF,SACtBiV,IAAWtU,UAAQgC,OAAOO,eACjCgC,KAAKg8C,KAAKC,eAGVsW,EAAgBvyD,KAAK6yD,yBAChBN,GACD9kD,aAAazN,KAAK8yD,eAIrBP,IACDD,EAAeviD,KAAWoyB,GAC1BniC,KAAKwf,aAAa0D,KAAKm2B,EAAe0Z,OAAO3B,oBAAqBrhD,IAS1EijD,oBACI,IAAK,MAAMC,KAAYjzD,KAAK6xD,aACxBoB,EAAS/1B,OAAO,IAAI7lC,MAAM,eAE9B2I,KAAK6xD,aAAe,GAQxBqB,iBACQlzD,KAAKsxD,cAAgBtxD,KAAKsxD,aAAa3oD,SACvC3I,KAAKsxD,aAAa3oD,OAAO0S,eACzBrb,KAAKsxD,aAAa3oD,OAAOkT,SAAS,OAS1CtM,cAAc4yB,GACVniC,KAAK2xD,YAAYgB,SACjBllD,aAAazN,KAAK8yD,cAClB9yD,KAAKgzD,oBACLhzD,KAAKsxD,aAAa/hD,cAAc4yB,GAQpC30B,SAAS20B,GACLniC,KAAKsxD,aAAa9jD,SAAS20B,GAQ/B+c,0BACI,OAAOl/C,KAAKwxD,oBAAoBtS,0BAQpCsT,2BACI,IAAKxyD,KAAKqxD,SAASnY,sBAEf,OAGJ,MAAM,iBAAEia,GAAqBnzD,KAAKsxD,aAE7BtxD,KAAK89C,iBAEEqV,EAEAA,EAAiB31B,cAEjB21B,EAAiBC,mBACzBn1C,EAAO/Y,KAAK,uCACZiuD,EAAiB3xB,QAAoB,IAHrCvjB,EAAO9Y,KAAK,sEAFZ8Y,EAAO9Y,KAAK,+EAFZ8Y,EAAO9Y,KAAK,yDAiBpBstD,yBACI,MAAM,mBAAErZ,GAAuBp5C,KAAKqxD,SAEpC,GAAIrxD,KAAKuxD,gBAAkBnY,EAAqB,EAAG,CAC/Cp5C,KAAK8yD,cAAgB70C,EAAO/Y,KAAM,kCAAiCk0C,OACnE3rC,aAAazN,KAAK8yD,cAElB,MAAMO,EACiC,GAArBja,EAA0D,GAAhBhuC,KAAKC,SAAiB+tC,EAElFn7B,EAAOhZ,MAAO,2CAA0CouD,OAExDrzD,KAAK8yD,aAAevoD,WAAW,KAC3B,MAAMw2B,EAAM/gC,KAAKuI,QAAQ3G,QAAQ,SAAU,YAAYA,QAAQ,QAAS,WAExE0xD,MAAMvyB,GAAK3D,MACPp4B,IACIiZ,EAAOjZ,MAAO,wCAAuC+7B,EAAO,CAAE/7B,YAEjE01B,KAAK,IAAM16B,KAAKyyD,2BACtBY,IAUXX,sBACI,IAAK,MAAMO,KAAYjzD,KAAK6xD,aACxB,GAAIoB,EAAShgD,GAAI,CACbxF,aAAawlD,EAASrlD,SAEtB,MAAM2lD,EAAWprD,KAAKgM,MAAQ8+C,EAASl2C,MAEvC/c,KAAKoO,OACD6kD,EAAShgD,GACTxN,GAAUwtD,EAASh2B,QAAQx3B,GAC3BT,GAASiuD,EAAS/1B,OAAOl4B,GACzBuuD,GAIZvzD,KAAK6xD,aAAe,GASxBxkD,KAAKW,GACD,IAAKhO,KAAK4J,UACN,MAAM,IAAIvS,MAAM,iBAEpB2I,KAAKsxD,aAAajkD,KAAKW,GAa3BI,OAAO1O,EAAM8L,EAAUmC,EAASC,GAC5B,GAAK5N,KAAK4J,UAMV,OAAO5J,KAAKsxD,aAAaljD,OAAO1O,EAAM8L,EAAUmC,EAASC,GALrDD,EAAQ,iBAgBhB6lD,QAAQvgD,GAAI,QAAErF,IACV,OAAO,IAAIovB,QAAQ,CAACC,EAASC,KACzB,GAAIl9B,KAAK4J,UACL5J,KAAKoO,OACD6E,EACAxN,GAAUw3B,EAAQx3B,GAClBT,GAASk4B,EAAOl4B,GAChB4I,OACD,CACH,MAAMqlD,EAAW,CACbhgD,KACAgqB,UACAC,SACAngB,MAAO5U,KAAKgM,MACZvG,QAASrD,WAAW,KAEhB0oD,EAAShgD,QAAK1S,EAGd28B,OAAO38B,IACRqN,IAGP5N,KAAK6xD,aAAa3uD,KAAK+vD,MAUnCjB,gCACQhyD,KAAK89C,mBACL7/B,EAAO9Y,KAAK,yDACZnF,KAAKkzD,kBAebxlD,aAAahO,EAAM8L,EAAUmC,EAASC,GAC7B5N,KAAK4J,UAKV5J,KAAKsxD,aAAa5jD,aAAahO,EAAM8L,EAAUmC,EAASC,GAJpDD,EAAQ,iBAYhBqwC,wBACI,IAAKxjB,UAAUi5B,YAAczzD,KAAKsxD,aAAaznD,gBAAkB7J,KAAKsxD,aAAa1nD,UAC/E,OAAO,EAGX5J,KAAKsxD,aAAaplD,qBAAqBzQ,UAAQgC,OAAOQ,eACtD+B,KAAKsxD,aAAaznD,eAAgB,EAElC,MAAMiO,EAAO9X,KAAKsxD,aAAa3oD,OAAOgP,aACjCnc,MAAM,CACHyL,KAAM,cAERwI,EAAO7T,gBAAM,CACfqK,MAAOxK,UAAQK,GAAGG,OAClBgL,KAAM,gBAGV6Q,EAAKvR,MAAMkJ,EAAKpK,QAEhB,MAAM44B,EAAMzD,UAAUi5B,YACoB,IAAtCzzD,KAAKuI,QAAQ3E,QAAQ,YAAsB,SAAQ5D,KAAKuI,QAAYvI,KAAKuI,QACzE9M,UAAQ2J,UAAU0S,EAAKzS,SAO3B,OALA4Y,EAAO/Y,KAAM,wCAAuC+4B,GAEpDj+B,KAAKsxD,aAAa3oD,OAAOkH,oBACzB7P,KAAKsxD,aAAaxhD,iBAEX,EAWX+iD,yBACI,MAAM,iBAAEM,GAAqBnzD,KAAKsxD,aAGlC,SAFoB6B,IAAoBA,EAAiBC,oBAGrDpzD,KAAK2xD,YAAY+B,YAEV,O,2DCvhBnB3nE,EAAOD,QAZkB,CAIrB6nE,YAAa,cAKb59B,KAAM,S,cClBV,IAAI69B,EAGJA,EAAK,WACJ,OAAO5zD,KADH,GAIL,IAEC4zD,EAAIA,GAAK,IAAIvoB,SAAS,cAAb,GACR,MAAOhyC,GAEc,iBAAXnN,SAAqB0nE,EAAI1nE,QAOrCH,EAAOD,QAAU8nE,G,iBCnBjB,cAUA,IAaIlL,EAAU,qBAQVG,EAAS,eAGTgL,EAAY,kBAIZ/K,EAAS,eAyBTE,EAAe,8BAGfC,EAAW,mBAGX6K,EAAiB,GACrBA,EAxBiB,yBAwBYA,EAvBZ,yBAwBjBA,EAvBc,sBAuBYA,EAtBX,uBAuBfA,EAtBe,uBAsBYA,EArBZ,uBAsBfA,EArBsB,8BAqBYA,EApBlB,wBAqBhBA,EApBgB,yBAoBY,EAC5BA,EAAepL,GAAWoL,EAnDX,kBAoDfA,EAhCqB,wBAgCYA,EAlDnB,oBAmDdA,EAhCkB,qBAgCYA,EAlDhB,iBAmDdA,EAlDe,kBAkDYA,EAjDb,qBAkDdA,EAAejL,GAAUiL,EA/CT,mBAgDhBA,EAAeD,GAAaC,EA3CZ,mBA4ChBA,EAAehL,GAAUgL,EA1CT,mBA2ChBA,EAxCiB,qBAwCY,EAG7B,IAAI3K,EAA8B,iBAAVvzD,GAAsBA,GAAUA,EAAO5I,SAAWA,QAAU4I,EAGhFwzD,EAA0B,iBAARvzD,MAAoBA,MAAQA,KAAK7I,SAAWA,QAAU6I,KAGxEjK,EAAOu9D,GAAcC,GAAY/d,SAAS,cAATA,GAGjCge,EAA4Cv9D,IAAYA,EAAQiU,UAAYjU,EAG5Ew9D,EAAaD,GAAgC,iBAAVt9D,GAAsBA,IAAWA,EAAOgU,UAAYhU,EAGvFw9D,EAAgBD,GAAcA,EAAWx9D,UAAYu9D,EAGrD0K,EAAcxK,GAAiBJ,EAAW6K,QAG1CC,EAAY,WACd,IACE,OAAOF,GAAeA,EAAYG,SAAWH,EAAYG,QAAQ,QACjE,MAAO76D,KAHK,GAOZ86D,EAAmBF,GAAYA,EAASG,aAuD5C,SAASC,EAAUzjC,EAAO0jC,GAIxB,IAHA,IAAI3jC,GAAS,EACTp6B,EAAkB,MAATq6B,EAAgB,EAAIA,EAAMr6B,SAE9Bo6B,EAAQp6B,GACf,GAAI+9D,EAAU1jC,EAAMD,GAAQA,EAAOC,GACjC,OAAO,EAGX,OAAO,EAkET,SAASo5B,EAAWxkD,GAClB,IAAImrB,GAAS,EACTlrB,EAASrP,MAAMoP,EAAIyd,MAKvB,OAHAzd,EAAI4J,SAAQ,SAAS7hB,EAAOM,GAC1B4X,IAASkrB,GAAS,CAAC9iC,EAAKN,MAEnBkY,EAwBT,SAASykD,EAAW9oC,GAClB,IAAIuP,GAAS,EACTlrB,EAASrP,MAAMgrB,EAAI6B,MAKvB,OAHA7B,EAAIhS,SAAQ,SAAS7hB,GACnBkY,IAASkrB,GAASpjC,KAEbkY,EAIT,IAeM0kD,EAvCWvqD,EAAMwjD,EAwBnBgH,EAAah0D,MAAMlI,UACnBm8D,EAAYhf,SAASn9C,UACrBo8D,EAAct9D,OAAOkB,UAGrBq8D,EAAa3+D,EAAK,sBAGlB++D,EAAeN,EAAU3zD,SAGzBvI,EAAiBm8D,EAAYn8D,eAG7Bq8D,GACEL,EAAM,SAASM,KAAKF,GAAcA,EAAWhlD,MAAQglD,EAAWhlD,KAAKmlD,UAAY,KACvE,iBAAmBP,EAAO,GAQtCoK,EAAuBjK,EAAY5zD,SAGnCm0D,EAAaC,OAAO,IACtBH,EAAal+D,KAAK0B,GAAgByT,QA7PjB,sBA6PuC,QACvDA,QAAQ,yDAA0D,SAAW,KAI5EmpD,EAASxB,EAAgB39D,EAAKm/D,YAASxqD,EACvClT,EAASzB,EAAKyB,OACd29D,EAAap/D,EAAKo/D,WAClBG,EAAuBb,EAAYa,qBACnClnD,EAASmmD,EAAWnmD,OACpBuwD,EAAiBnnE,EAASA,EAAOC,iBAAciT,EAG/C6qD,EAAmBp+D,OAAOu+C,sBAC1B8f,EAAiBN,EAASA,EAAOO,cAAW/qD,EAC5CgrD,GAnEa3rD,EAmEQ5S,OAAOuY,KAnET69C,EAmEep2D,OAlE7B,SAASoU,GACd,OAAOxB,EAAKwjD,EAAUhiD,MAoEtBoqD,EAAWC,GAAU7/D,EAAM,YAC3B2zB,EAAMksC,GAAU7/D,EAAM,OACtBoxC,EAAUyuB,GAAU7/D,EAAM,WAC1Bk1B,EAAM2qC,GAAU7/D,EAAM,OACtB8/D,EAAUD,GAAU7/D,EAAM,WAC1B+/D,EAAeF,GAAUz+D,OAAQ,UAGjC4+D,EAAqBC,GAASL,GAC9BM,EAAgBD,GAAStsC,GACzBwsC,EAAoBF,GAAS7uB,GAC7BgvB,EAAgBH,GAAS/qC,GACzBmrC,EAAoBJ,GAASH,GAG7BQ,GAAc7+D,EAASA,EAAOa,eAAYqS,EAC1C4rD,GAAgBD,GAAcA,GAAYE,aAAU7rD,EASxD,SAAS8rD,GAAKC,GACZ,IAAI37B,GAAS,EACTp6B,EAAoB,MAAX+1D,EAAkB,EAAIA,EAAQ/1D,OAG3C,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KA+F7B,SAASC,GAAUF,GACjB,IAAI37B,GAAS,EACTp6B,EAAoB,MAAX+1D,EAAkB,EAAIA,EAAQ/1D,OAG3C,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KA4G7B,SAASE,GAASH,GAChB,IAAI37B,GAAS,EACTp6B,EAAoB,MAAX+1D,EAAkB,EAAIA,EAAQ/1D,OAG3C,IADAyJ,KAAK61C,UACIllB,EAAQp6B,GAAQ,CACvB,IAAIg2D,EAAQD,EAAQ37B,GACpB3wB,KAAKohB,IAAImrC,EAAM,GAAIA,EAAM,KA+F7B,SAASkI,GAASrxC,GAChB,IAAIuN,GAAS,EACTp6B,EAAmB,MAAV6sB,EAAiB,EAAIA,EAAO7sB,OAGzC,IADAyJ,KAAK2sD,SAAW,IAAIF,KACX97B,EAAQp6B,GACfyJ,KAAKugB,IAAI6C,EAAOuN,IA2CpB,SAAS+7B,GAAMJ,GACb,IAAI3yD,EAAOqG,KAAK2sD,SAAW,IAAIH,GAAUF,GACzCtsD,KAAKijB,KAAOtpB,EAAKspB,KAmGnB,SAAS2pC,GAAcr/D,EAAOs/D,GAC5B,IAAIiB,EAAQz3D,GAAQ9I,GAChBmnE,GAAS5G,GAASb,GAAY1/D,GAC9BonE,GAAU7G,IAAU4G,GAASpJ,GAAS/9D,GACtCqnE,GAAU9G,IAAU4G,IAAUC,GAAUP,GAAa7mE,GACrD4/D,EAAcW,GAAS4G,GAASC,GAAUC,EAC1CnvD,EAAS0nD,EAloBf,SAAmBp/D,EAAG67D,GAIpB,IAHA,IAAIj5B,GAAS,EACTlrB,EAASrP,MAAMrI,KAEV4iC,EAAQ5iC,GACf0X,EAAOkrB,GAASi5B,EAASj5B,GAE3B,OAAOlrB,EA2nBoBynD,CAAU3/D,EAAMgJ,OAAQuC,QAAU,GACzDvC,EAASkP,EAAOlP,OAEpB,IAAK,IAAI1I,KAAON,GACTs/D,IAAa1+D,EAAe1B,KAAKc,EAAOM,IACvCs/D,IAEQ,UAAPt/D,GAEC8mE,IAAkB,UAAP9mE,GAA0B,UAAPA,IAE9B+mE,IAAkB,UAAP/mE,GAA0B,cAAPA,GAA8B,cAAPA,IAEtDu/D,GAAQv/D,EAAK0I,KAElBkP,EAAOvC,KAAKrV,GAGhB,OAAO4X,EAWT,SAAS+nD,GAAa58B,EAAO/iC,GAE3B,IADA,IAAI0I,EAASq6B,EAAMr6B,OACZA,KACL,GAAIg3D,GAAG38B,EAAMr6B,GAAQ,GAAI1I,GACvB,OAAO0I,EAGX,OAAQ,EA0BV,SAASs+D,GAAWtnE,GAClB,OAAa,MAATA,OACegT,IAAVhT,EAt1BQ,qBARL,gBAg2BJinE,GAAkBA,KAAkBxnE,OAAOO,GA0arD,SAAmBA,GACjB,IAAIunE,EAAQ3mE,EAAe1B,KAAKc,EAAOinE,GACnCp3D,EAAM7P,EAAMinE,GAEhB,IACEjnE,EAAMinE,QAAkBj0D,EACxB,IAAIw0D,GAAW,EACf,MAAO17D,IAET,IAAIoM,EAAS8uD,EAAqB9nE,KAAKc,GACnCwnE,IACED,EACFvnE,EAAMinE,GAAkBp3D,SAEjB7P,EAAMinE,IAGjB,OAAO/uD,EA1bHuvD,CAAUznE,GA4iBhB,SAAwBA,GACtB,OAAOgnE,EAAqB9nE,KAAKc,GA5iB7Bq9D,CAAer9D,GAUrB,SAAS0nE,GAAgB1nE,GACvB,OAAOu/D,GAAav/D,IAAUsnE,GAAWtnE,IAAUm7D,EAiBrD,SAASwM,GAAY3nE,EAAOwjE,EAAOoE,EAASvH,EAAYxpD,GACtD,OAAI7W,IAAUwjE,IAGD,MAATxjE,GAA0B,MAATwjE,IAAmBjE,GAAav/D,KAAWu/D,GAAaiE,GACpExjE,GAAUA,GAASwjE,GAAUA,EAmBxC,SAAyB/iE,EAAQ+iE,EAAOoE,EAASvH,EAAYwH,EAAWhxD,GACtE,IAAIixD,EAAWh/D,GAAQrI,GACnBsnE,EAAWj/D,GAAQ06D,GACnBwE,EAASF,EAl6BA,iBAk6BsBnH,GAAOlgE,GACtCwnE,EAASF,EAn6BA,iBAm6BsBpH,GAAO6C,GAKtC0E,GAHJF,EAASA,GAAU7M,EAAUmL,EAAY0B,IAGhB1B,EACrB6B,GAHJF,EAASA,GAAU9M,EAAUmL,EAAY2B,IAGhB3B,EACrB8B,EAAYJ,GAAUC,EAE1B,GAAIG,GAAarK,GAASt9D,GAAS,CACjC,IAAKs9D,GAASyF,GACZ,OAAO,EAETsE,GAAW,EACXI,GAAW,EAEb,GAAIE,IAAcF,EAEhB,OADArxD,IAAUA,EAAQ,IAAIsoD,IACd2I,GAAYjB,GAAapmE,GAC7B4nE,GAAY5nE,EAAQ+iE,EAAOoE,EAASvH,EAAYwH,EAAWhxD,GAiKnE,SAAoBpW,EAAQ+iE,EAAO3zD,EAAK+3D,EAASvH,EAAYwH,EAAWhxD,GACtE,OAAQhH,GACN,IApkCc,oBAqkCZ,GAAKpP,EAAOihE,YAAc8B,EAAM9B,YAC3BjhE,EAAOghE,YAAc+B,EAAM/B,WAC9B,OAAO,EAEThhE,EAASA,EAAOogE,OAChB2C,EAAQA,EAAM3C,OAEhB,IA7kCiB,uBA8kCf,QAAKpgE,EAAOihE,YAAc8B,EAAM9B,aAC3BmG,EAAU,IAAIpK,EAAWh9D,GAAS,IAAIg9D,EAAW+F,KAKxD,IAtmCU,mBAumCV,IAtmCU,gBAumCV,IAlmCY,kBAqmCV,OAAOxD,IAAIv/D,GAAS+iE,GAEtB,IA3mCW,iBA4mCT,OAAO/iE,EAAOnB,MAAQkkE,EAAMlkE,MAAQmB,EAAOyW,SAAWssD,EAAMtsD,QAE9D,IArmCY,kBAsmCZ,IApmCY,kBAwmCV,OAAOzW,GAAW+iE,EAAQ,GAE5B,KAAKlI,EACH,IAAIgN,EAAU7L,EAEhB,KAAKlB,EACH,IAAIgN,EAroCiB,EAqoCLX,EAGhB,GAFAU,IAAYA,EAAU3L,GAElBl8D,EAAOi1B,MAAQ8tC,EAAM9tC,OAAS6yC,EAChC,OAAO,EAGT,IAAIlG,EAAUxrD,EAAMjX,IAAIa,GACxB,GAAI4hE,EACF,OAAOA,GAAWmB,EAEpBoE,GA/oCuB,EAkpCvB/wD,EAAMgd,IAAIpzB,EAAQ+iE,GAClB,IAAItrD,EAASmwD,GAAYC,EAAQ7nE,GAAS6nE,EAAQ9E,GAAQoE,EAASvH,EAAYwH,EAAWhxD,GAE1F,OADAA,EAAK,OAAWpW,GACTyX,EAET,IAhoCY,kBAioCV,GAAI0mD,GACF,OAAOA,GAAc1/D,KAAKuB,IAAWm+D,GAAc1/D,KAAKskE,GAG9D,OAAO,EA9NDgF,CAAW/nE,EAAQ+iE,EAAOwE,EAAQJ,EAASvH,EAAYwH,EAAWhxD,GAExE,KAj8ByB,EAi8BnB+wD,GAAiC,CACrC,IAAIa,EAAeP,GAAYtnE,EAAe1B,KAAKuB,EAAQ,eACvDioE,EAAeP,GAAYvnE,EAAe1B,KAAKskE,EAAO,eAE1D,GAAIiF,GAAgBC,EAAc,CAChC,IAAIC,EAAeF,EAAehoE,EAAOT,QAAUS,EAC/CmoE,EAAeF,EAAelF,EAAMxjE,QAAUwjE,EAGlD,OADA3sD,IAAUA,EAAQ,IAAIsoD,IACf0I,EAAUc,EAAcC,EAAchB,EAASvH,EAAYxpD,IAGtE,IAAKuxD,EACH,OAAO,EAGT,OADAvxD,IAAUA,EAAQ,IAAIsoD,IA6NxB,SAAsB1+D,EAAQ+iE,EAAOoE,EAASvH,EAAYwH,EAAWhxD,GACnE,IAAI0xD,EA9qCqB,EA8qCTX,EACZiB,EAAWjG,GAAWniE,GACtBqoE,EAAYD,EAAS7/D,OAErB+/D,EADWnG,GAAWY,GACDx6D,OAEzB,GAAI8/D,GAAaC,IAAcR,EAC7B,OAAO,EAET,IAAInlC,EAAQ0lC,EACZ,KAAO1lC,KAAS,CACd,IAAI9iC,EAAMuoE,EAASzlC,GACnB,KAAMmlC,EAAYjoE,KAAOkjE,EAAQ5iE,EAAe1B,KAAKskE,EAAOljE,IAC1D,OAAO,EAIX,IAAI+hE,EAAUxrD,EAAMjX,IAAIa,GACxB,GAAI4hE,GAAWxrD,EAAMjX,IAAI4jE,GACvB,OAAOnB,GAAWmB,EAEpB,IAAItrD,GAAS,EACbrB,EAAMgd,IAAIpzB,EAAQ+iE,GAClB3sD,EAAMgd,IAAI2vC,EAAO/iE,GAEjB,IAAIuoE,EAAWT,EACf,OAASnlC,EAAQ0lC,GAAW,CAC1BxoE,EAAMuoE,EAASzlC,GACf,IAAI28B,EAAWt/D,EAAOH,GAClB2oE,EAAWzF,EAAMljE,GAErB,GAAI+/D,EACF,IAAI6I,EAAWX,EACXlI,EAAW4I,EAAUlJ,EAAUz/D,EAAKkjE,EAAO/iE,EAAQoW,GACnDwpD,EAAWN,EAAUkJ,EAAU3oE,EAAKG,EAAQ+iE,EAAO3sD,GAGzD,UAAmB7D,IAAbk2D,EACGnJ,IAAakJ,GAAYpB,EAAU9H,EAAUkJ,EAAUrB,EAASvH,EAAYxpD,GAC7EqyD,GACD,CACLhxD,GAAS,EACT,MAEF8wD,IAAaA,EAAkB,eAAP1oE,GAE1B,GAAI4X,IAAW8wD,EAAU,CACvB,IAAIG,EAAU1oE,EAAOiI,YACjB0gE,EAAU5F,EAAM96D,YAGhBygE,GAAWC,KACV,gBAAiB3oE,MAAU,gBAAiB+iE,IACzB,mBAAX2F,GAAyBA,aAAmBA,GACjC,mBAAXC,GAAyBA,aAAmBA,IACvDlxD,GAAS,GAKb,OAFArB,EAAK,OAAWpW,GAChBoW,EAAK,OAAW2sD,GACTtrD,EAzRAmxD,CAAa5oE,EAAQ+iE,EAAOoE,EAASvH,EAAYwH,EAAWhxD,GA3D5DyyD,CAAgBtpE,EAAOwjE,EAAOoE,EAASvH,EAAYsH,GAAa9wD,IAsEzE,SAASksD,GAAa/iE,GACpB,SAAKsgE,GAAStgE,IAwahB,SAAkBqS,GAChB,QAAS4qD,GAAeA,KAAc5qD,EAzadk3D,CAASvpE,MAGnBgjE,GAAWhjE,GAASs9D,EAAa7B,GAChC92C,KAAK25C,GAASt+D,IAsB/B,SAAS4jE,GAASnjE,GAChB,GAyZI6gE,GADethE,EAxZFS,IAyZGT,EAAM0I,YACtBwS,EAAwB,mBAARomD,GAAsBA,EAAK3gE,WAAco8D,EAEtD/8D,IAAUkb,EA3Zf,OAAO8iD,EAAWv9D,GAuZtB,IAAqBT,EACfshE,EACApmD,EAvZAhD,EAAS,GACb,IAAK,IAAI5X,KAAOb,OAAOgB,GACjBG,EAAe1B,KAAKuB,EAAQH,IAAe,eAAPA,GACtC4X,EAAOvC,KAAKrV,GAGhB,OAAO4X,EAgBT,SAASmwD,GAAYhlC,EAAOmgC,EAAOoE,EAASvH,EAAYwH,EAAWhxD,GACjE,IAAI0xD,EAlhCqB,EAkhCTX,EACZ4B,EAAYnmC,EAAMr6B,OAClB+/D,EAAYvF,EAAMx6D,OAEtB,GAAIwgE,GAAaT,KAAeR,GAAaQ,EAAYS,GACvD,OAAO,EAGT,IAAInH,EAAUxrD,EAAMjX,IAAIyjC,GACxB,GAAIg/B,GAAWxrD,EAAMjX,IAAI4jE,GACvB,OAAOnB,GAAWmB,EAEpB,IAAIpgC,GAAS,EACTlrB,GAAS,EACTuxD,EA/hCuB,EA+hCf7B,EAAoC,IAAIV,QAAWl0D,EAM/D,IAJA6D,EAAMgd,IAAIwP,EAAOmgC,GACjB3sD,EAAMgd,IAAI2vC,EAAOngC,KAGRD,EAAQomC,GAAW,CAC1B,IAAIE,EAAWrmC,EAAMD,GACjB6lC,EAAWzF,EAAMpgC,GAErB,GAAIi9B,EACF,IAAI6I,EAAWX,EACXlI,EAAW4I,EAAUS,EAAUtmC,EAAOogC,EAAOngC,EAAOxsB,GACpDwpD,EAAWqJ,EAAUT,EAAU7lC,EAAOC,EAAOmgC,EAAO3sD,GAE1D,QAAiB7D,IAAbk2D,EAAwB,CAC1B,GAAIA,EACF,SAEFhxD,GAAS,EACT,MAGF,GAAIuxD,GACF,IAAK3C,EAAUtD,GAAO,SAASyF,EAAUU,GACnC,GA72BarpE,EA62BOqpE,GAANF,EA52BXjzC,IAAIl2B,KA62BFopE,IAAaT,GAAYpB,EAAU6B,EAAUT,EAAUrB,EAASvH,EAAYxpD,IAC/E,OAAO4yD,EAAK9zD,KAAKg0D,GA/2B/B,IAAyBrpE,KAi3BX,CACN4X,GAAS,EACT,YAEG,GACDwxD,IAAaT,IACXpB,EAAU6B,EAAUT,EAAUrB,EAASvH,EAAYxpD,GACpD,CACLqB,GAAS,EACT,OAKJ,OAFArB,EAAK,OAAWwsB,GAChBxsB,EAAK,OAAW2sD,GACTtrD,EAyKT,SAAS0qD,GAAWniE,GAClB,OApZF,SAAwBA,EAAQ8hE,EAAUC,GACxC,IAAItqD,EAASqqD,EAAS9hE,GACtB,OAAOqI,GAAQrI,GAAUyX,EAhuB3B,SAAmBmrB,EAAOxN,GAKxB,IAJA,IAAIuN,GAAS,EACTp6B,EAAS6sB,EAAO7sB,OAChBy5D,EAASp/B,EAAMr6B,SAEVo6B,EAAQp6B,GACfq6B,EAAMo/B,EAASr/B,GAASvN,EAAOuN,GAEjC,OAAOC,EAwtB2Bq/B,CAAUxqD,EAAQsqD,EAAY/hE,IAkZzDkiE,CAAeliE,EAAQuX,GAAMkpD,IAWtC,SAASiC,GAAWlrD,EAAK3X,GACvB,IAsHiBN,EACb0Z,EAvHAtN,EAAO6L,EAAImnD,SACf,OAuHgB,WADZ1lD,SADa1Z,EArHAM,KAuHmB,UAARoZ,GAA4B,UAARA,GAA4B,WAARA,EACrD,cAAV1Z,EACU,OAAVA,GAxHDoM,EAAmB,iBAAP9L,EAAkB,SAAW,QACzC8L,EAAK6L,IAWX,SAASimD,GAAUz9D,EAAQH,GACzB,IAAIN,EAxjCN,SAAkBS,EAAQH,GACxB,OAAiB,MAAVG,OAAiBuS,EAAYvS,EAAOH,GAujC/B8iE,CAAS3iE,EAAQH,GAC7B,OAAOyiE,GAAa/iE,GAASA,OAAQgT,EAp2BvC8rD,GAAKn+D,UAAU2nD,MAvEf,WACE71C,KAAK2sD,SAAWhB,EAAeA,EAAa,MAAQ,GACpD3rD,KAAKijB,KAAO,GAsEdopC,GAAKn+D,UAAL,OAzDA,SAAoBL,GAClB,IAAI4X,EAASzF,KAAK+jB,IAAIl2B,WAAemS,KAAK2sD,SAAS9+D,GAEnD,OADAmS,KAAKijB,MAAQxd,EAAS,EAAI,EACnBA,GAuDT4mD,GAAKn+D,UAAUf,IA3Cf,SAAiBU,GACf,IAAI8L,EAAOqG,KAAK2sD,SAChB,GAAIhB,EAAc,CAChB,IAAIlmD,EAAS9L,EAAK9L,GAClB,MA3YiB,8BA2YV4X,OAA4BlF,EAAYkF,EAEjD,OAAOtX,EAAe1B,KAAKkN,EAAM9L,GAAO8L,EAAK9L,QAAO0S,GAsCtD8rD,GAAKn+D,UAAU61B,IA1Bf,SAAiBl2B,GACf,IAAI8L,EAAOqG,KAAK2sD,SAChB,OAAOhB,OAA8BprD,IAAd5G,EAAK9L,GAAsBM,EAAe1B,KAAKkN,EAAM9L,IAyB9Ew+D,GAAKn+D,UAAUkzB,IAZf,SAAiBvzB,EAAKN,GACpB,IAAIoM,EAAOqG,KAAK2sD,SAGhB,OAFA3sD,KAAKijB,MAAQjjB,KAAK+jB,IAAIl2B,GAAO,EAAI,EACjC8L,EAAK9L,GAAQ89D,QAA0BprD,IAAVhT,EA3aV,4BA2akDA,EAC9DyS,MAuHTwsD,GAAUt+D,UAAU2nD,MApFpB,WACE71C,KAAK2sD,SAAW,GAChB3sD,KAAKijB,KAAO,GAmFdupC,GAAUt+D,UAAV,OAvEA,SAAyBL,GACvB,IAAI8L,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAE/B,QAAI8iC,EAAQ,KAIRA,GADYh3B,EAAKpD,OAAS,EAE5BoD,EAAK+W,MAELzM,EAAOxX,KAAKkN,EAAMg3B,EAAO,KAEzB3wB,KAAKijB,MACA,IA0DTupC,GAAUt+D,UAAUf,IA9CpB,SAAsBU,GACpB,IAAI8L,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAE/B,OAAO8iC,EAAQ,OAAIpwB,EAAY5G,EAAKg3B,GAAO,IA2C7C67B,GAAUt+D,UAAU61B,IA/BpB,SAAsBl2B,GACpB,OAAO2/D,GAAaxtD,KAAK2sD,SAAU9+D,IAAQ,GA+B7C2+D,GAAUt+D,UAAUkzB,IAlBpB,SAAsBvzB,EAAKN,GACzB,IAAIoM,EAAOqG,KAAK2sD,SACZh8B,EAAQ68B,GAAa7zD,EAAM9L,GAQ/B,OANI8iC,EAAQ,KACR3wB,KAAKijB,KACPtpB,EAAKuJ,KAAK,CAACrV,EAAKN,KAEhBoM,EAAKg3B,GAAO,GAAKpjC,EAEZyS,MAyGTysD,GAASv+D,UAAU2nD,MAtEnB,WACE71C,KAAKijB,KAAO,EACZjjB,KAAK2sD,SAAW,CACd,KAAQ,IAAIN,GACZ,IAAO,IAAK9sC,GAAOitC,IACnB,OAAU,IAAIH,KAkElBI,GAASv+D,UAAT,OArDA,SAAwBL,GACtB,IAAI4X,EAASirD,GAAW1wD,KAAMnS,GAAjB,OAAgCA,GAE7C,OADAmS,KAAKijB,MAAQxd,EAAS,EAAI,EACnBA,GAmDTgnD,GAASv+D,UAAUf,IAvCnB,SAAqBU,GACnB,OAAO6iE,GAAW1wD,KAAMnS,GAAKV,IAAIU,IAuCnC4+D,GAASv+D,UAAU61B,IA3BnB,SAAqBl2B,GACnB,OAAO6iE,GAAW1wD,KAAMnS,GAAKk2B,IAAIl2B,IA2BnC4+D,GAASv+D,UAAUkzB,IAdnB,SAAqBvzB,EAAKN,GACxB,IAAIoM,EAAO+2D,GAAW1wD,KAAMnS,GACxBo1B,EAAOtpB,EAAKspB,KAIhB,OAFAtpB,EAAKynB,IAAIvzB,EAAKN,GACdyS,KAAKijB,MAAQtpB,EAAKspB,MAAQA,EAAO,EAAI,EAC9BjjB,MAyDTy0D,GAASvmE,UAAUqyB,IAAMk0C,GAASvmE,UAAUgV,KAnB5C,SAAqB3V,GAEnB,OADAyS,KAAK2sD,SAASvrC,IAAI7zB,EA3qBC,6BA4qBZyS,MAkBTy0D,GAASvmE,UAAU61B,IANnB,SAAqBx2B,GACnB,OAAOyS,KAAK2sD,SAAS5oC,IAAIx2B,IAqG3Bm/D,GAAMx+D,UAAU2nD,MA3EhB,WACE71C,KAAK2sD,SAAW,IAAIH,GACpBxsD,KAAKijB,KAAO,GA0EdypC,GAAMx+D,UAAN,OA9DA,SAAqBL,GACnB,IAAI8L,EAAOqG,KAAK2sD,SACZlnD,EAAS9L,EAAI,OAAW9L,GAG5B,OADAmS,KAAKijB,KAAOtpB,EAAKspB,KACVxd,GA0DTinD,GAAMx+D,UAAUf,IA9ChB,SAAkBU,GAChB,OAAOmS,KAAK2sD,SAASx/D,IAAIU,IA8C3B6+D,GAAMx+D,UAAU61B,IAlChB,SAAkBl2B,GAChB,OAAOmS,KAAK2sD,SAAS5oC,IAAIl2B,IAkC3B6+D,GAAMx+D,UAAUkzB,IArBhB,SAAkBvzB,EAAKN,GACrB,IAAIoM,EAAOqG,KAAK2sD,SAChB,GAAIhzD,aAAgB6yD,GAAW,CAC7B,IAAIqE,EAAQl3D,EAAKgzD,SACjB,IAAKptC,GAAQsxC,EAAMt6D,OAASu6D,IAG1B,OAFAD,EAAM3tD,KAAK,CAACrV,EAAKN,IACjByS,KAAKijB,OAAStpB,EAAKspB,KACZjjB,KAETrG,EAAOqG,KAAK2sD,SAAW,IAAIF,GAASoE,GAItC,OAFAl3D,EAAKynB,IAAIvzB,EAAKN,GACdyS,KAAKijB,KAAOtpB,EAAKspB,KACVjjB,MA+hBT,IAAIyuD,GAAcrD,EAA+B,SAASp9D,GACxD,OAAc,MAAVA,EACK,IAETA,EAAShB,OAAOgB,GA9sClB,SAAqB4iC,EAAO0jC,GAM1B,IALA,IAAI3jC,GAAS,EACTp6B,EAAkB,MAATq6B,EAAgB,EAAIA,EAAMr6B,OACnC4gE,EAAW,EACX1xD,EAAS,KAEJkrB,EAAQp6B,GAAQ,CACvB,IAAIhJ,EAAQqjC,EAAMD,GACd2jC,EAAU/mE,EAAOojC,EAAOC,KAC1BnrB,EAAO0xD,KAAc5pE,GAGzB,OAAOkY,EAmsCA2xD,CAAYhM,EAAiBp9D,IAAS,SAAS0hE,GACpD,OAAOvE,EAAqB1+D,KAAKuB,EAAQ0hE,QAsd7C,WACE,MAAO,IA5cLxB,GAAS2G,GAkCb,SAASzH,GAAQ7/D,EAAOgJ,GAEtB,SADAA,EAAmB,MAAVA,EAt2CY,iBAs2CwBA,KAE1B,iBAAThJ,GAAqB07D,EAAS/2C,KAAK3kB,KAC1CA,GAAS,GAAKA,EAAQ,GAAK,GAAKA,EAAQgJ,EA4D7C,SAASs1D,GAASjsD,GAChB,GAAY,MAARA,EAAc,CAChB,IACE,OAAO+qD,EAAal+D,KAAKmT,GACzB,MAAOvG,IACT,IACE,OAAQuG,EAAO,GACf,MAAOvG,KAEX,MAAO,GAmCT,SAASk0D,GAAGhgE,EAAOwjE,GACjB,OAAOxjE,IAAUwjE,GAAUxjE,GAAUA,GAASwjE,GAAUA,GA5IrDvF,GA7yCa,qBA6yCD0C,GAAO,IAAI1C,EAAS,IAAIwF,YAAY,MAChDzxC,GAAO2uC,GAAO,IAAI3uC,IAAQspC,GAC1B7rB,GAzzCY,oBAyzCDkxB,GAAOlxB,EAAQC,YAC1Bnc,GAAOotC,GAAO,IAAIptC,IAAQgoC,GAC1B4C,GApzCY,oBAozCDwC,GAAO,IAAIxC,MACzBwC,GAAS,SAAS3gE,GAChB,IAAIkY,EAASovD,GAAWtnE,GACpBshE,EAAOppD,GAAUouD,EAAYtmE,EAAM0I,iBAAcsK,EACjD0wD,EAAapC,EAAOhD,GAASgD,GAAQ,GAEzC,GAAIoC,EACF,OAAQA,GACN,KAAKrF,EAAoB,MAzzCf,oBA0zCV,KAAKE,EAAe,OAAOjD,EAC3B,KAAKkD,EAAmB,MAr0Cf,mBAs0CT,KAAKC,EAAe,OAAOlD,EAC3B,KAAKmD,EAAmB,MAh0Cf,mBAm0Cb,OAAOxmD,IA8IX,IAAIwnD,GAAcgI,GAAgB,WAAa,OAAO9zD,UAApB,IAAsC8zD,GAAkB,SAAS1nE,GACjG,OAAOu/D,GAAav/D,IAAUY,EAAe1B,KAAKc,EAAO,YACtD49D,EAAqB1+D,KAAKc,EAAO,WA0BlC8I,GAAUD,MAAMC,QAgDpB,IAAIi1D,GAAWD,GA4Of,WACE,OAAO,GA1LT,SAASkF,GAAWhjE,GAClB,IAAKsgE,GAAStgE,GACZ,OAAO,EAIT,IAAI6P,EAAMy3D,GAAWtnE,GACrB,MApmDY,qBAomDL6P,GAnmDI,8BAmmDcA,GAxmDZ,0BAwmD6BA,GA7lD7B,kBA6lDgDA,EA6B/D,SAAS8zD,GAAS3jE,GAChB,MAAuB,iBAATA,GACZA,GAAS,GAAKA,EAAQ,GAAK,GAAKA,GA5oDb,iBAwqDvB,SAASsgE,GAAStgE,GAChB,IAAI0Z,SAAc1Z,EAClB,OAAgB,MAATA,IAA0B,UAAR0Z,GAA4B,YAARA,GA2B/C,SAAS6lD,GAAav/D,GACpB,OAAgB,MAATA,GAAiC,iBAATA,EAoBjC,IAAI6mE,GAAeD,EAhiDnB,SAAmBv0D,GACjB,OAAO,SAASrS,GACd,OAAOqS,EAAKrS,IA8hDsB8pE,CAAUlD,GAnvBhD,SAA0B5mE,GACxB,OAAOu/D,GAAav/D,IAClB2jE,GAAS3jE,EAAMgJ,WAAau9D,EAAee,GAAWtnE,KA+wB1D,SAASgY,GAAKvX,GACZ,OA1NgB,OADGT,EA2NAS,IA1NKkjE,GAAS3jE,EAAMgJ,UAAYg6D,GAAWhjE,GA0NjCq/D,GAAc5+D,GAAUmjE,GAASnjE,GA3NhE,IAAqBT,EAqQrBxB,EAAOD,QAlNP,SAAiByB,EAAOwjE,GACtB,OAAOmE,GAAY3nE,EAAOwjE,M,sbCjmD5B,MAAM9yC,EAASb,EAAQ,GAAqBW,UAAUM,GAEtD,IAAIi5C,EAAc,KAKlB,MAAMC,EAAiB,CAQnB75B,aAAc,KASd9yB,KAAKzD,EAAU,GAAI0e,GACf7lB,KAAKmH,QAAUA,EACfmwD,EAAczxC,EAEd7lB,KAAK09B,aAAe19B,KAAKw3D,4BAEpBx3D,KAAK09B,cACNzf,EAAO/Y,KAAK,6BAWpBsyD,4BACI,OAAI13C,IAAQs1B,SACD,CAAClD,EAAG3+B,EAAWU,KAClB/nB,OAAOurE,YAAYC,oBACfnkD,EACA,CAACvO,EAAO+gB,KACJ,IAAI4xC,EAmBAA,EADA3yD,GAAwB,sBAAfA,EAAMnY,KACF,IAAI+4B,IACbipB,+BAGS,IAAIjpB,IACb5gB,EAAO+gB,EAAa,CAAE,YAER,mBAAd9R,GACDA,EAAU0jD,MAGtB73C,IAAQgY,aACR93B,KAAK43D,uBACL93C,IAAQ+3C,0BACR73D,KAAK83D,iCAEhB75C,EAAOrZ,IAAI,mCAAoCkb,IAAQm1B,WAEhD,OAQXzX,cACI,OAA6B,OAAtBx9B,KAAK09B,cAahBk6B,uBAAuBzwD,EAAU,GAAIoM,EAAWU,GAC5C,GAAI/nB,OAAO6rE,yBACJ7rE,OAAO6rE,wBAAwBC,kBAAmB,CACrD,MAAM,sBAAEz4B,EAAF,WAAyBC,GAAer4B,EAE9Cjb,OAAO6rE,wBAAwBC,kBAC3B,CACIz4B,sBAAuBA,GAAyB,CAAE,SAAU,WAEhE,CAACiJ,EAAUyvB,EAAYlgC,GAAmB,IA0G1D,SACQ5wB,EAAU,CACNwL,SAAU,GACV6sB,WAAY,IAEhBjsB,EACAU,GACJ,MAAM,SAAEu0B,EAAF,WAAYyvB,EAAZ,iBAAwBlgC,EAAxB,MAA0C/yB,GAAUmC,EAAQwL,UAAY,GAE9E,GAAI61B,EAAU,CACV,MAAMhJ,E,+VAAa,CAAH,CACZxH,cAAewQ,EACfzQ,oBACG5wB,EAAQq4B,YAGf83B,EAAY,CAAE,WAAa93B,GACtB9E,KAAKnZ,GAAUhO,EAAU,CACtBgO,SACAqU,SAAU4S,EACVnK,WAAY45B,IACZhkD,OACL,CAIH,GAAiB,KAAbu0B,EAIA,YAHAv0B,EAAU,IAAI2R,IACVipB,gCAKR56B,EAAU,IAAI2R,IACVipB,8BACA7pC,KA5IQkzD,CACI,CACIvlD,SAAU,CACN61B,WACAyvB,aACAlgC,oBAEJyH,cAEJjsB,EACAU,GAER9D,GAAO8D,EAAU,IAAI2R,IACjBipB,gCACA1+B,UAIR8D,EAAU,IAAI2R,IACVipB,uCAUZipB,gCAAgC3wD,EAASqE,EAAU2sD,GAG/C,IAAIC,EAFJn6C,EAAO/Y,KAAK,4CAKRkzD,EADA59B,UAAU49B,gBACQ59B,UAAU49B,gBAAgBtqE,KAAK0sC,WAG/BA,UAAUC,aAAa29B,gBAAgBtqE,KAAK0sC,UAAUC,cAG5E29B,EAAgB,CACZnkC,OAAO,EACPoB,OAAO,EACPgjC,OAAQ,WAEP39B,KAAKnZ,IACF,IAAI+2C,EAEJ,GAAI/2C,GACGA,EAAOue,aACPve,EAAOue,YAAYvpC,OAAS,EAAG,CAClC,MAAMgiE,EAAah3C,EAAOuX,iBAAiB,GAGvCy/B,IACAD,EAA0BC,EAAWx4B,iBAAiB54B,EAAQs4B,oBAGlE64B,EAA0Bt7B,QAAQC,UAGtCq7B,EAAwB59B,KAAK,IACzBlvB,EAAS,CACL+V,SACAqU,SAAUrU,EAAOra,QAG5Bk2B,MAAMp4B,IACH,MAAMwzD,EAAe,CACjBC,UAAWzzD,GAASA,EAAMnY,KAC1B6rE,SAAU1zD,GAASA,EAAMP,QACzBk0D,WAAY3zD,GAASA,EAAMZ,OAG/B6Z,EAAOjZ,MAAM,wBAAyBwzD,GAElCA,EAAaE,WAAmE,IAAvDF,EAAaE,SAAS90D,QAAQ,oBAGvDu0D,EAAc,IAAIvyC,IAAgBipB,sBAKtCspB,EAAc,IAAIvyC,IAAgBipB,oCA2DnC0oB,Q,4DC1Qf,MAAMqB,EAAyBx7C,EAAQ,KAQjC4C,EAAa,CAgBfC,WACQqb,EACAn5B,EACAmqC,EACAusB,EACAC,EACAX,GACJ,MAAMvrE,EAAIyO,SAEJ09D,EAASnsE,EAAEiU,cADD,UAEVm4D,EAAgBpsE,EAAEkkB,qBAFR,UAEsC,GAItD,GAFAioD,EAAO52D,MAAQA,EAEX02D,EAAa,CAGb,MAAMI,EAAWL,IAEjB,GAAIK,EAAU,CACV,MAAMC,EAAYD,EAAS39B,IACrB69B,EACAD,EAAU/rC,UAAU,EAAG+rC,EAAUE,YAAY,KAAO,GAEtDF,GAAaC,IAEb79B,EAAM69B,EAAgB79B,IAK9Bw9B,IACAC,EAAOM,OAASP,GAEhBX,IACAY,EAAOr9C,QAAUy8C,GAGrBY,EAAOz9B,IAAMA,EACTgR,EACA0sB,EAAc5yD,WAAWkzD,aAAaP,EAAQC,GAE9CA,EAAc5yD,WAAWxF,YAAYm4D,KAOjDhtE,EAAOD,QAAUk0B,G,22BC9DjB,MAAM/B,EAASF,oBAAUM,GAsCZk7C,EAA8B,CAIvC5V,OAAQ,SAQR6V,SAAU,WAKVC,YAAa,cAKbC,UAAW,aAOA,MAAMC,EAwBjB,8BACQC,EACA10B,EACA20B,EACAC,EACAC,GACJ,OAAKH,EAKME,EAIAP,EAA4B5V,OAInC7jC,IAAQk6C,qCACHD,EAGM70B,EACA20B,EACDN,EAA4BE,YAC5BF,EAA4BG,UAG/BH,EAA4BC,SAPxBD,EAA4B5V,OAYpCze,EACDq0B,EAA4B5V,OAC5B4V,EAA4BC,SA1BvBD,EAA4BE,YAyC3C,8BAA8BK,EAAcC,GACxC,OAAKj6C,IAAQk6C,qCAMNF,IAAiBC,EAClBR,EAA4B5V,OAC5B4V,EAA4BE,YALvBF,EAA4B5V,OAoB3C1tD,YAAYgkE,EAAK13C,EAAYpb,GACzBnH,KAAKi6D,IAAMA,EACXj6D,KAAKuiB,WAAaA,EAQlBviB,KAAKk6D,YAAc,GAQnBl6D,KAAKm6D,kBAAoB,GAazBn6D,KAAKo6D,kBACsC,iBAA9BjzD,EAAQizD,kBACXjzD,EAAQizD,kBA/LY,IAyM9Bp6D,KAAKq6D,eACmC,iBAA3BlzD,EAAQkzD,eACXlzD,EAAQkzD,eAnMO,IA0NzBr6D,KAAKs6D,kBAAoB,GACzBr8C,EAAO/Y,KAAM,0BAAyBlF,KAAKq6D,gBAY3Cr6D,KAAKu6D,sBAAwB,IAAIh7C,IASjCvf,KAAKw6D,gBAAkB,IAAIj7C,IAQ3Bvf,KAAKy6D,oBAAsB,IAAIl7C,IAUnCm7C,uBAAuBxzD,GACnB,OAAOlH,KAAKi6D,IAAI/0B,UAAUh+B,GACpBlH,KAAKq6D,eAAiBr6D,KAAKo6D,kBAOrCxvD,OAEI5K,KAAK26D,6BACC36D,KAAK46D,4BAA4B9sE,KAAKkS,MAE5CA,KAAKi6D,IAAIr2B,YACL5K,IAAUnR,6BACV7nB,KAAK26D,8BAGT36D,KAAK66D,aAAe76D,KAAK86D,8BAA8BhtE,KAAKkS,MAC5DA,KAAKuiB,WAAWZ,GAAGgB,aAAkC3iB,KAAK66D,cAG1D76D,KAAK+6D,YAAc/6D,KAAKg7D,WAAWltE,KAAKkS,MACxCA,KAAKuiB,WAAWZ,GAAGgB,YAAiC3iB,KAAK+6D,aAKrDj7C,IAAQk6C,uCAERh6D,KAAKi7D,iBAAmBj7D,KAAKk7D,gBAAgBptE,KAAKkS,MAClDA,KAAKi6D,IAAIr2B,YACL5K,IAAU1Q,kBAAmBtoB,KAAKi7D,kBAEtCj7D,KAAKm7D,mBAAqBn7D,KAAKo7D,kBAAkBttE,KAAKkS,MACtDA,KAAKi6D,IAAIr2B,YACL5K,IAAUxQ,oBAAqBxoB,KAAKm7D,oBAIxCn7D,KAAKq7D,oBAAsBr7D,KAAKs7D,mBAAmBxtE,KAAKkS,MACxDA,KAAKuiB,WAAWZ,GACZgB,cACA3iB,KAAKq7D,qBAETr7D,KAAKu7D,sBAAwBv7D,KAAKw7D,qBAAqB1tE,KAAKkS,MAC5DA,KAAKuiB,WAAWZ,GACZgB,gBACA3iB,KAAKu7D,uBAITv7D,KAAKy7D,yBACCz7D,KAAK07D,wBAAwB5tE,KAAKkS,MAGxCA,KAAK27D,yBACC37D,KAAK47D,wBAAwB9tE,KAAKkS,OAG5CA,KAAKyjC,gBAAkBzjC,KAAKyjC,gBAAgB31C,KAAKkS,MACjDA,KAAKuiB,WAAWZ,GACZgB,2BACA3iB,KAAKyjC,iBAETzjC,KAAK67D,qBACC77D,KAAK86D,8BAA8BhtE,KAAKkS,MAC9CA,KAAKi6D,IAAIt4C,GACLqX,IAAU/Q,oBAAqBjoB,KAAK67D,sBAO5C74C,UAEIhjB,KAAKi6D,IAAIn4C,eACLkX,IAAUnR,6BACV7nB,KAAK26D,8BAEL76C,IAAQk6C,uCACRh6D,KAAKi6D,IAAIn4C,eACLkX,IAAU1Q,kBACVtoB,KAAKi7D,kBACTj7D,KAAKi6D,IAAIn4C,eACLkX,IAAUxQ,oBACVxoB,KAAKm7D,oBAETn7D,KAAKuiB,WAAWksB,IACZ9rB,cACA3iB,KAAKq7D,qBACTr7D,KAAKuiB,WAAWksB,IACZ9rB,gBACA3iB,KAAKu7D,wBAGbv7D,KAAKuiB,WAAWksB,IACZ9rB,2BACA3iB,KAAKyjC,iBAETzjC,KAAKi6D,IAAIn4C,eACLkX,IAAU/Q,oBAAqBjoB,KAAK67D,sBAExC77D,KAAKuiB,WAAWksB,IACZ9rB,aAAkC3iB,KAAK66D,cAE3C76D,KAAKuiB,WAAWksB,IACZ9rB,YAAiC3iB,KAAK+6D,aAE1C,MAAMe,EAAiB9uE,OAAOuY,KAAKvF,KAAKk6D,aAExC,IAAK,MAAM/uC,KAAiB2wC,EACxB97D,KAAKyN,aAAa0d,GAClBnrB,KAAK+7D,uBAAuB5wC,GAGhC,IAAK,MAAMjkB,KAAMlH,KAAKy6D,oBACdz6D,KAAKy6D,oBAAoBtsE,eAAe+Y,IACxClH,KAAKg7D,WAAW9zD,GAKxBlH,KAAKm6D,kBAAoB,GAU7BS,4BAA4BoB,EAAYC,GAEpCh+C,EAAOhZ,MACF,mDAAkDkD,KAAKgM,WACpD6nD,MAAeC,KAGnBD,IAAeh8D,KAAKuiB,WAAW25C,aAE/Bl8D,KAAKm6D,kBAAkB6B,GAAcC,EACrCj8D,KAAKm8D,0BAA0BH,IASvCI,wBAAwBC,EAAaC,GACjC,GAAID,EAAYE,wBAA0BD,EAAW,CAEjD,MAAMN,EAAaK,EAAYG,QAE/BH,EAAYI,qBAAqBH,GAEjCr+C,EAAOhZ,MACF,6BAA4BkD,KAAKgM,UAAU6nD,MACxCM,KAGRl9C,IAAWkH,QACP1Z,KAAKwL,UAAU,CACXlR,GAAI,mBACJm1D,YAAaL,EACbjsD,OAAQusD,KAIhBt8D,KAAKuiB,WAAW/C,aAAa0D,KACzBP,kCACAq5C,EAAYM,IAWxB7uD,aAAa0d,GACLnrB,KAAKk6D,YAAY/uC,KACjBj/B,OAAOuhB,aAAazN,KAAKk6D,YAAY/uC,IACrCnrB,KAAKk6D,YAAY/uC,GAAiB,MAU1C4wC,uBAAuB5wC,GACnBnrB,KAAKs6D,kBAAkBnvC,GAAiB,KAU5CmwC,mBAAmBoB,GACVA,EAAYn3C,WACNm3C,EAAYp1B,YAAc/I,MAEjCtgB,EAAOhZ,MACF,uCACGy3D,EAAYC,oBAEpBD,EAAY/6C,GACRi7C,qBACA58D,KAAKy7D,0BACTiB,EAAY/6C,GACRi7C,0BACAp+B,GAAax+B,KAAK27D,yBAAyBe,EAAal+B,KAWpEg9B,qBAAqBkB,GACjB,IAAKA,EAAYn3C,WACNm3C,EAAYp1B,YAAc/I,IAAiB,CAElD,MAAMy9B,EAAaU,EAAYC,mBAE/B1+C,EAAOhZ,MAAO,qCAAoC+2D,GAElDU,EAAYjuB,IACRmuB,qBACA58D,KAAKy7D,0BAETz7D,KAAKyN,aAAauuD,GAClBh8D,KAAK+7D,uBAAuBC,GAE5Bh8D,KAAKm8D,0BAA0BH,IAiBvCjC,mBAAmBsC,GACf,IAAKv8C,IAAQk6C,qCACT,OAAO,EAGX,MAAM9yD,EAAKm1D,EAAYG,QACjBK,EAAsBR,EAAYS,8BAClCxC,EAAoBt6D,KAAKs6D,kBAAkBpzD,GAC3C0G,EAAU5N,KAAK06D,uBAAuBxzD,GAE5C,OAAO21D,GAC6B,iBAAtBvC,GACNnyD,KAAKgM,MAAQmmD,GAAsB1sD,EAQ/CktD,gCACI,MAAMiC,EAAe/8D,KAAKuiB,WAAWy6C,kBAErC,IAAK,MAAMX,KAAeU,EACtB/8D,KAAKm8D,0BAA0BE,EAAYG,SAUnDL,0BAA0Bj1D,GACtB,MAAMm1D,EAAcr8D,KAAKuiB,WAAW06C,mBAAmB/1D,GAEvD,IAAKm1D,EAQD,YAFAp+C,EAAOhZ,MAAO,gDAA+CiC,GAKjE,MAAMg2D,EAAYl9D,KAAKuiB,WAAW46C,cAC5BC,EAAsBp9D,KAAKq9D,qBAAqBn2D,GAChDo2D,EAAwC,IAAxBt9D,KAAKi6D,IAAInzB,WAIzBgzB,EAAeuC,EAAYvC,gBAAkBwD,EAC7CvD,EAAqB/5D,KAAK+5D,mBAAmBsC,GAC7Cn3B,EAAYllC,KAAKi6D,IAAI/0B,UAAUh+B,GACrC,IAAIq2D,EAAoBv9D,KAAKm6D,kBAAkBjzD,GAEd,kBAAtBq2D,IAGPt/C,EAAOhZ,MAAM,uDACbs4D,GAAoB,GAGxB,MAAMC,EACAN,EACIvD,EAAmC8D,uBACjC3D,EACAC,GACFJ,EAAmC+D,uBACjCH,EACAr4B,EACAk4B,EACAtD,EACAC,GAIRyD,IAAajE,EAA4BG,WACzC15D,KAAK29D,qBAAqBz2D,GAG9B+W,EAAOhZ,MACF,8BAA6BiC,sBAC1B4yD,qBACAyD,yBACAxD,eACAmD,mBACAh4B,iCACAm3B,EAAYE,4BAA4BiB,KAEhD,MAAMI,EAAsB59D,KAAKy6D,oBAAoBvzD,IAAO,GAI5D,KAAM,QAAS02D,MACN,qBAAsBA,IACxBA,EAAoBzf,MAAQ+e,GAC5BU,EAAoBC,mBAAqBL,EAAU,CAEtD,MAAMM,EAAQ31D,KAAKgM,MAanB,GAXAnU,KAAK+9D,0CAA0C72D,EAAI42D,GAEnD99D,KAAKy6D,oBAAoBvzD,GAAzB,OACO02D,GADP,IAEIC,iBAAkBL,EAClBrf,IAAK+e,EACLc,UAAWF,MAKT,cAAe99D,KAAKy6D,oBAAoBvzD,IAAM,CAChD,MAAM20B,EAAcwgC,EAAY4B,qBAAqB1/B,KAEjDnoC,MAAMC,QAAQwlC,IAAuC,IAAvBA,EAAYtlC,SAC1CyJ,KAAKy6D,oBAAoBvzD,GAAIs3B,UAAY3C,EAAY,GAAG2C,YAIpEx+B,KAAKo8D,wBAAwBC,EAAamB,GAU9CO,0CAA0C72D,EAAI42D,GAC1C,MAAMI,EAA8Bl+D,KAAKy6D,oBAAoBvzD,GAEzDg3D,GACG,cAAeA,GACf,cAAeA,GACf,qBAAsBA,GACtB,QAASA,IACZA,EAA4B3wE,MAAQuwE,EAAQI,EAA4BF,UACxE5+C,IAAWoI,cACPgE,YAAuC0yC,KAYnDz6B,gBAAgB06B,EAAe,GAAIC,EAAgB,IAC/C,MAAMjqD,EAAMhM,KAAKgM,MAEjB8J,EAAOhZ,MACH,yBAA0Bk5D,EAAcC,EAAejqD,GAE3D,IAAK,MAAMjN,KAAMi3D,EACbn+D,KAAKu6D,sBAAsB72C,OAAOxc,GAClClH,KAAK29D,qBAAqBz2D,GAC1BlH,KAAKm8D,0BAA0Bj1D,GAEnC,IAAK,MAAMA,KAAMk3D,EAEbp+D,KAAKu6D,sBAAsBn5C,IAAIla,EAAIiN,GACnCnU,KAAKm8D,0BAA0Bj1D,GAYvCy2D,qBAAqBxyC,GACjB,MAAMkzC,EAASr+D,KAAKw6D,gBAAgBrtE,IAAIg+B,GAEpCkzC,IACA5wD,aAAa4wD,GACbr+D,KAAKw6D,gBAAgB92C,OAAOyH,IAkBpCkyC,qBAAqBlyC,GACjB,MAAMovC,EACAv6D,KAAKu6D,sBAAsBptE,IAAIg+B,GAErC,GAAIovC,GACIpyD,KAAKgM,MAAQomD,GAptBK,IAstBtB,OAAO,EAcX,OARev6D,KAAKw6D,gBAAgBrtE,IAAIg+B,IAGpCnrB,KAAKw6D,gBAAgBp5C,IAAI+J,EAAe5gB,WACpC,IAAMvK,KAAKm8D,0BAA0BhxC,GAhuBnB,OAouBnB,EAQX6vC,WAAW9zD,GACPlH,KAAK+9D,0CAA0C72D,EAAIiB,KAAKgM,cACjDnU,KAAKy6D,oBAAoBvzD,GASpCg0D,gBAAgB58B,GACZ,MAAMnT,EAAgBmT,EAAMq+B,mBACtBN,EAAcr8D,KAAKuiB,WAAW06C,mBAAmB9xC,GAGvD,GADAlN,EAAOhZ,MAAO,6BAA4BkmB,EAAiBhjB,KAAKgM,OAC3DkoD,GAML,GADAr8D,KAAKs6D,kBAAkBnvC,GAAiBhjB,KAAKgM,OACxCkoD,EAAYvC,eAAgB,CAI7B95D,KAAKyN,aAAa0d,GAGlB,MAAMvd,EAAU5N,KAAK06D,uBAAuBvvC,GAE5CnrB,KAAKk6D,YAAY/uC,GAAiBj/B,OAAOqe,WAAW,KAChD0T,EAAOhZ,MACF,6BAA4BkmB,4BACvBvd,QACV5N,KAAKyN,aAAa0d,GAClBnrB,KAAKm8D,0BAA0BhxC,IAChCvd,SApBHqQ,EAAOjZ,MAAO,0BAAyBmmB,GA8B/CiwC,kBAAkB98B,GACd,MAAMnT,EAAgBmT,EAAMq+B,mBAE5B1+C,EAAOhZ,MACF,+BAA8BkmB,EAAiBhjB,KAAKgM,OAEzDnU,KAAKyN,aAAa0d,GAClBnrB,KAAK+7D,uBAAuB5wC,GAE5BnrB,KAAKm8D,0BAA0BhxC,GASnCuwC,wBAAwBp9B,GACpB,MAAMnT,EAAgBmT,EAAMq+B,mBAE5B1+C,EAAOhZ,MACF,8CAA6CkmB,EAC9CmT,EAAMggC,WAEVt+D,KAAKm8D,0BAA0BhxC,GAUnCywC,wBAAwBt9B,EAAOr3B,GAC3B,MAAMC,EAAKo3B,EAAMq+B,mBACXmB,EAAQ31D,KAAKgM,MAEnBnU,KAAK+9D,0CAA0C72D,EAAI42D,GAEnD99D,KAAKy6D,oBAAoBvzD,GAAzB,OACOlH,KAAKy6D,oBAAoBvzD,IAAO,IADvC,IAEIs3B,UAAWv3B,EACX+2D,UAAWF,Q,gGC52BvB,iEAIO,MAAMS,EAAqB,uBAE5BtgD,EAASF,oBAAUM,GASlB,MAAMmgD,UAAoBpkC,IAI7BnkC,cACIokC,QACAr6B,KAAKy+D,SAAW,CACZC,UAAU,GAQlBC,mBAAkB,SAAED,IAChBzgD,EAAOhZ,MAAM,oBAAqB,CAAEy5D,aACpC1+D,KAAKy+D,SAAW,CACZC,UAAuB,IAAbA,GAEd1+D,KAAKwf,aAAa0D,KAAKq7C,EAAoBv+D,KAAKy+D,UAUpDC,WACI,OAAkC,IAA3B1+D,KAAKy+D,SAASC,UAI7B,MAAME,EAAc,IAAIJ,EAETI,Q,+EChDf,IAAIC,EAAW,CAIfA,mBAA8B,WAC5B,OAAOzzD,KAAKC,SAAS3U,SAAS,IAAI0e,OAAO,EAAG,MAI9CypD,EAASC,WAAaD,EAASE,qBAG/BF,EAASG,WAAa,SAASC,GAC7B,OAAOA,EAAKpgB,OAAO97C,MAAM,MAAMyC,KAAI,SAAShB,GAC1C,OAAOA,EAAKq6C,WAIhBggB,EAASK,cAAgB,SAASD,GAEhC,OADYA,EAAKl8D,MAAM,QACVyC,KAAI,SAAS25D,EAAMxuC,GAC9B,OAAQA,EAAQ,EAAI,KAAOwuC,EAAOA,GAAMtgB,OAAS,WAKrDggB,EAASO,eAAiB,SAASH,GACjC,IAAII,EAAWR,EAASK,cAAcD,GACtC,OAAOI,GAAYA,EAAS,IAI9BR,EAASS,iBAAmB,SAASL,GACnC,IAAII,EAAWR,EAASK,cAAcD,GAEtC,OADAI,EAAS3xC,QACF2xC,GAITR,EAASU,YAAc,SAASN,EAAMO,GACpC,OAAOX,EAASG,WAAWC,GAAMvuC,QAAO,SAASlsB,GAC/C,OAAgC,IAAzBA,EAAKZ,QAAQ47D,OAOxBX,EAASY,eAAiB,SAASj7D,GAqBjC,IApBA,IAAIR,EAQAyqB,EAAY,CACdE,YANA3qB,EADmC,IAAjCQ,EAAKZ,QAAQ,gBACPY,EAAK2oB,UAAU,IAAIpqB,MAAM,KAEzByB,EAAK2oB,UAAU,IAAIpqB,MAAM,MAIf,GAClB6rB,UAAWtW,SAAStU,EAAM,GAAI,IAC9B0E,SAAU1E,EAAM,GAAGrB,cACnBkP,SAAUyG,SAAStU,EAAM,GAAI,IAC7B6qB,GAAI7qB,EAAM,GACV07D,QAAS17D,EAAM,GACf2pB,KAAMrV,SAAStU,EAAM,GAAI,IAEzBiD,KAAMjD,EAAM,IAGL1X,EAAI,EAAGA,EAAI0X,EAAMzN,OAAQjK,GAAK,EACrC,OAAQ0X,EAAM1X,IACZ,IAAK,QACHmiC,EAAUkxC,eAAiB37D,EAAM1X,EAAI,GACrC,MACF,IAAK,QACHmiC,EAAUmxC,YAActnD,SAAStU,EAAM1X,EAAI,GAAI,IAC/C,MACF,IAAK,UACHmiC,EAAUoxC,QAAU77D,EAAM1X,EAAI,GAC9B,MACF,IAAK,QACHmiC,EAAU1B,MAAQ/oB,EAAM1X,EAAI,GAC5BmiC,EAAUqxC,iBAAmB97D,EAAM1X,EAAI,GACvC,MACF,QACEmiC,EAAUzqB,EAAM1X,IAAM0X,EAAM1X,EAAI,GAItC,OAAOmiC,GAITowC,EAASkB,eAAiB,SAAStxC,GACjC,IAAIuD,EAAM,GACVA,EAAI9uB,KAAKurB,EAAUE,YACnBqD,EAAI9uB,KAAKurB,EAAUG,WACnBoD,EAAI9uB,KAAKurB,EAAU/lB,SAASs3D,eAC5BhuC,EAAI9uB,KAAKurB,EAAU5c,UACnBmgB,EAAI9uB,KAAKurB,EAAUixC,SAAWjxC,EAAUI,IACxCmD,EAAI9uB,KAAKurB,EAAUd,MAEnB,IAAI1mB,EAAOwnB,EAAUxnB,KAkBrB,OAjBA+qB,EAAI9uB,KAAK,OACT8uB,EAAI9uB,KAAK+D,GACI,SAATA,GAAmBwnB,EAAUkxC,gBAC7BlxC,EAAUmxC,cACZ5tC,EAAI9uB,KAAK,SACT8uB,EAAI9uB,KAAKurB,EAAUkxC,gBACnB3tC,EAAI9uB,KAAK,SACT8uB,EAAI9uB,KAAKurB,EAAUmxC,cAEjBnxC,EAAUoxC,SAAgD,QAArCpxC,EAAU/lB,SAAS/F,gBAC1CqvB,EAAI9uB,KAAK,WACT8uB,EAAI9uB,KAAKurB,EAAUoxC,WAEjBpxC,EAAUqxC,kBAAoBrxC,EAAU1B,SAC1CiF,EAAI9uB,KAAK,SACT8uB,EAAI9uB,KAAKurB,EAAUqxC,kBAAoBrxC,EAAU1B,QAE5C,aAAeiF,EAAI7uB,KAAK,MAKjC07D,EAASoB,gBAAkB,SAASz7D,GAClC,OAAOA,EAAK4Q,OAAO,IAAIrS,MAAM,MAK/B87D,EAASqB,YAAc,SAAS17D,GAC9B,IAAIR,EAAQQ,EAAK4Q,OAAO,GAAGrS,MAAM,KAC7Bo9D,EAAS,CACXptB,YAAaz6B,SAAStU,EAAM0pB,QAAS,KAUvC,OAPA1pB,EAAQA,EAAM,GAAGjB,MAAM,KAEvBo9D,EAAOtzE,KAAOmX,EAAM,GACpBm8D,EAAOC,UAAY9nD,SAAStU,EAAM,GAAI,IACtCm8D,EAAOlyC,SAA4B,IAAjBjqB,EAAMzN,OAAe+hB,SAAStU,EAAM,GAAI,IAAM,EAEhEm8D,EAAOE,YAAcF,EAAOlyC,SACrBkyC,GAKTtB,EAASyB,YAAc,SAAS9tC,GAC9B,IAAIhD,EAAKgD,EAAMugB,iBACoBxyC,IAA/BiyB,EAAM+tC,uBACR/wC,EAAKgD,EAAM+tC,sBAEb,IAAItyC,EAAWuE,EAAMvE,UAAYuE,EAAM6tC,aAAe,EACtD,MAAO,YAAc7wC,EAAK,IAAMgD,EAAM3lC,KAAO,IAAM2lC,EAAM4tC,WACvC,IAAbnyC,EAAiB,IAAMA,EAAW,IAAM,QAM/C4wC,EAASnvC,YAAc,SAASlrB,GAC9B,IAAIR,EAAQQ,EAAK4Q,OAAO,GAAGrS,MAAM,KACjC,MAAO,CACLmE,GAAIoR,SAAStU,EAAM,GAAI,IACvB2rB,UAAW3rB,EAAM,GAAGJ,QAAQ,KAAO,EAAII,EAAM,GAAGjB,MAAM,KAAK,GAAK,WAChE6sB,IAAK5rB,EAAM,KAMf66D,EAAS2B,YAAc,SAASC,GAC9B,MAAO,aAAeA,EAAgBv5D,IAAMu5D,EAAgBC,cACvDD,EAAgB9wC,WAA2C,aAA9B8wC,EAAgB9wC,UAC1C,IAAM8wC,EAAgB9wC,UACtB,IACJ,IAAM8wC,EAAgB7wC,IAAM,QAMlCivC,EAAStwC,UAAY,SAAS/pB,GAI5B,IAHA,IACIosC,EADAuvB,EAAS,GAETn8D,EAAQQ,EAAK4Q,OAAO5Q,EAAKZ,QAAQ,KAAO,GAAGb,MAAM,KAC5C7J,EAAI,EAAGA,EAAI8K,EAAMzN,OAAQ2C,IAEhCinE,GADAvvB,EAAK5sC,EAAM9K,GAAG2lD,OAAO97C,MAAM,MACjB,GAAG87C,QAAUjO,EAAG,GAE5B,OAAOuvB,GAITtB,EAAS8B,UAAY,SAASnuC,GAC5B,IAAIhuB,EAAO,GACPgrB,EAAKgD,EAAMugB,YAIf,QAHmCxyC,IAA/BiyB,EAAM+tC,uBACR/wC,EAAKgD,EAAM+tC,sBAET/tC,EAAMouC,YAAc5zE,OAAOuY,KAAKitB,EAAMouC,YAAYrqE,OAAQ,CAC5D,IAAIk5B,EAAS,GACbziC,OAAOuY,KAAKitB,EAAMouC,YAAYxxD,SAAQ,SAASyxD,GACzCruC,EAAMouC,WAAWC,GACnBpxC,EAAOvsB,KAAK29D,EAAQ,IAAMruC,EAAMouC,WAAWC,IAE3CpxC,EAAOvsB,KAAK29D,MAGhBr8D,GAAQ,UAAYgrB,EAAK,IAAMC,EAAOtsB,KAAK,KAAO,OAEpD,OAAOqB,GAKTq6D,EAASiC,YAAc,SAASt8D,GAC9B,IAAIR,EAAQQ,EAAK4Q,OAAO5Q,EAAKZ,QAAQ,KAAO,GAAGb,MAAM,KACrD,MAAO,CACLkE,KAAMjD,EAAM0pB,QACZulB,UAAWjvC,EAAMb,KAAK,OAI1B07D,EAASkC,YAAc,SAASvuC,GAC9B,IAAIlD,EAAQ,GACRE,EAAKgD,EAAMugB,YAYf,YAXmCxyC,IAA/BiyB,EAAM+tC,uBACR/wC,EAAKgD,EAAM+tC,sBAET/tC,EAAMwuC,cAAgBxuC,EAAMwuC,aAAazqE,QAE3Ci8B,EAAMwuC,aAAa5xD,SAAQ,SAAS+iC,GAClC7iB,GAAS,aAAeE,EAAK,IAAM2iB,EAAGlrC,MACrCkrC,EAAGc,WAAad,EAAGc,UAAU18C,OAAS,IAAM47C,EAAGc,UAAY,IACxD,UAGD3jB,GAKTuvC,EAASoC,eAAiB,SAASz8D,GACjC,IAAI08D,EAAK18D,EAAKZ,QAAQ,KAClBI,EAAQ,CACVghB,KAAM1M,SAAS9T,EAAK4Q,OAAO,EAAG8rD,EAAK,GAAI,KAErCC,EAAQ38D,EAAKZ,QAAQ,IAAKs9D,GAO9B,OANIC,GAAS,GACXn9D,EAAM1G,UAAYkH,EAAK4Q,OAAO8rD,EAAK,EAAGC,EAAQD,EAAK,GACnDl9D,EAAMzW,MAAQiX,EAAK4Q,OAAO+rD,EAAQ,IAElCn9D,EAAM1G,UAAYkH,EAAK4Q,OAAO8rD,EAAK,GAE9Bl9D,GAGT66D,EAASuC,eAAiB,SAAS58D,GACjC,IAAIR,EAAQQ,EAAK4Q,OAAO,IAAIrS,MAAM,KAClC,MAAO,CACLouB,UAAWntB,EAAM0pB,QACjB8C,MAAOxsB,EAAMwB,KAAI,SAASwf,GACxB,OAAO1M,SAAS0M,EAAM,SAO5B65C,EAASwC,OAAS,SAASC,GACzB,IAAI5xB,EAAMmvB,EAASU,YAAY+B,EAAc,UAAU,GACvD,GAAI5xB,EACF,OAAOA,EAAIt6B,OAAO,IAItBypD,EAASxwC,iBAAmB,SAAS7pB,GACnC,IAAIR,EAAQQ,EAAK4Q,OAAO,IAAIrS,MAAM,KAClC,MAAO,CACLw+D,UAAWv9D,EAAM,GAAGrB,cACpBpV,MAAOyW,EAAM,KAOjB66D,EAAS2C,kBAAoB,SAASF,EAAcvxC,GAKlD,MAAO,CACL0xC,KAAM,OACNC,aANU7C,EAASU,YAAY+B,EAAevxC,EAC9C,kBAKoBvqB,IAAIq5D,EAASxwC,oBAKrCwwC,EAAS8C,oBAAsB,SAASlyC,EAAQmyC,GAC9C,IAAI5vC,EAAM,WAAa4vC,EAAY,OAInC,OAHAnyC,EAAOiyC,aAAatyD,SAAQ,SAASyyD,GACnC7vC,GAAO,iBAAmB6vC,EAAGN,UAAY,IAAMM,EAAGt0E,MAAQ,UAErDykC,GAKT6sC,EAASiD,gBAAkB,SAASt9D,GAClC,IAAIR,EAAQQ,EAAK4Q,OAAO,GAAGrS,MAAM,KACjC,MAAO,CACL3F,IAAKkb,SAAStU,EAAM,GAAI,IACxB+9D,YAAa/9D,EAAM,GACnBg+D,UAAWh+D,EAAM,GACjBi+D,cAAej+D,EAAMk2B,MAAM,KAI/B2kC,EAASqD,gBAAkB,SAAStB,GAClC,MAAO,YAAcA,EAAWxjE,IAAM,IACpCwjE,EAAWmB,YAAc,KACQ,iBAAzBnB,EAAWoB,UACfnD,EAASsD,qBAAqBvB,EAAWoB,WACzCpB,EAAWoB,YACdpB,EAAWqB,cAAgB,IAAMrB,EAAWqB,cAAc9+D,KAAK,KAAO,IACvE,QAKJ07D,EAASuD,qBAAuB,SAASJ,GACvC,GAAqC,IAAjCA,EAAUp+D,QAAQ,WACpB,OAAO,KAET,IAAII,EAAQg+D,EAAU5sD,OAAO,GAAGrS,MAAM,KACtC,MAAO,CACLs/D,UAAW,SACXC,QAASt+D,EAAM,GACfu+D,SAAUv+D,EAAM,GAChBw+D,SAAUx+D,EAAM,GAAKA,EAAM,GAAGjB,MAAM,KAAK,QAAKxC,EAC9CkiE,UAAWz+D,EAAM,GAAKA,EAAM,GAAGjB,MAAM,KAAK,QAAKxC,IAInDs+D,EAASsD,qBAAuB,SAASH,GACvC,OAAOA,EAAUK,UAAY,IACzBL,EAAUM,SACXN,EAAUO,SAAW,IAAMP,EAAUO,SAAW,KAChDP,EAAUQ,UAAYR,EAAUS,UAC7B,IAAMT,EAAUQ,SAAW,IAAMR,EAAUS,UAC3C,KAIR5D,EAAS6D,oBAAsB,SAASpB,EAAcvxC,GAGpD,OAFY8uC,EAASU,YAAY+B,EAAevxC,EAC9C,aACWvqB,IAAIq5D,EAASiD,kBAM5BjD,EAAS8D,iBAAmB,SAASrB,EAAcvxC,GACjD,IAAIhD,EAAQ8xC,EAASU,YAAY+B,EAAevxC,EAC9C,gBAAgB,GACdjD,EAAM+xC,EAASU,YAAY+B,EAAevxC,EAC5C,cAAc,GAChB,OAAMhD,GAASD,EAGR,CACLgzC,iBAAkB/yC,EAAM3X,OAAO,IAC/BumC,SAAU7uB,EAAI1X,OAAO,KAJd,MASXypD,EAAS+D,mBAAqB,SAASnzC,GACrC,MAAO,eAAiBA,EAAOqwC,iBAAxB,iBACYrwC,EAAOksB,SAAW,QAIvCkjB,EAASgE,mBAAqB,SAASvB,GASrC,IARA,IAAIwB,EAAc,CAChBC,OAAQ,GACRC,iBAAkB,GAClBC,cAAe,GACfC,KAAM,IAGJp1C,EADQ+wC,EAASG,WAAWsC,GACd,GAAGv+D,MAAM,KAClBzW,EAAI,EAAGA,EAAIwhC,EAAMv3B,OAAQjK,IAAK,CACrC,IAAIkjC,EAAK1B,EAAMxhC,GACX62E,EAAatE,EAASU,YACxB+B,EAAc,YAAc9xC,EAAK,KAAK,GACxC,GAAI2zC,EAAY,CACd,IAAI3wC,EAAQqsC,EAASqB,YAAYiD,GAC7BC,EAAQvE,EAASU,YACnB+B,EAAc,UAAY9xC,EAAK,KAQjC,OANAgD,EAAMouC,WAAawC,EAAM7sE,OAASsoE,EAAStwC,UAAU60C,EAAM,IAAM,GACjE5wC,EAAMwuC,aAAenC,EAASU,YAC5B+B,EAAc,aAAe9xC,EAAK,KACjChqB,IAAIq5D,EAASiC,aAChBgC,EAAYC,OAAO7/D,KAAKsvB,GAEhBA,EAAM3lC,KAAKmzE,eACjB,IAAK,MACL,IAAK,SACH8C,EAAYG,cAAc//D,KAAKsvB,EAAM3lC,KAAKmzE,iBAWlD,OAJAnB,EAASU,YAAY+B,EAAc,aAAalyD,SAAQ,SAAS5K,GAC/Ds+D,EAAYE,iBAAiB9/D,KAAK27D,EAASnvC,YAAYlrB,OAGlDs+D,GAKTjE,EAASwE,oBAAsB,SAASjqC,EAAMqgB,GAC5C,IAAIznB,EAAM,GAGVA,GAAO,KAAOoH,EAAO,IACrBpH,GAAOynB,EAAKspB,OAAOxsE,OAAS,EAAI,IAAM,IACtCy7B,GAAO,sBACPA,GAAOynB,EAAKspB,OAAOv9D,KAAI,SAASgtB,GAC9B,YAAmCjyB,IAA/BiyB,EAAM+tC,qBACD/tC,EAAM+tC,qBAER/tC,EAAMugB,eACZ5vC,KAAK,KAAO,OAEf6uB,GAAO,uBACPA,GAAO,8BAGPynB,EAAKspB,OAAO3zD,SAAQ,SAASojB,GAC3BR,GAAO6sC,EAASyB,YAAY9tC,GAC5BR,GAAO6sC,EAAS8B,UAAUnuC,GAC1BR,GAAO6sC,EAASkC,YAAYvuC,MAE9B,IAAI8wC,EAAW,EAiBf,OAhBA7pB,EAAKspB,OAAO3zD,SAAQ,SAASojB,GACvBA,EAAM8wC,SAAWA,IACnBA,EAAW9wC,EAAM8wC,aAGjBA,EAAW,IACbtxC,GAAO,cAAgBsxC,EAAW,QAEpCtxC,GAAO,iBAEHynB,EAAKupB,kBACPvpB,EAAKupB,iBAAiB5zD,SAAQ,SAASm0D,GACrCvxC,GAAO6sC,EAAS2B,YAAY+C,MAIzBvxC,GAKT6sC,EAAS2E,2BAA6B,SAASlC,GAC7C,IAcI9e,EAdAihB,EAAqB,GACrBX,EAAcjE,EAASgE,mBAAmBvB,GAC1CoC,GAAuD,IAA9CZ,EAAYG,cAAcr/D,QAAQ,OAC3C+/D,GAA6D,IAAjDb,EAAYG,cAAcr/D,QAAQ,UAG9C4sB,EAAQquC,EAASU,YAAY+B,EAAc,WAC5C97D,KAAI,SAAShB,GACZ,OAAOq6D,EAASoC,eAAez8D,MAEhCksB,QAAO,SAAS1sB,GACf,MAA2B,UAApBA,EAAM1G,aAEbyzB,EAAcP,EAAMj6B,OAAS,GAAKi6B,EAAM,GAAGxL,KAG3C4+C,EAAQ/E,EAASU,YAAY+B,EAAc,oBAC5C97D,KAAI,SAAShB,GAEZ,OADYA,EAAK4Q,OAAO,IAAIrS,MAAM,KACrByC,KAAI,SAAS25D,GACxB,OAAO7mD,SAAS6mD,EAAM,UAGxByE,EAAMrtE,OAAS,GAAKqtE,EAAM,GAAGrtE,OAAS,GAAKqtE,EAAM,GAAG,KAAO7yC,IAC7DyxB,EAAgBohB,EAAM,GAAG,IAG3Bd,EAAYC,OAAO3zD,SAAQ,SAASojB,GAClC,GAAiC,QAA7BA,EAAM3lC,KAAKmzE,eAA2BxtC,EAAMouC,WAAWiD,IAAK,CAC9D,IAAIC,EAAW,CACb9+C,KAAM+L,EACNgzC,iBAAkBzrD,SAASka,EAAMouC,WAAWiD,IAAK,KAE/C9yC,GAAeyxB,IACjBshB,EAASE,IAAM,CAACh/C,KAAMw9B,IAExBihB,EAAmBvgE,KAAK4gE,GACpBJ,KACFI,EAAWl3D,KAAKsL,MAAMtL,KAAKwL,UAAU0rD,KAC5BG,IAAM,CACbj/C,KAAM+L,EACNzhB,UAAWq0D,EAAY,aAAe,OAExCF,EAAmBvgE,KAAK4gE,QAII,IAA9BL,EAAmBltE,QAAgBw6B,GACrC0yC,EAAmBvgE,KAAK,CACtB8hB,KAAM+L,IAKV,IAAImH,EAAY2mC,EAASU,YAAY+B,EAAc,MAenD,OAdIppC,EAAU3hC,SAEV2hC,EADsC,IAApCA,EAAU,GAAGt0B,QAAQ,WACX0U,SAAS4f,EAAU,GAAG9iB,OAAO,GAAI,IACF,IAAlC8iB,EAAU,GAAGt0B,QAAQ,SAEqB,IAAvC0U,SAAS4f,EAAU,GAAG9iB,OAAO,GAAI,IAAa,IACnD,UAEK7U,EAEdkjE,EAAmBr0D,SAAQ,SAASqgB,GAClCA,EAAOy0C,WAAahsC,MAGjBurC,GAIT5E,EAASsF,oBAAsB,SAAS7C,GACtC,IAAI8C,EAAiB,GAIjBC,EAAaxF,EAASU,YAAY+B,EAAc,WACjD97D,KAAI,SAAShB,GACZ,OAAOq6D,EAASoC,eAAez8D,MAEhCksB,QAAO,SAAS36B,GACf,MAAyB,UAAlBA,EAAIuH,aACV,GACD+mE,IACFD,EAAeE,MAAQD,EAAW92E,MAClC62E,EAAep/C,KAAOq/C,EAAWr/C,MAKnC,IAAIu/C,EAAQ1F,EAASU,YAAY+B,EAAc,gBAC/C8C,EAAeI,YAAcD,EAAMhuE,OAAS,EAC5C6tE,EAAeK,SAA4B,IAAjBF,EAAMhuE,OAIhC,IAAImuE,EAAM7F,EAASU,YAAY+B,EAAc,cAG7C,OAFA8C,EAAeM,IAAMA,EAAInuE,OAAS,EAE3B6tE,GAKTvF,EAAS8F,UAAY,SAASrD,GAC5B,IAAIt9D,EACA4gE,EAAO/F,EAASU,YAAY+B,EAAc,WAC9C,GAAoB,IAAhBsD,EAAKruE,OAEP,MAAO,CAACgrB,QADRvd,EAAQ4gE,EAAK,GAAGxvD,OAAO,GAAGrS,MAAM,MACV,GAAIu7B,MAAOt6B,EAAM,IAEzC,IAAI6gE,EAAQhG,EAASU,YAAY+B,EAAc,WAC5C97D,KAAI,SAAShB,GACZ,OAAOq6D,EAASoC,eAAez8D,MAEhCksB,QAAO,SAASo0C,GACf,MAA+B,SAAxBA,EAAUxnE,aAErB,OAAIunE,EAAMtuE,OAAS,EAEV,CAACgrB,QADRvd,EAAQ6gE,EAAM,GAAGt3E,MAAMwV,MAAM,MACP,GAAIu7B,MAAOt6B,EAAM,SAFzC,GASF66D,EAASkG,qBAAuB,SAASzD,GACvC,IAEI0D,EAFAl3C,EAAQ+wC,EAASrxC,WAAW8zC,GAC5B2D,EAAcpG,EAASU,YAAY+B,EAAc,uBAEjD2D,EAAY1uE,OAAS,IACvByuE,EAAiB1sD,SAAS2sD,EAAY,GAAG7vD,OAAO,IAAK,KAEnDsE,MAAMsrD,KACRA,EAAiB,OAEnB,IAAIE,EAAWrG,EAASU,YAAY+B,EAAc,gBAClD,GAAI4D,EAAS3uE,OAAS,EACpB,MAAO,CACLo3B,KAAMrV,SAAS4sD,EAAS,GAAG9vD,OAAO,IAAK,IACvC1M,SAAUolB,EAAMF,IAChBo3C,eAAgBA,GAIpB,GADmBnG,EAASU,YAAY+B,EAAc,cACrC/qE,OAAS,EAAG,CAC3B,IAAIyN,EAAQ66D,EAASU,YAAY+B,EAAc,cAAc,GAC1DlsD,OAAO,IACPrS,MAAM,KACT,MAAO,CACL4qB,KAAMrV,SAAStU,EAAM,GAAI,IACzB0E,SAAU1E,EAAM,GAChBghE,eAAgBA,KAUtBnG,EAASsG,qBAAuB,SAAS13C,EAAOolB,GAC9C,IAAIuyB,EAAS,GAiBb,OAfEA,EADqB,cAAnB33C,EAAM/kB,SACC,CACP,KAAO+kB,EAAM2L,KAAO,MAAQ3L,EAAM/kB,SAAW,IAAMmqC,EAAKnqC,SAAW,OACnE,uBACA,eAAiBmqC,EAAKllB,KAAO,QAGtB,CACP,KAAOF,EAAM2L,KAAO,MAAQ3L,EAAM/kB,SAAW,IAAMmqC,EAAKllB,KAAO,OAC/D,uBACA,aAAeklB,EAAKllB,KAAO,IAAMklB,EAAKnqC,SAAW,mBAGzBnI,IAAxBsyC,EAAKmyB,gBACPI,EAAOliE,KAAK,sBAAwB2vC,EAAKmyB,eAAiB,QAErDI,EAAOjiE,KAAK,KAOrB07D,EAASwG,kBAAoB,WAC3B,OAAOj6D,KAAKC,SAAS3U,WAAW0e,OAAO,EAAG,KAQ5CypD,EAASyG,wBAA0B,SAASC,EAAQC,EAASC,GAC3D,IACI1xB,OAAsBxzC,IAAZilE,EAAwBA,EAAU,EAQhD,MAAO,aAFIC,GAAY,qBAGL,KARdF,GAGU1G,EAASwG,qBAKa,IAAMtxB,EADnC,yCAOT8qB,EAAS6G,kBAAoB,SAASC,EAAalsB,EAAMxyC,EAAMsa,GAC7D,IAAIyQ,EAAM6sC,EAASwE,oBAAoBsC,EAAYvsC,KAAMqgB,GAyBzD,GAtBAznB,GAAO6sC,EAAS+D,mBACd+C,EAAYC,YAAYC,sBAG1B7zC,GAAO6sC,EAAS8C,oBACdgE,EAAYG,cAAcD,qBACjB,UAAT5+D,EAAmB,UAAY,UAEjC+qB,GAAO,SAAW2zC,EAAYj2B,IAAM,OAEhCi2B,EAAYh2C,UACdqC,GAAO,KAAO2zC,EAAYh2C,UAAY,OAC7Bg2C,EAAYI,WAAaJ,EAAYK,YAC9Ch0C,GAAO,iBACE2zC,EAAYI,UACrB/zC,GAAO,iBACE2zC,EAAYK,YACrBh0C,GAAO,iBAEPA,GAAO,iBAGL2zC,EAAYI,UAAW,CAEzB,IAAIjkB,EAAO,QAAUvgC,EAAOra,GAAK,IAC7By+D,EAAYI,UAAUznC,MAAMp3B,GAAK,OACrC8qB,GAAO,KAAO8vB,EAGd9vB,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjhD,KACrD,IAAM88B,EACN6jB,EAAYM,uBAAuB,GAAGjC,MACxChyC,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KACzD,IAAM88B,EACV9vB,GAAO,oBACH2zC,EAAYM,uBAAuB,GAAGjhD,KAAO,IAC7C2gD,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KAC1C,QAUR,OANAgN,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjhD,KACrD,UAAY65C,EAASC,WAAa,OAClC6G,EAAYI,WAAaJ,EAAYM,uBAAuB,GAAGjC,MACjEhyC,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KACzD,UAAY65C,EAASC,WAAa,QAEjC9sC,GAIT6sC,EAASqH,aAAe,SAAS5E,EAAcvxC,GAG7C,IADA,IAAIT,EAAQuvC,EAASG,WAAWsC,GACvBh1E,EAAI,EAAGA,EAAIgjC,EAAM/4B,OAAQjK,IAChC,OAAQgjC,EAAMhjC,IACZ,IAAK,aACL,IAAK,aACL,IAAK,aACL,IAAK,aACH,OAAOgjC,EAAMhjC,GAAG8oB,OAAO,GAK7B,OAAI2a,EACK8uC,EAASqH,aAAan2C,GAExB,YAGT8uC,EAASsH,QAAU,SAAS7E,GAG1B,OAFYzC,EAASG,WAAWsC,GACd,GAAGv+D,MAAM,KACd,GAAGqS,OAAO,IAGzBypD,EAASuH,WAAa,SAAS9E,GAC7B,MAAyC,MAAlCA,EAAav+D,MAAM,IAAK,GAAG,IAGpC87D,EAASrxC,WAAa,SAAS8zC,GAC7B,IACIt9D,EADQ66D,EAASG,WAAWsC,GACd,GAAGlsD,OAAO,GAAGrS,MAAM,KACrC,MAAO,CACLq2B,KAAMp1B,EAAM,GACZ2pB,KAAMrV,SAAStU,EAAM,GAAI,IACzB0E,SAAU1E,EAAM,GAChB4pB,IAAK5pB,EAAMk2B,MAAM,GAAG/2B,KAAK,OAI7B07D,EAASwH,WAAa,SAAS/E,GAC7B,IACIt9D,EADO66D,EAASU,YAAY+B,EAAc,MAAM,GACnClsD,OAAO,GAAGrS,MAAM,KACjC,MAAO,CACLy9C,SAAUx8C,EAAM,GAChBuuC,UAAWvuC,EAAM,GACjBsiE,eAAgBhuD,SAAStU,EAAM,GAAI,IACnCuiE,QAASviE,EAAM,GACfwiE,YAAaxiE,EAAM,GACnB07D,QAAS17D,EAAM,KAKnB66D,EAAS4H,WAAa,SAASxH,GAC7B,GAAoB,iBAATA,GAAqC,IAAhBA,EAAK1oE,OACnC,OAAO,EAGT,IADA,IAAI+4B,EAAQuvC,EAASG,WAAWC,GACvB3yE,EAAI,EAAGA,EAAIgjC,EAAM/4B,OAAQjK,IAChC,GAAIgjC,EAAMhjC,GAAGiK,OAAS,GAA4B,MAAvB+4B,EAAMhjC,GAAGqM,OAAO,GACzC,OAAO,EAIX,OAAO,GAKP5M,EAAOD,QAAU+yE,G,6BCnzBJ,KAQX6H,wBAAwBC,GACpB,MAAMC,EAAcD,GACbA,EAAS71D,qBAAqB,0BAA0B,GAE/D,GAAK81D,EAIL,MAAO,CACH5hE,MAAO4hE,EAAYhkE,aAAa,kBAChCikE,UAAWD,EAAYhkE,aAAa,aACpCkkE,cAAeF,EAAYhkE,aAAa,kBACxCmkE,UAAWH,EAAYhkE,aAAa,cACpCmN,OAAQ62D,EAAYhkE,aAAa,YAWzCokE,sBAAsBL,GAClB,MAAMM,EACAN,EAAS71D,qBAAqB,wBAAwB,GACtDo2D,EAAoBD,GACnBA,EAA2B/vD,YAC5BiwD,EACAR,EAAS71D,qBAAqB,QAAQ,GACtCrjB,EAAO05E,GACNA,EAAcjwD,aACdiwD,EAAcjwD,YAAYvU,cAC3BykE,EACAT,EAAS71D,qBAAqB,cAAc,GAIlD,MAAO,CACHo2D,oBACAz5E,OACAs5E,UALEK,GAAsBA,EAAmBlwD,cAenDmwD,mBAAmB10D,GACf,MAAM20D,EAAQ30D,GAAYA,EAAS7B,qBAAqB,SAAS,GAEjE,OAAOw2D,GAASA,EAAM1kE,aAAa,eASvC2kE,aAAaZ,GACT,MAAMa,EACAb,EAAS71D,qBAAqB,cAAc,GAGlD,OAFkB02D,GAAsBA,EAAmBtwD,aAW/DuwD,YAAYd,GACDA,EAAS/jE,aAAa,QAAQ6wB,SAAS,W,8BC3FtD,gIAaA,MAAMxV,EAASF,oBAAUM,GASlB,MAAM+7B,EAKTnkD,YAAYssB,GACRviB,KAAKuiB,WAAaA,EAElBviB,KAAK0nE,mBAAoB,EACzB1nE,KAAK2nE,UAAW,EAChB3nE,KAAK4nE,cAAe,EACpB5nE,KAAK6nE,UAAOtnE,EAEZP,KAAK8nE,SAAW,IAAIC,IACpB/nE,KAAKgoE,YAAc,IAAIC,IAAW1lD,GAGlCviB,KAAKkoE,YAAcC,IAASnoE,KAAKooE,gBAtBjB,KAuBhBpoE,KAAKqoE,WAAaF,IAASnoE,KAAKsoE,eAvBhB,KA4BhBtoE,KAAKuiB,WAAWZ,GACZgB,cACA3iB,KAAKuoE,qBAAqBz6E,KAAKkS,OACnCA,KAAKuiB,WAAWZ,GACZgB,YACA3iB,KAAKwoE,mBAAmB16E,KAAKkS,OACjCA,KAAKuiB,WAAWZ,GACZgB,oBACA,KACI3iB,KAAK0nE,mBAAoB,IAQjC1nE,KAAKuiB,WAAWZ,GACZgB,yBACA3iB,KAAKyoE,uBAAuB36E,KAAKkS,OACrCA,KAAKuiB,WAAWZ,GACZgB,cACA2b,GAASA,EAAM/Y,WAAavlB,KAAK0oE,mBAAmBpqC,IACxDt+B,KAAKuiB,WAAW03C,IAAIt4C,GAChBqX,IAAU3Q,mBACV,CAACiW,EAAOhb,IAAQtjB,KAAK2oE,2BAA2BrlD,EAAKgb,IACzDt+B,KAAKuiB,WAAWZ,GACZgB,qBACA3iB,KAAK4oE,kBAAkB96E,KAAKkS,OAGhCA,KAAKgoE,YAAYrmD,GACbsmD,IAAW17B,OAAOs8B,+BAClB7oE,KAAK8oE,+BAA+Bh7E,KAAKkS,OAC7CA,KAAKgoE,YAAYrmD,GACbsmD,IAAW17B,OAAOw8B,wBAClB/oE,KAAKgpE,yBAAyBl7E,KAAKkS,OAS3C,mBAAmBwzB,GACf,OAAO1T,IAAQmpD,6BACRhB,IAAWzqC,iBACThK,EAAO01C,SAAW11C,EAAO01C,QAAQC,aAQ9CC,YACI,OAAOppE,KAAK2nE,SAShB0B,WAAWC,GACHA,IAAYtpE,KAAK2nE,WAIrB3nE,KAAK2nE,SAAW2B,GAEXtpE,KAAK4nE,cAAgB0B,IAKtBtpE,KAAKuiB,WAAWgnD,wBAEhBvpE,KAAK4nE,cAAe,GAIxB5nE,KAAK6nE,OAAOyB,GAAUtpE,KAAKwpE,eAG3BxpE,KAAKgoE,YAAYyB,UAAUzpE,KAAK6nE,MAAMntC,KAAK/J,IAEvC3wB,KAAK8nE,SAAS4B,OAAO1pE,KAAKuiB,WAAW25C,WAAYl8D,KAAK6nE,KAAMl3C,MAUpE64C,eACI,OAAOt9E,OAAOy9E,OAAOC,gBAAgB,IAAI5e,WAAW,KAQxD0d,mBAAmBpqC,GACf,IAAK,MAAMrmB,KAAWjY,KAAKuiB,WAAWsnD,oBAClC7pE,KAAK8pE,yBAAyB7xD,EAASqmB,GAS/CmqC,uBAAuBxwD,GACnB,MAAM+qB,EAAchjC,KAAKuiB,WAAW2kB,iBAEpC,IAAK,MAAM5I,KAAS0E,EAChBhjC,KAAK8pE,yBAAyB7xD,EAASqmB,GAQ/CiqC,qBAAqBrhE,GACjB+W,EAAOhZ,MAAO,eAAciC,YAExBlH,KAAK0nE,mBAAqB1nE,KAAK2nE,UAC/B3nE,KAAKkoE,cAQbM,mBAAmBthE,GACf+W,EAAOhZ,MAAO,eAAciC,UAE5BlH,KAAK8nE,SAASiC,QAAQ7iE,GAElBlH,KAAK2nE,UACL3nE,KAAKqoE,aAQbS,+BAA+B5hE,GAC3B+W,EAAOhZ,MAAO,iCAAgCiC,cAWlD8hE,yBAAyB9hE,EAAIrZ,EAAK8iC,GAC9B1S,EAAOhZ,MAAO,eAAciC,uBAE5BlH,KAAK8nE,SAAS4B,OAAOxiE,EAAIrZ,EAAK8iC,GAQlC,wBACI1S,EAAOhZ,MAAM,mBAEb,MAAM+kE,QAAiBC,YAAUjqE,KAAK6nE,MAChCqC,QAAeC,YAAQH,GAE7BhqE,KAAK6nE,KAAO,IAAI7c,WAAWkf,GAE3B,MAAMv5C,QAAc3wB,KAAKgoE,YAAYoC,iBAAiBpqE,KAAK6nE,MAE3D7nE,KAAK8nE,SAAS4B,OAAO1pE,KAAKuiB,WAAW25C,WAAYl8D,KAAK6nE,KAAMl3C,GAShE,uBACI1S,EAAOhZ,MAAM,gBAEbjF,KAAK6nE,KAAO7nE,KAAKwpE,eACjB,MAAM74C,QAAc3wB,KAAKgoE,YAAYyB,UAAUzpE,KAAK6nE,MAEpD7nE,KAAK8nE,SAAS4B,OAAO1pE,KAAKuiB,WAAW25C,WAAYl8D,KAAK6nE,KAAMl3C,GAQhEg4C,2BAA2BrlD,EAAKgb,GAC5B,IAAKt+B,KAAK2nE,SACN,OAGJ,MAAMv8B,EAAW9nB,EAAI+mD,qBAAqB/rC,EAAMA,OAE5C8M,EACAprC,KAAK8nE,SAASwC,eAAel/B,EAAU9M,EAAMgJ,UAAWhJ,EAAMq+B,oBAE9D1+C,EAAO9Y,KAAM,6BAA4Bm5B,6BAAiChb,KAWlFwmD,yBAAyB7xD,EAASqmB,GAC9B,IAAKt+B,KAAK2nE,SACN,OAGJ,MAAMriB,EAAKrtC,EAAQ+I,eACbupD,EAASjlB,GAAMA,EAAGklB,mBAAmBlsC,EAAMA,OAE7CisC,EACAvqE,KAAK8nE,SAAS2C,aAAaF,EAAQjsC,EAAMgJ,UAAWhJ,EAAMq+B,oBAE1D1+C,EAAO9Y,KAAM,6BAA4Bm5B,0BAA8BgnB,KAS/EsjB,kBAAkBtqC,GACd,GAAIxe,IAAQ4qD,+BAAiCpsC,EAAM/Y,WAAa+Y,EAAMqsC,iBAAmBrsC,EAAMggC,UAC3F,IAAK,MAAMrmD,KAAWjY,KAAKuiB,WAAWsnD,oBAClC7pE,KAAK8pE,yBAAyB7xD,EAASqmB,O,0EC9SvD,0OA2BA,MAAMrgB,EAASF,oBAAUM,GA2CV,MAAMusD,UAAwBC,IASzC,yBAAyBC,GACrB,MAAMC,EAAgBD,EAAe75C,KAAK,0BAE1C,GAAI85C,EAAcx0E,OAAQ,CACtB,MAAM86C,EAAU05B,EAAc,GAAGnoE,aAAa,WAE9C,GAAgB,SAAZyuC,GACe,cAAZA,GACY,cAAZA,GACY,SAAZA,EACH,OAAOA,EAIf,OAAO,KASX,2BAA2By5B,GACvB,MAAME,EAAoBF,EAAe75C,KAAK,2CAE9C,OAAO+5C,EAAkBz0E,OAASgjB,OAAOyxD,EAAkBrpE,QAAU,KAyBzE1L,YACQoW,EACA4+D,EACAC,EACA32D,EACA42D,EACAtlC,EACAC,EACAslC,GACJ/wC,MACIhuB,EACA4+D,EACAC,EAAW32D,EAAY42D,EAAkBtlC,EAAWulC,GAWxDprE,KAAKqrE,iBAAmB,KAQxBrrE,KAAKsrE,wBAAqB/qE,EAQ1BP,KAAKurE,wBAAqBhrE,EAQ1BP,KAAKwrE,6BAA+B,KAYpCxrE,KAAKyrE,2BAA6B,KAOlCzrE,KAAK0rE,6BAA0BnrE,EAgB/BP,KAAK2rE,mBAAoB,EAYzB3rE,KAAK4rE,oBAAqB,EAS1B5rE,KAAK6rE,oBAAqB,EAE1B7rE,KAAK8rE,kBAAmB,EACxB9rE,KAAK+rE,QAAS,EAQd/rE,KAAK8lC,MAAQA,EAOb9lC,KAAKgsE,8BAA2BzrE,EAMhCP,KAAKisE,eAAiB,IAAIC,IAO1BlsE,KAAKmsE,kBAAoB,IAAIC,IAO7BpsE,KAAKqsE,cAAe,EAQpBrsE,KAAKssE,2BAAwB/rE,EAE7BP,KAAKusE,eAAiB,GACtBvsE,KAAKusE,eAAerpE,KAChBqR,EAAW8nB,iBACPgd,IAAe0Z,OAAO3B,oBACtBpxD,KAAKwsE,oBAAoB1+E,KAAKkS,QAGtCA,KAAKysE,iDAA8ClsE,EAUvDmsE,kBACI,OAAO1sE,KAAKitC,QAAU0/B,IAO1BC,aAAazlE,GACTnH,KAAKovC,QAAUzW,QAAQxxB,EAAQioC,SAC/BpvC,KAAK8rE,kBAAmB,EACxB9rE,KAAKmH,QAAUA,EAMfnH,KAAK6sE,aAAc,EAMnB7sE,KAAK8sE,WAAY,EACjB9sE,KAAK+sE,oBAAsBp0C,QAAQxxB,EAAQ4lE,qBAC3C/sE,KAAKgtE,oBAAsBr0C,QAAQxxB,EAAQ6lE,qBAE3C,MAAMC,EAAY,CAAElzB,WAAY5yC,EAAQ4yC,YAexC,GAbI5yC,EAAQ+lE,cACRD,EAAUE,SAhSI,KAkSlBF,EAAUG,uBAAwB,EAClCH,EAAU/mC,wBAA0B/+B,EAAQ++B,wBAC5C+mC,EAAUI,aAAelmE,EAAQkmE,aAG7BJ,EAAUI,eACVJ,EAAUK,cAAgBL,EAAUI,aAAaC,cACjDL,EAAUM,eAAiBN,EAAUI,aAAaE,gBAGlDvtE,KAAK8lC,MAAO,CAEZmnC,EAAUO,kBAAmB,EAC7BP,EAAUQ,YAActmE,EAAQg3C,KAAOh3C,EAAQg3C,IAAIsvB,YACnDR,EAAUS,WAAavmE,EAAQg3C,KAAOh3C,EAAQg3C,IAAIuvB,WAG9CvmE,EAAQg3C,MAGR8uB,EAAUK,cAAgBnmE,EAAQ++B,wBAC5B9S,IAAcC,KACdlsB,EAAQg3C,IAAImvB,cAClBL,EAAUM,eAAiBpmE,EAAQg3C,IAAIovB,gBAG3C,MAAMvnC,EAAqBhmC,KAAK2tE,2BAA2BxmE,QAEzB,IAAvB6+B,IACPinC,EAAUjnC,mBAAqBA,QAInCinC,EAAUO,iBACJrmE,EAAQqmE,kBACFrmE,EAAQumE,aAAevmE,EAAQsmE,YAC3CR,EAAUS,WAAavmE,EAAQumE,WAI3BvmE,EAAQ+hE,SACL/hE,EAAQ+hE,QAAQkE,uBACiC,iBAA1CjmE,EAAQ+hE,QAAQkE,wBAC1BH,EAAUG,sBACJhiE,KAAKC,SACLlE,EAAQ+hE,QAAQkE,sBAKtBhuD,IAAWyB,UAAUolB,uBAAuB,CAAEmnC,sBAAuBH,EAAUG,yBAInFjmE,EAAQymE,cACRX,EAAUW,aAAc,GAG5B5tE,KAAKghB,eACChhB,KAAKi6D,IAAIt0B,qBACH3lC,KAAKisE,eACLjsE,KAAK6lC,UACL7lC,KAAK8lC,MACLmnC,GAEZjtE,KAAKghB,eAAe6sD,eAAiBh0B,IACjC,IAAKA,EAMD,OAIJ,MAAMprB,EAAYorB,EAAGprB,UACfta,EAAMjoB,OAAOsuD,YAAYrmC,MAE/B,GAAIsa,EAAW,CAC6B,OAApCzuB,KAAKyrE,6BACLzrE,KAAKyrE,2BAA6Bt3D,GAItC,IAAIzL,EAAW+lB,EAAU/lB,SAEzB,GAAwB,iBAAbA,EAEP,GADAA,EAAWA,EAAS/F,cACH,QAAb+F,GAAmC,WAAbA,GACtB,GAAI1I,KAAKgtE,oBACL,YAED,GAAiB,QAAbtkE,GACH1I,KAAK+sE,oBACL,YAIJ/sE,KAAK6rE,qBAEbzsD,IAAWoI,cACP0C,IACA,CACI4jD,MAAO,YACPvgF,MAAO4mB,EAAMnU,KAAKyrE,2BAClBttB,IAAKn+C,KAAK8lC,MACV+gC,UAAW7mE,KAAKorE,cAExBprE,KAAK6rE,oBAAqB,GAE9B7rE,KAAK+tE,iBAAiBt/C,IAU1BzuB,KAAKghB,eAAegtD,uBAAyB,KACE,WAAvChuE,KAAKghB,eAAeitD,eACpBjuE,KAAK8sE,WAAY,EAC6B,WAAvC9sE,KAAKghB,eAAeitD,gBACgB,WAAxCjuE,KAAKghB,eAAektD,iBACvBluE,KAAKm/C,KAAK3/B,aAAa0D,KAAKy3B,IAAWhoD,iBAAkBqN,OAUjEA,KAAKghB,eAAemtD,2BAA6B,KAC7C,MAAMh6D,EAAMjoB,OAAOsuD,YAAYrmC,MA0B/B,OAxBKnU,KAAK8lC,QACN9lC,KAAKm/C,KAAKrG,gBACL,aAAY94C,KAAKghB,eAAeotD,oBAC/Bj6D,GAEV8J,EAAOrZ,IACF,cAAa5E,KAAKghB,eAAeotD,mBAC3B,SAAQpuE,KAAK8lC,WACpB3xB,GAEJiL,IAAWoI,cACP4C,IACA,CACI+zB,IAAKn+C,KAAK8lC,MACVmH,MAAOjtC,KAAKghB,eAAeotD,mBAC3B,gBAAmBpuE,KAAKghB,eAAeitD,eACvCI,UAAWruE,KAAK6sE,YAChBt/E,MAAO4mB,IAGfnU,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAW3nD,6BACXgN,KACAA,KAAKghB,eAAeotD,oBAChBpuE,KAAKghB,eAAeotD,oBAC5B,IAAK,WACDpuE,KAAKwrE,6BAA+Br3D,EACpC,MACJ,IAAK,YAUD,GAP2C,WAAvCnU,KAAKghB,eAAeitD,gBAChBjuE,KAAK6sE,aACL7sE,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAWzrD,oBAAqB8Q,OAIvCA,KAAKqsE,cAAgBrsE,KAAK8sE,UAAW,CAEtC1tD,IAAWoI,cACP0C,IACA,CACI4jD,MAAO,WACPvgF,MAAO4mB,EAAMnU,KAAKwrE,6BAClBrtB,IAAKn+C,KAAK8lC,MACV+gC,UAAW7mE,KAAKorE,cAMxB,MAAMkD,EACAljE,KAAKqP,IACHza,KAAKwrE,6BACLxrE,KAAKyrE,4BAEbzrE,KAAKssE,sBAAwBn4D,EAAMm6D,EAEnClvD,IAAWoI,cACP0C,IACA,CACI4jD,MAAO,gBACPvgF,MAAOyS,KAAKssE,sBACZnuB,IAAKn+C,KAAK8lC,MACV+gC,UAAW7mE,KAAKorE,cAGxBprE,KAAKqsE,cAAe,EACpBrsE,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAW5rD,uBAAwBiR,MAE3CA,KAAK6sE,aAAc,EACnB,MACJ,IAAK,eACD7sE,KAAK6sE,aAAc,EAIf7sE,KAAK8sE,WACL9sE,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAW1rD,uBAAwB+Q,MAE3C,MACJ,IAAK,SACDA,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAWxrD,sBAAuB6Q,QAS9CA,KAAKghB,eAAeutD,oBAAsB,KACtC,MAAMthC,EAAQjtC,KAAKghB,eAAeitD,eAC5BO,EAAoBxuE,KAAKghB,eAAewtD,kBAE9C,GAAI1uD,IAAQ2uD,mBAA+B,WAAVxhC,GAC1BuhC,GAAsD,iBAA1BA,EAAkBx8C,IAAkB,CACnE/T,EAAOhZ,MAAO,gCAA+BjF,KAAKghB,4BAA4BisB,KAC9E,MAAMyhC,EAAeC,IACjB,MAAMC,EAAS,IAAI1/B,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAE5DhyB,KAAK8uE,eACAp0C,KAAK,KACF,MAAMq0C,EAAS,IAAI7/B,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAE5DhyB,KAAKgvE,mBAAmBJ,EAAQG,GAChCJ,KAEJA,IAGR3uE,KAAKmsE,kBAAkBjpE,KACnBwrE,EACA1pE,IACQA,EACAiZ,EAAOjZ,MAAM,4BAA6BA,GAE1CiZ,EAAOhZ,MAAM,yCAOjCjF,KAAKisE,eAAegD,YAAYjvE,KAAKm/C,OAEhCn/C,KAAK8lC,OAAS3+B,EAAQ+nE,wBAGvBlvE,KAAKysE,4CAA8CzsE,KAAKi6D,IAAIr2B,YACxD5K,IAAUhR,iCAAkC,KACxChoB,KAAKwf,aAAa0D,KACdisD,IAAmBC,iCAAkCpvE,SAUzEqvE,8BACI,OAAIrvE,KAAK8lC,MACE9lC,KAAKgsE,yBAGThsE,KAAKmH,QAAQ+nE,sBAAwBlvE,KAAKi6D,IAAIlzB,4BAA4BuoC,iBAAc/uE,EAQnGwtE,iBAAiBt/C,GACb,MAAM8gD,EAAW,IAAIrgC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAE9D,GAAIvD,GAAaA,EAAUA,UAAUl4B,SAAWyJ,KAAK8rE,iBAAkB,CACnE,MAAM0D,EACA/iD,IAAQE,UACN4iD,EAAS9hD,MAAMgB,EAAUghD,eAAgBF,EAASt3D,SACpDy3D,EAAQjjD,IAAQyD,kBAAkBzB,EAAUA,WAElD,IAAM+gD,IAAOE,EAAQ,CACjB,MAAMC,EAAgB,6BAKtB,OAHA/3C,IAAqBC,iBAAiB,IAAIxgC,MAAMs4E,SAChD1xD,EAAOjZ,MAAM2qE,GAIjBH,EAAIvpE,MAAQ,uCAERjG,KAAK4vE,SAC6B,IAA9B5vE,KAAK6vE,cAAct5E,QAEnBgU,WAAW,KAC2B,IAA9BvK,KAAK6vE,cAAct5E,SAGvByJ,KAAK8vE,kBAAkB9vE,KAAK6vE,eAC5B7vE,KAAK6vE,cAAgB,KACtB,IAEP7vE,KAAK6vE,cAAc3sE,KAAKurB,IAExBzuB,KAAK8vE,kBAAkB,CAAErhD,SAG7BxQ,EAAOrZ,IAAI,qCAGX5E,KAAK8rE,kBAAmB,EAUhCgE,kBAAkBC,GACd,IAAK/vE,KAAK0sE,gBAAgB,qBAEtB,OAGJzuD,EAAOrZ,IAAI,oBAAqBmrE,GAChC,MAAM7gD,EAAOvzB,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UACxBjkE,KAAM,QACLta,EAAE,SAAU,CAAEsZ,MAAO,oBAClBukB,OAAQ,iBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAEZkjE,EAAW,IAAIrgC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAE9D,IAAK,IAAI0d,EAAM,EAAGA,EAAM6/B,EAAS9hD,MAAMl3B,OAAQm5C,IAAO,CAClD,MAAMugC,EAAQF,EAAWr/C,OAAOxwB,GAAMA,EAAGuvE,gBAAkB//B,GACrD5hB,EACArB,IAAQe,WAAW+hD,EAAS9hD,MAAMiiB,GAAK3sC,MAAM,QAAQ,IAE3D,GAAIktE,EAAM15E,OAAS,EAAG,CAClB,MAAMi5E,EACA/iD,IAAQE,UAAU4iD,EAAS9hD,MAAMiiB,GAAM6/B,EAASt3D,SAEtDu3D,EAAIvpE,MAAQ,uCACZipB,EAAKviC,EAAE,UAAW,CACdujD,QAASlwC,KAAKgwE,eAAiBhwE,KAAKirE,SAC9B,YAAc,YACpBp+E,KAAMojF,EAAM,GAAGC,OAASD,EAAM,GAAGC,OAASpiD,EAAML,QACjD9gC,EAAE,YAAa6iF,GAClB,IAAK,IAAIljF,EAAI,EAAGA,EAAI2jF,EAAM15E,OAAQjK,IAAK,CACnC,MAAMmiC,EACAhC,IAAQyD,kBAAkB+/C,EAAM3jF,GAAGmiC,WAIrCzuB,KAAKovC,UACL3gB,EAAUI,GAAK,WAEnBK,EAAKviC,EAAE,YAAa8hC,GAAWtoB,KAInC,MAAMgqE,EACA1jD,IAAQO,SACNuiD,EAAS9hD,MAAMiiB,GACf,iBAAkB6/B,EAASt3D,SAEnC,GAAIk4D,EAAiB,CACjB,MAAMC,EAAM3jD,IAAQ4B,iBAAiB8hD,GAErCC,EAAIC,UAAW,EACfnhD,EAAKviC,EACD,cACA,CAAEsZ,MAAO,gCACRzY,EAAE4iF,EAAI9hD,oBACJ8hD,EAAI9hD,YACXY,EAAK1zB,MAAM40E,GACXlhD,EAAK/oB,KAET+oB,EAAK/oB,KACL+oB,EAAK/oB,MAObnG,KAAKuU,WAAWnG,OACZ8gB,EAAM,KAAMlvB,KAAKswE,sBAAsBphD,GAxsBhC,KAktBfqhD,4BACI,MAAMC,EACA70E,cAAI,CACFotC,GAAI/oC,KAAKkrE,UACTjkE,KAAM,QACTta,EAAE,SAAU,CAAEsZ,MAAO,oBAClBukB,OAAQ,eACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MACb1f,EAAE,YAAa,CAAEsZ,MAAO,oCACxBzY,EAAE,UACF2Y,KAELnG,KAAKqrE,kBACEmF,EAAY7jF,EACX,iBAAkB,CACdsZ,MAAO,kCACPiB,GAAIlH,KAAKqrE,mBAGrBrrE,KAAKuU,WAAWi/C,QACZgd,EAAa,CAMT5iE,QAAS,KAEZwvB,MAAMp9B,KAAKswE,sBAAsBE,IAM1CC,iBAAiB/wE,GACb,GAA2C,WAAvCM,KAAKghB,eAAeitD,eAGpB,YAFAhwD,EAAO9Y,KAAK,kDAKhB,MAAMurE,EAAgB,GAyBtB,GAvBAhxE,EAAKuxB,KAAK,gCACLghB,KAAK,CAACrC,EAAKnhB,KACR,IAAIjqB,EAAOioB,IAAQ0D,oBAAoB1B,GAEvCjqB,EAAOA,EAAK5C,QAAQ,OAAQ,IAAIA,QAAQ,KAAM,IAI9C,MAAM+uE,EAAe,IAAIC,gBAAgB,CACrCnB,cAAe,EAOfS,OAAQ,GACRzhD,UAAWjqB,IAGfksE,EAAcxtE,KAAKytE,MAGtBD,EAAcn6E,OAIf,YAHA0nB,EAAOjZ,MACH,6BAA8BtF,EAAK,IAAMA,EAAK,GAAGmxE,WAoBzD5yD,EAAOhZ,MACF,eAAcyrE,EAAcn6E,kCACjCyJ,KAAKmsE,kBAAkBjpE,KAbFyrE,IACjB,IAAK,MAAMmC,KAAgBJ,EACvB1wE,KAAKghB,eAAe+vD,gBAAgBD,GAC/Bp2C,KACG,IAAMzc,EAAOhZ,MAAM,uBACnBkL,GAAO8N,EAAOjZ,MAAM,0BAA2BmL,IAG3Dw+D,MAYRqC,aAAat+B,GAEHD,EAAEC,GAAUzhB,KACV,gEAGFghB,KAAK,CAAC3lD,EAAG2kF,KACX,MAAMjsD,EAAOzL,OAAO03D,EAAYruE,aAAa,SAEzC5C,KAAK8lC,MAEL9lC,KAAKisE,eAAeiF,aAChBlsD,EAAMvpB,UAAQyI,mBAAmBlE,KAAKkrE,YAE1Cz4B,EAAEw+B,GACGhgD,KAAK,gDACLghB,KAAK,CAACk/B,EAAIC,KACP,MAAMjpC,EAAQipC,EAAgBxuE,aAAa,SAEvCulC,GAASA,EAAM5xC,SACXmjB,MAAMsL,IAASA,EAAO,EACtB/G,EAAO9Y,KACF,gBAAe6f,wBACFmjB,GAElBnoC,KAAKisE,eAAeiF,aAChBlsD,EACAvpB,UAAQyI,mBAAmBikC,SAa3DkpC,uBACQrxE,KAAKghB,eACLhhB,KAAKghB,eAAeqwD,uBAEpBpzD,EAAOjZ,MACH,wDAwBZssE,YAAYC,EAAa7pB,EAAS8pB,EAASxuC,GACvChjC,KAAKyxE,oBACDF,EACA,KAIIvxE,KAAK0xE,kBAAkBhqB,EAAS8pB,IAEpCA,EACAxuC,GAWR2uC,OAAO3uC,EAAc,IACjB,IAAKhjC,KAAKorE,YACN,MAAM,IAAI/zE,MAAM,+CAoBpB2I,KAAKmsE,kBAAkBjpE,KAlBFyrE,IACjB,MAAMiD,EAAY,GAElB,IAAK,MAAM1tC,KAAclB,EACrB4uC,EAAU1uE,KAAKlD,KAAKghB,eAAemd,SAAS+F,EAAYlkC,KAAKorE,cAGjEpuC,QAAQ+K,IAAI6pC,GACPl3C,KAAK,IAAM16B,KAAKghB,eAAe6wD,YAAY7xE,KAAKmrE,mBAChDzwC,KAAKo3C,GAAY9xE,KAAKghB,eAAe+wD,oBAAoBD,IACzDp3C,KAAK,KAGF16B,KAAKgyE,oBAAoBhyE,KAAKghB,eAAe6tD,iBAAiB78C,OAEjE0I,KAAK,IAAMi0C,IAAoB3pE,GAAS2pE,EAAiB3pE,KAK9DA,IACQA,EACAiZ,EAAOjZ,MAAM,eAAgBA,GAE7BiZ,EAAOhZ,MAAM,0BAe7B+sE,oBAAoBF,GAChB,IAAIlnE,EAAOjP,cAAI,CACXotC,GAAI/oC,KAAKkrE,UACTjkE,KAAM,QACPta,EAAE,SAAU,CACXsZ,MAAO,oBACPukB,OAAQ,mBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAGd,IAAI6iC,IAAI4iC,GAAU/hC,SACdnlC,EACA5K,KAAKorE,YAAc,YAAc,aACrCxgE,EAAOA,EAAKvF,OACZ4Y,EAAO/Y,KAAK,qBAAsB0F,GAClC5K,KAAKuU,WAAWnG,OAAOxD,EACnB,KACIqT,EAAO/Y,KAAK,sCAEhBF,IACIiZ,EAAOjZ,MAAM,2BAA4BA,IA98BtC,KAu9BfitE,UAAUC,GACN,IAAKlyE,KAAKorE,YACN,MAAM,IAAI/zE,MAAM,oDAEpB2I,KAAKyxE,oBACDS,EACA,KACIj0D,EAAO/Y,KAAK,0BAEhBF,IACIiZ,EAAOjZ,MAAM,qBAAsBA,KAmB/CysE,oBAAoBU,EAAqBzqB,EAAS8pB,EAASxuC,EAAc,IA+DrEhjC,KAAKmsE,kBAAkBjpE,KA9DFyrE,IACjB,MAAMiD,EAAY,GAElB,IAAK,MAAMtzC,KAAS0E,EAChB4uC,EAAU1uE,KAAKlD,KAAKghB,eAAemd,SAASG,EAAOt+B,KAAKorE,cAG5D,MAAMgH,EACApyE,KAAKqyE,yBAAyBF,GAC9BG,EACAtyE,KAAKghB,eAAe6tD,iBAAiB78C,IAMrCugD,EAHA9/B,EAAE0/B,GACClhD,KAAK,4DAEwB1vB,KAAK,MAEvCgxE,IAAoBvyE,KAAKqrE,mBACzBrrE,KAAKqrE,iBAAmBkH,GAG5Bv1C,QAAQ+K,IAAI6pC,GACPl3C,KAAK,IAAM16B,KAAK8uE,aAAasD,EAAa9hE,MAC1CoqB,KAAK,KA2BF,GA1BI16B,KAAKitC,QAAU0/B,MACf3sE,KAAKitC,MAAQ0/B,KAeT3sE,KAAK8lC,OACA9lC,KAAK2rE,oBAAqB3rE,KAAK0rE,yBACpC1rE,KAAKwyE,qBAQTF,EAAa,CACb,MAAMG,EACA,IAAIvjC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAEnDhyB,KAAKgvE,mBACD,IAAI9/B,IAAIojC,GAAcG,MAGjC/3C,KAAK,IAAMi0C,IAAoB3pE,GAAS2pE,EAAiB3pE,KAK9DA,IACIA,EAAQwsE,EAAQxsE,GAAS0iD,MAcrCgrB,iBAAiBC,EAAiBjrB,EAAS8pB,GACvCxxE,KAAKm/C,KAAK3/B,aAAa0D,KAAKy3B,IAAW/qD,eAAgBoQ,MAMvD,MAAM4yE,EAAgBD,EAAgBE,QAEtCF,EACK1hD,KAAK,yBACL1vB,KAAK,UAAW,YAQrBoxE,EACK1hD,KAAK,+BACL6hD,SACLH,EACK1hD,KAAK,mCACL6hD,SAML,MAAMC,EAAiBJ,EAAgB1hD,KAAK,kCAE5C8hD,EAAexxE,KAAK,OAAQ,SAC5BwxE,EAAepxE,KAAK,+DAGpB3B,KAAKyxE,oBACDkB,EACA,KAEI3yE,KAAKyxE,oBACDmB,EACA,KACI,MAAMrD,EACA,IAAIrgC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAEnDhyB,KAAKgzE,oBAAoBzD,EAAU7nB,EAAS8pB,GAE5CxxE,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAW9qD,oBACXmQ,KACA4yE,IAERpB,IAERA,GAYRE,kBAAkBhqB,EAAS8pB,GAGvB,MAAMjC,EAAW,IAAIrgC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAC9D,IAAIihD,EAASt3E,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UACxBjkE,KAAM,QACLta,EAAE,SAAU,CAAEsZ,MAAO,oBAClBukB,OAAQ,iBACRq8C,UAAW7mE,KAAKgwE,aAChBkD,UAAWlzE,KAAKmzE,aAChB9mE,IAAKrM,KAAKqM,MAEdrM,KAAKgtE,sBACLuC,EAASlgC,qBAAsB,GAE/BrvC,KAAK+sE,sBACLwC,EAASjgC,qBAAsB,GAE/BtvC,KAAKovC,UACLmgC,EAASngC,SAAU,GAEvBmgC,EAASx/B,SACLkjC,EACAjzE,KAAKgwE,eAAiBhwE,KAAKirE,SAAW,YAAc,YACpD,MAGJgI,EAASA,EAAO5tE,OAChB4Y,EAAO/Y,KAAK,yBAA0B+tE,GACtCjzE,KAAKuU,WAAWnG,OAAO6kE,EACnBvrB,EACA1nD,KAAKswE,sBAAsB2C,EAAQjuE,IAC/BwsE,EAAQxsE,GAIRhF,KAAKm/C,KAAK3/B,aAAa0D,KACnBy3B,IAAWroD,uBAAwB0N,QA5qCpC,KA0sCfwyE,oBACI,MAAMntC,EAAiBrlC,KAAK0rE,wBACtBr6B,EAAUrxC,KAAK2rE,kBAAoB,OAAS,OAElD,IAAIyH,EACEz3E,cAAI,CACFotC,GAAI/oC,KAAKkrE,UACTjkE,KAAM,QAELta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,iBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAEb1f,EAAE,UAAW,CACVE,KAAM,QACNwkD,iBAGkB,IAAnBhM,IACP+tC,EAAgBA,EACXzmF,EAAE,mBAAoB,CAAEsZ,MAAO,mCAC/BzY,EAAE63C,IAGXpnB,EAAO/Y,KAAM,GAAElF,+CAA+CqxC,wBAA8BhM,KAE5FrlC,KAAKuU,WAAWnG,OACZglE,EACA,KACApzE,KAAKswE,sBAAsB8C,GAzuCpB,KAmvCfhuC,2BAA2BC,GACvBpnB,EAAO/Y,KAAM,GAAElF,uDAAuDqlC,KAEtErlC,KAAK0rE,wBAA0BrmC,EAE3BrlC,KAAK8lC,MAGD9lC,KAAKitC,QAAU0/B,KACf3sE,KAAKwyE,oBAGTxyE,KAAKi6D,IAAI70B,2BAA2BC,GAc5C2tC,oBAAoBzD,EAAU7nB,EAAS8pB,GACnC,IAAI6B,EAAkB13E,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UACjCjkE,KAAM,QACLta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,mBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAGlBkjE,EAAS9hD,MAAMre,QAAQ,CAACkkE,EAAY1jC,KAChC,MAAM9hB,EAAQrB,IAAQe,WAAW8lD,EAAWvwE,MAAM,QAAQ,IAE1DswE,EAAgB1mF,EAAE,UACd,CACIujD,QACIlwC,KAAKgwE,eAAiBhwE,KAAKirE,SACrB,YACA,YACVp+E,KAAMihC,EAAML,QAGpB8hD,EAASj+B,kBAAkB1B,EAAKyjC,GAChCA,EAAgBltE,OAIpBktE,EAAkBA,EAAgBhuE,OAClC4Y,EAAO/Y,KAAK,6BAA8BmuE,GAE1CrzE,KAAKuU,WAAWnG,OAAOilE,EACnB3rB,EACA1nD,KAAKswE,sBAAsB+C,EAAiB7B,GA7yCrC,KA4zCf+B,oBAAoB7rB,EAAS8pB,GAGzB,IAAIgC,EAAkB73E,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UACjCjkE,KAAM,QACLta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,mBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAGlBmnE,EAAkBA,EAAgBnuE,OAClC4Y,EAAO/Y,KAAK,4BAA8BsuE,GAE1CxzE,KAAKuU,WAAWnG,OAAOolE,EACnB9rB,EACA1nD,KAAKswE,sBAAsBkD,EAAiBhC,GA70CrC,KAu1CfiC,uBACI,OAAIzzE,KAAK0sE,kBACE1sE,KAAKghB,eAAe0yD,gBAGxB12C,QAAQC,UASnB02C,yBAAyBtuC,GACrB,OAAIrlC,KAAK0sE,mBACLzuD,EAAO/Y,KAAM,GAAElF,kCAAkCqlC,KAE1CrlC,KAAKghB,eAAe2yD,yBAAyBtuC,IAGjDrI,QAAQC,UASnB22C,sCACI,OAAI5zE,KAAK0sE,kBACE1sE,KAAKghB,eAAe4yD,sCAGxB52C,QAAQC,UAMnB42C,UAAUnsB,EAAS8pB,EAASrqE,GACxB,GAAInH,KAAKitC,QAAU0/B,IAAnB,CAIA,IAAKxlE,GAAWwxB,QAAQxxB,EAAQ2sE,sBAAuB,CACnD,IAAIC,EACEp4E,cAAI,CACFotC,GAAI/oC,KAAKkrE,UACTjkE,KAAM,QAELta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,oBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAEb1f,EAAE,UACFA,EAAGwa,GAAWA,EAAQqI,QAAW,WACjCrJ,KAELgB,GAAWA,EAAQ6sE,kBACnBD,EACKpnF,EAAE,QACFa,EAAE2Z,EAAQ6sE,mBACV7tE,KACAA,KAEL4tE,EAAiB5tE,KAGrBnG,KAAKqrE,kBACE0I,EAAiBpnF,EAChB,iBAAkB,CACdsZ,MAAO,kCACPiB,GAAIlH,KAAKqrE,iBACT4I,QAAS9sE,IAAsC,IAA3BA,EAAQ+sE,iBAC7B/tE,KAGX4tE,EAAmBA,EAAiB1uE,OACpC4Y,EAAO/Y,KAAK,4BAA6B6uE,GACzC/zE,KAAKuU,WAAWnG,OACZ2lE,EACArsB,EACA1nD,KAAKswE,sBAAsByD,EAAkBvC,GA76C1C,UAg7CPvzD,EAAO/Y,KAAM,yCAAwClF,MAIzDA,KAAKuU,WAAW+9B,OAAOuhC,UAAU7zE,KAAKqM,MAQ1C8nE,aAAaC,EAAiBC,GAI1Bp2D,EAAO/Y,KAAM,sBAAqBlF,KAAQo0E,EAAiBC,GAE3Dr0E,KAAKusE,eAAen9D,QAAQ0S,GAAkBA,KAC9C9hB,KAAKusE,eAAiB,GAElBvsE,KAAKysE,6CACLzsE,KAAKysE,8CAGTzsE,KAAKyc,QAQT+vD,oBAAoBz8D,GACZA,IAAWspC,IAAe57C,OAAOM,WAAaiC,KAAKsrE,qBACnDrtD,EAAO/Y,KAAK,oCACZlF,KAAKgvE,mBACDhvE,KAAKsrE,mBACLtrE,KAAKurE,qBAcjB+I,4BAA4BC,EAAeC,GACvC,MAAMC,EAAc,GAiEpB,OA/DAhiC,EAAE8hC,GAAetiC,KAAK,CAACyiC,EAAI/hC,KACvB,MAAM9lD,EAAO4lD,EAAEE,GAASpxC,KAAK,QAC7B,IAAI+tB,EAAQ,GAEZmjB,EAAEE,GACG1hB,KAAK,uDACLghB,MAAK,WAEF,MAAM9gB,EAAYnxB,KAAK4C,aAAa,aAC9B4tB,EACAiiB,EAAEzyC,MACCixB,KAAK,WACLzrB,KAAI,WAED,OAAOxF,KAAK4C,aAAa,WAE5BzV,MAELqjC,EAAMj6B,SACN+4B,GACQ,gBAAe6B,KACfX,EAAMrtB,KAAK,eAMzBsvC,EAAEE,GAAS1hB,KACT,mDAGJghB,MAAK,WACL,MAAMjtB,EAAOytB,EAAEzyC,MAAMuB,KAAK,QAEtBizE,EAAiB3kC,aAAa7qB,GAC9B/G,EAAO9Y,KACF,yCAAwC6f,GAMjDytB,EAAEzyC,MAAMixB,KAAK,cAAcghB,MAAK,WAC5B3iB,GAAU,UAAStK,KAAQytB,EAAEzyC,MAAMuB,KAAK,UACpCkxC,EAAEzyC,MAAMuB,KAAK,UAAYkxC,EAAEzyC,MAAMuB,KAAK,SAAShL,SAC/C+4B,GAAU,IAAGmjB,EAAEzyC,MAAMuB,KAAK,UAE9B+tB,GAAS,aAKjBklD,EAAiB/mD,MAAMre,QAAQ,CAACqe,EAAOknD,KAC9BloD,IAAQO,SAASS,EAAQ,SAAQ5gC,KAGjC4nF,EAAYE,KACbF,EAAYE,GAAM,IAEtBF,EAAYE,IAAOrlD,OAIpBmlD,EAOXG,gBAAgBl1E,GACZM,KAAK60E,0BAAyB,EAAgBn1E,GAOlDo1E,mBAAmBp1E,GACfM,KAAK60E,0BAAyB,EAAoBn1E,GAWtDm1E,yBAAyBE,EAAOr1E,GAC5B,MAAMs1E,EAAYD,EAAQ,kBAAoB,qBAE1CA,GACA/0E,KAAKgxE,aAAatxE,GA+CtBM,KAAKmsE,kBAAkBjpE,KA5CFyrE,IACjB,IAAK3uE,KAAKghB,eAAe6tD,mBACjB7uE,KAAKghB,eAAe6tD,iBAAiB78C,IAAK,CAC9C,MAAMmqB,EAAY64B,EAAF,oCAKhB,OAHA/2D,EAAOjZ,MAAMm3C,QACbwyB,EAAiBxyB,GAKrBl+B,EAAOrZ,IAAK,cAAaowE,GACzB/2D,EAAOrZ,IACH,yBACA5E,KAAKghB,eAAeotD,oBAExB,MAAMkE,EACA,IAAIpjC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAC7CA,EAAM,IAAIkd,IAAIlvC,KAAKghB,eAAewtD,kBAAkBx8C,KACpDijD,EACAF,EACI/0E,KAAKs0E,4BAA4B50E,EAAMsyB,GACvChyB,KAAKk1E,+BAA+Bx1E,EAAMsyB,GAC9CogD,EACA2C,EACI/0E,KAAKm1E,wBAAwBF,GAC7Bj1E,KAAKo1E,2BAA2BH,GAE1Cj1E,KAAK8uE,aAAasD,EAAa9hE,KAC1BoqB,KAAK,KACF,MAAM+3C,EACA,IAAIvjC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAEnD/T,EAAOrZ,IACAowE,EAAF,gBAA4B1C,EAAaG,GAC9CzyE,KAAKgvE,mBAAmBsD,EAAaG,GACrC9D,KACD3pE,IACCiZ,EAAOjZ,MAASgwE,EAAF,WAAuBhwE,GACrC2pE,EAAiB3pE,OAajCqtE,yBAAyBgD,GACrB,MAAMC,EAAY,IAAIpmC,IAAI,IAe1B,OAbIlvC,KAAKgtE,sBACLsI,EAAUjmC,qBAAsB,GAEhCrvC,KAAK+sE,sBACLuI,EAAUhmC,qBAAsB,GAEhCtvC,KAAKovC,UACLkmC,EAAUlmC,SAAU,GAGxBkmC,EAAUjjC,WAAWgjC,GACrBr1E,KAAKgxE,aAAav+B,EAAE4iC,GAASpkD,KAAK,aAE3BqkD,EAUXF,2BAA2BG,GACvB,MAAMD,EAAYx1D,IAAQ01D,YACpB,IAAItmC,IAAIlvC,KAAKghB,eAAewtD,kBAAkBx8C,KAC9C,IAAIkd,IAAIlvC,KAAKghB,eAAeA,eAAewtD,kBAAkBx8C,KA+BnE,OA7BAujD,EAAenmE,QAAQ,CAACkgB,EAAOsgB,MAE3BtgB,EAAQA,EAAMvsB,MAAM,SACd2N,MACFoP,IAAQ01D,YACRlmD,EAAMlgB,QAAQ5K,IACV8wE,EAAU7nD,MAAMmiB,GACV0lC,EAAU7nD,MAAMmiB,GAAKhuC,QAAW4C,EAAF,OAAc,MAGtD8qB,EAAMlgB,QAAQ5K,IACV,MAAMkrC,EAAM4lC,EAAU7nD,MAAMgoD,UAAUhkD,GAASA,EAAMgC,SAASjvB,IAE1DkrC,GAAO,IACP4lC,EAAU7nD,MAAMiiB,GAAO4lC,EAAU7nD,MAAMiiB,GAAK9tC,QAAW4C,EAAF,OAAc,IAM/Dsb,IAAQsQ,cACRklD,EAAU7nD,MAAMiiB,GAAO4lC,EAAU7nD,MAAMiiB,GAAK9tC,QAAQ,aAAc,oBAMtF0zE,EAAUhlE,IAAMglE,EAAUr9D,QAAUq9D,EAAU7nD,MAAMtqB,KAAK,IAElDmyE,EAUXH,wBAAwBV,GACpB,MAAMa,EAAY,IAAIpmC,IAAIlvC,KAAKghB,eAAewtD,kBAAkBx8C,KAOhE,OALAyiD,EAAYrlE,QAAQ,CAACkgB,EAAOsgB,KACxB0lC,EAAU7nD,MAAMmiB,IAAQtgB,IAE5BgmD,EAAUhlE,IAAMglE,EAAUr9D,QAAUq9D,EAAU7nD,MAAMtqB,KAAK,IAElDmyE,EAYXxG,aAAa4G,GACT,GAA2C,WAAvC11E,KAAKghB,eAAeitD,eAA6B,CACjD,MAAMjpE,EAAQ,IAAI3N,MAAM,4CAIxB,OAFA2I,KAAKm/C,KAAK3/B,aAAa0D,KAAKy3B,IAAW9oD,qBAAsBmT,EAAOhF,MAE7Dg9B,QAAQE,OAAOl4B,GAG1B,MAAMswE,EACAI,GAAqB11E,KAAKghB,eAAewtD,kBAAkBx8C,IAEjE,IAAKsjD,EAAW,CACZ,MAAMtwE,EAAQ,IAAI3N,MAAO,kEAAiE2I,KAAKitC,OAI/F,OAFAjtC,KAAKm/C,KAAK3/B,aAAa0D,KAAKy3B,IAAW9oD,qBAAsBmT,EAAOhF,MAE7Dg9B,QAAQE,OAAOl4B,GAG1B,MAAMwpE,EAAoB,IAAImH,sBAAsB,CAChD1uE,KAAMjH,KAAKorE,YAAc,SAAW,QACpCp5C,IAAKsjD,IAGT,OAAIt1E,KAAKorE,YACEprE,KAAK41E,sBAAsBpH,GAG/BxuE,KAAK61E,sBAAsBrH,GAStCqH,sBAAsBrH,GAGlB,OAFAvwD,EAAOhZ,MAAM,2CAENjF,KAAKghB,eAAe80D,qBAAqBtH,GAC3C9zC,KAAK,KACFzc,EAAOhZ,MAAM,gCAENjF,KAAKghB,eAAe+0D,aAAa/1E,KAAKmrE,kBACxCzwC,KAAKs7C,IACF/3D,EAAOhZ,MAAM,0CAENjF,KAAKghB,eAAe+wD,oBAAoBiE,OAWnEJ,sBAAsBpH,GAGlB,OAFAvwD,EAAOhZ,MAAM,+BAENjF,KAAKghB,eAAe6wD,YAAY7xE,KAAKmrE,kBACvCzwC,KAAKu7C,IACFh4D,EAAOhZ,MAAM,0CAENjF,KAAKghB,eAAe+wD,oBAAoBkE,GAC1Cv7C,KAAK,KACFzc,EAAOhZ,MACH,2CAGGjF,KAAKghB,eAAe80D,qBAAqBtH,OAiBpE0H,aAAaC,EAAUC,GACnB,MAAM1H,EAAeC,IACjB,MAAM2D,EAActyE,KAAKghB,eAAe6tD,iBAAiB78C,IAErDlS,IAAQ01D,cAKJx1E,KAAKghB,eAAe7Z,QAAQimE,uBACzB+I,GAAYC,GAAYA,EAASzL,gBAMpC3qE,KAAKghB,eAAeq1D,qBAInBF,GAAYC,GAAYA,EAASzL,eAMlC3qE,KAAKghB,eAAeq1D,oBAGbF,GAAYA,EAASxL,iBAAmByL,IAK/Cp2E,KAAKghB,eAAeq1D,oBACpBr2E,KAAKghB,eAAeqwD,yBAI5BrxE,KAAKghB,eAAek1D,aAAaC,EAAUC,GACtC17C,KAAK47C,IACF,IAAIv0C,EAAU/E,QAAQC,UAYtB,OAVIq5C,IACIH,GAAYC,IACbp2E,KAAKitC,QAAU0/B,MAClB5qC,EAAU/hC,KAAK8uE,eAAep0C,KAAK,KAC/B,MAAM67C,EAAc,IAAIrnC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KAEjEhyB,KAAKgvE,mBAAmB,IAAI9/B,IAAIojC,GAAciE,MAI/Cx0C,EAAQrH,KAAK,KAChB,GAAI07C,GAAYA,EAASzL,eAGrB,OAAO3qE,KAAKghB,eAAe4yD,sCAGtBl5C,KAAK,IAAM16B,KAAKghB,eAAe2yD,4BAC/Bj5C,KAAK,IAAM16B,KAAKghB,eAAe0yD,qBAI/Ch5C,KAAK,IAAMi0C,IAAoB3pE,GAAS2pE,EAAiB3pE,KAGlE,OAAO,IAAIg4B,QAAQ,CAACC,EAASC,KACzBl9B,KAAKmsE,kBAAkBjpE,KACnBwrE,EACA1pE,IACQA,GACAiZ,EAAOjZ,MAAM,uBAAwBA,GACrCk4B,EAAOl4B,KAEPiZ,EAAO/Y,KAAK,uBACZ+3B,SAgBpBi4C,+BAA+BsB,EAAkBhC,GAC7C,MAAMe,EAAiB,GA2DvB,OAzDA9iC,EAAE+jC,GAAkBvkC,KAAK,CAACyiC,EAAI/hC,KAC1B,MAAM9lD,EAAO4lD,EAAEE,GAASpxC,KAAK,QAC7B,IAAI+tB,EAAQ,GAEZmjB,EAAEE,GACG1hB,KAAK,uDACLghB,MAAK,WAEF,MAAM9gB,EAAYnxB,KAAK4C,aAAa,aAC9B4tB,EACAiiB,EAAEzyC,MACCixB,KAAK,WACLzrB,KAAI,WACD,OAAOxF,KAAK4C,aAAa,WAE5BzV,MAELqjC,EAAMj6B,SACN+4B,GACQ,gBAAe6B,KACfX,EAAMrtB,KAAK,eAK/B,MAAMqtB,EAAQ,GAIRiiB,EAAEE,GAAS1hB,KACT,mDAEJghB,MAAK,WAEL,MAAMjtB,EAAOytB,EAAEzyC,MAAMuB,KAAK,QAE1BivB,EAAMttB,KAAK8hB,MAEfwvD,EAAiB/mD,MAAMre,QAAQ,CAACqe,EAAOknD,KAC9BloD,IAAQO,SAASS,EAAQ,SAAQ5gC,KAGjC0oF,EAAeZ,KAChBY,EAAeZ,GAAM,IAEzBnkD,EAAMphB,QAAQ4V,IACV,MAAMyxD,EACAhqD,IAAQuD,UAAUvC,EAAQ,UAASzI,GAErCyxD,EAAUlgF,SACVg/E,EAAeZ,IAAU8B,EAAUtzE,KAAK,QAAjB,UAG/BoyE,EAAeZ,IAAOrlD,OAIvBimD,EAeXmB,qBAAqBC,EAAeC,GAChC,MAAMC,EACA,IAAI3nC,IAAIlvC,KAAKghB,eAAe6tD,iBAAiB78C,KACnD,IAAI8kD,EAAU,IAAIC,IAAUH,EAAQC,GACpC,MAAMG,EAAaF,EAAQG,cAE3B,GAAIjqF,OAAOuY,KAAKyxE,GAAYzgF,OAKxB,OAJA0nB,EAAOjZ,MACF,GAAEhF,kCAAkC22E,IACrCK,IAEG,EAGXF,EAAU,IAAIC,IAAUF,EAAiBD,GACzC,MAAMM,EAAeJ,EAAQG,cAE7B,OAAIjqF,OAAOuY,KAAK2xE,GAAc3gF,SAC1B0nB,EAAOjZ,MACF,GAAEhF,qCAAqC22E,IACxCO,IAEG,GAcfC,iBAAiB74C,GACb,OAAOt+B,KAAKo3E,6BACR,EAA2B94C,GAC1B5D,KAAK,KAGF,GAAI4D,EAAMqsC,gBAAkB7qD,IAAQ4qD,8BAChC,OAAO1qE,KAAKyzE,uBACP/4C,KAAK,IAAM16B,KAAK4zE,uCAChBl5C,KAAK,IAAM16B,KAAK2zE,8BAarC0D,kBAAkB/4C,GACd,OAAOt+B,KAAKo3E,6BACR,EAA2B94C,GAUnC84C,4BAA4BE,EAAQh5C,GAChC,IAAKA,EACD,OAAOtB,QAAQE,OAAO,kCAE1B,MAAMy5C,EAAgBW,EAAS,kBAAoB,iBAC7C5I,EAAeC,IACjB,MAAMrrD,EAAMtjB,KAAKghB,eAEjB,IAAKsC,EAKD,YAJAqrD,EACK,iBAAgBgI,yCAKzB,MAAMY,EAAcj0D,EAAIurD,iBAAiB78C,KAEnCslD,EACIh0D,EAAIk0D,gBAAgBl5C,GACpBhb,EAAIm0D,eAAen5C,IAGxB5D,KAAK47C,IACEA,GAAqBiB,GAAej0D,EAAIkrD,kBAAkBx8C,IAC1DhyB,KAAK8uE,eACAp0C,KAAK,KAIF16B,KAAK02E,qBACDC,EAAe,IAAIznC,IAAIqoC,IAC3B5I,MAGRA,KAGRA,IAGR,OAAO,IAAI3xC,QAAQ,CAACC,EAASC,KACzBl9B,KAAKmsE,kBAAkBjpE,KACnBwrE,EACA1pE,IACQA,EACAk4B,EAAOl4B,GAEPi4B,QAgBpBy6C,uBAAuBC,EAAaC,GAChC,IAAK53E,KAAKghB,eACN,OAAOgc,QAAQE,OACX,uEAIR,MAAM26C,EAAcF,EAAc,eAAiB,iBAC7CG,EAAcF,EAAc,eAAiB,iBAEnD35D,EAAO/Y,KAAM,eAAc4yE,MAAgBD,aAE3C,MAAMnJ,EAAeC,IACjB,MAAMoJ,EAAkB/3E,KAAKitC,QAAU0/B,IAMjCqL,EACAh4E,KAAKghB,eAAei3D,uBAAuBN,GAE7C33E,KAAK2rE,oBAAsBiM,IAC3B53E,KAAK2rE,kBAAoBiM,EAUrB53E,KAAK8lC,OAASiyC,GACd/3E,KAAKwyE,qBAIb,MAAM0F,EACAl4E,KAAKghB,eAAem3D,uBAClBn4E,KAAK2rE,mBAAqB3rE,KAAK4rE,oBAInCmM,IACQC,GAAsBE,GAC9Bl4E,KAAK8uE,eACAp0C,KACGi0C,EACAA,GAERA,KAIR,OAAO,IAAI3xC,QAAQ,CAACC,EAASC,KACzBl9B,KAAKmsE,kBAAkBjpE,KACnBwrE,EACA1pE,IACQA,EACAk4B,EAAOl4B,GAEPi4B,QAgBpBm7C,eAAetN,GACX,MAAMuN,EACAzN,EAAgB0N,kBAAkBxN,GAClCyN,EACA3N,EAAgB4N,oBAAoB1N,GAU1C,GAPIyN,IACAt6D,EAAO/Y,KAAM,GAAElF,0CAA0Cu4E,KACzDv4E,KAAKgsE,yBAA2BuM,EAChCv4E,KAAKwf,aAAa0D,KACdisD,IAAmBC,iCAAkCpvE,OAGrC,OAApBq4E,EAKA,YAJAp6D,EAAOjZ,MACAhF,KAAF,0EAkBTie,EAAOhZ,MACAjF,KAAF,gCACM,mBAAkBq4E,OAE7Br4E,KAAKmsE,kBAAkBjpE,KAhBFyrE,IACb3uE,KAAK0sE,gBAAgB,mBACd1sE,KAAKy4E,yBAAyBJ,GAGrCr4E,KAAK8uE,eACAp0C,KAAKi0C,EAAkBA,GAE5BA,KAUJ3pE,IACQA,GACAiZ,EAAOjZ,MAAM,0BAA2BA,KAexDyzE,yBAAyBC,GACrB,MAAMC,EACuB,SAAvBD,GAC6B,cAAvBA,GAAsC14E,KAAKorE,aACpB,cAAvBsN,IAAuC14E,KAAKorE,YAQxD,OANIuN,IAAwB34E,KAAK4rE,qBAC7B3tD,EAAOhZ,MACF,GAAEjF,iCAAiC24E,KACxC34E,KAAK4rE,mBAAqB+M,GAGvB34E,KAAKghB,eAAem3D,uBACvBn4E,KAAK2rE,mBAAqB3rE,KAAK4rE,oBAQvCoD,mBAAmB4H,EAAQgC,GAEvB,GAAI54E,KAAKitC,QAAU0/B,IAGf,YAFA1uD,EAAO9Y,KAAM,4BAA2BnF,KAAKitC,kBAKjD,IAAKjtC,KAAKuU,WAAW3K,UAQjB,OANK5J,KAAKsrE,qBACNtrE,KAAKsrE,mBAAqBsL,GAE9B52E,KAAKurE,mBAAqBqN,OAC1B36D,EAAO9Y,KAAK,+DAKhBnF,KAAKsrE,wBAAqB/qE,EAC1BP,KAAKurE,wBAAqBhrE,EAG1B,IAAIs4E,EAAY,IAAI9B,IAAU6B,EAAQhC,GACtC,MAAM9D,EAASn3E,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UAC1BjkE,KAAM,QACLta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,gBACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAGMwsE,EAAU9oC,SAAS+iC,IAGvC70D,EAAO/Y,KAAK,wBAAyB4tE,EAAOztE,QAC5CrF,KAAKuU,WAAWnG,OACZ0kE,EAAQ,KACR9yE,KAAKswE,sBAAsBwC,GAhyExB,MAkyEP70D,EAAOrZ,IAAI,yBAIfi0E,EAAY,IAAI9B,IAAUH,EAAQgC,GAClC,MAAMr4D,EAAM5kB,cAAI,CAAEotC,GAAI/oC,KAAKkrE,UACvBjkE,KAAM,QACLta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQ,aACRq8C,UAAW7mE,KAAKgwE,aAChB3jE,IAAKrM,KAAKqM,MAIOwsE,EAAU9oC,SAASxvB,IAGxCtC,EAAO/Y,KAAK,qBAAsBqb,EAAIlb,QACtCrF,KAAKuU,WAAWnG,OACZmS,EAAK,KAAMvgB,KAAKswE,sBAAsB/vD,GAtzEnC,MAwzEPtC,EAAOrZ,IAAI,0BAsBnB0rE,sBAAsBwI,EAASC,GAC3B,OAAOC,IAEH,MAAMh0E,EAAQ,GAGRi0E,EAAaxmC,EAAEumC,GAAa/nD,KAAK,SAEvC,GAAIgoD,EAAW1iF,OAAQ,CACnByO,EAAM2X,KAAOs8D,EAAW13E,KAAK,QAC7B,MAAM23E,EAAiBzmC,EAAEumC,GAAa/nD,KAAK,gBAEvCioD,EAAe3iF,SACfyO,EAAMwK,OAAS0pE,EAAe,GAAG/4E,SAGrC,MAAMg5E,EAAcF,EAAWhoD,KAAK,SAEhCkoD,EAAY5iF,SACZyO,EAAMF,IAAMq0E,EAAYx3E,QAI3Bq3E,IACDh0E,EAAMwK,OAAS,WAGnBxK,EAAMiT,QAAUjY,KAAKtJ,WAEjBqiF,EACAA,EAAU/zE,GACHhF,KAAKitC,QAAU0/B,KACM,mBAAjB3nE,EAAMwK,OAMjByO,EAAOhZ,MAAO,iBAAgB2H,KAAKwL,UAAUpT,IAE7C4yB,IAAqBC,iBACjB,IAAIxgC,MACC,iBAAgBuV,KAAKwL,UAAUpT,MASpDo0E,wBACI,OAAOp5E,KAAKghB,eAAeq4D,qBAM/B58D,QACIzc,KAAKitC,MAAQ0/B,IACb3sE,KAAKssE,2BAAwB/rE,EAEzBP,KAAKghB,iBACLhhB,KAAKghB,eAAe6sD,eAAiB,KACrC7tE,KAAKghB,eAAemtD,2BAA6B,KACjDnuE,KAAKghB,eAAeutD,oBAAsB,KAC1CvuE,KAAKghB,eAAegtD,uBAAyB,MAIjDhuE,KAAKmsE,kBAAkBt2B,QAEvB71C,KAAKmsE,kBAAkBjpE,KAAKo2E,IAExBt5E,KAAKisE,eAAegD,YAAY,MAGhCjvE,KAAKghB,gBAAkBhhB,KAAKghB,eAAevE,QAC3C68D,MAIJt5E,KAAKmsE,kBAAkBoN,WAO3B7iF,WACI,MAAQ,uBAAsBsJ,KAAK8lC,mBACR9lC,KAAKorE,mBAAmBprE,KAAKqM,OAS5DshE,4BAA2B,UAAE6L,IACzB,IAAKA,IAAcA,EAAUC,uBACzB,OAMJ,MAAM91E,EAAM3D,KAAK05E,mBAEjB,OAAOC,sBAAYh2E,GAAO,GAAM,M,2EC99EzB,KAIXyrE,iCAAkC,mD,6BCJtC,sHAYO,MAAMxmD,EAAsB,4BAWtBgxD,EACP,wC,8BCxBN,mIAYA,MAAM37D,EAASF,oBAAUM,GAInBw7D,EACK,QADLA,EAEQ,WAFRA,EAGY,eAHZA,EAIW,cAJXA,EAKY,eAGZC,EAAWzsF,OAAO,WAElB0sF,EAAmB,CACrBlR,+BAAgC,qCAChCE,wBAAyB,+BAuBtB,MAAMd,UAAmB7tC,IAI5BnkC,YAAYssB,GACR8X,QAEAr6B,KAAKg6E,MAAQz3D,EACbviB,KAAKi6E,MAAQ,IAAIC,IACjBl6E,KAAK6nE,UAAOtnE,EACZP,KAAKm6E,WAAa,EAClBn6E,KAAKo6E,MAAQ,IAAI76D,IAEb0oD,EAAWzqC,eACXx9B,KAAKq6E,gBAELr6E,KAAKg6E,MAAMr4D,GAAGgB,4BAAiD3iB,KAAKs6E,2BAA2BxsF,KAAKkS,OACpGA,KAAKg6E,MAAMr4D,GAAGgB,oBAAyC3iB,KAAKu6E,oBAAoBzsF,KAAKkS,OACrFA,KAAKg6E,MAAMr4D,GAAGgB,kBAAuC3iB,KAAKw6E,kBAAkB1sF,KAAKkS,OACjFA,KAAKg6E,MAAMr4D,GAAGgB,YAAiC3iB,KAAKwoE,mBAAmB16E,KAAKkS,QAE5EA,KAAKi6E,MAAM/8C,OAAO,IAAI7lC,MAAM,sBASpC,qBACI,YAA6B,IAAfnL,OAAOuuF,IAUzB,uBAAuB5sF,GAGnB,OAFAmS,KAAK6nE,KAAOh6E,EAELmS,KAAKm6E,UAUhB,gBAAgBtsF,GAEZmS,KAAK6nE,KAAOh6E,EACZmS,KAAKm6E,YAGL,MAAMO,EAAW,GAEjB,IAAK,MAAMre,KAAer8D,KAAKg6E,MAAMhd,kBAAmB,CACpD,MAAM2d,EAAMte,EAAYG,QAClBoe,EAAU56E,KAAK66E,uBAAuBxe,GAI5C,IAAKue,EAAQ3iE,QAAS,CAClBgG,EAAO9Y,KAAM,oCAAmCw1E,4BAGhD,SAGJ,MAAMxvE,EAAO2vE,cACPnhF,EAAO,CACT,CAAC++C,KAjHQ,MAkHTqiC,IAAK,CACD9zE,KAAM4yE,EACNlgF,KAAM,CACFqhF,WAAYh7E,KAAKi7E,gBAAgBL,EAAQ3iE,SACzC9M,UAINve,EAAI,IAAIstF,IAEdttF,EAAEsuF,iBA7HM,KA8HRtuF,EAAEwwC,MAAM,KACJp9B,KAAKo6E,MAAM12D,OAAOvY,KAEtBnL,KAAKo6E,MAAMh5D,IAAIjW,EAAMve,GACrB8tF,EAASx3E,KAAKtW,GAEdoT,KAAKm7E,aAAaxhF,EAAMghF,GAO5B,aAJM39C,QAAQo+C,WAAWV,GAIlB16E,KAAKm6E,UAShB,sBACIl8D,EAAOhZ,MAAM,uBAEb,UACUw1E,IAAI7vE,OAEV5K,KAAKq7E,YAAc,IAAIZ,IAAIa,QAC3Bt7E,KAAKq7E,YAAYztF,SAEjB,MAAM2tF,EAAS3uE,KAAKsL,MAAMlY,KAAKq7E,YAAYG,iBAE3Cx7E,KAAKy7E,OAASF,EAAOG,WAErBz9D,EAAOhZ,MAAM,oBACbjF,KAAKi6E,MAAMh9C,UACb,MAAO5jC,GACL4kB,EAAOjZ,MAAM,2BAA4B3L,GACzC2G,KAAKi6E,MAAM/8C,OAAO7jC,IAY1B4hF,gBAAgBhjE,GACZ,MAAM0jE,EAAU,GAOhB,YALkBp7E,IAAdP,KAAK6nE,OACL8T,EAAQ9tF,MAAMmS,KAAK6nE,MAAO+T,IAASC,cAAc77E,KAAK6nE,MACtD8T,EAAQG,SAAW97E,KAAKm6E,WAGrBliE,EAAQ8jE,QAAQnvE,KAAKwL,UAAUujE,IAU1Cd,uBAAuBxe,GAGnB,OAFAA,EAAYyd,GAAYzd,EAAYyd,IAAa,GAE1Czd,EAAYyd,GASvB,4BACI77D,EAAOhZ,MAAM,2BAEPjF,KAAKi6E,MAEX,MAAMS,EAAW,GAKjB,IAAK,MAAMre,KAAer8D,KAAKg6E,MAAMhd,kBACjC0d,EAASx3E,KAAKlD,KAAKg8E,iBAAiB3f,UAGlCr/B,QAAQo+C,WAAWV,GAW7B,0BACIz8D,EAAOhZ,MAAM,yBAEPjF,KAAKi6E,MAEX,IAAK,MAAM5d,KAAer8D,KAAKg6E,MAAMhd,kBACjCh9D,KAAKwoE,mBAAmBnM,EAAYG,QAASH,GAG7Cr8D,KAAKq7E,cACLr7E,KAAKq7E,YAAYY,OACjBj8E,KAAKq7E,iBAAc96E,GAU3B,iCAAiC87D,EAAa5pC,GAC1C,GA7PiB,QA6PbA,EAAQimB,KACR,OAGJ,IAAKjmB,EAAQsoD,IAGT,YAFA98D,EAAO9Y,KAAK,uCAKVnF,KAAKi6E,MAEX,MAAMn1E,EAAM2tB,EAAQsoD,IACdJ,EAAMte,EAAYG,QAClBoe,EAAU56E,KAAK66E,uBAAuBxe,GAE5C,OAAQv3D,EAAImC,MACZ,KAAK4yE,EACD,GAAIe,EAAQ3iE,QACRgG,EAAO9Y,KAAM,eAAcw1E,2BAE3B36E,KAAKk8E,WAAW7f,EAAa,mCAC1B,CAGH,MAAMpkD,EAAU,IAAIwiE,IAAI0B,QAExBlkE,EAAQmkE,gBAAgBp8E,KAAKq7E,YAAav2E,EAAInL,KAAK0iF,MAAOv3E,EAAInL,KAAK2iF,OACnE1B,EAAQ3iE,QAAUA,EAGlB,MAAMskE,EAAM,CACR,CAAC7jC,KA7RI,MA8RLqiC,IAAK,CACD9zE,KAAM4yE,EACNlgF,KAAM,CACFqhF,WAAYh7E,KAAKi7E,gBAAgBhjE,GACjC9M,KAAMrG,EAAInL,KAAKwR,QAK3BnL,KAAKm7E,aAAaoB,EAAK5B,GAE3B,MAEJ,KAAKd,EACD,GAAIe,EAAQ3iE,QACRgG,EAAO9Y,KAAM,eAAcw1E,2BAE3B36E,KAAKk8E,WAAW7f,EAAa,yBAC1B,GAAIv3D,EAAInL,KAAKwR,OAASyvE,EAAQ4B,mBAAoB,CACrD,MAAM,WAAExB,GAAel2E,EAAInL,KACrB/M,EAAIoT,KAAKo6E,MAAMjtF,IAAI2X,EAAInL,KAAKwR,MAC5B8M,EAAU,IAAIwiE,IAAI0B,QAExBlkE,EAAQwkE,eAAez8E,KAAKq7E,YAAaL,EAAWljE,MAGpD9X,KAAKq7E,YAAYqB,qBAAqBzkE,GAGtC,MAAMte,EAAOse,EAAQ0kE,QAAQ3B,EAAW/zE,KAAM+zE,EAAWljE,MAEzD8iE,EAAQ3iE,QAAUA,EAClB2iE,EAAQ4B,wBAAqBj8E,EAE7B0d,EAAOhZ,MAAO,gCAA+B01E,GAC7C36E,KAAKwf,aAAa0D,KAAK62D,EAAiBlR,+BAAgC8R,GAExE36E,KAAKo6E,MAAM12D,OAAO5e,EAAInL,KAAKwR,MAC3Bve,EAAEqwC,UAEF,MAAMqiB,EAAOs9B,EAAcjjF,GAE3B,GAAI2lD,EAAKzxD,IAAK,CACV,MAAMA,EAAM+tF,IAASiB,YAAYv9B,EAAKzxD,KAChCiuF,EAAWx8B,EAAKw8B,SAEtBlB,EAAQkC,QAAUjvF,EAClBmS,KAAKwf,aAAa0D,KAAK62D,EAAiBhR,wBAAyB4R,EAAK9sF,EAAKiuF,SAG/E79D,EAAO9Y,KAAK,oCAEZnF,KAAKk8E,WAAW7f,EAAa,gBAEjC,MAEJ,KAAKwd,EACD57D,EAAOjZ,MAAMF,EAAInL,KAAKqL,OAEtB,MAEJ,KAAK60E,EACD,GAAIe,EAAQ3iE,QAAS,CACjB,MAAM,WAAE+iE,GAAel2E,EAAInL,KAErB2lD,EAAOs9B,EADAhC,EAAQ3iE,QAAQ0kE,QAAQ3B,EAAW/zE,KAAM+zE,EAAWljE,OAGjE,QAAiBvX,IAAb++C,EAAKzxD,UAAuC0S,IAAlB++C,EAAKw8B,SAAwB,CACvD,MAAMjuF,IAAMyxD,EAAKzxD,KAAM+tF,IAASiB,YAAYv9B,EAAKzxD,KAC3CiuF,EAAWx8B,EAAKw8B,SAEjBiB,IAAQnC,EAAQkC,QAASjvF,KAC1B+sF,EAAQkC,QAAUjvF,EAClBmS,KAAKwf,aAAa0D,KAAK62D,EAAiBhR,wBAAyB4R,EAAK9sF,EAAKiuF,IAI/E,MAAMS,EAAM,CACR,CAAC7jC,KA5WA,MA6WDqiC,IAAK,CACD9zE,KAAM4yE,EACNlgF,KAAM,CACFqhF,WAAYh7E,KAAKi7E,gBAAgBL,EAAQ3iE,SACzC9M,KAAMrG,EAAInL,KAAKwR,QAK3BnL,KAAKm7E,aAAaoB,EAAK5B,SAG3B18D,EAAOhZ,MAAO,kCAAiC01E,sCAE/C36E,KAAKk8E,WAAW7f,EAAa,8CAEjC,MAEJ,KAAKwd,EACD,GAAIe,EAAQ3iE,QAAS,CACjB,MAAM,WAAE+iE,GAAel2E,EAAInL,KAErB2lD,EAAOs9B,EADAhC,EAAQ3iE,QAAQ0kE,QAAQ3B,EAAW/zE,KAAM+zE,EAAWljE,OAGjE,QAAiBvX,IAAb++C,EAAKzxD,UAAuC0S,IAAlB++C,EAAKw8B,SAAwB,CACvD,MAAMjuF,IAAMyxD,EAAKzxD,KAAM+tF,IAASiB,YAAYv9B,EAAKzxD,KAC3CiuF,EAAWx8B,EAAKw8B,SAEjBiB,IAAQnC,EAAQkC,QAASjvF,KAC1B+sF,EAAQkC,QAAUjvF,EAClBmS,KAAKwf,aAAa0D,KAAK62D,EAAiBhR,wBAAyB4R,EAAK9sF,EAAKiuF,IAInF,MAAMlvF,EAAIoT,KAAKo6E,MAAMjtF,IAAI2X,EAAInL,KAAKwR,MAElCnL,KAAKo6E,MAAM12D,OAAO5e,EAAInL,KAAKwR,MAC3Bve,EAAEqwC,eAEFhf,EAAOhZ,MAAO,sCAAqC01E,sCAEnD36E,KAAKk8E,WAAW7f,EAAa,mDAazCmM,mBAAmBthE,EAAIm1D,GACnBp+C,EAAOhZ,MAAO,eAAciC,UAE5B,MAAM0zE,EAAU56E,KAAK66E,uBAAuBxe,GAExCue,EAAQ3iE,UACR2iE,EAAQ3iE,QAAQgkE,OAChBrB,EAAQ3iE,aAAU1X,GAW1B27E,WAAW7f,EAAar3D,GACpB,MAAM21E,EAAMte,EAAYG,QAClBrsD,EAAM,CACR,CAACuoC,KAxbY,MAybbqiC,IAAK,CACD9zE,KAAM4yE,EACNlgF,KAAM,CACFqL,WAKZhF,KAAKm7E,aAAahrE,EAAKwqE,GAW3BQ,aAAaxhF,EAAMwxB,GACfnrB,KAAKg6E,MAAMhxC,YAAYrvC,EAAMwxB,GAUjC6wD,iBAAiB3f,GACb,MAAMse,EAAMte,EAAYG,QAClBoe,EAAU56E,KAAK66E,uBAAuBxe,GAE5C,GAAIue,EAAQ3iE,QAGR,OAFAgG,EAAO9Y,KAAM,iCAAgCw1E,mCAEtC39C,QAAQE,SAGnB,QAAmC38B,IAA/Bq6E,EAAQ4B,mBAGR,OAFAv+D,EAAO9Y,KAAM,iCAAgCw1E,2CAEtC39C,QAAQE,SAInBl9B,KAAKq7E,YAAY2B,uBAAuB,GAExC,MAAMC,EAASrwE,KAAKsL,MAAMlY,KAAKq7E,YAAY6B,iBACrCZ,EAAQtvF,OAAOo2B,OAAO65D,EAAOvB,YAAY,GAE/C,IAAKY,EACD,OAAOt/C,QAAQE,OAAO,IAAI7lC,MAAM,+BAIpC2I,KAAKq7E,YAAY8B,yBAEjB,MAAMhyE,EAAO2vE,cACPlwE,EAAO,CACT,CAAC8tC,KAtfY,MAufbqiC,IAAK,CACD9zE,KAAM4yE,EACNlgF,KAAM,CACF0iF,MAAOr8E,KAAKy7E,OACZa,QACAnxE,UAKNve,EAAI,IAAIstF,IAcd,OAZAttF,EAAEsuF,iBApgBU,KAqgBZtuF,EAAEwwC,MAAM,KACJp9B,KAAKo6E,MAAM12D,OAAOvY,GAClByvE,EAAQ4B,wBAAqBj8E,IAEjCP,KAAKo6E,MAAMh5D,IAAIjW,EAAMve,GAErBoT,KAAKm7E,aAAavwE,EAAM+vE,GAGxBC,EAAQ4B,mBAAqBrxE,EAEtBve,GAYf,SAASgwF,EAAcjjF,GACnB,IACI,OAAOiT,KAAKsL,MAAMve,GACpB,MAAON,GACL,MAAO,IAZf4uE,EAAW17B,OAASwtC,I,sEChiBpBjuF,EAAQmjE,WAuCR,SAAqBmuB,GACnB,IAAIC,EAAOC,EAAQF,GACfG,EAAWF,EAAK,GAChBG,EAAkBH,EAAK,GAC3B,OAAuC,GAA9BE,EAAWC,GAAuB,EAAKA,GA1ClD1xF,EAAQ+wF,YAiDR,SAAsBO,GACpB,IAAIhN,EAcA9jF,EAbA+wF,EAAOC,EAAQF,GACfG,EAAWF,EAAK,GAChBG,EAAkBH,EAAK,GAEvBlnF,EAAM,IAAIsnF,EAVhB,SAAsBL,EAAKG,EAAUC,GACnC,OAAuC,GAA9BD,EAAWC,GAAuB,EAAKA,EAS9BE,CAAYN,EAAKG,EAAUC,IAEzCG,EAAU,EAGVxlF,EAAMqlF,EAAkB,EACxBD,EAAW,EACXA,EAGJ,IAAKjxF,EAAI,EAAGA,EAAI6L,EAAK7L,GAAK,EACxB8jF,EACGwN,EAAUR,EAAI7lF,WAAWjL,KAAO,GAChCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,KAAO,GACpCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,KAAO,EACrCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,IAC/B6J,EAAIwnF,KAAcvN,GAAO,GAAM,IAC/Bj6E,EAAIwnF,KAAcvN,GAAO,EAAK,IAC9Bj6E,EAAIwnF,KAAmB,IAANvN,EAGK,IAApBoN,IACFpN,EACGwN,EAAUR,EAAI7lF,WAAWjL,KAAO,EAChCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,KAAO,EACvC6J,EAAIwnF,KAAmB,IAANvN,GAGK,IAApBoN,IACFpN,EACGwN,EAAUR,EAAI7lF,WAAWjL,KAAO,GAChCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,KAAO,EACpCsxF,EAAUR,EAAI7lF,WAAWjL,EAAI,KAAO,EACvC6J,EAAIwnF,KAAcvN,GAAO,EAAK,IAC9Bj6E,EAAIwnF,KAAmB,IAANvN,GAGnB,OAAOj6E,GA3FTrK,EAAQ+vF,cAkHR,SAAwBgC,GAQtB,IAPA,IAAIzN,EACAj4E,EAAM0lF,EAAMtnF,OACZunF,EAAa3lF,EAAM,EACnB6L,EAAQ,GAIH1X,EAAI,EAAGyxF,EAAO5lF,EAAM2lF,EAAYxxF,EAAIyxF,EAAMzxF,GAH9B,MAInB0X,EAAMd,KAAK86E,EACTH,EAAOvxF,EAAIA,EALM,MAKgByxF,EAAOA,EAAQzxF,EAL/B,QAUF,IAAfwxF,GACF1N,EAAMyN,EAAM1lF,EAAM,GAClB6L,EAAMd,KACJ+6E,EAAO7N,GAAO,GACd6N,EAAQ7N,GAAO,EAAK,IACpB,OAEsB,IAAf0N,IACT1N,GAAOyN,EAAM1lF,EAAM,IAAM,GAAK0lF,EAAM1lF,EAAM,GAC1C6L,EAAMd,KACJ+6E,EAAO7N,GAAO,IACd6N,EAAQ7N,GAAO,EAAK,IACpB6N,EAAQ7N,GAAO,EAAK,IACpB,MAIJ,OAAOpsE,EAAMb,KAAK,KA3IpB,IALA,IAAI86E,EAAS,GACTL,EAAY,GACZH,EAA4B,oBAAfzyB,WAA6BA,WAAa50D,MAEvDumB,EAAO,mEACFrwB,EAAI,EAAG6L,EAAMwkB,EAAKpmB,OAAQjK,EAAI6L,IAAO7L,EAC5C2xF,EAAO3xF,GAAKqwB,EAAKrwB,GACjBsxF,EAAUjhE,EAAKplB,WAAWjL,IAAMA,EAQlC,SAASgxF,EAASF,GAChB,IAAIjlF,EAAMilF,EAAI7mF,OAEd,GAAI4B,EAAM,EAAI,EACZ,MAAM,IAAId,MAAM,kDAKlB,IAAIkmF,EAAWH,EAAIx5E,QAAQ,KAO3B,OANkB,IAAd25E,IAAiBA,EAAWplF,GAMzB,CAAColF,EAJcA,IAAaplF,EAC/B,EACA,EAAKolF,EAAW,GAsEtB,SAASS,EAAaH,EAAO9gE,EAAOmhE,GAGlC,IAFA,IAAI9N,EARoBx4E,EASpBwtE,EAAS,GACJ94E,EAAIywB,EAAOzwB,EAAI4xF,EAAK5xF,GAAK,EAChC8jF,GACIyN,EAAMvxF,IAAM,GAAM,WAClBuxF,EAAMvxF,EAAI,IAAM,EAAK,QACP,IAAfuxF,EAAMvxF,EAAI,IACb84E,EAAOliE,KAdF+6E,GADiBrmF,EAeMw4E,IAdT,GAAK,IACxB6N,EAAOrmF,GAAO,GAAK,IACnBqmF,EAAOrmF,GAAO,EAAI,IAClBqmF,EAAa,GAANrmF,IAaT,OAAOwtE,EAAOjiE,KAAK,IAjGrBy6E,EAAU,IAAIrmF,WAAW,IAAM,GAC/BqmF,EAAU,IAAIrmF,WAAW,IAAM,I,6BCnB/B,6CAQA,SAAS4mF,EAAYC,EAAQC,GAEzB,IAAKA,EACD,OAAO,EAIX,GAAID,EAAO7nF,SAAW8nF,EAAO9nF,OACzB,OAAO,EAGX,IAAK,IAAIjK,EAAI,EAAGC,EAAI6xF,EAAO7nF,OAAQjK,EAAIC,EAAGD,IAEtC,GAAI8xF,EAAO9xF,aAAc8J,OAASioF,EAAO/xF,aAAc8J,OAEnD,IAAKgoF,EAAO9xF,GAAGgyF,OAAOD,EAAO/xF,IACzB,OAAO,OAER,GAAI8xF,EAAO9xF,KAAO+xF,EAAO/xF,GAG5B,OAAO,EAIf,OAAO,EAQI,SAASyqF,EAAUwH,EAAOC,GAGrC,GAFAx+E,KAAKu+E,MAAQA,EACbv+E,KAAKw+E,SAAWA,GACXD,EACD,MAAM,IAAIlnF,MAAM,yBACb,IAAKmnF,EACR,MAAM,IAAInnF,MAAM,4BAQxB0/E,EAAU7oF,UAAU+oF,YAAc,WAE9B,MAAMwH,EAAWz+E,KAAKu+E,MAAMhvC,kBACtBmvC,EAAe1+E,KAAKw+E,SAASjvC,kBAC7BovC,EAAW,GAoFjB,OAlFA3xF,OAAOuY,KAAKm5E,GAActvE,QAAQwvE,IAC9B,MAAMC,EAAUJ,EAASG,GACnBE,EAAcJ,EAAaE,GAE5BC,IAAWC,GAQhB9xF,OAAOuY,KAAKu5E,EAAYtuD,OAAOphB,QAAQ4V,IACnC,IAAkD,IAA9Ch4B,OAAOuY,KAAKs5E,EAAQruD,OAAO5sB,QAAQohB,GAG9B25D,EAASC,KACVD,EAASC,GAAkB,CACvBnvC,WAAYqvC,EAAYrvC,WACxBC,IAAKovC,EAAYpvC,IACjBlf,MAAO,GACPM,WAAY,KAGpB6tD,EAASC,GAAgBpuD,MAAMxL,GAAQ85D,EAAYtuD,MAAMxL,QACtD,GAAI85D,EAAYtuD,MAAMxL,GAAMsK,OACpBuvD,EAAQruD,MAAMxL,GAAMsK,MAAO,MAGO/uB,IADvBs+E,EAAQruD,MAAMxL,GAAMsK,MAAM2B,KAC5CzsB,IAAkC,IAA1BA,EAAKZ,QAAQ,iBAEoBrD,IADtBu+E,EAAYtuD,MAAMxL,GAAMsK,MAAM2B,KACjDzsB,IAAkC,IAA1BA,EAAKZ,QAAQ,YAGhB+6E,EAASC,KACVD,EAASC,GAAkB,CACvBnvC,WAAYqvC,EAAYrvC,WACxBC,IAAKovC,EAAYpvC,IACjBlf,MAAO,GACPM,WAAY,KAGpB6tD,EAASC,GAAgBpuD,MAAMxL,GACzB85D,EAAYtuD,MAAMxL,OAMpC85D,EAAYhuD,WAAW1hB,QAAQ2vE,IAG3B,IAAIxtE,GAAU,EAEd,IAAK,IAAIjlB,EAAI,EAAGA,EAAIuyF,EAAQ/tD,WAAWv6B,OAAQjK,IAAK,CAChD,MAAM0yF,EAAcH,EAAQ/tD,WAAWxkC,GAEvC,GAAIyyF,EAAe5tD,YAAc6tD,EAAY7tD,WACtCgtD,EAAYY,EAAevuD,MAAOwuD,EAAYxuD,OAAQ,CAEzDjf,GAAU,EACV,OAIHA,IAIIotE,EAASC,KACVD,EAASC,GAAkB,CACvBnvC,WAAYqvC,EAAYrvC,WACxBC,IAAKovC,EAAYpvC,IACjBlf,MAAO,GACPM,WAAY,KAGpB6tD,EAASC,GAAgB9tD,WAAW5tB,KAAK67E,OAvE7CJ,EAASC,GAAkBE,IA4E5BH,GAMX5H,EAAU7oF,UAAU6hD,SAAW,SAASkvC,GACpC,MAAMC,EAAgBl/E,KAAKi3E,cAE3B,IAAIkI,GAAW,EA+Df,OA7DAnyF,OAAOuY,KAAK25E,GAAe9vE,QAAQqgC,IAC/B0vC,GAAW,EACX,MAAM1xD,EAAQyxD,EAAczvC,GAE5BwvC,EAAOtyF,EAAE,UAAW,CAAEE,KAAM4gC,EAAMiiB,MAElCuvC,EAAOtyF,EAAE,cACL,CAAEsZ,MAAO,6BACLwnB,MAAOA,EAAMiiB,MAKrB1iD,OAAOuY,KAAKkoB,EAAM+C,OAAOphB,QAAQmyC,IAC7B,MAAM69B,EAAY3xD,EAAM+C,MAAM+wB,GAE9B09B,EAAOtyF,EAAE,SAAU,CAAEsZ,MAAO,oCAC5Bg5E,EAAOzjF,MAAM,CAAEwpB,KAAMo6D,EAAUp6D,OAG/Bo6D,EAAU9vD,MAAMlgB,QAAQ5K,IACpB,MAAMorC,EAAMprC,EAAKZ,QAAQ,KACnBgtC,EAAKpsC,EAAK4Q,OAAOw6B,EAAM,GAG7B,GADAqvC,EAAOtyF,EAAE,cACgB,IAArBikD,EAAGhtC,QAAQ,KACXq7E,EAAOzjF,MAAM,CAAE3O,KAAM+jD,QAClB,CACH,MAAMyuC,EAAKzuC,EAAG7tC,MAAM,IAAK,GACnBlW,EAAOwyF,EAAG,GACV9xF,EAAQk/B,IAAQC,mBAAmB2yD,EAAG,IAE5CJ,EAAOzjF,MAAM,CAAE3O,SACfoyF,EAAOzjF,MAAM,CAAEjO,UAEnB0xF,EAAO94E,OAEX84E,EAAO94E,OAIXsnB,EAAMqD,WAAW1hB,QAAQyiB,IACjBA,EAAUrB,MAAMj6B,SAEhB0oF,EAAOtyF,EAAE,aAAc,CACnBwkC,UAAWU,EAAUV,UACrBlrB,MAAO,oCAGX4rB,EAAUrB,MAAMphB,QAAQ4V,IACpBi6D,EAAOtyF,EAAE,SAAU,CAAEq4B,SAChB7e,OAET84E,EAAO94E,QAIf84E,EAAO94E,KACP84E,EAAO94E,OAGJg5E,I,cCtNX,IAOIG,EACAC,EARAvrB,EAAUjoE,EAAOD,QAAU,GAU/B,SAAS0zF,IACL,MAAM,IAAInoF,MAAM,mCAEpB,SAASooF,IACL,MAAM,IAAIpoF,MAAM,qCAsBpB,SAASqoF,EAAWC,GAChB,GAAIL,IAAqB/0E,WAErB,OAAOA,WAAWo1E,EAAK,GAG3B,IAAKL,IAAqBE,IAAqBF,IAAqB/0E,WAEhE,OADA+0E,EAAmB/0E,WACZA,WAAWo1E,EAAK,GAE3B,IAEI,OAAOL,EAAiBK,EAAK,GAC/B,MAAMtmF,GACJ,IAEI,OAAOimF,EAAiB7yF,KAAK,KAAMkzF,EAAK,GAC1C,MAAMtmF,GAEJ,OAAOimF,EAAiB7yF,KAAKuT,KAAM2/E,EAAK,MAvCnD,WACG,IAEQL,EADsB,mBAAf/0E,WACYA,WAEAi1E,EAEzB,MAAOnmF,GACLimF,EAAmBE,EAEvB,IAEQD,EADwB,mBAAjB9xE,aACcA,aAEAgyE,EAE3B,MAAOpmF,GACLkmF,EAAqBE,GAjB5B,GAwED,IAEIG,EAFAC,EAAQ,GACRC,GAAW,EAEXC,GAAc,EAElB,SAASC,IACAF,GAAaF,IAGlBE,GAAW,EACPF,EAAarpF,OACbspF,EAAQD,EAAa5lF,OAAO6lF,GAE5BE,GAAc,EAEdF,EAAMtpF,QACN0pF,KAIR,SAASA,IACL,IAAIH,EAAJ,CAGA,IAAIlyE,EAAU8xE,EAAWM,GACzBF,GAAW,EAGX,IADA,IAAI3nF,EAAM0nF,EAAMtpF,OACV4B,GAAK,CAGP,IAFAynF,EAAeC,EACfA,EAAQ,KACCE,EAAa5nF,GACdynF,GACAA,EAAaG,GAAYh4E,MAGjCg4E,GAAc,EACd5nF,EAAM0nF,EAAMtpF,OAEhBqpF,EAAe,KACfE,GAAW,EAnEf,SAAyBI,GACrB,GAAIX,IAAuB9xE,aAEvB,OAAOA,aAAayyE,GAGxB,IAAKX,IAAuBE,IAAwBF,IAAuB9xE,aAEvE,OADA8xE,EAAqB9xE,aACdA,aAAayyE,GAExB,IAEWX,EAAmBW,GAC5B,MAAO7mF,GACL,IAEI,OAAOkmF,EAAmB9yF,KAAK,KAAMyzF,GACvC,MAAO7mF,GAGL,OAAOkmF,EAAmB9yF,KAAKuT,KAAMkgF,KAgD7CC,CAAgBvyE,IAiBpB,SAASwyE,EAAKT,EAAK/uD,GACf5wB,KAAK2/E,IAAMA,EACX3/E,KAAK4wB,MAAQA,EAYjB,SAASyvD,KA5BTrsB,EAAQssB,SAAW,SAAUX,GACzB,IAAIx9C,EAAO,IAAI/rC,MAAM+K,UAAU5K,OAAS,GACxC,GAAI4K,UAAU5K,OAAS,EACnB,IAAK,IAAIjK,EAAI,EAAGA,EAAI6U,UAAU5K,OAAQjK,IAClC61C,EAAK71C,EAAI,GAAK6U,UAAU7U,GAGhCuzF,EAAM38E,KAAK,IAAIk9E,EAAKT,EAAKx9C,IACJ,IAAjB09C,EAAMtpF,QAAiBupF,GACvBJ,EAAWO,IASnBG,EAAKlyF,UAAU6Z,IAAM,WACjB/H,KAAK2/E,IAAI/9C,MAAM,KAAM5hC,KAAK4wB,QAE9BojC,EAAQusB,MAAQ,UAChBvsB,EAAQl0C,SAAU,EAClBk0C,EAAQwsB,IAAM,GACdxsB,EAAQysB,KAAO,GACfzsB,EAAQjgB,QAAU,GAClBigB,EAAQ0sB,SAAW,GAInB1sB,EAAQryC,GAAK0+D,EACbrsB,EAAQpwB,YAAcy8C,EACtBrsB,EAAQtoB,KAAO20C,EACfrsB,EAAQvlB,IAAM4xC,EACdrsB,EAAQlyC,eAAiBu+D,EACzBrsB,EAAQvwC,mBAAqB48D,EAC7BrsB,EAAQ9wC,KAAOm9D,EACfrsB,EAAQ7lB,gBAAkBkyC,EAC1BrsB,EAAQ5lB,oBAAsBiyC,EAE9BrsB,EAAQ9lB,UAAY,SAAUrhD,GAAQ,MAAO,IAE7CmnE,EAAQE,QAAU,SAAUrnE,GACxB,MAAM,IAAIwK,MAAM,qCAGpB28D,EAAQ2sB,IAAM,WAAc,MAAO,KACnC3sB,EAAQ4sB,MAAQ,SAAUC,GACtB,MAAM,IAAIxpF,MAAM,mCAEpB28D,EAAQ8sB,MAAQ,WAAa,OAAO,I,6BCvLpC,kCAgBA50F,OAAO60F,aAAe70F,OAAO60F,cAAgB70F,OAAO80F,mBAEpD,IAAI/yC,EAAU,KAkEC,SAASgzC,EAAoB1/D,EAAQ2/D,EAAU11E,GAC1DxL,KAAKuhB,OAASA,EACdvhB,KAAKmhF,WAAa,KAClBnhF,KAAKohF,cAAgBF,EACrBlhF,KAAK2oC,WAAa,EAClB3oC,KAAKwL,SAAWA,EArEhBtf,OAAO60F,eACP9yC,EAAU,IAAI8yC,aAUd9yC,EAAQozC,SAAWpzC,EAAQozC,WAgE/BJ,EAAoB/yF,UAAU6uB,MAAQ,WAClC,IAAKkkE,EAAoBK,wBACrB,OAEJrzC,EAAQjjC,SACR,MAAMu2E,EAAWtzC,EAAQuzC,iBAEzBD,EAASE,sBAxF2B,GAyFpCF,EAASG,QA/FsB,KAiGhBzzC,EAAQ0zC,wBAAwB3hF,KAAKuhB,QAE7C9V,QAAQ81E,GAEfvhF,KAAKmhF,WAAa7kD,YACd,KACI,MAAM1L,EAAQ,IAAIo6B,WAAWu2B,EAASK,mBAEtCL,EAASM,sBAAsBjxD,GAC/B,MAAM+X,EA3ElB,SAAoCm5C,GAEhC,IAAIC,EAAY,EAEhB,MAAMxrF,EAASurF,EAAQvrF,OAEvB,IAAK,IAAIjK,EAAI,EAAGA,EAAIiK,EAAQjK,IACpBy1F,EAAYD,EAAQx1F,KACpBy1F,EAAYD,EAAQx1F,IAI5B,OAAO01F,aAAaD,EAAY,KAAO,KAAKE,QAAQ,IA+DzBC,CAA2BtxD,GAM9C5wB,KAAK2oC,WA5DjB,SAAsBw5C,EAAUC,GAC5B,IAAI70F,EAAQ,EACZ,MAAM80F,EAAOD,EAAYD,EAUzB,OAPI50F,EADA80F,EAAO,GACCD,EAAY,GACbC,GAAQ,GACPD,EAAY,GAEZD,EAGLH,WAAWz0F,EAAM00F,QAAQ,IAgDNK,CAAa35C,EAAY3oC,KAAK2oC,YAChD3oC,KAAKwL,SAASxL,KAAK2oC,aAEvB3oC,KAAKohF,gBAObH,EAAoB/yF,UAAU01B,KAAO,WAC7B5jB,KAAKmhF,aACL7mD,cAAct6B,KAAKmhF,YACnBnhF,KAAKmhF,WAAa,OAU1BF,EAAoBK,sBAAwB,WACxC,OAAO3oD,QAAQsV,K,6BChJnB,kCAAO,MAAM9tB,EAAuB,uD,8BCApC,mHAOA,MAAMlC,EAASb,EAAQ,GAAqBW,UAAUM,GAKhDkkE,EAAsB,CAAE,WAAY,OAAQ,OAAQ,QACpDC,EAAkC,CAAE,WAAY,OAAQ,QAGjDC,EAAiC,2BAO9C,SAASC,EAAkBhrF,EAAGC,GAC1B,IAAIsmC,EAAM,EAMV,OAJAukD,EAAgCG,KAAK90F,GACiC,KAAjEowC,EAAQvmC,EAAE7J,GAAO8J,EAAE9J,GAAS,EAAQ6J,EAAE7J,GAAO8J,EAAE9J,KAAU,IAGvDowC,EAUX,SAAS2kD,EAAY5nC,EAAYlyC,GAC7B,MAAM+5E,EAAmB7nC,EAAW15C,KAAKohF,GAAmBh9E,OACxD,CAACo9E,EAAkB5nC,IACfqnC,EAAoB78E,OAChB,CAAC0qE,EAAKviF,EAAK+hD,IACPwgC,GACe,IAARxgC,EAAY,GAAK,MACjBsL,EAASrtD,GAAOqtD,EAASrtD,GAAO,IAC3C,IANyB,IAO5B,IACHk1F,EAAiBj6E,EAASxH,OAAOoE,OACnC,CAAC0qE,EAAK4S,IAAe5S,EAAM4S,EAAR,IAAoB,IAE3C,OAAO1oF,mBAASuoF,EAAmBE,GAMxB,MAAMrpC,UAAatf,IAO9BnkC,YAAYse,EAAa,GAAIrT,EAAO,8BAIhC,GAHAm5B,QACAr6B,KAAKkB,KAAOA,EACZlB,KAAKoyD,MAAQ79C,EAAW69C,OACnBpyD,KAAKoyD,MACN,MAAM,IAAI/6D,MACN,uDAIR2I,KAAKijF,sBAAwBj2F,OAAOY,OAAO,MAC3CoS,KAAKkjF,aAAel2F,OAAOY,OAAO,MAClCoS,KAAK+zC,QAAU,GACf/zC,KAAKmjF,MAAQ,IAAIriE,IAEjB,MAAMs8B,EAAO7oC,EAAW6oC,KAExBA,EAAKxZ,YAAY+W,IAAWrrD,gBACxB6vD,GAAQn/C,KAAKojF,aAAajkC,IAC9B/B,EAAKxZ,YAAY+W,IAAWprD,kBACxB4vD,GAAQn/C,KAAKqjF,gBAAgBlkC,IACjCnyD,OAAOuY,KAAK63C,EAAK+lC,OAAO/zE,QAAQzL,IAC5B3D,KAAKojF,aAAahmC,EAAK+lC,MAAMx/E,MAGjClI,UAAQ+D,aAAa,OAAQ,mCAC7BQ,KAAKoyD,MAAMtY,WAAWr+C,UAAQK,GAAGwnF,MACjC/uE,EAAWxG,WAAW/N,KAAKujF,YAAYz1F,KAAKkS,MAAOvE,UAAQK,GAAGwnF,MAE9DtjF,KAAKwjF,iBAAmBxjF,KAAKyjF,yBAAyB31F,KAAKkS,MAU/D85C,WAAWkpC,EAASU,GAAS,GACzB1jF,KAAKoyD,MAAMtY,WAAWkpC,GACtBhjF,KAAK2jF,mBACDD,GACA1jF,KAAK0jF,SAWbE,cAAcZ,EAASU,GAAS,GAC5B1jF,KAAKoyD,MAAMwxB,cAAcZ,GACzBhjF,KAAK2jF,mBACDD,GACA1jF,KAAK0jF,SAObA,SACI1jF,KAAKmjF,MAAM/zE,QAAQ+vC,GAAQA,EAAKzxC,gBASpCm2E,YAAYlgF,EAAKiK,EAAU,KACvB,MAAMtG,EACA3D,KAAO3D,KAAKkjF,aAAeljF,KAAKkjF,aAAav/E,GAAO,KAE1D,IAAK2D,KAAUA,EAAKysC,WAAW/zC,KAAKijF,uBAAwB,CACxD,MAAM/hF,EAAOoG,EAAQ,GAAEA,EAAKpG,QAAQoG,EAAKysC,UAAY,KAErD,OAAO/zC,KAAK8jF,cAAcngF,EAAKzC,EAAM0M,GAChC8sB,KAAK,EAAG5xB,WAAUkyC,iBACf,GAAI1zC,EAAM,CACN,MAAMy8E,EAAMnB,EACRxsF,MAAMO,KAAKqkD,GACX5kD,MAAMO,KAAKmS,IAETk7E,EAAgB,GAAE18E,EAAKpG,QAAQ6iF,IAErC,OAAIC,IAAiB9iF,GACjBlB,KAAKijF,sBAAsBe,GAAgBl7E,EAEpCA,GAIP9I,KAAKijF,sBAAsBe,GACpBhkF,KAAKijF,sBAAsBe,IAGtC/lE,EAAOjZ,MAAO,iBAAgB9D,kBAC1B8iF,KAEGhnD,QAAQE,OAAOulD,IAG1B,OAAO35E,IAInB,OAAOk0B,QAAQC,QAAQj9B,KAAKijF,sBAAsB37E,EAAKysC,UAS3DgH,yBAAyBp3C,EAAKzC,EAAM0M,EAAU,KAC1C,OAAO5N,KAAK8jF,cAAcngF,EAAKzC,EAAM0M,GAWzCk2E,cAAcngF,EAAKzC,EAAM0M,GACrB,OAAO,IAAIovB,QAAQ,CAACC,EAASC,IACzBl9B,KAAKoyD,MAAMltD,KAAKvB,EAAKzC,EAAMyR,IACvB,MAAM7J,EAAW,IAAIgY,IACfk6B,EAAa,IAAIl6B,IAEvB2xB,EAAE9/B,GACGse,KAAK,kBACLghB,KACG,CAACC,EAAGhyC,IAAO4I,EAASyX,IAAIrgB,EAAG0C,aAAa,SAChD6vC,EAAE9/B,GACGse,KAAK,mBACLghB,KACG,CAACC,EAAGhyC,IAAO86C,EAAWz6B,IAAI,CACtBtZ,KAAM/G,EAAG0C,aAAa,QACtB/V,KAAMqT,EAAG0C,aAAa,QACtBqhF,SAAU/jF,EAAG0C,aAAa,eAEtCq6B,EAAQ,CACJn0B,WACAkyC,gBACL9d,EAAQtvB,IASnBw1E,aAAajkC,GACTn/C,KAAKmjF,MAAM5iE,IAAI4+B,GACfA,EAAKvb,YAAY+W,IAAWnqD,gBAAiBwP,KAAKwjF,kBAClDxjF,KAAKkkF,wBAAwB/kC,GAQjCkkC,gBAAgBlkC,GACZn/C,KAAKmjF,MAAMz/D,OAAOy7B,GAClBA,EAAKr9B,eAAe64B,IAAWnqD,gBAAiBwP,KAAKwjF,kBAOzDU,wBAAwB/kC,GACpBA,EAAKglC,cAAc,IAAK,CACpBlnF,WAAY,CACRgJ,MAAOxK,UAAQK,GAAGwnF,KAClBzqF,KA/OH,QAgPGqI,KAAMlB,KAAKkB,KACX+a,IAAKjc,KAAK+zC,WAQtBqwC,wBAEIpkF,KAAKmjF,MAAM/zE,QAAQ+vC,GAAQn/C,KAAKkkF,wBAAwB/kC,IAM5DwkC,mBACI3jF,KAAK+zC,QACC6uC,EAAY5iF,KAAKoyD,MAAMiyB,YAAarkF,KAAKoyD,MAAMkyB,WAErDtkF,KAAKokF,wBAOTb,YAAYv1E,GACR,MAAMrX,EAAOqX,EAAOpL,aAAa,QAC3B62C,EAAOzrC,EAAOiJ,cAAc,KAC5B88B,EAAU0F,EAAK72C,aAAa,OAC5B1B,EAAOu4C,EAAK72C,aAAa,QACzB2hF,EAAavkF,KAAKkjF,aAAavsF,GAUrC,OARAqJ,KAAKkjF,aAAavsF,GAAQ,CAAEo9C,UACxB7yC,QACAqjF,GAAcA,EAAWxwC,UAAYA,GACrC/zC,KAAKwf,aAAa0D,KAAKy3B,IAAWvpD,4BAC9BuF,IAID,EAOX8sF,yBAAyB9/E,GACjBA,KAAO3D,KAAKkjF,qBACLljF,KAAKkjF,aAAav/E,O,gEClTrC,8EAKO,MAAM6gF,EAAK,KAOLC,EAAM,O,8BCZnB,wGAUA,MAAMxmE,EAASF,oBAAUM,GAKnBqmE,EAAoB,CACtB,WAAc,SACd,aAAgB,WAChB,YAAe,WAMJ,MAAMC,UAAmBllE,IAcpCxpB,YACQssB,EACAhB,EACA+c,EACAsmD,EACAC,EACArmD,GACJnE,QAGAr6B,KAAKq8B,iBAAmBr8B,KAAK4jC,YAC7B5jC,KAAKivC,oBAAsBjvC,KAAKyuC,IAAMzuC,KAAK8hB,eAM3C9hB,KAAK8kF,WAAa,GAClB9kF,KAAKuiB,WAAaA,EAClBviB,KAAK2oC,YAAc,EACnB3oC,KAAKiH,KAAO49E,EACZ7kF,KAAKs+B,MAAQA,EACbt+B,KAAKw+B,UAAYA,EACjBx+B,KAAKmJ,SAAW,IAAIoW,IASpBvf,KAAK+kF,UAAW,EAShB/kF,KAAKglF,uBAAyBJ,EAE9B5kF,KAAKilF,WAAW1jE,GAWpB2jE,+BAA+B3gF,GACvBub,IAAQsQ,YACRpwB,KAAKs+B,MAAM6mD,QAAU5gF,EAErBvE,KAAKuhB,OAAO6jE,WAAa7gF,EAUjC8gF,YAAYp+E,EAAM1C,GACd,GAAKmgF,EAAkBv2F,eAAe8Y,IAWtC,GANI1C,EACAvE,KAAKmJ,SAASiY,IAAIna,EAAM1C,GAExBvE,KAAKmJ,SAASua,OAAOzc,GAGrBjH,KAAKuhB,OACL,IAAK,MAAM+c,KAASt+B,KAAKuhB,OAAOue,YAC5BxB,EAAMomD,EAAkBz9E,IAAS1C,OAZrC0Z,EAAOjZ,MAAO,wBAAuBiC,GAqB7Cq+E,sBACI,GAAKtlF,KAAKuhB,OAAV,CAOA,IAAK,MAAMta,KAAQjH,KAAKmJ,SAAS5D,OAE7B,IAAK,MAAMgzD,KAAcv4D,KAAKuhB,OAAOuX,iBACjCy/B,EAAWmsB,EAAkBz9E,SAAS1G,EAG1CP,KAAKglF,wBACLhlF,KAAKklF,oCAA+B3kF,QAbpC0d,EAAO9Y,KACAnF,KAAF,sDAuBbilF,WAAW1jE,GACP,GAAIvhB,KAAKuhB,SAAWA,IAIpBvhB,KAAKuhB,OAASA,EAMVvhB,KAAKuhB,QAAQ,CACb,IAAK,MAAMta,KAAQjH,KAAKmJ,SAAS5D,OAC7BvF,KAAKqlF,YAAYp+E,EAAMjH,KAAKmJ,SAAShc,IAAI8Z,IAEzCjH,KAAKglF,wBACLhlF,KAAKklF,+BAA+BllF,KAAKglF,yBAQrD19C,UACI,OAAOtnC,KAAKiH,KAMhB4hC,eACI,OAAO7oC,KAAKsnC,YAAc/I,IAS9BgnD,qBACI,OAAOvlF,KAAKs+B,OAASt+B,KAAKs+B,MAAMxZ,MAMpC6lD,eACI,OAAO3qE,KAAKsnC,YAAc/I,IAQ9BhZ,UACI,MAAM,IAAIluB,MAAM,+BAQpBmuF,oBACI,OAAOxlF,KAAK6oC,gBAAkB7oC,KAAKulB,UAMvCkgE,oBACI,OAAOzlF,KAAKuhB,OAOhBmkE,cACI,OAAO1lF,KAAKuhB,OAASvhB,KAAKuhB,OAAOra,GAAK,KAO1Cy+E,WACI,OAAO3lF,KAAKs+B,MAOhBsnD,gBACI,OAAO5lF,KAAKs+B,MAAM5E,MAOtBmsD,aACI,OAAO7lF,KAAKs+B,MAAQt+B,KAAKs+B,MAAMp3B,GAAK,KAQxC4+E,gBACI,OAAI9lF,KAAK6oC,eACE,MAGJ7oC,KAAKw+B,UAAYx+B,KAAKw+B,UAAY,UAS7CunD,wBAAwBC,GAChBhmF,KAAKuiB,YAAcyjE,GACnBhmF,KAAKuiB,WAAW0jE,eAAejmF,KAAMgmF,GAc7C55E,OAAO45E,GACChmF,KAAKuhB,SACLvhB,KAAKimF,eAAeD,GACpB7rD,IAASa,kBAAkBgrD,EAAWhmF,KAAKuhB,SAE/CvhB,KAAK8kF,WAAW5hF,KAAK8iF,GACrBhmF,KAAK+lF,wBAAwBC,GAC7BhmF,KAAKkmF,mBAAmBF,GAW5BG,OAAOH,GACH,IAAK,IAAI5hE,EAAKpkB,KAAK8kF,WAAYx4F,EAAI83B,EAAG7tB,OAAS,EAAGjK,GAAK,IAAKA,EAAG,CAC3D,MAAMK,EAAIy3B,EAAG93B,GAER05F,IACDhmF,KAAKomF,eAAez5F,GACpBwtC,IAASa,kBAAkBruC,EAAG,OAE7Bq5F,GAAar5F,IAAMq5F,GACpB5hE,EAAGngB,OAAO3X,EAAG,GAIjB05F,IACAhmF,KAAKomF,eAAeJ,GACpB7rD,IAASa,kBAAkBgrD,EAAW,OAW9CC,eAAeD,IAWfI,eAAeJ,IAYfE,mBAAmBF,IASnBhjE,UAKI,OAJAhjB,KAAKyjB,qBAELzjB,KAAK+kF,UAAW,EAET/nD,QAAQC,UAOnBopD,mBAQA7pB,QACI,OAAIx8D,KAAKuhB,OACE4Y,IAASgB,YAAYn7B,KAAKuhB,QAG9B,KASX06C,WACI,YAAkC,IAAvBj8D,KAAKuhB,OAAO+kE,QACZtmF,KAAKuhB,OAAO+kE,OAc3B59C,cAAcC,EAAYrlB,GACtB,IAAIijE,EAAgB59C,EAMhB7oB,IAAQ0mE,8BAA0C,IAARljE,GAAuBtjB,KAAKs+D,YACtEioB,EAAgB,GAGhBvmF,KAAK2oC,aAAe49C,GACpBvmF,KAAK2oC,WAAa49C,EAClBvmF,KAAKkjB,KACD05C,4BACA2pB,EACAjjE,IAIuB,IAApBtjB,KAAK2oC,YACS,IAAlB49C,GACAvmF,KAAKulB,YACJvlB,KAAKulF,sBACTvlF,KAAKkjB,KACD05C,iBACA2pB,GAQZE,UACI,MAAMj+C,EAAWxoC,KAAK0lF,cAChBgB,EAAU1mF,KAAK6lF,aAErB,OAAOr9C,GAAYk+C,EAAW,GAAEl+C,KAAYk+C,IAAY,KAW5Dv9C,eAAe7U,GACX,OAAK6F,IAASwG,wBAAwB,UAOlC3gC,KAAK2qE,eACE3tC,QAAQC,UAIfD,QAAQ+K,IACJ/nC,KAAK8kF,WAAWt/E,IACZ+I,GACIA,EAAQwmB,UAAUT,GACb8I,MAAMp4B,IAOH,MANAiZ,EAAO9Y,KACH,+GAGAoJ,EACAvJ,GACEA,MAGrB01B,KAAK,KACF16B,KAAKkjB,KACD05C,6BACAtoC,KA5BL0I,QAAQE,OACX,IAAI7lC,MAAM,oD,qEC7d1B,+EAgBe,MAAMsvF,UAAwBlnE,IASzCxpB,YAAY2wF,EAAoBC,EAAcC,GAC1CzsD,QAKAr6B,KAAK+mF,oBAAsBH,EAK3B5mF,KAAKgnF,cAAgBH,EAKrB7mF,KAAKinF,YAAcH,EAKnB9mF,KAAKknF,eAAiB,IAAIC,aAAa,IAKvCnnF,KAAKonF,cAAgBC,YAAmB,CAAEC,WAAYT,EAAaU,4BAMnEvnF,KAAKwnF,eAAiBX,EAAaY,kBAMnCznF,KAAK0nF,gBAAkB1nF,KAAK0nF,gBAAgB55F,KAAKkS,MAEjDA,KAAK2nF,0BAeT,cAAc9wD,EAAa+vD,EAAoBC,GAC3C,OAAO/jD,IAAIlF,+BAA+B,CACtCjD,QAAS,CAAE,SACX9D,gBACD6D,KAAKwJ,IAEJ,IAAKA,EAAW,GACZ,MAAM,IAAI7sC,MAAO,qDAAoDw/B,GAGzE,OAAO,IAAI8vD,EAAgBC,EAAoBC,EAAc3iD,EAAW,MAYhFyjD,0BACI3nF,KAAK4nF,aAAe5nF,KAAKonF,cAAczF,wBAAwB3hF,KAAKinF,YAAY1lE,QAQhFvhB,KAAK6nF,qBAAuB7nF,KAAKonF,cAAcU,sBAAsB9nF,KAAK+mF,oBAAqB,EAAG,GActGW,gBAAgBK,GAEZ,MAAMC,EAASD,EAAWE,YAAYC,eAAe,GAC/CC,EAAiB,IAAKnoF,KAAKknF,kBAAmBc,GAC9CI,EAAkBjgF,KAAKgM,MAE7B,IAAI7nB,EAAI,EAER,KAAOA,EAAI0T,KAAKwnF,eAAiBW,EAAe5xF,OAAQjK,GAAK0T,KAAKwnF,eAAgB,CAC9E,MAAMa,EAAYF,EAAejuD,MAAM5tC,EAAGA,EAAI0T,KAAKwnF,gBAG7Cc,EAAWtoF,KAAKgnF,cAAcuB,uBAAuBF,EAAUnuD,SAErEl6B,KAAKkjB,KAAK0zB,sBAAqB,CAC3B4xC,UAAWJ,EACXK,MAAOH,EACPI,QAASL,EACT1yD,SAAU31B,KAAKinF,YAAY0B,gBAInC3oF,KAAKknF,eAAiBiB,EAAejuD,MAAM5tC,EAAG67F,EAAe5xF,QAQjEqyF,qBACI5oF,KAAK6nF,qBAAqBgB,eAAiB7oF,KAAK0nF,gBAChD1nF,KAAK4nF,aAAan8E,QAAQzL,KAAK6nF,sBAC/B7nF,KAAK6nF,qBAAqBp8E,QAAQzL,KAAKonF,cAAc0B,aAQzDC,wBAII/oF,KAAK6nF,qBAAqBgB,eAAiB,OAC3C7oF,KAAK6nF,qBAAqBt4E,aAC1BvP,KAAK4nF,aAAar4E,aAQtBy5E,oBACIhpF,KAAK+oF,wBACL/oF,KAAKinF,YAAYgC,aAQrBN,cACI,OAAO3oF,KAAKinF,YAAY0B,cAS5B/C,gBACI,OAAO5lF,KAAKinF,YAAYiC,iBAQ5BnsE,QACI/c,KAAK4oF,qBAQThlE,OACI5jB,KAAK+oF,wBACL/oF,KAAKknF,eAAiB,GAQ1BrjD,UACQ7jC,KAAKmpF,aAITnpF,KAAKgpF,oBACLhpF,KAAKmpF,YAAa,M,6BCrOnB,SAAS9B,EAAmBlgF,GAC/B,MAAMiiF,EAAmBl9F,OAAO60F,cAAgB70F,OAAO80F,mBAEvD,GAAKoI,EAIL,OAAO,IAAIA,EAAiBjiF,GAZhC,mC,6BCAA,uDAGO,MAAMkiF,EAAkB,2B,6BCF/B,kCAOe,MAAMnP,EAIjBjkF,cACI+J,KAAK+hC,QAAU,IAAI/E,QAAQ,CAACC,EAASC,KACjCl9B,KAAKi9B,QAAU,IAAIkF,KACfniC,KAAKspF,qBACLrsD,KAAWkF,IAEfniC,KAAKk9B,OAAS,IAAIiF,KACdniC,KAAKspF,qBACLpsD,KAAUiF,MAGlBniC,KAAK06B,KAAO16B,KAAK+hC,QAAQrH,KAAK5sC,KAAKkS,KAAK+hC,SACxC/hC,KAAKo9B,MAAQp9B,KAAK+hC,QAAQ3E,MAAMtvC,KAAKkS,KAAK+hC,SAM9CunD,qBACI77E,aAAazN,KAAKupF,UAMtBrO,iBAAiBsO,GACbxpF,KAAKupF,SAAWh/E,WAAW,KACvBvK,KAAKk9B,OAAO,IAAI7lC,MAAM,aACvBmyF,M,8BCxCX,0IAOA,MAAMvrE,EAASF,oBAAUM,GAKZorE,EAAiB,CAJN,IACA,IACA,KAQjB,MAAMC,EAQTzzF,YAAY+qB,EAAgB2oE,GACxB3pF,KAAKslD,GAAKtkC,EACVhhB,KAAK2pF,cAAgBA,EAerB3pF,KAAK4pF,2BAA6B,CAC9B,CACItD,QAAQ,EACRpiB,WAAYpkD,IAAQsQ,YAAcpwB,KAAK2pF,cAAcE,KAAO7pF,KAAK2pF,cAAcG,IAC/Ex9E,IAvCQ,IAwCRy9E,sBAAuBjqE,IAAQsQ,YAAc,EAAM,GAEvD,CACIk2D,QAAQ,EACRpiB,WAAYlkE,KAAK2pF,cAAcK,SAC/B19E,IA5CQ,IA6CRy9E,sBAAuB,GAE3B,CACIzD,QAAQ,EACRpiB,WAAYpkD,IAAQsQ,YAAcpwB,KAAK2pF,cAAcG,IAAM9pF,KAAK2pF,cAAcE,KAC9Ev9E,IAjDQ,IAkDRy9E,sBAAuBjqE,IAAQsQ,YAAc,EAAM,IAa/D65D,0BAA0BnnB,GACtB,MAAMonB,EAAY9mC,IAAUlrC,MAAM4qD,EAAY9wC,KAmB9C,OAjBAk4D,EAAUz8D,MAAMre,QAAQqiB,IACpB,GAAmB,UAAfA,EAAMxqB,KACN,OAEJ,IAAKwqB,EAAMX,aAAeW,EAAMX,WAAWv6B,OACvC,OAEJ,IAAI4zF,EAAiB,GAErB14D,EAAMX,WAAW,GAAGN,MAAMztB,MAAM,KAAKqM,QAAQ4V,IACzC,MAAMolE,EAAU34D,EAAMjB,MAAME,OAAO3F,GAAUA,EAAO7jB,GAAGxQ,aAAesuB,GAEtEmlE,EAAiBA,EAAenwF,OAAOowF,KAE3C34D,EAAMjB,MAAQ25D,IAGX,IAAIxU,sBAAsB,CAC7B1uE,KAAM67D,EAAY77D,KAClB+qB,IAAKoxB,IAAUrL,MAAMmyC,KAS7BG,oBAAoBnmD,GAChB,OAAIlkC,KAAKslD,GAAGglC,iBAAmBpmD,EAAWymC,eAC/B3qE,KAAK4pF,2BAGT1lD,EAAWymC,eACZ,CAAE,CACA2b,QAAQ,EACRpiB,WAAYlkE,KAAK2pF,cAAcE,OAEjC,CAAE,CAAEvD,QAAQ,IAatBiE,kCAAkCl7D,GAI9B,GAAIvP,IAAQ0qE,6BACR,OAAOn7D,EAEX,MAAM2C,EAAMoxB,IAAUlrC,MAAMmX,EAAK2C,KAC3B4d,EAAM5d,EAAIvE,MAAMgoD,UAAU3nD,GAAwB,UAAfA,EAAM7mB,MAE/C,GAAI+qB,EAAIvE,MAAMmiB,GAAKoB,OAAShf,EAAIvE,MAAMmiB,GAAK66C,cAAgBz4D,EAAIvE,MAAMmiB,GAAK86C,WAatE,OAVA14D,EAAIvE,MAAMre,QAAQ,CAAC0e,EAAOxhC,KACH,UAAfwhC,EAAM7mB,MAAoB3a,IAAMsjD,IAChC5d,EAAIvE,MAAMnhC,GAAG0kD,UAAOzwC,EACpByxB,EAAIvE,MAAMnhC,GAAGo+F,eAAYnqF,EAGzByxB,EAAIvE,MAAMnhC,GAAGm+F,kBAAelqF,KAI7B,IAAIo1E,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKoxB,IAAUrL,MAAM/lB,KAK7BA,EAAIvE,MAAMmiB,GAAKoB,KAAO,CAClB,CACI9pC,GAvJQ,IAwJRyoB,UAAW,QAEf,CACIzoB,GA1JQ,IA2JRyoB,UAAW,QAEf,CACIzoB,GA7JQ,IA8JRyoB,UAAW,SAOnB,MAAMg7D,EAAgB7qE,IAAQsQ,aAAetQ,IAAQ21B,qBAAqB,IACnE,QAAOg0C,EAAetmF,KAAK,KAC3B,YAAWsmF,EAAetmF,KAAK,KAOtC,OAJA6uB,EAAIvE,MAAMmiB,GAAK66C,aAAe,CAC1Bl9F,MAAOo9F,GAGJ,IAAIhV,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKoxB,IAAUrL,MAAM/lB,KAW7BmM,SAAS+F,EAAYknC,GACjB,MAAM9sC,EAAQ4F,EAAWyhD,WAEzB,GAAIva,EAAa,CAGb,MAAMwf,EAAkB,CACpBj7D,UAAW,WACXmO,QAAS,CAAEoG,EAAWuhD,qBACtBoF,cAAe,IAGd/qE,IAAQsQ,cACTw6D,EAAgBC,cAAgB7qF,KAAKqqF,oBAAoBnmD,IAE7DlkC,KAAKslD,GAAGtkC,eAAe8pE,eAAexsD,EAAOssD,QAK7C5qF,KAAKslD,GAAGtkC,eAAemd,SAASG,GASxCm5C,eAAevzC,GACX,MAAMvY,EAAYuY,EAAWoD,UACvBhJ,EAAQ4F,EAAWyhD,WAInBhgB,EAAc3lE,KAAKslD,GAAGtkC,eAAe+pE,kBACtC95D,KAAKzjC,GAAKA,EAAE49C,UAAY59C,EAAE49C,SAAS9M,OAAS9wC,EAAE49C,SAAS9M,MAAMlF,OAASzN,GAE3E,IAAKg6C,EACD,OAAO3oC,QAAQE,OAAO,IAAI7lC,MAAO,yBAAwBs0B,gBAM7D,GAJA1N,EAAOhZ,MAAO,UAASi/B,QAAiBlkC,KAAKslD,MAIf,aAA1BqgB,EAAYh2C,UAA0B,CAGtC,OAFeuU,EAAWuhD,qBAGtBzlF,KAAKslD,GAAGtkC,eAAegqE,UAAU9mD,EAAWuhD,qBAErCzlF,KAAKirF,aAAa/mD,GAAYxJ,KAAK,KACtC16B,KAAKslD,GAAGtiB,YAAY5hB,IAAI8iB,EAAWrB,MAAOqB,GAC1CyhC,EAAYh2C,UAAY,cAIzBqN,QAAQC,UAGnB,OAAO0oC,EAAY4E,OAAO2L,aAAa53C,GAW3C4sD,gCAAgChnD,GAE5B,GAAIpkB,IAAQC,gBACR,OAAO,KAGX,MAAMorE,EAA8B,GAC9Bj3D,EAASgQ,EAAWxB,cAAcxO,OAExC,IAAK,MAAMk3D,KAAYprF,KAAK4pF,2BACxBuB,EAA4BjoF,KAAKgxB,EAASk3D,EAASrB,uBAGvD,OAAOoB,EAQX3T,gBAAgBtzC,GACZ,MAAMvY,EAAYuY,EAAWoD,UACvBq+B,EAAc3lE,KAAKslD,GAAGtkC,eAAe+pE,kBACtC95D,KAAKzjC,GAAKA,EAAE+8E,QAAU/8E,EAAE+8E,OAAOjsC,OAAS9wC,EAAE+8E,OAAOjsC,MAAMp3B,KAAOg9B,EAAW2hD,cAE9E,OAAKlgB,GAIL1nD,EAAOhZ,MAAO,YAAWi/B,QAAiBlkC,KAAKslD,MAExCqgB,EAAY4E,OAAO2L,aAAa,OAL5Bl5C,QAAQE,OAAO,IAAI7lC,MAAO,yBAAwBs0B,gBAcjEuqD,aAAaC,EAAUC,GACnB,GAAID,GAAYC,EAAU,CACtB,MAAMzqD,EAAYyqD,EAAS9uC,UACrB/lB,EAAS60D,EAASqP,oBAMxB,IAAKlkE,EAID,OAHAvhB,KAAKslD,GAAGtiB,YAAYtf,OAAOyyD,EAAStzC,OACpC7iC,KAAKslD,GAAGtiB,YAAY5hB,IAAIg1D,EAASvzC,MAAOuzC,GAEjCp5C,QAAQC,UAEnB,MAAMqB,EAAQ3S,IAAc4S,IACtBhd,EAAOqX,iBAAiB,GACxBrX,EAAOuX,iBAAiB,GACxB6sC,EAAc3lE,KAAKslD,GAAGtkC,eAAe+pE,kBACtC95D,KAAKzjC,GAAKA,EAAE49C,SAAS9M,MAAMlF,OAASzN,IAAcn+B,EAAE69F,SAEzD,OAAK1lB,GAGL1nD,EAAOhZ,MAAO,aAAYkxE,UAAiBC,QAAep2E,KAAKslD,MAExDqgB,EAAY4E,OAAO2L,aAAa53C,GAClC5D,KAAK,KACF,MAAM1V,EAAOhlB,KAAKslD,GAAGgmC,WAAWn+F,IAAIgpF,EAAStzC,OAE7C7iC,KAAKslD,GAAGtiB,YAAYtf,OAAOyyD,EAAStzC,OACpC7iC,KAAKslD,GAAGgmC,WAAW5nE,OAAOyyD,EAAStzC,OACnC7iC,KAAKslD,GAAGimC,cAAgBvrF,KAAKslD,GAAGimC,cAAc76D,OAAOriC,GAAKA,IAAMkzB,GAChEvhB,KAAKslD,GAAGtiB,YAAY5hB,IAAIg1D,EAASvzC,MAAOuzC,GAExCp2E,KAAKslD,GAAGimC,cAAcroF,KAAKqe,GAC3BvhB,KAAKslD,GAAGgmC,WAAWlqE,IAAIg1D,EAASvzC,MAAO7d,GACvChlB,KAAKslD,GAAG9lC,aAAa0D,KAAK8V,IAAU9Q,yBAChCkuD,EACAp2E,KAAKslD,GAAGkmC,oBAAoBxmE,OAjB7BgY,QAAQE,OAAO,IAAI7lC,MAAM,yBAmBjC,GAAI8+E,IAAaC,EACpB,OAAOp2E,KAAKw3E,gBAAgBrB,GACvBz7C,KAAK,KACF16B,KAAKslD,GAAGtiB,YAAYtf,OAAOyyD,EAAStzC,OACpC7iC,KAAKslD,GAAGgmC,WAAW5nE,OAAOyyD,EAAStzC,SAExC,GAAIuzC,IAAaD,EAAU,CAC9B,MAAMnxD,EAAOhlB,KAAKslD,GAAGgmC,WAAWn+F,IAAIipF,EAASvzC,OAE7C,OAAO7iC,KAAKy3E,eAAerB,GACtB17C,KAAK,KACF16B,KAAKslD,GAAGtiB,YAAY5hB,IAAIg1D,EAASvzC,MAAOuzC,GACxCp2E,KAAKslD,GAAGgmC,WAAWlqE,IAAIg1D,EAASvzC,MAAO7d,MAcvDizD,uBAAuBqO,GACnBtmF,KAAK03E,uBAAuBn5C,IAAiB+nD,GASjD2E,aAAa3sD,GACT,MAAMqnC,EAAc3lE,KAAKslD,GAAGtkC,eAAe+pE,kBACtC95D,KAAKzjC,GAAKA,EAAE+8E,QAAU/8E,EAAE+8E,OAAOjsC,OAAS9wC,EAAE+8E,OAAOjsC,MAAMlF,OAASkF,EAAMgJ,WACrEs5B,EAAa+E,EAAY4E,OAAOkhB,gBAItC,OAFA7qB,EAAW8qB,UAAY1rF,KAAKqqF,oBAAoB/rD,GAEzCqnC,EAAY4E,OAAOohB,cAAc/qB,GAW5C8W,uBAAuB/rD,EAAW26D,GAC9B,MAAMsF,EAAe5rF,KAAKslD,GAAGtkC,eAAe+pE,kBACvCr6D,OAAOljC,GAAKA,EAAE49C,UAAY59C,EAAE49C,SAAS9M,OAAS9wC,EAAE49C,SAAS9M,MAAMlF,OAASzN,GACvEqX,EAAchjC,KAAKslD,GAAGpe,eAAevb,GAE3C1N,EAAO/Y,KAAM,GAAEohF,EAAS,WAAa,gBAAgB36D,uBAA+B3rB,KAAKslD,MACzFsmC,EAAax8E,QAAQ,CAACu2D,EAAa/1B,KAC3B02C,EAEY,IAAR12C,GAAa5M,EAAYzsC,OACzBovE,EAAYh2C,UAAY,WAExBg2C,EAAYh2C,UAAY,WAG5Bg2C,EAAYh2C,UAAY,aAcpCwoD,uBAAuBmO,GACnBtmF,KAAK03E,uBAAuBn5C,IAAiB+nD,O,oDCjTrDv6F,EAAOD,QA1HP,MAWImK,YAAYuvB,EAAQqmE,EAAaC,GAC7B9rF,KAAK+rF,QAAUvmE,EACfxlB,KAAKgsF,eAAeH,GACpB7rF,KAAKisF,cAAgBH,IAAgB,EACrC9rF,KAAKksF,oBAAmB,GACxBlsF,KAAKmsF,yBAA2B,EAChCnsF,KAAKosF,sBAAwB,EAC7BpsF,KAAKqsF,UAAW,EAQpBC,YACI,OAAOtsF,KAAK+rF,QAQhBQ,iBACI,OAAOvsF,KAAK6rF,YAShBG,eAAeQ,GACXxsF,KAAK6rF,YAAcW,EAQvBV,eACI,OAAO9rF,KAAKisF,cAQhBQ,oBACI,OAAOzsF,KAAKosF,sBAAwB,EAWxCF,mBAAmBQ,GACf,IAAK1sF,KAAKysF,qBAAuBC,EAC7B1sF,KAAKosF,sBAAwBjkF,KAAKgM,WAC/B,GAAInU,KAAKysF,sBAAwBC,EAAsB,CAC1D,MACMC,EADMxkF,KAAKgM,MACSnU,KAAKosF,sBAE/BpsF,KAAKmsF,0BAA4BQ,EACjC3sF,KAAKosF,sBAAwB,GASrCQ,8BACI,IAAIC,EAAQ7sF,KAAKmsF,yBAMjB,OAJInsF,KAAKysF,sBACLI,GAAS1kF,KAAKgM,MAAQnU,KAAKosF,uBAGxBS,EAQXC,UACI,OAAO9sF,KAAKqsF,SAQhBU,gBACI/sF,KAAKqsF,UAAW,EAChBrsF,KAAKksF,oBAAmB,M,cCjHhCngG,EAAOD,QAZsB,CASzBkhG,iBAAkB,oC,8CCTtB;;;;;;;;;IASA,WACE,aAEA,IAAItvF,MAAQ,wBACRuvF,OAA2B,iBAAX/gG,OAChBN,KAAOqhG,OAAS/gG,OAAS,GACzBN,KAAKshG,mBACPD,QAAS,GAEX,IAAIE,YAAcF,QAA0B,iBAATp3F,KAC/Bu3F,SAAWxhG,KAAKyhG,mBAAwC,iBAAZr5B,SAAwBA,QAAQ0sB,UAAY1sB,QAAQ0sB,SAASx/E,KACzGksF,QACFxhG,KAAOgK,OACEu3F,aACTvhG,KAAOiK,MAET,IAAIy3F,WAAa1hG,KAAK2hG,qBAAyC,iBAAXxhG,QAAuBA,OAAOD,QAC9E0hG,IAAsCxhG,yBACtCyhG,cAAgB7hG,KAAK8hG,wBAAiD,oBAAhB18B,YACtD28B,UAAY,mBAAmB5qF,MAAM,IACrC6qF,MAAQ,CAAC,IAAK,MAAO,SAAU,YAC/BC,MAAQ,CAAC,EAAG,EAAG,GAAI,IACnBC,aAAe,CAAC,MAAO,QAAS,SAAU,SAAU,cAAe,UACnEC,mBAAqB,mEAAmEhrF,MAAM,IAE9FirF,OAAS,GAAIC,QACjB,GAAIR,aAAc,CAChB,IAAIr/B,OAAS,IAAI4C,YAAY,IAC7Bi9B,QAAU,IAAIjjC,WAAWoD,QACzB4/B,OAAS,IAAIE,YAAY9/B,SAGvBxiE,KAAKyhG,mBAAsBj3F,MAAMC,UACnCD,MAAMC,QAAU,SAAUN,GACxB,MAA+C,mBAAxC/I,OAAOkB,UAAUwI,SAASjK,KAAKsJ,MAItC03F,eAAiB7hG,KAAKuiG,gCAAmCn9B,YAAYo9B,SACvEp9B,YAAYo9B,OAAS,SAAUr4F,GAC7B,MAAsB,iBAARA,GAAoBA,EAAIq4D,QAAUr4D,EAAIq4D,OAAOn4D,cAAgB+6D,cA6D/E,IAAIq9B,mBAAqB,SAAUC,GACjC,OAAO,SAAU7pF,GACf,OAAO,IAAI8pF,KAAI,GAAMC,OAAO/pF,GAAS6pF,OAwBrCG,aAAe,WACjB,IAAIC,EAASL,mBAAmB,OAC5BjB,UACFsB,EAASC,SAASD,IAEpBA,EAAO9gG,OAAS,WACd,OAAO,IAAI2gG,KAEbG,EAAOF,OAAS,SAAU/pF,GACxB,OAAOiqF,EAAO9gG,SAAS4gG,OAAO/pF,IAEhC,IAAK,IAAInY,EAAI,EAAGA,EAAIwhG,aAAav3F,SAAUjK,EAAG,CAC5C,IAAI2a,EAAO6mF,aAAaxhG,GACxBoiG,EAAOznF,GAAQonF,mBAAmBpnF,GAEpC,OAAOynF,GAGLC,SAAW,SAAUD,QACvB,IAAI/kB,OAASilB,KAAK,qBACd7jC,OAAS6jC,KAAK,4BACdC,WAAa,SAAUpqF,GACzB,GAAuB,iBAAZA,EACT,OAAOklE,OAAOmlB,WAAW,OAAON,OAAO/pF,EAAS,QAAQsqF,OAAO,OAE/D,GAAItqF,QACF,MAAM/G,MAKV,OAJa+G,EAAQxO,cAAgB+6D,cACjCvsD,EAAU,IAAIumD,WAAWvmD,IAGzBrO,MAAMC,QAAQoO,IAAYusD,YAAYo9B,OAAO3pF,IAC/CA,EAAQxO,cAAgB80D,OACjB4e,OAAOmlB,WAAW,OAAON,OAAO,IAAIzjC,OAAOtmD,IAAUsqF,OAAO,OAE5DL,OAAOjqF,IAGlB,OAAOoqF,YAST,SAASN,IAAIS,GACX,GAAIA,EACFhB,OAAO,GAAKA,OAAO,IAAMA,OAAO,GAAKA,OAAO,GAAKA,OAAO,GACxDA,OAAO,GAAKA,OAAO,GAAKA,OAAO,GAAKA,OAAO,GAC3CA,OAAO,GAAKA,OAAO,GAAKA,OAAO,IAAMA,OAAO,IAC5CA,OAAO,IAAMA,OAAO,IAAMA,OAAO,IAAMA,OAAO,IAAM,EACpDhuF,KAAKguF,OAASA,OACdhuF,KAAKiuF,QAAUA,aAEf,GAAIR,aAAc,CAChB,IAAIr/B,EAAS,IAAI4C,YAAY,IAC7BhxD,KAAKiuF,QAAU,IAAIjjC,WAAWoD,GAC9BpuD,KAAKguF,OAAS,IAAIE,YAAY9/B,QAE9BpuD,KAAKguF,OAAS,CAAC,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,EAAG,GAGnEhuF,KAAKivF,GAAKjvF,KAAKkvF,GAAKlvF,KAAKmvF,GAAKnvF,KAAKovF,GAAKpvF,KAAK+c,MAAQ/c,KAAKqvF,MAAQrvF,KAAKsvF,OAAS,EAChFtvF,KAAKuvF,UAAYvvF,KAAKwvF,QAAS,EAC/BxvF,KAAKyvF,OAAQ,EAYflB,IAAIrgG,UAAUsgG,OAAS,SAAU/pF,GAC/B,IAAIzE,KAAKuvF,UAAT,CAIA,IAAIG,EAAWzoF,SAAcxC,EAC7B,GAAa,WAATwC,EAAmB,CACrB,GAAa,WAATA,EAWF,MAAMvJ,MAVN,GAAgB,OAAZ+G,EACF,MAAM/G,MACD,GAAI+vF,cAAgBhpF,EAAQxO,cAAgB+6D,YACjDvsD,EAAU,IAAIumD,WAAWvmD,QACpB,KAAKrO,MAAMC,QAAQoO,IACnBgpF,cAAiBz8B,YAAYo9B,OAAO3pF,IACvC,MAAM/G,MAMZgyF,GAAY,EAKd,IAHA,IAAI/yE,EAAiBrwB,EAAXqkC,EAAQ,EAAMp6B,EAASkO,EAAQlO,OAAQy3F,EAAShuF,KAAKguF,OAC3DC,EAAUjuF,KAAKiuF,QAEZt9D,EAAQp6B,GAAQ,CAUrB,GATIyJ,KAAKwvF,SACPxvF,KAAKwvF,QAAS,EACdxB,EAAO,GAAKA,EAAO,IACnBA,EAAO,IAAMA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAC5CA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAC3CA,EAAO,GAAKA,EAAO,GAAKA,EAAO,IAAMA,EAAO,IAC5CA,EAAO,IAAMA,EAAO,IAAMA,EAAO,IAAMA,EAAO,IAAM,GAGlD0B,EACF,GAAIjC,aACF,IAAKnhG,EAAI0T,KAAK+c,MAAO4T,EAAQp6B,GAAUjK,EAAI,KAAMqkC,EAC/Cs9D,EAAQ3hG,KAAOmY,EAAQksB,QAGzB,IAAKrkC,EAAI0T,KAAK+c,MAAO4T,EAAQp6B,GAAUjK,EAAI,KAAMqkC,EAC/Cq9D,EAAO1hG,GAAK,IAAMmY,EAAQksB,IAAUk9D,MAAY,EAANvhG,UAI9C,GAAImhG,aACF,IAAKnhG,EAAI0T,KAAK+c,MAAO4T,EAAQp6B,GAAUjK,EAAI,KAAMqkC,GAC/ChU,EAAOlY,EAAQlN,WAAWo5B,IACf,IACTs9D,EAAQ3hG,KAAOqwB,EACNA,EAAO,MAChBsxE,EAAQ3hG,KAAO,IAAQqwB,GAAQ,EAC/BsxE,EAAQ3hG,KAAO,IAAe,GAAPqwB,GACdA,EAAO,OAAUA,GAAQ,OAClCsxE,EAAQ3hG,KAAO,IAAQqwB,GAAQ,GAC/BsxE,EAAQ3hG,KAAO,IAASqwB,GAAQ,EAAK,GACrCsxE,EAAQ3hG,KAAO,IAAe,GAAPqwB,IAEvBA,EAAO,QAAoB,KAAPA,IAAiB,GAAqC,KAA9BlY,EAAQlN,aAAao5B,IACjEs9D,EAAQ3hG,KAAO,IAAQqwB,GAAQ,GAC/BsxE,EAAQ3hG,KAAO,IAASqwB,GAAQ,GAAM,GACtCsxE,EAAQ3hG,KAAO,IAASqwB,GAAQ,EAAK,GACrCsxE,EAAQ3hG,KAAO,IAAe,GAAPqwB,QAI3B,IAAKrwB,EAAI0T,KAAK+c,MAAO4T,EAAQp6B,GAAUjK,EAAI,KAAMqkC,GAC/ChU,EAAOlY,EAAQlN,WAAWo5B,IACf,IACTq9D,EAAO1hG,GAAK,IAAMqwB,GAAQkxE,MAAY,EAANvhG,KACvBqwB,EAAO,MAChBqxE,EAAO1hG,GAAK,KAAO,IAAQqwB,GAAQ,IAAOkxE,MAAY,EAANvhG,KAChD0hG,EAAO1hG,GAAK,KAAO,IAAe,GAAPqwB,IAAiBkxE,MAAY,EAANvhG,MACzCqwB,EAAO,OAAUA,GAAQ,OAClCqxE,EAAO1hG,GAAK,KAAO,IAAQqwB,GAAQ,KAAQkxE,MAAY,EAANvhG,KACjD0hG,EAAO1hG,GAAK,KAAO,IAASqwB,GAAQ,EAAK,KAAUkxE,MAAY,EAANvhG,KACzD0hG,EAAO1hG,GAAK,KAAO,IAAe,GAAPqwB,IAAiBkxE,MAAY,EAANvhG,OAElDqwB,EAAO,QAAoB,KAAPA,IAAiB,GAAqC,KAA9BlY,EAAQlN,aAAao5B,IACjEq9D,EAAO1hG,GAAK,KAAO,IAAQqwB,GAAQ,KAAQkxE,MAAY,EAANvhG,KACjD0hG,EAAO1hG,GAAK,KAAO,IAASqwB,GAAQ,GAAM,KAAUkxE,MAAY,EAANvhG,KAC1D0hG,EAAO1hG,GAAK,KAAO,IAASqwB,GAAQ,EAAK,KAAUkxE,MAAY,EAANvhG,KACzD0hG,EAAO1hG,GAAK,KAAO,IAAe,GAAPqwB,IAAiBkxE,MAAY,EAANvhG,MAK1D0T,KAAK2vF,cAAgBrjG,EACrB0T,KAAKqvF,OAAS/iG,EAAI0T,KAAK+c,MACnBzwB,GAAK,IACP0T,KAAK+c,MAAQzwB,EAAI,GACjB0T,KAAKnH,OACLmH,KAAKwvF,QAAS,GAEdxvF,KAAK+c,MAAQzwB,EAOjB,OAJI0T,KAAKqvF,MAAQ,aACfrvF,KAAKsvF,QAAUtvF,KAAKqvF,MAAQ,YAAc,EAC1CrvF,KAAKqvF,MAAQrvF,KAAKqvF,MAAQ,YAErBrvF,OAGTuuF,IAAIrgG,UAAU0hG,SAAW,WACvB,IAAI5vF,KAAKuvF,UAAT,CAGAvvF,KAAKuvF,WAAY,EACjB,IAAIvB,EAAShuF,KAAKguF,OAAQ1hG,EAAI0T,KAAK2vF,cACnC3B,EAAO1hG,GAAK,IAAMshG,MAAU,EAAJthG,GACpBA,GAAK,KACF0T,KAAKwvF,QACRxvF,KAAKnH,OAEPm1F,EAAO,GAAKA,EAAO,IACnBA,EAAO,IAAMA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAC5CA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAAKA,EAAO,GAC3CA,EAAO,GAAKA,EAAO,GAAKA,EAAO,IAAMA,EAAO,IAC5CA,EAAO,IAAMA,EAAO,IAAMA,EAAO,IAAMA,EAAO,IAAM,GAEtDA,EAAO,IAAMhuF,KAAKqvF,OAAS,EAC3BrB,EAAO,IAAMhuF,KAAKsvF,QAAU,EAAItvF,KAAKqvF,QAAU,GAC/CrvF,KAAKnH,SAGP01F,IAAIrgG,UAAU2K,KAAO,WACnB,IAAInB,EAAGC,EAAGhL,EAAGC,EAAGijG,EAAIC,EAAI9B,EAAShuF,KAAKguF,OAElChuF,KAAKyvF,MAQP93F,IADAA,IALAD,IADAA,EAAIs2F,EAAO,GAAK,YACN,EAAIt2F,IAAM,IAAM,WAAa,IAIvC/K,IADAA,IAAM,WADNC,IADAA,IAAM,WAAiB,WAAJ8K,GAAkBs2F,EAAO,GAAK,YACvC,GAAKphG,IAAM,IAAM8K,GAAK,KACH,UAALA,IAAoBs2F,EAAO,GAAK,aAC9C,GAAKrhG,IAAM,IAAMC,GAAK,IACjBA,EAAI8K,IAAOs2F,EAAO,GAAK,aAC5B,GAAKr2F,IAAM,IAAMhL,GAAK,GAEhC+K,EAAIsI,KAAKivF,GACTt3F,EAAIqI,KAAKkvF,GACTviG,EAAIqT,KAAKmvF,GASTx3F,IADAA,KALAD,IADAA,KADA9K,EAAIoT,KAAKovF,IACEz3F,GAAKhL,EAAIC,IAAOohG,EAAO,GAAK,YAC7B,EAAIt2F,IAAM,IAAMC,GAAK,IAI/BhL,IADAA,IAAMgL,GADN/K,IADAA,IAAMD,EAAK+K,GAAKC,EAAIhL,IAAOqhG,EAAO,GAAK,YAC7B,GAAKphG,IAAM,IAAM8K,GAAK,IAChBA,EAAIC,IAAOq2F,EAAO,GAAK,YAC7B,GAAKrhG,IAAM,IAAMC,GAAK,IAChBA,EAAI8K,IAAOs2F,EAAO,GAAK,aAC7B,GAAKr2F,IAAM,IAAMhL,GAAK,GAUlCgL,IADAA,KALAD,IADAA,IAAM9K,EAAK+K,GAAKhL,EAAIC,IAAOohG,EAAO,GAAK,YAC7B,EAAIt2F,IAAM,IAAMC,GAAK,IAI/BhL,IADAA,IAAMgL,GADN/K,IADAA,IAAMD,EAAK+K,GAAKC,EAAIhL,IAAOqhG,EAAO,GAAK,aAC7B,GAAKphG,IAAM,IAAM8K,GAAK,IAChBA,EAAIC,IAAOq2F,EAAO,GAAK,aAC7B,GAAKrhG,IAAM,IAAMC,GAAK,IAChBA,EAAI8K,IAAOs2F,EAAO,GAAK,WAC7B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KALAD,IADAA,IAAM9K,EAAK+K,GAAKhL,EAAIC,IAAOohG,EAAO,GAAK,aAC7B,EAAIt2F,IAAM,IAAMC,GAAK,IAI/BhL,IADAA,IAAMgL,GADN/K,IADAA,IAAMD,EAAK+K,GAAKC,EAAIhL,IAAOqhG,EAAO,GAAK,aAC7B,GAAKphG,IAAM,IAAM8K,GAAK,IAChBA,EAAIC,IAAOq2F,EAAO,IAAM,QAC9B,GAAKrhG,IAAM,IAAMC,GAAK,IAChBA,EAAI8K,IAAOs2F,EAAO,IAAM,aAC9B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KALAD,IADAA,IAAM9K,EAAK+K,GAAKhL,EAAIC,IAAOohG,EAAO,IAAM,aAC9B,EAAIt2F,IAAM,IAAMC,GAAK,IAI/BhL,IADAA,IAAMgL,GADN/K,IADAA,IAAMD,EAAK+K,GAAKC,EAAIhL,IAAOqhG,EAAO,IAAM,WAC9B,GAAKphG,IAAM,IAAM8K,GAAK,IAChBA,EAAIC,IAAOq2F,EAAO,IAAM,aAC9B,GAAKrhG,IAAM,IAAMC,GAAK,IAChBA,EAAI8K,IAAOs2F,EAAO,IAAM,aAC9B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,EAAKhL,IADX+K,IADAA,IAAM/K,EAAKC,GAAK+K,EAAIhL,IAAOqhG,EAAO,GAAK,YAC7B,EAAIt2F,IAAM,IAAMC,GAAK,GACXA,IAAOq2F,EAAO,GAAK,aAC7B,EAAIphG,IAAM,IAAM8K,GAAK,GAGpBA,IADX/K,IADAA,IAAM+K,EAAKC,GAAK/K,EAAI8K,IAAOs2F,EAAO,IAAM,YAC9B,GAAKrhG,IAAM,IAAMC,GAAK,GACZA,IAAOohG,EAAO,GAAK,YAC7B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,EAAKhL,IADX+K,IADAA,IAAM/K,EAAKC,GAAK+K,EAAIhL,IAAOqhG,EAAO,GAAK,YAC7B,EAAIt2F,IAAM,IAAMC,GAAK,GACXA,IAAOq2F,EAAO,IAAM,WAC9B,EAAIphG,IAAM,IAAM8K,GAAK,GAGpBA,IADX/K,IADAA,IAAM+K,EAAKC,GAAK/K,EAAI8K,IAAOs2F,EAAO,IAAM,YAC9B,GAAKrhG,IAAM,IAAMC,GAAK,GACZA,IAAOohG,EAAO,GAAK,YAC7B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,EAAKhL,IADX+K,IADAA,IAAM/K,EAAKC,GAAK+K,EAAIhL,IAAOqhG,EAAO,GAAK,YAC7B,EAAIt2F,IAAM,IAAMC,GAAK,GACXA,IAAOq2F,EAAO,IAAM,aAC9B,EAAIphG,IAAM,IAAM8K,GAAK,GAGpBA,IADX/K,IADAA,IAAM+K,EAAKC,GAAK/K,EAAI8K,IAAOs2F,EAAO,GAAK,YAC7B,GAAKrhG,IAAM,IAAMC,GAAK,GACZA,IAAOohG,EAAO,GAAK,aAC7B,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,EAAKhL,IADX+K,IADAA,IAAM/K,EAAKC,GAAK+K,EAAIhL,IAAOqhG,EAAO,IAAM,aAC9B,EAAIt2F,IAAM,IAAMC,GAAK,GACXA,IAAOq2F,EAAO,GAAK,WAC7B,EAAIphG,IAAM,IAAM8K,GAAK,GAGpBA,IADX/K,IADAA,IAAM+K,EAAKC,GAAK/K,EAAI8K,IAAOs2F,EAAO,GAAK,aAC7B,GAAKrhG,IAAM,IAAMC,GAAK,GACZA,IAAOohG,EAAO,IAAM,aAC9B,GAAKr2F,IAAM,IAAMhL,GAAK,EAUhCgL,IADAA,KAHAm4F,GADAljG,IADAA,KAHAijG,EAAKl4F,EAAIhL,IAET+K,IADAA,IAAMm4F,EAAKjjG,GAAKohG,EAAO,GAAK,SAClB,EAAIt2F,IAAM,IAAMC,GAAK,IACfq2F,EAAO,GAAK,aAClB,GAAKphG,IAAM,IAAM8K,GAAK,GACvBA,IAET/K,IADAA,IAAMmjG,EAAKn4F,GAAKq2F,EAAO,IAAM,aACnB,GAAKrhG,IAAM,IAAMC,GAAK,IAChBohG,EAAO,IAAM,WACnB,GAAKr2F,IAAM,GAAKhL,GAAK,EAU/BgL,IADAA,KAHAm4F,GADAljG,IADAA,KAHAijG,EAAKl4F,EAAIhL,IAET+K,IADAA,IAAMm4F,EAAKjjG,GAAKohG,EAAO,GAAK,aAClB,EAAIt2F,IAAM,IAAMC,GAAK,IACfq2F,EAAO,GAAK,aAClB,GAAKphG,IAAM,IAAM8K,GAAK,GACvBA,IAET/K,IADAA,IAAMmjG,EAAKn4F,GAAKq2F,EAAO,GAAK,YAClB,GAAKrhG,IAAM,IAAMC,GAAK,IAChBohG,EAAO,IAAM,aACnB,GAAKr2F,IAAM,GAAKhL,GAAK,EAU/BgL,IADAA,KAHAm4F,GADAljG,IADAA,KAHAijG,EAAKl4F,EAAIhL,IAET+K,IADAA,IAAMm4F,EAAKjjG,GAAKohG,EAAO,IAAM,YACnB,EAAIt2F,IAAM,IAAMC,GAAK,IACfq2F,EAAO,GAAK,YAClB,GAAKphG,IAAM,IAAM8K,GAAK,GACvBA,IAET/K,IADAA,IAAMmjG,EAAKn4F,GAAKq2F,EAAO,GAAK,YAClB,GAAKrhG,IAAM,IAAMC,GAAK,IAChBohG,EAAO,GAAK,WAClB,GAAKr2F,IAAM,GAAKhL,GAAK,EAU/BgL,IADAA,KAHAm4F,GADAljG,IADAA,KAHAijG,EAAKl4F,EAAIhL,IAET+K,IADAA,IAAMm4F,EAAKjjG,GAAKohG,EAAO,GAAK,YAClB,EAAIt2F,IAAM,IAAMC,GAAK,IACfq2F,EAAO,IAAM,YACnB,GAAKphG,IAAM,IAAM8K,GAAK,GACvBA,IAET/K,IADAA,IAAMmjG,EAAKn4F,GAAKq2F,EAAO,IAAM,YACnB,GAAKrhG,IAAM,IAAMC,GAAK,IAChBohG,EAAO,GAAK,YAClB,GAAKr2F,IAAM,GAAKhL,GAAK,EAQ/BgL,IADAA,KAHA/K,IADAA,IAAM+K,IADND,IADAA,IAAM/K,GAAKgL,GAAK/K,IAAMohG,EAAO,GAAK,YACxB,EAAIt2F,IAAM,IAAMC,GAAK,IACfhL,IAAMqhG,EAAO,GAAK,aACxB,GAAKphG,IAAM,IAAM8K,GAAK,KAEhC/K,IADAA,IAAM+K,GAAK9K,GAAK+K,IAAMq2F,EAAO,IAAM,aACzB,GAAKrhG,IAAM,IAAMC,GAAK,IAChB8K,IAAMs2F,EAAO,GAAK,WACxB,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,IADND,IADAA,IAAM/K,GAAKgL,GAAK/K,IAAMohG,EAAO,IAAM,aACzB,EAAIt2F,IAAM,IAAMC,GAAK,IACfhL,IAAMqhG,EAAO,GAAK,aACxB,GAAKphG,IAAM,IAAM8K,GAAK,KAEhC/K,IADAA,IAAM+K,GAAK9K,GAAK+K,IAAMq2F,EAAO,IAAM,UACzB,GAAKrhG,IAAM,IAAMC,GAAK,IAChB8K,IAAMs2F,EAAO,GAAK,aACxB,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,IADND,IADAA,IAAM/K,GAAKgL,GAAK/K,IAAMohG,EAAO,GAAK,aACxB,EAAIt2F,IAAM,IAAMC,GAAK,IACfhL,IAAMqhG,EAAO,IAAM,WACzB,GAAKphG,IAAM,IAAM8K,GAAK,KAEhC/K,IADAA,IAAM+K,GAAK9K,GAAK+K,IAAMq2F,EAAO,GAAK,aACxB,GAAKrhG,IAAM,IAAMC,GAAK,IAChB8K,IAAMs2F,EAAO,IAAM,aACzB,GAAKr2F,IAAM,IAAMhL,GAAK,EAQhCgL,IADAA,KAHA/K,IADAA,IAAM+K,IADND,IADAA,IAAM/K,GAAKgL,GAAK/K,IAAMohG,EAAO,GAAK,YACxB,EAAIt2F,IAAM,IAAMC,GAAK,IACfhL,IAAMqhG,EAAO,IAAM,aACzB,GAAKphG,IAAM,IAAM8K,GAAK,KAEhC/K,IADAA,IAAM+K,GAAK9K,GAAK+K,IAAMq2F,EAAO,GAAK,YACxB,GAAKrhG,IAAM,IAAMC,GAAK,IAChB8K,IAAMs2F,EAAO,GAAK,YACxB,GAAKr2F,IAAM,IAAMhL,GAAK,EAE5BqT,KAAKyvF,OACPzvF,KAAKivF,GAAKv3F,EAAI,YAAc,EAC5BsI,KAAKkvF,GAAKv3F,EAAI,WAAa,EAC3BqI,KAAKmvF,GAAKxiG,EAAI,YAAc,EAC5BqT,KAAKovF,GAAKxiG,EAAI,WAAa,EAC3BoT,KAAKyvF,OAAQ,IAEbzvF,KAAKivF,GAAKjvF,KAAKivF,GAAKv3F,GAAK,EACzBsI,KAAKkvF,GAAKlvF,KAAKkvF,GAAKv3F,GAAK,EACzBqI,KAAKmvF,GAAKnvF,KAAKmvF,GAAKxiG,GAAK,EACzBqT,KAAKovF,GAAKpvF,KAAKovF,GAAKxiG,GAAK,IAc7B2hG,IAAIrgG,UAAU6hG,IAAM,WAClB/vF,KAAK4vF,WAEL,IAAIX,EAAKjvF,KAAKivF,GAAIC,EAAKlvF,KAAKkvF,GAAIC,EAAKnvF,KAAKmvF,GAAIC,EAAKpvF,KAAKovF,GAExD,OAAOzB,UAAWsB,GAAM,EAAK,IAAQtB,UAAe,GAALsB,GAC7CtB,UAAWsB,GAAM,GAAM,IAAQtB,UAAWsB,GAAM,EAAK,IACrDtB,UAAWsB,GAAM,GAAM,IAAQtB,UAAWsB,GAAM,GAAM,IACtDtB,UAAWsB,GAAM,GAAM,IAAQtB,UAAWsB,GAAM,GAAM,IACtDtB,UAAWuB,GAAM,EAAK,IAAQvB,UAAe,GAALuB,GACxCvB,UAAWuB,GAAM,GAAM,IAAQvB,UAAWuB,GAAM,EAAK,IACrDvB,UAAWuB,GAAM,GAAM,IAAQvB,UAAWuB,GAAM,GAAM,IACtDvB,UAAWuB,GAAM,GAAM,IAAQvB,UAAWuB,GAAM,GAAM,IACtDvB,UAAWwB,GAAM,EAAK,IAAQxB,UAAe,GAALwB,GACxCxB,UAAWwB,GAAM,GAAM,IAAQxB,UAAWwB,GAAM,EAAK,IACrDxB,UAAWwB,GAAM,GAAM,IAAQxB,UAAWwB,GAAM,GAAM,IACtDxB,UAAWwB,GAAM,GAAM,IAAQxB,UAAWwB,GAAM,GAAM,IACtDxB,UAAWyB,GAAM,EAAK,IAAQzB,UAAe,GAALyB,GACxCzB,UAAWyB,GAAM,GAAM,IAAQzB,UAAWyB,GAAM,EAAK,IACrDzB,UAAWyB,GAAM,GAAM,IAAQzB,UAAWyB,GAAM,GAAM,IACtDzB,UAAWyB,GAAM,GAAM,IAAQzB,UAAWyB,GAAM,GAAM,KAa1Db,IAAIrgG,UAAUwI,SAAW63F,IAAIrgG,UAAU6hG,IAYvCxB,IAAIrgG,UAAU6gG,OAAS,WACrB/uF,KAAK4vF,WAEL,IAAIX,EAAKjvF,KAAKivF,GAAIC,EAAKlvF,KAAKkvF,GAAIC,EAAKnvF,KAAKmvF,GAAIC,EAAKpvF,KAAKovF,GACxD,MAAO,CACA,IAALH,EAAYA,GAAM,EAAK,IAAOA,GAAM,GAAM,IAAOA,GAAM,GAAM,IACxD,IAALC,EAAYA,GAAM,EAAK,IAAOA,GAAM,GAAM,IAAOA,GAAM,GAAM,IACxD,IAALC,EAAYA,GAAM,EAAK,IAAOA,GAAM,GAAM,IAAOA,GAAM,GAAM,IACxD,IAALC,EAAYA,GAAM,EAAK,IAAOA,GAAM,GAAM,IAAOA,GAAM,GAAM,MAcjEb,IAAIrgG,UAAU0iC,MAAQ29D,IAAIrgG,UAAU6gG,OAYpCR,IAAIrgG,UAAUsiE,YAAc,WAC1BxwD,KAAK4vF,WAEL,IAAIxhC,EAAS,IAAI4C,YAAY,IACzBg9B,EAAS,IAAIE,YAAY9/B,GAK7B,OAJA4/B,EAAO,GAAKhuF,KAAKivF,GACjBjB,EAAO,GAAKhuF,KAAKkvF,GACjBlB,EAAO,GAAKhuF,KAAKmvF,GACjBnB,EAAO,GAAKhuF,KAAKovF,GACVhhC,GAcTmgC,IAAIrgG,UAAUkgE,OAASmgC,IAAIrgG,UAAUsiE,YAYrC+9B,IAAIrgG,UAAU8hG,OAAS,WAErB,IADA,IAAIC,EAAIC,EAAIC,EAAIC,EAAY,GAAIf,EAAQrvF,KAAK4wB,QACpCtkC,EAAI,EAAGA,EAAI,IAClB2jG,EAAKZ,EAAM/iG,KACX4jG,EAAKb,EAAM/iG,KACX6jG,EAAKd,EAAM/iG,KACX8jG,GAAarC,mBAAmBkC,IAAO,GACrClC,mBAA0C,IAAtBkC,GAAM,EAAIC,IAAO,IACrCnC,mBAA0C,IAAtBmC,GAAM,EAAIC,IAAO,IACrCpC,mBAAwB,GAALoC,GAMvB,OAJAF,EAAKZ,EAAM/iG,GACX8jG,GAAarC,mBAAmBkC,IAAO,GACrClC,mBAAoBkC,GAAM,EAAK,IAC/B,MAIJ,IAAInkG,QAAU2iG,eAEVnB,UACFvhG,OAAOD,QAAUA,SAmBjBF,KAAKykG,IAAMvkG,QACP0hG,MACFxhG,yCACE,OAAOF,SADH,0IA5pBZ,K,6ECTA,YAUA,IASIwkG,EAAS,aAGTC,EAAa,qBAGbC,EAAa,aAGbC,EAAY,cAGZC,EAAep4E,SAGf6wC,EAA8B,iBAAVvzD,GAAsBA,GAAUA,EAAO5I,SAAWA,QAAU4I,EAGhFwzD,EAA0B,iBAARvzD,MAAoBA,MAAQA,KAAK7I,SAAWA,QAAU6I,KAGxEjK,EAAOu9D,GAAcC,GAAY/d,SAAS,cAATA,GAUjCuf,EAPc59D,OAAOkB,UAOQwI,SAG7Bi6F,EAAYvlF,KAAKgpB,IACjBw8D,EAAYxlF,KAAKqP,IAkBjBtG,EAAM,WACR,OAAOvoB,EAAKuc,KAAKgM,OA4MnB,SAAS05C,EAAStgE,GAChB,IAAI0Z,SAAc1Z,EAClB,QAASA,IAAkB,UAAR0Z,GAA4B,YAARA,GA4EzC,SAAS4pF,EAAStjG,GAChB,GAAoB,iBAATA,EACT,OAAOA,EAET,GAhCF,SAAkBA,GAChB,MAAuB,iBAATA,GAtBhB,SAAsBA,GACpB,QAASA,GAAyB,iBAATA,EAsBtBu/D,CAAav/D,IAzTF,mBAyTYq9D,EAAen+D,KAAKc,GA8B1CujG,CAASvjG,GACX,OA3VM,IA6VR,GAAIsgE,EAAStgE,GAAQ,CACnB,IAAIwjE,EAAgC,mBAAjBxjE,EAAM6+D,QAAwB7+D,EAAM6+D,UAAY7+D,EACnEA,EAAQsgE,EAASkD,GAAUA,EAAQ,GAAMA,EAE3C,GAAoB,iBAATxjE,EACT,OAAiB,IAAVA,EAAcA,GAASA,EAEhCA,EAAQA,EAAMqU,QAAQ0uF,EAAQ,IAC9B,IAAIS,EAAWP,EAAWt+E,KAAK3kB,GAC/B,OAAQwjG,GAAYN,EAAUv+E,KAAK3kB,GAC/BmjG,EAAanjG,EAAM2sC,MAAM,GAAI62D,EAAW,EAAI,GAC3CR,EAAWr+E,KAAK3kB,GAxWb,KAwW6BA,EAGvCxB,EAAOD,QAtPP,SAAkB8T,EAAM+L,EAAMxE,GAC5B,IAAI6pF,EACAC,EACAC,EACAzrF,EACA0rF,EACAC,EACAC,EAAiB,EACjBC,GAAU,EACVC,GAAS,EACTC,GAAW,EAEf,GAAmB,mBAAR5xF,EACT,MAAM,IAAI/I,UArIQ,uBA+IpB,SAAS46F,EAAWC,GAClB,IAAIvvD,EAAO6uD,EACPW,EAAUV,EAKd,OAHAD,EAAWC,OAAW1wF,EACtB8wF,EAAiBK,EACjBjsF,EAAS7F,EAAKgiC,MAAM+vD,EAASxvD,GAI/B,SAASyvD,EAAYF,GAMnB,OAJAL,EAAiBK,EAEjBP,EAAU5mF,WAAWsnF,EAAclmF,GAE5B2lF,EAAUG,EAAWC,GAAQjsF,EAWtC,SAASqsF,EAAaJ,GACpB,IAAIK,EAAoBL,EAAON,EAM/B,YAAyB7wF,IAAjB6wF,GAA+BW,GAAqBpmF,GACzDomF,EAAoB,GAAOR,GANJG,EAAOL,GAM8BH,EAGjE,SAASW,IACP,IAAIH,EAAOv9E,IACX,GAAI29E,EAAaJ,GACf,OAAOM,EAAaN,GAGtBP,EAAU5mF,WAAWsnF,EAzBvB,SAAuBH,GACrB,IAEIjsF,EAASkG,GAFW+lF,EAAON,GAI/B,OAAOG,EAASX,EAAUnrF,EAAQyrF,GAHRQ,EAAOL,IAGkC5rF,EAoBhCwsF,CAAcP,IAGnD,SAASM,EAAaN,GAKpB,OAJAP,OAAU5wF,EAINixF,GAAYR,EACPS,EAAWC,IAEpBV,EAAWC,OAAW1wF,EACfkF,GAeT,SAASysF,IACP,IAAIR,EAAOv9E,IACPg+E,EAAaL,EAAaJ,GAM9B,GAJAV,EAAW7vF,UACX8vF,EAAWjxF,KACXoxF,EAAeM,EAEXS,EAAY,CACd,QAAgB5xF,IAAZ4wF,EACF,OAAOS,EAAYR,GAErB,GAAIG,EAGF,OADAJ,EAAU5mF,WAAWsnF,EAAclmF,GAC5B8lF,EAAWL,GAMtB,YAHgB7wF,IAAZ4wF,IACFA,EAAU5mF,WAAWsnF,EAAclmF,IAE9BlG,EAIT,OAxGAkG,EAAOklF,EAASllF,IAAS,EACrBkiD,EAAS1mD,KACXmqF,IAAYnqF,EAAQmqF,QAEpBJ,GADAK,EAAS,YAAapqF,GACHwpF,EAAUE,EAAS1pF,EAAQ+pF,UAAY,EAAGvlF,GAAQulF,EACrEM,EAAW,aAAcrqF,IAAYA,EAAQqqF,SAAWA,GAiG1DU,EAAUv/B,OAnCV,gBACkBpyD,IAAZ4wF,GACF1jF,aAAa0jF,GAEfE,EAAiB,EACjBL,EAAWI,EAAeH,EAAWE,OAAU5wF,GA+BjD2xF,EAAU1kF,MA5BV,WACE,YAAmBjN,IAAZ4wF,EAAwB1rF,EAASusF,EAAa79E,MA4BhD+9E,K,+CCtMF/vF,eAAegoE,EAAQH,GAC1B,MAAMooB,EAAc,IAAIC,YAGxB,OAAO1oB,OAAO2oB,OAAOC,WAAW,CAC5B1lG,KAAM,OACNgoB,KAAMu9E,EAAYI,OAAO,oBACzB35F,KAAM,UACNqM,KAAM,IAAI8rD,aACXgZ,EAAU,KAUV7nE,eAAe8nE,EAAUwoB,GAE5B,OAAO9oB,OAAO2oB,OAAOroB,UAAU,MAAOwoB,EAAU,QAAQ,EAAO,CAAE,aAAc,cAxEnF,qE,8BCAA,+KA6BA,MAAMx0E,EAASF,oBAAUM,GAMV,MAAMukB,UAAwB+hD,IAoBzC1uF,aAAY,SACR0/B,EADQ,WAERE,EAFQ,UAGRlK,EAHQ,WAIR0K,EAJQ,MAKRwM,EALQ,SAMRjN,EANQ,WAORyI,EAPQ,OAQR9c,EARQ,MASR+c,EATQ,UAURE,EAVQ,QAWRkC,EAAU,KAEVrG,MACqB,KACjB9Y,EACA+c,EAC4B,IAAMt+B,KAAKkjB,KAAK6mB,uBAC5Cpe,EACA6S,GAEJx+B,KAAK0yF,sBAAuB,EAC5B,MAAMC,EAASjyD,EAAQzP,KAAK53B,GAAKA,EAAE+vE,UAAUppE,OAEzC2yF,GACA3yF,KAAK4yF,mBAAmBD,GAQ5B3yF,KAAK6iC,MAAQA,EACb7iC,KAAK41B,SAAWA,EAChB51B,KAAKq+B,WAAaA,EAEdve,IAAQ+a,kBAGR76B,KAAKq2B,WAAaiI,EAAMoE,cAAcxO,OACtCl0B,KAAK6yF,qBAAuBx8D,EAI5Br2B,KAAK8yF,aAAex0D,EAAMnJ,iBAGrBnoC,OAAOuY,KAAKvF,KAAK8yF,cAAcv8F,QAAUioC,IAAcC,IAAUE,SAClE3+B,KAAK8yF,aAAe,CAChB5+D,OAAQoK,EAAMoE,cAAcxO,OAC5BqC,MAAO+H,EAAMoE,cAAcnM,UAOnCv2B,KAAKq2B,WAAavW,IAAQsQ,YAAc,KAAOiG,EAC/Cr2B,KAAK6yF,qBAAuB7yF,KAAKq2B,YAGrCr2B,KAAK21B,SAAWA,EAUhB31B,KAAK+yF,cAAgB/1D,QAAQC,UAS7Bj9B,KAAKgzF,YAAcn9D,EAMnB71B,KAAKizF,aAAc,EAKnBjzF,KAAKkzF,cAAe,EAOpBlzF,KAAKmzF,eAAgB,EAQrBnzF,KAAKozF,cAAkC,KAAlBpzF,KAAK21B,cAAkBp1B,EAAYP,KAAK21B,SAE7D31B,KAAKqzF,cAAgB,EAErBrzF,KAAKszF,wBAA0B34D,IAC3B,MAAM44D,EAAkBvzF,KAAKozF,cAE7BpzF,KAAKwzF,+BAA+B74D,SAMO,IAA/B36B,KAAK2lF,WAAW3sE,iBACa,IAAvBhZ,KAAKozF,gBACXz4D,EAAQ1J,KAAKrkC,GAAKA,EAAE+oC,WAAa31B,KAAKozF,qBAOf,IAApBG,QAAiE,IAAvBvzF,KAAKozF,iBAE1DpzF,KAAKizF,aAAc,IAQvBjzF,KAAK6oC,gBAAkB1O,IAASwG,wBAAwB,YACxD3gC,KAAKyzF,4BAA8BzzF,KAAKmpC,eAAer7C,KAAKkS,MAC5Dm6B,IAASyJ,YACL5K,IAAUrQ,4BACV3oB,KAAKyzF,8BAGbt5D,IAASyJ,YAAY5K,IAAUnQ,wBAAyB7oB,KAAKszF,yBAE7DtzF,KAAK0zF,gCAQTC,UACI,OAAI3zF,KAAK2qE,gBAAkB3qE,KAAKs+D,UAGrBt+D,KAAKizF,YAGsB,UAA/BjzF,KAAK2lF,WAAW3sE,YAA0BhZ,KAAKizF,YAO1DS,gCACS1zF,KAAK4zF,qCAIV5zF,KAAKqlF,YAAY,aAAc,KAC3BrlF,KAAKqzF,cAAgBnnG,OAAOsuD,YAAYrmC,MACxCnU,KAAK6zF,+BAGT7zF,KAAKqlF,YAAY,eAAgB,KAC7BrlF,KAAK6zF,6BACLz0E,IAAWgI,oBACPkD,IACA,CACI,WAActqB,KAAKsnC,UACnB,WAAc,QACd/5C,MAAOrB,OAAOsuD,YAAYrmC,MAAQnU,KAAKqzF,kBAI/CrzF,KAAK2qE,gBAAkB3qE,KAAKw+B,YAAcC,IAAUE,QACpD3+B,KAAKqlF,YAAY,cAAe,KACvBrlF,KAAK8zF,mBACN9zF,KAAK6zF,gCAWrBD,mCAEI,OAAQ5zF,KAAK2qE,gBAAkB3qE,KAAKw+B,YAAcC,IAAUC,QAMhEm1D,6BACI,MAAMtmG,GAASyS,KAAK8zF,kBAEpB9zF,KAAKkjB,KAAKgnB,sBAAqB38C,GAG/B6xB,IAAWoI,cAAckE,YAA4B1rB,KAAKsnC,UAAW/5C,IACrE6xB,IAAWkH,QAAQ1Z,KAAKwL,UAAU,CAC9BvrB,KAAMq9C,sBACNtlC,IAAKrX,KAYbimG,+BAA+B74D,GAC3B,MAAM2D,EAAQt+B,KAAK2lF,WACbvsD,EAAUkF,EAAMlF,KAAR,QACd,IAAII,EAASmB,EAAQ1J,KAAKrkC,GAAKA,EAAEwsC,OAASA,GAAQxsC,EAAE8sC,QAAU4E,EAAM5E,OAEpE,IAAKF,GAAiC,YAAvBx5B,KAAKozF,cAA6B,CAI7C,MAAM15D,GAAS4E,EAAM5E,OAAS,IAAI93B,QAAQ,aAAc,IAExD43B,EAASmB,EAAQ1J,KAAKrkC,GAAKA,EAAEwsC,OAASA,GAAQxsC,EAAE8sC,QAAUA,GAI1D15B,KAAKozF,cADL55D,EACqBA,EAAO7D,cAEPp1B,EAW7B0kF,WAAW1jE,GACP8Y,MAAM4qD,WAAW1jE,GAEbA,GAEAvhB,KAAK+zF,WAAa/zF,KAAKymF,UACvBxoE,EAAOhZ,MAAO,qBAAoBjF,KAAK+zF,iBAAiB/zF,SAExDie,EAAOhZ,MAAO,4BAA2BjF,MAWjD4yF,mBAAmBD,GACf3yF,KAAKg0F,cAAgBrB,EACrB3yF,KAAKi0F,gBAAkBj0F,KAAKuhB,OAC5BvhB,KAAKilF,WAAWjlF,KAAKg0F,cAAcE,YAAYl0F,KAAKi0F,kBACpDj0F,KAAKs+B,MAAQt+B,KAAKuhB,OAAOue,YAAY,GASzCq0D,oBACQn0F,KAAKg0F,gBACLh0F,KAAKg0F,cAAcI,aACnBp0F,KAAKilF,WAAWjlF,KAAKi0F,iBACrBj0F,KAAKi0F,gBAAkB,KACvBj0F,KAAKs+B,MAAQt+B,KAAKuhB,OAAOue,YAAY,IAS7Cu0D,oBAAoB1B,GACZ3yF,KAAKg0F,gBACLh0F,KAAKm0F,oBACLn0F,KAAKg0F,mBAAgBzzF,GAErBoyF,GACA3yF,KAAK4yF,mBAAmBD,GAUhC2B,UAAU3B,GACN,QAAkC,IAAvB3yF,KAAKg0F,oBAAmD,IAAXrB,EACpD,OAAO31D,QAAQC,UAGnB,QAAsB,IAAX01D,IAA2BA,EAAOvpB,UAAUppE,MACnD,OAAOg9B,QAAQE,OAAO,IAAI7lC,MAAM,kCAGpC,IAAkC,IAA9B2I,KAAK0yF,qBACL,OAAO11D,QAAQE,OAAO,IAAI7lC,MAAM,mCAKpC,GAAI2I,KAAKs+D,YAAct+D,KAAK6oC,eAGxB,OAFA7oC,KAAKg0F,cAAgBrB,EAEd31D,QAAQC,UAGnB,MAAM1a,EAAaviB,KAAKuiB,WAExB,OAAKA,GAMLviB,KAAK0yF,sBAAuB,EAExB5yE,IAAQ2uD,mBACRzuE,KAAKq0F,oBAAoB1B,GACrB3yF,KAAK2qE,gBACL3qE,KAAK8kF,WAAW11E,QAAQmlF,GAAQp6D,IAASa,kBAAkBu5D,EAAMv0F,KAAKuhB,SAGnEgB,EAAW2zD,aAAal2E,KAAMA,MAChC06B,KAAK,KACF16B,KAAK0yF,sBAAuB,IAE/Bt1D,MAAMp4B,IAIH,MAHAhF,KAAK0yF,sBAAuB,EAC5B1yF,KAAKq0F,sBACLp2E,EAAOjZ,MAAM,sCAAuCA,GAC9CA,KAKXud,EAAWiyE,YAAYx0F,MACzB06B,KAAK,KACF16B,KAAKq0F,oBAAoB1B,GACrB3yF,KAAK2qE,gBACL3qE,KAAK8kF,WAAW11E,QAAQmlF,GAAQp6D,IAASa,kBAAkBu5D,EAAMv0F,KAAKuhB,SAGnEgB,EAAW4b,SAASn+B,QAE9B06B,KAAK,KACF16B,KAAK0yF,sBAAuB,IAE/Bt1D,MAAMp4B,IAMH,MAHAhF,KAAK0yF,sBAAuB,EAC5B1yF,KAAKq0F,sBACLp2E,EAAOjZ,MAAM,sCAAuCA,GAC9CA,MA5CVhF,KAAKq0F,oBAAoB1B,GAElB31D,QAAQC,WAmDvB4K,OACI,OAAO7nC,KAAKy0F,gBAAe,GAQ/B3sD,SACI,OAAO9nC,KAAKy0F,gBAAe,GAY/BA,eAAe3vE,GACX,MAAM4vE,EAAW10F,KAAK20F,UAAU7mG,KAAKkS,KAAM8kB,GAI3C,OAFA9kB,KAAK+yF,cAAgB/yF,KAAK+yF,cAAcr4D,KAAKg6D,EAAUA,GAEhD10F,KAAK+yF,cAWhB4B,UAAU7vE,GACN,GAAI9kB,KAAKs+D,YAAcx5C,EACnB,OAAOkY,QAAQC,UAGnB,GAAIj9B,KAAK+kF,SACL,OAAO/nD,QAAQE,OAAO,IAAItX,IAAgBgkB,sBAG9C,IAAI7H,EAAU/E,QAAQC,UAGtB,MAAM23D,EAAc,IAAM32E,EAAO/Y,KAAM,QAAOlF,SAAS8kB,KAEvD,GAAI9kB,KAAK6oC,gBACE7oC,KAAKw+B,YAAcC,IAAUC,UAC5B5e,IAAQ4qD,8BAChBkqB,IACI50F,KAAKs+B,QACLt+B,KAAKs+B,MAAMgrC,SAAWxkD,QAEvB,GAAIA,EACPid,EAAU,IAAI/E,QAAQ,CAACC,EAASC,KAC5B03D,IACA50F,KAAK60F,kCACD,KACQ70F,KAAKg0F,eACLh0F,KAAKm0F,oBAMTn0F,KAAKslF,sBACLtlF,KAAKipF,aACLjpF,KAAKilF,WAAW,MAChBhoD,KAEJC,SAEL,CACH03D,IAGA,MAAME,EAAgB,CAClBp/D,eAAgB11B,KAAK2oF,cACrBhuD,QAAS,CAAE4D,KACXmC,QAAS1gC,KAAKg0F,cAAgB,CAAEh0F,KAAKg0F,eAAkB,GACvDn+D,WAAY71B,KAAK+0F,uBAGjBj1E,IAAQ+a,iBACRkH,EACM5H,IAASuF,kCAAkC1yC,OAAOgoG,OAChD,GACAF,EACA,CAAE/uE,YAAa,CAAEkO,MAAOj0B,KAAK8yF,kBAEjC9yF,KAAKq2B,aACLy+D,EAAcz+D,WAAar2B,KAAKq2B,YAGpC0L,EACM5H,IAASyD,+BAA+Bk3D,IAGlD/yD,EAAUA,EAAQrH,KAAKu6D,IAEnB,MAAMtpE,EAAY3rB,KAAKsnC,YAAc/I,IAAsBA,IAAkBv+B,KAAKsnC,UAC5E4tD,EACAp1E,IAAQ+a,iBACJo6D,EAAYhkE,KACV/rB,GAAQA,EAAKo5B,MAAMlF,OAASzN,GAC9BspE,EAAYhkE,KACV/rB,GAAQA,EAAKymB,YAAcA,GAEvC,IAAIupE,EAaA,MAAM,IAAItvE,IAAgBikB,yBAU9B,OAtBI7pC,KAAKilF,WAAWiQ,EAAW3zE,QAC3BvhB,KAAKs+B,MAAQ42D,EAAW52D,MAIpBt+B,KAAKw+B,YAAc02D,EAAW12D,YAC9BvgB,EAAO9Y,KACAnF,KAAF,yCACDA,KAAKw+B,UAAW02D,EAAW12D,WAC/Bx+B,KAAKw+B,UAAY02D,EAAW12D,WAMhCx+B,KAAKg0F,eACLh0F,KAAK4yF,mBAAmB5yF,KAAKg0F,eAGjCh0F,KAAK8kF,WAAWt/E,IACZ+uF,GAAQp6D,IAASa,kBAAkBu5D,EAAMv0F,KAAKuhB,SAE3CvhB,KAAKm1F,mCAIpB,OAAOpzD,EACFrH,KAAK,IAAM16B,KAAKo1F,gBAAgBtwE,IAChC4V,KAAK,IAAM16B,KAAKkjB,KAAKjuB,qBAAoB+K,OASlDm1F,iCACI,OAAKn1F,KAAKuiB,WAcH,IAAIya,QAAQ,CAACC,EAASC,KACzBl9B,KAAKuiB,WAAW8yE,uBAAuBr1F,MAClC06B,KAAKuC,EAASj4B,GAASk4B,EAAO,IAAI7lC,MAAM2N,OAftCg4B,QAAQC,UA0BvB43D,kCAAkCS,EAAiBn9B,GAC1Cn4D,KAAKuiB,WAKVviB,KAAKuiB,WAAWgzE,wBAAwBv1F,MAAM06B,KAC1C46D,EACAtwF,GAASmzD,EAAc,IAAI9gE,MAAM2N,KANjCswF,IAgBRF,gBAAgBvtD,GACZ,OAAK7nC,KAAKuiB,YAAeviB,KAAKuiB,WAAW48B,KAIlC,IAAIniB,QAAQC,IACfj9B,KAAKuiB,WAAW48B,KACZn/C,KAAK6oC,eACC,eACA,gBAAgBhB,EAAM5K,KAPzBD,QAAQC,UAoBvBja,UACIhjB,KAAKq0F,sBAEL,IAAItyD,EAAU/E,QAAQC,UAkBtB,OAhBIj9B,KAAKuiB,aACLwf,EAAU/hC,KAAKuiB,WAAWiyE,YAAYx0F,OAGtCA,KAAKuhB,SACLvhB,KAAKipF,aACLjpF,KAAKmmF,UAGThsD,IAASrY,eAAekX,IAAUnQ,wBAAyB7oB,KAAKszF,yBAE5DtzF,KAAKyzF,6BACLt5D,IAASrY,eAAekX,IAAUrQ,4BAC9B3oB,KAAKyzF,6BAGN1xD,EAAQrH,KAAK,IAAML,MAAMrX,WAUpCs7C,UAEI,OAAKt+D,KAAKuhB,YAGNvhB,KAAK2qE,gBAAmB3qE,KAAKi8D,eAIzBj8D,KAAKs+B,QAAUt+B,KAAKs+B,MAAMgrC,UAStCksB,eAAejzE,GACXviB,KAAKuiB,WAAaA,EAMlB,IAAK,IAAIj2B,EAAI,EAAGA,EAAI0T,KAAK8kF,WAAWvuF,OAAQjK,IACxC0T,KAAK+lF,wBAAwB/lF,KAAK8kF,WAAWx4F,IASrDi5B,UACI,OAAO,EAQXojE,cACI,OAAO3oF,KAAKozF,eAAiBpzF,KAAK21B,SAStCgnC,mBACI,OAAO38D,KAAKuiB,YAAcviB,KAAKuiB,WAAW25C,WAU9Cu5B,yBAAyBnyE,EAAKoyE,GACtBA,EAAY,IACZ11F,KAAKkzF,cAAe,GAExB,MAAM9kB,EAAqB9qD,EAAI+1D,qBAE3Br5E,KAAKmzF,eAAwC,cAAvB/kB,IACtB7jE,WAAW,KACFvK,KAAKkzF,eACNj1E,EAAO9Y,KAAM,GAAEnF,mDACT01F,KAENt2E,IAAWyB,UAAUkG,UAAUsD,IAAe,CAAE,WAAcrqB,KAAKsnC,cAExE,KACHtnC,KAAKmzF,eAAgB,GAU7B4B,sBACI,GAAI/0F,KAAK2qE,gBAAkB3qE,KAAKw+B,YAAcC,IAAUE,OAAQ,CAQ5D,IAAIg3D,EAEJ,IACIA,EAAgB31F,KAAKs+B,MAAMoE,cAC7B,MAAOrpC,IAKT,OAAIs8F,GAAiB,eAAgBA,EAC1BA,EAAc9/D,gBAGO,IAArB71B,KAAKgzF,YACLhzF,KAAKgzF,YAMTl9D,IAAiBC,MAShCkzD,aASIjpF,KAAK41F,uBAAwB,EAE7B,IACIz7D,IAAS8E,gBAAgBj/B,KAAKuhB,QADlC,QAGIvhB,KAAK41F,uBAAwB,GAiBrCC,gBACQ71F,KAAK2qE,gBACE3qE,KAAKw+B,YAAcC,IAAUE,QACO,mBAA7B3+B,KAAKs+B,MAAMu3D,gBACzB71F,KAAKs+B,MAAMu3D,gBAEX71F,KAAKgzF,YACChzF,KAAKgzF,cAAgBl9D,IAAiB69B,YAClC79B,IAAiBC,KACjBD,IAAiB69B,aAenCmgC,kBACI,GAAI9zF,KAAK2qE,iBACD3qE,KAAKs+D,WAAat+D,KAAK41F,uBAAyB51F,KAAKw+B,YAAcC,IAAUC,SACjF,OAAO,EAGX,IAAK1+B,KAAKuhB,OACN,OAAO,EAcX,OAFevhB,KAAK81F,eAAiB91F,KAAKi0F,gBAAkBj0F,KAAKuhB,QAEnDue,YAAY6iD,KAAKrkD,KACxB,eAAgBA,GAA+B,SAArBA,EAAMtlB,YACzB,UAAWslB,IAA0B,IAAhBA,EAAMxZ,QAQ7CpuB,WACI,MAAQ,cAAasJ,KAAK6iC,SAAS7iC,KAAKsnC,iB,2EC15BhD,wFAQA,MAAMrpB,EAASF,oBAAUM,GAYV,MAAM03E,UAAyBt2E,eAW1CxpB,YAAYssB,EAAYyzE,GACpB37D,QAKAr6B,KAAKi2F,oBAAsBD,EAM3Bh2F,KAAKk2F,YAAc,KAKnBl2F,KAAKm2F,sBAAuB,EAK5Bn2F,KAAKo2F,mBAAqB,GAS1Bp2F,KAAKq2F,gBAAkBr5D,QAAQC,UAK/Bj9B,KAAKs2F,iBAAmBt2F,KAAKs2F,iBAAiBxoG,KAAKkS,MAEnDuiB,EAAWZ,GAAGgB,cAAmC3iB,KAAKu2F,YAAYzoG,KAAKkS,OACvEuiB,EAAWZ,GAAGgB,gBAAqC3iB,KAAKw2F,cAAc1oG,KAAKkS,OAC3EuiB,EAAWZ,GAAGgB,qBAA0C3iB,KAAK4oE,kBAAkB96E,KAAKkS,OAQxFy2F,uBAAuBC,GACnB12F,KAAKo2F,mBAAmBlzF,KAAKwzF,GAC7BA,EAAW/0E,GAAG80B,wBAAuB,MAGVz2C,KAAKo2F,mBAAmB1lE,OAAOimE,IAAoC,IAAxBA,EAAS16B,YAKvD1lE,QAAUyJ,KAAKm2F,qBAC/Bn2F,KAAK42F,kBACG52F,KAAKm2F,sBACbn2F,KAAK62F,qBASjBA,mBACI72F,KAAKk2F,YAAYv0E,GAAGi1B,sBAAqB52C,KAAKs2F,kBAC9Ct2F,KAAKk2F,YAAYn5E,QACjB/c,KAAKm2F,sBAAuB,EAOhCS,kBACI52F,KAAKk2F,YAAYp0E,eAAe80B,sBAAqB52C,KAAKs2F,kBAC1Dt2F,KAAKk2F,YAAYtyE,OACjB5jB,KAAKm2F,sBAAuB,EAahCG,iBAAiBhO,GACb,IAAK,MAAMqO,KAAY32F,KAAKo2F,mBACxBO,EAASG,gBAAgBxO,GASjCyO,0BAA0Bz4B,GACtB,IAAK,MAAMq4B,KAAY32F,KAAKo2F,mBACxBO,EAASK,gBAAgB14B,GAWjCi4B,YAAYj4D,GACJA,EAAMknD,sBAGNxlF,KAAKq2F,gBAAkBr2F,KAAKq2F,gBAAgB37D,KAAK,IAAM16B,KAAKi2F,uBACvDv7D,KAAKmsD,GACFF,IAAgB/4F,OAAO0wC,EAAMqqD,cA/IjB,KA+IyD9B,IAExEnsD,KAAKu8D,IACFh5E,EAAOhZ,MAAM,kCAAmCq5B,EAAMsnD,iBAEtD5lF,KAAKk2F,YAAce,EAInBj3F,KAAK+2F,0BAA0Bz4D,EAAMggC,cAYrDsK,kBAAkBtqC,GACVA,EAAMknD,sBAENxlF,KAAKq2F,gBAAkBr2F,KAAKq2F,gBAAgB37D,KAAK,KAE7C16B,KAAK+2F,0BAA0Bz4D,EAAMggC,cAajDk4B,cAAcl4D,GACNA,EAAMknD,sBAENxlF,KAAKq2F,gBAAkBr2F,KAAKq2F,gBAAgB37D,KAAK,KAC7Czc,EAAOhZ,MAAM,uCAAwCq5B,EAAMsnD,iBAGvD5lF,KAAKk2F,cACLl2F,KAAK42F,kBACL52F,KAAKk2F,YAAYryD,UACjB7jC,KAAKk2F,YAAc,MAIvB,IAAK,MAAMS,KAAY32F,KAAKo2F,mBACxBO,EAAStuF,e,iFCnN7B,qDAOe,MAAM6uF,EAMjBjhG,YAAYkR,EAAU,IAClBnH,KAAKwU,YAAcrN,EAAQoN,WAC3BvU,KAAKm3F,MAAQhwF,EAAQ1Z,KAErBuS,KAAKo3F,cAAcjwF,EAAQ4/D,WAC3B/mE,KAAKq3F,UAAUlwF,EAAQ4I,QAQ3BunF,WACI,OAAOt3F,KAAK2lD,OAQhB4xC,QACI,OAAOv3F,KAAKw3F,WAQhBC,eACI,OAAOz3F,KAAK03F,WAQhBC,uBACI,OAAO33F,KAAK43F,mBAQhBC,YACI,OAAO73F,KAAKkyD,QAQhB4lC,gBACI,OAAO93F,KAAK+3F,YAQhBC,UACI,OAAOh4F,KAAKm3F,MAUhBc,SAASjzF,GACLhF,KAAK2lD,OAAS3gD,EAUlBkzF,qBAAqBn3D,GACjB/gC,KAAK43F,mBAAqB72D,EAS9Bs2D,UAAUtnF,GACN/P,KAAKkyD,QAAUniD,EAOnBooF,aAAa97B,GACTr8D,KAAK03F,WAAar7B,EAQtB+7B,cAAc/7B,GACVr8D,KAAK+3F,YAAc17B,EAoBvBt/C,OAAM,QAAEs7E,EAAF,YAAWC,EAAX,YAAwBC,EAAxB,SAAqC/vD,IACvC,OAAO,IAAIxL,QAAQ,CAACC,EAASC,KACzBl9B,KAAKwU,YAAYpG,OACbpO,KAAKw4F,UAAU,CACXhuE,OAAQ,QACR6tE,UACAE,cACAD,cACA9vD,aAEJ/iC,IAKIzF,KAAKq3F,UAAU,WACfr3F,KAAKo3F,cACDqB,IAAkBpxB,mBAAmB5hE,IAEzCw3B,KAEJj4B,IACIhF,KAAK04F,gBAAgB1zF,GAErBk4B,EAAOl4B,OAcvB4e,MAAK,YAAE20E,IACH,OAAO,IAAIv7D,QAAQ,CAACC,EAASC,KACzBl9B,KAAKwU,YAAYpG,OACbpO,KAAKw4F,UAAU,CACXhuE,OAAQ,OACR+tE,gBAEJt7D,EACAC,KAqBZs7D,WAAU,OAAEhuE,EAAF,QAAU6tE,EAAV,YAAmBC,EAAnB,YAAgCC,EAAhC,SAA6C/vD,IACnD,OAAO7sC,cAAI,CACPotC,GAAIwvD,EACJtxF,KAAM,QAETta,EAAE,QAAS,CACR,MAAS,kCACT,OAAU69B,EACV,SAAY6tE,EACZ,eAAkBr4F,KAAKm3F,MACvB,SAAY3uD,EACZ,sBAAyB8vD,IAE5BnyF,KAULuyF,gBAAgBC,GACZ,MAAM3zF,EAAQ2zF,EAAQ7nF,qBAAqB,SAAS,GAEpD9Q,KAAKi4F,SAASjzF,EAAM4zF,SAAS,GAAGz4F,SAUpCi3F,cAAcrwB,GACV/mE,KAAKw3F,WAAazwB,K,8xBCmBX,QAjQf,MAKI9wE,cACI+J,KAAK64F,cAAgB,IAAIp5E,IACzBzf,KAAK84F,oBAAsB,GAE3Bh2D,IAAIc,YACA5K,IAAUpQ,oBACV+R,GACI36B,KAAK64F,cAAc31E,KACf61E,sBACAp+D,IACZmI,IAAIc,YACA5K,IAAUlQ,sBACV6R,GACI36B,KAAKg5F,iBACDh5F,KAAKmhC,uBACLxG,IACZmI,IAAIc,YACA5K,IAAUjR,oBACVgR,GACI/4B,KAAKi5F,0BAA0BlgE,IAKvC/4B,KAAKk5F,yBAA2B,IAAIl8D,QAAQC,IACnCzC,UAAU2+D,YAMf3+D,UAAU2+D,YAAYC,MAAM,CAAEvsG,KAzCZ,WA0Cb6tC,KAAK,IAAMuC,GAAQ,GAAO,IAAMA,GAAQ,IANzCA,GAAQ,KAkBpBg8D,0BAA0BlgE,GACtB/4B,KAAK84F,oBAAL,OACO94F,KAAK84F,qBACL//D,GASXigE,iBAAiBK,EAAU1+D,GACvB,MAAMnB,EACAmB,EAAQ1J,KACNrkC,GAAgB,gBAAXA,EAAEwsC,MAA0BxsC,EAAE+oC,WAAa0jE,GAEpD7/D,GACApa,IAAW+F,0BACP2d,IAAIzB,4BAA4B7H,IAQ5Ce,iBAAiB/uB,GACbs3B,IAAIvI,iBAAiB/uB,GASzB0wB,wBACI,OAAO4G,IAAI5G,wBAUfyE,wBAAwBC,GACpB,OAAOkC,IAAInC,wBAAwBC,GAUvC04D,0BAA0BryF,GACtB,OAAO,IAAI+1B,QAAQC,IAGXh2B,KAAQjH,KAAK84F,oBACb77D,EAAQj9B,KAAK84F,oBAAoB7xF,IAMrCjH,KAAKk5F,yBAAyBx+D,KAAK6+D,IAC/B,IAAKA,EAID,OAAIz5E,IAAQyV,gBACR0H,GAAQ,QAIZA,GAAQ,GAKZ,MAAMy9C,EAAW,GAEjB,OAAQzzE,GACR,KAAKs3B,IACDm8C,EAASx3E,KACLs3B,UAAU2+D,YAAYC,MAAM,CACxBvsG,KAjJE,YAmJV,MACJ,KAAK0xC,IACDm8C,EAASx3E,KACLs3B,UAAU2+D,YAAYC,MAAM,CACxBvsG,KAzJE,gBA2JV,MACJ,QACI6tF,EAASx3E,KACLs3B,UAAU2+D,YAAYC,MAAM,CACxBvsG,KA7JE,YA+JV6tF,EAASx3E,KACLs3B,UAAU2+D,YAAYC,MAAM,CACxBvsG,KAnKE,gBAuKdmwC,QAAQ+K,IAAI2yC,GAAUhgD,KAClB8+D,GAAWv8D,EAAQu8D,EAAQC,MAAMC,GAvKnB,aA2KUA,EAAiBzsD,OAC9BysD,EAAiB3pF,UAI5B,IAAMktB,GAAQ,QAY9B08D,gCACI,OAAQ75E,IAAQsQ,YAQpB+Q,uBACI,OAAO2B,IAAI3B,uBAWfD,qBAAqBvL,GAWjB,OAVyBmN,IAAI1B,oCAER7qC,OAAS,GAI1ByJ,KAAKg5F,iBACDrjE,EAAUmN,IAAI1B,qCAGf0B,IAAI5B,qBAAqBvL,GAQpC0G,iBAAiBhV,EAAO9iB,GACpBvE,KAAK64F,cAAcj1D,YAAYvc,EAAO9iB,GAQ1C0qC,oBAAoB5nB,EAAO9iB,GACvBvE,KAAK64F,cAAc/2E,eAAeuF,EAAO9iB,GAO7Cq1F,UAAUvyE,KAAU8a,GAChBniC,KAAK64F,cAAc31E,KAAKmE,KAAU8a,GAStC1E,gBAII,OAAO3d,IAAQ2d,mB,cC1PvB,IAAIhgB,EAAS,CACT,MAAS,EACT,MAAS,EACT,KAAQ,EACR,IAAO,EACP,KAAQ,EACR,MAAS,GAObN,EAAO08E,iBAAmB90F,QAM1B,IAAI+0F,EAAmB,CAAE38E,EAAO08E,kBAOhC18E,EAAOQ,mBAAqB,SAASC,IACY,IAAzCk8E,EAAiBl2F,QAAQga,IACzBk8E,EAAiB52F,KAAK0a,IAS9BT,EAAOU,sBAAwB,SAASD,GACpC,IAAIm8E,EAAeD,EAAiBl2F,QAAQga,IACtB,IAAlBm8E,GACAD,EAAiB71F,OAAO81F,EAAc,IAO9C,IAAIC,EAAgB,GAgBpB,SAASC,IACL,IAAIC,EAAa,CACbn0C,WAAY,GACZo0C,aAAc,GACd31F,KAAM,KACN41F,OAAQ,MAGRp1F,EAAQ,IAAI3N,MACZ+M,EAAQY,EAAMZ,MAAOY,EAAMZ,MAAMrB,MAAM,MAAQ,GACnD,IAAIqB,GAASA,EAAM7N,OAAS,EACxB,OAAO2jG,EAEX,IAAIxtG,EAAI,KAIR,OAHG0X,EAAM,KACL1X,EAAI0X,EAAM,GAAGkP,MAAM,iDAEnB5mB,GAAKA,EAAE6J,QAAU,GAEe,IAA7B6N,EAAM,GAAGR,QAAQ,QAEhBs2F,EAAWn0C,WAAa3hD,EAAM,GAAGgR,OAAO,EAAGhR,EAAM,GAAGR,QAAQ,MAG5Ds2F,EAAWn0C,WAAa3hD,EAAM,GAAGgR,OAAO,EAAGhR,EAAM,GAAGR,QAAQ,MAEzDs2F,IAGXA,EAAWn0C,WAAar5D,EAAE,GAC1BwtG,EAAWC,aAAeztG,EAAE,GAC5BwtG,EAAW11F,KAAO9X,EAAE,GACpBwtG,EAAWE,OAAS1tG,EAAE,GACfwtG,GASX,SAASt1F,IACL,IAAIqZ,EAAS9c,UAAU,GAAI0D,EAAQ1D,UAAU,GACzCghC,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GACjD,KAAGsc,EAAO5Y,GAASoZ,EAAOpZ,OAQ1B,IAJA,IAAIq1F,IACIj8E,EAAO9W,QAAQkzF,mBAAqBL,EAAcK,oBAClDJ,IACJj8E,EAAa87E,EAAiB9/F,OAAOikB,EAAOD,YACxC1xB,EAAI,EAAGA,EAAI0xB,EAAWznB,OAAQjK,IAAK,CACvC,IAAIkB,EAAIwwB,EAAW1xB,GACfC,EAAIiB,EAAEqX,GACV,GAAGtY,GAAmB,mBAAPA,EAAmB,CAC9B,IAAI+tG,EAAc,GAElBA,EAAYp3F,MAAK,IAAIiF,MAAOoyF,eAExBt8E,EAAO/W,IACPozF,EAAYp3F,KAAK,IAAM+a,EAAO/W,GAAK,KAGnCgzF,GAAcA,EAAWn0C,WAAWxvD,OAAS,GAC7C+jG,EAAYp3F,KAAK,IAAMg3F,EAAWn0C,WAAa,OAGnD,IAAIy0C,EAAeF,EAAYtgG,OAAOmoC,GAEtC51C,EAAEuB,KAAKN,GAAGo0C,MAAMp0C,EAAGgtG,KAiB/B,SAASr9E,EAAOtY,EAAOqC,EAAI8W,EAAY7W,GACnCnH,KAAKkH,GAAKA,EACVlH,KAAKmH,QAAUA,GAAW,GAC1BnH,KAAKge,WAAaA,EACdhe,KAAKge,aACLhe,KAAKge,WAAa,IAEtBhe,KAAK6E,MAAQ4Y,EAAO5Y,GAEpB,IADA,IAAI41F,EAAUztG,OAAOuY,KAAKkY,GAClBnxB,EAAI,EAAGA,EAAImuG,EAAQlkG,OAAQjK,IAC/B0T,KAAKy6F,EAAQnuG,IACTsY,EAAI9W,KAAK,KAAMkS,KAAMy6F,EAAQnuG,IA7GzC6wB,EAAOW,iBAAmB,SAAS3W,GAC/B6yF,EAAgB7yF,GAAW,IAoH/BgW,EAAOjvB,UAAUiwB,SAAW,SAAUtZ,GAClC7E,KAAK6E,MAAQ4Y,EAAO5Y,IAExB9Y,EAAOD,QAAUqxB,EAKjBA,EAAOM,OAAS,CACZC,MAAO,QACP7e,MAAO,QACPC,KAAM,OACN47F,IAAK,MACL37F,KAAM,OACNrB,MAAO,U,cC3MX3R,EAAOD,QAAU,SAASC,GAoBzB,OAnBKA,EAAO4uG,kBACX5uG,EAAO6uG,UAAY,aACnB7uG,EAAO8uG,MAAQ,GAEV9uG,EAAO6sG,WAAU7sG,EAAO6sG,SAAW,IACxC5rG,OAAOC,eAAelB,EAAQ,SAAU,CACvCmB,YAAY,EACZC,IAAK,WACJ,OAAOpB,EAAOQ,KAGhBS,OAAOC,eAAelB,EAAQ,KAAM,CACnCmB,YAAY,EACZC,IAAK,WACJ,OAAOpB,EAAOO,KAGhBP,EAAO4uG,gBAAkB,GAEnB5uG,I,cCpBR,IAAI+uG,EAAU/uG,EAAOD,QAAU,CAC7B+kD,EAAG,CAAC,CACFhkD,KAAM,UACNkuG,IAAK,YAEPhuG,EAAG,CAAC,CAEFF,KAAM,SACNkuG,IAAK,wCACLz1F,MAAO,CAAC,WAAY,YAAa,iBAAkB,UAAW,QAAS,WACvE01F,OAAQ,wBAGV3sG,EAAG,CAAC,CAAExB,KAAM,SACZP,EAAG,CAAC,CAAEO,KAAM,gBACZouG,EAAG,CAAC,CAAEpuG,KAAM,QACZwM,EAAG,CAAC,CAAExM,KAAM,UACZuB,EAAG,CAAC,CAAEvB,KAAM,UACZquG,EAAG,CAAC,CAAEruG,KAAM,cACZO,EAAG,CAAC,CAAEP,KAAM,YAEZW,EAAG,CAAC,CACFX,KAAM,SACNkuG,IAAK,eACLz1F,MAAO,CAAC,QAAS,QACjB01F,OAAQ,UAEVruG,EAAG,CAAC,CACFE,KAAM,aACNkuG,IAAK,mBACLz1F,MAAO,CAAC,UAAW,MACnB01F,OAAQ,eAEVrjG,EAAG,CAAC,CACFuL,KAAM,YACN63F,IAAK,4BACLz1F,MAAO,CAAC,OAAQ,SAChB01F,OAAQ,UAEVtuG,EAAG,CAAC,CAGFquG,IAAK,mCACLz1F,MAAO,CAAC,OAAQ,OAAQ,WAAY,YACpC01F,OAAQ,gBAEVtjG,EAAG,CACD,CACEwL,KAAM,MACN63F,IAAK,0DACLz1F,MAAO,CAAC,UAAW,QAAS,OAAQ,YACpC01F,OAAQ,SAAUjuG,GAChB,OAAQA,EAAEq+F,SACR,qBACAr+F,EAAEouG,KACF,kBACA,iBAGN,CAEEj4F,KAAM,OACN63F,IAAK,wBACLz1F,MAAO,CAAC,UAAW,UACnB01F,OAAQ,cAEV,CACEnuG,KAAM,UACNkuG,IAAK,gBACLC,OAAQ,cAEV,CACEnuG,KAAM,OACNkuG,IAAK,sCACLz1F,MAAO,CAAC,OAAQ,UAAW,QAAS,WACpC01F,OAAQ,SAAUjuG,GAChB,OAAqB,MAAbA,EAAE2yE,QACR,qBACA,YAGN,CACEx8D,KAAM,eACN63F,IAAK,kCACLz1F,MAAO,CAAC,UAAW,SACnB01F,OAAQ,yBAEV,CACE93F,KAAM,SACN63F,IAAK,6CACLz1F,MAAO,CAAC,UAAW,OAAQ,WAC3B01F,OAAQ,SAAUjuG,GAChB,OAAqB,MAAbA,EAAEquG,QACR,mBACA,kBAGN,CAEEl4F,KAAM,MACN63F,IAAK,6CACLz1F,MAAO,CAAC,QAAS,YAAa,MAAO,UACrC01F,OAAQ,SAAUjuG,GAChB,MAAO,aAAeA,EAAE4iC,UAAY,MAAQ,MAAQ,OAAS5iC,EAAEymC,OAAS,MAAQ,MAGpF,CACEtwB,KAAM,SACN63F,IAAK,0CACLz1F,MAAO,CAAC,KAAM,QAAS,SAAU,iBACjC01F,OAAQ,SAAUjuG,GAChB,OAA2B,MAAnBA,EAAEsuG,cACR,qBACA,oBAGN,CACExuG,KAAM,QACNkuG,IAAK,eACLC,OAAQ,YAEV,CACEnuG,KAAM,MACNkuG,IAAK,gBACLC,OAAQ,UAEV,CACEnuG,KAAM,OACNkuG,IAAK,aACLC,OAAQ,WAEV,CACEnuG,KAAM,QACNkuG,IAAK,eACLC,OAAQ,YAEV,CACEnuG,KAAM,WACNkuG,IAAK,kBACLC,OAAQ,eAEV,CACEnuG,KAAM,YACNkuG,IAAK,0CAEP,CACEluG,KAAM,UACNkuG,IAAK,eAEP,CACEluG,KAAM,WACNkuG,IAAK,mBACLC,OAAQ,gBAEV,CACEnuG,KAAM,SACNkuG,IAAK,iBACLC,OAAQ,cAEV,CACEnuG,KAAM,cACNkuG,IAAK,2BACLz1F,MAAO,CAAC,OAAQ,QAChB01F,OAAQ,qBAEV,CAKE93F,KAAK,aACL63F,IAAK,+KACLz1F,MAAO,CAAC,aAAc,YAAa,YAAa,WAAY,KAAM,OAAQ,OAAQ,QAAS,QAAS,UAAW,aAAc,aAAc,gBAC3I01F,OAAQ,SAAUjuG,GAChB,IAAIqK,EAAM,qCAaV,OAXAA,GAAmB,MAAXrK,EAAEuuG,MAAiB,qBAAuB,OAGlDlkG,GAAqB,MAAbrK,EAAEgiC,QAAmB,cAAgB,KAEzB,MAAhBhiC,EAAE+hC,aACJ13B,GAAO,kBAGTA,GAA2B,MAAnBrK,EAAE,cAAyB,iBAAmB,KACtDqK,GAA6B,MAArBrK,EAAE,gBAA2B,mBAAqB,OAI9D,CACEF,KAAM,kBACNkuG,IAAK,wBAEP,CACEluG,KAAM,mBACNkuG,IAAK,0BACLC,OAAQ,wBAEV,CACEnuG,KAAM,aACNkuG,IAAK,qBACLC,OAAQ,kBAEV,CACE93F,KAAM,QACN63F,IAAK,iCACLz1F,MAAO,CAAC,KAAM,YAAa,SAC3B01F,OAAQ,SAAUjuG,GAChB,IAAIqK,EAAM,UAOV,OANmB,MAAfrK,EAAEuQ,YACJlG,GAAO,MACQ,MAAXrK,EAAEQ,QACJ6J,GAAO,QAGJA,IAGX,CAEE8L,KAAM,aAEN63F,IAAK,mEACLz1F,MAAO,CAAC,YAAa,SACrB01F,OAAQ,oBAEV,CACEnuG,KAAM,eACNkuG,IAAK,gCACLz1F,MAAO,CAAC,WAAY,SACpB01F,OAAQ,wBAEV,CACE93F,KAAM,SACN63F,IAAK,oBACLz1F,MAAO,CAAC,OAAQ,QAChB01F,OAAQ,eAEV,CACEnuG,KAAM,UACNkuG,IAAK,eAEP,CACEluG,KAAM,YACNkuG,IAAK,iBAEP,CACEluG,KAAM,UACNkuG,IAAK,uCACLz1F,MAAO,CAAC,gBAAiB,MAAO,kBAChC01F,OAAQ,SAAUjuG,GAChB,OAA4B,MAApBA,EAAEi4E,eACR,mBACA,kBAGN,CACEn4E,KAAM,cACNkuG,IAAK,0BACLC,OAAQ,oBAEV,CACE93F,KAAM,OACN63F,IAAK,sCACLz1F,MAAO,CAAC,KAAM,YAAa,UAC3B01F,OAAQ,SAAUjuG,GAChB,OAAQA,EAAE0iC,OAAU,eAAiB,cAGzC,CAGEvsB,KAAM,aACN63F,IAAK,IAAIjwC,OAEP,wKAMFxlD,MAAO,CAAC,KAAM,OAAQ,SAAU,OAAQ,UACxC01F,OAAQ,SAAUjuG,GAChB,MAAO,sBAAwBA,EAAEwuG,KAAO,SAAW,MAGvD,CAEE1uG,KAAM,YACNkuG,IAAK,IAAIjwC,OAEP,2FAQFxlD,MAAO,CAAC,OAAQ,QAAS,OAAQ,SACjC01F,OAAQ,SAAUjuG,GAChB,MAAO,mBAAqBA,EAAEwuG,KAAO,SAAW,MAGpD,CAIE1uG,KAAM,eACNkuG,IAAK,kCACLz1F,MAAO,CAAC,SACR01F,OAAQ,iBAEV,CAGEnuG,KAAM,YACNkuG,IAAK,8BACLC,OAAQ,gBAEV,CACE93F,KAAM,UACNoC,MAAO,CAAC,YAMdtY,OAAOuY,KAAKu1F,GAAS1rF,SAAQ,SAAUvhB,GAC1BitG,EAAQjtG,GACduhB,SAAQ,SAAUrZ,GAChBA,EAAIglG,MACPhlG,EAAIglG,IAAM,QAEPhlG,EAAIilG,SACPjlG,EAAIilG,OAAS,a,gBC9UnB,MAAMQ,EAAkBp+E,EAAQ,KAa1Bq+E,EAAgB,SAASn9D,GAE3Bt+B,KAAKs+B,MAAQA,EAGbt+B,KAAK07F,SAAW,KAIhB17F,KAAKrG,KAAO,KAIZqG,KAAKnT,KAAO,KAGZmT,KAAK27F,UAAY,MAQrB,SAASC,EAAcC,GACnB,QAA+Bt7F,IAA3Bs7F,EAAcH,SACd,MAAM,IAAIrkG,MAAM,yEAGpBwkG,EAAcH,SAAS3+E,QACvB8+E,EAAcF,UAAY,IAAIxzF,KAQlC,SAAS2zF,EAAaD,GAClB,QAA+Bt7F,IAA3Bs7F,EAAcH,SACd,MAAM,IAAIrkG,MAAM,wEAGpBwkG,EAAcH,SAAS93E,OAO3B,SAASm4E,IACL,GAAIC,cAAcC,gBA3DH,cA4DX,MA5DW,aA6DR,GAAID,cAAcC,gBA5DX,aA6DV,MA7DU,YA+Dd,MAAM,IAAI5kG,MACN,6DASR,SAAS6kG,EAAcC,GAGnBn8F,KAAKo8F,UAAY,GAGjBp8F,KAAKq8F,SAAWN,IAGhB/7F,KAAKs8F,aAAc,EAGnBt8F,KAAKm8F,gBAAkBA,EAM3BD,EAAcH,yBAA2BA,EAOzCG,EAAchuG,UAAUiwC,SAAW,SAASG,GACxC,GAAIA,EAAMuK,eAAgB,CAEtB,MAAMgzD,EAAgB77F,KAAKu8F,yBAAyBj+D,GAIpDt+B,KAAKo8F,UAAUl5F,KAAK24F,GAGpB77F,KAAKw8F,cAIDx8F,KAAKs8F,aACLV,EAAcC,KAU1BK,EAAchuG,UAAUquG,yBAA2B,SAASj+D,GACxD,MAAMu9D,EAAgB,IAAIJ,EAAcn9D,GAGlCm+D,EAAiBZ,EAAcv9D,MAAMmnD,oBACrClkE,EA2KV,WACI,GAA2B,oBAAhBqa,YACP,OAAO,IAAIA,YAEf,MAAM,IAAIvkC,MAAM,qCA/KDqlG,GAmBf,OAjBAD,EAAe7jE,iBAAiBxpB,QAAQ5hB,GAAK+zB,EAAO4c,SAAS3wC,IAG7DquG,EAAcH,SAAW,IAAIM,cAAcz6E,EACvC,CAAEo7E,SAAU38F,KAAKq8F,WAIrBR,EAAcliG,KAAO,GAGrBkiG,EAAcH,SAASkB,gBAAkB,SAASC,GAC1CA,EAAUljG,KAAKspB,KAAO,GACtB44E,EAAcliG,KAAKuJ,KAAK25F,EAAUljG,OAInCkiG,GAaXK,EAAchuG,UAAUsmG,YAAc,SAASl2D,GAC3C,GAAIA,EAAMqsC,eACN,OAGJ,MAAM/5C,EAAQ5wB,KAAKo8F,UACnB,IAAI9vG,EAEJ,IAAKA,EAAI,EAAGA,EAAIskC,EAAMr6B,OAAQjK,IAC1B,GAAIskC,EAAMtkC,GAAGgyC,MAAMq+B,qBAAuBr+B,EAAMq+B,mBAAoB,CAChE,MAAMmgC,EAAmBlsE,EAAMtkC,GAE3B0T,KAAKs8F,YACLR,EAAagB,GAGblsE,EAAM3sB,OAAO3X,EAAG,GAM5B0T,KAAKw8F,eAQTN,EAAchuG,UAAUsuG,YAAc,WAClC,MAAMj6E,EAAaviB,KAAKm8F,gBAExBn8F,KAAKo8F,UAAUhtF,QAAQysF,IACnB,GAAIA,EAAcv9D,MAAM/Y,UACpBs2E,EAAchvG,KAAO,sBAClB,CACH,MAAMqa,EAAK20F,EAAcv9D,MAAMq+B,mBAEzB6vB,EADcjqE,EAAW06C,mBAAmB/1D,GACtBqlF,iBAEZ,cAAZC,IACAqP,EAAchvG,KAAO2/F,OASrC0P,EAAchuG,UAAU6uB,MAAQ,WAC5B,GAAI/c,KAAKs8F,YACL,MAAM,IAAIjlG,MAAM,sCAKpB2I,KAAKs8F,aAAc,EAGnBt8F,KAAKo8F,UAAUhtF,QAAQysF,GAAiBD,EAAcC,IAGtD92F,QAAQH,IACH,2DACG5E,KAAKo8F,UAAU7lG,6BAM3B2lG,EAAchuG,UAAU01B,KAAO,WAE3B5jB,KAAKs8F,aAAc,EAGnBt8F,KAAKo8F,UAAUhtF,QAAQysF,GAAiBC,EAAaD,IACrD92F,QAAQH,IAAI,sBAMhBs3F,EAAchuG,UAAU6uG,SAAW,WAC/B/8F,KAAKo8F,UAAUhtF,QAAQysF,IACnB,MAAM58B,EAAO,IAAI+9B,KAAKnB,EAAcliG,KAAM,CAAEsN,KAAMjH,KAAKq8F,WACjDt7D,EAAMtF,IAAIC,gBAAgBujC,GAC1BvnE,EAAI2D,SAASwF,cAAc,KAEjCxF,SAASyc,KAAKlX,YAAYlJ,GAC1BA,EAAE8F,MAAQ,gBACV9F,EAAEulG,KAAOl8D,EACTrpC,EAAEqlG,SAAY,QAAO/8F,KAAKq8F,SAASt5F,MAAM,KAAK,GAC9CrL,EAAEwlG,QACFhxG,OAAOuvC,IAAIuF,gBAAgBD,MASnCm7D,EAAchuG,UAAUivG,oBAAsB,WAC1C,GAAIn9F,KAAKs8F,YACL,MAAM,IAAIjlG,MACN,kEAIR2I,KAAKw8F,cAEL,MAAM5rE,EAAQ,GAUd,OARA5wB,KAAKo8F,UAAUhtF,QACXssF,GACI9qE,EAAM1tB,KACF,IAAIs4F,EACA,IAAIwB,KAAKtB,EAAS/hG,KAAM,CAAEsN,KAAMjH,KAAKq8F,WACrCX,EAAS7uG,KACT6uG,EAASC,aAElB/qE,GAOXsrE,EAAchuG,UAAUkvG,YAAc,WAClC,OAAOp9F,KAAKq8F,UAkBhBtwG,EAAOD,QAAUowG,G,6BC7TjB,4EAmBe,SAASmB,EAAgBC,EAAO1kD,EAAOzxC,GAClDnH,KAAKs9F,MAAQA,EACbt9F,KAAK44C,MAAQA,EACb54C,KAAKmH,QAAUA,EACfnH,KAAKqf,KAAO,IAAIs5B,IAAKxxC,EAASyxC,GAG9B54C,KAAKq8B,iBAAiBof,oBAClB,CAAC8hD,EAASz4F,EAAKy1C,EAAa1vB,KACxBzL,IAAWgI,oBACPsD,YAA4B6yE,EAASz4F,EAAK+lB,MAItD7qB,KAAKq8B,iBAAiBof,0BAClB32C,IAMQA,GACAsa,IAAWoI,cACPg2E,IACA,CAAE/4F,QAASK,IAEnBsa,IAAWkH,QACP1Z,KAAKwL,UACD,CACIlR,GAAIs2F,IACJ14F,WAUxBu4F,EAAgBnvG,UAAUud,QAAU,SAAStE,EAAU,IACnDnH,KAAKqf,KAAK5T,QAAQtE,EAAQD,GAAIC,EAAQw0C,WAU1C0hD,EAAgBnvG,UAAUke,OAAS,SAASjF,GACxCnH,KAAKqf,KAAKjT,OAAOjF,IAOrBk2F,EAAgBnvG,UAAUqhB,WAAa,YAAY4yB,GAK/C,OAAOniC,KAAKqf,KAAK9P,cAAc4yB,IAQnCk7D,EAAgBnvG,UAAUmvD,OAAS,WAC/B,OAAOr9C,KAAKqf,KAAKg+B,UAOrBggD,EAAgBnvG,UAAUuvG,SAAW,SAAS7kD,GAC1C54C,KAAK44C,MAAQA,GAWjBykD,EAAgBnvG,UAAUwvG,oBAAsB,SAAS7wG,EAAMsa,GAC3D,OAAO,IAAIw2F,IAAgB,CACvB9wG,OACA2mC,OAAQrsB,EACRoN,WAAYvU,QASpBq9F,EAAgBnvG,UAAUmuC,iBAAmB,SAAShV,EAAO3F,GACzD1hB,KAAKqf,KAAKukB,YAAYvc,EAAO3F,IAQjC27E,EAAgBnvG,UAAU+gD,oBAAsB,SAAS5nB,EAAO3F,GAC5D1hB,KAAKqf,KAAKyC,eAAeuF,EAAO3F,IAMpC27E,EAAgBnvG,UAAU0vG,mBAAqB,WAC3C,OAAO59F,KAAKqf,KAAKy5B,iBAUrBukD,EAAgBnvG,UAAU4rD,WAAa,SAASkpC,EAASU,GAAS,GAC9D,OAAO1jF,KAAKqf,KAAKo6B,KAAKK,WAAWkpC,EAASU,IAU9C2Z,EAAgBnvG,UAAU01F,cAAgB,SAASZ,EAASU,GAAS,GACjE,OAAO1jF,KAAKqf,KAAKo6B,KAAKmqC,cAAcZ,EAASU,IAMjD2Z,EAAgBnvG,UAAU2vG,QAAU,WAChC,MAAMlkG,EAAOqG,KAAKqf,KAAKi+B,eAEjBwgD,EAAW,GAEjBA,EAASpM,KAAO,IAAIvpF,KACpB21F,EAAS/8D,IAAM70C,OAAO6uB,SAASkiF,KAC/Ba,EAASC,GAAKvjE,UAAUsZ,UAExB,MAAMlvC,EAAM5E,KAAKqf,KAAKm+B,aAQtB,OANI54C,IACAk5F,EAASz+E,KAAOza,GAGpBjL,EAAKmkG,SAAWA,EAETnkG,I,4qCC3HX,MAAMskB,GAASF,oBAAUM,GAmDV,SAASs/E,GAAgBx2F,GACpC,IAAKA,EAAQta,MAAQsa,EAAQta,KAAK8V,gBAAkBwE,EAAQta,KAAM,CAC9D,MAAM8qC,EACA,8GAIN,MADA1Z,GAAOjZ,MAAM2yB,GACP,IAAItgC,MAAMsgC,GAEpB33B,KAAKwf,aAAe,IAAIC,IACxBzf,KAAKmH,QAAUA,EACfnH,KAAKg+F,aAAe,IAAIC,IAA4Bj+F,MACpDA,KAAK+8D,aAAe,GACpB/8D,KAAKi6E,MAAM9yE,GACXnH,KAAKk+F,mBAAqB,IAAIC,IAAmBn+F,MAMjDA,KAAKo+F,iBAAmB,KACxBp+F,KAAKq+F,oBAAsB,KAC3Br+F,KAAKs+F,YAAc,KACnBt+F,KAAKu+F,sBAAuB,EAC5Bv+F,KAAKw+F,aAAc,EACnBx+F,KAAKy+F,iBAAkB,EACvBz+F,KAAK0+F,iBAAkB,EACvB1+F,KAAK2+F,iBAAmB,CACpBtpE,OAAO,EACPpB,OAAO,GAEXj0B,KAAK4+F,gBAAiB,EAGtB5+F,KAAK6+F,kBAAoB,KAMzB7+F,KAAK8+F,YAAa,EAGlB9+F,KAAKsnB,WAAa,GAOlBtnB,KAAK++F,kBACC,IAAIC,IAAkBh/F,KAAMA,KAAKwf,aAAcrY,GAMrDnH,KAAKi/F,oBACC,IAAIC,IAAoBl/F,KAAMmH,EAAQqsB,OAAO2rE,cAAgB,IAMnEn/F,KAAKo/F,4BAA8B,IAAIC,IAA2Br/F,MAKlEA,KAAKs/F,4BAA6B,EAKlCt/F,KAAKu/F,sBAAwB,IAAIC,IAAsBx/F,MAUvDA,KAAKy/F,qBAAuB,KAE5B,MAAMC,EACApnF,SAASnR,EAAQqsB,OAAO2qB,KAAOh3C,EAAQqsB,OAAO2qB,IAAIwhD,eAAgB,IAOxE3/F,KAAK2/F,eAAiBjmF,MAAMgmF,GAAS,EAAIA,EACzCzhF,GAAO/Y,KAAM,mBAAkBlF,KAAK2/F,gBAQpC3/F,KAAK4/F,4BAA6B,EAQlC5/F,KAAKm+C,KAAM,EAMXn+C,KAAK6/F,iBAAmB,KAExB7/F,KAAK8/F,kBAAoB,IAAIC,IAAW//F,KAAKm/C,MAC7Cn/C,KAAKggG,iBAAmB,IAAIC,IAAiBjgG,KAAKm/C,MAQlDn/C,KAAKkgG,uCAAoC3/F,EAKrCP,KAAKmgG,oBACLliF,GAAO/Y,KAAK,uCAEZlF,KAAKogG,eAAiB,IAAIhmD,IAAcp6C,OAKhD29F,GAAgBzvG,UAAU+H,YAAc0nG,GAcxCA,GAAgB0C,gBAAkB,SAAS18F,EAAK28F,GAC5C,IAAIpjD,EAEJ,GAAIojD,EAEApjD,EAAc5rB,IAAW6rB,gBAAgB,GAAGx6C,kBACzC,CAIHu6C,EAAczhD,UAAQiI,eAAeC,GAAKyR,OAAO,EAAG,GAC/CzS,cAIM,eAEHuP,KAAKgrC,KACTA,EAAc5rB,IAAW6rB,gBAAgB,GAAGx6C,eAIpD,OAAOu6C,GAQXygD,GAAgBzvG,UAAU+rF,MAAQ,SAAS9yE,EAAU,IAG7CA,EAAQoN,aACRvU,KAAKuU,WAAapN,EAAQoN,WAC1BvU,KAAKqf,KAAOrf,KAAKuU,WAAW8K,KAG5Brf,KAAKg+F,aAAauC,sBAGtB,MAAM,OAAE/sE,GAAWxzB,KAAKmH,QAExBnH,KAAKwgG,gBAAkBhtE,EAAOitE,aAAejtE,EAAOitE,aAAeC,IAASC,kBAC5E3gG,KAAKm/C,KAAOn/C,KAAKqf,KAAKu9B,WAClB58C,KAAKmH,QAAQta,KADL,SAED2mC,GAFC,IAGJotE,QAAS5gG,KAAKwgG,kBAElB7C,GAAgB0C,iBAIpBrgG,KAAK6gG,4BACC7gG,KAAK6gG,4BAA4B/yG,KAAKkS,MAC5CA,KAAKm/C,KAAKvb,YACN+W,yBAAmC36C,KAAK6gG,6BAE5C7gG,KAAK8gG,yBAA2B9gG,KAAK8gG,yBAAyBhzG,KAAKkS,MACnEA,KAAKm/C,KAAKvb,YACN+W,sBAAgC36C,KAAK8gG,0BAEzC9gG,KAAK+gG,4BACC/gG,KAAK+gG,4BAA4BjzG,KAAKkS,MAC5CA,KAAKm/C,KAAKvb,YACN+W,yBAAmC36C,KAAK+gG,6BAE5C/gG,KAAKghG,kBAAoBhhG,KAAKghG,kBAAkBlzG,KAAKkS,MACrDA,KAAKm/C,KAAKvb,YAAY+W,gCAClB36C,KAAKghG,mBAEThhG,KAAKihG,kCAAoCjhG,KAAKihG,kCAAkCnzG,KAAKkS,MACrFA,KAAKm/C,KAAKvb,YAAY+W,iBAA2B36C,KAAKihG,mCAEtDjhG,KAAKkhG,QAAU,IAAIC,IACfnhG,KACAwzB,EACA,CAAC/uB,EAASskC,KACN,IACI/oC,KAAKgpC,YACDvkC,EAASskC,GAAI,GACnB,MAAO/jC,GACLiZ,GAAO9Y,KAAK,+CAAgDH,GAASA,EAAMF,QAIlF9E,KAAKi6D,MACNj6D,KAAKi6D,IAAM,IAAIn3B,IAAI9iC,KAAMmH,GACzBnH,KAAKg+F,aAAaoD,qBAGtBphG,KAAKqhG,kBAAoB,IAAIC,IAAkBthG,MAE/CA,KAAKk+D,4BACC,IAAIvE,IACF35D,KAAKi6D,IACLj6D,KACA,CAKIq6D,eAAgB7mC,EAAO+tE,8BACvBnnC,kBAAmB5mC,EAAOguE,mCAEtCxhG,KAAKk+D,4BAA4BtzD,OAGjC,IAAI+U,GAAkB,EAuCtB,GArCI6T,EAAO01C,SAAW11C,EAAO01C,QAAQu4B,qBACjC9hF,EAAmC,IAAhBvU,KAAKC,UAAmBmoB,EAAO01C,QAAQu4B,oBAGzDzhG,KAAKmkB,aACNnkB,KAAKmkB,WAAa,IAAI/E,IAAWpf,KAAKqf,KAAM,CACxCN,UAAW/e,KAAKwgG,gBAChB1hF,SAAU0U,EAAOkuE,sBAAwBluE,EAAOkuE,sBAAwB1hG,KAAKk8D,WAC7Eh9C,OAAQsU,EAAOtU,QAAW,GAAElf,KAAKuU,WAAWpN,QAAQs1C,MAAM3hD,UAAUkF,KAAKmH,QAAQta,OACjFsyB,OAAQqU,EAAOrU,OACfe,gBAAiBsT,EAAOmuE,yBACxB/iF,YAAa4U,EAAO5U,YACpBC,gBAAiB2U,EAAO3U,gBACxBgB,iCAAkC2T,EAAO3T,iCACzCF,kBACAk9B,SAAU78C,KAAKmH,QAAQta,KACvBmyB,gBAAiBwU,EAAOxU,gBACxBC,mBAAoBuU,EAAOvU,qBAE/BG,IAAWyB,UAAUolB,uBAAuB,CACxC,eAAkBjmC,KAAKwgG,kBAIvBhtE,EAAO5S,wBACP5gB,KAAKmkB,WAAW7B,qBAAqBtiB,OAI7CA,KAAKg+F,aAAa4D,yBAIlB5hG,KAAKg+F,aAAa6D,2BAIdruE,EAAOsuE,uBAAyBhiF,IAAQyV,WAIxC,GAAI/B,EAAOwiE,mBAAoB,CAC3B/3E,GAAO/Y,KAAK,8DAEPlF,KAAK+hG,iBACN/hG,KAAK+hG,eAAiB,IAAIhM,IAAiB/1F,KAAMwzB,EAAOwiE,qBAG5D,MAAMgM,EAAwB,IAAIC,IAElCD,EAAsBrgF,GAAGugF,uBAAsC,IAC3DliG,KAAKwf,aAAa0D,KAAKP,qBAE3B3iB,KAAK+hG,eAAetL,uBAAuBuL,QAE3C/jF,GAAO9Y,KAAK,0FAMpB,GAAIquB,EAAO2uE,0BAA4BriF,IAAQyV,WAC3C,GAAI/B,EAAOwiE,mBAAoB,CACtBh2F,KAAK+hG,iBACN/hG,KAAK+hG,eAAiB,IAAIhM,IAAiB/1F,KAAMwzB,EAAOwiE,qBAG5D,MAAMoM,EAAoB,IAAIC,IAE9BD,EAAkBzgF,GAAGugF,mBAAkC,IACnDliG,KAAKwf,aAAa0D,KAAKP,cAE3B3iB,KAAK+hG,eAAetL,uBAAuB2L,QAE3CnkF,GAAO9Y,KAAK,0FAKhBquB,EAAO8uE,yBACPtiG,KAAKuiG,wBAA0B,IAAIC,IAAuBxiG,MAC1DA,KAAKuiG,wBAAwB5gF,GAAGugF,iBAAgC,KAC5DliG,KAAKwf,aAAa0D,KAAKP,oBAE3B3iB,KAAKuiG,wBAAwB5gF,GAAGugF,2BAA0CO,IACtEziG,KAAKwf,aAAa0D,KAAKP,2BAAgD8/E,MAK3E,iBAAkBjvE,GAClBxzB,KAAKipC,SAASzV,EAAOkvE,cAOzB1iG,KAAK2iG,aAAe,IAAIC,IAAqB5iG,MAG7CA,KAAK6iG,4BAA8B,IAAIC,IAA4B9iG,MAE/DwzB,GAAUA,EAAOkrB,gBAAkBlrB,EAAOkrB,eAAeqkD,YACzD/iG,KAAKgjG,4BACD,SAAUxvE,EAAOkrB,eAAeqkD,aAQ5CpF,GAAgBzvG,UAAUiV,KAAO,SAASw4C,GAClC37C,KAAKm/C,MACLn/C,KAAKm/C,KAAKh8C,KAAKw4C,GAAUjhB,KAAK,IAAM16B,KAAKijG,uBAYjDtF,GAAgBzvG,UAAUg1G,2BAA6B,SAAS/7F,GAC5D,OAAO+7F,IAA2Bz2G,KAAKuT,KAAhC,SACAmH,GADA,IAEH21C,iBAAkB6gD,GAAgB0C,oBAO1C1C,GAAgBzvG,UAAUi1G,SAAW,WACjC,OAAOnjG,KAAKm/C,MAAQn/C,KAAKm/C,KAAKikD,QAOlCzF,GAAgBzvG,UAAUm1G,aAAe,WACrC,OAAO1qE,QAAQ34B,KAAKmH,QAAQqsB,OAAO2qB,KAAOn+C,KAAKmH,QAAQqsB,OAAO2qB,IAAImrB,eAGxB,IAA5BtpE,KAAKmH,QAAQqsB,OAAO2qB,KAQtCw/C,GAAgBzvG,UAAUo1G,qBAAuB,WAC7C,OAAO3qE,QAAQ34B,KAAKmH,QAAQqsB,OAAO01C,SAC5BlpE,KAAKmH,QAAQqsB,OAAO01C,QAAQq6B,cAOvC5F,GAAgBzvG,UAAUs1G,MAAQ,WA2C9B,GA1CIxjG,KAAKk+D,8BACLl+D,KAAKk+D,4BAA4Bl7C,UACjChjB,KAAKk+D,4BAA8B,MAEnCl+D,KAAKi/F,sBACLj/F,KAAKi/F,oBAAoBj8E,UACzBhjB,KAAKi/F,oBAAsB,MAG3Bj/F,KAAKo/F,8BACLp/F,KAAKo/F,4BAA4Bp8E,UACjChjB,KAAKo/F,4BAA8B,MAGnCp/F,KAAKkhG,UACLlhG,KAAKkhG,QAAQt9E,OACb5jB,KAAKkhG,QAAU,MAGnBlhG,KAAKknC,iBAAiB93B,QAAQkvB,GAASt+B,KAAKyjG,oBAAoBnlE,IAEhEt+B,KAAKi6D,IAAIxxB,qBAETzoC,KAAK0jG,oCAED1jG,KAAKmkB,YACLnkB,KAAKmkB,WAAWnB,UAGpBhjB,KAAK2jG,mBAAqB3jG,KAAK2jG,kBAAkBhxC,SAG7C3yD,KAAKo+F,mBACLp+F,KAAKo+F,iBAAiB3hF,QACtBzc,KAAKo+F,iBAAmB,MAExBp+F,KAAK6/F,mBACL7/F,KAAK6/F,iBAAiBpjF,QACtBzc,KAAK6/F,iBAAmB,MAIxB7/F,KAAKm/C,KAAM,CACX,MAAMA,EAAOn/C,KAAKm/C,KAuBlB,OApBAA,EAAKr9B,eACD64B,yBACA36C,KAAK6gG,6BACT1hD,EAAKr9B,eACD64B,sBACA36C,KAAK8gG,0BACT3hD,EAAKr9B,eACD64B,yBACA36C,KAAK+gG,6BAET5hD,EAAKr9B,eACD64B,gCACA36C,KAAKghG,mBAET7hD,EAAKr9B,eAAe64B,iBAA2B36C,KAAKihG,mCAEpDjhG,KAAKg+F,aAAa4F,sBAElB5jG,KAAKm/C,KAAO,KAELA,EAAKqkD,QACP9oE,KAAK,KACE16B,KAAKi6D,KACLj6D,KAAKi6D,IAAIp2B,YAGhBzG,MAAMp4B,IAOH,MAHAhF,KAAKg9D,kBAAkB5tD,QACnBitD,GAAer8D,KAAK6jG,aAAaxnC,EAAYhf,WAE3Cr4C,IAKlB,OAAOg4B,QAAQE,OACX,IAAI7lC,MAAM,6CASlBsmG,GAAgBzvG,UAAU41G,uBAAyB,WAC/C,OAAO9jG,KAAKm9D,cAAgBn9D,KAAK6/F,iBAAmB7/F,KAAKo+F,kBAS7DT,GAAgBzvG,UAAU27E,kBAAoB,WAC1C,MAAMlsB,EAAW,GAKjB,OAHA39C,KAAKo+F,kBAAoBzgD,EAASz6C,KAAKlD,KAAKo+F,kBAC5Cp+F,KAAK6/F,kBAAoBliD,EAASz6C,KAAKlD,KAAK6/F,kBAErCliD,GAMXggD,GAAgBzvG,UAAU+mD,QAAU,WAChC,OAAOj1C,KAAKmH,QAAQta,MAMxB8wG,GAAgBzvG,UAAUmsD,cAAgB,WACtC,OAAOr6C,KAAKuU,YAMhBopF,GAAgBzvG,UAAU61G,cAAgB,WACtC,OAAO/jG,KAAKw+F,aAMhBb,GAAgBzvG,UAAU81G,WAAa,WACnC,OAAOrrE,QAAQ34B,KAAKikG,eAMxBtG,GAAgBzvG,UAAUg2G,aAAe,WACrC,OAAOlkG,KAAKikG,cAMhBtG,GAAgBzvG,UAAUi2G,sBAAwB,WAC9C,OAAOnkG,KAAKm/C,MAAQn/C,KAAKm/C,KAAKilD,UAAUD,yBAS5CxG,GAAgBzvG,UAAUm2G,mBAAqB,SAASC,GACpD,OAAO,IAAItnE,QAAQ,CAACC,EAASC,KACpBl9B,KAAKmkG,wBAKNG,EACAtkG,KAAKm/C,KAAKilD,UAAUG,iBAAiBtnE,EAASC,GAE9Cl9B,KAAKm/C,KAAKilD,UAAUI,YAAYvnE,EAASC,GAPzCA,OAiBZygE,GAAgBzvG,UAAUg5C,eAAiB,SAASvb,GAChD,IAAI0b,EAAS,GAMb,OAJIrnC,KAAKi6D,MACL5yB,EAASrnC,KAAKi6D,IAAI/yB,eAAevb,IAG9B0b,GAOXs2D,GAAgBzvG,UAAUi5C,mBAAqB,WAC3C,OAAOnnC,KAAKi6D,IAAMj6D,KAAKi6D,IAAI9yB,qBAAuB,MAOtDw2D,GAAgBzvG,UAAU84C,mBAAqB,WAC3C,OAAOhnC,KAAKi6D,IAAMj6D,KAAKi6D,IAAIjzB,qBAAuB,MAOtD22D,GAAgBzvG,UAAUu2G,oBAAsB,WAC5C,MAAO,CACHC,eAAgB1kG,KAAKmkB,WAAWrB,sBAaxC66E,GAAgBzvG,UAAUyzB,GAAK,SAASgjF,EAASpgG,GACzCvE,KAAKwf,cACLxf,KAAKwf,aAAamC,GAAGgjF,EAASpgG,IAYtCo5F,GAAgBzvG,UAAUugD,IAAM,SAASk2D,EAASpgG,GAC1CvE,KAAKwf,cACLxf,KAAKwf,aAAasC,eAAe6iF,EAASpgG,IAKlDo5F,GAAgBzvG,UAAUmuC,iBAAmBshE,GAAgBzvG,UAAUyzB,GACvEg8E,GAAgBzvG,UAAU+gD,oBAAsB0uD,GAAgBzvG,UAAUugD,IAQ1EkvD,GAAgBzvG,UAAU02G,mBAAqB,SAASC,EAAStgG,GACzDvE,KAAKm/C,MACLn/C,KAAKm/C,KAAK2lD,oBAAoBD,EAAStgG,IAS/Co5F,GAAgBzvG,UAAU62G,sBAAwB,SAASF,EAAStgG,GAC5DvE,KAAKm/C,MACLn/C,KAAKm/C,KAAK6lD,uBAAuBH,EAAStgG,IAUlDo5F,GAAgBzvG,UAAU+2G,gBAAkB,SACpCxgG,EAASygG,EAAc,QAC3B,GAAIllG,KAAKm/C,KAAM,CACX,MAAM0sC,GAAe7rF,KAAKm/C,KAAKgmD,gBAAgB,SAAW,IAAI53G,MAE9DyS,KAAKm/C,KAAKnW,YAAYvkC,EAASygG,EAAarZ,KAWpD8R,GAAgBzvG,UAAUk3G,uBAAyB,SAC3Cl+F,EAAIzC,EAASygG,EAAc,QAC3BllG,KAAKm/C,MACLn/C,KAAKm/C,KAAKkmD,mBAAmBn+F,EAAIzC,EAASygG,IASlDvH,GAAgBzvG,UAAUo3G,YAAc,SAASz4G,EAAMu2B,GAC/CpjB,KAAKm/C,MACLn/C,KAAKm/C,KAAKglC,cAAct3F,EAAMu2B,GAC9BpjB,KAAKm/C,KAAKzxC,gBAEVuQ,GAAO9Y,KAAK,iDAUpBw4F,GAAgBzvG,UAAUq3G,gBAAkB,SAAS14G,EAAMu2B,GACvDpjB,KAAKslG,YAAYz4G,EAAMu2B,GACvBpjB,KAAKwlG,cAAc34G,IAOvB8wG,GAAgBzvG,UAAUs3G,cAAgB,SAAS34G,GAC3CmT,KAAKm/C,MACLn/C,KAAKm/C,KAAKsmD,mBAAmB54G,IAQrC8wG,GAAgBzvG,UAAU89F,eAAiB,SAASn/F,GAC5CmT,KAAKm/C,OACLn/C,KAAKm/C,KAAKglC,cAAc,OAAQ,CAC5BlnF,WAAY,CAAEgJ,MAAO,mCACrB1Y,MAAOV,IAEXmT,KAAKm/C,KAAKzxC,iBAQlBiwF,GAAgBzvG,UAAUw3G,WAAa,SAASC,GACxC3lG,KAAKm/C,MAAQn/C,KAAK4lG,eAClB5lG,KAAKm/C,KAAKumD,WAAWC,IAQ7BhI,GAAgBzvG,UAAU23G,eAAiB,WACvC,QAAyBtlG,IAArBP,KAAK8lG,YAA2B,CAChC9lG,KAAK8lG,YAAc,IAAIC,IAGvB,MAAMC,EAAmBhmG,KAAKknC,eAAe3I,KAE7C,IAAK,MAAM6I,KAAc4+D,EACrBhmG,KAAK8lG,YAAY3nE,SAASiJ,GAI9B,MAAM8B,EAAoBlpC,KAAKi6D,IAAI1yB,gBAAgBhJ,KAEnD,IAAK,MAAMm+B,KAAexzB,EACtBlpC,KAAK8lG,YAAY3nE,SAASu+B,GAIlC,OAAO18D,KAAK8lG,aAQhBnI,GAAgBzvG,UAAU+3G,uBAAyB,WAC/C,OAAOjmG,KAAKm/C,KAAK+mD,qBAUrBvI,GAAgBzvG,UAAUiwC,SAAW,SAASG,GAC1C,GAAIA,EAAMqsC,eAAgB,CAEtB,MAAMw7B,EAAkBnmG,KAAKi6D,IAAIjzB,qBAEjC,GAAIm/D,EAGA,OAAI7nE,IAAU6nE,EACHnpE,QAAQC,QAAQqB,GAGpBtB,QAAQE,OAAO,IAAI7lC,MACtB,oDAKZ,OAAO2I,KAAKk2E,aAAa,KAAM53C,IAQnCq/D,GAAgBzvG,UAAUk4G,2BAA6B,SAC/Cz9D,EACArlB,GACJ,MAAM+iF,EAAYrmG,KAAKsmG,0BAOlBhjF,GAAO+iF,IAAc/iF,GACtBtjB,KAAKwf,aAAa0D,KACdP,4BACA3iB,KAAKk8D,WAAYvzB,IAQ7Bg1D,GAAgBzvG,UAAUq4G,qBAAuB,SAASjoE,GAStD,IAAIkoE,EAEJ,GATIxmG,KAAK4+F,gBAAkBtgE,EAAMuK,iBAAmBvK,EAAMggC,YACtDt+D,KAAK4+F,gBAAiB,EAGtB5+F,KAAKm/C,KAAKsnD,gBAAgBzmG,KAAKm/C,KAAKunD,WAAW,IAK/C1mG,KAAK6+F,kBAAmB,CACxB,MAAM8H,EAAUlrG,UAAQyI,mBAAmBlE,KAAK6+F,mBAEhD2H,EAAmBxmG,KAAK+8D,aAAa4pC,GAGzC3mG,KAAKwf,aAAa0D,KAAKP,qBAA0C2b,EAAOkoE,IAO5E7I,GAAgBzvG,UAAUu1G,oBAAsB,SAASnlE,GACrDA,EAAMk3D,eAAe,MACrBx1F,KAAKi6D,IAAIjyB,iBAAiB1J,GAC1BA,EAAM2Q,oBAAoB2tB,qBACtBt+B,EAAMsoE,aACVtoE,EAAM2Q,oBAAoB2tB,4BACtBt+B,EAAMuoE,mBAKNvoE,EAAMqsC,gBAAkBrsC,EAAME,YAAcC,IAAUC,SACtD1+B,KAAKmkB,WAAWY,wBAAuB,GAG3C/kB,KAAKwf,aAAa0D,KAAKP,gBAAqC2b,IAShEq/D,GAAgBzvG,UAAUsmG,YAAc,SAASl2D,GAC7C,OAAOt+B,KAAKk2E,aAAa53C,EAAO,OAYpCq/D,GAAgBzvG,UAAUgoF,aAAe,SAASC,EAAUC,GAExD,OAAID,GACIA,EAAS4O,UAKb3O,GACIA,EAAS2O,SALF/nD,QAAQE,OACX,IAAItX,IAAgBipB,sBAWzB7uC,KAAK8mG,gBAAgB3wB,EAAUC,GACjC17C,KAAK,KACEy7C,GACAn2E,KAAKyjG,oBAAoBttB,GAEzBC,GAGAp2E,KAAK+mG,eAAe3wB,GAGjBp5C,QAAQC,WAChBj4B,GAASg4B,QAAQE,OAAO,IAAI7lC,MAAM2N,MAe7C24F,GAAgBzvG,UAAU44G,gBAAkB,SAAS3wB,EAAUC,GAC3D,MAAM4wB,EAAuB,GAgB7B,OAdIhnG,KAAKo+F,iBACL4I,EAAqB9jG,KACjBlD,KAAKo+F,iBAAiBloB,aAAaC,EAAUC,IAEjDn4D,GAAO/Y,KAAK,0CAGZlF,KAAK6/F,iBACLmH,EAAqB9jG,KACjBlD,KAAK6/F,iBAAiB3pB,aAAaC,EAAUC,IAEjDn4D,GAAO/Y,KAAK,0CAGT83B,QAAQ+K,IAAIi/D,IAOvBrJ,GAAgBzvG,UAAU64G,eAAiB,SAAS3wB,GAChD,GAAIA,EAASvtC,gBAAmButC,EAASzL,gBAC9ByL,EAAS53C,YAAcC,IAAUC,QAAU,CAElD,MACMlF,EADUsJ,IAAI1B,oCAENnQ,KACNrkC,GACIA,EAAEwsC,OAAYg9C,EAASuP,WAAWvsD,KAAtB,SACLxsC,EAAE8sC,QAAU08C,EAASuP,WAAWjsD,OAE/CF,GACApa,IAAW+F,0BACP2d,IAAIzB,4BAA4B7H,IAGxC48C,EAASzL,iBACT3qE,KAAKwlG,cAAc,aACnBxlG,KAAKslG,YAAY,YAAa,CAC1B/3G,MAAO6oF,EAAS53C,UAChBvhC,WAAY,CACRgJ,MAAO,qCAInBjG,KAAKi6D,IAAIpzB,cAAcuvC,GAGnBA,EAASvtC,eACT7oC,KAAKm/C,KAAKzX,aAAa0uC,EAAS9X,WAEhCt+D,KAAKm/C,KAAK8nD,aAAa7wB,EAAS9X,WAGpC8X,EAASwwB,YAAc5mG,KAAKumG,qBAAqBz4G,KAAKkS,KAAMo2E,GAC5DA,EAASywB,kBAAoB7mG,KAAKomG,2BAA2Bt4G,KAAKkS,MAClEo2E,EAAS/5C,iBACLugC,qBACAwZ,EAASwwB,aACbxwB,EAAS/5C,iBACLugC,4BACAwZ,EAASywB,mBAEbzwB,EAASof,eAAex1F,MAExBA,KAAKwf,aAAa0D,KAAKP,cAAmCyzD,IAY9DunB,GAAgBzvG,UAAUmnG,uBAAyB,SAAS/2D,GACxD,MAAM4oE,EAAsB,GAkB5B,OAhBIlnG,KAAKo+F,iBACL8I,EAAoBhkG,KAAKlD,KAAKo+F,iBAAiBjnB,iBAAiB74C,IAEhErgB,GAAO/Y,KACH,uEAIJlF,KAAK6/F,iBACLqH,EAAoBhkG,KAAKlD,KAAK6/F,iBAAiB1oB,iBAAiB74C,IAEhErgB,GAAO/Y,KACH,uEAID83B,QAAQ+K,IAAIm/D,IAUvBvJ,GAAgBzvG,UAAUqnG,wBAA0B,SAASj3D,GACzD,MAAM6oE,EAAuB,GAiB7B,OAfInnG,KAAKo+F,iBACL+I,EAAqBjkG,KACjBlD,KAAKo+F,iBAAiB/mB,kBAAkB/4C,IAE5CrgB,GAAO/Y,KACH,+DAEJlF,KAAK6/F,iBACLsH,EAAqBjkG,KACjBlD,KAAK6/F,iBAAiBxoB,kBAAkB/4C,IAE5CrgB,GAAO/Y,KACH,+DAGD83B,QAAQ+K,IAAIo/D,IAOvBxJ,GAAgBzvG,UAAUk5G,QAAU,WAChC,OAAOpnG,KAAKm/C,KAAKsiB,MAUrBk8B,GAAgBzvG,UAAUm5G,SAAW,WACjC,OAAKrnG,KAAKuU,WAIH9Y,UAAQoI,iBAAiB7D,KAAKuU,WAAW8oC,YACxCr9C,KAAKmH,QAAQqsB,OAAO8zE,aAJjB,MAYf3J,GAAgBzvG,UAAU03G,YAAc,WACpC,OAAO5lG,KAAKm/C,KAAOn/C,KAAKm/C,KAAKymD,cAAgB,MAQjDjI,GAAgBzvG,UAAUq5G,KAAO,SAAS5rD,GACtC,OAAK37C,KAAK4lG,cAIH,IAAI5oE,QAAQ,CAACC,EAASC,KACzBl9B,KAAKm/C,KAAKqoD,SACN7rD,GAAY,GACZ,IAAM1e,IACN9sB,GAAO+sB,EAAO/sB,GACd,IAAM+sB,EAAOuqE,6BARVzqE,QAAQE,OAAO,IAAI7lC,MAAM,4BAgBxCsmG,GAAgBzvG,UAAUw5G,OAAS,WAC/B,OAAO1nG,KAAKunG,QAWhB5J,GAAgBzvG,UAAUy5G,kBAAoB,SAASx8E,GACnDnrB,KAAK4nG,mBAAmB,CAAEz8E,KAe9BwyE,GAAgBzvG,UAAU05G,mBAAqB,SAAS9rC,GACpD,IAAK1lE,MAAMC,QAAQylE,GACf,MAAM,IAAIzkE,MAAM,sDAGpB2I,KAAKi6D,IAAI10B,gBAAgBu2B,IAU7B6hC,GAAgBzvG,UAAU25G,eAAiB,SAAS18E,GAChDnrB,KAAKi6D,IAAIx0B,YAAYta,IAOzBwyE,GAAgBzvG,UAAU44C,SAAW,WACjC,OAAO9mC,KAAKi6D,IAAInzB,YAWpB62D,GAAgBzvG,UAAU+6C,SAAW,SAAS6+D,GAC1C,IAAKvuF,OAAOwuF,UAAUD,KAAWvuF,OAAOjB,SAASwvF,EAAO,IACpD,MAAM,IAAIzwG,MAAO,4BAA2BywG,GAEhD,MAAM/5G,EAAIwrB,OAAOuuF,GAEjB,GAAI/5G,GAAK,EACL,MAAM,IAAI4/C,WAAW,mCAMzB,GAJA3tC,KAAKi6D,IAAIhxB,SAASl7C,GAIdiS,KAAK6/F,iBAAkB,CACvB,MAAMmI,EAAsB,IAANj6G,EAEtBiS,KAAK6/F,iBACAnoB,wBAAuB,EAAMswB,GAC7B5qE,MAAMp4B,IACHiZ,GAAOjZ,MACF,2CAA0CgjG,KAC3ChjG,OAepB24F,GAAgBzvG,UAAUg3C,UAAY,SAAS/Z,GAC3C,OAAOnrB,KAAKi6D,IAAI/0B,UAAU/Z,IAO9BwyE,GAAgBzvG,UAAU8uE,gBAAkB,WACxC,OAAOhwE,OAAOo2B,OAAOpjB,KAAK+8D,eAS9B4gC,GAAgBzvG,UAAU+5G,oBACpB,SAASC,GAAc,GAErB,IAAInrC,EAAe/8D,KAAKg9D,kBAOxB,OALKkrC,IACDnrC,EAAeA,EAAarsC,OAAOtiC,IAAMA,EAAEi5G,aAIxCtqC,EAAaxmE,OAAS,GAQrConG,GAAgBzvG,UAAU+uE,mBAAqB,SAAS/1D,GACpD,OAAOlH,KAAK+8D,aAAa71D,IAO7By2F,GAAgBzvG,UAAUi6G,WAAa,SAASjhG,GAC5C,MAAMm1D,EAAcr8D,KAAKi9D,mBAAmB/1D,GAEvCm1D,GAGLr8D,KAAKm/C,KAAKipD,eAAe/rC,EAAYhf,SAAU,UAOnDsgD,GAAgBzvG,UAAUm6G,gBAAkB,SAASnhG,GACjD,MAAMm1D,EAAcr8D,KAAKi9D,mBAAmB/1D,GAEvCm1D,GAGLr8D,KAAKm/C,KAAKmpD,KAAKjsC,EAAYhf,WAQ/BsgD,GAAgBzvG,UAAUq6G,qBAAuB,WACzCvoG,KAAKwoG,0BACGxoG,KAAKo+F,kBAAoBp+F,KAAKioG,sBAAwB,KAC9D/7G,OAAOuhB,aAAazN,KAAKwoG,yBACzBxoG,KAAKwoG,wBAA0B,OASvC7K,GAAgBzvG,UAAU+0G,mBAAqB,YAEtCjjG,KAAKo+F,kBACCp+F,KAAKioG,uBAAyB,IAC7BjoG,KAAKwoG,0BACbxoG,KAAKwoG,wBAA0Bt8G,OAAOqe,WAAW,KAC7CvK,KAAKwoG,wBAA0B,KAC/BppF,IAAWoI,cAAciE,YACrBjC,IACA,CACI20B,KAAK,EACL5wD,MAn4CM,eA64C1BowG,GAAgBzvG,UAAUu4G,gBAAkB,SAASv/F,GACjD,MAAMm1D,EAAcr8D,KAAKi9D,mBAAmB/1D,GAEvCm1D,GAGLr8D,KAAKm/C,KAAKsnD,gBAAgBpqC,EAAYhf,UAAU,IAoBpDsgD,GAAgBzvG,UAAUu6G,eAAiB,SACnC9kG,EAAK+kG,EAAMjnC,EAAM4lC,EAAUsB,EAAS54F,EAAQmrC,EAAU0tD,GAC1D,MAAM1hG,EAAKzL,UAAQyI,mBAAmBP,GAEtC,GAAW,UAAPuD,GAAkBlH,KAAKk8D,aAAeh1D,EACtC,OAGJ,MAAMm1D,EACA,IAAIwsC,IAAiBllG,EAAK3D,KAAM0oG,EAAMrB,EAAUsB,EAAS54F,EAAQmrC,GAEvEmhB,EAAYysC,MAAQrnC,EACpBpF,EAAY0sC,SAAWH,EACvB5oG,KAAK+8D,aAAa71D,GAAMm1D,EACxBr8D,KAAKwf,aAAa0D,KACdP,cACAzb,EACAm1D,GAEJr8D,KAAKgpG,gBAAgB3sC,GAErBr8D,KAAKipG,uBACLjpG,KAAKijG,sBAWTtF,GAAgBzvG,UAAU86G,gBAAkB,SAAS3sC,GACjDA,EAAYwnB,cACPnpD,KAAK5xB,IACFuzD,EAAY6sC,cAAgBpgG,EAASib,IAAI,0BACzC/jB,KAAKmpG,oBAEDrgG,EAASib,IAAI,qCACbs4C,EAAY+sC,YAAY,mBAAmB,GAG3CtgG,EAASib,IAAI,gCACbs4C,EAAY+sC,YAAY,iBAAiB,KAGhDhsE,MAAM,KAAM,IASrBugE,GAAgBzvG,UAAUm7G,wBAA0B,SAAS1lG,EAAKilG,GAI9D,MACMU,EADQtpG,KAAKg9D,kBACU/rC,KAAK7iC,GAAKA,EAAEivD,WAAa15C,GAEtD,GAAI2lG,EAAgB,CAChBA,EAAeP,SAAWH,EAC1B,MAAM1hG,EAAKzL,UAAQyI,mBAAmBP,GAEtC3D,KAAKwf,aAAa0D,KACdP,mBACAzb,EACA0hG,GAOHU,EAAeP,UAChB/oG,KAAKipG,wBAIbtL,GAAgBzvG,UAAU21G,aAAe,SAASlgG,GAC9C,MAAMuD,EAAKzL,UAAQyI,mBAAmBP,GAEtC,GAAW,UAAPuD,GAAkBlH,KAAKk8D,aAAeh1D,EACtC,OAGJ,MAAMm1D,EAAcr8D,KAAK+8D,aAAa71D,UAE/BlH,KAAK+8D,aAAa71D,GAEHlH,KAAKi6D,IAAI/xB,mBAAmBhhC,GAEpCkI,QACVkvB,GACIt+B,KAAKwf,aAAa0D,KAAKP,gBAAqC2b,IAGhE+9B,GACAr8D,KAAKwf,aAAa0D,KACdP,YAAiCzb,EAAIm1D,GAG7Cr8D,KAAKipG,sBAAqB,GAC1BjpG,KAAKuoG,wBAYT5K,GAAgBzvG,UAAUq7G,eAAiB,SAASC,EAAgB7C,EAAS8C,GAIzE,GAAI9C,IAAY3mG,KAAKk8D,WACjB,OAGJ,MAAMsqC,EAAmBxmG,KAAK+8D,aAAa4pC,GAE3C,GAAI6C,EAMA,OALAxpG,KAAKwf,aAAa0D,KACdP,SAA8B6jF,QAElCxmG,KAAKwjG,QAKT,MAAMkG,EAAoB1pG,KAAK+8D,aAAa0sC,GAE5CzpG,KAAKwf,aAAa0D,KACdP,qBAA0C6jF,EAAkBkD,IAOpE/L,GAAgBzvG,UAAUy7G,mBAAqB,SAASloC,GAEpDzhE,KAAKwf,aAAa0D,KACdP,oBAAyC3iB,KAAKk8D,WAAYuF,IAGlEk8B,GAAgBzvG,UAAU07G,kBAAoB,SAASjmG,EAAK89D,GACxD,MAAMv6D,EAAKzL,UAAQyI,mBAAmBP,GAChC04D,EAAcr8D,KAAKi9D,mBAAmB/1D,GAEvCm1D,IAGLA,EAAYysC,MAAQrnC,EACpBzhE,KAAKwf,aAAa0D,KAAKP,oBAAyCzb,EAAIu6D,KAGxEk8B,GAAgBzvG,UAAU27G,qBAAuB,SAASlmG,EAAKkoF,GAC3D,MAAM3kF,EAAKzL,UAAQyI,mBAAmBP,GAChC04D,EAAcr8D,KAAKi9D,mBAAmB/1D,GAEvCm1D,GAIDA,EAAYytC,eAAiBje,IAIjCxvB,EAAYytC,aAAeje,EAC3B7rF,KAAKwf,aAAa0D,KACdP,uBACAzb,EACA2kF,KAUR8R,GAAgBzvG,UAAUotE,mBAAqB,SAASh9B,GACpD,GAAIA,EAAMwH,QAAU9lC,KAAKm9D,cAIrB,YAHAl/C,GAAO/Y,KACH,6DAGD,IAAKo5B,EAAMwH,OAAS9lC,KAAKm9D,cAI5B,YAHAl/C,GAAO/Y,KACH,yDAKR,MAAMgC,EAAKo3B,EAAMq+B,mBACXN,EAAcr8D,KAAKi9D,mBAAmB/1D,GAE5C,IAAKm1D,EAGD,YAFAp+C,GAAOjZ,MAAO,gCAA+BkC,GAMjDm1D,EAAY0tC,QAAQ7mG,KAAKo7B,GAErBt+B,KAAK8lG,aACL9lG,KAAK8lG,YAAY3nE,SAASG,GAG9B,MAAMqN,EAAU3rC,KAAKwf,aAErB8e,EAAMjC,iBACFugC,qBACA,IAAMjxB,EAAQzoB,KAAKP,qBAA0C2b,IACjEA,EAAMjC,iBACFugC,4BACA,CAACj0B,EAAYrlB,KACStjB,KAAKsmG,4BAELhjF,GACdqoB,EAAQzoB,KACJP,4BACAzb,EACAyhC,KAKhBgD,EAAQzoB,KAAKP,cAAmC2b,IAUpDq/D,GAAgBzvG,UAAU87G,eAAiB,SAAS/xF,EAAS+9D,GACrDh2E,KAAK6/F,mBAAqB5nF,IAC1BgG,GAAO/Y,KAAK,iBAEZlF,KAAK6/F,iBAAiB5tB,UAAU+D,GAChCh2E,KAAKwf,aAAa0D,KAAKP,yBAA8C3iB,KAAK6/F,oBAYlFlC,GAAgBzvG,UAAU+7G,gBAAkB,SAAShyF,EAASiyF,GACtDlqG,KAAK6/F,mBAAqB5nF,IAC1BgG,GAAO/Y,KAAK,wBACZlF,KAAK6/F,iBAAiBpvB,iBAAiBy5B,KAU/CvM,GAAgBzvG,UAAUstE,qBAAuB,SAAS2uC,GACtDnqG,KAAKg9D,kBAAkB5tD,QAAQitD,IAC3B,MAAMh1B,EAASg1B,EAAYv8B,YAE3B,IAAK,IAAIxzC,EAAI,EAAGA,EAAI+6C,EAAO9wC,OAAQjK,IAC/B,GAAI+6C,EAAO/6C,KAAO69G,EAAc,CAG5B9tC,EAAY0tC,QAAQ9lG,OAAO3X,EAAG,GAE9B0T,KAAKwf,aAAa0D,KACdP,gBAAqCwnF,GAErCnqG,KAAK8lG,aACL9lG,KAAK8lG,YAAYtR,YAAY2V,GAGjC,QAGTnqG,OAMP29F,GAAgBzvG,UAAUk8G,mBAAqB,SACvCC,EACA94B,GAEJ,IAAI+4B,EAECxqF,IAAQyqF,cAMDvqG,KAAKqjG,gBAAmBrjG,KAAKsjG,uBAM9BtjG,KAAK6/F,iBAEZyK,EAAe,CACX96F,OAAQ,OACRwkE,kBAAmB,0BACnBtb,SAAU,qCAEN14D,KAAKwqG,uBACbF,EAAe,CACX96F,OAAQ,UACRwkE,kBAAmB,2BACnBtb,SAAU,kEAEdt5C,IAAWoI,cAAciE,YAAkB7B,OAlB3C0gF,EAAe,CACX96F,OAAQ,UACRwkE,kBAAmB,eACnBtb,SAAU,0CATd4xC,EAAe,CACX96F,OAAQ,2BACRwkE,kBAAmB,oBACnBtb,SAAU,gDAwBd4xC,EACAtqG,KAAKyqG,oBAAoBJ,EAAeC,GAExCtqG,KAAK0qG,uBAAuBL,EAAe94B,IAOnDosB,GAAgBzvG,UAAUy8G,eAAiB,SACnCN,EACA94B,EACAp9D,GAEJ,GAAIk2F,EAAcvkE,MACd9lC,KAAKoqG,mBAAmBC,EAAe94B,OACpC,CACH,IAAKvxE,KAAKm/C,KAAKyrD,QAAQP,EAAcn/B,WAAY,CAC7C,MAAMpI,EAAc,6CASpB,YAPA9iE,KAAKyqG,oBACDJ,EAAe,CACX76F,OAAQ,iBACRwkE,kBAAmBlR,EACnBpK,SAAUoK,IAKtB9iE,KAAK6qG,uBAAuBR,EAAe94B,EAAap9D,KAOhEwpF,GAAgBzvG,UAAU28G,uBAAyB,SAC3CR,EACA94B,EACAp9D,GAGJnU,KAAKo+F,iBAAmBiM,EACxBrqG,KAAKm/C,KAAKrG,gBAAgB,oBAAsB3kC,EAChDnU,KAAKihG,oCAEDjhG,KAAK8+F,YACL1/E,IAAWgI,oBACPqE,YAAkBpC,IAAuB,CAAE80B,KAAK,KAGxD,MAAM2sD,EACAr4D,EAAE8+B,GACCtgD,KAAK,4DACL1vB,KAAK,UAEdvB,KAAKwf,aAAa0D,KACdP,wBACAmoF,GAEJ9qG,KAAKuoG,uBACLnpF,IAAWoI,cAAciE,YACrBlC,IACA,CACI40B,KAAK,EACL5wD,MAAO4mB,KAGf,IACIk2F,EAAc3jD,WAAW1mD,KAAKm/C,KAAMn/C,KAAKi6D,IAAzC,SACOj6D,KAAKmH,QAAQqsB,QADpB,IAEI0S,wBAAyBlmC,KAAK+qG,oBAEpC,MAAO/lG,GACL4yB,IAAqBC,iBAAiB7yB,GAI1ChF,KAAKgrG,kBAAkBz5B,EAAa84B,EAAcrpF,gBAGlD,MAAMgiB,EAAchjC,KAAKknC,iBAEzB,IACImjE,EAAc/4B,YACVC,EACA,KAIQvxE,KAAKm9D,eAAiBn9D,KAAKo+F,kBAC3Bp+F,KAAKirG,wCAGTjrG,KAAKwf,aAAa0D,KACdP,yBACA0nF,GACCrqG,KAAKm9D,eACNn9D,KAAKwf,aAAa0D,KACdP,gCACA0nF,IAGZrlG,IACI4yB,IAAqBC,iBAAiB7yB,GACtCiZ,GAAOjZ,MACH,2CAA4CA,IAEpDg+B,GAOJ/kB,GAAO/Y,KAAK,4CACZlF,KAAKmkB,WAAWN,eACZ7jB,KAAKo+F,iBAAiBp9E,eACtB,SACJhhB,KAAKmkB,WAAWpD,iBAAiB/gB,KAAKo+F,iBAAiBp9E,gBACzD,MAAO3nB,GACLu+B,IAAqBC,iBAAiBx+B,GACtC4kB,GAAOjZ,MAAM3L,KAarBskG,GAAgBzvG,UAAU88G,kBAAoB,SAAS31B,EAAS/vB,GAC5D,IAAIjhB,EAAQ,KACZ,MAAM6mE,EACAz4D,EAAE4iC,GACCpkD,KAAK,iCACLw+D,QAMT,IAAI0b,EAEJ,OANyB,IAArBD,EAAU30G,SACV8tC,EAAQ6mE,EAAU,GAAGtoG,aAAa,QAK9B5C,KAAKmH,QAAQqsB,OAAO43E,mBAC5B,IAAK,cACL,KAAK,EACL,UAAK7qG,EACD4qG,EAAoB,cACpB,MACJ,IAAK,YACDA,EAAoB,YAIE,gBAAtBA,EACAnrG,KAAKi6D,IAAI71B,wBAAwBkhB,EAAI,MACR,cAAtB6lD,GAAqC9mE,GAC5CrkC,KAAKi6D,IAAI71B,wBAAwB,KAAMC,IAgB/Cs5D,GAAgBzvG,UAAUu8G,oBAAsB,SACxCJ,EACAljG,GACAA,GAAWA,EAAQuxD,UACnB9gC,IAAqBC,iBAAiB,IAAIxgC,MAAM8P,EAAQuxD,WAI5D2xC,EAAcx2B,UACV,KACA7uE,IACIiZ,GAAO9Y,KACH,qEACiCH,IACtC,CACCwK,OAAQrI,GAAWA,EAAQqI,OAC3BwkE,kBAAmB7sE,GAAWA,EAAQ6sE,kBACtCF,sBAAsB,KAclC6pB,GAAgBzvG,UAAUi3C,YAAc,SAChCklE,EACAj2B,EACAC,GACJp2D,GAAO/Y,KACF,eAAckvE,OAAqBC,UAChCg2B,EAAcvkE,SAClBukE,IAAkBrqG,KAAKo+F,kBACvBp+F,KAAK8+F,YAAa,EAElB1/E,IAAWoI,cACPiE,YAAkBhC,IAAyB,CAAE00B,KAAK,KAGlDn+C,KAAKmkB,aACLnkB,KAAKmkB,WAAWlD,gBACZjhB,KAAKo+F,iBAAiBp9E,gBAC1B/C,GAAO/Y,KAAK,0BACZlF,KAAKmkB,WAAWd,cACZrjB,KAAKo+F,iBAAiBp9E,iBAI9BhhB,KAAKo+F,iBAAmB,KAGxBp+F,KAAKi6D,IAAI90B,eACFklE,IAAkBrqG,KAAK6/F,kBAGN,YAApBzrB,GAAgD,iBAAfC,GACjCp2D,GAAO/Y,KAAK,6BACZka,IAAWyB,UAAUolB,uBAAuB,CAAEolE,aAAa,KAChC,uBAApBj3B,GACW,eAAfC,GAIHj1D,IAAWyB,UAAUolB,uBAAuB,CAAEqlE,WAAW,IAE7DtrG,KAAKurG,mBAELttF,GAAOjZ,MACH,2CACAqlG,EAAch+F,IACdg+F,EAAcn/B,UACdkJ,EACAC,IAQZspB,GAAgBzvG,UAAUs9G,kBAAoB,SAASnB,GAC9CA,EAAcvkE,QACf9lC,KAAKwjG,QACLxjG,KAAKwf,aAAa0D,KAAKP,sBAI/Bg7E,GAAgBzvG,UAAUi7G,kBAAoB,WAC1C,IAAI5K,GAAuB,EAC3B,MAAMxhC,EAAe/8D,KAAKg9D,kBAG1B,IAAK,IAAI1wE,EAAI,EAAGA,EAAIywE,EAAaxmE,OAAQjK,GAAK,EAC1C,GAAIywE,EAAazwE,GAAGm/G,eAAgB,CAChClN,GAAuB,EACvB,MAGJA,IAAyBv+F,KAAKu+F,uBAC9Bv+F,KAAKu+F,qBAAuBA,EAC5Bv+F,KAAKwf,aAAa0D,KACdP,uBACA47E,KASZZ,GAAgBzvG,UAAUw9G,gBAAkB,WACxC,OAAO1rG,KAAKu+F,sBAOhBZ,GAAgBzvG,UAAUguE,SAAW,WACjC,OACIl8D,KAAKm/C,MAAQn/C,KAAKm/C,KAAKunD,UACjBjrG,UAAQyI,mBAAmBlE,KAAKm/C,KAAKunD,WACrC,MAGd/I,GAAgBzvG,UAAUy9G,UAAY,SAASC,EAAOC,EAAU9gG,GAC5D,MAAM+gG,EAAiB9rG,KAAKsmG,0BAExBwF,EACAA,EAAeH,UAAUC,EAAOC,EAAU9gG,GAE1CkT,GAAO9Y,KAAK,yCAWpBw4F,GAAgBzvG,UAAU69G,eAAiB,SAAS5kG,GAChD,OAAInH,KAAKm/C,KACEn/C,KAAKggG,iBAAiB+L,eAAe5kG,GAGzC61B,QAAQE,OAAO,IAAI7lC,MAAM,wCAUpCsmG,GAAgBzvG,UAAU89G,cAAgB,SAASjlC,GAC/C,OAAI/mE,KAAKm/C,KACEn/C,KAAKggG,iBAAiBgM,cAAcjlC,GAGxC/pC,QAAQE,OAAO,IAAI7lC,MAAM,wCAMpCsmG,GAAgBzvG,UAAU+9G,sBAAwB,WAC9C,QAAIjsG,KAAKm/C,MACEn/C,KAAKm/C,KAAK8sD,yBAUzBtO,GAAgBzvG,UAAUuvD,KAAO,SAAShM,GACtC,OAAIzxC,KAAKm/C,KACEn/C,KAAKm/C,KAAK1B,KAAKhM,GAGnB,IAAIzU,QAAQ,CAACC,EAASC,KACzBA,EAAO,IAAI7lC,MAAM,0CAOzBsmG,GAAgBzvG,UAAUg+G,OAAS,WAC/B,OAAIlsG,KAAKm/C,KACEn/C,KAAKm/C,KAAK+sD,SAGd,IAAIlvE,QAAQ,CAACC,EAASC,KACzBA,EAAO,IAAI7lC,MAAM,0CAOzBsmG,GAAgBzvG,UAAUi+G,iBAAmB,WACzC,OAAOnsG,KAAKy9C,KAAK,0BAOrBkgD,GAAgBzvG,UAAUk+G,gBAAkBzO,GAAgBzvG,UAAUg+G,OAKtEvO,GAAgBzvG,UAAUm+G,eAAiB,WACvC,OAAIrsG,KAAKm/C,KACEn/C,KAAKm/C,KAAKktD,iBAGd,MAMX1O,GAAgBzvG,UAAUo+G,YAAc,WACpC,OAAItsG,KAAKm/C,KACEn/C,KAAKm/C,KAAKmtD,cAGd,MAQX3O,GAAgBzvG,UAAUq+G,mBAAqB,WAC3C,GAAIvsG,KAAKm/C,KACL,OAAOn/C,KAAKm/C,KAAKqtD,gBAYzB7O,GAAgBzvG,UAAUo4G,wBAA0B,WAChD,OAAItmG,KAAKm9D,cACEn9D,KAAK6/F,iBAAiB7+E,eAG1BhhB,KAAKo+F,iBAAmBp+F,KAAKo+F,iBAAiBp9E,eAAiB,MAW1E28E,GAAgBzvG,UAAUmrF,mBAAqB,WAC3C,MAAMyyB,EAAiB9rG,KAAKsmG,0BAE5B,OAAOwF,EAAiBA,EAAezyB,qBAAuB,MASlEskB,GAAgBzvG,UAAUu+G,oBAAsB,SAASC,GAChD1sG,KAAK4lG,gBAGV5lG,KAAK2+F,iBAAmB+N,EACxB1sG,KAAKm/C,KAAKglC,cAAc,aAAc,CAClClnF,WAAY,CACRo4B,MAAOq3E,EAAOr3E,MACdpB,MAAOy4E,EAAOz4E,MACdhuB,MAAO,0CAGfjG,KAAKm/C,KAAKzxC,iBAOdiwF,GAAgBzvG,UAAUy+G,oBAAsB,WAC5C,OAAO3sG,KAAK2+F,kBAMhBhB,GAAgBzvG,UAAU0+G,kBAAoB,WAC1C,OAAO5sG,KAAKy+F,iBAMhBd,GAAgBzvG,UAAU2+G,kBAAoB,WAC1C,OAAO7sG,KAAK0+F,iBAMhBf,GAAgBzvG,UAAU0vG,mBAAqB,WAC3C,OAAO59F,KAAKm/C,KAAKrG,iBAMrB6kD,GAAgBzvG,UAAU80G,4BAA8B,SAASn2G,EAAMU,GACnEyS,KAAKslG,YAAa,qBAAoBz4G,EAAQ,CAAEU,WAMpDowG,GAAgBzvG,UAAU4+G,+BAAiC,SAASjgH,GAChEmT,KAAKwlG,cAAe,qBAAoB34G,GACxCmT,KAAKm/C,KAAKzxC,gBASdiwF,GAAgBzvG,UAAU6+G,4BAA8B,SAASlgH,GAC7D,MAAMoB,EAAW+R,KAAKm/C,KAAK6tD,QAAQC,MAAMh8E,KAAKi8E,GAC1CA,EAAK/sG,UAAa,qBAAoBtT,GAG1C,OAAOoB,EAAWA,EAASV,WAAQgT,GAWvCo9F,GAAgBzvG,UAAU04B,aAAe,SACjCumF,EACAC,GACJ,OAAOptG,KAAKmkB,WAAWyC,aAAaumF,EAAiBC,IAUzDzP,GAAgBzvG,UAAUq2B,mBAAqB,WAC3C,OAAOvkB,KAAKmkB,WAAWI,sBAS3Bo5E,GAAgBzvG,UAAUm/G,eAAiB,SAAS/uE,GAAO,MACvD,OAAOA,EAAM/Y,UAAN,UAAkBvlB,KAAKsmG,iCAAvB,aAAkB,EAAgCgH,aAAahvE,GAASA,EAAMivE,WASzF5P,GAAgBzvG,UAAU+3F,eAAiB,SAAS3nD,EAAO0nD,GACvD,MAAMzgE,EAAU+Y,EAAM/Y,UACtB,IAAIP,EAAO,KACX,MAAM8gB,EAAQxH,EAAMwH,MACd0nE,EAAe1nE,EAAQxH,EAAMq+B,mBAAqB,QAClDmvC,EACAhmE,EACI9lC,KAAK6/F,kBAAoB7/F,KAAK6/F,iBAAiB7+E,eAC/ChhB,KAAKo+F,kBAAoBp+F,KAAKo+F,iBAAiBp9E,eAErDuE,EAEIumF,IACA9mF,EAAO8mF,EAAewB,aAAahvE,IAGvCtZ,EAAOsZ,EAAMivE,UAEZvnB,EAAU9+E,IAAO8d,GAAS8mF,GAI/B9rG,KAAKmkB,WAAWmB,4BACZwmF,EACA9mF,EACAO,EACAioF,EACAlvE,EAAMwnD,gBACNE,EAAU9+E,KAUlBy2F,GAAgBzvG,UAAUy4B,mBAAqB,SAASliB,GACpD2a,IAAWkH,QAAQ7hB,IAWvBk5F,GAAgBzvG,UAAUu/G,SAAW,SAASC,GAC1C,OAAO1tG,KAAKm/C,KAAOn/C,KAAKm/C,KAAKyrD,QAAQ8C,GAAU,MAMnD/P,GAAgBzvG,UAAUy/G,+BAAiC,WACvD3tG,KAAKwf,aAAa0D,KAAKP,oBACnB8kF,iCAWR9J,GAAgBzvG,UAAU0/G,oBAAsB,SAAS7kE,EAAItW,GACzDzyB,KAAKi6D,IAAInxB,mBAAmBC,EAAItW,IASpCkrE,GAAgBzvG,UAAU2/G,yBAA2B,SAASp7E,GAC1DzyB,KAAK4tG,oBAAoB,GAAIn7E,IAiBjCkrE,GAAgBzvG,UAAU86C,YAAc,SAChCvkC,EACAskC,EAAK,GACL+kE,GAAyB,GAC7B,MAAMC,SAAqBtpG,EAK3B,GAAoB,WAAhBspG,IACQD,GAA0C,WAAhBC,EAMtC,GAAID,EACA9tG,KAAK4tG,oBAAoB7kE,EAAItkC,OAC1B,CACH,IAAIupG,EAAgBvpG,EAIhBygG,EAAc,OAElB,GAAoB,WAAhB6I,EAA0B,CAC1B7I,EAAc,eAGT8I,EAAc7/G,eAAeuqD,OAC9Bs1D,EAAct1D,KAAuB,IAGzC,IACIs1D,EAAgBphG,KAAKwL,UAAU41F,GACjC,MAAO30G,GAGL,YAFA4kB,GAAOjZ,MAAM,6CAA8C3L,IAM/D0vC,EACA/oC,KAAKolG,uBAAuBr8D,EAAIilE,EAAe9I,GAG/CllG,KAAKilG,gBAAgB+I,EAAe9I,QAnCxCjnF,GAAOjZ,MAAO,kCAAiC+oG,IAyCvDpQ,GAAgBzvG,UAAU+/G,wBAA0B,WAChD,OAAOjuG,KAAKm9D,cACNn9D,KAAK4/F,2BAA6B5/F,KAAKs/F,4BAQjD3B,GAAgBzvG,UAAU2yG,4BAA8B,SAAS5oF,GACzDA,EAAQ6tB,MACR9lC,KAAK4/F,4BAA6B,EAElC5/F,KAAKs/F,4BAA6B,EAElCrnF,EAAQ6tB,QAAU9lC,KAAKm9D,eACvBn9D,KAAKwf,aAAa0D,KAAKP,2BAS/Bg7E,GAAgBzvG,UAAUggH,uBAAyB,SAASj2F,GAGpDA,EAAQ6tB,OAGR1mB,IAAWyB,UAAUolB,uBAAuB,CAAEqlE,WAAW,IAErDtrG,KAAK6/F,kBACLzgF,IAAWgI,oBACPwE,YACI9B,IACA,CACI+8C,UAAW7mE,KAAK6/F,iBAAiBz0B,eAIjDprE,KAAKurG,gBAAgB,qBAAsB,eACpCtzF,GAAWjY,KAAKo+F,mBAAqBnmF,IAC5CjY,KAAK2jG,kBAAoB,IAAIwK,IAAkBnuG,MAC/CA,KAAK2jG,kBAAkB5mF,MAAM9E,KASrC0lF,GAAgBzvG,UAAU4yG,yBAA2B,SAAS7oF,GACtDA,EAAQ6tB,MACR9lC,KAAK4/F,4BAA6B,GAElC5/F,KAAKs/F,4BAA6B,EAClCt/F,KAAK2jG,mBAAqB3jG,KAAK2jG,kBAAkBhxC,UAGjD16C,EAAQ6tB,QAAU9lC,KAAKm9D,eACvBn9D,KAAKwf,aAAa0D,KAAKP,wBAU/Bg7E,GAAgBzvG,UAAUw8G,uBAAyB,SAC3CL,EACA94B,GACJvxE,KAAK4/F,4BAA6B,EAGlC5/F,KAAK6/F,iBAAmBwK,EACxBrqG,KAAKihG,oCAELjhG,KAAK6/F,iBAAiBn5C,WAClB1mD,KAAKm/C,KACLn/C,KAAKi6D,IAFT,SAGWj6D,KAAKmH,QAAQqsB,QAHxB,IAIQ0S,wBAAyBlmC,KAAK+qG,oBAGtC9sF,GAAO/Y,KAAK,4CAEZ,IAAIkpG,EAAW3yG,UAAQyI,mBAAmBlE,KAAK6/F,iBAAiB30B,WAEhE,MAAM7O,EAAcr8D,KAAK+8D,aAAaqxC,GAElC/xC,IACA+xC,EAAW/xC,EAAYgyC,cAAgBD,GAG3CpuG,KAAKmkB,WAAWN,eACZ7jB,KAAK6/F,iBAAiB7+E,eACtBotF,GAEJ,MAAMprE,EAAchjC,KAAKknC,iBAEzBlnC,KAAK6/F,iBAAiBvuB,YAClBC,EACA,KACItzD,GAAOhZ,MAAM,uCAEbjF,KAAKwf,aAAa0D,KACdP,yBACA3iB,KAAK6/F,mBAEb76F,IACIiZ,GAAOjZ,MACH,+CAAgDA,IAExDg+B,IAOR26D,GAAgBzvG,UAAUogH,oBAAsB,WAC5CtuG,KAAKuuG,iBACD,MAAOvuG,KAAKo+F,iBAAiBp9E,eAAeumB,oBAOpDo2D,GAAgBzvG,UAAUsgH,oBAAsB,WAC5CxuG,KAAKuuG,iBACD,MAAOvuG,KAAK6/F,iBAAiB7+E,eAAeumB,oBAUpDo2D,GAAgBzvG,UAAUqgH,iBAAmB,SAASE,EAASjnE,GAC3D,IAAK,MAAMlJ,KAASkJ,EAChBvpB,GAAO/Y,KAAM,iBAAgBupG,YAAkBnwE,KAC/Ct+B,KAAKs7D,mBAAmBh9B,IAWhCq/D,GAAgBzvG,UAAU6yG,4BAA8B,SAChDsJ,GAC0B,OAA1BrqG,KAAK6/F,mBAIL7/F,KAAK0uG,yBACC1uG,KAAK6/F,iBAAiBvzB,uBAGF,OAA1BtsE,KAAKo+F,mBACLp+F,KAAK2uG,yBACC3uG,KAAKo+F,iBAAiB9xB,uBAGhC,IAAIsiC,GAAO,EACX,MAAMC,EAAmB7uG,KAAKmH,QAAQqsB,OAAOq7E,iBAmB7C,GAhBKxE,EAAcvkE,MAER9lC,KAAK6/F,mBAAqBwK,GACjCpsF,GAAOjZ,MAAM,0DAEb4pG,GAAO,IACCvE,EAAcj/B,aACS,iBAArByjC,GACPzjG,KAAKC,SAAWwjG,IACnB5wF,GAAO/Y,KAAM,+BAA8B2pG,SAC3CzvF,IAAWyB,UAAUolB,uBAAuB,CAAEolE,aAAa,IAC3DrrG,KAAKurG,gBAAgB,UAAW,gBAEhCqD,GAAO,GAZPA,GAAO,GAeNl1F,MAAM1Z,KAAK0uG,4BACRh1F,MAAM1Z,KAAK2uG,0BAA2B,CAC1C,MAAMG,EACA9uG,KAAK0uG,yBAA2B1uG,KAAK2uG,yBAE3CvvF,IAAWoI,cACP2C,IACA,CAAE58B,MAAOuhH,IAGbzE,EAAcvkE,QAAU9lC,KAAKm9D,eAC7Bn9D,KAAKwf,aAAa0D,KAAKP,0BAGvBisF,IAMJ5uG,KAAK+uG,eAAc,GAGf/uG,KAAKo+F,iBACLp+F,KAAKgvG,yBAEL/wF,GAAO/Y,KAAK,mDAGhBlF,KAAKwuG,sBAGDxuG,KAAKo+F,kBACLp+F,KAAKirG,wCAGThtF,GAAO/Y,KAAK,6CACZlF,KAAKmkB,WAAWpD,iBAAiB/gB,KAAK6/F,iBAAiB7+E,gBAEvD5B,IAAWgI,oBACPwE,YACI/B,IACA,CACIg9C,UAAW7mE,KAAK6/F,iBAAiBz0B,iBAajDuyB,GAAgBzvG,UAAU8yG,kBAAoB,SAAS15E,EAAa,IAChE,MAAM2nF,GAAWlyB,IAAQz1D,EAAYtnB,KAAKsnB,YAG1C,GADAtnB,KAAKsnB,WAAaA,EACd2nF,EAAS,CACTjvG,KAAKwf,aAAa0D,KACdP,qBACA3iB,KAAKsnB,YAGa,CAIlB,eAGA,aACA,gBAGUlY,QAAQvhB,SACM0S,IAApB+mB,EAAWz5B,IACXuxB,IAAWyB,UAAUolB,uBAAuB,CACxC,CAACp4C,EAAI+T,QAAQ,IAAK,MAAO0lB,EAAWz5B,SAaxD8vG,GAAgBzvG,UAAUghH,YAAc,SAASrhH,GAC7C,OAAOmS,KAAKsnB,WAAWz5B,IAO3B8vG,GAAgBzvG,UAAUihH,4BAA8B,WAChDnvG,KAAKy/F,uBACLxhF,GAAO/Y,KAAK,mCACZuI,aAAazN,KAAKy/F,sBAClBz/F,KAAKy/F,qBAAuB,OASpC9B,GAAgBzvG,UAAU8gH,uBAAyB,WAC/ChvG,KAAKovG,oBACD,MAAOpvG,KAAKo+F,iBAAiBp9E,eAAeumB,oBAQpDo2D,GAAgBzvG,UAAUmhH,uBAAyB,WAC/CrvG,KAAKovG,oBACD,MAAOpvG,KAAK6/F,iBAAiB7+E,eAAeumB,oBAUpDo2D,GAAgBzvG,UAAUkhH,oBAAsB,SACxCE,EACA9nE,GACJ,IAAK,MAAMlJ,KAASkJ,EAChBvpB,GAAO/Y,KAAM,mBAAkBoqG,YAA0BhxE,KACzDt+B,KAAKw7D,qBAAqBl9B,IAQlCq/D,GAAgBzvG,UAAUqhH,qCAAuC,WAC7DtxF,GAAO/Y,KAAK,sDACZlF,KAAKo+F,iBAAiB1mB,wBAAuB,GAAM,GAAMh9C,KACrD,KACIzc,GAAO/Y,KAAK,oDAEhBF,IACIiZ,GAAOjZ,MACH,2DACAA,MAWhB24F,GAAgBzvG,UAAU6gH,cAAgB,SAASzyC,GAC/C,GAAIt8D,KAAKm+C,MAAQme,EAAjB,CAMA,GADAt8D,KAAKm+C,IAAMme,EACPA,EAAW,CACXr+C,GAAO/Y,KAAK,wCAIZka,IAAWyB,UAAUolB,uBAAuB,CACxCqlE,WAAW,EACXD,aAAa,IAKjB,MAAMrD,EAAwC,IAAxBhoG,KAAKi6D,IAAInzB,WAE/B9mC,KAAK6/F,iBACAnoB,wBAAuB,EAAMswB,GAC7B5qE,MAAMp4B,IACHiZ,GAAOjZ,MAEI,+CAAGgjG,KAAkBhjG,UAGxCiZ,GAAO/Y,KAAK,mCAIZlF,KAAKo+F,kBACLp+F,KAAKmkB,WAAWK,gCACZxkB,KAAKo+F,iBAAiBp9E,gBAAiBs7C,GAI/Ct8D,KAAKs+F,YAAc,KAGnBt+F,KAAKwf,aAAa0D,KACdP,aACA3iB,KACAA,KAAKm+C,KACTn+C,KAAKwf,aAAa0D,KACdP,gCACA3iB,KAAK8jG,0BAGT9jG,KAAKwf,aAAa0D,KACdljB,KAAKiuG,0BACCtrF,yBACAA,4BApDN1E,GAAOhZ,MAAO,8CAA6Cq3D,IA4DnEqhC,GAAgBzvG,UAAUshH,iBAAmB,SAAStkC,GAElD,GADAlrE,KAAKmvG,8BACDnvG,KAAK6/F,iBAGL,YAFA5hF,GAAOjZ,MAAM,gCAKjBhF,KAAK4/F,4BAA6B,EAClC5/F,KAAK6/F,iBACC7/F,KAAKqf,KAAK9K,WAAW+9B,OAAOm9D,oBAC1BzvG,KAAKm/C,KAAKunD,UACVx7B,GACRjtD,GAAO/Y,KACH,gCAAiClF,KAAKm/C,KAAKunD,UAAWx7B,GAC1DlrE,KAAKihG,oCAELjhG,KAAK6/F,iBAAiBn5C,WAClB1mD,KAAKm/C,KACLn/C,KAAKi6D,IAFT,SAGWj6D,KAAKmH,QAAQqsB,QAHxB,IAIQ0S,wBAAyBlmC,KAAK+qG,oBAGtC9sF,GAAO/Y,KAAK,4CAEZ,IAAIkpG,EAAW3yG,UAAQyI,mBAAmBlE,KAAK6/F,iBAAiB30B,WAEhE,MAAM7O,EAAcr8D,KAAK+8D,aAAaqxC,GAElC/xC,IACA+xC,EAAW/xC,EAAYgyC,cAAgBD,GAG3CpuG,KAAKmkB,WAAWN,eACZ7jB,KAAK6/F,iBAAiB7+E,eACtBotF,GAKJ,MAAMprE,EAAchjC,KAAKknC,iBAEzBlnC,KAAK6/F,iBAAiBluB,OAAO3uC,IAOjC26D,GAAgBzvG,UAAU+8G,sCAAwC,WAC9DhtF,GAAO/Y,KAAK,wDACZlF,KAAKo+F,iBAAiB1mB,wBAAuB,GAAO,GAAOh9C,KACvD,KACIzc,GAAO/Y,KAAK,uDAEhBF,IACIiZ,GAAOjZ,MACH,4DACAA,MAWhB24F,GAAgBzvG,UAAU+6G,qBAAuB,SAASyG,GACtD,IAAK5vF,IAAQyqF,gBACLvqG,KAAKqjG,gBACNrjG,KAAKsjG,uBAGR,YAFArlF,GAAO/Y,KAAK,qBAIhB,MAAMyqG,EAAQ3vG,KAAKg9D,kBACb4yC,EAAYD,EAAMp5G,OAGlBs5G,EAAgB7vG,KAAKwqG,qBAQ3B,IALKqF,GAAiB7vG,KAAKy/F,sBACvBz/F,KAAKmvG,+BAIJnvG,KAAK6/F,kBAAoBgQ,EAAe,CACzC,MAAMzoD,EAAOwoD,GAAaD,EAAM,GAG1BG,EAAO9vG,KAAKk8D,WACZ6zC,EAAU3oD,EAAKoV,QAErB,GAAIszC,EAAOC,EAKP,YAJA9xF,GAAOhZ,MACH,2DACqC6qG,EAAMC,GAG5C,GAAID,IAASC,EAGhB,YAFA9xF,GAAOjZ,MAAM,kBAAmB8qG,EAAMC,GAK1C,MAAMpsG,EAAMyjD,EAAK/J,SAEjB,GAAIqyD,EAAe,CACf,GAAI1vG,KAAKy/F,qBAGL,YAFAxhF,GAAOjZ,MAAM,+CAIjBiZ,GAAO/Y,KACF,wBAAuBvB,WACpB3D,KAAK2/F,6BACb3/F,KAAKy/F,qBAAuBl1F,WACxBvK,KAAKwvG,iBAAiB1hH,KAAKkS,KAAM2D,GACX,IAAtB3D,KAAK2/F,qBAET1hF,GAAO/Y,KAAM,wBAAuBvB,GACpC3D,KAAKwvG,iBAAiB7rG,QAEnB3D,KAAK6/F,mBAAqBgQ,IACjC5xF,GAAO/Y,KAAM,uBAAsBlF,KAAK6/F,iBAAiB30B,WAGrDlrE,KAAK6/F,iBAAiBz0B,aAAewkC,EAAY,GACjDxwF,IAAWgI,oBACPwE,YAAe7B,MAEvB/pB,KAAKurG,oBAUb5N,GAAgBzvG,UAAUs8G,mBAAqB,WAC3C,MAAMmF,EAAQ3vG,KAAKg9D,kBACb4yC,EAAYD,EAAMp5G,OAClBy5G,OAA+DzvG,IAAlDovG,EAAM1+E,KAAK7iC,GAAoB,gBAAfA,EAAE26G,UAC/B8G,EAA8B,IAAdD,IAAoBI,EAI1C,OAFA/xF,GAAOhZ,MAAO,mBAAkB2qG,kBAA0BI,QAAiBH,KAEpEA,GAWXlS,GAAgBzvG,UAAUq9G,gBAAkB,SACpC/7F,EACAwkE,GACJ,IAAKh0E,KAAK6/F,iBAGN,YAFA5hF,GAAOjZ,MAAM,iCAKjB,MAAMirG,EAAoBjwG,KAAKm9D,cAG3B8yC,IACIjwG,KAAKo+F,kBACLp+F,KAAKuvG,uCAITvvG,KAAKqvG,0BAITpxF,GAAO/Y,KAAK,4CACZlF,KAAKmkB,WAAWlD,gBAAgBjhB,KAAK6/F,iBAAiB7+E,gBACtD/C,GAAO/Y,KAAK,yCACZlF,KAAKmkB,WAAWd,cAAcrjB,KAAK6/F,iBAAiB7+E,gBAEpDhhB,KAAK6/F,iBAAiBhsB,UAClB,KACI51D,GAAO/Y,KAAK,iCAEhBF,IAcQwK,GACAyO,GAAOjZ,MACH,iEAC6BA,IAEtC,CACCwK,OAAQA,GAAkB,UAC1BwkE,kBAAmBA,GACO,yBAC1BF,qBAAsB9zE,KAAKm/C,MACpBn/C,KAAKi9D,mBACJxhE,UAAQyI,mBAAmBlE,KAAK6/F,iBAAiB30B,cAGjElrE,KAAK6/F,iBAAmB,KAGxB7/F,KAAK+uG,eAAc,GAEfkB,IAEIjwG,KAAKo+F,iBACLp+F,KAAKsuG,sBAELrwF,GAAO/Y,KAAK,mDAWxBy4F,GAAgBzvG,UAAUivE,YAAc,WACpC,OAAOn9D,KAAKm+C,KAShBw/C,GAAgBzvG,UAAUgiH,sBAAwB,WAC9C,OAAIlwG,KAAKm9D,cACEn9D,KAAK6/F,iBAAiB7+E,eAAeq4D,qBAGzC,MAOXskB,GAAgBzvG,UAAUiiH,gBAAkB,WACxC,MAAMR,EAAQ3vG,KAAKg9D,kBAGnB,GAAqB,IAAjB2yC,EAAMp5G,OAKN,MAAM,IAAIc,MACN,kEANgB,CACpB,MAAM+4G,EAAUT,EAAM,GAAGtyD,SAEzBr9C,KAAKwvG,iBAAiBY,KAU9BzS,GAAgBzvG,UAAUmiH,eAAiB,WACvCrwG,KAAKurG,mBAOT5N,GAAgBzvG,UAAUoiH,gBAAkB,WACxC,OAAOtwG,KAAKu/F,sBAAsBgR,YAWtC5S,GAAgBzvG,UAAUk3C,2BAA6B,SAASC,GAC5DrlC,KAAKqhG,kBAAkBmP,kCAAkCnrE,IAU7Ds4D,GAAgBzvG,UAAUylF,yBAA2B,SAAStuC,GAC1D,OAAOrlC,KAAKqhG,kBAAkBoP,+BAA+BprE,IAgBjEs4D,GAAgBzvG,UAAUwiH,wBACpB,SAASC,EAAY9kB,GACnB,OAAK7rF,KAAKm/C,KAIHn/C,KAAK8/F,kBACP4Q,wBAAwBC,EAAY9kB,GAJ9B,IAAIx0F,MAAMu5G,wBAY7BjT,GAAgBzvG,UAAU+yG,kCAAoC,WAC1D,MAAM4P,EAAY7wG,KAAKusG,sBAEnBvsG,KAAKkgG,mCAAsC2Q,GAAgD,OAAnC7wG,KAAKsmG,4BAIjElnF,IAAWoI,cAAcsD,YAAsB,SAAU,CACrD+lF,YACA1lF,cAAgB,GAAE0lF,KAAa7wG,KAAKwgG,qBAExCxgG,KAAKkgG,kCAAoC/3F,KAAKgM,QAOlDwpF,GAAgBzvG,UAAUw1G,kCAAoC,WAC1D,MAAMmN,EAAY7wG,KAAKusG,qBAElBsE,GAAc7wG,KAAKkgG,mCAKxB9gF,IAAWoI,cAAcsD,YAAsB,OAAQ,CACnD+lF,YACA1lF,cAAgB,GAAE0lF,KAAa7wG,KAAKwgG,kBACpCh6E,MAAO,CACHqlF,SAAUzgG,KAAKmM,OAAOpP,KAAKgM,MAAQnU,KAAKkgG,mCAAqC,KAC7E4Q,KAAM9wG,KAAKykG,2BAUvB9G,GAAgBzvG,UAAUq7E,sBAAwB,WAC1CvpE,KAAK6/F,kBACL7/F,KAAKqwG,iBAGLrwG,KAAKo+F,kBACLp+F,KAAKo+F,iBAAiBvqB,UAClB,KACA7uE,IACIiZ,GAAO9Y,KAAK,8DAA+DH,IAC5E,CACCwK,OAAQ,UACRwkE,kBAAmB,mBACnBE,gBAAgB,EAChBJ,sBAAsB,IAIlC9zE,KAAKipG,sBAAqB,IAQ9BtL,GAAgBzvG,UAAU68G,eAAiB,WACvC,OAAO/qG,KAAKogG,gBAAkBpgG,KAAKogG,eAAeh3B,aAStDu0B,GAAgBzvG,UAAUiyG,gBAAkB,WACxC,OAAO/lD,IAAc5c,YAAYx9B,KAAKmH,QAAQqsB,SASlDmqE,GAAgBzvG,UAAU6iH,WAAa,SAASznC,GACvCtpE,KAAKmgG,kBAMVngG,KAAKogG,eAAe/2B,WAAWC,GAL3BrrD,GAAO9Y,KAAK,6DAapBw4F,GAAgBzvG,UAAU8iH,iBAAmB,WACzC,OAAOr4E,QAAQ34B,KAAKm/C,MAAQn/C,KAAKm/C,KAAK8xD,WAAWzzE,gBAQrDmgE,GAAgBzvG,UAAUgjH,cAAgB,WACtC,OAAOv4E,QAAQ34B,KAAKm/C,MAAQn/C,KAAKm/C,KAAKgyD,qBAQ1CxT,GAAgBzvG,UAAUkjH,YAAc,WACpC,OAAIpxG,KAAKm/C,MAAQn/C,KAAK4lG,cACX5lG,KAAKm/C,KAAK8xD,WAAWzvE,SAGzBxE,QAAQE,OACX,IAAI7lC,MAAM,yDAQlBsmG,GAAgBzvG,UAAUmjH,aAAe,WACjCrxG,KAAKm/C,MAAQn/C,KAAK4lG,eAClB5lG,KAAKm/C,KAAK8xD,WAAWK,WAW7B3T,GAAgBzvG,UAAUqjH,UAAY,SAAS1lB,EAAa2lB,GACxD,OAAIxxG,KAAKm/C,KACEn/C,KAAKm/C,KAAK8xD,WAAW9tG,KAAK0oF,EAAa2lB,GAG3Cx0E,QAAQE,OAAO,IAAI7lC,MAAM,gCAOpCsmG,GAAgBzvG,UAAUujH,gBAAkB,SAASvqG,GAC7ClH,KAAKm/C,MACLn/C,KAAKm/C,KAAK8xD,WAAWS,WAAWxqG,IASxCy2F,GAAgBzvG,UAAUyjH,mBAAqB,SAASzqG,GAChDlH,KAAKm/C,MACLn/C,KAAKm/C,KAAK8xD,WAAWW,cAAc1qG,M,+DC/8G3C,uLAwBA,MAAM+W,EAASF,oBAAUM,GAMV,SAAS4/E,EAA4B17E,GAChDviB,KAAKuiB,WAAaA,EAClBviB,KAAK6xG,cAAgB,GAGrBtvF,EAAWZ,GAAGgB,qBACV2b,IACI,IAAKA,EAAM/Y,YAAchD,EAAW4B,WAChC,OAEJ,MAAMlM,EACAqmB,EAAMwH,MACFvjB,EAAWs9E,iBAAmBt9E,EAAW67E,iBAI7C96E,EAAOrL,GAAWA,EAAQ+I,gBAAmB,KAEnDuB,EAAW4B,WAAWU,cAClBvB,EACAgb,EAAMggC,UACNhgC,EAAMgJ,aAOtB22D,EAA4B/vG,UAAU0zG,uBAAyB,WAC3D,MAAMr/E,EAAaviB,KAAKuiB,WAClBuvF,EAAWvvF,EAAW48B,KAE5Bn/C,KAAK+xG,kBAAoB,IAAIC,IAAsBF,EAC/C9xG,KAAKuiB,WAAW/C,cAEpBsyF,EAASluE,YAAY+W,IAAW/qD,eAAgBy6G,IACvCA,EAAcvkE,OAMfvjB,EAAW03C,IAAIxxB,uBAMvBqpE,EAASluE,YACL+W,IAAW9qD,oBACX,CAACw6G,EAAeh1B,MAGXg1B,EAAcvkE,OACRvjB,EAAWyoF,kBACV31B,EAASg1B,EAAcrpF,kBAIvC8wF,EAASluE,YAAY+W,IAAWpsD,qBAC5B0jH,IAII7yF,IAAWoI,cAAcqE,eAEzBtJ,EAAWs8E,kBAAoBoT,EAG/B1vF,EAAW03C,IAAIvyB,cAAa,GAAMhN,KAC9B,KACInY,EAAWq8E,gBAAiB,EAC5Br8E,EAAWs8E,kBAAoB,OAElCzhE,MACGp4B,IACIud,EAAWs8E,kBAAoB,KAC/B5gF,EAAO9Y,KACH,gDAAiDH,OAKzEhF,KAAK+xG,kBAAkBG,QAAQv3D,IAAWjoD,gBACtCiwB,mBAEJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAWrqD,WACtCqyB,qBAGJmvF,EAASluE,YAAY+W,IAAWrqD,WAC5B,KACI0P,KAAKuiB,WAAW+8E,4BAA6B,EAG7CtyG,OAAOuY,KAAKusG,EAASh5D,iBAAiB1pC,QAAQvhB,IAC1C,MAAMw5B,EACA2D,YACG,cAAan9B,EACd,CAAEN,MAAOukH,EAASh5D,gBAAgBjrD,KAE1CuxB,IAAWoI,cAAcH,KAI7Br6B,OAAOuY,KAAKusG,EAASzyF,KAAKy5B,iBAAiB1pC,QAAQvhB,IAC/C,MAAMw5B,EACA2D,YACG,QAAOn9B,EACR,CAAEN,MAAOukH,EAASzyF,KAAKy5B,gBAAgBjrD,KAE/CuxB,IAAWoI,cAAcH,OAIrCyqF,EAASluE,YAAY+W,IAAW9oD,qBAAsB,CAACwH,EAAG4e,KACjDA,EAAQ6tB,OACTvjB,EAAW/C,aAAa0D,KAAKP,oBACzB8kF,sBAA2CpuG,KAIvD2G,KAAK+xG,kBAAkBG,QAAQv3D,IAAW1oD,gBACtC0wB,oBACA8kF,oBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAW5oD,mBACtC4wB,oBACA8kF,oBACJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAW3oD,+BACtC2wB,oBACA8kF,qBACJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWzoD,gCACtCywB,oBACA8kF,sBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWxoD,qBACtCwwB,oBACA8kF,wBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWtpD,kBACtCsxB,oBACA8kF,qBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWnsD,wBACtCm0B,oBACA8kF,2BAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWlsD,YACtCk0B,oBACA8kF,6BACJqK,EAASluE,YACL+W,IAAWlsD,YACX,IAAM2wB,IAAWoI,cAAc+C,gBAEnCvqB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW7oD,kBACtC6wB,oBACA8kF,qBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWhrD,kBACtCgzB,oBACA8kF,qBAEJqK,EAASluE,YAAY+W,IAAWxrD,sBAC5Bk7G,IACI9nF,EAAW2rF,uBAAuB7D,KAG1CrqG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWtqD,cACtCsyB,oBACA8kF,wBAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAW9rD,oBACtC8zB,mBACA8kF,cAEJznG,KAAK+xG,kBAAkBG,QAAQv3D,IAAWlrD,mBACtCkzB,oBACA8kF,sBAEJqK,EAASluE,YAAY+W,IAAWjrD,WAC5B,KACI0vB,IAAWoI,cAAc8D,eACzB/I,EAAW/C,aAAa0D,KACpBP,oBACA8kF,gBAGZqK,EAASluE,YAAY+W,IAAWroD,uBAC5B+3G,IACIjrF,IAAWgI,oBACPqE,YACInC,IACA,CAAE60B,IAAKksD,EAAcvkE,WAGrCgsE,EAASluE,YAAY+W,IAAWhpD,uBAC5B,CAACsmB,EAAStU,KAEN,GAAIA,EAAK,CACL,MAAM04D,EAAc95C,EAAW06C,mBAC3BxhE,UAAQyI,mBAAmBP,IAEH,QAAxBsU,EAAQ4/E,YACR5/E,EAAQmgF,cAAc/7B,GACS,OAAxBpkD,EAAQ4/E,aACf5/E,EAAQkgF,aAAa97B,GAI7B95C,EAAW/C,aAAa0D,KACpBP,yBACA1K,KAGZjY,KAAK+xG,kBAAkBG,QAAQv3D,IAAW/nD,6BACtC+vB,gCAEJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW7nD,kCACtC6vB,qCAEJ3iB,KAAK+xG,kBAAkBG,QACnBv3D,IAAW5nD,mCACX4vB,sCAEJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAWrpD,qBACtCqxB,wBAEJmvF,EAASK,+BAA+B,CAACjxG,EAAMvK,KAC3C,MAAM0lE,EAAc95C,EAAW06C,mBAAmBtmE,GAE7C0lE,GAILA,EAAY+sC,YACRloG,EAAKf,QAAQgtB,UAAU,qBAAqB52B,QAC5C2K,EAAK3T,SAGbukH,EAASluE,YAAY+W,IAAW7qD,OAC5ByyB,EAAWgnF,eAAez7G,KAAKy0B,IACnCuvF,EAASluE,YAAY+W,IAAWhoD,iBAC5B4vB,EAAWipF,kBAAkB19G,KAAKy0B,IAEtCviB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW5pD,iBACtC4xB,sBAEJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW3pD,yBACtC2xB,wBAEJmvF,EAASluE,YAAY+W,IAAWpqD,kBAC5BgyB,EAAWkmF,eAAe36G,KAAKy0B,IACnCviB,KAAK+xG,kBAAkBG,QAAQv3D,IAAWlqD,wBACtCkyB,qBACJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAWjqD,yBACtCiyB,sBACJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAWhqD,sBACtCgyB,mBACJmvF,EAASluE,YAAY+W,IAAWvqD,4BAC5BmyB,EAAW8mF,wBAAwBv7G,KAAKy0B,IAC5CuvF,EAASluE,YAAY+W,IAAWnqD,gBAC5B+xB,EAAWshF,aAAa/1G,KAAKy0B,IACjCviB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW9pD,SACtC8xB,mBACJ3iB,KAAK+xG,kBAAkBG,QAAQv3D,IAAW/pD,kBACtC+xB,oBACA8kF,4BAEJqK,EAASluE,YAAY+W,IAAWtrD,qBAC5BkzB,EAAWsnF,qBAAqB/7G,KAAKy0B,IAEzCuvF,EAASluE,YAAY+W,IAAW5qD,mBAAoB0xE,IAChDl/C,EAAWonF,mBAAmBloC,GAG1Bl/C,EAAW4B,YAAc5B,EAAWqjF,eACpCrjF,EAAWZ,GAAGgB,yBACVyvF,IACI,MAAMC,EAAY,CACdrtG,MAAOotG,EAAgB9a,WACvBpwF,GAAI,kBACJ6I,OAAQqiG,EAAgBva,aAG5Bz4E,IAAWkH,QAAQ1Z,KAAKwL,UAAUi6F,QAKlDP,EAASluE,YAAY+W,IAAW7pD,iBAC5ByxB,EAAWqnF,kBAAkB97G,KAAKy0B,IAEtCuvF,EAASluE,YAAY0uE,IAAqBtlB,iBACtC,CAACwR,EAAayF,KACV1hF,EAAWi8E,YAAcA,EACzBj8E,EAAW0hF,aAAeA,EAC1B1hF,EAAW/C,aAAa0D,KACpBP,sBAA2C67E,EAC3CyF,KAGZ6N,EAASluE,YACL+W,IAAW1qD,iBAGX,CAAC0T,EAAKkoF,EAAa0mB,EAAKC,EAAOC,KAC3B,MAAMvrG,EAAKzL,UAAQyI,mBAAmBP,GAEtC4e,EAAW/C,aAAa0D,KACpBP,mBACAzb,EAAIqrG,EAAKE,EAAI5mB,KAGzBimB,EAASluE,YACL+W,IAAWxqD,yBAGX,CAACwT,EAAKkoF,EAAa0mB,EAAKC,EAAOC,KAC3B,MAAMvrG,EAAKzL,UAAQyI,mBAAmBP,GAEtC4e,EAAW/C,aAAa0D,KACpBP,2BACAzb,EAAIqrG,EAAKE,KAGrBX,EAASluE,YAAY+W,IAAWnpD,gBAC5B,CAACmS,EAAKoM,KACF,MAAM7I,EAAKzL,UAAQyI,mBAAmBP,GAChC04D,EAAc95C,EAAW06C,mBAAmB/1D,GAE7Cm1D,GAAeA,EAAYnK,UAAYniD,IAG5CssD,EAAYnK,QAAUniD,EACtBwS,EAAW/C,aAAa0D,KACpBP,sBAA2Czb,EAAI6I,MAG3D+hG,EAASluE,YAAY+W,IAAW1nD,sBAC5B,CAAC0D,EAAM87B,KACH,MAAMvrB,EAAKzL,UAAQyI,mBAAmBvN,GAChC0lE,EAAc95C,EAAW06C,mBAAmB/1D,GAE9Cm1D,EACA95C,EAAW/C,aAAa0D,KACpBP,4BACA05C,EAAa5pC,GAEjBxU,EAAO9Y,KACH,0EACkBxO,EAClB87B,KAIhBq/E,EAAShN,oBAAoB,aAAc,CAACnrG,EAAMhD,KAC9C,IAAIivG,GAAc,EAElB,GAAIrjF,EAAW25C,aAAevlE,GAAQ4rB,EAAWqjF,cAC7CA,GAAc,MACX,CACH,MAAMvpC,EAAc95C,EAAW06C,mBAAmBtmE,GAE9C0lE,GAAeA,EAAYupC,gBAC3BA,GAAc,GAItB,IAAKA,EACD,OAGJ,MAAMnH,EAA4C,SAA1B9kG,EAAKsD,WAAWo4B,MAClCqpE,EAA4C,SAA1B/kG,EAAKsD,WAAWg3B,MAExC,IAAIy+E,GAAU,EAEVjU,IAAoBl8E,EAAWo8E,iBAAiBtpE,QAChD9S,EAAWo8E,iBAAiBtpE,MAAQopE,EACpCiU,GAAU,GAGVhU,IAAoBn8E,EAAWo8E,iBAAiB1qE,QAChD1R,EAAWo8E,iBAAiB1qE,MAAQyqE,EACpCgU,GAAU,GAGVA,GACAnwF,EAAW/C,aAAa0D,KACpBP,6BACAJ,EAAWo8E,oBAKnBp8E,EAAW4B,aAEX2tF,EAASluE,YAAY+W,IAAWxrD,sBAC5B8oB,IACIsK,EAAW4B,WAAWS,6BAClB3M,EAAQ+I,kBAIpB8wF,EAASluE,YAAY+W,IAAWrsD,yBAC5B,CAAC+K,EAAGisD,KACA/iC,EAAW4B,WAAWkC,0BAA0BhtB,EAAGisD,OAQnE24C,EAA4B/vG,UAAUkzG,kBAAoB,WACtD,MAAM7+E,EAAaviB,KAAKuiB,WAClB03C,EAAM13C,EAAW03C,IAEvBA,EAAIr2B,YACA5K,IAAU3Q,mBACV9F,EAAW+4C,mBAAmBxtE,KAAKy0B,IAEvC03C,EAAIr2B,YACA5K,IAAUzQ,qBACVhG,EAAWi5C,qBAAqB1tE,KAAKy0B,IAEzC03C,EAAIr2B,YAAY5K,IAAUtlC,yBACtBwT,IACQqb,EAAW87E,sBAAwBn3F,GAAMqb,EAAW48B,OACpD58B,EAAW87E,oBAAsBn3F,EACjCqb,EAAW/C,aAAa0D,KACpBP,2BAAgDzb,GAEhDqb,EAAW4B,YAAc5B,EAAW25C,aAAeh1D,GAEnDqb,EAAW4B,WAAWc,yBAClB1C,EAAW48B,KAAKpC,YAKpCkd,EAAIr2B,YAAY5K,IAAUpR,kBAAmB,KACzC,MAAMzT,EAAMjoB,OAAOsuD,YAAYrmC,MACzBtmB,EAAM,sBAGZowB,EAAOrZ,IAAK,UAAS/W,OAAUsmB,GAC/BoO,EAAW48B,KAAKrG,gBAAgBjrD,GAAOsmB,EACvCiL,IAAWoI,cACPwD,YAAkCn9B,EAAK,CAAEN,MAAO4mB,KAEpDoO,EAAW/C,aAAa0D,KAAKP,yBAGjCs3C,EAAIr2B,YAAY5K,IAAUnlC,0BACtB,CAAC8C,EAAM87B,KACH,MAAM4pC,EAAc95C,EAAW06C,mBAAmBtmE,GAE9C0lE,EACA95C,EAAW/C,aAAa0D,KACpBP,4BACA05C,EAAa5pC,GAEjBxU,EAAO9Y,KACH,mEACsBxO,EACtB87B,KAIhBwnC,EAAIr2B,YAAY5K,IAAUjQ,oBACtB,CAACzF,EAAKyJ,KACGzJ,EAAIwiB,OACL1mB,IAAWkH,QACP1Z,KAAKwL,UAAU,CACXlR,GAAI,cACJ3Z,MAAOw/B,OAI3BktC,EAAIr2B,YAAY5K,IAAUhQ,qBACtB,CAAC1F,EAAKyJ,KACGzJ,EAAIwiB,OACL1mB,IAAWkH,QACP1Z,KAAKwL,UAAU,CACXlR,GAAI,eACJ3Z,MAAOw/B,OAK3BktC,EAAIr2B,YAAY5K,IAAUtR,qBACtB,CAACruB,EAAGiqB,KACAf,EAAW4B,WAAW+B,uBAAuB7sB,EAAGiqB,GAC3CA,EAAIwiB,OACLvjB,EAAW/C,aAAa0D,KAAKP,oBACzB8kF,sBAA2CpuG,KAI3D4gE,EAAIr2B,YAAY5K,IAAUrR,oBACtB,CAACtuB,EAAGiqB,KACAf,EAAW4B,WAAW8B,sBAAsB5sB,EAAGiqB,GAC1CA,EAAIwiB,OACLvjB,EAAW/C,aAAa0D,KAAKP,oBACzB8kF,sBAA2CpuG,KAI3D4gE,EAAIr2B,YAAY5K,IAAUvQ,6BACtB,CAACpvB,EAAGiqB,KACAf,EAAW4B,WAAWgC,uBAAuB9sB,EAAGiqB,GAC3CA,EAAIwiB,OACLvjB,EAAW/C,aAAa0D,KAAKP,oBACzB8kF,sBAA2CpuG,KAI3D4gE,EAAIr2B,YAAY5K,IAAUtQ,8BACtB,CAACrvB,EAAGiqB,KACAf,EAAW4B,WAAWiC,wBAAwB/sB,EAAGiqB,GAC5CA,EAAIwiB,OACLvjB,EAAW/C,aAAa0D,KAAKP,oBACzB8kF,sBAA2CpuG,KAI3D4gE,EAAIr2B,YAAY5K,IAAU9Q,yBACtB,CAACoW,EAAOtZ,KAKAsZ,EAAMqsC,gBAAkBrsC,EAAME,YAAcC,IAAUC,SACtDnc,EAAW4B,WAAWY,wBAAuB,EAAMC,MAQnEi5E,EAA4B/vG,UAAU01G,oBAAsB,WACxD,MAAMrhF,EAAaviB,KAAKuiB,WAExBA,EAAWlD,KAAKo6B,KAAK33B,eACjB64B,IAAWvpD,4BACX4O,KAAK6xG,cAAcl3D,IAAWvpD,qCAC3B4O,KAAK6xG,cAAcl3D,IAAWvpD,6BAErCpE,OAAOuY,KAAKvF,KAAK6xG,eAAeziG,QAAQqY,IACpClF,EAAWlD,KAAKyC,eACZ2F,EACAznB,KAAK6xG,cAAcpqF,MAE3BznB,KAAK6xG,cAAgB,IAOzB5T,EAA4B/vG,UAAUqyG,mBAAqB,WACvD,MAAMh+E,EAAaviB,KAAKuiB,WAElBowF,EAA0Bh8G,IAC5B,MAAM0lE,EACA95C,EAAW06C,mBACbxhE,UAAQyI,mBAAmBvN,IAE3B0lE,GACA95C,EAAW/C,aAAa0D,KACpBP,8BACA05C,IAIZ95C,EAAWlD,KAAKo6B,KAAK7V,YACjB+W,IAAWvpD,4BACXuhH,GACJ3yG,KAAK6xG,cAAcl3D,IAAWvpD,6BACxBuhH,EAEN3yG,KAAK4yG,2BACDj4D,IAAWhsD,cACX4zB,EAAWooF,eAAe78G,KAAKy0B,IACnCviB,KAAK4yG,2BACDj4D,IAAWjsD,cACX6zB,EAAWynF,eAAel8G,KAAKy0B,IACnCviB,KAAK4yG,2BACDj4D,IAAW9nD,eACX0vB,EAAW0nF,gBAAgBn8G,KAAKy0B,IACpCviB,KAAK4yG,2BACDj4D,IAAW/rD,WACX2zB,EAAW4iB,YAAYr3C,KAAKy0B,IAEhCviB,KAAK4yG,2BAA2Bj4D,IAAWloD,uBACvC,CAACogH,EAAYC,KACLvwF,EAAWpb,QAAQqsB,OAAOu/E,mBAI9BxwF,EAAWk8E,gBAAkBoU,EAC7BtwF,EAAWm8E,gBAAkBoU,EAI7BvwF,EAAW2kB,iBAAiB93B,QAAQkvB,IAChC,OAAQA,EAAMgJ,WACd,KAAK/I,IACDhc,EAAWk8E,iBAAmBngE,EAAMuJ,OACpC,MACJ,KAAKtJ,IACDhc,EAAWm8E,iBAAmBpgE,EAAMuJ,UAK5CtlB,EAAW/C,aAAa0D,KAAKP,oBAGrC3iB,KAAK4yG,2BAA2Bj4D,IAAWnoD,8BACvCwgH,IACIzwF,EAAW/C,aAAa0D,KAAKP,+BAAoDqwF,MAO7F/U,EAA4B/vG,UAAU0kH,2BAA6B,SAC3DnrF,EAAW/F,GACf1hB,KAAK6xG,cAAcpqF,GAAa/F,EAChC1hB,KAAKuiB,WAAWlD,KAAKukB,YAAYnc,EAAW/F,IAMhDu8E,EAA4B/vG,UAAU2zG,yBAA2B,WAC7D,MAAMt/E,EAAaviB,KAAKuiB,WAEnBA,EAAW4B,aAKhB5B,EAAW4B,WAAW1C,sBAAsB,CAAC6B,EAAK0B,EAAMngB,EAAO0gB,KAC3DhD,EAAW03C,IAAIvxB,cAAcplB,EAAK0B,EAAMngB,EAAO0gB,KAMnDhD,EAAW4B,WAAWpC,0BAA0B,KAC5CQ,EAAW/C,aAAa0D,KACpBP,gCAIHJ,EAAWpb,QAAQqsB,OAAOo6C,aAC3BrrD,EAAW4B,WAAWhC,yBAAyB,CAACmB,EAAKkD,KACjDjE,EAAW2kB,eAAe3I,KAAiBnvB,QAAQkvB,IAC/C,MAAMtZ,EAAO1B,EAAIgqF,aAAahvE,GAEzBtZ,GAASwB,EAAMr4B,eAAe62B,IAInCsZ,EAAMm3D,yBAAyBnyE,EAAKkD,EAAMxB,Y,2EC5rB1D,mEAGA,MAAM/G,EAASF,oBAAUM,GAWV,MAAMnB,UAA4Bw3B,IAI7Cz+C,cACIokC,QACApc,EAAO/Y,KACF,sBAAqBlF,KAAKi1C,mBAAmBj1C,KAAKq1C,gBAU3Dq1B,8BACI,OAAO1qE,KAAKq7B,mBAAqBr7B,KAAKu1B,WAQ1Cg1E,cACI,OAAQvqG,KAAKyuE,kBAejBpzC,kBACI,OAAOr7B,KAAKs3B,YACLt3B,KAAK83B,cACL93B,KAAKo1C,UACLp1C,KAAKk1C,UAQhB1X,cACI,OAAOx9B,KAAKq7B,mBACLr7B,KAAKowB,aACLpwB,KAAK+f,iBACJ/f,KAAKu1B,aAAev1B,KAAK01C,kBAAkB,QASvDu9D,qCACI,OAAOjzG,KAAKowB,aAAepwB,KAAK01C,kBAAkB,MAStDskB,qCACI,OAAOh6D,KAAKq7B,mBAAqBr7B,KAAK+f,iBAAmB/f,KAAKu1B,WAQlE29E,8BAGI,OAAQlzG,KAAKowB,cAAgBpwB,KAAKu1B,WAOtC49E,2BACI,OAAOnzG,KAAKyuE,wBAC+B,IAA7BviF,OAAOknH,mBACdpmH,OAAOuY,KAAKrZ,OAAOknH,kBAAkBllH,WAAW0V,QAAQ,wBAA0B,GAClF5W,OAAOuY,KAAK8tG,aAAanlH,WAAW0V,QAAQ,oBAAsB,IAIjE5D,KAAKu1B,WAOjB6G,4BACI,OAAO5B,UAAUC,mBACuC,IAA1CD,UAAUC,aAAa64E,qBACqB,IAA5C94E,UAAUC,aAAa4B,iBAOzCk3E,sCACI,OAAOvzG,KAAKq7B,mBAAqBr7B,KAAK+f,iBAAmB/f,KAAKu1B,WAQlE/S,8BACI,YAA6C,IAA/Bt2B,OAAOsnH,qBACdA,oBAAoBC,oBAAoB7vG,QAAQ,aAAe,EAM1E4iF,wBACI,YAAwC,IAA1Bt6F,OAAOwnH,gBACd1mH,OAAOuY,KAAKmuG,eAAexlH,WAAW0V,QAAQ,8BAAgC,EAQzF+vG,wBASI,OAAQ3zG,KAAKowB,YASjBqN,gBACI,OAAO,EAQX+3C,YACI,OAAQx1E,KAAKyuE,kBAQjB+b,6BACI,OAAOxqF,KAAKq7B,mBAAqBr7B,KAAK+f,iBAAmB/f,KAAKu1B,WAQlEk5C,kBACI,QAAIzuE,KAAKowB,gBAILpwB,KAAKu1B,iBAAkD,IAA7BrpC,OAAOknH,oBAI1BpmH,OAAOuY,KAAK6tG,kBAAkBllH,WAC7B0V,QAAQ,qBAAuB,EAc/Ci3B,iBAGI,OAAI76B,KAAKs3B,YACGt3B,KAAK01C,kBAHe,OAM5B11C,KAAKowB,cAAepwB,KAAKu1B,eAIzBv1B,KAAKq7B,mBACEr7B,KAAK4zG,4BAXgB,GAuBpC9/E,cACI,OAAO9zB,KAAK66B,iBAOhBkW,uBACI,OAAO,EAOX8mB,0BACI,YAA4C,IAA9Br9B,UAAU49B,sBACkB,IAA3B59B,UAAUC,mBAET,IADED,UAAUC,aAAa29B,gBAQ7C6Q,4BACI,QAAqC,IAAxB/8E,OAAOmnH,eACZnnH,OAAOmnH,aAAanlH,UAAU2lH,uBAC3B3nH,OAAOmnH,aAAanlH,UAAU4lH,0BACrC,OAAO,EAKX,MAAMvyF,EAAS,IAAIwyF,eAEnB,IAGI,OAFA7nH,OAAO8nH,YAAYzyF,EAAQ,IAAK,CAAEA,KAE3B,EACT,SACE,OAAO,GAOf04B,mBACI,OAAOthB,QAAQzsC,OAAOmnH,cACfnnH,OAAOmnH,aAAaY,iBACpB/nH,OAAOmnH,aAAaY,gBAAgB,SAASlxC,OAAO4f,KAAKnwD,GAA4B,cAAnBA,EAAMmqE,WACxEzwG,OAAOwnH,gBACPxnH,OAAOwnH,eAAeO,iBACtB/nH,OAAOwnH,eAAeO,gBAAgB,SAASlxC,OAAO4f,KAAKnwD,GAA4B,cAAnBA,EAAMmqE,WASrFr2D,uBACI,OAAOtmC,KAAKq7B,mBAAqBr7B,KAAK4zG,4BAA8B,GAQxEA,2BACI,GAAI5zG,KAAKq7B,kBAAmB,CAExB,GAAIr7B,KAAKo1C,SAEL,OAAO77B,OAAOjB,SAAS07C,EAAQ0sB,SAASwzB,SAAU,IAQtD,MAAMnW,EAAKvjE,UAAUsZ,UAErB,GAAIiqD,EAAGzqF,MAAM,UAAW,CAIpB,OAFMiG,OAAOjB,SAASylF,EAAGzqF,MAAM,oBAAoB,GAAI,KAM/D,OAAQ,M,2EChWiDvnB,EAAOD,QAAiJ,SAASuN,GAAG,IAAI7L,EAAE,GAAG,SAASJ,EAAEd,GAAG,GAAGkB,EAAElB,GAAG,OAAOkB,EAAElB,GAAGR,QAAQ,IAAIiC,EAAEP,EAAElB,GAAG,CAACA,EAAEA,EAAEC,GAAE,EAAGT,QAAQ,IAAI,OAAOuN,EAAE/M,GAAGG,KAAKsB,EAAEjC,QAAQiC,EAAEA,EAAEjC,QAAQsB,GAAGW,EAAExB,GAAE,EAAGwB,EAAEjC,QAAQ,OAAOsB,EAAEV,EAAE2M,EAAEjM,EAAET,EAAEa,EAAEJ,EAAER,EAAE,SAASyM,EAAE7L,EAAElB,GAAGc,EAAEL,EAAEsM,EAAE7L,IAAIR,OAAOC,eAAeoM,EAAE7L,EAAE,CAACN,YAAW,EAAGC,IAAIb,KAAKc,EAAEA,EAAE,SAASiM,GAAG,oBAAoBhM,QAAQA,OAAOC,aAAaN,OAAOC,eAAeoM,EAAEhM,OAAOC,YAAY,CAACC,MAAM,WAAWP,OAAOC,eAAeoM,EAAE,aAAa,CAAC9L,OAAM,KAAMH,EAAEI,EAAE,SAAS6L,EAAE7L,GAAG,GAAG,EAAEA,IAAI6L,EAAEjM,EAAEiM,IAAI,EAAE7L,EAAE,OAAO6L,EAAE,GAAG,EAAE7L,GAAG,iBAAiB6L,GAAGA,GAAGA,EAAE3L,WAAW,OAAO2L,EAAE,IAAI/M,EAAEU,OAAOY,OAAO,MAAM,GAAGR,EAAEA,EAAEd,GAAGU,OAAOC,eAAeX,EAAE,UAAU,CAACY,YAAW,EAAGK,MAAM8L,IAAI,EAAE7L,GAAG,iBAAiB6L,EAAE,IAAI,IAAItL,KAAKsL,EAAEjM,EAAER,EAAEN,EAAEyB,EAAE,SAASP,GAAG,OAAO6L,EAAE7L,IAAIM,KAAK,KAAKC,IAAI,OAAOzB,GAAGc,EAAEW,EAAE,SAASsL,GAAG,IAAI7L,EAAE6L,GAAGA,EAAE3L,WAAW,WAAW,OAAO2L,EAAE86G,SAAS,WAAW,OAAO96G,GAAG,OAAOjM,EAAER,EAAEY,EAAE,IAAIA,GAAGA,GAAGJ,EAAEL,EAAE,SAASsM,EAAE7L,GAAG,OAAOR,OAAOkB,UAAUC,eAAe1B,KAAK4M,EAAE7L,IAAIJ,EAAEgB,EAAE,GAAGhB,EAAEA,EAAEiB,EAAE,IAAj5B,CAAs5B,CAAC+lH,GAAG,SAAS/6G,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEc,EAAE,IAAIW,EAAE,WAAW,SAASsL,KAAK,OAAOA,EAAEg7G,cAAc,SAASh7G,EAAE7L,GAAG,IAAIJ,EAAEI,EAAE8lB,MAAMja,GAAG,OAAOjM,GAAGA,EAAEmJ,OAAO,GAAGnJ,EAAE,IAAI,IAAIiM,EAAEi7G,eAAe,SAASj7G,EAAE7L,GAAG,IAAIJ,EAAEI,EAAE8lB,MAAMja,GAAG,OAAOjM,GAAGA,EAAEmJ,OAAO,GAAGnJ,EAAE,IAAI,IAAIiM,EAAEk7G,oBAAoB,SAASl7G,EAAE7L,EAAEJ,GAAG,GAAGiM,EAAE6Y,KAAK1kB,GAAG,OAAOJ,GAAGiM,EAAEm7G,sBAAsB,SAASn7G,GAAG,OAAOA,GAAG,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,MAAM,KAAK,IAAI,SAAS,MAAM,OAAO,IAAI,SAAS,MAAM,KAAK,IAAI,SAAS,MAAM,OAAO,IAAI,SAAS,MAAM,QAAQ,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,MAAM,MAAM,IAAI,UAAU,MAAM,KAAK,QAAQ,SAASA,EAAEo7G,oBAAoB,SAASp7G,GAAG,IAAI7L,EAAE6L,EAAE0J,MAAM,KAAKkB,OAAO,EAAE,GAAGuB,KAAK,SAASnM,GAAG,OAAOif,SAASjf,EAAE,KAAK,KAAK,GAAG7L,EAAE0V,KAAK,GAAG,KAAK1V,EAAE,GAAG,OAAOA,EAAE,IAAI,KAAK,EAAE,MAAM,UAAU,KAAK,EAAE,MAAM,eAAe,KAAK,EAAE,MAAM,OAAO,KAAK,EAAE,MAAM,gBAAgB,KAAK,EAAE,MAAM,YAAY,KAAK,GAAG,MAAM,WAAW,KAAK,GAAG,MAAM,aAAa,KAAK,GAAG,MAAM,SAAS,KAAK,GAAG,MAAM,cAAc,KAAK,GAAG,MAAM,SAAS,KAAK,GAAG,MAAM,WAAW,QAAQ,SAAS6L,EAAEq7G,sBAAsB,SAASr7G,GAAG,IAAI7L,EAAE6L,EAAE0J,MAAM,KAAKkB,OAAO,EAAE,GAAGuB,KAAK,SAASnM,GAAG,OAAOif,SAASjf,EAAE,KAAK,KAAK,GAAG7L,EAAE0V,KAAK,KAAK,IAAI1V,EAAE,IAAIA,EAAE,GAAG,GAAG,OAAO,IAAIA,EAAE,IAAIA,EAAE,GAAG,EAAE,UAAU,IAAIA,EAAE,IAAIA,EAAE,IAAI,EAAE,QAAQ,IAAIA,EAAE,IAAIA,EAAE,GAAG,EAAE,SAAS,IAAIA,EAAE,IAAI,IAAIA,EAAE,GAAG,QAAQ,IAAIA,EAAE,IAAIA,EAAE,GAAG,EAAE,cAAc,IAAIA,EAAE,GAAG,YAAY,IAAIA,EAAE,IAAIA,EAAE,GAAG,EAAE,qBAAqB,IAAIA,EAAE,IAAIA,EAAE,GAAG,EAAE,aAAa,IAAIA,EAAE,IAAIA,EAAE,IAAI,EAAE,SAAS,IAAIA,EAAE,GAAG,WAAW,IAAIA,EAAE,GAAG,cAAc,IAAIA,EAAE,GAAG,SAAS,IAAIA,EAAE,GAAG,OAAO,IAAIA,EAAE,GAAG,WAAM,GAAQ6L,EAAEs7G,oBAAoB,SAASt7G,GAAG,OAAOA,EAAE0J,MAAM,KAAKxM,QAAQ8C,EAAEu7G,gBAAgB,SAASpnH,EAAEJ,EAAEd,QAAG,IAASA,IAAIA,GAAE,GAAI,IAAIyB,EAAEsL,EAAEs7G,oBAAoBnnH,GAAGa,EAAEgL,EAAEs7G,oBAAoBvnH,GAAGL,EAAEqe,KAAKgpB,IAAIrmC,EAAEM,GAAGqJ,EAAE,EAAEujG,EAAE5hG,EAAEmM,IAAI,CAAChY,EAAEJ,IAAI,SAASI,GAAG,IAAIJ,EAAEL,EAAEsM,EAAEs7G,oBAAoBnnH,GAAGlB,EAAEkB,EAAE,IAAI4I,MAAMhJ,EAAE,GAAG+V,KAAK,MAAM,OAAO9J,EAAEmM,IAAIlZ,EAAEyW,MAAM,MAAM,SAAS1J,GAAG,OAAO,IAAIjD,MAAM,GAAGiD,EAAE9C,QAAQ4M,KAAK,KAAK9J,KAAKu5B,aAAa,IAAItmC,IAAIoL,EAAE3K,EAAEqe,KAAKqP,IAAI1sB,EAAEM,IAAItB,GAAG,EAAEA,GAAG2K,GAAG,CAAC,GAAGujG,EAAE,GAAGluG,GAAGkuG,EAAE,GAAGluG,GAAG,OAAO,EAAE,GAAGkuG,EAAE,GAAGluG,KAAKkuG,EAAE,GAAGluG,GAAG,CAAC,GAAGA,IAAI2K,EAAE,OAAO,EAAE3K,GAAG,OAAO,GAAGkuG,EAAE,GAAGluG,GAAGkuG,EAAE,GAAGluG,GAAG,OAAO,IAAIsM,EAAEmM,IAAI,SAASnM,EAAE7L,GAAG,IAAIJ,EAAEd,EAAE,GAAG,GAAG8J,MAAMlI,UAAUsX,IAAI,OAAOpP,MAAMlI,UAAUsX,IAAI/Y,KAAK4M,EAAE7L,GAAG,IAAIJ,EAAE,EAAEA,EAAEiM,EAAE9C,OAAOnJ,GAAG,EAAEd,EAAE4W,KAAK1V,EAAE6L,EAAEjM,KAAK,OAAOd,GAAG+M,EAAEw7G,gBAAgB,SAASx7G,GAAG,OAAO/M,EAAEwoH,oBAAoBz7G,IAAIA,EAAE07G,sBAAsB,SAAS17G,GAAG,OAAO/M,EAAE0oH,YAAY37G,IAAI,IAAIA,EAAtvE,GAA2vE7L,EAAE2mH,QAAQpmH,EAAEsL,EAAEvN,QAAQ0B,EAAE2mH,SAASc,GAAG,SAAS57G,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE0nH,WAAW1nH,EAAE2nH,OAAO3nH,EAAE4nH,cAAc5nH,EAAEwnH,YAAYxnH,EAAEsnH,yBAAoB,EAAOtnH,EAAEsnH,oBAAoB,CAAC,cAAc,cAAc,kBAAkB,UAAUO,KAAK,OAAOC,WAAW,aAAaC,OAAO,SAASC,SAAS,WAAWC,SAAS,WAAWC,QAAQ,UAAUC,MAAM,QAAQC,QAAQ,UAAU,gBAAgB,gBAAgBC,UAAU,YAAY,oBAAoB,KAAK,WAAW,WAAWC,QAAQ,UAAU,iBAAiB,OAAO,aAAa,KAAK,sBAAsB,QAAQC,MAAM,QAAQ,cAAc,cAAcC,UAAU,YAAYC,OAAO,SAASC,SAAS,WAAWC,GAAG,KAAKC,OAAO,SAASC,OAAO,SAASC,SAAS,WAAW,+BAA+B,mBAAmBC,UAAU,YAAYC,SAAS,WAAWC,MAAM,QAAQC,MAAM,QAAQ,aAAa,KAAKC,QAAQ,UAAU,gBAAgB,QAAQC,OAAO,SAAS,iBAAiB,SAASC,KAAK,QAAQrpH,EAAEwnH,YAAY,CAAC8B,YAAY,cAAcC,QAAQ,kBAAkBC,KAAK,OAAOC,WAAW,aAAaC,OAAO,SAAShD,SAAS,WAAWiD,SAAS,WAAWC,QAAQ,UAAUC,MAAM,QAAQC,QAAQ,UAAUC,UAAU,YAAYC,cAAc,gBAAgBC,GAAG,oBAAoBC,SAAS,WAAWC,QAAQ,UAAUC,KAAK,iBAAiBC,GAAG,aAAaC,MAAM,sBAAsBC,MAAM,QAAQC,YAAY,cAAcC,UAAU,YAAYC,OAAO,SAASC,SAAS,WAAWC,GAAG,aAAaC,OAAO,kBAAkBC,OAAO,SAASC,SAAS,WAAWC,iBAAiB,+BAA+BC,UAAU,YAAYC,SAAS,WAAWC,MAAM,QAAQC,MAAM,QAAQC,GAAG,aAAaC,QAAQ,UAAUC,MAAM,gBAAgBC,OAAO,SAASC,OAAO,kBAAkBzrH,EAAE4nH,cAAc,CAAC8D,OAAO,SAASC,OAAO,SAAS/6E,QAAQ,UAAUg7E,GAAG,MAAM5rH,EAAE2nH,OAAO,CAACkE,aAAa,gBAAgBC,QAAQ,UAAUC,MAAM,QAAQC,IAAI,MAAMC,QAAQ,UAAUC,MAAM,QAAQpE,WAAW,aAAaD,KAAK,OAAOqB,MAAM,QAAQiD,MAAM,QAAQC,SAAS,YAAYC,aAAa,gBAAgBhD,KAAK,QAAQrpH,EAAE0nH,WAAW,CAAC4E,SAAS,WAAWC,MAAM,QAAQC,QAAQ,UAAUC,OAAO,SAASC,MAAM,QAAQC,OAAO,WAAWC,GAAG,SAAS/gH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEyB,GAAGzB,EAAEc,EAAE,MAAMd,EAAEoB,WAAWpB,EAAE,CAAC6nH,QAAQ7nH,GAAG+B,EAAEjB,EAAE,IAAI,SAASL,EAAEsM,EAAE7L,GAAG,IAAI,IAAIJ,EAAE,EAAEA,EAAEI,EAAE+I,OAAOnJ,IAAI,CAAC,IAAId,EAAEkB,EAAEJ,GAAGd,EAAEY,WAAWZ,EAAEY,aAAY,EAAGZ,EAAE+tH,cAAa,EAAG,UAAU/tH,IAAIA,EAAEguH,UAAS,GAAIttH,OAAOC,eAAeoM,EAAE/M,EAAEuB,IAAIvB,IAAI,IAAIoL,EAAE,WAAW,SAAS2B,KAAK,IAAI7L,EAAIlB,EAAE,OAAO+M,EAAEw7C,UAAU,SAASx7C,EAAE7L,GAAG,QAAG,IAASA,IAAIA,GAAE,GAAI,iBAAiB6L,EAAE,MAAM,IAAIhC,MAAM,gCAAgC,OAAO,IAAItJ,EAAEomH,QAAQ96G,EAAE7L,IAAI6L,EAAE6e,MAAM,SAAS7e,GAAG,OAAO,IAAItL,EAAEomH,QAAQ96G,GAAGkhH,aAAa/sH,EAAE6L,GAAE/M,EAAE,CAAC,CAACuB,IAAI,cAAcV,IAAI,WAAW,OAAOkB,EAAE2mH,cAAc,CAACnnH,IAAI,aAAaV,IAAI,WAAW,OAAOkB,EAAE6mH,aAAa,CAACrnH,IAAI,SAASV,IAAI,WAAW,OAAOkB,EAAE8mH,SAAS,CAACtnH,IAAI,gBAAgBV,IAAI,WAAW,OAAOkB,EAAE+mH,mBAA+CroH,EAAES,EAAElB,GAAG+M,EAA1f,GAA+f7L,EAAE2mH,QAAQz8G,EAAE2B,EAAEvN,QAAQ0B,EAAE2mH,SAASqG,GAAG,SAASnhH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAE2uG,EAAE7tG,EAAE,KAAKW,EAAEktG,EAAE7tG,EAAE,KAAKiB,EAAE4sG,EAAE7tG,EAAE,KAAKL,EAAEkuG,EAAE7tG,EAAE,KAAKsK,EAAEujG,EAAE7tG,EAAE,KAAK,SAAS6tG,EAAE5hG,GAAG,OAAOA,GAAGA,EAAE3L,WAAW2L,EAAE,CAAC86G,QAAQ96G,GAAG,IAAIzM,EAAE,WAAW,SAASyM,EAAEA,EAAE7L,GAAG,QAAG,IAASA,IAAIA,GAAE,GAAI,MAAM6L,GAAG,KAAKA,EAAE,MAAM,IAAIhC,MAAM,sCAAsC2I,KAAKy6G,IAAIphH,EAAE2G,KAAK06G,aAAa,IAAG,IAAKltH,GAAGwS,KAAKkY,QAAQ,IAAI1qB,EAAE6L,EAAEnL,UAAU,OAAOV,EAAEmtH,MAAM,WAAW,OAAO36G,KAAKy6G,KAAKjtH,EAAE0kB,KAAK,SAAS7Y,GAAG,OAAOA,EAAE6Y,KAAKlS,KAAKy6G,MAAMjtH,EAAEotH,aAAa,WAAW,IAAIvhH,EAAE2G,KAAKA,KAAK06G,aAAa56F,QAAQ,GAAG,IAAItyB,EAAElB,EAAE6nH,QAAQljF,MAAM,SAASzjC,GAAG,GAAG,mBAAmBA,EAAE0kB,KAAK,OAAO1kB,EAAE0kB,KAAK7Y,GAAG,GAAG7L,EAAE0kB,gBAAgB9b,MAAM,OAAO5I,EAAE0kB,KAAKywE,MAAM,SAASn1F,GAAG,OAAO6L,EAAE6Y,KAAK1kB,MAAM,MAAM,IAAI6J,MAAM,2CAA2C,OAAO7J,IAAIwS,KAAK06G,aAAa56F,QAAQtyB,EAAEqtH,SAAS76G,KAAK26G,UAAU36G,KAAK06G,aAAa56F,SAAStyB,EAAEstH,WAAW,WAAW,OAAO96G,KAAK06G,aAAa56F,QAAQ9f,KAAK06G,aAAa56F,QAAQ9f,KAAK46G,gBAAgBptH,EAAE+mD,eAAe,SAASl7C,GAAG,OAAOA,EAAEP,OAAOkH,KAAK86G,aAAajuH,MAAM8V,eAAe,GAAG3C,KAAK86G,aAAajuH,MAAM,IAAIW,EAAEgnD,kBAAkB,WAAW,OAAOx0C,KAAK86G,aAAa/mE,SAASvmD,EAAEutH,MAAM,WAAW,OAAO/6G,KAAK06G,aAAaM,GAAGh7G,KAAK06G,aAAaM,GAAGh7G,KAAKi7G,WAAWztH,EAAEytH,QAAQ,WAAW,IAAI5hH,EAAE2G,KAAKA,KAAK06G,aAAaM,GAAG,GAAG,IAAIxtH,EAAEO,EAAEomH,QAAQljF,MAAM,SAASzjC,GAAG,GAAG,mBAAmBA,EAAE0kB,KAAK,OAAO1kB,EAAE0kB,KAAK7Y,GAAG,GAAG7L,EAAE0kB,gBAAgB9b,MAAM,OAAO5I,EAAE0kB,KAAKywE,MAAM,SAASn1F,GAAG,OAAO6L,EAAE6Y,KAAK1kB,MAAM,MAAM,IAAI6J,MAAM,2CAA2C,OAAO7J,IAAIwS,KAAK06G,aAAaM,GAAGxtH,EAAEqtH,SAAS76G,KAAK26G,UAAU36G,KAAK06G,aAAaM,IAAIxtH,EAAE0tH,UAAU,SAAS7hH,GAAG,IAAI7L,EAAEwS,KAAK+6G,QAAQluH,KAAK,OAAOwM,EAAEP,OAAOtL,GAAGmV,eAAe,GAAGnV,GAAG,IAAIA,EAAE2tH,aAAa,WAAW,OAAOn7G,KAAK+6G,QAAQhnE,SAASvmD,EAAE4tH,YAAY,WAAW,OAAOp7G,KAAK06G,aAAaW,SAASr7G,KAAK06G,aAAaW,SAASr7G,KAAKs7G,iBAAiB9tH,EAAE+tH,gBAAgB,SAASliH,QAAG,IAASA,IAAIA,GAAE,GAAI,IAAI7L,EAAEwS,KAAKo7G,cAAcn0G,KAAK,OAAO5N,EAAEP,OAAOtL,GAAGmV,eAAe,GAAGnV,GAAG,IAAIA,EAAE8tH,cAAc,WAAW,IAAIjiH,EAAE2G,KAAKA,KAAK06G,aAAaW,SAAS,GAAG,IAAI7tH,EAAEa,EAAE8lH,QAAQljF,MAAM,SAASzjC,GAAG,GAAG,mBAAmBA,EAAE0kB,KAAK,OAAO1kB,EAAE0kB,KAAK7Y,GAAG,GAAG7L,EAAE0kB,gBAAgB9b,MAAM,OAAO5I,EAAE0kB,KAAKywE,MAAM,SAASn1F,GAAG,OAAO6L,EAAE6Y,KAAK1kB,MAAM,MAAM,IAAI6J,MAAM,2CAA2C,OAAO7J,IAAIwS,KAAK06G,aAAaW,SAAS7tH,EAAEqtH,SAAS76G,KAAK26G,UAAU36G,KAAK06G,aAAaW,UAAU7tH,EAAEguH,UAAU,WAAW,OAAOx7G,KAAK06G,aAAae,OAAOz7G,KAAK06G,aAAae,OAAOz7G,KAAK07G,eAAeluH,EAAEmuH,cAAc,SAAStiH,GAAG,OAAOA,EAAEP,OAAOkH,KAAKw7G,YAAY3uH,MAAM8V,eAAe,GAAG3C,KAAKw7G,YAAY3uH,MAAM,IAAIW,EAAEkuH,YAAY,WAAW,IAAIriH,EAAE2G,KAAKA,KAAK06G,aAAae,OAAO,GAAG,IAAIjuH,EAAET,EAAEonH,QAAQljF,MAAM,SAASzjC,GAAG,GAAG,mBAAmBA,EAAE0kB,KAAK,OAAO1kB,EAAE0kB,KAAK7Y,GAAG,GAAG7L,EAAE0kB,gBAAgB9b,MAAM,OAAO5I,EAAE0kB,KAAKywE,MAAM,SAASn1F,GAAG,OAAO6L,EAAE6Y,KAAK1kB,MAAM,MAAM,IAAI6J,MAAM,2CAA2C,OAAO7J,IAAIwS,KAAK06G,aAAae,OAAOjuH,EAAEqtH,SAAS76G,KAAK26G,UAAU36G,KAAK06G,aAAae,QAAQjuH,EAAE0qB,MAAM,WAAW,OAAOlY,KAAK46G,eAAe56G,KAAKi7G,UAAUj7G,KAAKs7G,gBAAgBt7G,KAAK07G,cAAc17G,MAAMxS,EAAE+sH,UAAU,WAAW,OAAOvtH,OAAOgoG,OAAO,GAAGh1F,KAAK06G,eAAeltH,EAAEgoD,UAAU,SAASn8C,GAAG,IAAI7L,EAAEwS,KAAK5S,EAAE,GAAGd,EAAE,EAAEyB,EAAE,GAAGM,EAAE,EAAE,GAAGrB,OAAOuY,KAAKlM,GAAG+V,SAAS,SAAS5hB,GAAG,IAAIT,EAAEsM,EAAE7L,GAAG,iBAAiBT,GAAGgB,EAAEP,GAAGT,EAAEsB,GAAG,GAAG,iBAAiBtB,IAAIK,EAAEI,GAAGT,EAAET,GAAG,MAAMA,EAAE,EAAE,CAAC,IAAIS,EAAEC,OAAOuY,KAAKnY,GAAGsK,EAAE3K,EAAEkkC,MAAM,SAAS53B,GAAG,OAAO7L,EAAEouH,KAAKviH,MAAM,GAAG3B,EAAE,CAAC,IAAIujG,EAAEj7F,KAAKw1C,UAAUpoD,EAAEsK,IAAI,QAAG,IAASujG,EAAE,OAAOA,EAAE,IAAIruG,EAAEG,EAAEkkC,MAAM,SAAS53B,GAAG,OAAO7L,EAAEquH,WAAWxiH,MAAM,GAAGzM,EAAE,CAAC,IAAID,EAAEqT,KAAKw1C,UAAUpoD,EAAER,IAAI,QAAG,IAASD,EAAE,OAAOA,GAAG,GAAG0B,EAAE,EAAE,CAAC,IAAIitD,EAAEtuD,OAAOuY,KAAKxX,GAAGkjC,MAAM,SAAS53B,GAAG,OAAO7L,EAAEsuH,UAAUziH,GAAE,MAAO,QAAG,IAASiiD,EAAE,OAAOt7C,KAAK+7G,eAAehuH,EAAEutD,MAAM9tD,EAAEsuH,UAAU,SAASziH,EAAE7L,QAAG,IAASA,IAAIA,GAAE,GAAI,IAAIJ,EAAE4S,KAAKu0C,iBAAiB5xC,cAAcrW,EAAE+M,EAAEsJ,cAAc5U,EAAE2J,EAAEy8G,QAAQY,sBAAsBzoH,GAAG,OAAOkB,GAAGO,IAAIzB,EAAEyB,EAAE4U,eAAerW,IAAIc,GAAGI,EAAEuuH,eAAe,SAAS1iH,GAAG,IAAI7L,EAAE,CAAC,GAAGJ,EAAEiM,EAAE/M,GAAE,EAAGyB,EAAEiS,KAAKw0C,oBAAoB,GAAG,iBAAiBzmD,EAAE,MAAM,MAAMsL,EAAE,IAAI,MAAMA,EAAE,IAAIjM,EAAEiM,EAAE+b,OAAO,GAAG,MAAM/b,EAAE,IAAI/M,GAAE,EAAGc,EAAEiM,EAAE+b,OAAO,IAAI5nB,EAAE,GAAG,MAAM6L,EAAE,GAAG7L,EAAE0V,KAAK,GAAG1V,EAAE0V,MAAM,IAAI,MAAM7J,EAAE,GAAGjM,EAAEiM,EAAE+b,OAAO,GAAG,MAAM/b,EAAE,KAAK/M,GAAE,EAAGc,EAAEiM,EAAE+b,OAAO,IAAI5nB,EAAEoW,QAAQlM,EAAEy8G,QAAQS,gBAAgB7mH,EAAEX,EAAEd,KAAK,GAAGkB,EAAEouH,KAAK,SAASviH,GAAG,OAAO2G,KAAKk7G,WAAU,KAAMpiH,OAAOO,GAAGsJ,eAAenV,EAAEquH,WAAW,SAASxiH,GAAG,OAAO2G,KAAKu7G,iBAAgB,KAAMziH,OAAOO,GAAGsJ,eAAenV,EAAEwuH,SAAS,SAAS3iH,GAAG,OAAO2G,KAAK27G,eAAc,KAAM7iH,OAAOO,GAAGsJ,eAAenV,EAAEyuH,GAAG,SAAS5iH,GAAG,OAAO2G,KAAK87G,UAAUziH,IAAI2G,KAAK47G,KAAKviH,IAAI2G,KAAK67G,WAAWxiH,IAAI7L,EAAEm1F,KAAK,SAAStpF,GAAG,IAAI7L,EAAEwS,KAAK,YAAO,IAAS3G,IAAIA,EAAE,IAAIA,EAAEspF,MAAM,SAAStpF,GAAG,OAAO7L,EAAEyuH,GAAG5iH,OAAOA,EAAluI,GAAuuI7L,EAAE2mH,QAAQvnH,EAAEyM,EAAEvN,QAAQ0B,EAAE2mH,SAAS+H,GAAG,SAAS7iH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEyB,GAAGzB,EAAEc,EAAE,MAAMd,EAAEoB,WAAWpB,EAAE,CAAC6nH,QAAQ7nH,GAAO+B,EAAE,6BAA6BtB,EAAE,CAAC,CAACmlB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,aAAaO,EAAEW,EAAEomH,QAAQE,cAAc,2BAA2Bh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,SAASO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,kCAAkCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,gBAAgB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,SAASO,EAAEW,EAAEomH,QAAQE,cAAc,2BAA2Bh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,mBAAmB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,gCAAgCO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,2CAA2Ch7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,uBAAuBO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,iCAAiCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,cAAcO,EAAEW,EAAEomH,QAAQE,cAAc,qCAAqCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,SAASO,EAAEW,EAAEomH,QAAQE,cAAc,iCAAiCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,SAASO,EAAEW,EAAEomH,QAAQE,cAAc,iCAAiCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,eAAeO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,kCAAkCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,kBAAkBO,EAAEW,EAAEomH,QAAQE,cAAc,sCAAsCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,cAAcO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,sCAAsCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,kBAAkB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,WAAWO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,0CAA0Ch7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,WAAW2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,UAAUO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,mCAAmCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,mBAAmB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,UAAUO,EAAEW,EAAEomH,QAAQE,cAAc,2CAA2Ch7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,iBAAiBqlB,KAAK7Y,GAAG,kBAAkB,cAAcjM,EAAEW,EAAEomH,QAAQE,cAAc,kDAAkDh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,iBAAiB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,qBAAqBO,EAAEW,EAAEomH,QAAQE,cAAc,iCAAiCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,kBAAkBO,EAAEW,EAAEomH,QAAQE,cAAc,2BAA2Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,kBAAkB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,kBAAkBO,EAAEW,EAAEomH,QAAQG,eAAe,mCAAmCj7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,WAAWO,EAAEW,EAAEomH,QAAQE,cAAc,6BAA6Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,aAAaO,EAAEW,EAAEomH,QAAQE,cAAc,+BAA+Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,SAAS2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,eAAeO,EAAEW,EAAEomH,QAAQE,cAAc,0BAA0Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,aAAaO,EAAEW,EAAEomH,QAAQE,cAAc,+BAA+Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAc,8BAA8Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,sBAAsB,gBAAgB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,cAAcO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,mBAAmB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,iBAAiBO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,IAAItL,EAAEomH,QAAQE,cAAc,0CAA0Ch7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,SAAS2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,QAAQO,EAAEW,EAAEomH,QAAQE,cAAc,4BAA4Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,SAASO,EAAEW,EAAEomH,QAAQE,cAAc,0CAA0Ch7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAc,qCAAqCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,4BAA4B2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,WAAWO,EAAEW,EAAEomH,QAAQE,cAAc,oDAAoDh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,YAAYO,EAAEW,EAAEomH,QAAQE,cAAc,qCAAqCh7G,IAAItL,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,sBAAsB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,UAAUO,EAAEW,EAAEomH,QAAQE,cAAc,2CAA2Ch7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,QAAQ2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,iBAAiBO,EAAEW,EAAEomH,QAAQE,cAAc,6BAA6Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,SAAS7Y,GAAG,IAAI7L,GAAG6L,EAAE6Y,KAAK,iBAAiB9kB,EAAEiM,EAAE6Y,KAAK,YAAY,OAAO1kB,GAAGJ,GAAGytH,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,mBAAmBO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,kBAAkB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,iBAAiBO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,uBAAuB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAK,UAAUO,EAAEW,EAAEomH,QAAQE,cAAchmH,EAAEgL,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,OAAO2oG,SAAS,SAASxhH,GAAG,IAAI7L,GAAG,IAAI6L,EAAEwjB,OAAO,OAAO,yBAAyB,eAAe,MAAM,CAAChwB,KAAKkB,EAAEomH,QAAQE,cAAc7mH,EAAE6L,GAAG06C,QAAQhmD,EAAEomH,QAAQG,eAAe9mH,EAAE6L,OAAO7L,EAAE2mH,QAAQpnH,EAAEsM,EAAEvN,QAAQ0B,EAAE2mH,SAASgI,GAAG,SAAS9iH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEyB,GAAGzB,EAAEc,EAAE,MAAMd,EAAEoB,WAAWpB,EAAE,CAAC6nH,QAAQ7nH,GAAG+B,EAAEjB,EAAE,IAAQL,EAAE,CAAC,CAACmlB,KAAK,CAAC,aAAa2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,wBAAwBh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAO0B,KAAK9iE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,kBAAkB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,yCAAyCh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAOkE,aAAatlE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,iCAAiCh7G,GAAGjM,EAAEW,EAAEomH,QAAQK,sBAAsBhnH,GAAG,MAAM,CAACX,KAAKwB,EAAE8mH,OAAOmE,QAAQvlE,QAAQvmD,EAAE4uH,YAAYhvH,KAAK,CAAC8kB,KAAK,CAAC,cAAc2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,6BAA6Bh7G,GAAGuI,QAAQ,SAAS,KAAKxU,EAAEW,EAAEomH,QAAQM,oBAAoBjnH,GAAGlB,EAAE,CAACO,KAAKwB,EAAE8mH,OAAOoE,MAAMxlE,QAAQvmD,GAAG,OAAOJ,IAAId,EAAE8vH,YAAYhvH,GAAGd,IAAI,CAAC4lB,KAAK,CAAC,uBAAuB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,qCAAqCh7G,GAAGuI,QAAQ,SAAS,KAAK,MAAM,CAAC/U,KAAKwB,EAAE8mH,OAAOqE,IAAIzlE,QAAQvmD,KAAK,CAAC0kB,KAAK,SAAS7Y,GAAG,IAAI7L,GAAG6L,EAAE6Y,KAAK,iBAAiB9kB,EAAEiM,EAAE6Y,KAAK,YAAY,OAAO1kB,GAAGJ,GAAGytH,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,8BAA8Bh7G,GAAGjM,EAAEW,EAAEomH,QAAQO,sBAAsBlnH,GAAGlB,EAAE,CAACO,KAAKwB,EAAE8mH,OAAOsE,QAAQ1lE,QAAQvmD,GAAG,OAAOJ,IAAId,EAAE8vH,YAAYhvH,GAAGd,IAAI,CAAC4lB,KAAK,CAAC,mBAAmB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,mCAAmCh7G,GAAGjM,EAAE,CAACP,KAAKwB,EAAE8mH,OAAOuE,OAAO,OAAOlsH,GAAGA,EAAE+I,SAASnJ,EAAE2mD,QAAQvmD,GAAGJ,IAAI,CAAC8kB,KAAK,CAAC,sBAAsB,gBAAgB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,kCAAkCh7G,IAAItL,EAAEomH,QAAQE,cAAc,mCAAmCh7G,IAAItL,EAAEomH,QAAQE,cAAc,aAAah7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAOG,WAAWvhE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,SAAS2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,uBAAuBh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAOE,KAAKthE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,2BAA2Bh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAOuB,MAAM3iE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,UAAU2oG,SAAS,WAAW,MAAM,CAAChuH,KAAKwB,EAAE8mH,OAAOwE,SAAS,CAACznG,KAAK,CAAC,QAAQ2oG,SAAS,WAAW,MAAM,CAAChuH,KAAKwB,EAAE8mH,OAAOyE,YAAY,CAAC1nG,KAAK,CAAC,iBAAiB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,mCAAmCh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE8mH,OAAO0E,aAAa9lE,QAAQvmD,MAAMA,EAAE2mH,QAAQpnH,EAAEsM,EAAEvN,QAAQ0B,EAAE2mH,SAASkI,GAAG,SAAShjH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEyB,GAAGzB,EAAEc,EAAE,MAAMd,EAAEoB,WAAWpB,EAAE,CAAC6nH,QAAQ7nH,GAAG+B,EAAEjB,EAAE,IAAQL,EAAE,CAAC,CAACmlB,KAAK,CAAC,cAAc2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK,MAAMq1G,OAAO,YAAY,CAACpqG,KAAK,CAAC,WAAW2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,aAAah7G,IAAI,OAAOjM,EAAE,CAAC6Z,KAAK5Y,EAAE+mH,cAAc+D,OAAOmD,OAAO,UAAU,OAAO9uH,IAAIJ,EAAEmvH,MAAM/uH,GAAGJ,IAAI,CAAC8kB,KAAK,CAAC,2BAA2B2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,OAAOoD,OAAO,WAAW,CAACpqG,KAAK,CAAC,SAAS2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,OAAOoD,OAAO,QAAQC,MAAM,UAAU,CAACrqG,KAAK,CAAC,eAAe2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,OAAOoD,OAAO,SAASC,MAAM,sBAAsB,CAACrqG,KAAK,CAAC,SAAS2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,OAAOoD,OAAO,YAAY,CAACpqG,KAAK,CAAC,kBAAkB2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,UAAU,CAAChnG,KAAK,SAAS7Y,GAAG,IAAI7L,EAAE6L,EAAE6Y,KAAK,gBAAgB9kB,EAAEiM,EAAE6Y,KAAK,uBAAuB,OAAO1kB,IAAIJ,GAAGytH,SAAS,SAASxhH,GAAG,IAAI7L,EAAEO,EAAEomH,QAAQE,cAAc,iBAAiBh7G,GAAG,MAAM,CAAC4N,KAAK5Y,EAAE+mH,cAAc+D,OAAOmD,OAAO,QAAQC,MAAM/uH,KAAK,CAAC0kB,KAAK,CAAC,mBAAmB,iBAAiB2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,OAAOmD,OAAO,WAAW,CAACpqG,KAAK,CAAC,aAAa2oG,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,UAAU,CAACjnG,KAAK,SAAS7Y,GAAG,MAAM,eAAeA,EAAEk7C,gBAAe,IAAKsmE,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,OAAOmD,OAAO,gBAAgB,CAACpqG,KAAK,SAAS7Y,GAAG,MAAM,SAASA,EAAEk7C,gBAAe,IAAKsmE,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,UAAU,CAACjnG,KAAK,SAAS7Y,GAAG,MAAM,kBAAkBA,EAAEk7C,kBAAkBsmE,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,OAAOmD,OAAO,eAAe,CAACpqG,KAAK,SAAS7Y,GAAG,IAAI7L,EAAE+rB,OAAOzgB,OAAOO,EAAE8hH,gBAAgBp4G,MAAM,KAAK,IAAI,MAAM,YAAY1J,EAAE6hH,WAAU,IAAK1tH,GAAG,GAAGqtH,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc8D,UAAU,CAAChnG,KAAK,SAAS7Y,GAAG,MAAM,YAAYA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAc+D,UAAU,CAACjnG,KAAK,SAAS7Y,GAAG,MAAM,UAAUA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAch3E,QAAQk+E,OAAO,WAAW,CAACpqG,KAAK,SAAS7Y,GAAG,MAAM,YAAYA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAch3E,WAAW,CAAClsB,KAAK,SAAS7Y,GAAG,MAAM,UAAUA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAch3E,WAAW,CAAClsB,KAAK,SAAS7Y,GAAG,MAAM,kBAAkBA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAcgE,MAAM,CAAClnG,KAAK,SAAS7Y,GAAG,MAAM,SAASA,EAAE6hH,WAAU,IAAKL,SAAS,WAAW,MAAM,CAAC5zG,KAAK5Y,EAAE+mH,cAAcgE,OAAO5rH,EAAE2mH,QAAQpnH,EAAEsM,EAAEvN,QAAQ0B,EAAE2mH,SAASqI,GAAG,SAASnjH,EAAE7L,EAAEJ,GAAG,aAAaI,EAAEE,YAAW,EAAGF,EAAE2mH,aAAQ,EAAO,IAAI7nH,EAAEyB,GAAGzB,EAAEc,EAAE,MAAMd,EAAEoB,WAAWpB,EAAE,CAAC6nH,QAAQ7nH,GAAG+B,EAAEjB,EAAE,IAAQL,EAAE,CAAC,CAACmlB,KAAK,SAAS7Y,GAAG,MAAM,mBAAmBA,EAAEk7C,gBAAe,IAAKsmE,SAAS,SAASxhH,GAAG,GAAG,WAAW6Y,KAAK7Y,GAAG,MAAM,CAACxM,KAAKwB,EAAE6mH,WAAW6E,OAAO,IAAIvsH,EAAEO,EAAEomH,QAAQE,cAAc,0BAA0Bh7G,GAAG,MAAM,CAACxM,KAAKwB,EAAE6mH,WAAW4E,SAAS/lE,QAAQvmD,KAAK,CAAC0kB,KAAK,CAAC,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAKwB,EAAE6mH,WAAW8E,SAAS5sH,EAAEW,EAAEomH,QAAQE,cAAc,6BAA6Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,SAAS7Y,GAAG,OAAOA,EAAE6Y,KAAK,YAAY2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAKwB,EAAE6mH,WAAW+E,QAAQ7sH,EAAEW,EAAEomH,QAAQE,cAAc,4BAA4Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,SAAS7Y,GAAG,IAAI7L,EAAE6L,EAAE6Y,KAAK,UAAU9kB,EAAEiM,EAAE6Y,KAAK,eAAe,OAAO1kB,IAAIJ,GAAGytH,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAKwB,EAAE6mH,WAAWgF,OAAO9sH,EAAEW,EAAEomH,QAAQE,cAAc,2BAA2Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,IAAI,CAAC0kB,KAAK,CAAC,4BAA4B2oG,SAAS,WAAW,MAAM,CAAChuH,KAAKwB,EAAE6mH,WAAW6E,SAAS,CAAC7nG,KAAK,CAAC,mBAAmB2oG,SAAS,SAASxhH,GAAG,IAAI7L,EAAE,CAACX,KAAKwB,EAAE6mH,WAAWiF,QAAQ/sH,EAAEW,EAAEomH,QAAQE,cAAc,4BAA4Bh7G,GAAG,OAAOjM,IAAII,EAAEumD,QAAQ3mD,GAAGI,KAAKA,EAAE2mH,QAAQpnH,EAAEsM,EAAEvN,QAAQ0B,EAAE2mH,Y,0vBCU3+vB,MAGMl2F,EAASF,oBAAUM,GAoVV,QAxSf,MAIIpoB,cACI+J,KAAKqI,QAQTA,QAOIrI,KAAK+kF,UAAW,EAMhB/kF,KAAKy8G,kBAAoB,IAAI37F,IAO7B9gB,KAAK4wD,MAAQ,GAMb5wD,KAAK08G,oBAAsB,GAO3B18G,KAAK28G,eAAiB,GAEtB38G,KAAKimC,uBAAuB,CACxB,WAAczL,UAAUsZ,UACxB,aAAgBh0B,IAAQm1B,YAOhCjyB,UACI/E,EAAO9Y,KAAK,mCAERnF,KAAKy8G,mBAAqBz8G,KAAKy8G,kBAAkBx5F,KAAO,GACxDjjB,KAAKy8G,kBAAkBrtG,QAAQ7K,IACI,mBAApBA,EAAQye,SACfze,EAAQye,YAKpBhjB,KAAK48G,qBAAqB,IAC1B58G,KAAK+kF,UAAW,EAQpB63B,qBAAqBzzG,GACjB,GAAInJ,KAAK+kF,SACL,OAGJ/kF,KAAKy8G,kBAAoB,IAAI37F,IAAI3X,GAEjCnJ,KAAK68G,qBAGL,MAAMjsD,EAAQ5wD,KAAK4wD,MAEnB5wD,KAAK4wD,MAAQ,KACTA,GACAA,EAAMxhD,QAAQiY,GAASrnB,KAAK88G,WAAWz1F,IAS/Cw1F,qBACI78G,KAAKy8G,kBAAkBrtG,QAAQ7K,IAC3B,IACIA,EAAQw4G,kBAAkB/8G,KAAK08G,qBACjC,MAAO13G,GACLiZ,EAAO9Y,KAAK,uEACiBH,MAezCihC,uBAAuB3e,GACnBtnB,KAAK08G,oBAAL,OACO18G,KAAK08G,qBACLp1F,GAGPtnB,KAAK68G,qBAQTG,kBAAkBnwH,GACdmT,KAAK28G,eAAiB9vH,EACtBmT,KAAKimC,uBAAuB,CAAE,gBAAmBp5C,IAgBrDk6B,UAAUU,EAAWH,EAAa,IAC9B,GAAItnB,KAAK+kF,SACL,OAGJ,IAAI19D,EAAQ,KAEa,iBAAdI,EACPJ,EAAQ,CACJpgB,KAAMgiB,IACNuB,OAAQ/C,EACRgD,cAAehD,EACfsD,OAAQtD,EACRxqB,WAAYqqB,GAEY,iBAAdG,IACdJ,EAAQI,GAGPznB,KAAKi9G,sBAAsB51F,GAOhCrnB,KAAK88G,WAAWz1F,GANZpJ,EAAOjZ,MACF,mCAAkC4H,KAAKwL,UAAUiP,IAoB9D41F,sBAAsB51F,GAClB,IAAKA,EACD,OAAO,EAGNA,EAAMpgB,OACPogB,EAAMpgB,KAAOgiB,KAGjB,MAAMhiB,EAAOogB,EAAMpgB,KAEnB,OAAIA,IAASgiB,KAAoBhiB,IAASiiB,KACnCjiB,IAASmiB,KAAWniB,IAASkiB,KAChClL,EAAOjZ,MAAO,uBAAsBiC,IAE7B,GAGPA,IAASiiB,IACFyP,QAAQtR,EAAMx6B,OAKzBw6B,EAAMmD,OAASnD,EAAMmD,QAAUnD,EAAMx6B,MAAQw6B,EAAMoD,cACnDpD,EAAMoD,cAAgBpD,EAAMoD,eAAiBpD,EAAMx6B,MAAQw6B,EAAMmD,OACjEnD,EAAM0D,OAAS1D,EAAM0D,QAAU1D,EAAMx6B,MAAQw6B,EAAMmD,QAC5CnD,EAAMoD,cAERpD,EAAMmD,QAAWnD,EAAMoD,eAAkBpD,EAAM0D,UAQhD9jB,IAASkiB,MACT9B,EAAM61F,WAAa71F,EAAM61F,YAAc,sBACvC71F,EAAM81F,cAAgB91F,EAAM81F,eAAiB,aACjB,eAAxB91F,EAAM81F,eAAmC91F,EAAM3B,cAC/C2B,EAAM3B,YAAc1lB,KAAK28G,gBAIxBt1F,EAAM61F,YAAe71F,EAAM+1F,UACxB/1F,EAAM81F,eAAkB91F,EAAM3B,gBAClCzH,EAAOjZ,MACH,gFAGG,IArBXiZ,EAAOjZ,MACH,6DAEG,IAgCfq4G,iBAAiBh2F,GACb,QAAIrnB,KAAK4wD,QACL5wD,KAAK4wD,MAAM1tD,KAAKmkB,GAIZrnB,KAAK4wD,MAAMr6D,OAxTJ,KAyTPyJ,KAAK4wD,MAAM3sD,OAAO,EAAG,IAGlB,GAYf64G,WAAWz1F,GACHrnB,KAAKq9G,iBAAiBh2F,IAGtBrnB,KAAKy8G,kBAAkBrtG,QAAQ7K,IAC3B,IACIA,EAAQwiB,UAAUM,GACpB,MAAOhuB,GACL4kB,EAAO9Y,KAAM,kCAAiC9L,U,mFCzVlE,yEAKA,MAAM4kB,EAASF,oBAAUM,GAUlB,MAAMqE,EAOTzsB,YAAY01C,EAAS2xE,GACjBt9G,KAAKwf,aAAemsB,EACpB3rC,KAAKu9G,UAAY,EACjBv9G,KAAKw9G,YAAc,EACnBx9G,KAAKy9G,yBAA2BH,EAChCt9G,KAAKwmB,MAAQ,IAAIw5B,IAQrBl9B,oBACI,MAAO,CACH46F,kBA9BI,GA8Be19G,KAAKwmB,MAAM25B,cAAwB8hC,QAAQ,GAC9D07B,cAAe39G,KAAKw9G,aAS5B56F,gBAEI5iB,KAAK49G,qBAAuBvvE,IACxB,MAAMie,EAAUje,EAAKwvE,aAErB,IAAK,MAAMC,KAAQxxD,EACftsD,KAAKu9G,YACLv9G,KAAKw9G,YAAcpyG,KAAKgpB,IAAIp0B,KAAKw9G,YAAaM,EAAKjS,UAAU5pB,QAAQ,IAK7EhkE,EAAO/Y,KAAK,6DACZlF,KAAK+9G,SAAW,IAAIvK,oBAAoBxzG,KAAK49G,sBAC7C59G,KAAK+9G,SAASC,QAAQ,CAAE/2G,KAAM,WAC1Bg3G,UAAU,IACd,MAAMtiB,EAAYxzF,KAAKgM,MAGvBnU,KAAKk+G,oBAAsB5hF,YAAY,KACnC,MAAMnoB,EAAMhM,KAAKgM,MACX+sE,EAAWlhF,KAAKm+G,gBACfhqG,EAAMnU,KAAKm+G,gBA/DR,KAgEHhqG,EAAMwnF,GAhEH,IAiEJR,EAAOn7F,KAAKu9G,UAAYr8B,EAE9BlhF,KAAKwmB,MAAM05B,QAAQi7C,GACnBn7F,KAAKwf,aAAa0D,KACdtB,IAAmC5hB,KAAK8iB,qBAG5C9iB,KAAKu9G,UAAY,EACjBv9G,KAAKm+G,eAAiBh2G,KAAKgM,OAC5BnU,KAAKy9G,0BAOZ56F,eACI7iB,KAAK+9G,UAAY/9G,KAAK+9G,SAASxuG,aAC/BvP,KAAK49G,qBAAuB,KACxB59G,KAAKk+G,sBACL5jF,cAAct6B,KAAKk+G,qBACnBl+G,KAAKk+G,oBAAsB,U,2FC7FvC,uFAOA,MAAMtmF,EAAuBxa,EAAQ,IAE/Ba,EAASF,oBAAUM,GAMnB+/F,EAAuB,GA6D7B,SAASC,EAAoBC,EAAaC,GACtC,OAAKA,GAAgBA,GAAgB,IACzBD,GAAeA,GAAe,EAC/B,EAGJlzG,KAAKozG,MAAOF,EAAcC,EAAgB,KAOrD,SAASE,IACLz+G,KAAK0+G,KAAO,GACZ1+G,KAAK2+G,QAAU,CACX5hB,SAAU,EACV6hB,OAAQ,GAEZ5+G,KAAKq2B,WAAa,GAClBr2B,KAAK6+G,UAAY,EACjB7+G,KAAKwyB,MAAQ,GAqDjB,SAASssF,IAML9+G,KAAKk4B,UAAY,GAMjBl4B,KAAK2+G,QAAU,GAMf3+G,KAAK++G,WAAa,KAMlB/+G,KAAK4d,UAAY,GAkBN,SAASohG,EAChBh+F,EACAL,EACA28F,EACA99F,GAgBJxf,KAAKi/G,aAAen/F,IAAQm1B,UAC5B,MAAM1vC,EAAO64G,EAAqBp+G,KAAKi/G,cAEvC,IAAK15G,EAED,KAAO,qBAAoBvF,KAAKi/G,iCAOpCj/G,KAAKk/G,qBACCp/F,IAAQyV,YAAczV,IAAQsQ,YAUpCpwB,KAAKm/G,cACCn/G,KAAKk/G,qBACDl/G,KAAKo/G,6BAA6B75G,GAClCvF,KAAKq/G,0BAA0B95G,GAEzCvF,KAAKghB,eAAiBA,EACtBhhB,KAAKs/G,0BAA4B,KACjCt/G,KAAKu/G,yBAA2B,KAChCv/G,KAAKw/G,mBAAqB,KAC1Bx/G,KAAKy/G,oBAAsB,KAC3Bz/G,KAAK0/G,wBAA0B,GAC/B1/G,KAAK2/G,sBAAwB,KAC7B3/G,KAAKwf,aAAeA,EACpBxf,KAAK4/G,gBAAkB,IAAId,EAG3B9+G,KAAK6/G,yBAA2Bl/F,EAEhC3gB,KAAK8/G,gBAAkB,KACvB9/G,KAAK+/G,mBAAqBzC,EAM1Bt9G,KAAKggH,WAAa,IAAIzgG,IAnP1B6+F,EAAqB6B,IAAS5sE,SAAW,CACrC,KAAQ,OACR,gBAAmB,kBACnB,YAAe,cACf,YAAe,cACf,cAAiB,gBACjB,UAAa,YACb,cAAiB,gBACjB,GAAM,UACN,KAAQ,OACR,SAAY,YAEhB+qE,EAAqB6B,IAAS9sE,QAAU,CACpC,iBAAoB,gCACpB,cAAiB,6BACjB,cAAiB,oBACjB,cAAiB,oBACjB,aAAgB,mBAChB,iBAAoB,uBACpB,KAAQ,OACR,gBAAmB,kBACnB,YAAe,cACf,YAAe,cACf,cAAiB,gBACjB,UAAa,YACb,cAAiB,gBACjB,wBAA2B,0BAC3B,uBAA0B,yBAC1B,oBAAuB,sBACvB,mBAAsB,qBACtB,sBAAyB,wBACzB,kBAAqB,oBACrB,gBAAmB,kBACnB,iBAAoB,mBACpB,qBAAwB,UACxB,oBAAuB,0BACvB,mBAAsB,yBACtB,GAAM,KACN,KAAQ,OACR,SAAY,YAEhBirE,EAAqB6B,IAAS7sE,OACxBgrE,EAAqB6B,IAAS9sE,QACpCirE,EAAqB6B,IAASzsE,MACxB4qE,EAAqB6B,IAAS9sE,QACpCirE,EAAqB6B,IAASxsE,UACxB2qE,EAAqB6B,IAAS9sE,QACpCirE,EAAqB6B,IAAS1sE,QACxB6qE,EAAqB6B,IAAS9sE,QACpCirE,EAAqB6B,IAASvsE,cACxB0qE,EAAqB6B,IAAS9sE,QAqCpCsrE,EAAUvwH,UAAUgyH,QAAU,SAASxB,GACnC1+G,KAAK0+G,KAAOA,GAAQ,IAOxBD,EAAUvwH,UAAUiyH,cAAgB,SAAS9pF,GACzCr2B,KAAKq2B,WAAaA,GAAc,IAQpCooF,EAAUvwH,UAAUkyH,WAAa,SAASzB,GACtC3+G,KAAK2+G,QAAQ5hB,UAAY4hB,EAAQ5hB,SACjC/8F,KAAK2+G,QAAQC,QAAUD,EAAQC,QAOnCH,EAAUvwH,UAAUmyH,aAAe,WAC/BrgH,KAAK2+G,QAAQ5hB,SAAW,EACxB/8F,KAAK2+G,QAAQC,OAAS,GAO1BH,EAAUvwH,UAAUoyH,aAAe,SAASzB,GACxC7+G,KAAK6+G,UAAYA,GAAa,GAGlCJ,EAAUvwH,UAAUqyH,SAAW,SAAS/tF,GACpCxyB,KAAKwyB,MAAQA,GAAS,IA4H1BwsF,EAAe9wH,UAAU01B,KAAO,WACxB5jB,KAAK2/G,wBACLrlF,cAAct6B,KAAK2/G,uBACnB3/G,KAAK2/G,sBAAwB,MAG7B3/G,KAAK8/G,kBACLxlF,cAAct6B,KAAK8/G,iBACnB9/G,KAAK8/G,gBAAkB,OAQ/Bd,EAAe9wH,UAAUiqE,cAAgB,SAASnzD,GAC9C4yB,EAAqBC,iBAAiB7yB,GACtCiZ,EAAOjZ,MAAM,kBAAmBA,GAChChF,KAAK4jB,QAMTo7F,EAAe9wH,UAAU6uB,MAAQ,SAASyjG,GAClCA,IACI1gG,IAAQ0mE,yBACRvoE,EAAO/Y,KAAK,6DAEhBlF,KAAK2/G,sBAAwBrjF,YACzB,KACI,GAAIxc,IAAQ0mE,wBAAyB,CACjC,MAAMi6B,EAAczgH,KAAKghB,eAAe0/F,iBAExC,IAAK,MAAM17F,KAAQy7F,EACf,GAAIA,EAAYtyH,eAAe62B,GAAO,CAGlC,MAAM2jB,EAAiC,IAApB83E,EAAYz7F,GAE/BhlB,KAAKwf,aAAa0D,KACdtB,IACA5hB,KAAKghB,eACLzH,OAAOjB,SAAS0M,EAAM,IACtB2jB,GACA,SAKZ3oC,KAAKghB,eAAeuvF,SAChBrrD,IACI,IAAIs0C,EAAU,KAMVA,EAJCt0C,GAAWA,EAAOz/C,QACS,mBAAlBy/C,EAAOz/C,OAGPy/C,EAAOz/C,SAFPy/C,EAIdllD,KAAKu/G,yBAA2B/lB,EAC5Bx5F,KAAKk/G,qBACLl/G,KAAK2gH,6BAEL3gH,KAAK4gH,0BAGT5gH,KAAKs/G,0BACCt/G,KAAKu/G,0BAEfv6G,GAAShF,KAAKm4D,cAAcnzD,KAIxChF,KAAK6/G,2BAIb7/G,KAAK8/G,gBAAkBxjF,YACnB,KAEIt8B,KAAKghB,eAAeuvF,SAChBrrD,IACI,IAAIs0C,EAAU,KAQVA,EANCt0C,GAAWA,EAAOz/C,QACS,mBAAlBy/C,EAAOz/C,OAKPy/C,EAAOz/C,SAHPy/C,EAMdllD,KAAKw/G,mBAAqBhmB,EAC1B,IACQx5F,KAAKk/G,qBACLl/G,KAAK6gH,wBAEL7gH,KAAK8gH,qBAEX,MAAOznH,GACLu+B,EAAqBC,iBAAiBx+B,GACtC4kB,EAAOjZ,MAAO,mBAAkB3L,EAAKA,GAGzC2G,KAAKy/G,oBAAsBz/G,KAAKw/G,oBAEpCx6G,GAAShF,KAAKm4D,cAAcnzD,KAGpChF,KAAK+/G,qBAYbf,EAAe9wH,UAAUmxH,0BAA4B,SAAS95G,GAkB1D,IAAIw7G,EAEJ,OAAQ/gH,KAAKi/G,cACb,KAAKgB,IAAS9sE,OACd,KAAK8sE,IAAS7sE,MACd,KAAK6sE,IAASzsE,KACd,KAAKysE,IAASxsE,SAQVstE,EAAgB,CAACxtF,EAAM1lC,IAAQ0lC,EAAKytF,KAAKnzH,GACzC,MACJ,KAAKoyH,IAASvsE,aAIVqtE,EAAgB,SAASxtF,EAAM1lC,GAC3B,IAAIN,EAaJ,OAXAgmC,EAAKnQ,OAAOu/D,KAAKl5B,KACTA,EAAKt7D,eAAeN,KACpBN,EAAQk8D,EAAK57D,IAEN,IAORN,GAEX,MACJ,QACIwzH,EAAgB,CAACxtF,EAAM1lC,IAAQ0lC,EAAK1lC,GAMxC,MAAO,CAAC0lC,EAAM1mC,IAASk0H,EAAcxtF,EA1DjB,SAAS1mC,GACzB,MAAMgB,EAAM0X,EAAK1Y,GAEjB,GAAIgB,EACA,OAAOA,EAIX,KAAO,iBAAgBhB,sBAkDgBo0H,CAAYp0H,KAW3DmyH,EAAe9wH,UAAUgzH,mBAAqB,SAASh8D,EAAQr4D,GAC3D,IAAIU,EAAQyS,KAAKm/G,cAAcj6D,EAAQr4D,GAMvC,MAJqB,iBAAVU,IACPA,EAAQgsB,OAAOhsB,IAGfmsB,MAAMnsB,GACC,EAGJ6d,KAAKgpB,IAAI,EAAG7mC,IAQvByxH,EAAe9wH,UAAU4yH,mBAAqB,WAC1C,IAAK9gH,KAAKy/G,oBACN,OAGJ,MAAM0B,EAAenhH,KAAKm/G,cACpBiC,EAAgB,GAEtB,IAAK,MAAMxxE,KAAO5vC,KAAKw/G,mBAAoB,CACvC,IAAKx/G,KAAKw/G,mBAAmBrxH,eAAeyhD,GACxC,SAEJ,MAAMz7B,EAAMnU,KAAKw/G,mBAAmB5vE,GAGpC,IAAKz7B,EACD,SAGJ,IACI,MAAMktG,EAAmBF,EAAahtG,EAAK,oBACrCmtG,EAAgBH,EAAahtG,EAAK,kBAEpCktG,GAAoBC,KACpBthH,KAAK4/G,gBAAgB1nF,UAAY,CAC7B,SAAY9sB,KAAKozG,MAAM6C,EAAmB,KAC1C,OAAUj2G,KAAKozG,MAAM8C,EAAgB,OAG/C,MAAOjoH,IAET,GAAiB,sBAAb8a,EAAIlN,KAA8B,CAClC,IAAIq/E,EAAQz3D,EAAI0yF,EAAoBC,EAChCC,EAAqBp2F,EAAKpkB,EAE9B,IAEI,GADAq/E,EAAS66B,EAAahtG,EAAK,qBACtBmyE,EACD,SAGJz3D,EAAKsyF,EAAahtG,EAAK,iBACvBlN,EAAOk6G,EAAahtG,EAAK,iBACzBqtG,EAAUL,EAAahtG,EAAK,gBAC5BotG,EAAqBJ,EAAahtG,EAAK,sBACvCstG,EAAsBN,EAAahtG,EAAK,uBACxCkX,EAAMrrB,KAAKkhH,mBAAmB/sG,EAAK,wBACrC,MAAO9a,IACT,IAAKw1B,IAAO5nB,IAASu6G,GAAsB,SAAXl7B,EAC5B,SAIJ,MAAMo7B,EAA2B1hH,KAAK4/G,gBAAgBhiG,UAEjD8jG,EAAyB/+B,KACtBn1F,GACIA,EAAEqhC,KAAOA,GACFrhC,EAAEyZ,OAASA,GACXzZ,EAAEg0H,UAAYA,IAC7BE,EAAyBx+G,KAAK,CAC1B2rB,KACA5nB,OACAu6G,UACArjE,IAAKn+C,KAAKghB,eAAe8kB,MACzBy7E,qBACAE,sBACAp2F,QAGR,SAGJ,GAAiB,kBAAblX,EAAIlN,KAA0B,CAE9B,GAAkB,cAAdkN,EAAI84B,QAA0B94B,EAAIwtG,SAClC,SAGJ,MAAMC,EAAQ5hH,KAAKw/G,mBAAmBrrG,EAAI0tG,kBACpCC,EAAS9hH,KAAKw/G,mBAAmBrrG,EAAI4tG,mBAE3C/hH,KAAK4/G,gBAAgBhiG,UAAU1a,KAAK,CAChC2rB,GAAK,GAAEizF,EAAOE,aAAaF,EAAOG,aAClCh7G,KAAM26G,EAAMhkG,UACZ4jG,QAAU,GAAEI,EAAMI,aAAaJ,EAAMK,aACrC9jE,IAAKn+C,KAAKghB,eAAe8kB,MACzBy7E,mBAAoBK,EAAMM,cAC1BT,oBAAqBK,EAAOI,gBAIpC,GAAiB,SAAb/tG,EAAIlN,MAAgC,gBAAbkN,EAAIlN,MACX,eAAbkN,EAAIlN,MAAsC,UAAbkN,EAAIlN,KACpC,SAGJ,MAAMk7G,EAASniH,KAAKy/G,oBAAoB7vE,GACxC,IAAI5qB,EAAOhlB,KAAKkhH,mBAAmB/sG,EAAK,QAOxC,GAJiB,UAAbA,EAAIlN,MAAoB7Q,MAAMC,QAAQ8d,EAAIiuG,WAC1Cp9F,EAAOzL,OAAOpF,EAAIiuG,QAAQ,MAGzBD,IAAWn9F,EACZ,SAYJ,IAAqB,IAAjB7Q,EAAIkuG,WAA0C,IAArBluG,EAAImuG,aAC7B,SAGJ,IAAIC,EAAYviH,KAAKggH,WAAW7yH,IAAI63B,GAE/Bu9F,IACDA,EAAY,IAAI9D,EAChBz+G,KAAKggH,WAAW5+F,IAAI4D,EAAMu9F,IAG9B,IAAIC,GAAmB,EACnB30H,EAAM,kBACN40H,EAAatB,EAAahtG,EAAKtmB,GAE/B,MAAO40H,GACkC,KAAfA,IAC1BD,GAAmB,EACnB30H,EAAM,cACN40H,EAAatB,EAAahtG,EAAKtmB,GAC3B,MAAO40H,GACPxkG,EAAO9Y,KAAK,oDAGfs9G,GAAcA,EAAa,KAC5BA,EAAa,GAGjB,MAAMC,EAAgB1iH,KAAKkhH,mBAAmBiB,EAAQt0H,GAChD80H,EAAcv3G,KAAKgpB,IAAI,EAAGquF,EAAaC,GAEvCE,EACA5iH,KAAKkhH,mBAAmB/sG,EAAK,eAC7B0uG,EACA7iH,KAAKkhH,mBAAmBiB,EAAQ,eAChCW,EAAkB13G,KAAKgpB,IAAI,EAAGwuF,EAAiBC,GAErDN,EAAUrC,QAAQ,CACd6C,aAAcJ,EAAcG,EAC5BE,YAAaF,EACbN,qBAGJ,MAAMS,EACAjjH,KAAKkhH,mBAAmB/sG,EAAK,iBAC7B+uG,EACAljH,KAAKkhH,mBAAmBiB,EAAQ,iBAChCgB,EACA/3G,KAAKgpB,IAAI,EAAG6uF,EAAmBC,GAErC,IAAIxtB,EAAY,EAGZ0tB,EAAsBjC,EAAahtG,EAAK,aAET,iBAAxBivG,GAC2B,iBAAxBA,IACVA,EAAsB7pG,OAAO6pG,GACxB1pG,MAAM0pG,KACPhC,EAAcp8F,GAAQo+F,EAClBA,EAAsB,IACtB1tB,EAAY0tB,EACNjC,EAAagB,EAAQ,gBAIvCzsB,EAAYtqF,KAAKgpB,IAAI,EAAGshE,GAExB,MAAM2tB,EAASlvG,EAAIq0E,UAAY25B,EAAO35B,UACtC,IAAI86B,EAAsB,EAAGC,EAAkB,EAE3CF,EAAS,IAETC,EAAsBl4G,KAAKozG,MAAuB,EAAhB2E,EAAqBE,GACvDE,EAAkBn4G,KAAKozG,MAAmB,EAAZ9oB,EAAiB2tB,IAGnDd,EAAUnC,WAAW,CACjB,SAAYkD,EACZ,OAAUC,IAGd,MAAMltF,EAAa,CACfnC,OAAQ,KACRqC,MAAO,MAGX,IACI,IAAIrC,EAAQqC,IAEPrC,EAASitF,EAAahtG,EAAK,8BACxBoiB,EAAQ4qF,EAAahtG,EAAK,6BAGtB+f,EAASitF,EAAahtG,EAAK,0BAC/BoiB,EAAQ4qF,EAAahtG,EAAK,0BAH9BkiB,EAAWnC,OAASA,EACpBmC,EAAWE,MAAQA,GAMzB,MAAOl9B,IAGT,IAAIq+B,EAoBAlF,EAlBJ,IACIkF,EAAYypF,EAAahtG,EAAK,0BACvBgtG,EAAahtG,EAAK,sBAAwB,EACnD,MAAO9a,GAGL,IACIq+B,EAAY13B,KAAKkhH,mBAAmB/sG,EAAK,iBAC3C,MAAOhE,KAEboyG,EAAUjC,aAAal1G,KAAKozG,MAAM9mF,GAAa,IAE3CrB,EAAWnC,QAAUmC,EAAWE,MAChCgsF,EAAUpC,cAAc9pF,GAExBksF,EAAUpC,cAAc,MAM5B,IACI3tF,EAAQ2uF,EAAahtG,EAAK,kBAAoB,GAChD,MAAO9a,IAETkpH,EAAUhC,SAAS/tF,GAIvBxyB,KAAKwf,aAAa0D,KACdtB,IAAkC5hB,KAAKghB,eAAgBogG,GAE3DphH,KAAKwjH,yBAMTxE,EAAe9wH,UAAUs1H,sBAAwB,WAE7C,MAAMjF,EAAe,CACjBxhB,SAAU,EACV6hB,OAAQ,GAENN,EAAc,CAChBvhB,SAAU,EACV6hB,OAAQ,GAEZ,IAAI6E,EAAkB,EAClBC,EAAgB,EACpB,MAAMC,EAAc,GACdC,EAAa,GACb7gD,EAAS,GACf,IAAI8gD,EAAuB,EACvBC,EAAqB,EACrBC,EAAa,GACbC,EAAuB,EACvBC,EAAqB,EACrBC,EAAa,GAEjB,IAAK,MAAQl/F,EAAMu9F,KAAeviH,KAAKggH,WAAY,CAE/C,MAAMtB,EAAO6D,EAAU7D,KACjBz3G,EAAOy3G,EAAK8D,iBAAmB,WAAa,SAElDjE,EAAat3G,IAASy3G,EAAKqE,aAC3BzE,EAAYr3G,IAASy3G,EAAKsE,YAG1BS,GAAmBlB,EAAU5D,QAAQ5hB,SACrC2mB,GAAiBnB,EAAU5D,QAAQC,OAGnC,MAAMtgF,EAAQt+B,KAAKghB,eAAe4nB,eAAe5jB,GAEjD,GAAIsZ,EAAO,CACHA,EAAMuK,gBACNg7E,GAAwBtB,EAAU5D,QAAQ5hB,SAC1C+mB,GAAsBvB,EAAU5D,QAAQC,OACxCmF,EAAaxB,EAAU/vF,QAEvBwxF,GAAwBzB,EAAU5D,QAAQ5hB,SAC1CknB,GAAsB1B,EAAU5D,QAAQC,OACxCsF,EAAa3B,EAAU/vF,OAG3B,MAAMrH,EAAgBmT,EAAMq+B,mBAE5B,GAAIxxC,EAAe,CACf,MAAMkL,EAAaksF,EAAUlsF,WAE7B,GAAIA,EAAWE,OACJF,EAAWnC,SACW,IAAtBmC,EAAWE,QACY,IAAvBF,EAAWnC,OAAe,CACjC,MAAMiwF,EAAkBR,EAAYx4F,IAAkB,GAEtDg5F,EAAgBn/F,GAAQqR,EACxBstF,EAAYx4F,GAAiBg5F,EAEjC,GAA4B,IAAxB5B,EAAU1D,UAAiB,CAC3B,MAAMuF,EAAiBR,EAAWz4F,IAAkB,GAEpDi5F,EAAep/F,GAAQu9F,EAAU1D,UACjC+E,EAAWz4F,GAAiBi5F,EAEhC,GAAIL,EAAWxtH,QAAU2tH,EAAW3tH,OAAQ,CACxC,MAAM8tH,EAAY,CACd,MAASN,EACT,MAASG,GAGPI,EAAavhD,EAAO53C,IAAkB,GAE5Cm5F,EAAWt/F,GAAQq/F,EACnBthD,EAAO53C,GAAiBm5F,QAG5BrmG,EAAOjZ,MAAO,iCAAgCs5B,GAItDikF,EAAUlC,eAGdrgH,KAAK4/G,gBAAgBjB,QAAU,CAC3B,OAAU+E,EACV,SAAYD,GAGhBzjH,KAAK4/G,gBAAgBjB,QAAQtpF,MAAQ,CACjC,OAAUyuF,EACV,SAAYD,GAGhB7jH,KAAK4/G,gBAAgBjB,QAAQ1qF,MAAQ,CACjC,OAAUgwF,EACV,SAAYD,GAGhBhkH,KAAK4/G,gBAAgBb,WAAa,CAC9BlyB,MACIwxB,EACIC,EAAYvhB,SAAWuhB,EAAYM,OACnCL,EAAaxhB,SAAWwhB,EAAaK,QAC7C7hB,SACIshB,EAAoBC,EAAYvhB,SAAUwhB,EAAaxhB,UAC3D6hB,OACIP,EAAoBC,EAAYM,OAAQL,EAAaK,SAG7D,MAAM2F,EAAiB,GACvB,IAAIC,EAEJx3H,OAAOuY,KAAKvF,KAAK0/G,yBAAyBtwG,QAAQ4V,IAC9C,MAAM,KAAErrB,EAAF,QAAQ4rB,GAAYvlB,KAAK0/G,wBAAwB16F,GACjDy/F,EAAgB9qH,EAAK+L,OAAO,CAACg/G,EAAKC,IAAiBD,EAAMC,GAAgBhrH,EAAKpD,OAEpF,GAAIgvB,EACAi/F,EAAsBC,MACnB,CACH,MAAMnmF,EAAQt+B,KAAKghB,eAAe4nB,eAAervB,OAAOyL,IAExD,GAAIsZ,EAAO,CACP,MAAMnT,EAAgBmT,EAAMq+B,mBAExBxxC,IACAo5F,EAAep5F,GAAiBs5F,OAKhDzkH,KAAK0/G,wBAA0B,GAE/B1/G,KAAKwf,aAAa0D,KACdtB,IACA5hB,KAAKghB,eACL,CACI,UAAahhB,KAAK4/G,gBAAgB1nF,UAClC,QAAWl4B,KAAK4/G,gBAAgBjB,QAChC,WAAc3+G,KAAK4/G,gBAAgBb,WACnC,WAAc4E,EACd,UAAaC,EACb,MAAS7gD,EACT,UAAa/iE,KAAK4/G,gBAAgBhiG,UAClC4mG,sBACAD,mBAERvkH,KAAK4/G,gBAAgBhiG,UAAY,IAMrCohG,EAAe9wH,UAAU0yH,wBAA0B,WAC/C,IAAK5gH,KAAKs/G,0BACN,OAGJ,MAAM6B,EAAenhH,KAAKm/G,cAE1B,IAAK,MAAMvvE,KAAO5vC,KAAKu/G,yBAA0B,CAC7C,IAAKv/G,KAAKu/G,yBAAyBpxH,eAAeyhD,GAC9C,SAGJ,MAAMz7B,EAAMnU,KAAKu/G,yBAAyB3vE,GAE1C,GAAiB,SAAbz7B,EAAIlN,MAAgC,UAAbkN,EAAIlN,KAC3B,SAGJ,MAAMk7G,EAASniH,KAAKs/G,0BAA0B1vE,GAC9C,IAmBIjH,EAnBA3jB,EAAOhlB,KAAKkhH,mBAAmB/sG,EAAK,QAMxC,IAJK6Q,GAAQ5uB,MAAMC,QAAQ8d,EAAIiuG,WAC3Bp9F,EAAOzL,OAAOpF,EAAIiuG,QAAQ,KAGzBD,EAKL,GAAKn9F,EAAL,CAUA,IACI2jB,EACMw4E,EAAahtG,EAAK,oBACbgtG,EAAahtG,EAAK,oBAC/B,MAAO9a,GAIL,OAHA4kB,EAAO9Y,KAAK,0DACZm1B,cAAct6B,KAAK2/G,uBAKvB,GAAIh3E,EAAY,CACZ,IAAIpjB,EAIAA,EADa,SAAbpR,EAAIlN,MACOk6G,EAAahtG,EAAK,oBAIlBA,EAAImuG,aAQnB35E,GAA0B,MAEpB3jB,KAAQhlB,KAAK0/G,0BACf1/G,KAAK0/G,wBAAwB16F,GAAQ,CACjCO,UACA5rB,KAAM,KAGdqG,KAAK0/G,wBAAwB16F,GAAMrrB,KAAKuJ,KAAKylC,GAE7C3oC,KAAKwf,aAAa0D,KACdtB,IACA5hB,KAAKghB,eACLgE,EACA2jB,EACApjB,SApDCpd,KAAKgM,MAAQA,EAAIq0E,UAAa,KAC/BvqE,EAAO9Y,KAAK,kBANhB8Y,EAAO9Y,KAAQ6f,EAAF,sBAiFzBg6F,EAAe9wH,UAAUkxH,6BAA+B,SAAS75G,GAkB7D,MAAO,CAACguB,EAAM1mC,IAAS0mC,EAdH,SAAS1mC,GACzB,MAAMgB,EAAM0X,EAAK1Y,GAEjB,GAAIgB,EACA,OAAOA,EAIX,KAAO,iBAAgBhB,sBAMCo0H,CAAYp0H,KAU5CmyH,EAAe9wH,UAAU02H,oBAAsB,SAAS/zE,GACpD,IAAItjD,EAAQsjD,EAMZ,MAJqB,iBAAVtjD,IACPA,EAAQgsB,OAAOhsB,IAGfmsB,MAAMnsB,GACC,EAGJ6d,KAAKgpB,IAAI,EAAG7mC,IAavByxH,EAAe9wH,UAAU22H,kBAAoB,SAAS1wG,EAAKguG,EAAQ2C,GAC/D,MAAMC,EAAW/kH,KAAK4kH,oBAAoBzwG,EAAI2wG,IACxCE,EAAchlH,KAAK4kH,oBAAoBzC,EAAO2C,IAC9CG,EAAiB75G,KAAKgpB,IAAI,EAAG2wF,EAAWC,GAExC3B,EAASlvG,EAAIq0E,UAAY25B,EAAO35B,UACtC,IAAI08B,EAAc,EAOlB,OALI7B,EAAS,IAET6B,EAAc95G,KAAKozG,MAAwB,EAAjByG,EAAsB5B,IAG7C6B,GAMXlG,EAAe9wH,UAAU2yH,sBAAwB,WAC7C,IAAK7gH,KAAKy/G,oBACN,OAGJ,MAAM0B,EAAenhH,KAAKm/G,cACpBiC,EAAgB,GAEtBphH,KAAKw/G,mBAAmBpwG,QAAQ+E,IAI5B,GAAiB,mBAAbA,EAAIlN,MACDkN,EAAIgxG,WACU,cAAdhxG,EAAI84B,MAAuB,CAE9B,MAAMm4E,EAA2BjxG,EAAIixG,yBAC/BC,EAA2BlxG,EAAIkxG,0BAEjCD,GAA4BC,KAC5BrlH,KAAK4/G,gBAAgB1nF,UAAY,CAC7B,SAAY9sB,KAAKozG,MAAM4G,EAA2B,KAClD,OAAUh6G,KAAKozG,MAAM6G,EAA2B,OAIxD,MAAMC,EACAtlH,KAAKw/G,mBAAmBryH,IAAIgnB,EAAI4tG,mBAChCwD,EACAvlH,KAAKw/G,mBAAmBryH,IAAIgnB,EAAI0tG,kBAKtC,GAAIyD,GAAuBC,EAAoB,CAC3C,MAEM12F,EAAM,GAFYsyF,EAAamE,EAAqB,SACvCnE,EAAamE,EAAqB,UAM/CE,EAAW,GAHMrE,EAAaoE,EAAoB,SACtCpE,EAAaoE,EAAoB,UAG7Ct+G,EAAOk6G,EAAamE,EAAqB,YAGzC5D,EAA2B1hH,KAAK4/G,gBAAgBhiG,UAEjD8jG,EAAyB/+B,KACtBn1F,GACIA,EAAEqhC,KAAOA,GACNrhC,EAAEyZ,OAASA,GACXzZ,EAAEg0H,UAAYgE,IACzB9D,EAAyBx+G,KAAK,CAC1B2rB,KACA5nB,OACAu+G,UACArnE,IAAKn+C,KAAKghB,eAAe8kB,MACzBy7E,mBAAoBgE,EAAmBrD,cACvCT,oBAAqB6D,EAAoBpD,cACzCuD,YAAaF,EAAmBE,YAChCp6F,IAAgC,IAA3BlX,EAAIuxG,6BASlB,GAAiB,gBAAbvxG,EAAIlN,MAAuC,iBAAbkN,EAAIlN,KAAyB,CAClE,MAAMk7G,EAASniH,KAAKy/G,oBAAoBtyH,IAAIgnB,EAAIjN,IAC1C8d,EAAOhlB,KAAK4kH,oBAAoBzwG,EAAI6Q,MAE1C,IAAKm9F,IAAWn9F,EACZ,OAGJ,IAAIu9F,EAAYviH,KAAKggH,WAAW7yH,IAAI63B,GAE/Bu9F,IACDA,EAAY,IAAI9D,EAChBz+G,KAAKggH,WAAW5+F,IAAI4D,EAAMu9F,IAG9B,IAAIC,GAAmB,EACnB30H,EAAM,kBAEO,iBAAbsmB,EAAIlN,OACJu7G,GAAmB,EACnB30H,EAAM,eAGV,IAAI40H,EAAatuG,EAAItmB,KAEhB40H,GAAcA,EAAa,KAC5BA,EAAa,GAGjB,MAAMC,EAAgB1iH,KAAK4kH,oBAAoBzC,EAAOt0H,IAChD80H,EAAcv3G,KAAKgpB,IAAI,EAAGquF,EAAaC,GAEvCE,EACA5iH,KAAK4kH,oBAAoBzwG,EAAI6uG,aAC7BH,EACA7iH,KAAK4kH,oBAAoBzC,EAAOa,aAChCF,EACA13G,KAAKgpB,IAAI,EAAGwuF,EAAiBC,GAEnCN,EAAUrC,QAAQ,CACd6C,aAAcJ,EAAcG,EAC5BE,YAAaF,EACbN,qBAGa,gBAAbruG,EAAIlN,KAEJs7G,EAAUnC,WAAW,CACjB,SAAYpgH,KAAK6kH,kBACD1wG,EAAKguG,EAAQ,iBAC7B,OAAU,KAQdf,EAAcp8F,GAAQhlB,KAAK4kH,oBAAoBzwG,EAAIuhF,WACnD6sB,EAAUnC,WAAW,CACjB,SAAY,EACZ,OAAUpgH,KAAK6kH,kBACH1wG,EAAKguG,EAAQ,gBAUjC,MAAMwD,EAAgBxxG,EAAIwxG,cAEtBA,GACApD,EAAUjC,aAAal1G,KAAKozG,MAAMmH,GAAiB,SAQpD,GAAiB,UAAbxxG,EAAIlN,KAAkB,CAE7B,MAAMovB,EAAa,CACfnC,OAAQ/f,EAAIyxG,YACZrvF,MAAOpiB,EAAI0xG,YAIf,IAAInuF,EAAYvjB,EAAI2xG,gBAEpB,IAAKpuF,EAAW,CAEZ,MAAMyqF,EAASniH,KAAKy/G,oBAAoBtyH,IAAIgnB,EAAIjN,IAEhD,GAAIi7G,EAAQ,CACR,MAAMkB,EAASlvG,EAAIq0E,UAAY25B,EAAO35B,UAEtC,GAAI66B,EAAS,GAAKlvG,EAAI4xG,WAAY,CAI9BruF,GAFMvjB,EAAI4xG,WAAa5D,EAAO4D,YAEW1C,EAAU,KAI3D,IAAK3rF,EACD,OAIR,MAAMsuF,EAAkB7xG,EAAI6xG,gBACtBhhG,EAAOhlB,KAAKghB,eAAeilG,iBAAiBD,GAElD,IAAKhhG,EACD,OAEJ,IAAIu9F,EAAYviH,KAAKggH,WAAW7yH,IAAI63B,GAE/Bu9F,IACDA,EAAY,IAAI9D,EAChBz+G,KAAKggH,WAAW5+F,IAAI4D,EAAMu9F,IAE9BA,EAAUjC,aAAal1G,KAAKozG,MAAM9mF,GAAa,IAE3CrB,EAAWnC,QAAUmC,EAAWE,MAChCgsF,EAAUpC,cAAc9pF,GAExBksF,EAAUpC,cAAc,SAKpCngH,KAAKwf,aAAa0D,KACdtB,IAAkC5hB,KAAKghB,eAAgBogG,GAE3DphH,KAAKwjH,yBAMTxE,EAAe9wH,UAAUyyH,2BAA6B,WAC7C3gH,KAAKs/G,2BAIVt/G,KAAKu/G,yBAAyBnwG,QAAQ+E,IAClC,GAAiB,UAAbA,EAAIlN,KACJ,OAIJ,MAAM0hC,EAAax0B,EAAIw0B,WAEvB,IAAKA,EACD,OAGJ,MAAMq9E,EAAkB7xG,EAAI6xG,gBACtBhhG,EAAOhlB,KAAKghB,eAAeilG,iBAAiBD,GAElD,GAAIhhG,EAAM,CACN,MAAMO,EACAP,IAAShlB,KAAKghB,eAAessF,aAC/BttG,KAAKghB,eAAekmB,eAAe3I,MAEvCv+B,KAAKwf,aAAa0D,KACdtB,IACA5hB,KAAKghB,eACLgE,EACA2jB,EACApjB,S,oECnyChB,SAASysF,EAAsB12E,EAAK4qF,GAChC,IAAK5qF,IAAQ4qF,GAAmC,mBAApB5qF,EAAIsI,aACJ,mBAAdsiF,EAAKhjG,KACf,MAAM,IAAI7rB,MAAM,qDAEpB2I,KAAKs7B,IAAMA,EACXt7B,KAAKkmH,KAAOA,EAWhBlU,EAAsB9jH,UAAUgkH,QAAU,YAAY/vE,GAClD,MAAMgkF,EAAWhkF,EAAK,GAItBA,EAAK,GAAKniC,KAAKkmH,KAGflmH,KAAKs7B,IAAIsI,YACLuiF,EACA96E,SAASn9C,UAAUJ,KAAK8zC,MAAM5hC,KAAKkmH,KAAKhjG,KAAMif,KAGtDp2C,EAAOD,QAAUkmH,G,8BCpCjB,8FAUA,MAAM/zF,EAASF,oBAAUM,GAKV,MAAMwqF,EAiBjB5yG,YAAY0N,EAAK4e,EAAYspE,EAAau6B,EAAQzd,EAAS54F,EAAQmrC,GAC/Dl7C,KAAKqmH,KAAO1iH,EACZ3D,KAAKsmH,IAAM7qH,UAAQyI,mBAAmBP,GACtC3D,KAAKumH,YAAchkG,EACnBviB,KAAK8pG,aAAeje,EACpB7rF,KAAKkpG,eAAgB,EACrBlpG,KAAK+pG,QAAU,GACf/pG,KAAK8oG,MAAQ,OACb9oG,KAAKkyD,QAAUniD,EACf/P,KAAKwmH,QAAUJ,EACfpmH,KAAKymH,SAAW9d,EAChB3oG,KAAK0mH,kBAAoBntD,IAA4B5V,OACrD3jD,KAAK2mH,YAAc,GACnB3mH,KAAK4mH,UAAY1rE,EASrB2rE,gBACI,OAAO7mH,KAAKumH,YAMhBrX,YAAYriH,GACR,OAAOmT,KAAK2mH,YAAY95H,GAW5BiwE,8BACI,OACI98D,KAAK8/B,YAAY6iD,KACbmkC,GACIA,EAAWx/E,YAAc/I,KAClBuoF,EAAWvhC,sBASlC9oB,qBAAqB1sD,GACjB/P,KAAK0mH,kBAAoB32G,EAU7BwsD,sBACI,OAAOv8D,KAAK0mH,kBAShBtd,YAAYv8G,EAAMU,GACd,MAAMw5H,EAAW/mH,KAAK2mH,YAAY95H,GAE9BU,IAAUw5H,IACV/mH,KAAK2mH,YAAY95H,GAAQU,EACzByS,KAAKumH,YAAY/mG,aAAa0D,KAC1BP,+BACA3iB,KACAnT,EACAk6H,EACAx5H,IAQZuyC,YACI,OAAO9/B,KAAK+pG,QAAQ7vE,QAQxB+jC,qBAAqBtyC,GACjB,OAAO3rB,KAAK8/B,YAAYpP,OAAO4N,GAASA,EAAMgJ,YAAc3b,GAMhE6wC,QACI,OAAOx8D,KAAKsmH,IAMhBjpE,SACI,OAAOr9C,KAAKqmH,KAMhB95B,iBACI,OAAOvsF,KAAK8pG,aAMhBuE,aACI,OAAOruG,KAAKymH,SAMhB5uB,YACI,OAAO73F,KAAKkyD,QAMhB0zC,cACI,MAAsB,cAAf5lG,KAAK8oG,MAQhBzB,WACI,OAAOrnG,KAAKwmH,QAMhBQ,eACI,OAAOhnH,KAAKinH,kBAAkB1oF,KAclC0oF,kBAAkBt7F,GACd,OAAO3rB,KAAK8/B,YAAYp6B,OACpB,CAACof,EAAOwZ,IACJxZ,IAAUwZ,EAAMgJ,YAAc3b,GAAa2S,EAAMggC,YACrD,GAMRxE,eACI,OAAO95D,KAAKinH,kBAAkB1oF,KAMlC6oE,UACI,OAAOpnG,KAAK8oG,MAMhB2C,eACI,OAAOzrG,KAAKkpG,cAQhBrlB,YAAYj2E,EAAU,KAClB,OAAI5N,KAAKknH,oBACElnH,KAAKknH,qBAGhBlnH,KAAKknH,oBAAsBlnH,KAAKumH,YAAYlnG,KAAKo6B,KAAKoqC,YAAY7jF,KAAKqmH,KAAMz4G,GACxEwvB,MAAMp4B,GAECA,IAAUy9E,IACHziF,KAAKumH,YAAYlnG,KAAKo6B,KAAKoqC,YAAY7jF,KAAKqmH,KAAMz4G,IAG7DqQ,EAAO9Y,KAAM,kCAAiCnF,KAAKqmH,KAAQrhH,GAEpDg4B,QAAQE,OAAOl4B,KAGvBhF,KAAKknH,oBACPxsF,KAAKj1B,IACFzF,KAAKknH,yBAAsB3mH,EAEpBkF,GACRT,IAGC,MAFAhF,KAAKknH,yBAAsB3mH,EAErByE,KASlBmiH,aACI,OAAOnnH,KAAK+oG,a,+DCrRpB,sDA2De,SAAS7F,GAA2B,GAE/Ch8F,EAF+C,SAG/Cy0C,EAH+C,iBAI/CmB,EAJ+C,kBAQ/CsqE,EAR+C,aAW/CC,IAEA,IACIC,EADAC,GAAW,EAEXloG,EAAO,IAAIs5B,IAAK34C,KAAKuU,WAAWpN,SAEpC,MAAM6sD,EAAU,IAAIh3B,QAAQ,CAACC,EAASC,KAIlCoqF,EAAgBpqF,EAGhB7d,EAAKukB,YACD3Z,0BACA,KACI5K,OAAO9e,IAEf8e,EAAKukB,YACD70C,yBACA,KACI,GAAIw4H,EACA,OAIJH,GAAqBA,IAGR/nG,EAAKu9B,WACd58C,KAAKmH,QAAQta,KACbmT,KAAKmH,QAAQqsB,OACbspB,GAGCsnD,UAAU3yF,eACVipB,KAAK,KACFrb,GAAQA,EAAK9P,aAETg4G,IAOJvnH,KAAKmD,KAAKkkH,GAEVpqF,OAEHG,MAAM,EAAGp4B,QAAOP,cACb4a,EAAK9P,aAEL2tB,EAAO,CACHsqF,oBAAqBxiH,EACrBP,gBAIpB4a,EAAKukB,YACD50C,oBACA,CAACy4H,EAAiBhjH,EAAS81C,KACvBrd,EAAO,CACHuqF,kBACAltE,cACA91C,YAEJ4a,OAAO9e,IAGfgnH,GAAYloG,EAAK5T,QAAQvE,EAAIy0C,KAgBjC,OANAqY,EAAQrB,OAAS,KACb40D,GAAW,EACXD,EAAc,IACdjoG,GAAQA,EAAK9P,cAGVykD,I,8BC5JX,yDAIA,MAAM/1C,EAASF,oBAAUM,GAInBqpG,EAAar6H,OAAO,cAcX,MAAMs6H,EAIjB1xH,cAGI,IAAI2xH,EAAU,GACd,MAAMC,EAAMxsH,SAAS4b,cAAc,iCAEnC,GAAI4wG,EAAK,CACL,MAAMj4E,EAAMi4E,EAAIvsF,IAAI89B,YAAY,KAEhCwuD,EAAaC,EAAIvsF,IAAInO,UAAU,EAAGyiB,GAAvB,IAKf,MACMk4E,EACA,IAAI9qB,KAAK,CAAG,kBAFG4qB,EAAF,sCAEkC,CAAE3gH,KAAM,2BACvD8gH,EAAU77H,OAAOuvC,IAAIC,gBAAgBosF,GAE3C9nH,KAAKgoH,QAAU,IAAIC,OAAOF,EAAS,CAAEl7H,KAAM,gBAC3CmT,KAAKgoH,QAAQtsG,QAAUriB,GAAK4kB,EAAOvC,QAAQriB,GAS/C0wE,QAAQ5+C,GACJnrB,KAAKgoH,QAAQhU,YAAY,CACrBkU,UAAW,UACX/8F,kBAYRm/C,eAAel/B,EAAUhS,EAAMjO,GAC3B,GAAIigB,EAASs8E,GACT,OAIJ,IAAIS,EAFJ/8E,EAASs8E,IAAc,EAKnBS,EADA/8E,EAASyoE,qBACSzoE,EAASyoE,uBAEA,UAATz6E,EAAmBgS,EAAS0oE,4BACxC1oE,EAASg9E,4BAGnBpoH,KAAKgoH,QAAQhU,YAAY,CACrBkU,UAAW,SACXG,eAAgBF,EAAgBG,UAAYH,EAAgBE,eAC5DE,eAAgBJ,EAAgB7N,UAAY6N,EAAgBI,eAC5Dp9F,iBACD,CAAEg9F,EAAgBG,UAAYH,EAAgBE,eAC7CF,EAAgB7N,UAAY6N,EAAgBI,iBAWpD99C,aAAaF,EAAQnxC,EAAMjO,GACvB,GAAIo/C,EAAOm9C,GACP,OAIJ,IAAIc,EAFJj+C,EAAOm9C,IAAc,EAKjBc,EADAj+C,EAAOspC,qBACStpC,EAAOspC,uBAEE,UAATz6E,EAAmBmxC,EAAOupC,4BACpCvpC,EAAO69C,4BAGjBpoH,KAAKgoH,QAAQhU,YAAY,CACrBkU,UAAW,SACXG,eAAgBG,EAAcF,UAAYE,EAAcH,eACxDE,eAAgBC,EAAclO,UAAYkO,EAAcD,eACxDp9F,iBACD,CAAEq9F,EAAcF,UAAYE,EAAcH,eACzCG,EAAclO,UAAYkO,EAAcD,iBAUhD7+C,OAAOv+C,EAAet9B,EAAKiuF,GACvB97E,KAAKgoH,QAAQhU,YAAY,CACrBkU,UAAW,SACX/8F,gBACAt9B,MACAiuF,iB,wEC1IZ,yEAQA,MAAM79D,EAASF,oBAAUM,GAQV,MAAMuzC,EAKjB37D,YAAYwyH,GACRzoH,KAAKsxD,aAAem3D,EAOpBzoH,KAAK0oH,cAAgB,EAErB1oH,KAAK2oH,iBAAcpoH,EAOvB,iBACI,OAAOP,KAAK2oH,YAQhBj1D,WACI1zD,KAAK4oH,gBAEL5oH,KAAK0oH,eAAiB,EAEtB1oH,KAAK6oH,uBACCrqD,IAAYniC,iBACVkiC,IACA,EAAGG,eACKA,EACA1+D,KAAK8oH,kBAEL9oH,KAAK4oH,kBAIrBpqD,IAAYE,YAAc1+D,KAAK8oH,kBAQnCA,kBACQ9oH,KAAK+oH,iBAUT/oH,KAAK0oH,cAAgBt9G,KAAKqP,IAAI,EAAGza,KAAK0oH,eACtC1oH,KAAK2oH,YAAcK,YACHhpH,KAAK0oH,cACmB,KAArB1oH,KAAK0oH,cACpB,GAEJzqG,EAAO/Y,KAAM,6CAA4ClF,KAAKipH,gBAE9DjpH,KAAK+oH,eAAiBx+G,WAAW,IAAMvK,KAAKkpH,oBAAqBlpH,KAAKipH,aAS1EL,gBACQ5oH,KAAK+oH,iBACL9qG,EAAO/Y,KAAK,oCACZuI,aAAazN,KAAK+oH,gBAClB/oH,KAAK+oH,oBAAiBxoH,EACtBP,KAAK2oH,iBAAcpoH,GAU3B2oH,oBACI,MAAM,iBAAE/1D,GAAqBnzD,KAAKsxD,aAC5B63D,EAAch2D,EAAiBC,iBAGrC,IAAK+1D,EACD,OAGJlrG,EAAO/Y,KAAK,wCAEZ,MAAM67B,EAAM,IAAItF,IAAIz7B,KAAKsxD,aAAa/oD,SACtC,IAAI,OAAEsU,GAAWkkB,GAGkB,IAA/BlkB,EAAOjZ,QAAQ,aACfiZ,IAAmC,IAAzBA,EAAOjZ,QAAQ,KAAe,WAAUulH,EAAiB,WAAUA,GAGjFpoF,EAAIlkB,OAASA,EAEb7c,KAAKsxD,aAAa/oD,QAAUw4B,EAAIrqC,WAEhCy8D,EAAiBnoD,SASrB2nD,SACI3yD,KAAK4oH,gBACL5oH,KAAK0oH,cAAgB,EACjB1oH,KAAK6oH,yBACL7oH,KAAK6oH,yBACL7oH,KAAK6oH,uBAAyB,U,sEC5InC,SAASG,EAAeI,EAAOC,EAAW,IAAKlhE,EAAO,GACzD,OAAO/8C,KAAKmM,MAAOnM,KAAKC,UAAqC,IAAxBD,KAAKsP,IAAIytC,EAAMihE,GAAiBC,GAAaA,GAZtF,mC,6BCAA,kCAGe,MAAMC,EAIjBrzH,cACI+J,KAAKupH,aAAe,KASxB73D,cAAc83D,EAAgBf,GAC1B,MAAMgB,EAAmBhB,EAAkBv7G,SAE3Cu7G,EAAkBv7G,SAAW,IAAIi1B,KAGzBqnF,EAAe5/G,YACf5J,KAAKupH,aAAephH,KAAKgM,OAE7Bs1G,EAAiB7nF,MAAM6mF,EAAmBtmF,IASlD+c,0BACI,OAAOl/C,KAAKupH,aACNphH,KAAKgM,MAAQnU,KAAKupH,aAClB,Q,8BCtCd,yFAQA,MAAMtrG,EAASF,oBAAUM,GA+BV,MAAMyzC,UAA6B43D,IAQ9CzzH,aAAY,wBAAE87D,IACV13B,QACAr6B,KAAK2pH,YAAc,EACnB3pH,KAAK4pH,kBAAoB,IAAIxzH,MAlBL,IAmBxB4J,KAAK6pH,yBAA2B93D,EAOpCnnD,KAAK2J,GACD8lB,MAAMzvB,KAAK2J,GACX9Y,UAAQ+D,aAAa,OAAQ,iBAajCw8C,KAAKr4C,EAAK+jD,EAAS1iD,EAAO4I,GACtB5N,KAAK8pH,6BAEL,MAAM72G,EAAKtX,cAAI,CACXsL,KAAM,MACN8hC,GAAIplC,IAGRsP,EAAGtmB,EAAE,OAAQ,CAAEsZ,MAAOxK,UAAQK,GAAGm/C,OACjCj7C,KAAKuU,WAAWi/C,QAAQvgD,EAAI,CAAErF,YACzB8sB,KAAKgtB,EAAS1iD,GAYvB4tD,cAAcsY,EAAWgW,EAlFP,KAmFd5mD,cAAct6B,KAAKmhF,YACnBnhF,KAAKmhF,WAAaj1F,OAAOowC,YAAY,KACjCt8B,KAAKg8C,KAAKkvB,EAAW,KACjBlrE,KAAK2pH,YAAc,GACpB3kH,IACChF,KAAK2pH,aAAe,EACpB,MAAMhyF,EAAU,SAAO3yB,EAAQ,QAAU,WAErChF,KAAK2pH,aA/EF,GAgFH/xF,IAAqBC,iBAAiB,IAAIxgC,MAAMsgC,IAChD1Z,EAAOjZ,MAAM2yB,EAAQ3yB,GACrBhF,KAAK6pH,0BAA4B7pH,KAAK6pH,4BAEtC5rG,EAAO9Y,KAAKwyB,EAAQ3yB,IA3FnB,MA8FVk8E,GACHjjE,EAAO/Y,KAAM,iCAAgCg8E,QAMjDjlC,eACQj8C,KAAKmhF,aACLj1F,OAAOouC,cAAct6B,KAAKmhF,YAC1BnhF,KAAKmhF,WAAa,KAClBnhF,KAAK2pH,YAAc,EACnB1rG,EAAO/Y,KAAK,0BAQpB4kH,6BACI9pH,KAAK4pH,kBAAkB1mH,MAAK,IAAIiF,MAAOC,WAGnCpI,KAAK4pH,kBAAkBrzH,OAxGH,IAyGpByJ,KAAK4pH,kBAAkBl8F,QAY/BsxB,qBACI,MAAM+qE,EAAgB/pH,KAAK4pH,kBAAkB1vF,QAM7C6vF,EAAc7mH,MAAK,IAAIiF,MAAOC,WAE9B,IAAI4hH,EAAc,EACdC,EAAaF,EAAc,GAkB/B,OAhBAA,EAAc36G,QAAQ/V,IAClB,MAAM6wH,EAAkB7wH,EAAI4wH,EAExBC,EAAkBF,IAClBA,EAAcE,GAGlBD,EAAa5wH,IAMjB2wH,GAjKc,IAoKP5+G,KAAKgpB,IAAI41F,EAAa,O,yECjLrC,iGAUA,MAAM/rG,EAASF,oBAAUM,GAKV,MAAMkgC,UAA4B6J,IAK7CnyD,YAAYopB,GACRgb,QACAr6B,KAAKqf,KAAOA,EACZrf,KAAKmjF,MAAQ,GAOjBv4E,KAAK2J,GACD8lB,MAAMzvB,KAAK2J,GAGXvU,KAAKuU,WAAWxG,WAAW/N,KAAKmqH,WAAWr8H,KAAKkS,MAAO,KACnD,WAAY,KAAM,KAAM,KAAM,MAClCA,KAAKuU,WAAWxG,WAAW/N,KAAKoqH,sBAAsBt8H,KAAKkS,MACvD,KAAM,WAAY,cAAe,MACrCA,KAAKuU,WAAWxG,WAAW/N,KAAKqqH,gBAAgBv8H,KAAKkS,MAAO,KACxD,WAAY,QAAS,MACzBA,KAAKuU,WAAWxG,WAAW/N,KAAKsqH,UAAUx8H,KAAKkS,MAAO,KAClD,UAAW,KAAM,MACrBA,KAAKuU,WAAWxG,WAAW/N,KAAKuqH,OAAOz8H,KAAKkS,MACxC,iCAAkC,KAAM,MAAO,KAAM,MAS7D48C,WAAWj5C,EAAKg4C,EAAUx0C,GACtB,MAAM+d,EAAUzpB,UAAQsI,kBAAkBJ,GAE1C,GAAI3D,KAAKmjF,MAAMj+D,GAAU,CACrB,MAAMyS,EAAS,+BAGf,MADA1Z,EAAOjZ,MAAM2yB,GACP,IAAItgC,MAAMsgC,GAOpB,OALA33B,KAAKmjF,MAAMj+D,GAAW,IAAIslG,IAASxqH,KAAKuU,WAAY5Q,EAChDg4C,EAAU37C,KAAKqf,KAAMlY,GACzBnH,KAAKwf,aAAa0D,KACdy3B,IAAWrrD,gBAAiB0Q,KAAKmjF,MAAMj+D,IAEpCllB,KAAKmjF,MAAMj+D,GAOtBulG,QAAQ9mH,GACJ3D,KAAKwf,aAAa0D,KACdy3B,IAAWprD,kBAAmByQ,KAAKmjF,MAAMx/E,WACtC3D,KAAKmjF,MAAMx/E,GAOtBwmH,WAAW16G,GACP,MAAM9Y,EAAO8Y,EAAK7M,aAAa,QAG/B,GAAI6M,EAAK7M,aAAa,QAClB,OAAO,EAGX,MAAMu8C,EAAOn/C,KAAKmjF,MAAM1nF,UAAQsI,kBAAkBpN,IAElD,OAAKwoD,IAKD1M,EAAEhjC,GAAMwhB,KAAK,sEACY16B,QACzB4oD,EAAKurE,yBAGTvrE,EAAKgrE,WAAW16G,IAET,GAOX26G,sBAAsB36G,GAClB,MAAM9Y,EAAO8Y,EAAK7M,aAAa,QACzBu8C,EAAOn/C,KAAKmjF,MAAM1nF,UAAQsI,kBAAkBpN,IAElD,OAAKwoD,IAILA,EAAKirE,sBAAsB36G,EAAM9Y,IAE1B,GAOX0zH,gBAAgB56G,GACZ,MAAM9Y,EAAO8Y,EAAK7M,aAAa,QACzBu8C,EAAOn/C,KAAKmjF,MAAM1nF,UAAQsI,kBAAkBpN,IAElD,OAAKwoD,IAILA,EAAKkrE,gBAAgB56G,EAAM9Y,IAEpB,GAOX2zH,UAAUxlH,GAEN,MAAMnO,EAAOmO,EAAIlC,aAAa,QACxBu8C,EAAOn/C,KAAKmjF,MAAM1nF,UAAQsI,kBAAkBpN,IAElD,OAAKwoD,IAILA,EAAKmrE,UAAUxlH,EAAKnO,IAEb,GAOX4zH,OAAOt3G,GACH,MAAMtc,EAAOsc,EAAGrQ,aAAa,QACvBu8C,EAAOn/C,KAAKmjF,MAAM1nF,UAAQsI,kBAAkBpN,IAGlD,OAAKwoD,IAILA,EAAKorE,OAAOt3G,IAEL,O,yEC/Kf,kJAeA,MAAMgL,EAASF,oBAAUM,GAEZw5B,EAAS,CAClB8yE,YAAY1pH,EAAYgsG,GACpB,IAAK,MAAMrnG,KAASxP,MAAMO,KAAKsK,EAAW23F,UAAW,CACjD,MAAM13F,EAAO,CACTjE,WAAY,GACZ27F,SAAU,GACVz4F,QAASyF,EAAMzF,SAGnB,IAAK,MAAMoB,KAAQnL,MAAMO,KAAKiP,EAAM3I,YAChCiE,EAAKjE,WAAWsE,EAAK1U,MAAQ0U,EAAKhU,MAEtC,MAAMoU,EAAOlG,UAAQ4G,QAAQuD,GAEzBjE,IAIAT,EAAK3T,MAAQkO,UAAQoG,YAAYF,IAErCsrG,EAAM/pG,KAAKhC,GACXlB,KAAK2qH,YAAY/kH,EAAO1E,EAAK03F,YAGrCgyB,YAAY3d,EAAO4d,GACf,IAAK,IAAIv+H,EAAI,EAAGA,EAAI2gH,EAAM12G,OAAQjK,IAAK,CACnC,MAAM4U,EAAO+rG,EAAM3gH,GAEf4U,IACA2pH,EAAOl+H,EAAEuU,EAAKf,QAASe,EAAKjE,YACxBiE,EAAK3T,OACLs9H,EAAOr9H,EAAE0T,EAAK3T,OAEd2T,EAAK03F,UACL54F,KAAK4qH,YAAY1pH,EAAK03F,SAAUiyB,GAEpCA,EAAO1kH,SAcvB,SAAS2kH,EAA2Br7G,EAAMjN,GACtC,MAAMy7B,EAAM,GAEZ,IAAK,IAAI3xC,EAAI,EAAGA,EAAImjB,EAAKlZ,OAAQjK,IACzBmjB,EAAKnjB,GAAG6T,UAAYqC,GACpBy7B,EAAI/6B,KAAKuM,EAAKnjB,IAItB,OAAO2xC,EAWX,MAAM8sF,EAAuB,CAAE,QAAS,QAAS,UAKlC,MAAMP,UAAiBpwF,IAiBlCnkC,YAAYse,EAAY5Q,EAAKg4C,EAAUhD,EAAMxxC,GACzCkzB,QACAr6B,KAAKqf,KAAOs5B,EACZ34C,KAAKuU,WAAaA,EAClBvU,KAAK+8C,QAAUthD,UAAQsI,kBAAkBJ,GACzC3D,KAAK0mG,UAAY/iG,EACjB3D,KAAK27C,SAAWA,EAChB19B,EAAO/Y,KAAM,iBAAgBlF,KAAK0mG,WAClC1mG,KAAKgrH,QAAU,GACfhrH,KAAKgtG,QAAU,GACfhtG,KAAKirH,aAAe,GACpBjrH,KAAKkrH,qBAAuB,GAC5BlrH,KAAKojG,QAAS,EACdpjG,KAAKyhE,KAAO,KACZzhE,KAAKu4F,YAAc,KACnBv4F,KAAKmrH,mBAAoB,EACzBnrH,KAAKmH,QAAUA,GAAW,GAC1BnH,KAAKokG,UACC,IAAIgnB,IAAUprH,KAAK+8C,QAAS/8C,KAAKqf,KAAMrf,KAAKwf,aAAc,CACxDjL,WAAYvU,KAAKqf,KAAKlY,QACtBob,WAAYviB,KAAKmH,gBAEe,IAA7BnH,KAAKmH,QAAQiqG,aAA+BpxG,KAAKmH,QAAQiqG,eAChEpxG,KAAKqrH,MAAQ,IAAIC,IAAMtrH,OAE3BA,KAAKurH,gBAAgBpkH,GACrBnH,KAAKwrH,cAAgB,GACrBxrH,KAAKyrH,YAAc,KACnBzrH,KAAK0rH,SAAW,KAChB1rH,KAAK84C,gBAAkB,GACvB94C,KAAK2rH,4BAA8B,KAEnC3rH,KAAK4rH,QAAS,EACd5rH,KAAKkmG,oBAAsB2lB,MAQ/BN,gBAAgBpkH,EAAU,IACtBnH,KAAKgtG,QAAQjkE,GAAK/oC,KAAK0mG,UACvB1mG,KAAKgtG,QAAQ8e,IAAM,iCACnB9rH,KAAKgtG,QAAQC,MAAQ,GAEjB9lG,EAAQy5F,SACR5gG,KAAKgtG,QAAQC,MAAM/pG,KAAK,CACpB,QAAW,WACX,MAASiE,EAAQy5F,UAOzB5gG,KAAK+rH,wBAAuB,GAExB5kH,EAAQu3C,gBAAkBv3C,EAAQu3C,eAAeqkD,YACjD/iG,KAAKgtG,QAAQC,MAAM/pG,KAAK,CACpB,QAAW,SACX,WAAc,CACVgE,GAAIC,EAAQu3C,eAAeqkD,WAC3B98F,MAAO,iCAKnBjG,KAAKgsH,mBAAqB7jH,KAAKgM,MAYnChR,KAAKw4C,EAAUswE,GAGX,OAFAjsH,KAAK27C,SAAWA,EAET,IAAI3e,QAAQC,IACfj9B,KAAKmH,QAAQ+kH,cACNjuG,EAAO/Y,KAAM,iCAAgClF,KAAK+8C,UAGnD/8C,KAAKmH,QAAQ+kH,aACTlvF,QAAQC,UACRj9B,KAAKokG,UAAU+nB,2BAEjBzxF,KAAK,KACT16B,KAAK0N,cAAa,EAAMu+G,GACxBjsH,KAAKkrH,qBAAqBhoH,KACtBlD,KAAKuU,WAAW8nB,iBACZgd,IAAe0Z,OAAO3B,oBACtBpxD,KAAKosH,oBAAoBt+H,KAAKkS,QAEtCi9B,QAUZvvB,aAAa2+G,EAAUJ,GACnB,MAAMljF,EAAK/oC,KAAKgtG,QAAQjkE,GAExB,IAAK/oC,KAAKuU,aAAevU,KAAKuU,WAAW3K,YAAcm/B,IAAQ/oC,KAAKojG,SAAWipB,EAE3E,OAGJ,MAAM58G,EAAO7T,gBAAM,CAAEmtC,OAOjBsjF,IACA58G,EAAK9iB,EAAE,IAAK,CAAEsZ,MAAOjG,KAAKgtG,QAAQ8e,MAE9B9rH,KAAK27C,UACLlsC,EAAK9iB,EAAE,YAAYa,EAAEwS,KAAK27C,UAAUx1C,KAEpC8lH,GACAj/H,OAAOuY,KAAK0mH,GAA8B78G,QAAQvhB,IAC9C4hB,EAAK9iB,EAAEkB,GAAKL,EAAEy+H,EAA6Bp+H,IAAMsY,OAGzDsJ,EAAKtJ,MAGT0xC,EAAO+yE,YAAY5qH,KAAKgtG,QAAQC,MAAOx9F,GAGvCzP,KAAKssH,iBAAmBnkH,KAAKgM,MAE7BnU,KAAKuU,WAAWlH,KAAKoC,GACjB48G,GAKArsH,KAAKuU,WAAW/G,QAQxBi9G,UACIxsG,EAAOrZ,IAAI,WAAY5E,KAAK0mG,WAC5B,MAAMj3F,EAAO7T,gBAAM,CAAEmtC,GAAI/oC,KAAK0mG,UAC1Bz/F,KAAM,gBAEVjH,KAAKgtG,QAAQz2G,OAAS,GAerByJ,KAAKuU,WAAWupC,kBAAoB99C,KAAKuU,WAAW/G,QACrDxN,KAAKuU,WAAWlH,KAAKoC,GACrBzP,KAAKuU,WAAW/G,QAMpB++G,gBAGI,MAAMC,EACA7wH,cAAI,CACFsL,KAAM,MACN8hC,GAAI/oC,KAAK+8C,UAERpwD,EAAE,QAAS,CAAEsZ,MAAOxK,UAAQK,GAAGO,aAExC2D,KAAKuU,WAAWnG,OAAOo+G,EAAS/mH,IAC5B,MAAMmmH,EAGM,IAFNn5E,EAAEhtC,GAAQwrB,KAAK,+CACZ16B,OAGLq1H,IAAW5rH,KAAK4rH,SAChB5rH,KAAKwf,aAAa0D,KAAKy3B,IAAW5pD,iBAAkB66H,GACpD5rH,KAAK4rH,OAASA,GAGlB,MAAMa,EACAh6E,EAAEhtC,GAAQwrB,KAAK,qEAEjBw7F,EAAel2H,OACfyJ,KAAK0sH,aAAaD,EAAe9qH,QAEjCsc,EAAO9Y,KAAK,8BAGhB,MAAMwnH,EAAiF,IAAnEl6E,EAAEhtC,GAAQwrB,KAAK,yCAAyC16B,OAEtEq2H,EACAn6E,EAAEhtC,GAAQwrB,KAAK,qEAEjBjxB,KAAKqrH,OACLrrH,KAAKqrH,MAAMwB,gBAAgBD,GAAkBA,EAAer2H,OAASq2H,EAAejrH,YAASpB,GAG7FosH,IAAgB3sH,KAAKmxG,qBACrBnxG,KAAKmxG,mBAAqBwb,EAC1B3sH,KAAKwf,aAAa0D,KAAKy3B,IAAW3pD,yBAA0B27H,KAGjE3nH,IACC4yB,IAAqBC,iBAAiB7yB,GACtCiZ,EAAOjZ,MAAM,4BAA6BA,KAUlD0nH,aAAa7b,GACL7wG,KAAK6wG,YAAcA,IACf7wG,KAAK6wG,WACL5yF,EAAO9Y,KAAM,2BAA0BnF,KAAK6wG,gBAAgBA,KAEhE7wG,KAAK6wG,UAAYA,EACjB7wG,KAAKwf,aAAa0D,KAAKy3B,IAAW3qD,eAAgB6gH,IAO1D6Z,yBAGI,GAAI1qH,KAAKmH,QAAQ2lH,iBACb,OAGJ,MAAMC,EAAUpxH,cAAI,CAAEsL,KAAM,MACxB8hC,GAAI/oC,KAAK+8C,UACRpwD,EAAE,QAAS,CAAEsZ,MAAO,yCACpBtZ,EAAE,IAAK,CAAEsZ,MAAO,gBACbgB,KAAM,WAEdjH,KAAKuU,WAAWnG,OAAO2+G,EAASC,IAC5B,IAAKv6E,EAAEu6E,GAAM/7F,KACL,qEACwC16B,OAAQ,CACpD,MAAMohC,EAAS,oCAKf,OAHAC,IAAqBC,iBAAiB,IAAIxgC,MAAMsgC,SAChD1Z,EAAOjZ,MAAM2yB,GAKjB,MAAMs1F,EAAatxH,cAAI,CAAEotC,GAAI/oC,KAAK+8C,QAC9B91C,KAAM,QACLta,EAAE,QAAS,CAAEsZ,MAAO,yCAEzBgnH,EAAWtgI,EAAE,IAAK,CAAEsZ,MAAO,gBACvBgB,KAAM,WAEVgmH,EAAWtgI,EAAE,QAAS,CAAE,IAAO,cAC1BA,EAAE,SACFa,EAAE,6CAA6C2Y,KAAKA,KAEzD8mH,EAAWtgI,EAAE,QAAS,CAAE,IAAO,yBAC1BA,EAAE,SAASa,EAAE,UAAU2Y,KAAKA,KAEjCnG,KAAKuU,WAAWnG,OAAO6+G,IAExBjoH,IACC4yB,IAAqBC,iBAAiB7yB,GACtCiZ,EAAOjZ,MAAM,0CAA2CA,KAShEonH,oBAAoBr8G,GAEZA,IAAWspC,IAAe57C,OAAOM,WACjCiC,KAAK0N,eAQby8G,WAAW16G,GACP,MAAM9Y,EAAO8Y,EAAK7M,aAAa,QACzBsqH,EAAS,GACTC,EAAW19G,EAAKqB,qBAAqB,UAAU,GAEjDq8G,IACAD,EAAOn9G,OAASo9G,EAASj2G,aAAe,IAE5C,IAAIk2G,GAAkB,EAClBC,GAAmB,EACvB,MAAMC,EACA79G,EAAK6B,uBACH,sCAAuC,KAAK,GAC9Ci8G,EACAD,GAAYA,EAASx8G,qBAAqB,QAAQ,GAExDo8G,EAAOM,YACDD,GAAeA,EAAY3qH,aAAa,eAC9CsqH,EAAOzrD,KAAO8rD,GAAeA,EAAY3qH,aAAa,QAGtD,MAAMe,EAAM4pH,GAAeA,EAAY3qH,aAAa,OAEpDsqH,EAAOvpH,IAAMA,EACbupH,EAAOtiB,QACDjnG,GAA+D,IAAxDA,EAAIC,QAAW5D,KAAKokG,UAAUqpB,kBAAjB,KAC1BP,EAAOQ,eACD/pH,GAAOA,EAAIC,QAAQ,KAAO,GACrB5D,KAAKmH,QAAQmgG,eACR3jG,EAAIwpB,UAAUxpB,EAAIC,QAAQ,KAAO,EAAGD,EAAIC,QAAQ,MAEhE5D,KAAKwf,aAAa0D,KAAKy3B,IAAWppD,kBAAmB,CACjDo8H,iBAAkBT,EAAOQ,eACzB/mD,SAAUl3D,IAGd,MAAMm+G,EAAMn+G,EAAKwH,cAAc,KAE3B22G,GACAA,EAAI96C,SAGR,MAAMm6B,EAAQ,GAEdp1D,EAAO8yE,YAAYl7G,EAAMw9F,GACzBjtG,KAAKwrH,cAAc70H,GAAQs2G,EAI3B,MAAM4gB,EAA6B3sH,IAC/B,MAAMg6C,EAAW,GACX4yE,EAAW5sH,EAAK03F,SAAS3nE,KAAKtkC,GAAmB,SAAdA,EAAEwT,SAE3C,GAAI2tH,EAAU,CACV5yE,EAAS5zC,KAAO,GAChB,IAAK,MAAMlK,IAAO,CAAE,KAAM,OAAQ,UAAY,CAC1C,MAAMwI,EACAkoH,EAASl1B,SAAS3nE,KAAKtkC,GAAKA,EAAEwT,UAAY/C,GAE5CwI,IACAs1C,EAAS5zC,KAAKlK,GAAOwI,EAAMrY,QAIvC,MAAMo1D,EAAYzhD,EAAK03F,SAAS3nE,KAAKtkC,GAAmB,UAAdA,EAAEwT,SAM5C,OAJIwiD,IACAzH,EAAShqB,MAAQyxB,EAAUp1D,OAGxB2tD,GAGX,IAAK,IAAI5uD,EAAI,EAAGA,EAAI2gH,EAAM12G,OAAQjK,IAAK,CACnC,MAAM4U,EAAO+rG,EAAM3gH,GAEnB,OAAQ4U,EAAKf,SACb,IAAK,MAAO,CACR,MAAM,WAAElD,GAAeiE,EAEvB,IAAKjE,EACD,MAEJ,MAAM,KAAEgK,GAAShK,EAEjBiwH,EAAOtkB,QAAU3hG,EACjB,MAEJ,IAAK,OACDimH,EAAOxkB,KAAOxnG,EAAK3T,MACnB,MACJ,IAAK,SACD2/H,EAAOhmH,GAAKhG,EAAK3T,MACjB,MACJ,IAAK,WACD2/H,EAAOvkB,QAAUznG,EAAK3T,MACtB,MACJ,IAAK,WACD2/H,EAAOhyE,SAAW2yE,EAA2B3sH,GAC7C,MACJ,IAAK,OAAQ,CACT,MAAM,WAAEjE,GAAeiE,EAEvB,IAAKjE,EACD,MAEJ,MAAM,KAAEpQ,GAASoQ,EAEJ,YAATpQ,IACAqgI,EAAOn5E,QAAU92C,EAAW1P,OAEhC,QAKR,GAAIoJ,IAASqJ,KAAK0mG,UAAW,CACzB,MAAMqnB,EACuB,UAAvBb,EAAOM,YAA0BN,EAAOzrD,KAAO,OAQrD,GANIzhE,KAAKyhE,OAASssD,IACd/tH,KAAKyhE,KAAOssD,EACZ/tH,KAAKwf,aAAa0D,KACdy3B,IAAW5qD,mBACXiQ,KAAKyhE,QAERzhE,KAAKojG,OAAQ,CACdpjG,KAAKojG,QAAS,EACd,MAAMjvF,EAAMnU,KAAK84C,gBAAgB,cAC3B5sD,OAAOsuD,YAAYrmC,MAEzB8J,EAAOrZ,IAAI,uBAAwBuP,GAG/BnU,KAAK27C,WACL37C,KAAK4rH,QAAS,GAMd5rH,KAAKgsH,oBAAsBhsH,KAAKssH,kBAChCtsH,KAAK0N,eAGT1N,KAAKwf,aAAa0D,KAAKy3B,IAAWrqD,aAIjC0P,KAAKmH,QAAQ2lH,kBAAoB9sH,KAAKusH,sBAExC,QAAYhsH,IAARoD,EACPsa,EAAO/Y,KAAK,2CACT,QAA2B3E,IAAvBP,KAAKgrH,QAAQr0H,GAEpBqJ,KAAKgrH,QAAQr0H,GAAQu2H,EACrBjvG,EAAOrZ,IAAI,UAAWjO,EAAMu2H,GAC5BE,OAAoC7sH,IAAlB2sH,EAAOn9G,OACzBs9G,OAAsC9sH,IAAnB2sH,EAAOn5E,QACtBm5E,EAAOtiB,QACP5qG,KAAKguH,WAAWr3H,EAAMgN,IAKtB3D,KAAKwf,aAAa0D,KACdy3B,IAAWpqD,kBACXoG,EACAu2H,EAAOxkB,KACPwkB,EAAOzrD,KACPyrD,EAAOQ,eACPR,EAAOvkB,QACPukB,EAAOn9G,OACPm9G,EAAOhyE,SACPgyE,EAAOtkB,QACPskB,EAAOvpH,KAIXypH,GAAkB,OAEnB,CAGH,MAAMa,EAAejuH,KAAKgrH,QAAQr0H,GAE9Bs3H,EAAaxsD,OAASyrD,EAAOzrD,OAC7BwsD,EAAaxsD,KAAOyrD,EAAOzrD,KAC3BzhE,KAAKwf,aAAa0D,KACdy3B,IAAW7pD,iBAAkB6F,EAAMu2H,EAAOzrD,OAI9CwsD,EAAaT,cAAgBN,EAAOM,cACpCS,EAAaT,YAAcN,EAAOM,aAIlCS,EAAarlB,UAAYskB,EAAOtkB,UAChCqlB,EAAarlB,QAAUskB,EAAOtkB,QAC9B5oG,KAAKwf,aAAa0D,KACdy3B,IAAWvqD,4BACXuG,EACAu2H,EAAOtkB,UAGXskB,EAAOtiB,UAWPqjB,EAAarjB,SAAU,EACvB5qG,KAAKguH,WAAWr3H,EAAMgN,IAItBupH,EAAOrhC,cACPoiC,EAAapiC,YAAcqhC,EAAOrhC,aAIlCoiC,EAAal+G,SAAWm9G,EAAOn9G,SAC/Bq9G,GAAkB,EAClBa,EAAal+G,OAASm9G,EAAOn9G,QAG7Bk+G,EAAal6E,UAAYm5E,EAAOn5E,UAChCs5E,GAAmB,EACnBY,EAAal6E,QAAUm5E,EAAOn5E,SAMtC,IAAK,IAAIznD,EAAI,EAAGA,EAAI2gH,EAAM12G,OAAQjK,IAAK,CACnC,MAAM4U,EAAO+rG,EAAM3gH,GAEnB,OAAQ4U,EAAKf,SACb,IAAK,OACD,IAAK+sH,EAAOtiB,QAAS,CACjB,MAAM/e,EACA7rF,KAAKqf,KAAKlY,QAAQ+mH,YACdzyH,UAAQyI,mBAAmBvN,GAC3Bu2H,EAAOxkB,KAEjB1oG,KAAKwf,aAAa0D,KACdy3B,IAAWtrD,qBACXsH,EACAk1F,GAER,MACJ,IAAK,qBACGqhC,EAAOtiB,UAAY5qG,KAAKmrH,oBACxBnrH,KAAKmrH,mBAAoB,EACzBnrH,KAAKwf,aAAa0D,KAAKy3B,IAAWlsD,cAEtC,MACJ,IAAK,wBACD,GAAIy+H,EAAOtiB,QAAS,CAChB,MAAMtjF,EAAa,GAEnB,IAAK,IAAIpuB,EAAI,EAAGA,EAAIgI,EAAK03F,SAASriG,OAAQ2C,IAAK,CAC3C,MAAM,WAAE+D,GAAeiE,EAAK03F,SAAS1/F,GAEjC+D,GAAcA,EAAWpP,MACzBy5B,EAAWrqB,EAAWpP,KAAOoP,EAAW1P,OAIhDyS,KAAKwf,aAAa0D,KACdy3B,IAAW7rD,8BAA+Bw4B,GAElD,MACJ,IAAK,uBAAwB,CACzB,MAAM,WAAErqB,GAAeiE,EAEvB,IAAKjE,EACD,MAGJ,MAAM,OAAE8S,GAAW9S,EAEf8S,GAAUA,IAAW/P,KAAKkmG,sBAC1BlmG,KAAKkmG,oBAAsBn2F,EAC3B/P,KAAKwf,aAAa0D,KACdy3B,IAAW/nD,6BACXmd,IAKR,MAEJ,IAAK,eAAgB,CACjB,MAAMo+G,EAAMjtH,EAAKjE,WAEjB,IAAKkxH,EACD,MAEJnuH,KAAKyrH,YAAc0C,EAAIC,OAAS,KAChCpuH,KAAK0rH,SAAWyC,EAAIE,KAAO,KAC3BruH,KAAKwf,aAAa0D,KAAKy3B,IAAWrpD,sBAClC,MAEJ,QACI0O,KAAKsuH,YAAYptH,EAAMvK,IAK3By2H,GACAptH,KAAKwf,aAAa0D,KACdy3B,IAAWnpD,gBACXmF,EACAu2H,EAAOn9G,QAGXs9G,GACApvG,EAAO/Y,KAAM,wBAAuBvB,MAAQupH,EAAOn5E,WAS3Di6E,WAAWr3H,EAAM+2G,GACb1tG,KAAKu4F,YAAc5hG,EAEnBsnB,EAAO/Y,KAAM,iBAAgBvO,gBAAmB+2G,KAChD1tG,KAAKqf,KAAKo6B,KAAKoqC,YAAY6pB,EAAQ,MAAOhzE,KAAK5xB,IAC3C9I,KAAKuuH,cAAgBzlH,EACrBmV,EAAO/Y,KAAM,yCAAwClF,KAAKwuH,+BAC3DxpH,IACCiZ,EAAOjZ,MAAM,qCAAsCA,GAASA,EAAMP,WAQ1E0tG,+BAA+BzwF,GAC3B1hB,KAAK2rH,4BAA8BjqG,EAOvC8sG,6BACI,QAAOxuH,KAAKuuH,eACNvuH,KAAKuuH,cAAcxqG,IAAI,mDASjCuqG,YAAYptH,EAAMvK,GAGd,IACI,IAAI83H,EAAczuH,KAAKirH,aAAa/pH,EAAKf,SAErCe,EAAKf,QAAQgyB,WAAW,wBACxBs8F,EAAc,CAAEzuH,KAAK2rH,8BAGrB8C,GACAA,EAAYr/G,QAAQ7K,IAChBA,EAAQrD,EAAMzF,UAAQyI,mBAAmBvN,GAAOA,KAG1D,MAAO0C,GACLu+B,IAAqBC,iBAAiBx+B,GACtC4kB,EAAOjZ,MAAO,oBAAmB9D,EAAKf,gBAAiB9G,IAU/D2vC,YAAYvkC,EAASygG,EAAawpB,GAC9B,MAAM5pH,EAAMmY,eAAK,CAAE8rB,GAAI/oC,KAAK+8C,QACxB91C,KAAM,cAKU,SAAhBi+F,EACApgG,EAAInY,EAAEu4G,EAAazgG,GAAS0B,KAE5BrB,EAAInY,EAAEu4G,EAAa,CAAEj/F,MAAO,4BAA8BxB,GACrD0B,KAELuoH,GACA5pH,EAAInY,EAAE,OAAQ,CAAEsZ,MAAO,oCAClBzY,EAAEkhI,GACFvoH,KACAA,KAETnG,KAAKuU,WAAWlH,KAAKvI,GACrB9E,KAAKwf,aAAa0D,KAAKy3B,IAAWvoD,qBAAsBqS,GAW5D4gG,mBAAmBn+F,EAAIzC,EAASygG,EAAawpB,GACzC,MAAM5pH,EAAMmY,eAAK,CAAE8rB,GAAK,GAAE/oC,KAAK+8C,WAAW71C,IACtCD,KAAM,SAKU,SAAhBi+F,EACApgG,EAAInY,EAAEu4G,EAAazgG,GAAS0B,KAE5BrB,EAAInY,EAAEu4G,EAAa,CAAEj/F,MAAO,4BAA8BxB,GACrD0B,KAELuoH,GACA5pH,EAAInY,EAAE,OAAQ,CAAEsZ,MAAO,oCAClBzY,EAAEkhI,GACFvoH,KACAA,KAGTnG,KAAKuU,WAAWlH,KAAKvI,GACrB9E,KAAKwf,aAAa0D,KACdy3B,IAAWtoD,6BAA8BoS,GAQjDihG,WAAWC,GACP,MAAM7gG,EAAMmY,eAAK,CAAE8rB,GAAI/oC,KAAK+8C,QACxB91C,KAAM,cAEVnC,EAAInY,EAAE,UAAWg5G,GACjB3lG,KAAKuU,WAAWlH,KAAKvI,GASzB6pH,kBAAkBhrH,EAAKirH,UACZ5uH,KAAKwrH,cAAc7nH,GAEtBirH,IAIJ5uH,KAAKwf,aAAa0D,KAAKy3B,IAAWnqD,gBAAiBmT,GAEnD3D,KAAKokG,UAAUyqB,gBAAgBlrH,IAQnCymH,sBAAsB36G,EAAM9Y,GAExB,GAAI87C,EAAEhjC,GAAMwhB,KAAK,8CAA8C16B,OAC3D,OAAO,EAIX,MAAMu4H,EAAgBr8E,EAAEhjC,GAAMwhB,KAAK,2DAEnC,GAAI69F,EAAcv4H,OAAQ,CACtB,IAAIiZ,EACJ,MAAMu/G,EACAt8E,EAAEhjC,GAAMwhB,KACN,kEAUR,OAPI89F,EAAax4H,SACbiZ,EAASu/G,EAAaptH,QAG1B3B,KAAKwf,aAAa0D,KAAKy3B,IAAWtqD,cAAemf,EAAQs/G,EAAcvtH,KAAK,QAC5EvB,KAAKuU,WAAW6oC,KAAKqtE,QAAQzqH,KAAK+8C,UAE3B,EAIX,MAAMysD,EACA/2D,EAAEhjC,GACCwhB,KACG,sEAEH16B,OACHy4H,EACAv8E,EAAEhjC,GACCwhB,KACG,sEAEH16B,OACH04H,EAAcjiI,OAAOuY,KAAKvF,KAAKgrH,SAErC,GAAIgE,EAAQ,CACR,MAAME,EACAz8E,EAAEhjC,GACHwhB,KAAK,8DAEV,IAAIk+F,EAEAD,EAAY34H,SACZ44H,EAAYD,EAAY3tH,KAAK,SAMjCvB,KAAKwf,aAAa0D,KACdy3B,IAAW7qD,OACX05G,EACA2lB,EACA1zH,UAAQyI,mBAAmBvN,IAG/B6yG,GAIAylB,EAAY7/G,QAAQzL,IAChB,MAAMupH,EAASltH,KAAKgrH,QAAQrnH,UAErB3D,KAAKgrH,QAAQrnH,GACpB3D,KAAK2uH,kBAAkBhrH,EAAKupH,EAAOtiB,WAEvC5qG,KAAKuU,WAAW6oC,KAAKqtE,QAAQzqH,KAAK+8C,SAI7BiyE,GACDhvH,KAAKwf,aAAa0D,KAAKy3B,IAAW9pD,mBAG/BmP,KAAKgrH,QAAQr0H,GACpBqJ,KAAK2uH,kBAAkBh4H,GAAM,IASrC2zH,UAAUxlH,EAAKnO,GACX,MAAM+xG,EACAj2D,EAAE3tC,GAAKmsB,KAAK,kDACTtvB,QACFlG,UAAQyI,mBAAmBvN,GAE5BsQ,EAAOnC,EAAIlC,aAAa,QAE9B,GAAa,UAATqE,EAAkB,CAClB,MAAMyxD,EAAWjmB,EAAE3tC,GAAKmsB,KAAK,eAAetvB,OAI5C,OAFA3B,KAAKwf,aAAa0D,KAAKy3B,IAAW9rD,oBAAqB6pE,IAEhD,EAGX,MAAM65C,EAAM9/D,EAAE3tC,GAAKmsB,KAAK,SAAStvB,OAC3BgkG,EAAUlzD,EAAE3tC,GAAKmsB,KAAK,YAE5B,GAAI00E,EAAQpvG,OAAQ,CAChB,MAAM64H,EAAczpB,EAAQhkG,QAExBytH,GAA+B,KAAhBA,KACfpvH,KAAKwf,aAAa0D,KAAKy3B,IAAWjoD,gBAAiB08H,GACnDnxG,EAAOrZ,IAAK,yBAAwBwqH,IAK5C,IAAIC,EAAQ58E,EAAE3tC,GAAKmsB,KAAK,UAAU1vB,KAAK,SAEvC,IAAK8tH,IAEDA,EAAQ58E,EAAE3tC,GAAKmsB,KAAK,6BAA6B1vB,KAAK,SAElD8tH,GAAO,CAEP,MAAMC,EACAD,EAAM/7G,MAAM,2CAElB+7G,EAAS,GAAEC,EAAU,MAAMA,EAAU,MAAMA,EAAU,MAI7D,GAAI34H,IAASqJ,KAAK+8C,QAAS,CACvB,IAAI40B,EAEJ,GAAIl/B,EAAE3tC,GAAKmsB,KAAK,sEAAsE16B,OAClFyJ,KAAKusH,qBACF,IAAK56C,EAASl/B,EAAE3tC,GAAKmsB,KAAK,4DAClB0gD,EAAOp7E,OAAQ,CAC1B,MAAMg5H,EAAiB98E,EAAE3tC,GAAKmsB,KAAK,4DACnC,IAAI0qB,EAEA4zE,GAAkBA,EAAeh5H,SACjColD,EAAW4zE,EAAe5tH,QAG9B3B,KAAKwf,aAAa0D,KAAKy3B,IAAWzqD,wBAC9ByG,EAAMg7E,EAAOpwE,KAAK,QAASgxG,EAAK52D,IAI5C,MAAM4D,EAAc9M,EAAE3tC,GAAKmsB,KAAK,iBAAiBtvB,OAEjD,GAAI49C,EAAa,CACb,MAAMC,EAAax/C,KAAKqf,KAAK+/B,sBAAsBG,GAKnD,GAAIC,QAAwBj/C,IAAV8uH,EAId,YAHArvH,KAAKwf,aAAa0D,KAAKy3B,IAAW1nD,sBAC9B0D,EAAM6oD,GAMd+yD,IACa,SAATtrG,EACAjH,KAAKwf,aAAa0D,KAAKy3B,IAAWxqD,yBAC1BwG,EAAM+xG,EAAM6J,EAAKvyG,KAAK0mG,UAAW2oB,GACzB,cAATpoH,GACPjH,KAAKwf,aAAa0D,KAAKy3B,IAAW1qD,iBAC1B0G,EAAM+xG,EAAM6J,EAAKvyG,KAAK0mG,UAAW2oB,IAUrDhF,gBAAgB56G,EAAM9Y,GAClB,GAAI87C,EAAEhjC,GACGwhB,KACG,mFAGH16B,OACL0nB,EAAOrZ,IAAI,uBAAwBjO,GACnCqJ,KAAKwf,aAAa0D,KAAKy3B,IAAWtpD,wBAC/B,GAAIohD,EAAEhjC,GACJwhB,KACG,kFAGH16B,OAAQ,CACIkF,UAAQoI,iBAAiB4L,EAAK7M,aAAa,SAE3C5C,KAAKqf,KAAKlY,QAAQs1C,MAAMD,gBAKrCx8C,KAAKwf,aAAa0D,KAAKy3B,IAAW1oD,kBAGlCgsB,EAAO9Y,KAAK,eAAgBsK,GAC5BzP,KAAKwf,aAAa0D,KACdy3B,IAAW3oD,sCAEhB,GAAIygD,EAAEhjC,GAAMwhB,KAAK,8BAA8B16B,OAClD0nB,EAAO9Y,KAAK,oDACRsK,GACJzP,KAAKwf,aAAa0D,KAAKy3B,IAAWxoD,2BAC/B,GAAIsgD,EAAEhjC,GACRwhB,KACG,0FAEkD16B,OAAQ,CAG9D,MAAMi5H,EAAgB/8E,EAAEhjC,GAAMwhB,KAAK,cACnC,IAAIw+F,EAEAD,EAAcj5H,SACdk5H,EAAeD,EAAc7tH,QAGjC3B,KAAKwf,aAAa0D,KAAKy3B,IAAWzoD,gCAAiCu9H,QAEnExxG,EAAO9Y,KAAK,eAAgBsK,GAC5BzP,KAAKwf,aAAa0D,KAAKy3B,IAAW5oD,oBAS1Cq2G,eAAezkG,EAAK6pH,GAChB,MAAMkC,EAAU/zH,cAAI,CAChBotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QAETta,EAAE,QAAS,CAAEsZ,MAAO,yCACpBtZ,EAAE,OAAQ,CACP6gI,cACA9kB,KAAMjtG,UAAQyI,mBAAmBP,KAEpChX,EAAE,UAAUa,EAAG,yCAAwCggI,OACvDrnH,KAAKA,KAAKA,KAEXnG,KAAKuU,WAAWnG,OACZshH,EACAjqH,GAAUwY,EAAOrZ,IAAI,4CAA6CjB,EAAK,KAAM6pH,EAAa/nH,GAC1FT,GAASiZ,EAAOrZ,IAAI,yCAA0CI,IAOtEsjG,KAAK3kG,GACD,MAAMgsH,EAASh0H,cAAI,CAAEotC,GAAI/oC,KAAK+8C,QAC1B91C,KAAM,QACLta,EAAE,QAAS,CAAEsZ,MAAO,yCACpBtZ,EAAE,OAAQ,CAAE+7G,KAAMjtG,UAAQyI,mBAAmBP,GAC1C89D,KAAM,SACT90E,EAAE,UAAUa,EAAE,yBAAyB2Y,KAAKA,KAAKA,KAEtDnG,KAAKuU,WAAWnG,OACZuhH,EACAlqH,GAAUwY,EAAOrZ,IAAI,8BAA+BjB,EAAK8B,GACzDT,GAASiZ,EAAOrZ,IAAI,2BAA4BI,IAYxDwiG,SAAS35G,EAAK0lB,EAAWq8G,EAASC,GAE9B7vH,KAAKuU,WAAWnG,OACZzS,cAAI,CACAotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QAELta,EAAE,QAAS,CAAEsZ,MAAO,yCACzBg4B,IACI,GAAIwU,EAAExU,GACGhN,KACG,0EAEH16B,OAAQ,CACb,MAAMu5H,EACAn0H,cAAI,CACFotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QAELta,EAAE,QAAS,CACRsZ,MAAO,yCAGnB6pH,EAAWnjI,EAAE,IAAK,CACdsZ,MAAO,gBACPgB,KAAM,WAEV6oH,EACKnjI,EAAE,QAAS,CAAE,IAAO,cACpBA,EAAE,SACFa,EAAE,6CACF2Y,KACAA,KACL2pH,EACKnjI,EAAE,QAAS,CAAE,IAAO,8BACpBA,EAAE,SACFa,EAAEK,GACFsY,KACAA,KACL2pH,EACKnjI,EAAE,QACE,CAAE,IAAO,yCACbA,EAAE,SACFa,EAAU,OAARK,GAA+B,IAAfA,EAAI0I,OAAe,IAAM,KAC3C4P,KACAA,KAGDnG,KAAKmxG,oBACL2e,EACKnjI,EAAE,QAAS,CAAE,IAAO,+BACpBA,EAAE,SACFa,EAAE,QACF2Y,KACAA,KAKT2pH,EACKnjI,EAAE,QAAS,CAAE,IAAO,yBACpBA,EAAE,SACFa,EAAE,UACF2Y,KACAA,KAELnG,KAAKuU,WAAWnG,OAAO0hH,EAAYv8G,EAAWq8G,QAE9CC,KAGRD,GAYRG,eAAezmD,EAAS/1D,EAAWq8G,GAC3BtmD,GAAWt8E,OAAOo2B,OAAOpjB,KAAKgrH,SAASt6F,OAAOhkC,IAAMA,EAAEk+G,SAASr0G,QAI/DvJ,OAAOo2B,OAAOpjB,KAAKgrH,SAAS57G,QAAQ1iB,IAC5BA,EAAEiX,MAAQonH,EAAqBt3F,SAAS/mC,EAAE8gI,cAC1CxtH,KAAKqf,KAAK9K,WAAWnG,OACjBzS,cAAI,CACAotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QACTta,EAAE,QAAS,CACRsZ,MAAO,yCACVtZ,EAAE,OAAQ,CACP,YAAe,SACf,IAAOD,EAAEiX,MACVwC,KAAKA,QAKxB,MAAMgyD,EAAgBy3D,GAAoB,SAE1C5vH,KAAKqf,KAAK9K,WAAWnG,OACjBzS,cAAI,CACAotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QACPta,EAAE,QAAS,CAAEsZ,MAAO,yCACvBg4B,IACI,GAAIwU,EAAExU,GAAKhN,KAAK,2EAA2E16B,OAAQ,CAC/F,MAAMy5H,EACAr0H,cAAI,CACFotC,GAAI/oC,KAAK+8C,QACT91C,KAAM,QACPta,EAAE,QAAS,CAAEsZ,MAAO,yCAE3B+pH,EAAarjI,EAAE,IAAK,CAChBsZ,MAAO,gBACPgB,KAAM,WAEV+oH,EACKrjI,EAAE,QAAS,CAAE,IAAO,cACpBA,EAAE,SACFa,EAAE,6CACF2Y,KACAA,KACL6pH,EACKrjI,EAAE,QAAS,CAAE,IAAO,+BACpBA,EAAE,SACFa,EAAE87E,EAAU,OAAS,SACrBnjE,KACAA,KAGDnG,KAAK4rH,QACLoE,EACKrjI,EAAE,QACC,CAAE,IAAO,yCACZA,EAAE,SACFa,EAAE,KACF2Y,KACAA,KAGTnG,KAAKqf,KAAK9K,WAAWnG,OAAO4hH,EAAcz8G,EAAW4kD,QAErDA,EAAc,IAAI9gE,MAAM,8CAGhC8gE,GAQRgsB,cAAct2F,EAAKu1B,GACfA,EAAOjjB,QAAUtS,EACjBmS,KAAKylG,mBAAmB53G,GACxBmS,KAAKgtG,QAAQC,MAAM/pG,KAAKkgB,GACxBpjB,KAAKgsH,mBAAqB7jH,KAAKgM,MASnCgxF,gBAAgBt3G,GACZ,OAAOmS,KAAKgtG,QAAQC,MAAMh8E,KAAK/vB,GAAQrT,IAAQqT,EAAKf,SAOxDslG,mBAAmB53G,GACf,MAAMo/G,EAAQjtG,KAAKgtG,QAAQC,MAAMv8E,OAAOxvB,GAAQrT,IAAQqT,EAAKf,SAE7DH,KAAKgtG,QAAQC,MAAQA,EACrBjtG,KAAKgsH,mBAAqB7jH,KAAKgM,MAQnC2wF,oBAAoBj4G,EAAM0X,GACtB,GAAuB,mBAAZA,EACP,MAAM,IAAIlN,MAAM,+BAEpB,IAAIo3H,EAAczuH,KAAKirH,aAAap+H,GAE/B4hI,IACDzuH,KAAKirH,aAAap+H,GAAQ4hI,EAAc,KAEN,IAAlCA,EAAY7qH,QAAQW,GACpBkqH,EAAYvrH,KAAKqB,GAEjB0Z,EAAO9Y,KACF,sDAAqDtY,GASlEm4G,uBAAuBn4G,EAAM0X,GACzB,MAAMkqH,EAAczuH,KAAKirH,aAAap+H,GAChCojI,EAAaxB,EAAcA,EAAY7qH,QAAQW,IAAY,GAG7C,IAAhB0rH,EACAxB,EAAYxqH,OAAOgsH,EAAY,GAE/BhyG,EAAO9Y,KAAM,gBAAetY,wBAYpC+9G,QAAQ8C,GACJ,MAAMwf,EAASltH,KAAKgrH,QAAQtd,GAE5B,OAAIwf,EACOA,EAAOtiB,QAGX,KAMXhF,cACI,MAAqB,cAAd5lG,KAAKyhE,KAOhByuD,cAAc9f,GACV,OAAIpwG,KAAKgrH,QAAQ5a,GACNpwG,KAAKgrH,QAAQ5a,GAAS3uC,KAG1B,KAQXwlC,aAAap/D,EAAMr8B,GACfxL,KAAKmwH,sBAAsBtoF,GACvBr8B,GACAA,EAASq8B,GASjBH,aAAaG,EAAMr8B,GACf,OAAOxL,KAAKowH,sBAAsBvoF,EAAMr8B,GAO5C6kH,uBAAuBxoF,GACnB7nC,KAAKmkF,cACD,aACA,CACIlnF,WAAY,CAAE,MAAS,kCACvB1P,MAAOs6C,EAAKnxC,aASxB05H,sBAAsBvoF,EAAMr8B,GACxBxL,KAAKqwH,uBAAuBxoF,GAG5B7nC,KAAK0N,eACDlC,GACAA,IAQRugH,uBAAuBlkF,GACnB7nC,KAAKmkF,cACD,aACA,CACIlnF,WAAY,CAAE,MAAS,kCACvB1P,MAAOs6C,EAAKnxC,aAQxBy5H,sBAAsBtoF,GAClB7nC,KAAK+rH,uBAAuBlkF,GAC5B7nC,KAAK0N,eAcT4iH,qBAAqBt0D,EAAYrwC,GAE7B,MAAMlc,EAAOzP,KAAKwrH,cAAe,GAAExrH,KAAK+8C,WAAWif,KAEnD,IAAKvsD,EAED,OAAO,KAEX,MAAM9V,EAAO,CACTmrB,OAAO,EACP0Z,eAAWj+B,GAEf,IAAIgwH,EAAY,KAEhB,GAAI5kG,IAAc4S,IACdgyF,EAAYzF,EAA2Br7G,EAAM,kBAC1C,IAAIkc,IAAc4S,IAUrB,OAFAtgB,EAAOjZ,MAAO,2BAA0B2mB,GAEjC,KAV+B,CACtC4kG,EAAYzF,EAA2Br7G,EAAM,cAC7C,MAAM+gH,EAAgB1F,EAA2Br7G,EAAM,aAEnD+gH,EAAcj6H,OAAS,IACvBoD,EAAK6kC,UAAYgyF,EAAc,GAAGjjI,QAU1C,OAFAoM,EAAKmrB,MAAQyrG,EAAUh6H,OAAS,GAA4B,SAAvBg6H,EAAU,GAAGhjI,MAE3CoM,EAMXsyG,wBACI,QAAIjsG,KAAKokG,WACEpkG,KAAKokG,UAAUqsB,sBAU9BhzE,KAAKhM,GACD,OAAOzxC,KAAKuU,WAAW4lC,KAAKsD,KAAKhM,EAAQ,aACrCh2C,UAAQsI,kBAAkB/D,KAAK0mG,WAAY1mG,KAAK27C,SAChD37C,KAAKu4F,aAMb2T,SACI,OAAOlsG,KAAKuU,WAAW4lC,KAAK+xD,SAOhC+E,WACI,OAAOjxG,KAAKqrH,MAMhBhf,iBACI,OAAOrsG,KAAKyrH,YAMhBnf,cACI,OAAOtsG,KAAK0rH,SAQhBlf,eACI,OAAOxsG,KAAK6wG,UAQhBpK,gBAAgB9iG,EAAKkkC,GACjB5pB,EAAO/Y,KAAK,WAAY2iC,GACxB,MAAM6oF,EAAY/0H,cACd,CAAEotC,GAAI/oC,KAAKu4F,YACPtxF,KAAM,QACTta,EAAE,OAAQ,CACPsZ,MAAO,iCACPtC,QAEHnW,EAAEq6C,EAAKnxC,YACPyP,KAELnG,KAAKuU,WAAWnG,OACZsiH,EACAjrH,GAAUwY,EAAOrZ,IAAI,WAAYa,GACjCT,GAASiZ,EAAOrZ,IAAI,iBAAkBI,IAO9CulH,OAAOt3G,GAGH,GAFaA,EAAGrQ,aAAa,UAEhB5C,KAAKu4F,YAGd,YAFAt6E,EAAO9Y,KAAK,oCAIhB,MAAM0iC,EAAO4K,EAAEx/B,GAAIge,KAAK,QAEpB4W,EAAKtxC,QAA0B,SAAhBsxC,EAAKlmC,OACpB3B,KAAKwf,aAAa0D,KAAKy3B,IAAWpsD,qBAAsBs5C,EAAKtmC,KAAK,UAKlE0c,EAAO9Y,KAAK,sFAQpBwrH,QACI3wH,KAAKkrH,qBAAqB97G,QAAQ0jE,GAAUA,KAC5C9yE,KAAKkrH,qBAAuB,GAE5BlrH,KAAKojG,QAAS,EASlBI,QACI,OAAO,IAAIxmE,QAAQ,CAACC,EAASC,KACzB,MAAMtvB,EAAUrD,WAAW,IAAMqmH,GAAU,GAAO,KAC5CpxG,EAAexf,KAAKwf,aAQ1B,SAASoxG,EAAUC,GAAW,GAC1BrxG,EAAasC,eAAe64B,IAAW9pD,SAAU+/H,GACjDnjH,aAAaG,GACTijH,EAEA3zF,EAAO,IAAI7lC,MAAM,qEAGjB4lC,IAdRj9B,KAAK2wH,QAiBLnxG,EAAamC,GAAGg5B,IAAW9pD,SAAU+/H,GACrC5wH,KAAKyqH,gB,qECjsDjB,gFAKA,MAAMxsG,EAASF,oBAAUM,GAaV,MAAMitG,EAOjBr1H,YAAYkpD,GACRn/C,KAAKqf,KAAO8/B,EAAK9/B,KACjBrf,KAAK8wH,SAAW3xE,EAEhB,MAAM4xE,EAAqB/wH,KAAKgxH,oBAAoBljI,KAAKkS,MAEzDA,KAAK8wH,SAASz0F,iBACVse,IAAW5qD,mBACXghI,GAEJ/wH,KAAK8wH,SAASz0F,iBACVse,IAAW3pD,yBACX+/H,GAEJ/wH,KAAK8wH,SAASz0F,iBACVse,IAAWzoD,gCACXyR,IACI3D,KAAKyvH,aAAe9rH,IAShC65B,cACI,OAAOx9B,KAAKqf,KAAKg8B,eAQrB7Z,SACI,OAAKxhC,KAAKw9B,cAIH,IAAIR,QAAQ,CAACC,EAASC,KACzBl9B,KAAK8wH,SAASf,gBAAe,EAAM9yF,EAASC,KAJrCF,QAAQE,OAAO,IAAI7lC,MAAM,yBAaxCi6G,UACStxG,KAAKw9B,eAAkBx9B,KAAK8wH,SAASlrB,eAC9B5lG,KAAKixH,WAAcjxH,KAAK8wH,SAAS3f,oBAI7CnxG,KAAK8wH,SAASf,gBAAe,GAOjCmB,kBACQlxH,KAAKixH,WACLjxH,KAAKixH,UAAUztB,QACV9oE,KAAK,KACF16B,KAAKixH,eAAY1wH,EACjB0d,EAAO/Y,KAAK,sBAEfk4B,MAAM,QASnByvF,gBAAgBlpH,GACZ3D,KAAKyvH,aAAe9rH,EAOxBqtH,sBACI,IAAKhxH,KAAKw9B,cACN,OAGgBx9B,KAAK8wH,SAAS1tB,QAAUpjG,KAAK8wH,SAASlrB,eAEvC5lG,KAAK8wH,SAAS3f,qBAAuBnxG,KAAKixH,WAEzDjxH,KAAKmD,OACAu3B,KAAK,IAAMzc,EAAO/Y,KAAK,sBACvBk4B,MAAM/jC,GAAK4kB,EAAOjZ,MAAM,uBAAwB3L,IAW7D8J,KAAK0oF,EAAa2lB,GACd,MAAM5L,EAAc5lG,KAAK8wH,SAAS1tB,QAAUpjG,KAAK8wH,SAASlrB,cAE1D,IAAK5lG,KAAKyvH,aACN,OAAOzyF,QAAQE,OAAO,IAAI7lC,MAAM,kDAGpC,MAAMwlD,EAAWphD,UAAQiI,eAAe1D,KAAKyvH,cACvCzyE,EAAevhD,UAAQoI,iBAAiB7D,KAAKyvH,cAmHnD,OAjHAzvH,KAAKixH,UAAYjxH,KAAKqf,KAAKu9B,WACvBC,EAAU,CACNG,eACA8vE,kBAAkB,EAClBZ,cAAc,EACd9a,aAAa,IAIjBvlB,IAEA7rF,KAAKixH,UAAUxrB,mBAAmB,QAClCzlG,KAAKixH,UAAU9sC,cAAc,OAAQ,CACjClnF,WAAY,CAAEgJ,MAAO,mCACrB1Y,MAAOs+F,KAIX+Z,GACA5lG,KAAKixH,UAAUnsB,oBAvJL,QAuJwC,CAAC5jG,EAAMvK,KACrDqJ,KAAK8wH,SAAStxG,aAAa0D,KAAKy3B,IAAWjqD,yBAA0BiG,EAAM,CAAE66G,MAAOtwG,EAAK3T,UAE7FyS,KAAKixH,UAAU50F,iBACXse,IAAWpqD,kBAEX,CAACoG,EAAM+xG,EAAMjnC,EAAMisD,EAAgB/kB,EAAS54F,EAAQmrC,EAAU0tD,EAASjlG,KAE/D3W,OAAOo2B,OAAOpjB,KAAK8wH,SAAS9F,SAAS/5F,KAAKvkC,GAAKA,EAAEiX,MAAQA,IAM7D3D,KAAK8wH,SAAStxG,aAAa0D,KACvBy3B,IAAWlqD,wBACXgL,UAAQyI,mBAAmBvN,GAC3B+xG,EACAxtD,EAAWA,EAASi2E,YAAS5wH,KAGzCP,KAAKixH,UAAU50F,iBACXse,IAAWnqD,gBAAiBmG,IAGxBqJ,KAAK8wH,SAAStxG,aAAa0D,KACvBy3B,IAAWhqD,sBACX8K,UAAQyI,mBAAmBvN,MAGvCqJ,KAAKixH,UAAU50F,iBACXse,IAAWtqD,cACX,KAEIrD,OAAOuY,KAAKvF,KAAKixH,UAAUjG,SACtB57G,QAAQlW,GAAK8G,KAAK8wH,SAAStxG,aAAa0D,KACrCy3B,IAAWhqD,sBAAuB8K,UAAQyI,mBAAmBhL,KAErE8G,KAAKixH,UAAUN,QAEf3wH,KAAKixH,eAAY1wH,EACjB0d,EAAO/Y,KAAK,mCAIpBlF,KAAKixH,UAAU50F,iBAAiBse,IAAW7qD,OAAQ05G,IAC/C,GAAIA,EAKA,OAJAxpG,KAAK8wH,SAAStxG,aAAa0D,KAAKy3B,IAAW/pD,wBAE3CoP,KAAKixH,UAAUN,UASvB3wH,KAAK8wH,SAASz0F,iBACVse,IAAWzqD,wBACX,CAACg1B,EAASvuB,EAAM47G,EAAK6e,KACjBnzG,EAAOhZ,MAAO,6BAA4BigB,KAAWvuB,KAAQ47G,KACzDrtF,IAAYllB,KAAK8wH,SAAS/zE,UAE1B/8C,KAAK8wH,SAAS3tH,KAAKiuH,GAEnBpxH,KAAKkxH,qBAGjBlxH,KAAKixH,UAAU50F,iBACXse,IAAWtqD,cACX,CAACmf,EAAQ7L,KAGDA,GAAOA,IAAQ3D,KAAK8wH,SAAS/zE,QAC7B/8C,KAAK8wH,SAAS3tH,QAKlBnD,KAAKixH,UAAUN,QAEf3wH,KAAK8wH,SAAStxG,aAAa0D,KAAKy3B,IAAWtqD,cAAemf,MAKlExP,KAAK8wH,SAASz0F,iBACVse,IAAWrqD,WACX,KACI0P,KAAKkxH,qBAIV,IAAIl0F,QAAQ,CAACC,EAASC,KACzBl9B,KAAKixH,UAAU50F,iBAAiBse,IAAWrqD,WAAY,KACnD2sC,IAGIu0E,IAAU5L,IACV5lG,KAAKixH,UAAUxrB,mBA3Pb,SA4PFzlG,KAAKixH,UAAU9sC,cA5Pb,QA4P0C,CAAE52F,MAAOikH,IACrDxxG,KAAKixH,UAAUvjH,kBAGvB1N,KAAKixH,UAAU50F,iBAAiBse,IAAW1oD,gBAAiBirC,GAC5Dl9B,KAAKixH,UAAU50F,iBAAiBse,IAAW3oD,+BAAgCkrC,GAC3El9B,KAAKixH,UAAU50F,iBAAiBse,IAAW5oD,mBAAoBmrC,GAE/Dl9B,KAAKixH,UAAU9tH,SASvBuuG,WAAWxqG,GACP,IAAKlH,KAAKw9B,gBAAkBx9B,KAAK8wH,SAASlrB,cACtC,OAGJ,MAAMjiG,EAAM3W,OAAOuY,KAAKvF,KAAKixH,UAAUjG,SAClC/5F,KAAK/3B,GAAKuC,UAAQyI,mBAAmBhL,KAAOgO,GAE7CvD,EACA3D,KAAKixH,UAAU3oB,KAAK3kG,GAEpBsa,EAAOjZ,MAAO,wBAAuBkC,oBAQ7C0qG,cAAc1qG,GACV,IAAKlH,KAAKw9B,gBAAkBx9B,KAAK8wH,SAASlrB,cACtC,OAGJ,MAAMyrB,EAAgBrkI,OAAOuY,KAAKvF,KAAKixH,UAAUjG,SAC5C/5F,KAAK/3B,GAAKuC,UAAQyI,mBAAmBhL,KAAOgO,GAEjD,GAAImqH,EAAe,CACf,MAAM1tH,EAAM3D,KAAKixH,UAAUjG,QAAQqG,GAAe1tH,IAC5C2tH,EACAr0G,eAAK,CAAE8rB,GAAI/oC,KAAK8wH,SAAS/zE,UACtBpwD,EAAE,IAAK,CAAEsZ,MAAO,wCAChBtZ,EAAE,SAAU,CAAEo8C,GAAIplC,IAE3B3D,KAAKqf,KAAK9K,WAAWnG,OAAOkjH,EACxB,OACAj4H,IACI4kB,EAAOjZ,MAAO,4BAA2BrB,EAAOtK,UAGxD4kB,EAAOjZ,MAAO,wBAAuBqsH,wB,kECjUjD,wEAOA,MAAM/e,EACAl1F,EAAQ,IACRu9B,EAAav9B,EAAQ,GACrBwa,EAAuBxa,EAAQ,IAE/Ba,EAASF,oBAAUM,GAMzB,SAASkzG,EAAsBC,GAC3B,IAAI5kF,EAAQ,EAEZ,OAAO,SAASvkC,GAEZ,GAAIA,EAGA,YAFAukC,EAAQ,GAMZ,MAAMh/B,EAAUxC,KAAKsP,IAAI,EAAGkyB,EAAQ,GAIpC,OAFAA,GAAS,EAEFh/B,EAAU4jH,GAaV,SAASpG,EAAUvuE,EAAUx9B,EAAMssB,EAASxkC,GA0BvD,SAASua,EAAS2F,GACd,GAAIA,EAAM1tB,MAAQ0tB,EAAM1tB,KAAK44C,UAAW,CACpC,GAAIlrB,EAAMoqG,SAAWvlI,OAAO6uB,SAAS02G,OAKjC,YAJAxzG,EAAO9Y,KACF,6CACGkiB,EAAMoqG,QAIlB/wB,IAASnuD,UAAYlrB,EAAM1tB,KAAK44C,WAlCxCvyC,KAAK68C,SAAWA,EAChB78C,KAAK0xH,YAAcryG,EACnBrf,KAAK2xH,eAAiBJ,EAAsB,KAC5CvxH,KAAK4xH,oBAAsBL,EAAsB,KAGjDvxH,KAAK6xH,qBAAsB,EAC3B7xH,KAAKmH,QAAUA,EAKfnH,KAAK8xH,kBACC9xH,KAAKmH,QAAQoN,WAAWkoC,YAC4Bl8C,IAA/CP,KAAKmH,QAAQoN,WAAWkoC,MAAMs1E,aAEzC/xH,KAAKwf,aAAemsB,EAEpB3rC,KAAKuU,WAAavU,KAAK0xH,YAAYn9G,WAuB/BroB,OAAOmwC,iBACPnwC,OAAOmwC,iBAAiB,UAAW3a,GAAU,GAE7Cx1B,OAAO8lI,YAAY,YAAatwG,GAMxC0pG,EAAUl9H,UAAUi2G,sBAAwB,WACxC,OAAOnkG,KAAK6xH,qBAGhBzG,EAAUl9H,UAAUuiI,oBAAsB,WACtC,OAAOzwH,KAAK8xH,mBAGhB1G,EAAUl9H,UAAU2gI,gBAAkB,SAASlrH,GAC3Csa,EAAO/Y,KAAM,8BAA6BvB,GAGzB,UAFAlI,UAAQyI,mBAAmBP,KAGxCsa,EAAO/Y,KACH,gDACJlF,KAAKwf,aAAa0D,KAAKy3B,EAAWjrD,cAI1C07H,EAAUl9H,UAAU+jI,gBAAkB,SAASC,GACtClyH,KAAKmyH,eACNnyH,KAAKmyH,aAAeD,EACpBj0G,EAAO/Y,KAAM,sBAAqBlF,KAAKmyH,gBAI/C/G,EAAUl9H,UAAUu/H,gBAAkB,WAClC,OAAOztH,KAAKmyH,cAGhB/G,EAAUl9H,UAAUkkI,kBAAoB,WAEpC,IAAIC,EAAiBryH,KAAKmH,QAAQoN,WAAWkoC,MAAM46D,MAQnD,OAJKgb,IACDA,EAAkB,SAAQryH,KAAKmH,QAAQoN,WAAWkoC,MAAM3hD,QAGrDu3H,GAGXjH,EAAUl9H,UAAUokI,mBAAqB,WAErC,MAAM5yH,EAAO/D,cAAI,CAAEotC,GAAI/oC,KAAKoyH,oBACxBnrH,KAAM,SAGJ,UAAEsrC,GAAcmuD,IAChB6xB,EAAa7xB,IAAS8xB,UACtBh/F,EAASxzB,KAAKmH,QAAQob,WA6G5B,IAAIkwG,EAEJ,OA7GAx0G,EAAO/Y,KAAM,eAAcqtC,kBAA0BggF,KAErD7yH,EAAK/S,EAAE,aAAc,CACjBsZ,MAAO,kCACPk5C,KAAMn/C,KAAK68C,SACX,cAAe01E,IAGfhgF,GACA7yC,EAAKlE,MAAM,CAAE,aAAc+2C,SAEgBhyC,IAA3CP,KAAKmH,QAAQoN,WAAWm+G,gBACxBhzH,EAAK/S,EACD,WAAY,CACRE,KAAM,iBACNU,MAAOyS,KAAKmH,QAAQoN,WAAWm+G,iBAChCvsH,UAI2B5F,IAAlCP,KAAKmH,QAAQoN,WAAWkoC,YAC0Bl8C,IAA/CP,KAAKmH,QAAQoN,WAAWkoC,MAAMs1E,cACjCryH,EAAK/S,EACD,WAAY,CACRE,KAAM,eACNU,MAAOyS,KAAKmH,QAAQoN,WAAWkoC,MAAMs1E,eACtC5rH,UAEiB5F,IAAxBizB,EAAOkvE,cACPhjG,EAAK/S,EACD,WAAY,CACRE,KAAM,eACNU,MAAOimC,EAAOkvE,eACfv8F,KAEXzG,EAAK/S,EACD,WAAY,CACRE,KAAM,aACNU,MAAOorC,QAAQnF,EAAOumB,cACvB5zC,UAEkB5F,IAArBizB,EAAOm/F,WACPjzH,EAAK/S,EACG,WAAY,CACRE,KAAM,YACNU,MAAOorC,QAAQnF,EAAOm/F,aACvBxsH,UAEW5F,IAAtBizB,EAAOo/F,YACPlzH,EAAK/S,EACG,WAAY,CACRE,KAAM,aACNU,MAAOorC,QAAQnF,EAAOo/F,cACvBzsH,MAEc,IAAzBqtB,EAAOwmB,eACPt6C,EAAK/S,EACG,WAAY,CACRE,KAAM,gBACNU,OAAO,IACR4Y,UAEgB5F,IAA3BizB,EAAOq/F,iBACPnzH,EAAK/S,EACG,WAAY,CACRE,KAAM,kBACNU,MAAOimC,EAAOq/F,kBACf1sH,KAGfzG,EAAK/S,EACD,WAAY,CACRE,KAAM,gBACNU,OAAiD,IAA1CyS,KAAKmH,QAAQoN,WAAW2lC,gBAChC/zC,UACyB5F,IAA5BizB,EAAOs/F,kBACPpzH,EAAK/S,EACD,WAAY,CACRE,KAAM,mBACNU,MAAOimC,EAAOs/F,mBACf3sH,KAEPqtB,EAAOu/F,cACPrzH,EAAK/S,EACD,WAAY,CACRE,KAAM,eACNU,MAAOimC,EAAOu/F,eACf5sH,KAEPqtB,EAAOw/F,YACPtzH,EAAK/S,EACD,WAAY,CACRE,KAAM,aACNU,MAAOimC,EAAOw/F,aACf7sH,KAEPqtB,EAAO01C,SAAW11C,EAAO01C,QAAQ+pD,MACa,iBAApCz/F,EAAO01C,QAAQ+pD,KAAKC,aAC1B9nH,KAAKC,SAAWmoB,EAAO01C,QAAQ+pD,KAAKC,aACpCxzH,EAAK/S,EACD,WAAY,CACRE,KAAM,OACNU,OAAO,IACR4Y,KAMPnG,KAAKmH,QAAQob,WAAW6oF,mBAChC,IAAK,cACL,KAAK,EACL,UAAK7qG,EACDkyH,GAAW,EACX,MACJ,IAAK,YACDA,GAAW,EA+Cf,OA3CA/yH,EAAK/S,EACD,WAAY,CACRE,KAAM,WACNU,MAAOklI,IACRtsH,KAEHqtB,EAAO2/F,uBACPzzH,EAAK/S,EACD,WAAY,CACRE,KAAM,wBACNU,MAAOimC,EAAO2/F,wBACfhtH,UAEqC5F,IAA5CP,KAAKmH,QAAQob,WAAWk8E,iBACxB/+F,EAAK/S,EACD,WAAY,CACRE,KAAM,kBACNU,MAAOyS,KAAKmH,QAAQob,WAAWk8E,kBAChCt4F,UAEqC5F,IAA5CP,KAAKmH,QAAQob,WAAWm8E,iBACxBh/F,EAAK/S,EACD,WAAY,CACRE,KAAM,kBACNU,MAAOyS,KAAKmH,QAAQob,WAAWm8E,kBAChCv4F,UAE4B5F,IAAnCP,KAAKmH,QAAQob,WAAW6wG,QACxB1zH,EAAK/S,EACD,WAAY,CACRE,KAAM,SACNU,MAAOyS,KAAKmH,QAAQob,WAAW6wG,SAChCjtH,UAEiD5F,IAAxDP,KAAKmH,QAAQob,WAAW8wG,6BACxB3zH,EAAK/S,EACD,WAAY,CACRE,KAAM,8BACNU,MAAOyS,KAAKmH,QAAQob,WAAW8wG,8BAChCltH,KAEXzG,EAAKyG,KAEEzG,GAIX0rH,EAAUl9H,UAAUolI,eAAiB,SAASC,GAE1C,MAAMhhF,EAAYE,EAAE8gF,GAAUtiG,KAAK,cAAc1vB,KAAK,cAElDgxC,IACAt0B,EAAO/Y,KAAM,wBAAuBqtC,GACpCmuD,IAASnuD,UAAYA,IAI7B64E,EAAUl9H,UAAUslI,mBAAqB,SAASD,GAE9CvzH,KAAKiyH,gBAAgBx/E,EAAE8gF,GAAUtiG,KAAK,cAAc1vB,KAAK,aAEzD,MAAMkyH,EACAhhF,EAAE8gF,GAAUtiG,KACV,6DAC+C16B,OAAS,EAEhE0nB,EAAO/Y,KAAM,2BAA0BuuH,GAEvCzzH,KAAK6xH,oBAAsBp/E,EAAE8gF,GAAUtiG,KACnC,2DACiD16B,OAAS,EAE9D0nB,EAAO/Y,KACF,oCAAmClF,KAAK6xH,qBAExC7xH,KAAK6xH,qBAEN7xH,KAAKszH,eAAeC,GAIxB,MAAMtvB,EAAexxD,EAAE8gF,GAAUtiG,KAAK,eAAe1vB,KAAK,YAE1DvB,KAAKwf,aAAa0D,KAAKovF,EAAqBtlB,iBACxCymC,EAAuBxvB,GAIvBxxD,EAAE8gF,GAAUtiG,KACZ,gEACkD16B,SAClDyJ,KAAK8xH,mBAAoB,GAG7B7zG,EAAO/Y,KAAM,yBAAwBlF,KAAK8xH,oBAa9C1G,EAAUl9H,UAAUi+H,wBAA0B,WAC1C,OAAO,IAAInvF,QAAQC,IAEfj9B,KAAKiyH,gBAAgBjyH,KAAKmH,QAAQoN,WAAW49G,cAG7CnyH,KAAKuU,WAAWnG,OACZpO,KAAKsyH,qBACL7sH,GAAUzF,KAAK0zH,gCAAgCjuH,EAAQw3B,GACvDj4B,GAAShF,KAAK2zH,8BAA8B3uH,EAAOi4B,IAMvDj9B,KAAKuU,WAAW/G,WAaxB49G,EAAUl9H,UAAUylI,8BAAgC,SAAS3uH,EAAOwG,GAGhE,MAAMooH,EACAnhF,EAAEztC,GAAOisB,KAAK,0BAA0B16B,QACnCk8C,EAAEztC,GAAOisB,KAAK,yBAAyB16B,OAMlD,GAJIq9H,IACA31G,EAAO/Y,KAAK,+BACZw7F,IAASnuD,eAAYhyC,GAErBkyC,EAAEztC,GAAOisB,KAAK,4BAA4B16B,OAG1C,YAFAyJ,KAAKwf,aAAa0D,KAAKy3B,EAAWhrD,mBAMtC,MAAMkkI,EAAiBphF,EAAEztC,GAAOisB,KAAK,4BAErC,GAAI4iG,EAAet9H,OAAQ,CAEvB,MAAMu9H,EAAYD,EAAetyH,KAAK,cAChCwyH,EAAgBthF,EAAEztC,GAAOisB,KAAK,eACpC,IAAIynC,EAUJ,OARIq7D,IACAr7D,EAAWq7D,EAAcpyH,aAE7B3B,KAAKwf,aAAa0D,KACdy3B,EAAW7oD,kBACXgiI,EACAp7D,GAMR,GAAIjmB,EAAEztC,GAAOisB,KAAK,yBAAyB16B,OAAQ,CAC/C0nB,EAAO9Y,KAAK,uCAAwCH,GAUpD,OATiBvJ,UAAQoI,iBAAiBmB,EAAMpC,aAAa,SAE5C5C,KAAKmH,QAAQoN,WAAWkoC,MAAMD,kBAG3Cx8C,KAAK6xH,qBAAsB,QAE/B7xH,KAAKwf,aAAa0D,KAAKy3B,EAAWnsD,yBAItC,MAAMwlI,EAASh0H,KAAK4xH,sBACdj6F,EAAU,4BAA2Bq8F,EAE3Cp8F,EAAqBC,iBAAiB,IAAIxgC,MAAMsgC,IAChD1Z,EAAOjZ,MAAM2yB,EAAQ3yB,GAGrB,MAAMqtH,EAAiBryH,KAAKoyH,oBACtB6B,EAAWD,EAAS,IAKrBJ,GACD5zH,KAAKwf,aAAa0D,KACdy3B,EAAWlrD,mBACX4iI,EACA4B,GAIRj0H,KAAK2xH,gBAAe,GACpBzlI,OAAOqe,WACH,IAAMvK,KAAKmsH,0BAA0BzxF,KAAKlvB,GAC1CwoH,IAYR5I,EAAUl9H,UAAUwlI,gCAAkC,SAC9CjuH,EACA+F,GAQJ,GANAxL,KAAKwzH,mBAAmB/tH,GAGxBzF,KAAK4xH,qBAAoB,GAG0B,SAA/Cn/E,EAAEhtC,GAAQwrB,KAAK,cAAc1vB,KAAK,SAElCvB,KAAK2xH,gBAAe,GAGpBnmH,QACG,CACH,MAAMwoH,EAASh0H,KAAK2xH,iBAEpB1zG,EAAO/Y,KAAM,4BAA2B8uH,GACxC9nI,OAAOqe,WACH,IAAMvK,KAAKmsH,0BAA0BzxF,KAAKlvB,GAC1CwoH,KAIZ5I,EAAUl9H,UAAUujB,aAAe,WAC/B,OAAO,IAAIurB,QAAQ,CAACC,EAASC,KACzBl9B,KAAKuU,WAAWnG,OACZpO,KAAKsyH,qBACL7sH,IACIzF,KAAKszH,eAAe7tH,GACpBw3B,KAEJ07D,GAAWz7D,EAAO,CACdl4B,MAAOytC,EAAEkmD,GAAS1nE,KAAK,mBAClBi8E,KAAK,WACVzoG,QAASguC,EAAEkmD,GAAS1nE,KAAK,iBACpBtvB,aAMrBypH,EAAUl9H,UAAUs2G,YAAc,SAAS0vB,EAAaC,GACpDn0H,KAAKo0H,cAAyB,EAAOF,EAAaC,IAUtD/I,EAAUl9H,UAAUkmI,aAAe,SAASC,EAAOC,EAAOv7C,GACtD,MAAM9lE,EAAKtX,cAAI,CAAEotC,GAAI/oC,KAAKoyH,oBACtBnrH,KAAM,QACJzL,EAAQ,CACVyK,MAAO,kCACPk5C,KAAMn/C,KAAK68C,SACX,cAAe6jD,IAAS8xB,WAE5B,IAAIp7H,EAAM,WAeV,SAASwuD,EAAYjuB,EAAQxnB,GACzBynB,EAAqBC,iBAAiB,IAAIxgC,MAAMsgC,IAChD1Z,EAAOjZ,MAAM2yB,EAAQxnB,GACrB4oE,EAAU5oE,GAhBVkkH,IACA74H,EAAM64H,OAAQ,EACdj9H,EAAO,SAAQA,GAEnB6b,EAAGtmB,EAAE,YAAa6O,GAclBwE,KAAKuU,WAAWnG,OACZ6E,EACAxN,IAEI,IAAIs7B,EAAM0R,EAAEhtC,GAAQwrB,KAAK,aAAa1vB,KAAK,OAE3Cw/B,EAAMwzF,mBAAmBxzF,GACrBA,GACA9iB,EAAO/Y,KAAM,OAAM9N,MAAQ2pC,KAC3BuzF,EAAMvzF,IAEN6kB,EAAa,iBAAgBxuD,mBAAsBqO,IAG3DmgD,EAAY93D,UAAKyS,EAAY,OAAMnJ,aAI3Cg0H,EAAUl9H,UAAUq2G,iBAAmB,SAAS2vB,EAAaC,GACzDn0H,KAAKo0H,cAAyB,EAAMF,EAAaC,IAGrD/I,EAAUl9H,UAAUsmI,OAAS,SAAShpH,GAClC,MAAMyH,EAAKtX,cAAI,CAAEotC,GAAI/oC,KAAKoyH,oBACtBnrH,KAAM,SACJ,UAAEsrC,GAAcmuD,IAEjBnuD,GAKLt/B,EAAGtmB,EAAE,SAAU,CACXsZ,MAAO,kCACP,aAAcssC,IAElBvyC,KAAKuU,WAAWnG,OACZ6E,EACAxN,IAEI,IAAIgvH,EAAYhiF,EAAEhtC,GAAQwrB,KAAK,UAAU1vB,KAAK,cAE1CkzH,IACAA,EAAYF,mBAAmBE,IAEnCx2G,EAAO/Y,KAAM,oBAAmBuvH,EAAahvH,GAC7Ci7F,IAASnuD,eAAYhyC,EACrBiL,EAASipH,IAEbzvH,IAGI4yB,EAAqBC,iBAAiB,IAAIxgC,MAF3B,iBAGf4mB,EAAOjZ,MAHQ,eAGMA,MAzBzBwG,O,wDC5lBR,MAAM8lB,EAAalU,EAAQ,IASrB9X,EAAQ,CACV,UAAW,QAAS,UAAW,QAAS,QAAS,WAAY,OAC7D,SAAU,QAAS,WAAY,MAAO,OAAQ,WAAY,UAC1D,UAAW,WAAY,QAAS,UAAW,MAAO,OAAQ,YAC1D,UAAW,OAAQ,OAAQ,QAAS,UAAW,QAAS,WAAY,QACpE,SAAU,UAAW,QAAS,SAAU,SAAU,SAAU,QAC5D,SAAU,SAAU,WAAY,SAAU,SAAU,SAAU,UAC9D,WAAY,SAAU,SAAU,WAAY,QAAS,QAAS,QAC9D,UAAW,WAAY,QAAS,QAAS,OAAQ,QAAS,QAAS,SACnE,QAAS,SAAU,QAAS,KAAM,SAAU,OAAQ,QAAS,SAC7D,SAAU,SAAU,OAAQ,SAAU,UAAW,WAAY,UAC7D,QAAS,SAAU,OAAQ,QAAS,OAAQ,QAAS,YACrD,aAAc,OAAQ,QAAS,QAAS,aAAc,aACtD,UAAW,SAAU,OAAQ,QAAS,YAAa,YAAa,YAChE,aAAc,aAAc,cAAe,YAAa,UACxD,WAAY,SAAU,SAAU,SAAU,aAAc,MAAO,UAC/D,UAAW,SAAU,SAAU,UAAW,UAAW,MAAO,OAAQ,QACpE,SAAU,QAAS,SAAU,SAAU,SAAU,QAAS,SAAU,QACpE,QAAS,QAAS,QAAS,SAAU,QAAS,UAAW,OAAQ,WACjE,OAAQ,SAAU,OAAQ,QAAS,SAAU,SAAU,UAAW,SAClE,QAAS,QAAS,SAAU,QAAS,SAAU,SAAU,UACzD,SAAU,SAAU,QAAS,QAAS,QAAS,QAAS,QAAS,UACjE,SAAU,SAAU,QAAS,UAAW,UAAW,OAAQ,QAAS,OACpE,QAAS,QAAS,OAAQ,SAAU,MAAO,OAAQ,MAAO,SAC1D,WAAY,QAAS,QAAS,YAAa,YAAa,WAAY,QACpE,WAAY,YAAa,SAAU,SAAU,OAAQ,QAAS,SAC9D,WAAY,WAAY,WAAY,WAAY,SAAU,QAAS,QACnE,SAAU,QAAS,SAAU,QAAS,QAAS,SAAU,SAAU,OACnE,UAAW,WAAY,YAAa,WAAY,UAAW,YAC3D,OAAQ,UAAW,UAAW,QAAS,QAAS,SAAU,UAC1D,aAAc,SAAU,YAAa,YAAa,UAAW,aAC7D,WAAY,UAAW,SAAU,SAAU,OAAQ,QAAS,MAC5D,UAAW,UAAW,OAAQ,YAAa,UAAW,QAAS,SAC/D,QAAS,MAAO,SAAU,UAAW,OAAQ,QAAS,UAAW,QACjE,SAAU,QAAS,OAAQ,SAAU,UAAW,SAAU,UAAW,OACrE,OAAQ,SAAU,UAAW,UAAW,OAAQ,MAAO,SAAU,SACjE,QAAS,QAAS,UAAW,UAAW,MAAO,OAAQ,SAAU,WACjE,SAAU,QAAS,UAAW,SAAU,SAAU,OAAQ,UAC1D,SAAU,SAAU,SAAU,SAAU,QAAS,QAAS,YAC1D,SAAU,SAAU,UAAW,YAAa,WAAY,UACxD,UAAW,UAAW,SAAU,SAAU,SAAU,SAAU,SAC9D,MAAO,QAAS,OAAQ,OAAQ,QAAS,QAAS,OAAQ,QAAS,OACnE,SAAU,SAAU,UAAW,SAAU,QAAS,UAAW,QAC7D,OAAQ,aAAc,SAAU,SAAU,WAAY,OAAQ,UAC9D,OAAQ,QAAS,QAAS,MAAO,WAAY,WAAY,UACzD,SAAU,QAAS,SAAU,WAAY,aAAc,YACvD,UAAW,WAAY,WAAY,WAAY,UAAW,SAC1D,WAAY,UAAW,QAAS,OAAQ,QAAS,SAAU,UAC3D,WAAY,QAAS,SAAU,OAAQ,UAAW,SAAU,QAC5D,QAAS,SAAU,QAAS,SAAU,SAAU,UAAW,SAAU,OACrE,SAAU,QAAS,SAAU,QAAS,SAAU,QAAS,SACzD,UAAW,QAAS,KAAM,SAAU,QAAS,SAAU,SAAU,QACjE,OAAQ,OAAQ,SAAU,WAAY,UAAW,SAAU,QAC3D,UAAW,QAAS,SAAU,SAAU,UAAW,SAAU,SAC7D,UAAW,UAAW,UAAW,QAAS,UAAW,UAAW,SAChE,SAAU,UAAW,UAAW,SAAU,UAAW,UAAW,UAChE,SAAU,UAAW,UAAW,QAAS,OAAQ,QAAS,OAAQ,QAClE,SAAU,UAAW,QAAS,UAAW,YAAa,SAAU,UAChE,WAAY,UAAW,QAAS,UAAW,WAAY,QAAS,YAChE,QAAS,QAAS,SAAU,WAAY,SAAU,QAAS,QAC3D,SAAU,QAAS,SAAU,QAAS,OAAQ,MAAO,QAAS,SAC9D,QAAS,WAAY,SAAU,UAAW,SAAU,OAAQ,QAC5D,SAAU,UAAW,OAAQ,QAAS,UAAW,OAAQ,UACzD,SAAU,SAAU,UAAW,SAAU,UAAW,UAAW,SAC/D,SAAU,SAAU,UAAW,UAAW,aAAc,UACxD,UAAW,UAAW,OAAQ,QAAS,UAAW,SAAU,WAC5D,SAAU,QAAS,SAAU,QAAS,SAAU,WAAY,SAC5D,UAAW,WAAY,UAAW,SAAU,UAAW,QAAS,YAChE,SAAU,WAAY,WAAY,UAAW,WAAY,SACzD,UAAW,SAAU,SAAU,OAAQ,WAAY,QAAS,UAC5D,UAAW,SAAU,YAAa,YAAa,UAAW,SAC1D,WAAY,WAAY,YAAa,YAAa,WAAY,UAC9D,QAAS,QAAS,SAAU,UAAW,QAAS,SAAU,UAC1D,UAAW,YAAa,YAAa,QAAS,SAAU,QAAS,OACjE,QAAS,WAAY,QAAS,SAAU,WAAY,SAAU,WAC9D,UAAW,WAAY,UAAW,UAAW,UAAW,YACxD,QAAS,UAAW,WAAY,QAAS,OAAQ,UAAW,UAC5D,UAAW,UAAW,UAAW,OAAQ,WAAY,WAAY,QACjE,QAAS,SAAU,UAAW,aAAc,YAAa,aACzD,YAAa,YAAa,WAAY,aAAc,cACpD,UAAW,QAAS,QAAS,SAAU,QAAS,SAAU,QAC1D,WAAY,QAAS,SAAU,QAAS,aAAc,QAAS,WAC/D,QAAS,QAAS,SAAU,UAAW,UAAW,WAAY,OAC9D,UAAW,UAAW,aAAc,aAAc,UAAW,OAC7D,SAAU,QAAS,SAAU,QAAS,YAAa,WAAY,UAC/D,QAAS,UAAW,WAAY,SAAU,QAAS,QAAS,OAAQ,OACpE,QAAS,OAAQ,UAAW,QAAS,UAAW,SAAU,OAAQ,SAClE,SAAU,WAAY,aAAc,SAAU,SAAU,SAAU,QAClE,SAAU,YAAa,aAAc,WAAY,SAAU,OAC3D,UAAW,SAAU,WAAY,UAAW,SAAU,SAAU,SAChE,SAAU,YAAa,UAAW,UAAW,SAAU,UAAW,OAClE,OAAQ,WAAY,MAAO,QAAS,WAAY,SAAU,UAC1D,WAAY,WAAY,YAAa,aAAc,OAAQ,UAC3D,UAAW,SAAU,OAAQ,SAAU,SAAU,UAAW,QAC5D,QAAS,SAAU,SAAU,QAAS,SAAU,QAAS,SAAU,OACnE,SAAU,SAAU,SAAU,UAAW,SAAU,SAAU,SAC7D,SAAU,QAAS,MAAO,OAAQ,SAAU,OAAQ,WAAa,UACjE,SAAU,UAAW,WAAY,WAAY,SAAU,SAAU,QACjE,QAAS,SAAU,SAAU,UAAW,UAAW,QAAS,QAC5D,SAAU,UAAW,SAAU,QAAS,SAAU,SAAU,UAC5D,QAAS,SAAU,UAAW,SAAU,UAAW,SAAU,UAC7D,SAAU,SAAU,SAAU,QAAS,UAAW,QAAS,OAAQ,QACnE,QAAS,SAAU,QAAS,UAAW,OAAQ,SAAU,MAAO,SAChE,QAAS,QAAS,SAAU,OAAQ,WAAY,SAAU,UAC1D,SAAU,SAAU,UAAW,MAAO,QAAS,OAAQ,QAAS,QAChE,SAAU,UAAW,UAAW,UAAW,QAAS,UAAW,OAC/D,QAAS,SAAU,UAAW,SAAU,UAAW,WAAY,QAC/D,UAAW,WAAY,UAAW,WAAY,YAAa,SAAU,OACrE,QAAS,SAAU,OAAQ,UAAW,UAAW,SAAU,SAC3D,QAAS,SAAU,QAAS,UAAW,UAAW,UAAW,UAC7D,UAAW,SAAU,UAAW,SAAU,WAAY,WAAY,UAClE,UAAW,QAAS,UAAW,QAAS,QAAS,QAAS,UAC1D,QAAS,UAAW,SAAU,SAAU,UAAW,QAAS,SAC5D,QAAS,SAAU,SAAU,UAAW,OAAQ,OAAQ,OAAQ,QAChE,OAAQ,QAAS,UAAW,UAAW,WAAY,WAAY,WAC/D,UAAW,UAAW,YAAa,MAAO,SAAU,SAAU,UAC9D,QAAS,UAAW,SAAU,QAAS,OAAQ,SAAU,SAAU,QACnE,WAAY,UAAW,SAAU,SAAU,SAAU,OAAQ,UAC7D,QAAS,QAAS,QAAS,OAAQ,QAAS,SAAU,QAAS,SAC/D,UAAW,SAAU,QAAS,SAAU,QAAS,OAAQ,UACzD,UAAW,UAAW,aAAc,SAAU,SAAU,OAAQ,QAChE,KAAM,MAAO,MAAO,QAAS,OAAQ,QAAS,UAAW,SAAU,SACnE,SAAU,OAAQ,UAAW,SAAU,UAAW,QAAS,SAC3D,QAAS,SAAU,QAAS,SAAU,QAAS,SAAU,QAAS,OAClE,SAAU,SAAU,SAAU,OAAQ,QAAS,SAAU,SACzD,WAAY,WAAY,WAAY,UAAW,SAAU,QAAS,SAClE,UAAW,WAAY,WAAY,MAAO,QAAS,SAAU,QAC7D,UAAW,SAAU,SAAU,UAAW,QAAS,YAAa,QAChE,SAAU,SAAU,SAAU,QAAS,YAAa,OAAQ,QAC5D,QAAS,SAAU,UAAW,QAAS,YAAa,QAAS,SAC7D,OAAQ,SAAU,OAAQ,SAAU,QAAS,SAAU,SAAU,UACjE,OAAQ,QAAS,OAAQ,OAAQ,QAAS,OAAQ,QAAS,OAAQ,SACnE,QAAS,QAAS,QAAS,QAAS,QAAS,SAAU,OAAQ,UAC/D,SAAU,SAAU,QAAS,UAAW,UAAW,QAAS,OAAQ,OACpE,QAAS,SAAU,WAAY,SAAU,SAAU,QAAS,OAC5D,UAAW,WAAY,aAAc,QAAS,SAAU,QAAS,SACjE,OAAQ,QAAS,MAAO,OAAQ,QAAS,QAAS,OAAQ,SAC1D,UAAW,UAAW,MAAO,WAAY,OAAQ,QAAS,QAAS,QACnE,SAAU,OAAQ,QAAS,OAAQ,SAAU,OAAQ,SAAU,YAC/D,YAAa,UAAW,QAAS,QAAS,QAAS,OAAQ,YAC3D,YAAa,OAAQ,UAAW,YAAa,QAAS,SAAU,UAChE,UAAW,UAAW,SAAU,WAAY,OAAQ,QAAS,QAC7D,UAAW,QAAS,QAAS,SAAU,SAAU,UAAW,OAAQ,QACpE,UAAW,OAAQ,SAAU,UAAW,MAAO,SAAU,OACzD,aAAc,QAAS,MAAO,UAAW,SAAU,WAAY,UAC/D,WAAY,QAAS,OAAQ,QAAS,QAAS,UAAW,WAC1D,OAAQ,SAAU,UAAW,MAAO,SAAU,QAAS,SAAU,WACjE,SAAU,SAAU,MAAO,OAAQ,WAAY,UAAW,WAC1D,WAAY,SAAU,SAAU,QAAS,SAAU,SAAU,OAC7D,WAAY,QAAS,QAAS,YAAa,WAAY,OAAQ,QAC/D,SAAU,SAAU,QAAS,WAAY,MAAO,WAAY,YAC5D,UAAW,UAAW,UAAW,UAAW,OAAQ,QAAS,OAC7D,SAAU,UAAW,SAAU,UAAW,YAAa,YACvD,UAAW,YAAa,YAAa,SAAU,QAAS,UAAW,QACnE,OAAQ,QAAS,UAAW,SAAU,WAAY,YAAa,WAC/D,aAAc,WAAY,QAAS,SAAU,UAAW,SAAU,QAClE,SAAU,YAAa,QAAS,SAAU,OAAQ,UAAW,YAC7D,YAAa,UAAW,OAAQ,OAAQ,UAAW,SAAU,WAC7D,UAAW,SAAU,UAAW,SAAU,UAAW,UAAW,WAChE,QAAS,QAAS,SAAU,QAAS,MAAO,QAAS,UAAW,OAChE,UAAW,UAAW,YAAa,UAAW,WAAY,MAAO,WACjE,SAAU,YAAa,YAAa,aAAc,WAAY,WAC9D,UAAW,SAAU,YAAa,SAAU,UAAW,QAAS,UAChE,WAAY,SAAU,QAAS,SAAU,WAAY,MAAO,SAC5D,SAAU,UAAW,WAAY,QAAS,QAAS,UAAW,OAC9D,OAAQ,UAAW,WAAY,WAAY,WAAY,WACvD,WAAY,UAAW,SAAU,OAAQ,SAAU,SAAU,UAC7D,SAAU,UAAW,QAAS,SAAU,UAAW,SAAU,QAC7D,SAAU,WAAY,QAAS,SAAU,QAAS,YAAa,SAC/D,UAAW,QAAS,OAAQ,QAAS,WAAY,WAAY,UAC7D,QAAS,WAAY,UAAW,UAAW,SAAU,YAAa,SAClE,QAAS,YAAa,WAAY,SAAU,SAAU,MAAO,SAC7D,OAAQ,UAAW,MAAO,OAAQ,YAAa,SAAU,SAAU,SACnE,SAAU,MAAO,UAAW,QAAS,QAAS,QAAS,SAAU,OACjE,QAAS,SAAU,OAAQ,QAAS,SAAU,SAAU,UAAW,SACnE,WAAY,QAAS,SAAU,UAAW,SAAU,SAAU,SAC9D,QAAS,SAAU,SAAU,SAAU,SAAU,QAAS,QAAS,QACnE,UAAW,SAAU,QAAS,SAAU,QAAS,QAAS,SAC1D,SAAU,QAAS,SAAU,SAAU,UAAW,YAAa,QAC/D,YAAa,QAAS,UAAW,SAAU,UAAW,UAAW,WACjE,WAAY,UAAW,QAAS,SAAU,SAAU,SAAU,UAC9D,UAAW,QAAS,YAAa,UAAW,UAAW,QAAS,SAChE,WAAY,QAAS,SAAU,SAAU,SAAU,SAAU,QAC7D,OAAQ,SAAU,UAAW,WAAY,QAAS,UAAW,SAC7D,SAAU,OAAQ,SAAU,SAAU,OAAQ,QAAS,WAAY,SACnE,QAAS,MAAO,UAAW,OAAQ,MAAO,QAAS,SAAU,UAC7D,WAAY,MAAO,MAAO,QAAS,SAAU,MAAO,QAAS,SAC7D,WAAY,UAAW,OAAQ,OAAQ,SAAU,QAAS,QAAS,SACnE,UAAW,WAAY,WAAY,OAAQ,UAAW,OAAQ,SAC9D,SAAU,SAAU,SAAU,SAAU,OAAQ,SAAU,QAAS,QACnE,MAAO,OAAQ,QAAS,MAAO,WAAY,SAAU,SAAU,OAC/D,QAAS,WAAY,UAAW,OAAQ,YAAa,SAAU,UAC/D,UAAW,QAAS,SAAU,YAAa,UAAW,WAAY,OAClE,OAAQ,QAAS,QAAS,QAAS,SAAU,QAAS,SAAU,SAChE,QAAS,QAAS,UAAW,OAAQ,QAAS,SAAU,QAAS,QACjE,QAAS,SAAU,QAAS,QAAS,WAAY,QAAS,UAC1D,QAAS,QAAS,QAAS,QAAS,UAAW,SAAU,MAAO,OAChE,QAAS,OAAQ,UAAW,UAAW,QAAS,SAAU,SAAU,QACpE,QAAS,SAAU,SAAU,OAAQ,SAAU,WAAY,YAC3D,QAAS,QAAS,QAAS,QAAS,SAAU,UAAW,SACzD,UAAW,SAAU,SAAU,QAAS,SAAU,QAAS,SAC3D,UAAW,SAAU,QAAS,UAAW,MAAO,QAAS,SAAU,QACnE,QAAS,SAAU,SAAU,SAAU,SAAU,SAAU,SAC3D,QAAS,QAAS,SAAU,SAAU,SAAU,SAAU,UAAW,OACrE,WAAY,SAAU,SAAU,MAAO,WAAY,WAAY,OAC/D,WAAY,UAAW,UAAW,SAAU,OAAQ,UAAW,SAC/D,WAAY,WAAY,WAAY,SAAU,QAAS,SAAU,UACjE,SAAU,QAAS,UAAW,SAAU,UAAW,WAAY,SAC/D,QAAS,SAAU,SAAU,UAAW,SAAU,UAAW,QAC7D,OAAQ,QAAS,UAAW,SAAU,UAAW,QAAS,UAC1D,QAAS,OAAQ,SAAU,QAAS,QAAS,SAAU,UAAW,SAClE,QAAS,KAAM,OAAQ,QAAS,SAAU,SAAU,UAAW,SAC/D,QAAS,UAAW,UAAW,QAAS,OAAQ,MAAO,OAAQ,SAC/D,SAAU,OAAQ,QAAS,SAAU,UAAW,WAAY,OAC5D,YAAa,YAAa,UAAW,SAAU,WAAY,UAC3D,QAAS,MAAO,QAAS,UAAW,WAAY,WAAY,SAC5D,UAAW,QAAS,SAAU,QAAS,SAAU,QAAS,OAAQ,SAClE,WAAY,SAAU,YAAa,OAAQ,SAAU,UAAW,SAChE,UAAW,WAAY,QAAS,QAAS,QAAS,SAAU,UAC5D,SAAU,MAAO,QAAS,QAAS,UAAW,QAAS,OAAQ,OAC/D,QAAS,SAAU,OAAQ,QAAS,QAAS,SAAU,UACvD,WAAY,QAAS,SAAU,SAAU,QAAS,SAAU,OAC5D,SAAU,SAAU,SAAU,UAAW,UAAW,UAAW,SAC/D,SAAU,SAAU,UAAW,QAAS,QAAS,OAAQ,QAAS,OAClE,QAAS,QAAS,QAAS,SAAU,OAAQ,SAAU,SAAU,SACjE,UAAW,UAAW,OAAQ,QAAS,UAAW,QAAS,OAAQ,SACnE,UAAW,UAAW,SAAU,SAAU,SAAU,OAAQ,OAC5D,SAAU,UAAW,QAAS,OAAQ,UAAW,WAAY,SAC7D,SAAU,OAAQ,SAAU,SAAU,QAAS,SAAU,WACzD,SAAU,WAAY,QAAS,YAAa,WAAY,UACxD,UAAW,WAAY,YAAa,YAAa,WAAY,WAC7D,UAAW,UAAW,WAAY,SAAU,UAAW,UAAW,UAClE,SAAU,QAAS,MAAO,OAAQ,SAAU,SAAU,QAAS,SAC/D,SAAU,WAAY,SAAU,QAAS,SAAU,SAAU,SAC7D,QAAS,QAAS,SAAU,SAAU,SAAU,QAAS,SAAU,QACnE,QAAS,SAAU,SAAU,QAAS,SAAU,QAAS,QAAS,SAClE,SAAU,SAAU,MAAO,UAAW,SAAU,WAAY,QAC5D,UAAW,UAAW,UAAW,UAAW,QAAS,SAAU,QAC/D,WAAY,SAAU,OAAQ,UAAW,UAAW,QAAS,QAC7D,QAAS,QAAS,WAAY,SAAU,SAAU,OAAQ,QAAS,SACnE,QAAS,SAAU,OAAQ,QAAS,SAAU,QAAS,MAAO,WAC9D,OAAQ,MAAO,OAAQ,OAAQ,UAAW,UAAW,QAAS,OAC9D,OAAQ,OAAQ,QAAS,OAAQ,SAAU,QAAS,OAAQ,QAAS,OACrE,SAAU,WAAY,UAAW,WAAY,YAAa,aAC1D,aAAc,SAAU,UAAW,UAAW,WAAY,OAAQ,SAClE,OAAQ,OAAQ,QAAS,UAAW,QAAS,QAAS,OAAQ,QAC9D,OAAQ,WAAY,YAAa,QAAS,SAAU,QAAS,UAC7D,SAAU,QAAS,SAAU,OAAQ,QAAS,UAAW,QAAS,QAClE,QAAS,QAAS,SAAU,SAAU,WAAY,WAAY,WAC9D,YAAa,SAAU,UAAW,QAAS,SAAU,SAAU,SAC/D,UAAW,UAAW,SAAU,UAAW,QAAS,UAAW,WAC/D,SAAU,QAAS,QAAS,SAAU,MAAO,QAAS,SAAU,SAChE,SAAU,OAAQ,MAAO,OAAQ,QAAS,UAAW,OAAQ,QAC7D,SAAU,QAAS,QAAS,SAAU,QAAS,SAAU,QAAS,SAClE,SAAU,MAAO,QAAS,OAAQ,UAAW,WAAY,QAAS,SAClE,SAAU,SAAU,UAAW,UAAW,WAAY,QAAS,OAC/D,SAAU,SAAU,QAAS,SAAU,SAAU,OAAQ,QAAS,UAClE,OAAQ,MAAO,QAAS,OAAQ,QAAS,QAAS,MAAO,OAAQ,QACjE,SAAU,QAAS,OAAQ,SAAU,UAAW,UAAW,QAC3D,UAAW,WAAY,SAAU,QAAS,OAAQ,SAAU,OAC5D,UAAW,QAAS,UAAW,UAAW,SAAU,SAAU,UAC9D,SAAU,OAAQ,WAAY,UAAW,QAAS,OAAQ,SAC1D,SAAU,YAAa,QAAS,QAAS,OAAQ,OAAQ,SAAU,OACnE,MAAO,SAAU,QAAS,SAAU,QAAS,QAAS,OAAQ,UAC9D,QAAS,SAAU,SAAU,UAAW,UAAW,OAAQ,SAC3D,QAAS,SAAU,MAAO,QAAS,SAAU,UAAW,WACxD,SAAU,MAAO,QAAS,QAAS,QAAS,UAAW,QAAS,WAChE,SAAU,UAAW,QAAS,UAAW,SAAU,OAAQ,QAC3D,SAAU,MAAO,SAAU,QAAS,OAAQ,QAAS,QAAS,OAC9D,OAAQ,OAAQ,OAAQ,OAAQ,UAAW,SAAU,MAAO,OAAQ,QACpE,OAAQ,OAAQ,QAAS,UAAW,QAAS,UAAW,QAAS,MACjE,QAAS,OAAQ,QAAS,OAAQ,YAAa,OAAQ,WACvD,UAAW,WAAY,WAAY,UAAW,WAAY,QAAS,QACnE,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,MAClE,SAAU,QAAS,UAAW,SAAU,WAAY,YAAa,SACjE,WAAY,SAAU,OAAQ,QAAS,QAAS,QAAS,UACzD,UAAW,WAAY,UAAW,UAAW,SAAU,UAAW,SAClE,UAAW,UAAW,QAAS,SAAU,SAAU,UAAW,OAC9D,OAAQ,SAAU,YAAa,YAAa,WAAY,WACxD,YAAa,UAAW,SAAU,QAAS,SAAU,SAAU,WAC/D,YAAa,YAAa,aAAc,aAAc,YAAa,QACnE,SAAU,SAAU,UAAW,aAAc,QAAS,SAAU,SAChE,SAAU,UAAW,UAAW,WAAY,WAAY,UACxD,UAAW,QAAS,UAAW,WAAY,WAAY,UAAW,UAClE,WAAY,SAAU,QAAS,SAAU,SAAU,UAAW,UAC9D,aAAc,WAAY,UAAW,OAAQ,SAAU,SAAU,SACjE,UAAW,SAAU,SAAU,SAAU,UAAW,UAAW,WAC/D,WAAY,QAAS,SAAU,UAAW,UAAW,QAAS,SAC9D,OAAQ,SAAU,WAAY,SAAU,QAAS,QAAS,SAC1D,UAAW,WAAY,UAAW,UAAW,OAAQ,SAAU,SAC/D,OAAQ,QAAS,SAAU,UAAW,UAAW,WAAY,UAC7D,WAAY,QAAS,MAAO,QAAS,SAAU,aAAc,aAC7D,cAAe,SAAU,UAAW,SAAU,UAAW,MAAO,OAChE,UAAW,WAAY,OAAQ,SAAU,UAAW,QAAS,QAC7D,UAAW,UAAW,WAAY,SAAU,UAAW,OAAQ,SAC/D,SAAU,SAAU,QAAS,SAAU,QAAS,SAAU,UAC1D,SAAU,SAAU,SAAU,UAAW,SAAU,UAAW,WAC9D,WAAY,OAAQ,QAAS,SAAU,UAAW,SAAU,SAC5D,OAAQ,MAAO,UAAW,QAAS,UAAW,WAAY,UAC1D,UAAW,SAAU,UAAW,WAAY,SAAU,UAAW,OACjE,QAAS,QAAS,QAAS,UAAW,SAAU,SAAU,OAAQ,SAClE,OAAQ,UAAW,SAAU,UAAW,WAAY,SAAU,SAC9D,WAAY,QAAS,UAAW,WAAY,SAAU,UAAW,UACjE,UAAW,WAAY,WAAY,SAAU,SAAU,QAAS,OAChE,SAAU,UAAW,SAAU,YAAa,aAAc,UAC1D,QAAS,QAAS,SAAU,SAAU,SAAU,WAAY,SAC5D,OAAQ,QAAS,QAAS,SAAU,SAAU,UAAW,WACzD,SAAU,OAAQ,SAAU,SAAU,UAAW,MAAO,OAAQ,SAChE,QAAS,OAAQ,SAAU,OAAQ,QAAS,QAAS,UAAW,SAChE,SAAU,SAAU,QAAS,QAAS,OAAQ,SAAU,QAAS,SACjE,WAAY,UAAW,OAAQ,QAAS,MAAO,UAAW,UAC1D,UAAW,SAAU,YAAa,YAAa,YAAa,SAC5D,SAAU,OAAQ,MAAO,QAAS,OAAQ,OAAQ,QAAS,QAAS,QACpE,SAAU,OAAQ,SAAU,QAAS,SAAU,SAAU,OAAQ,SACjE,SAAU,MAAO,WAAY,YAAa,UAAW,OAAQ,WAC7D,WAAY,OAAQ,SAAU,UAAW,SAAU,YAAa,QAChE,SAAU,QAAS,QAAS,OAAQ,UAAW,OAAQ,OAAQ,OAC/D,QAAS,MAAO,OAAQ,SAAU,QAAS,SAAU,QAAS,OAC9D,QAAS,OAAQ,OAAQ,UAAW,WAAY,SAAU,QAAS,SACnE,SAAU,UAAW,OAAQ,UAAW,MAAO,OAAQ,SAAU,OACjE,SAAU,OAAQ,UAAW,MAAO,QAAS,SAAU,OAAQ,SAC/D,OAAQ,MAAO,OAAQ,MAAO,OAAQ,QAAS,OAAQ,OAAQ,SAC/D,QAAS,MAAO,QAAS,OAAQ,MAAO,OAAQ,OAAQ,UAAW,MACnE,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAAS,SAAU,UAC3D,OAAQ,QAAS,QAAS,QAAS,UAAW,UAAW,UAAW,QACpE,UAAW,SAAU,UAAW,OAAQ,OAAQ,SAAU,OAAQ,UAClE,QAAS,OAAQ,MAAO,OAAQ,SAAU,QAAS,QAAS,QAC5D,SAAU,QAAS,QAAS,QAAS,SAAU,UAAW,WAAY,MACtE,WAAY,WAAY,UAAW,QAAS,SAAU,OAAQ,QAC9D,UAAW,SAAU,SAAU,QAAS,UAAW,WAAY,QAC/D,SAAU,WAAY,WAAY,QAAS,QAAS,OAAQ,QAC5D,QAAS,SAAU,SAAU,SAAU,UAAW,SAAU,SAC5D,SAAU,OAAQ,SAAU,QAAS,QAAS,SAAU,WACxD,UAAW,UAAW,QAAS,SAAU,WAAY,YACrD,aAAc,WAAY,QAAS,UAAW,UAAW,SAAU,QACnE,UAAW,UAAW,UAAW,SAAU,WAAY,MAAO,SAC9D,SAAU,UAAW,SAAU,UAAW,QAAS,QAAS,UAC5D,QAAS,SAAU,QAAS,SAAU,SAAU,UAAW,QAAS,QACpE,SAAU,QAAS,UAAW,YAAa,SAAU,SAAU,UAC/D,UAAW,OAAQ,QAAS,MAAO,UAAW,WAAY,SAC1D,SAAU,OAAQ,SAAU,UAAW,SAAU,UAAW,QAAS,OACrE,QAAS,QAAS,SAAU,WAAY,OAAQ,SAAU,QAC1D,WAAY,YAAa,OAAQ,QAAS,SAAU,OAAQ,QAC5D,QAAS,SAAU,OAAQ,MAAO,MAAO,QAAS,WAAY,QAC9D,UAAW,OAAQ,QAAS,UAAW,WAAY,QAAS,UAC5D,UAAW,SAAU,WAAY,OAAQ,SAAU,SAAU,QAC7D,OAAQ,YAAa,QAAS,OAAQ,QAAS,OAAQ,SAAU,SACjE,UAAW,UAAW,QAAS,QAAS,QAAS,QAAS,MAAO,WACjE,SAAU,UAAW,UAAW,UAAW,OAAQ,UAAW,QAC9D,SAAU,UAAW,SAAU,OAAQ,UAAW,QAAS,MAAO,UAClE,QAAS,YAAa,OAAQ,OAAQ,UAAW,UAAW,WAC5D,YAAa,UAAW,WAAY,UAAW,UAAW,SAAU,OACpE,UAAW,UAAW,YAAa,WAAY,UAAW,UAC1D,QAAS,SAAU,SAAU,OAAQ,SAAU,QAAS,SAAU,UAClE,SAAU,UAAW,MAAO,QAAS,QAAS,UAAW,QAAS,QAClE,OAAQ,QAAS,UAAW,OAAQ,SAAU,OAAQ,SAAU,UAChE,QAAS,OAAQ,QAAS,SAAU,OAAQ,QAAS,QAAS,QAC9D,QAAS,UAAW,QAAS,SAAU,UAAW,UAAW,QAC7D,QAAS,OAAQ,QAAS,SAAU,QAAS,QAAS,WACtD,YAAa,MAAO,UAAW,WAAY,SAAU,QAAS,SAC9D,QAAS,SAAU,SAAU,WAAY,QAAS,UAAW,QAC7D,WAAY,UAAW,UAAW,SAAU,QAAS,QAAS,SAC9D,QAAS,OAAQ,UAAW,UAAW,WAAY,SAAU,WAC7D,WAAY,OAAQ,UAAW,SAAU,SAAU,OAAQ,YAC3D,UAAW,SAAU,SAAU,SAAU,SAAU,WAAY,OAC/D,OAAQ,SAAU,UAAW,QAAS,QAAS,SAAU,WACzD,SAAU,SAAU,UAAW,SAAU,UAAW,SAAU,SAC9D,QAAS,SAAU,QAAS,QAAS,SAAU,UAAW,SAC1D,SAAU,OAAQ,SAAU,UAAW,SAAU,WAAY,UAC7D,WAAY,UAAW,SAAU,UAAW,QAAS,MAAO,SAC5D,SAAU,SAAU,UAAW,SAAU,SAAU,QAAS,MAAO,SACnE,SAAU,UAAW,SAAU,OAAQ,QAAS,SAAU,QAC1D,UAAW,QAAS,QAAS,QAAS,QAAS,SAAU,SACzD,UAAW,SAAU,QAAS,OAAQ,WAAY,UAAW,UAC7D,SAAU,WAAY,SAAU,UAAW,YAAa,YACxD,WAAY,UAAW,UAAW,WAAY,QAAS,SAAU,UACjE,QAAS,SAAU,SAAU,QAAS,QAAS,SAAU,UAAW,QACpE,UAAW,OAAQ,QAAS,SAAU,SAAU,QAAS,SAAU,SACnE,YAAa,SAAU,UAAW,MAAO,QAAS,QAAS,SAC3D,QAAS,QAAS,SAAU,QAAS,OAAQ,QAAS,OAAQ,QAC9D,UAAW,UAAW,UAAW,OAAQ,SAAU,SAAU,MAAO,QACpE,UAAW,SAAU,WAAY,UAAW,WAAY,UAAW,QACnE,OAAQ,SAAU,QAAS,OAAQ,WAAY,SAAU,OAAQ,SACjE,OAAQ,WAAY,WAAY,UAAW,UAAW,WAAY,SAClE,SAAU,QAAS,UAAW,MAAO,QAAS,SAAU,QAAS,SACjE,UAAW,UAAW,WAAY,QAAS,UAAW,OAAQ,OAC9D,QAAS,QAAS,OAAQ,MAAO,MAAO,QAAS,SAAU,SAC3D,QAAS,OAAQ,OAAQ,QAAS,WAAY,SAAU,MAAO,QAC/D,SAAU,QAAS,SAAU,SAAU,MAAO,WAAY,WAC1D,QAAS,UAAW,SAAU,UAAW,QAAS,SAAU,UAC5D,SAAU,OAAQ,UAAW,SAAU,WAAY,UAAW,UAC9D,OAAQ,SAAU,SAAU,UAAW,SAAU,QAAS,QAAS,OACnE,QAAS,SAAU,QAAS,SAAU,SAAU,UAAW,UAC3D,QAAS,SAAU,SAAU,SAAU,MAAO,SAAU,QAAS,QACjE,QAAS,SAAU,OAAQ,WAAY,YAAa,YAAa,UACjE,SAAU,MAAO,QAAS,UAAW,SAAU,OAAQ,QAAS,QAChE,QAAS,QAAS,OAAQ,QAAS,SAAU,SAAU,QAAS,SAChE,OAAQ,QAAS,SAAU,UAAW,SAAU,SAAU,SAC1D,WAAY,QAAS,UAAW,UAAW,SAAU,QAAS,SAC9D,WAAY,OAAQ,QAAS,QAAS,QAAS,UAAW,WAC1D,WAAY,SAAU,QAAS,SAAU,WAAY,SAAU,SAC/D,WAAY,WAAY,OAAQ,OAAQ,OAAQ,SAAU,UAC1D,WAAY,SAAU,WAAY,WAAY,OAAQ,QAAS,QAC/D,SAAU,UAAW,SAAU,SAAU,QAAS,OAAQ,SAC1D,SAAU,OAAQ,SAAU,QAAS,UAAW,SAAU,aAC1D,UAAW,QAAS,SAAU,UAAW,SAAU,UAAW,SAC9D,UAAW,UAAW,QAAS,UAAW,UAAW,WAAY,UACjE,UAAW,aAAc,OAAQ,QAAS,UAAW,UAAW,SAChE,SAAU,SAAU,QAAS,QAAS,SAAU,SAAU,SAC1D,WAAY,WAAY,YAAa,SAAU,UAAW,UAC1D,QAAS,QAAS,SAAU,SAAU,UAAW,SAAU,UAC3D,SAAU,UAAW,SAAU,UAAW,WAAY,UAAW,UACjE,SAAU,SAAU,YAAa,UAAW,UAAW,OAAQ,UAC/D,UAAW,SAAU,SAAU,OAAQ,QAAS,YAAa,QAC7D,QAAS,QAAS,OAAQ,QAAS,OAAQ,OAAQ,MAAO,OAAQ,OAClE,QAAS,OAAQ,OAAQ,QAc7BvZ,EAAOD,QAAU,CACb40D,iBARJ,WAII,MAAQ,GAHKpvB,EAAWy2B,cAAcziD,MACvBgsB,EAAW22B,kBAAkB,Q,8BCjbhD,gJAkBA,MAAMhqC,EAASF,oBAAUM,GASV,MAAMmgC,UAA+BkrE,IAQhDzzH,YAAYopB,EAAMG,EAAcqmB,GAC5BxL,QACAr6B,KAAKqf,KAAOA,EACZrf,KAAKwf,aAAeA,EACpBxf,KAAK29C,SAAW,GAChB39C,KAAK00H,aAAe7uF,EAAUoY,IAC9Bj+C,KAAK20H,aAAe9uF,EAAUsY,IAC9Bn+C,KAAKmrE,iBAAmB,CACpBypD,qBAAqB,EACrBC,qBAAqB,GAQ7BjqH,KAAK2J,GACD8lB,MAAMzvB,KAAK2J,GACXvU,KAAKuU,WAAWxG,WAAW/N,KAAK80H,SAAShnI,KAAKkS,MAC1C,oBAAqB,KAAM,MAAO,KAAM,MAOhD80H,SAAS7hH,GACL,MAAM5G,EAAMomC,EAAEx/B,GAAIge,KAAK,UAAU1vB,KAAK,OAChCipB,EAASioB,EAAEx/B,GAAIge,KAAK,UAAU1vB,KAAK,UACnCwzH,EAAU9hH,EAAGrQ,aAAa,QAG1B25E,EAAM5gF,cAAI,CAAEsL,KAAM,SACpB8hC,GAAIgsF,EACJ7tH,GAAI+L,EAAGrQ,aAAa,QAGxBqb,EAAOrZ,IAAK,aAAY4lB,UAAeuqG,IAAW9hH,GAClD,IAAI+hH,EAAOh1H,KAAK29C,SAAStxC,GAEzB,GAAe,qBAAXme,EAA+B,CAC/B,IAAKwqG,EAaD,OAZAz4C,EAAI/gF,MAAM,CAAEyL,KAAM,UAClBs1E,EAAI5vF,EAAE,QAAS,CAAEsa,KAAM,WAClBta,EAAE,iBAAkB,CACjBsZ,MAAO,wCAEVE,KACAxZ,EAAE,kBAAmB,CAClBsZ,MAAO,6BAEfgY,EAAO9Y,KAAK,qBAAsB8N,GAClCjT,KAAKuU,WAAWlH,KAAKkvE,IAEd,EAIX,GAAIw4C,IAAYC,EAAK9pD,UAcjB,OAbAjtD,EAAO9Y,KACH,8BAA+BkH,EAAK2oH,EAAK9pD,UAAWj4D,GACxDspE,EAAI/gF,MAAM,CAAEyL,KAAM,UAClBs1E,EAAI5vF,EAAE,QAAS,CAAEsa,KAAM,WAClBta,EAAE,iBAAkB,CACjBsZ,MAAO,wCAEVE,KACAxZ,EAAE,kBAAmB,CAClBsZ,MAAO,6BAEfjG,KAAKuU,WAAWlH,KAAKkvE,IAEd,OAER,QAAah8E,IAATy0H,EAYP,OATAz4C,EAAI/gF,MAAM,CAAEyL,KAAM,UAClBs1E,EAAI5vF,EAAE,QAAS,CAAEsa,KAAM,WAClBta,EAAE,sBAAuB,CACtBsZ,MAAO,wCAEVE,KACL8X,EAAO9Y,KAAK,uBAAwBkH,EAAK4G,GACzCjT,KAAKuU,WAAWlH,KAAKkvE,IAEd,EAEX,MAAMpoE,EAAMjoB,OAAOsuD,YAAYrmC,MAKzB2xB,EAAgD,UAAxCrqC,UAAQyI,mBAAmB6wH,GAIzC,OAAQvqG,GACR,IAAK,mBAAoB,CACrBvM,EAAOrZ,IAAI,sCAAuCuP,GAClD,MAAM8gH,EAAaxiF,EAAEx/B,GAAIge,KAAK,qBAE9B,GAAIgkG,GAAcA,EAAW1+H,OAAS,EAAG,CACrC,MAAMs8G,EAAaoiB,EAAW1zH,KAAK,SAC7BuxG,EAAamiB,EAAW1zH,KAAK,SAEnCvB,KAAKwf,aAAa0D,KACdy3B,IAAWloD,uBACI,SAAfogH,EACe,SAAfC,GAGR70F,EAAO/Y,KACF,wBAAuB6vH,QACjBjvF,EAAQ,GAAK,eAExB,MAAMD,EAAYC,EAAQ9lC,KAAK20H,aAAe30H,KAAK00H,aAEnDM,EACM,IAAIpqD,IACFn4B,EAAEx/B,GAAIge,KAAK,UAAU1vB,KAAK,OAC1BkxC,EAAEx/B,GAAI1R,KAAK,MACXwzH,EACA/0H,KAAKuU,WACLvU,KAAKmrE,iBAILv+D,KAAKsL,MAAMtL,KAAKwL,UAAUytB,IAC1BC,GACgB,GAExB9lC,KAAK29C,SAASq3E,EAAK3oH,KAAO2oH,EAE1Bh1H,KAAKwf,aAAa0D,KAAKy3B,IAAWhsD,cAC9BqmI,EAAMviF,EAAEx/B,GAAIge,KAAK,WAAY9c,GACjC,MAEJ,IAAK,iBACDnU,KAAKwf,aAAa0D,KACdy3B,IAAWjsD,cAAesmI,EAAMviF,EAAEx/B,GAAIge,KAAK,YAC/C,MAEJ,IAAK,iBACD+jG,EAAK58C,eAAe3lC,EAAEx/B,GAAIge,KAAK,YAC/B,MAEJ,IAAK,iBACDjxB,KAAKwf,aAAa0D,KACdy3B,IAAW9nD,eAAgBmiI,EAAMviF,EAAEx/B,GAAIge,KAAK,YAChD,MAEJ,IAAK,oBAAqB,CACtBhT,EAAOrZ,IAAI,iBAAkBowH,EAAK3oH,KAClC,IAAI+nE,EAAkB,KAClBC,EAAa,KAEb5hC,EAAEx/B,GAAIge,KAAK,kBAAkB16B,SAC7B69E,EACM3hC,EAAEx/B,GAAIge,KAAK,yBAAyB,GAAG9wB,QAC7Ck0E,EAAa5hC,EAAEx/B,GAAIge,KAAK,uBAAuBtvB,QAEnD3B,KAAK6zE,UAAUmhD,EAAK3oH,IAAK+nE,EAAiBC,GAC1Cr0E,KAAKwf,aAAa0D,KAAKy3B,IAAW/rD,WAC9BomI,EAAM5gD,EAAiBC,GAC3B,MAEJ,IAAK,oBACDp2D,EAAO/Y,KAAK,oCAAqCiP,GACjDiL,IAAWoI,cAAciE,YACrB/B,IACA,CACIy0B,IAAKrY,EACLv4C,MAAO4mB,KAGf6gH,EAAKtiD,iBAAiBjgC,EAAEx/B,GAAIge,KAAK,WAAY,KACzC,MAAMikG,EAAchpI,OAAOsuD,YAAYrmC,MAEvC8J,EAAO/Y,KAAK,sCAAuCgwH,GACnD91G,IAAWoI,cAAciE,YACrB9B,IACA,CACIw0B,IAAKrY,EACLv4C,MAAO2nI,MAEhBlwH,IACC4yB,IAAqBC,iBAAiB7yB,GACtCiZ,EAAOjZ,MAAM,2BAA4BA,GACzCgwH,EAAKzhD,wBAET,MACJ,IAAK,YACL,IAAK,aACDyhD,EAAKpgD,gBAAgBniC,EAAEx/B,GAAIge,KAAK,oBAChC,MACJ,IAAK,eACL,IAAK,gBACD+jG,EAAKlgD,mBAAmBriC,EAAEx/B,GAAIge,KAAK,oBACnC,MACJ,QACIhT,EAAO9Y,KAAK,gCAAiCqlB,GAC7C+xD,EAAI/gF,MAAM,CAAEyL,KAAM,UAClBs1E,EAAI5vF,EAAE,QAAS,CAAEsa,KAAM,WAClBta,EAAE,cACC,CAAEsZ,MAAO,wCACZE,KAKT,OAFAnG,KAAKuU,WAAWlH,KAAKkvE,IAEd,EAUXkzB,oBAAoB0lB,EAAI/tE,GACpB,MAAM4tE,EACA,IAAIpqD,IACFt5C,IAAW6rB,gBAAgB,IAC3Bg4E,EACA/tE,EACApnD,KAAKuU,WACLvU,KAAKmrE,iBACLnrE,KAAK20H,cACK,GACM,GAIxB,OAFA30H,KAAK29C,SAASq3E,EAAK3oH,KAAO2oH,EAEnBA,EASXnhD,UAAUxnE,EAAK+nE,EAAiBC,GACxBr0E,KAAK29C,SAASxvD,eAAeke,KACI,UAA7BrM,KAAK29C,SAAStxC,GAAK4gC,OACnBjtC,KAAK29C,SAAStxC,GAAK8nE,aAAaC,EAAiBC,UAE9Cr0E,KAAK29C,SAAStxC,IAO7BuuC,4BAaI56C,KAAKuU,WAAWnG,OACZzS,cAAI,CAAEsL,KAAM,MACR8hC,GAAI/oC,KAAKuU,WAAWzZ,SACnBnO,EAAE,WAAY,CAAEsZ,MAAO,wBAC5Bg4B,IACI,MAAMm3F,EAAa,GAEnB3iF,EAAExU,GAAKhN,KAAK,qBAAqBghB,KAAK,CAACrC,EAAK1vC,KAGxC,MAAMm1H,EAAO,GACPpuH,GAFN/G,EAAKuyC,EAAEvyC,IAESqB,KAAK,QAErB,OAAQ0F,GACR,IAAK,OACDouH,EAAK58E,KAAQ,QAAOv4C,EAAGqB,KAAK,QACxBrB,EAAGqB,KAAK,UACR8zH,EAAK58E,MAAS,IAAGv4C,EAAGqB,KAAK,SAE7B6zH,EAAWlyH,KAAKmyH,GAChB,MACJ,IAAK,OACL,IAAK,QAAS,CACVA,EAAK58E,KAAUxxC,EAAF,IACb,MAAMu5C,EAAWtgD,EAAGqB,KAAK,YAKzB,GAAIi/C,EAAU,CACV,MAAMltC,EACAknB,UAAUsZ,UAAUxgC,MAClB,4BAEJA,GAASgF,SAAShF,EAAM,GAAI,IAAM,GAClC+hH,EAAK58E,MAAW+H,EAAF,IAGd60E,EAAK70E,SAAWA,EAGxB60E,EAAK58E,MAAQv4C,EAAGqB,KAAK,QACRrB,EAAGqB,KAAK,UAGjB8zH,EAAK58E,MAAS,IAAGv4C,EAAGqB,KAAK,SAE7B,MAAMqc,EAAY1d,EAAGqB,KAAK,aAEtBqc,GAA2B,QAAdA,IACby3G,EAAK58E,MAAS,cAAa76B,GAG/By3G,EAAKC,WAAap1H,EAAGqB,KAAK,aACf8zH,EAAKC,WAChBF,EAAWlyH,KAAKmyH,GAChB,UAKR,MAAMluH,EAAUnH,KAAKqf,KAAKlY,QAG1B,IAAK,IAAI7a,EAAI8oI,EAAW7+H,OAAS,EAAGjK,EAAI,EAAGA,IAAK,CAC5C,MAAM4M,EAAIkS,KAAKmM,MAAMnM,KAAKC,UAAY/e,EAAI,IACpCipI,EAAOH,EAAW9oI,GAExB8oI,EAAW9oI,GAAK8oI,EAAWl8H,GAC3Bk8H,EAAWl8H,GAAKq8H,EAGpB,IAAI7kG,EAGAA,EADAvpB,EAAQquH,WACCnnI,GAAKA,EAAEoqD,KAAKtmB,WAAW,QAGvB9jC,GAAKA,EAAEoqD,KAAKtmB,WAAW,SAAY9jC,EAAEoqD,KAAK70C,QAAQ,kBAAoB,EAGnF5D,KAAK00H,aAAax2E,WAAak3E,EAAW1kG,OAAOA,GACjD1wB,KAAK20H,aAAaz2E,WAAak3E,GAChCjlH,IACC8N,EAAO9Y,KAAK,kCAAmCgL,GAC/C8N,EAAO9Y,KAAK,kDASxBo4C,SACI,MAAM5jD,EAAO,GAgBb,OAdA3M,OAAOuY,KAAKvF,KAAK29C,UAAUvuC,QAAQ/C,IAC/B,MACMi5C,EADUtlD,KAAK29C,SAAStxC,GACX2U,eAEfskC,GAAMA,EAAGmwE,YAET97H,EAAM,UAAS0S,GAAS,CACpBopH,UAAWnwE,EAAGmwE,UACdjvG,MAAO8+B,EAAG9+B,MACVua,IAAK70C,OAAO6uB,SAASkiF,SAK1BtjG,M,0EChaf,wDAKe,MAAMyyE,EAIjBn2E,cACI+J,KAAK01H,OAASvzH,IAAM09E,MAAM7/E,KAAK21H,mBAAmB7nI,KAAKkS,MAAO,GAC9DA,KAAK41H,UAAW,EAMpB//E,QACI71C,KAAK01H,OAAOG,OAMhBF,mBAAmB7X,EAAMnvC,GACrBmvC,EAAKnvC,GAoBTzrE,KAAK46G,EAAMtyG,GACHxL,KAAK41H,SACLpqH,GAAYA,EAAS,IAAInU,MAAM,+BAInC2I,KAAK01H,OAAOxyH,KAAK46G,EAAMtyG,GAO3B+tE,WACIv5E,KAAK41H,UAAW,K,iBC3DxB;;;;;;;IASC,WAEG,IAGIhqI,EAAMkqI,EAHN3zH,EAAQ,GAeZ,SAAS4zH,EAAUC,GACf,IAAIC,GAAS,EACb,OAAO,WACH,GAAIA,EAAQ,MAAM,IAAI5+H,MAAM,gCAC5B4+H,GAAS,EACTD,EAAGp0F,MAAMh2C,EAAMuV,YAdX,OADZvV,EAAOoU,QAEL81H,EAAiBlqI,EAAKuW,OAGxBA,EAAM+zH,WAAa,WAEf,OADAtqI,EAAKuW,MAAQ2zH,EACN3zH,GAcX,IAAIg0H,EAAYnpI,OAAOkB,UAAUwI,SAE7B0/H,EAAWhgI,MAAMC,SAAW,SAAUN,GACtC,MAA+B,mBAAxBogI,EAAU1pI,KAAKsJ,IAGtBsgI,EAAQ,SAAUlgI,EAAKH,GACvB,GAAIG,EAAIiZ,QACJ,OAAOjZ,EAAIiZ,QAAQpZ,GAEvB,IAAK,IAAI1J,EAAI,EAAGA,EAAI6J,EAAII,OAAQjK,GAAK,EACjC0J,EAASG,EAAI7J,GAAIA,EAAG6J,IAIxBmgI,EAAO,SAAUngI,EAAKH,GACtB,GAAIG,EAAIqP,IACJ,OAAOrP,EAAIqP,IAAIxP,GAEnB,IAAIwjG,EAAU,GAId,OAHA68B,EAAMlgI,GAAK,SAAUa,EAAG1K,EAAGoL,GACvB8hG,EAAQt2F,KAAKlN,EAASgB,EAAG1K,EAAGoL,OAEzB8hG,GAaP+8B,EAAQ,SAAUxgI,GAClB,GAAI/I,OAAOuY,KACP,OAAOvY,OAAOuY,KAAKxP,GAEvB,IAAIwP,EAAO,GACX,IAAK,IAAI9D,KAAK1L,EACNA,EAAI5H,eAAesT,IACnB8D,EAAKrC,KAAKzB,GAGlB,OAAO8D,QAMY,IAAZyuD,GAA6BA,EAAQssB,UAgB5Cn+E,EAAMm+E,SAAWtsB,EAAQssB,SAErBn+E,EAAMq0H,kBADkB,IAAjBA,EACc,SAAUR,GAE7BQ,EAAaR,IAIM7zH,EAAMm+E,UAvBH,mBAAjBk2C,GACPr0H,EAAMm+E,SAAW,SAAU01C,GAEvBQ,EAAaR,IAEjB7zH,EAAMq0H,aAAer0H,EAAMm+E,WAG3Bn+E,EAAMm+E,SAAW,SAAU01C,GACvBzrH,WAAWyrH,EAAI,IAEnB7zH,EAAMq0H,aAAer0H,EAAMm+E,UAgBnCn+E,EAAM8vC,KAAO,SAAU97C,EAAKH,EAAUwV,GAElC,GADAA,EAAWA,GAAY,cAClBrV,EAAII,OACL,OAAOiV,IAEX,IAAIirH,EAAY,EAIhB,SAAS7nB,EAAKz+F,GACRA,GACA3E,EAAS2E,GACT3E,EAAW,eAGXirH,GAAa,IACItgI,EAAII,QACjBiV,IAXV6qH,EAAMlgI,GAAK,SAAUa,GACjBhB,EAASgB,EAAG++H,EAAUnnB,QAe9BzsG,EAAMiN,QAAUjN,EAAM8vC,KAEtB9vC,EAAMu0H,WAAa,SAAUvgI,EAAKH,EAAUwV,GAExC,GADAA,EAAWA,GAAY,cAClBrV,EAAII,OACL,OAAOiV,IAEX,IAAIirH,EAAY,EACZE,EAAU,WACV3gI,EAASG,EAAIsgI,IAAY,SAAUtmH,GAC3BA,GACA3E,EAAS2E,GACT3E,EAAW,eAGXirH,GAAa,IACItgI,EAAII,OACjBiV,IAGAmrH,QAKhBA,KAEJx0H,EAAMy0H,cAAgBz0H,EAAMu0H,WAE5Bv0H,EAAM00H,UAAY,SAAU1gI,EAAK2gI,EAAO9gI,EAAUwV,GACrCurH,EAAWD,GACjBl1F,MAAM,KAAM,CAACzrC,EAAKH,EAAUwV,KAEnCrJ,EAAM60H,aAAe70H,EAAM00H,UAE3B,IAAIE,EAAa,SAAUD,GAEvB,OAAO,SAAU3gI,EAAKH,EAAUwV,GAE5B,GADAA,EAAWA,GAAY,cAClBrV,EAAII,QAAUugI,GAAS,EACxB,OAAOtrH,IAEX,IAAIirH,EAAY,EACZQ,EAAU,EACVC,EAAU,GAEd,SAAUC,IACN,GAAIV,GAAatgI,EAAII,OACjB,OAAOiV,IAGX,KAAO0rH,EAAUJ,GAASG,EAAU9gI,EAAII,QAEpC2gI,GAAW,EACXlhI,EAASG,GAFT8gI,GAAW,GAEY,IAAI,SAAU9mH,GAC7BA,GACA3E,EAAS2E,GACT3E,EAAW,eAIX0rH,GAAW,GADXT,GAAa,IAEItgI,EAAII,OACjBiV,IAGA2rH,QApBpB,KA8BJC,EAAa,SAAUpB,GACvB,OAAO,WACH,IAAI7zF,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WACtC,OAAO60H,EAAGp0F,MAAM,KAAM,CAACz/B,EAAM8vC,MAAMj4C,OAAOmoC,MAS9Ck1F,EAAW,SAAUrB,GACrB,OAAO,WACH,IAAI7zF,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WACtC,OAAO60H,EAAGp0F,MAAM,KAAM,CAACz/B,EAAMu0H,YAAY18H,OAAOmoC,MAKpDm1F,EAAY,SAAUC,EAAQphI,EAAKH,EAAUwV,GAI7C,GAHArV,EAAMmgI,EAAKngI,GAAK,SAAUa,EAAG1K,GACzB,MAAO,CAACqkC,MAAOrkC,EAAGiB,MAAOyJ,MAExBwU,EAME,CACH,IAAIguF,EAAU,GACd+9B,EAAOphI,GAAK,SAAUa,EAAGwU,GACrBxV,EAASgB,EAAEzJ,OAAO,SAAU4iB,EAAK0gC,GAC7B2oD,EAAQxiG,EAAE25B,OAASkgB,EACnBrlC,EAAS2E,SAEd,SAAUA,GACT3E,EAAS2E,EAAKqpF,WAblB+9B,EAAOphI,GAAK,SAAUa,EAAGwU,GACrBxV,EAASgB,EAAEzJ,OAAO,SAAU4iB,GACxB3E,EAAS2E,UAezBhO,EAAMqD,IAAM4xH,EAAWE,GACvBn1H,EAAMq1H,UAAYH,EAASC,GAC3Bn1H,EAAMs1H,SAAW,SAAUthI,EAAK2gI,EAAO9gI,EAAUwV,GAC7C,OAAOksH,EAAUZ,EAAVY,CAAiBvhI,EAAKH,EAAUwV,IAG3C,IAAIksH,EAAY,SAASZ,GACrB,OA3CkB,SAASA,EAAOd,GAClC,OAAO,WACH,IAAI7zF,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WACtC,OAAO60H,EAAGp0F,MAAM,KAAM,CAACm1F,EAAWD,IAAQ98H,OAAOmoC,KAwC9Cw1F,CAAgBb,EAAOQ,IAKlCn1H,EAAMuD,OAAS,SAAUvP,EAAKyhI,EAAM5hI,EAAUwV,GAC1CrJ,EAAMu0H,WAAWvgI,GAAK,SAAUa,EAAGwU,GAC/BxV,EAAS4hI,EAAM5gI,GAAG,SAAUmZ,EAAK0gC,GAC7B+mF,EAAO/mF,EACPrlC,EAAS2E,SAEd,SAAUA,GACT3E,EAAS2E,EAAKynH,OAItBz1H,EAAM01H,OAAS11H,EAAMuD,OAErBvD,EAAM21H,MAAQ31H,EAAMuD,OAEpBvD,EAAM41H,YAAc,SAAU5hI,EAAKyhI,EAAM5hI,EAAUwV,GAC/C,IAAIwsH,EAAW1B,EAAKngI,GAAK,SAAUa,GAC/B,OAAOA,KACR47B,UACHzwB,EAAMuD,OAAOsyH,EAAUJ,EAAM5hI,EAAUwV,IAG3CrJ,EAAM81H,MAAQ91H,EAAM41H,YAEpB,IAAIG,EAAU,SAAUX,EAAQphI,EAAKH,EAAUwV,GAC3C,IAAIguF,EAAU,GAId+9B,EAHAphI,EAAMmgI,EAAKngI,GAAK,SAAUa,EAAG1K,GACzB,MAAO,CAACqkC,MAAOrkC,EAAGiB,MAAOyJ,OAEjB,SAAUA,EAAGwU,GACrBxV,EAASgB,EAAEzJ,OAAO,SAAUsjD,GACpBA,GACA2oD,EAAQt2F,KAAKlM,GAEjBwU,UAEL,SAAU2E,GACT3E,EAAS8qH,EAAK98B,EAAQl4F,MAAK,SAAU5J,EAAGC,GACpC,OAAOD,EAAEi5B,MAAQh5B,EAAEg5B,UACnB,SAAU35B,GACV,OAAOA,EAAEzJ,cAIrB4U,EAAMuuB,OAAS0mG,EAAWc,GAC1B/1H,EAAMg2H,aAAed,EAASa,GAE9B/1H,EAAMi2H,OAASj2H,EAAMuuB,OACrBvuB,EAAMk2H,aAAel2H,EAAMg2H,aAE3B,IAAIG,EAAU,SAAUf,EAAQphI,EAAKH,EAAUwV,GAC3C,IAAIguF,EAAU,GAId+9B,EAHAphI,EAAMmgI,EAAKngI,GAAK,SAAUa,EAAG1K,GACzB,MAAO,CAACqkC,MAAOrkC,EAAGiB,MAAOyJ,OAEjB,SAAUA,EAAGwU,GACrBxV,EAASgB,EAAEzJ,OAAO,SAAUsjD,GACnBA,GACD2oD,EAAQt2F,KAAKlM,GAEjBwU,UAEL,SAAU2E,GACT3E,EAAS8qH,EAAK98B,EAAQl4F,MAAK,SAAU5J,EAAGC,GACpC,OAAOD,EAAEi5B,MAAQh5B,EAAEg5B,UACnB,SAAU35B,GACV,OAAOA,EAAEzJ,cAIrB4U,EAAM+6B,OAASk6F,EAAWkB,GAC1Bn2H,EAAMo2H,aAAelB,EAASiB,GAE9B,IAAInkF,EAAU,SAAUojF,EAAQphI,EAAKH,EAAUwiI,GAC3CjB,EAAOphI,GAAK,SAAUa,EAAGwU,GACrBxV,EAASgB,GAAG,SAAUyO,GACdA,GACA+yH,EAAcxhI,GACdwhI,EAAgB,cAGhBhtH,UAGT,SAAU2E,GACTqoH,QAGRr2H,EAAMs2H,OAASrB,EAAWjjF,GAC1BhyC,EAAMu2H,aAAerB,EAASljF,GAE9BhyC,EAAMwgF,KAAO,SAAUxsF,EAAKH,EAAUwiI,GAClCr2H,EAAM8vC,KAAK97C,GAAK,SAAUa,EAAGwU,GACzBxV,EAASgB,GAAG,SAAU65C,GACdA,IACA2nF,GAAc,GACdA,EAAgB,cAEpBhtH,UAEL,SAAU2E,GACTqoH,GAAc,OAItBr2H,EAAMw2H,IAAMx2H,EAAMwgF,KAElBxgF,EAAMs3F,MAAQ,SAAUtjG,EAAKH,EAAUwiI,GACnCr2H,EAAM8vC,KAAK97C,GAAK,SAAUa,EAAGwU,GACzBxV,EAASgB,GAAG,SAAU65C,GACbA,IACD2nF,GAAc,GACdA,EAAgB,cAEpBhtH,UAEL,SAAU2E,GACTqoH,GAAc,OAItBr2H,EAAM4lC,IAAM5lC,EAAMs3F,MAElBt3F,EAAMy2H,OAAS,SAAUziI,EAAKH,EAAUwV,GACpCrJ,EAAMqD,IAAIrP,GAAK,SAAUa,EAAGwU,GACxBxV,EAASgB,GAAG,SAAUmZ,EAAK0oH,GACnB1oH,EACA3E,EAAS2E,GAGT3E,EAAS,KAAM,CAACje,MAAOyJ,EAAG6hI,SAAUA,UAG7C,SAAU1oH,EAAKqpF,GACd,GAAIrpF,EACA,OAAO3E,EAAS2E,GAOhB3E,EAAS,KAAM8qH,EAAK98B,EAAQl4F,MAJnB,SAAUw3H,EAAMC,GACrB,IAAIrhI,EAAIohI,EAAKD,SAAUlhI,EAAIohI,EAAMF,SACjC,OAAOnhI,EAAIC,GAAK,EAAID,EAAIC,EAAI,EAAI,MAEE,SAAUX,GAC5C,OAAOA,EAAEzJ,cAMzB4U,EAAM62H,KAAO,SAAUC,EAAOztH,GAC1BA,EAAWA,GAAY,aACvB,IAAIjG,EAAOgxH,EAAM0C,GACbC,EAAiB3zH,EAAKhP,OAC1B,IAAK2iI,EACD,OAAO1tH,IAGX,IAAIguF,EAAU,GAEVtrD,EAAY,GACZtK,EAAc,SAAUoyF,GACxB9nF,EAAUpb,QAAQkjG,IAUlBmD,EAAe,WACfD,IACA7C,EAAMnoF,EAAUhU,MAAM,IAAI,SAAU87F,GAChCA,QAIRpyF,GAAY,WACR,IAAKs1F,EAAgB,CACjB,IAAIE,EAAc5tH,EAElBA,EAAW,aAEX4tH,EAAY,KAAM5/B,OAI1B68B,EAAM9wH,GAAM,SAAU9D,GAClB,IAAIq8G,EAAOsY,EAAS6C,EAAMx3H,IAAMw3H,EAAMx3H,GAAI,CAACw3H,EAAMx3H,IAC7C43H,EAAe,SAAUlpH,GACzB,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAIjD,GAHIghC,EAAK5rC,QAAU,IACf4rC,EAAOA,EAAK,IAEZhyB,EAAK,CACL,IAAImpH,EAAc,GAClBjD,EAAME,EAAM/8B,IAAU,SAAS+/B,GAC3BD,EAAYC,GAAQ//B,EAAQ+/B,MAEhCD,EAAY73H,GAAK0gC,EACjB32B,EAAS2E,EAAKmpH,GAEd9tH,EAAW,kBAGXguF,EAAQ/3F,GAAK0gC,EACbhgC,EAAMq0H,aAAa2C,IAGvBK,EAAW1b,EAAK5jF,MAAM,EAAG9uB,KAAKuP,IAAImjG,EAAKvnH,OAAS,KAAO,GACvDkjI,EAAQ,WACR,OAlaiBzjI,EAkaQ,SAAU0B,EAAGV,GAClC,OAAQU,GAAK8hG,EAAQrrG,eAAe6I,IAnab4gI,GAoaxB,IApaSzhI,EAkaGqjI,GAjaf9zH,OACGvP,EAAIuP,OAAO1P,EAAU4hI,IAEhCvB,EAAMlgI,GAAK,SAAUa,EAAG1K,EAAGoL,GACvBkgI,EAAO5hI,EAAS4hI,EAAM5gI,EAAG1K,EAAGoL,MAEzBkgI,MA6Zcp+B,EAAQrrG,eAAesT,GApalC,IAAUtL,EAAKH,EAAU4hI,GAsa/B,GAAI6B,IACA3b,EAAKA,EAAKvnH,OAAS,GAAG8iI,EAAc7/B,OAEnC,CACD,IAAI93E,EAAW,WACP+3G,OA1DK,SAAUzD,GAC3B,IAAK,IAAI1pI,EAAI,EAAGA,EAAI4hD,EAAU33C,OAAQjK,GAAK,EACvC,GAAI4hD,EAAU5hD,KAAO0pI,EAEjB,YADA9nF,EAAUjqC,OAAO3X,EAAG,GAwDhBw1B,CAAeJ,GACfo8F,EAAKA,EAAKvnH,OAAS,GAAG8iI,EAAc7/B,KAG5C51D,EAAYliB,QAKxBvf,EAAMinH,MAAQ,SAASsQ,EAAO5b,EAAMtyG,GAChC,IACImuH,EAAW,GAEM,mBAAVD,IACPluH,EAAWsyG,EACXA,EAAO4b,EACPA,EANgB,GASpBA,EAAQphH,SAASohH,EAAO,KATJ,EAUpB,IAAIE,EAAc,SAASC,EAAiBC,GAQxC,IAPA,IAAIC,EAAe,SAASjc,EAAMkc,GAC9B,OAAO,SAASC,GACZnc,GAAK,SAAS3tG,EAAK1K,GACfw0H,GAAgB9pH,GAAO6pH,EAAc,CAAC7pH,IAAKA,EAAK1K,OAAQA,MACzDq0H,KAGJJ,GACHC,EAASz2H,KAAK62H,EAAajc,IAAQ4b,GAAO,KAE9Cv3H,EAAM+3H,OAAOP,GAAU,SAAS/qB,EAAMj1G,GAClCA,EAAOA,EAAKA,EAAKpD,OAAS,IACzBsjI,GAAmBruH,GAAU7R,EAAKwW,IAAKxW,EAAK8L,YAIrD,OAAO+F,EAAWouH,IAAgBA,GAGtCz3H,EAAMg4H,UAAY,SAAUlB,EAAOztH,GAE/B,GADAA,EAAWA,GAAY,cAClB4qH,EAAS6C,GAAQ,CACpB,IAAI9oH,EAAM,IAAI9Y,MAAM,6DACpB,OAAOmU,EAAS2E,GAElB,IAAK8oH,EAAM1iI,OACP,OAAOiV,IAEX,IAAI4uH,EAAe,SAAUpkI,GACzB,OAAO,SAAUma,GACb,GAAIA,EACA3E,EAASo2B,MAAM,KAAMzgC,WACrBqK,EAAW,iBAEV,CACD,IAAI22B,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7CslB,EAAOzwB,EAASywB,OAChBA,EACA0b,EAAKj/B,KAAKk3H,EAAa3zG,IAGvB0b,EAAKj/B,KAAKsI,GAEdrJ,EAAMq0H,cAAa,WACfxgI,EAAS4rC,MAAM,KAAMO,SAKrCi4F,EAAaj4H,EAAMnM,SAASijI,GAA5BmB,IAGJ,IAAIC,EAAY,SAAS9C,EAAQ0B,EAAOztH,GAEpC,GADAA,EAAWA,GAAY,aACnB4qH,EAAS6C,GACT1B,EAAO/xH,IAAIyzH,GAAO,SAAUjD,EAAIxqH,GACxBwqH,GACAA,GAAG,SAAU7lH,GACT,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7CghC,EAAK5rC,QAAU,IACf4rC,EAAOA,EAAK,IAEhB32B,EAAS/e,KAAK,KAAM0jB,EAAKgyB,QAGlC32B,OAEF,CACD,IAAIguF,EAAU,GACd+9B,EAAOtlF,KAAKskF,EAAM0C,IAAQ,SAAUx3H,EAAG+J,GACnCytH,EAAMx3H,IAAG,SAAU0O,GACf,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7CghC,EAAK5rC,QAAU,IACf4rC,EAAOA,EAAK,IAEhBq3D,EAAQ/3F,GAAK0gC,EACb32B,EAAS2E,SAEd,SAAUA,GACT3E,EAAS2E,EAAKqpF,QAK1Br3F,EAAMm4H,SAAW,SAAUrB,EAAOztH,GAC9B6uH,EAAU,CAAE70H,IAAKrD,EAAMqD,IAAKysC,KAAM9vC,EAAM8vC,MAAQgnF,EAAOztH,IAG3DrJ,EAAMo4H,cAAgB,SAAStB,EAAOnC,EAAOtrH,GACzC6uH,EAAU,CAAE70H,IAAKkyH,EAAUZ,GAAQ7kF,KAAM8kF,EAAWD,IAAUmC,EAAOztH,IAGzErJ,EAAM+3H,OAAS,SAAUjB,EAAOztH,GAE5B,GADAA,EAAWA,GAAY,aACnB4qH,EAAS6C,GACT92H,EAAMq1H,UAAUyB,GAAO,SAAUjD,EAAIxqH,GAC7BwqH,GACAA,GAAG,SAAU7lH,GACT,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7CghC,EAAK5rC,QAAU,IACf4rC,EAAOA,EAAK,IAEhB32B,EAAS/e,KAAK,KAAM0jB,EAAKgyB,QAGlC32B,OAEF,CACD,IAAIguF,EAAU,GACdr3F,EAAMu0H,WAAWH,EAAM0C,IAAQ,SAAUx3H,EAAG+J,GACxCytH,EAAMx3H,IAAG,SAAU0O,GACf,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7CghC,EAAK5rC,QAAU,IACf4rC,EAAOA,EAAK,IAEhBq3D,EAAQ/3F,GAAK0gC,EACb32B,EAAS2E,SAEd,SAAUA,GACT3E,EAAS2E,EAAKqpF,QAK1Br3F,EAAMnM,SAAW,SAAUijI,GACvB,IAAIuB,EAAe,SAAU7pG,GACzB,IAAIqlG,EAAK,WAIL,OAHIiD,EAAM1iI,QACN0iI,EAAMtoG,GAAOiR,MAAM,KAAMzgC,WAEtB60H,EAAGvvG,QAKd,OAHAuvG,EAAGvvG,KAAO,WACN,OAAQkK,EAAQsoG,EAAM1iI,OAAS,EAAKikI,EAAa7pG,EAAQ,GAAI,MAE1DqlG,GAEX,OAAOwE,EAAa,IAGxBr4H,EAAMy/B,MAAQ,SAAUo0F,GACpB,IAAI7zF,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GACjD,OAAO,WACH,OAAO60H,EAAGp0F,MACN,KAAMO,EAAKnoC,OAAO5D,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,eAKzD,IAAIs5H,EAAU,SAAUlD,EAAQphI,EAAK6/H,EAAIxqH,GACrC,IAAIpe,EAAI,GACRmqI,EAAOphI,GAAK,SAAUa,EAAG0jI,GACrB1E,EAAGh/H,GAAG,SAAUmZ,EAAKlZ,GACjB7J,EAAIA,EAAE4M,OAAO/C,GAAK,IAClByjI,EAAGvqH,SAER,SAAUA,GACT3E,EAAS2E,EAAK/iB,OAGtB+U,EAAMnI,OAASo9H,EAAWqD,GAC1Bt4H,EAAMw4H,aAAetD,EAASoD,GAE9Bt4H,EAAMy4H,OAAS,SAAU1oH,EAAMlc,EAAUwV,GACjC0G,IACAlc,GAAS,SAAUma,GACf,GAAIA,EACA,OAAO3E,EAAS2E,GAEpBhO,EAAMy4H,OAAO1oH,EAAMlc,EAAUwV,MAIjCA,KAIRrJ,EAAM04H,SAAW,SAAU7kI,EAAUkc,EAAM1G,GACvCxV,GAAS,SAAUma,GACf,GAAIA,EACA,OAAO3E,EAAS2E,GAEpB,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC7C+Q,EAAK0vB,MAAM,KAAMO,GACjBhgC,EAAM04H,SAAS7kI,EAAUkc,EAAM1G,GAG/BA,QAKZrJ,EAAM24H,MAAQ,SAAU5oH,EAAMlc,EAAUwV,GAC/B0G,IASD1G,IARAxV,GAAS,SAAUma,GACf,GAAIA,EACA,OAAO3E,EAAS2E,GAEpBhO,EAAM24H,MAAM5oH,EAAMlc,EAAUwV,OAQxCrJ,EAAM44H,QAAU,SAAU/kI,EAAUkc,EAAM1G,GACtCxV,GAAS,SAAUma,GACf,GAAIA,EACA,OAAO3E,EAAS2E,GAEpB,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC5C+Q,EAAK0vB,MAAM,KAAMO,GAIlB32B,IAHArJ,EAAM44H,QAAQ/kI,EAAUkc,EAAM1G,OAQ1CrJ,EAAM09E,MAAQ,SAAUm7C,EAAQC,GAI5B,SAASC,EAAQzjI,EAAGkC,EAAMsuC,EAAKz8B,GAO7B,GANK/T,EAAEw/H,UACLx/H,EAAEw/H,SAAU,GAETb,EAASz8H,KACVA,EAAO,CAACA,IAEM,GAAfA,EAAKpD,OAEL,OAAO4L,EAAMq0H,cAAa,WAClB/+H,EAAE0jI,OACF1jI,EAAE0jI,WAIb9E,EAAM18H,GAAM,SAASmkH,GACjB,IAAIvqF,EAAO,CACP55B,KAAMmkH,EACNtyG,SAA8B,mBAAbA,EAA0BA,EAAW,MAGtDy8B,EACFxwC,EAAEwhI,MAAMnmG,QAAQS,GAEhB97B,EAAEwhI,MAAM/1H,KAAKqwB,GAGX97B,EAAE2jI,WAAa3jI,EAAEwhI,MAAM1iI,SAAWkB,EAAEwjI,aACpCxjI,EAAE2jI,YAENj5H,EAAMq0H,aAAa/+H,EAAEu8D,iBAjCPzzD,IAAhB06H,IACAA,EAAc,GAoClB,IAAII,EAAU,EACV5jI,EAAI,CACJwhI,MAAO,GACPgC,YAAaA,EACbG,UAAW,KACXE,MAAO,KACPH,MAAO,KACPlE,SAAS,EACTltH,QAAQ,EACR7G,KAAM,SAAUvJ,EAAM6R,GACpB0vH,EAAQzjI,EAAGkC,GAAM,EAAO6R,IAE1BqqH,KAAM,WACJp+H,EAAE0jI,MAAQ,KACV1jI,EAAEwhI,MAAQ,IAEZnmG,QAAS,SAAUn5B,EAAM6R,GACvB0vH,EAAQzjI,EAAGkC,GAAM,EAAM6R,IAEzBwoD,QAAS,WACL,IAAKv8D,EAAEsS,QAAUsxH,EAAU5jI,EAAEwjI,aAAexjI,EAAEwhI,MAAM1iI,OAAQ,CACxD,IAAIunH,EAAOrmH,EAAEwhI,MAAMvrG,QACfj2B,EAAE6jI,OAA4B,IAAnB7jI,EAAEwhI,MAAM1iI,QACnBkB,EAAE6jI,QAEND,GAAW,EACX,IAUIX,EAAK3E,GAVE,WACPsF,GAAW,EACPvd,EAAKtyG,UACLsyG,EAAKtyG,SAASo2B,MAAMk8E,EAAM38G,WAE1B1J,EAAE0jI,OAAS1jI,EAAEwhI,MAAM1iI,OAAS8kI,IAAY,GACxC5jI,EAAE0jI,QAEN1jI,EAAEu8D,aAGNgnE,EAAOld,EAAKnkH,KAAM+gI,KAG1BnkI,OAAQ,WACJ,OAAOkB,EAAEwhI,MAAM1iI,QAEnB2gI,QAAS,WACL,OAAOmE,GAEXE,KAAM,WACF,OAAO9jI,EAAEwhI,MAAM1iI,OAAS8kI,IAAY,GAExCtwH,MAAO,YACc,IAAbtT,EAAEsS,SACNtS,EAAEsS,QAAS,EACXtS,EAAEu8D,YAENhpD,OAAQ,YACa,IAAbvT,EAAEsS,SACNtS,EAAEsS,QAAS,EACXtS,EAAEu8D,aAGV,OAAOv8D,GAGX0K,EAAMq5H,cAAgB,SAAUR,EAAQC,GAEpC,SAASQ,EAAc/jI,EAAGC,GACxB,OAAOD,EAAEma,SAAWla,EAAEka,SAiDxB,IAAIpa,EAAI0K,EAAM09E,MAAMm7C,EAAQC,GAU5B,OAPAxjI,EAAEyL,KAAO,SAAUvJ,EAAMkY,EAAUrG,IAnCnC,SAAiB/T,EAAGkC,EAAMkY,EAAUrG,GAOlC,GANK/T,EAAEw/H,UACLx/H,EAAEw/H,SAAU,GAETb,EAASz8H,KACVA,EAAO,CAACA,IAEM,GAAfA,EAAKpD,OAEL,OAAO4L,EAAMq0H,cAAa,WAClB/+H,EAAE0jI,OACF1jI,EAAE0jI,WAIb9E,EAAM18H,GAAM,SAASmkH,GACjB,IAAIvqF,EAAO,CACP55B,KAAMmkH,EACNjsG,SAAUA,EACVrG,SAA8B,mBAAbA,EAA0BA,EAAW,MAG1D/T,EAAEwhI,MAAMh1H,OApCd,SAAuBy3H,EAAUnoG,EAAMooG,GAGrC,IAFA,IAAIC,GAAO,EACP19C,EAAMw9C,EAASnlI,OAAS,EACrBqlI,EAAM19C,GAAK,CAChB,IAAIxuC,EAAMksF,GAAQ19C,EAAM09C,EAAM,IAAO,GACjCD,EAAQpoG,EAAMmoG,EAAShsF,KAAS,EAClCksF,EAAMlsF,EAENwuC,EAAMxuC,EAAM,EAGhB,OAAOksF,EAyBYC,CAAcpkI,EAAEwhI,MAAO1lG,EAAMkoG,GAAiB,EAAG,EAAGloG,GAE/D97B,EAAE2jI,WAAa3jI,EAAEwhI,MAAM1iI,SAAWkB,EAAEwjI,aACpCxjI,EAAE2jI,YAENj5H,EAAMq0H,aAAa/+H,EAAEu8D,YASzBknE,CAAQzjI,EAAGkC,EAAMkY,EAAUrG,WAItB/T,EAAEq7B,QAEFr7B,GAGX0K,EAAM25H,MAAQ,SAAUd,EAAQvoG,GAC5B,IAAIspG,GAAc,EACd9C,EAAc,GAEd6C,EAAQ,CACR7C,MAAOA,EACPxmG,QAASA,EACT2oG,UAAW,KACXE,MAAO,KACPH,MAAO,KACPa,SAAS,EACT94H,KAAM,SAAUvJ,EAAM6R,GACb4qH,EAASz8H,KACVA,EAAO,CAACA,IAEZ08H,EAAM18H,GAAM,SAASmkH,GACjBmb,EAAM/1H,KAAK,CACPvJ,KAAMmkH,EACNtyG,SAA8B,mBAAbA,EAA0BA,EAAW,OAE1DswH,EAAME,SAAU,EACZF,EAAMV,WAAanC,EAAM1iI,SAAWk8B,GACpCqpG,EAAMV,eAGdj5H,EAAMq0H,aAAasF,EAAM9nE,UAE7BA,QAAS,SAASA,IACd,IAAI+nE,EAAJ,CACA,GAAqB,IAAjB9C,EAAM1iI,OAGN,OAFGulI,EAAMX,QAAUW,EAAME,SAASF,EAAMX,aACxCW,EAAME,SAAU,GAIpB,IAAIvpB,EAAwB,iBAAZhgF,EACFwmG,EAAMh1H,OAAO,EAAGwuB,GAChBwmG,EAAMh1H,OAAO,EAAGg1H,EAAM1iI,QAEhC4lC,EAAKm6F,EAAK7jB,GAAI,SAAUqL,GACxB,OAAOA,EAAKnkH,QAGbmiI,EAAMR,OAAOQ,EAAMR,QACtBS,GAAU,EACVf,EAAO7+F,GAAI,WACP4/F,GAAU,EAEV,IAAI55F,EAAOhhC,UACXk1H,EAAM5jB,GAAI,SAAU94G,GACZA,EAAK6R,UACL7R,EAAK6R,SAASo2B,MAAM,KAAMO,MAIlC6xB,SAGRz9D,OAAQ,WACJ,OAAO0iI,EAAM1iI,QAEjB2gI,QAAS,WACL,OAAO6E,IAGf,OAAOD,GAGX,IAAIG,EAAc,SAAUpvI,GACxB,OAAO,SAAUmpI,GACb,IAAI7zF,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GACjD60H,EAAGp0F,MAAM,KAAMO,EAAKnoC,OAAO,CAAC,SAAUmW,GAClC,IAAIgyB,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GAC1B,oBAAZ4D,UACHoL,EACIpL,QAAQC,OACRD,QAAQC,MAAMmL,GAGbpL,QAAQlY,IACbwpI,EAAMl0F,GAAM,SAAUnrC,GAClB+N,QAAQlY,GAAMmK,aAOtCmL,EAAMyC,IAAMq3H,EAAY,OACxB95H,EAAM0+E,IAAMo7C,EAAY,OAKxB95H,EAAM+5H,QAAU,SAAUlG,EAAImG,GAC1B,IAAIvE,EAAO,GACPwE,EAAS,GACbD,EAASA,GAAU,SAAUnlI,GACzB,OAAOA,GAEX,IAAIqlI,EAAW,WACX,IAAIl6F,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WAClCqK,EAAW22B,EAAKzxB,MAChB7iB,EAAMsuI,EAAOv6F,MAAM,KAAMO,GACzBt0C,KAAO+pI,EACPz1H,EAAMm+E,UAAS,WACX90E,EAASo2B,MAAM,KAAMg2F,EAAK/pI,OAGzBA,KAAOuuI,EACZA,EAAOvuI,GAAKqV,KAAKsI,IAGjB4wH,EAAOvuI,GAAO,CAAC2d,GACfwqH,EAAGp0F,MAAM,KAAMO,EAAKnoC,OAAO,CAAC,WACxB49H,EAAK/pI,GAAOsT,UACZ,IAAI1J,EAAI2kI,EAAOvuI,UACRuuI,EAAOvuI,GACd,IAAK,IAAIvB,EAAI,EAAGC,EAAIkL,EAAElB,OAAQjK,EAAIC,EAAGD,IACnCmL,EAAEnL,GAAGs1C,MAAM,KAAMzgC,iBAO/B,OAFAk7H,EAASzE,KAAOA,EAChByE,EAASC,WAAatG,EACfqG,GAGXl6H,EAAMo6H,UAAY,SAAUvG,GAC1B,OAAO,WACL,OAAQA,EAAGsG,YAActG,GAAIp0F,MAAM,KAAMzgC,aAI7CgB,EAAMu3H,MAAQ,SAAU9sF,EAAO52C,EAAUwV,GAErC,IADA,IAAIgxH,EAAU,GACLlwI,EAAI,EAAGA,EAAIsgD,EAAOtgD,IACvBkwI,EAAQt5H,KAAK5W,GAEjB,OAAO6V,EAAMqD,IAAIg3H,EAASxmI,EAAUwV,IAGxCrJ,EAAMs6H,YAAc,SAAU7vF,EAAO52C,EAAUwV,GAE3C,IADA,IAAIgxH,EAAU,GACLlwI,EAAI,EAAGA,EAAIsgD,EAAOtgD,IACvBkwI,EAAQt5H,KAAK5W,GAEjB,OAAO6V,EAAMq1H,UAAUgF,EAASxmI,EAAUwV,IAG9CrJ,EAAMu6H,IAAM,WACR,IAAIC,EAAMx7H,UACV,OAAO,WACH,IAAIirC,EAAOpsC,KACPmiC,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WAClCqK,EAAW22B,EAAKzxB,MACpBvO,EAAMuD,OAAOi3H,EAAKx6F,GAAM,SAAUy6F,EAAS5G,EAAI0E,GAC3C1E,EAAGp0F,MAAMwK,EAAMwwF,EAAQ5iI,OAAO,CAAC,WAC3B,IAAImW,EAAMhP,UAAU,GAChB07H,EAAWzmI,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GACrDu5H,EAAGvqH,EAAK0sH,UAGhB,SAAU1sH,EAAKqpF,GACXhuF,EAASo2B,MAAMwK,EAAM,CAACj8B,GAAKnW,OAAOw/F,SAK9Cr3F,EAAM26H,QAAU,WACd,OAAO36H,EAAMu6H,IAAI96F,MAAM,KAAMxrC,MAAMlI,UAAU0kC,QAAQnmC,KAAK0U,aAG5D,IAAI47H,EAAa,SAAUxF,EAAQoF,GAC/B,IAAIK,EAAK,WACL,IAAI5wF,EAAOpsC,KACPmiC,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,WAClCqK,EAAW22B,EAAKzxB,MACpB,OAAO6mH,EAAOoF,GAAK,SAAU3G,EAAI0E,GAC7B1E,EAAGp0F,MAAMwK,EAAMjK,EAAKnoC,OAAO,CAAC0gI,OAEhClvH,IAEJ,GAAIrK,UAAU5K,OAAS,EAAG,CACtB,IAAI4rC,EAAO/rC,MAAMlI,UAAUgsC,MAAMztC,KAAK0U,UAAW,GACjD,OAAO67H,EAAGp7F,MAAM5hC,KAAMmiC,GAGtB,OAAO66F,GAGf76H,EAAM86H,UAAY7F,EAAW2F,GAC7B56H,EAAM+6H,gBAAkB7F,EAAS0F,GAEjC56H,EAAMg7H,QAAU,SAAUnH,EAAIxqH,IAC1B,SAASib,EAAKtW,GACV,GAAIA,EAAK,CACL,GAAI3E,EACA,OAAOA,EAAS2E,GAEpB,MAAMA,EAEV6lH,EAAGvvG,GAEPA,IAIiC16B,EAAOD,QACxCC,EAAOD,QAAUqW,OAIX,kBACF,OAAOA,GADL,QAAC,OAAD,aAhlCb,K,oDCcDpW,EAAOD,QAAU,CAAE6tF,YAhBnB,SAAqBt9D,GACjB,IAAKA,EACD,OAAO,EAGX,IAAI+gH,EAAgB9wI,EAAVuM,EAAO,EAEjB,IAAKvM,EAAI,EAAGA,EAAI+vB,EAAO9lB,OAAQjK,IAC3B8wI,EAAO/gH,EAAO9kB,WAAWjL,GACzBuM,GAAQukI,EAAOhyH,KAAKsP,IAAI,GAAI2B,EAAO9lB,OAAS,EAAIjK,GAChDuM,EAAOuS,KAAKuP,IAAW,EAAP9hB,GAGpB,OAAOA,K,8BCpBX,yEAOA,MAAMolB,EAASF,oBAAUM,GAOV,MAAMwsD,UAAsBzwC,IAiBvCnkC,YACQoW,EACA4+D,EACAC,EACA32D,EACA42D,EACAtlC,EACAulC,GACJ/wC,QACAr6B,KAAKqM,IAAMA,EACXrM,KAAKirE,SAAWA,EAChBjrE,KAAKkrE,UAAYA,EACjBlrE,KAAKuU,WAAaA,EAClBvU,KAAKmrE,iBAAmBA,EACxBnrE,KAAK6lC,UAAYA,EAOjB7lC,KAAKorE,YAAcA,EAMnBprE,KAAK4vE,SAAU,EAKf5vE,KAAK6vE,cAAgB,GAMrB7vE,KAAKm/C,KAAO,KAMZn/C,KAAKitC,MAAQ,KAMbjtC,KAAKi6D,IAAM,KAOf,mBACI,OAAOj6D,KAAKorE,YAAcprE,KAAKirE,SAAWjrE,KAAKkrE,UAOnD,mBACI,OAAOlrE,KAAKorE,YAAcprE,KAAKkrE,UAAYlrE,KAAKirE,SAapDvkB,WAAWvH,EAAM8a,EAAK9yD,GAClB,GAAmB,OAAfnH,KAAKitC,MAAgB,CACrB,MAAMtV,EACC,kCAAiC33B,KAAKqM,oCAC3BrM,KAAKitC,QAGvB,MADAhvB,EAAOjZ,MAAM2yB,GACP,IAAItgC,MAAMsgC,GAEpB33B,KAAKm/C,KAAOA,EACZn/C,KAAKi6D,IAAMA,EACXj6D,KAAKitC,MAAQ0/B,IACb3sE,KAAK4sE,aAAazlE,GAStBylE,aAAazlE,IAWbspE,iBAAiB/9B,IAQjB2qF,WACI,OAAOr9H,KAAKitC,MAUhBqwF,WAAW5qF,IAOX6qF,cAAc7qF,IAiBdmhC,UAAUnsB,EAAS8pB,EAASrqE,IAW5BmqE,YAAYh/B,EAAQoV,EAAS8pB,IAK7BkI,mBACI,OAAO15E,KAAKorE,YAAcprE,KAAKirE,SAAWjrE,KAAKkrE,c,0EC1MvD,iFAQA,MAAMjtD,EAASF,oBAAUM,GAMV,MAAM6tD,UAA2BsxD,IAI5CvnI,cACIokC,QAUAr6B,KAAKy9H,WAAa,IAAIl+G,IAMtBvf,KAAK8xG,SAAW,KAOpB7iC,YAAY9vB,GACR,MAAMu+E,EAAc19H,KAAK8xG,SAEzB9xG,KAAK8xG,SAAW3yD,EACZu+E,IACAA,EAAY14B,uBACR,aAAchlG,KAAK29H,mBACvBD,EAAY14B,uBACR,aAAchlG,KAAK49H,mBACvBF,EAAY14B,uBACR,YAAahlG,KAAK69H,oBAEtB1+E,IAEAn/C,KAAK29H,kBAAoB,CAACz8H,EAAMvK,KAC5BqJ,KAAKwf,aAAa0D,KACd46G,IACAnnI,EAAM4nC,IAAgC,SAAfr9B,EAAK3T,QAEpC4xD,EAAK2lD,oBAAoB,aAAc9kG,KAAK29H,mBAE5C39H,KAAK49H,kBAAoB,CAAC18H,EAAMvK,KAC5BqJ,KAAKwf,aAAa0D,KACd46G,IACAnnI,EAAM4nC,IAAgC,SAAfr9B,EAAK3T,QAEpC4xD,EAAK2lD,oBAAoB,aAAc9kG,KAAK49H,mBAE5C59H,KAAK69H,kBAAoB,CAAC38H,EAAMvK,KAC5BqJ,KAAKwf,aAAa0D,KACd46G,IACAnnI,EAAMuK,EAAK3T,QAEnB4xD,EAAK2lD,oBAAoB,YAAa9kG,KAAK69H,oBAOnDE,iBAAiB51F,EAAOxc,GACpB,GAAI3rB,KAAK8xG,SACL,OAAO9xG,KAAK8xG,SAASwe,qBAAqBnoF,EAAOxc,GAErD1N,EAAOjZ,MAAM,kDAMjBg5H,aAAah5G,GACT,OAAOhlB,KAAKy9H,WAAWtwI,IAAI63B,GAS/BksD,aAAalsD,EAAMg3C,GACf,GAAoB,iBAATh3C,EACP,MAAM,IAAInuB,UAAW,QAAOmuB,uBAEhChlB,KAAKy9H,WAAWr8G,IAAI4D,EAAMg3C,O,8ECzGlC,8CAgBe,MAAMwhE,UAAuBpjG,IAOxC4jG,aAAah5G,GACT,MAAM,IAAI3tB,MAAM,mBAcpB0mI,iBAAiB51F,EAAOxc,GACpB,MAAM,IAAIt0B,MAAM,sB,6BCxCxB,mBAOA,MAAM4mI,UAAsBvU,IAIxBzzH,cACIokC,QACAr6B,KAAK4E,IAAM,GAOfgG,KAAK2J,GACD8lB,MAAMzvB,KAAK2J,GACXvU,KAAKuU,WAAWrH,SAAWlN,KAAKk+H,YAAYpwI,KAAKkS,MACjDA,KAAKuU,WAAWpH,UAAYnN,KAAKm+H,YAAYrwI,KAAKkS,MAOtDk+H,YAAYlwH,GACRhO,KAAK4E,IAAI1B,KAAK,EAAE,IAAIiF,MAAOC,UAAW,WAAY4F,IAOtDmwH,YAAYnwH,GACRhO,KAAK4E,IAAI1B,KAAK,EAAE,IAAIiF,MAAOC,UAAW,WAAY4F,KAO3C,eACXvS,UAAQsK,oBAAoB,SAAU,IAAIk4H,K,8BC/C9C,wEAOA,MAAMhgH,EAASF,oBAAUM,GAOV,MAAMogC,UAA6BirE,IAK9C9+G,KAAK2J,GACD8lB,MAAMzvB,KAAK2J,GAEXvU,KAAKuU,WAAWxG,WACZ/N,KAAKo+H,OAAOtwI,KAAKkS,MAdV,kBAc6B,KAAM,MAAO,KAAM,MAO/Do+H,OAAOnrH,GACHgL,EAAO/Y,KAAK,UAAW+N,GAa3BwqC,KAAK1U,EAAIpyC,EAAMkmD,EAAUwhF,EAAU9lC,GAC/B,OAAO,IAAIv7D,QAAQ,CAACC,EAASC,KACzB,IAAKq7D,EAGD,YAFAr7D,EAAO,IAAI7lC,MAAM,oBAIrB,MAAMgZ,EAAM1U,cAAI,CACZsL,KAAM,MACN8hC,GAAIwvD,IAGRloF,EAAI1jB,EAAE,OAAQ,CACVsZ,MAhDG,kBAiDH8iC,KACApyC,SAEJ0Z,EAAI1jB,EAAE,SAAU,CACZE,KAAM,cACNU,MAAOsvD,IACR12C,KAECk4H,GAAYA,EAAS9nI,QACrB8Z,EAAI1jB,EAAE,SAAU,CACZE,KAAM,kBACNU,MAAO8wI,IACRl4H,KAGPnG,KAAKuU,WAAWnG,OACZiC,EACA5K,IACIwY,EAAO/Y,KAAK,eAAgBO,GAG5B,MAAMoO,EAAW4+B,EAAEhtC,GAAQwrB,KAAK,OAAO1vB,KAAK,OAE5CvB,KAAKs+H,aAAezqH,EAASuB,OAAO,QAAQ7e,QAC5C0nB,EAAO/Y,KAAM,2BAA0BlF,KAAKs+H,cAC5CrhG,KAEJj4B,IACIiZ,EAAO/Y,KAAK,cAAeF,GAC3Bk4B,EAAOl4B,OAUvBknG,SACI,OAAO,IAAIlvE,QAAQ,CAACC,EAASC,KACzB,IAAKl9B,KAAKs+H,aAIN,OAHAphG,EAAO,IAAI7lC,MAAM,6BACjB4mB,EAAO9Y,KAAK,uBAKhB,MAAMkL,EAAM1U,cAAI,CACZsL,KAAM,MACN8hC,GAAI/oC,KAAKs+H,eAGbjuH,EAAI1jB,EAAE,SAAU,CACZsZ,MAvGG,oBA0GPjG,KAAKuU,WAAWnG,OAAOiC,EAAK5K,IACxBwY,EAAO/Y,KAAK,iBAAkBO,GAC9BzF,KAAKs+H,aAAe,KACpBrhG,KACDj4B,IACCiZ,EAAO/Y,KAAK,gBAAiBF,GAC7BhF,KAAKs+H,aAAe,KACpBphG,EAAO,IAAI7lC,MAAM,0B,yEC1HjC,+CASA,MAAM4mB,EAASF,oBAAUM,GAkBzB,IAAIg+B,GAAmB,EAQvB,MAAMkiF,EAA8B,6BAO9BC,EACA,wDAKS,eAEX/iI,UAAQmJ,IAAM,SAASC,EAAOC,GAe1B,OATAmZ,EAAOwgH,MAAM,UAAW55H,EAAOC,GACZ,iBAARA,IAC6B,IAA7BA,EAAIlB,QAAQ,cAC0C,IAAtDkB,EAAIlB,QAAQ,uCAEnBiB,EAAQpJ,UAAQmD,SAASG,MAIrB8F,GACR,KAAKpJ,UAAQmD,SAASC,OAGO,IAArBw9C,GACOkiF,EAA4BrsH,KAAKpN,KACxCmZ,EAAOhZ,MAAM,yBACbo3C,GAAmB,GAEvB,MACJ,KAAK5gD,UAAQmD,SAASG,KAClBkf,EAAO9Y,KAAM,YAAWL,GACxB,MAAM45H,EAAmBF,EAAuB/zE,KAAK3lD,GAEjD45H,GAAgD,IAA5BA,EAAiBnoI,SACrC8lD,EAAkB/jC,SAASomH,EAAiB,GAAI,IAChDzgH,EAAOhZ,MAAO,2BAA0Bo3C,IAE5C,MACJ,KAAK5gD,UAAQmD,SAASlB,MACtB,KAAKjC,UAAQmD,SAASI,MAElB8F,EAAO,YAAWA,EAClB8yB,IAAqBC,iBAAiB,IAAIxgC,MAAMyN,IAChDmZ,EAAOjZ,MAAMF,KAarBrJ,UAAQ6gD,mBAAqB,WACzB,OAAOD,GAGX5gD,UAAQi/C,gBAAkB,SAAS3qC,GAC/B,OAAQA,GACR,KAAKtU,UAAQgC,OAAOY,aAChB,MAAO,eACX,KAAK5C,UAAQgC,OAAOC,MAChB,MAAO,QACX,KAAKjC,UAAQgC,OAAOE,WAChB,MAAO,aACX,KAAKlC,UAAQgC,OAAOG,SAChB,MAAO,WACX,KAAKnC,UAAQgC,OAAOI,eAChB,MAAO,iBACX,KAAKpC,UAAQgC,OAAOK,SAChB,MAAO,WACX,KAAKrC,UAAQgC,OAAOM,UAChB,MAAO,YACX,KAAKtC,UAAQgC,OAAOO,aAChB,MAAO,eACX,KAAKvC,UAAQgC,OAAOQ,cAChB,MAAO,gBACX,KAAKxC,UAAQgC,OAAOS,SAChB,MAAO,WACX,QACI,MAAO,e,yECjInB,wGAOA,MAAM+f,EAASF,oBAAUM,GAMV,MAAMimB,EAYjBruC,YAAY+qB,EAAgBqjB,EAAOsH,EAASgzF,GACxC,IAAK39G,IAAmBqjB,EACpB,MAAM,IAAIxtC,UACN,kDACD,GAAImqB,GAAkBqjB,EACzB,MAAM,IAAIxtC,UACN,qDA8BR,GA3BImqB,EACA/C,EAAOhZ,MAAM,qCAEbgZ,EAAOhZ,MAAO,6BAA4Bo/B,MAK9CrkC,KAAKijC,SAAW,KAGhBjjC,KAAK64F,cAAgBltD,EAIrB3rC,KAAKm3F,MAAQ,KAGbn3F,KAAK4+H,oBAAqB,EAG1B5+H,KAAK6+H,mBAAoB,EAEzB7+H,KAAKukC,+BAAiCo6F,EAIlC39G,EAAgB,CAChB,MAAM89G,EACA99G,EAAe+9G,kBACb,mBAAoB,CAChBr2H,SAAU,uCAItB1I,KAAKg/H,eAAeF,GACpB9+H,KAAKm3F,MAAQ,mBAGN9yD,IACPrkC,KAAK4+H,oBAAqB,EAC1B5+H,KAAKi/H,OAAS56F,EACdrkC,KAAKk/H,kBASbA,iBAEI,MAAMC,EAAK,IAAI5jH,UAAUvb,KAAKi/H,QAG9Bj/H,KAAKg/H,eAAeG,GACpBn/H,KAAKm3F,MAAQ,YAQjBioC,0BACI,IAAIC,EAAW,EAEf,MAAMC,EAAS,KACPt/H,KAAKslC,WAGTtlC,KAAKk/H,eAAel/H,KAAKi/H,QACzBI,EAAWj0H,KAAKqP,IAAe,EAAX4kH,EAAc,IAClCr/H,KAAKu/H,cAAgBh1H,WAAW+0H,EAAmB,IAAXD,KAG5Cr/H,KAAKu/H,cAAgBh1H,WAAW+0H,EAAmB,IAAXD,GAQ5CG,yBACQx/H,KAAKu/H,gBACL9xH,aAAazN,KAAKu/H,eAClBv/H,KAAKu/H,mBAAgBh/H,GAU7Bk/H,0BAA0BC,GACtB,IAAK1/H,KAAK4+H,mBACN,OAEJ,MAAM,KAAEjiH,EAAF,OAAQnN,GAAWkwH,EAEzBtgH,IAAWoI,cAAc4E,YAA+BzP,EAAMnN,IAC9DxP,KAAK4+H,oBAAqB,EAC1B5+H,KAAK64F,cAAcntD,KAAK1S,IAAUpR,kBAAmB,KACjD5nB,KAAKw/H,yBACLx/H,KAAK4+H,oBAAqB,IAE9B5+H,KAAKo/H,0BAOT,WACI,OAAOp/H,KAAKm3F,MAMhB16E,QAII,GAHAzc,KAAK6+H,mBAAoB,EACzB7+H,KAAKw/H,yBACLx/H,KAAK4+H,oBAAqB,EACtB5+H,KAAKijC,SAAU,CACf,IACIjjC,KAAKijC,SAASxmB,QAChB,MAAOzX,IAEThF,KAAKijC,SAAW,MASxBqC,SACI,OAAOtlC,KAAKijC,WAA0C,SAA7BjjC,KAAKijC,SAASjqB,YAChChZ,KAAKijC,SAASjqB,aAAeuC,UAAU42C,MAYlDnpB,YAAYD,EAAItW,GACZzyB,KAAKuN,MAAM,CACPoyH,aAAc,kBACdC,WAAYntG,EACZsW,OAQRnE,oBAAoBr3C,GAChB,MAAMsyI,EAAa,CACfF,aAAc,oBACd73B,MAAOv6G,GAGXyS,KAAKuN,MAAMsyH,GACX5hH,EAAOrZ,IAAK,yBAAwBrX,GAUxCi3C,0BAA0Bw3B,GACtB/9C,EAAOrZ,IACH,kEACAo3D,GAEJh8D,KAAKuN,MAAM,CACPoyH,aAAc,6BACdG,eAAgB9jE,GAAc,OAYtCv3B,6BAA6Bs7F,GACzB9hH,EAAOrZ,IACH,oEACAm7H,GAEJ//H,KAAKuN,MAAM,CACPoyH,aAAc,gCACdK,kBAAmBD,IAS3Br7F,mCAAmCu7F,GAC/BhiH,EAAOrZ,IACA,sEAAsBq7H,YAC7BjgI,KAAKuN,MAAM,CACPoyH,aAAc,0BACdt6F,eAAgB46F,IAOxBjB,eAAekB,GACX,MAAMv0F,EAAU3rC,KAAK64F,cAErBqnC,EAAQ1kH,OAAS,KACbyC,EAAO/Y,KAAQlF,KAAKm3F,MAAP,mBAQbxrD,EAAQzoB,KAAK8V,IAAUpR,oBAG3Bs4G,EAAQxkH,QAAU2L,IAGK,cAAfrnB,KAAKm3F,OACLl5E,EAAOjZ,MAAO,kBAAiBqiB,EAAM5iB,UAI7Cy7H,EAAQpkH,UAAY,EAAGniB,WAEnB,IAAI5D,EAEJ,IACIA,EAAM6W,KAAKsL,MAAMve,GACnB,MAAOqL,GAML,OALA4yB,IAAqBC,iBAAiB7yB,QACtCiZ,EAAOjZ,MACH,4CACArL,EAAMqL,GAKd,MAAM26H,EAAe5pI,EAAI4pI,aAEzB,OAAQA,GACR,IAAK,qCAAsC,CAEvC,MAAMQ,EAA0BpqI,EAAIoqI,wBAEpCliH,EAAO/Y,KACH,uCACAi7H,GACJx0F,EAAQzoB,KACJ8V,IAAUtlC,yBACVysI,GACJ,MAEJ,IAAK,wCAAyC,CAC1C,MAAMC,EAAWrqI,EAAIqqI,SACfnkE,EAA0B,SAAflmE,EAAIuwF,OAErBroE,EAAO/Y,KACF,uCAAsCk7H,cACnCnkE,KACRtwB,EAAQzoB,KAAK8V,IAAUnR,6BACnBu4G,EAAUnkE,GAEd,MAEJ,IAAK,kBACDtwB,EAAQzoB,KACJ8V,IAAUnlC,0BAA2BkC,EAAIY,KACzCZ,EAAI6pI,YAER,MAEJ,IAAK,4BAA6B,CAE9B,MAAM96F,EAAiB/uC,EAAI+uC,eAE3B7mB,EAAO/Y,KAAK,6BACR4/B,EAAgB/uC,GACpB41C,EAAQzoB,KAAK8V,IAAUlR,uBACnBgd,EAAgB/uC,GAEpB,MAEJ,IAAK,yBAA0B,CAC3B,MAAMsqI,EAAmBtqI,EAAIsqI,iBAEzBA,IACApiH,EAAO/Y,KAAM,2BAA0B0H,KAAKwL,UAAUioH,IACtDrgI,KAAKukC,+BAA+B87F,IAExC,MAEJ,QACIpiH,EAAOhZ,MAAM,mCAAoClP,GAMjD41C,EAAQzoB,KAAM,mBAAkBy8G,EAAgB5pI,KAKxDmqI,EAAQtkH,QAAUyL,IACdpJ,EAAO/Y,KAAM,sBAAoBlF,KAAK6+H,kBAAoB,SAAW,WAElD,cAAf7+H,KAAKm3F,QACAn3F,KAAK6+H,oBACN5gH,EAAOjZ,MAAO,mBAAkBqiB,EAAM1K,QAAQ0K,EAAM7X,UACpDxP,KAAKy/H,0BAA0Bp4G,KAKvCrnB,KAAKijC,SAAW,MAIpBjjC,KAAKijC,SAAWi9F,EAUpB3yH,MAAMsyH,GACF,MAAMK,EAAUlgI,KAAKijC,SAErB,IAAKjjC,KAAKslC,SAEN,MADArnB,EAAOjZ,MAAM,2CACP,IAAI3N,MAAM,qBAGpB6oI,EAAQ7yH,KAAKT,KAAKwL,UAAUynH,Q,wECzYpC,IAAIhhE,EAAWzhD,EAAQ,IAYvB,SAASsoD,EAAkBC,EAAalsB,EAAMxyC,EAAMsa,EAAQ++G,GAC1D,IAAItuG,EAAM6sC,EAASwE,oBAAoBsC,EAAYvsC,KAAMqgB,GAuBzD,GApBAznB,GAAO6sC,EAAS+D,mBACZ+C,EAAYC,YAAYC,sBAG5B7zC,GAAO6sC,EAAS8C,oBACZgE,EAAYG,cAAcD,qBACjB,UAAT5+D,EAAmB,UAAYq5H,GAAY,UAE/CtuG,GAAO,SAAW2zC,EAAYj2B,IAAM,OAEhCi2B,EAAYI,WAAaJ,EAAYK,YACvCh0C,GAAO,iBACE2zC,EAAYI,UACrB/zC,GAAO,iBACE2zC,EAAYK,YACrBh0C,GAAO,iBAEPA,GAAO,iBAGL2zC,EAAYI,UAAW,CACzB,IAAI2gB,EAAU/gB,EAAYI,UAAUw6D,iBAChC56D,EAAYI,UAAUznC,MAAMp3B,GAChCy+D,EAAYI,UAAUw6D,gBAAkB75C,EAExC,IAAI5kC,EAAO,SAAWvgC,EAASA,EAAOra,GAAK,KAAO,IAC9Cw/E,EAAU,OACd10D,GAAO,KAAO8vB,EAEd9vB,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjhD,KACrD,IAAM88B,EAGN6jB,EAAYM,uBAAuB,GAAGjC,MACxChyC,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KACzD,IAAM88B,EACV9vB,GAAO,oBACH2zC,EAAYM,uBAAuB,GAAGjhD,KAAO,IAC7C2gD,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KAC1C,QAUR,OANAgN,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjhD,KACrD,UAAY65C,EAASC,WAAa,OAClC6G,EAAYI,WAAaJ,EAAYM,uBAAuB,GAAGjC,MACjEhyC,GAAO,UAAY2zC,EAAYM,uBAAuB,GAAGjC,IAAIh/C,KACzD,UAAY65C,EAASC,WAAa,QAEjC9sC,EA2CT,SAASwuG,EAAsBC,EAAmBC,GAChD,IAAIC,EAAqB,CACvB59D,OAAQ,GACRC,iBAAkB,GAClBC,cAAe,IAGb29D,EAAyB,SAASpxG,EAAIuzC,GACxCvzC,EAAKlX,SAASkX,EAAI,IAClB,IAAK,IAAIljC,EAAI,EAAGA,EAAIy2E,EAAOxsE,OAAQjK,IACjC,GAAIy2E,EAAOz2E,GAAGymD,cAAgBvjB,GAC1BuzC,EAAOz2E,GAAGi0E,uBAAyB/wC,EACrC,OAAOuzC,EAAOz2E,IAKhBu0I,EAAuB,SAASC,EAAMC,EAAMC,EAASC,GACvD,IAAIC,EAASN,EAAuBE,EAAKlgE,WAAWiD,IAAKm9D,GACrDG,EAASP,EAAuBG,EAAKngE,WAAWiD,IAAKo9D,GACzD,OAAOC,GAAUC,GACbD,EAAOr0I,KAAK8V,gBAAkBw+H,EAAOt0I,KAAK8V,eAqDhD,OAlDA89H,EAAkB19D,OAAO3zD,SAAQ,SAAS8xH,GACxC,IAAK,IAAI50I,EAAI,EAAGA,EAAIo0I,EAAmB39D,OAAOxsE,OAAQjK,IAAK,CACzD,IAAI60I,EAAST,EAAmB39D,OAAOz2E,GACvC,GAAI40I,EAAOr0I,KAAK8V,gBAAkBw+H,EAAOt0I,KAAK8V,eAC1Cu+H,EAAO9gE,YAAc+gE,EAAO/gE,UAAW,CACzC,GAAkC,QAA9B8gE,EAAOr0I,KAAK8V,eACZu+H,EAAOtgE,YAAcugE,EAAOvgE,WAAWiD,MAGpCg9D,EAAqBK,EAAQC,EAC9BV,EAAkB19D,OAAQ29D,EAAmB39D,QAC/C,UAGJo+D,EAASv0H,KAAKsL,MAAMtL,KAAKwL,UAAU+oH,KAE5B9gE,YAAcj1D,KAAKqP,IAAIymH,EAAO7gE,YACjC8gE,EAAO9gE,aAEXsgE,EAAmB59D,OAAO7/D,KAAKi+H,GAG/BA,EAAOngE,aAAemgE,EAAOngE,aAAatwC,QAAO,SAASyhB,GACxD,IAAK,IAAIj5C,EAAI,EAAGA,EAAIgoI,EAAOlgE,aAAazqE,OAAQ2C,IAC9C,GAAIgoI,EAAOlgE,aAAa9nE,GAAG+N,OAASkrC,EAAGlrC,MACnCi6H,EAAOlgE,aAAa9nE,GAAG+5C,YAAcd,EAAGc,UAC1C,OAAO,EAGX,OAAO,KAIT,WAKNwtF,EAAkBz9D,iBAAiB5zD,SAAQ,SAASgyH,GAClD,IAAK,IAAI90I,EAAI,EAAGA,EAAIo0I,EAAmB19D,iBAAiBzsE,OACnDjK,IAAK,CACR,IAAI+0I,EAAmBX,EAAmB19D,iBAAiB12E,GAC3D,GAAI80I,EAAiBxxG,MAAQyxG,EAAiBzxG,IAAK,CACjD+wG,EAAmB39D,iBAAiB9/D,KAAKm+H,GACzC,WAMCV,EAIT,SAASW,EAAgC92G,EAAQvjB,EAAMgnE,GACrD,OAS6C,IATtC,CACLgI,MAAO,CACLlE,oBAAqB,CAAC,SAAU,oBAChC+D,qBAAsB,CAAC,SAAU,sBAEnCE,OAAQ,CACNjE,oBAAqB,CAAC,oBAAqB,uBAC3C+D,qBAAsB,CAAC,mBAAoB,0BAE7C7uE,GAAMujB,GAAQ5mB,QAAQqqE,GAG1B,SAASszD,EAAkBC,EAAc/yG,GAGvC,IAAIgzG,EAAeD,EAAaE,sBAC3BzwG,MAAK,SAAS0wG,GACb,OAAOlzG,EAAUE,aAAegzG,EAAgBhzG,YAC5CF,EAAUI,KAAO8yG,EAAgB9yG,IACjCJ,EAAUd,OAASg0G,EAAgBh0G,MACnCc,EAAU5c,WAAa8vH,EAAgB9vH,UACvC4c,EAAU/lB,WAAai5H,EAAgBj5H,UACvC+lB,EAAUxnB,OAAS06H,EAAgB16H,QAK7C,OAHKw6H,GACHD,EAAaI,mBAAmBnzG,IAE1BgzG,EAIV,SAASI,EAAUh1I,EAAMi2E,GACvB,IAAIzpE,EAAI,IAAIhC,MAAMyrE,GAUlB,OATAzpE,EAAExM,KAAOA,EAETwM,EAAEsjB,KAAO,CACPmlH,kBAAmB,EACnBC,kBAAmB,GACnBC,mBAAoB,GACpBnrI,eAAW0J,EACX0hI,oBAAgB1hI,GAChB1T,GACKwM,EAGTtN,EAAOD,QAAU,SAASI,EAAQg2I,GAIhC,SAASC,EAA6B7jG,EAAO/c,GAC3CA,EAAO4c,SAASG,GAChB/c,EAAO6gH,cAAc,IAAIl2I,EAAOm2I,sBAAsB,WAClD,CAAC/jG,MAAOA,KASd,SAASgkG,EAAah9E,EAAIhnB,EAAO8M,EAAUtN,GACzC,IAAIykG,EAAa,IAAIC,MAAM,SAC3BD,EAAWjkG,MAAQA,EACnBikG,EAAWn3F,SAAWA,EACtBm3F,EAAW58D,YAAc,CAACv6B,SAAUA,GACpCm3F,EAAWzkG,QAAUA,EACrB5xC,EAAOqe,YAAW,WAChB+6C,EAAGm9E,eAAe,QAASF,MAI/B,IAAIxnG,EAAoB,SAASvH,GAC/B,IAAI8xB,EAAKtlD,KAEL0iI,EAAernI,SAASgI,yBAwB5B,GAvBA,CAAC,mBAAoB,sBAAuB,iBACvC+L,SAAQ,SAASs/E,GAChBppC,EAAGopC,GAAUg0C,EAAah0C,GAAQ5gG,KAAK40I,MAG7C1iI,KAAK2iI,wBAA0B,KAE/B3iI,KAAK4iI,iBAAkB,EAEvB5iI,KAAK6iI,aAAe,GACpB7iI,KAAK8iI,cAAgB,GAErB9iI,KAAK+iI,kBAAoB,KACzB/iI,KAAKgjI,mBAAqB,KAE1BhjI,KAAKiuE,eAAiB,SACtBjuE,KAAKouE,mBAAqB,MAC1BpuE,KAAKkuE,gBAAkB,MACvBluE,KAAKijI,kBAAoB,MAEzBzvG,EAAS5mB,KAAKsL,MAAMtL,KAAKwL,UAAUob,GAAU,KAE7CxzB,KAAKkjI,YAAsC,eAAxB1vG,EAAOgT,aACG,cAAzBhT,EAAO2vG,cACT,MAAMtB,EAAU,oBACZ,8CAKN,OAJYruG,EAAO2vG,gBACjB3vG,EAAO2vG,cAAgB,WAGjB3vG,EAAO8qB,oBACb,IAAK,MACL,IAAK,QACH,MACF,QACE9qB,EAAO8qB,mBAAqB,MAIhC,OAAQ9qB,EAAOgT,cACb,IAAK,WACL,IAAK,aACL,IAAK,aACH,MACF,QACEhT,EAAOgT,aAAe,WAO1B,GAHAhT,EAAO0qB,WA/OX,SAA0BA,EAAYgkF,GACpC,IAAIkB,GAAU,EAEd,OADAllF,EAAatxC,KAAKsL,MAAMtL,KAAKwL,UAAU8lC,KACrBxtB,QAAO,SAAS22B,GAChC,GAAIA,IAAWA,EAAO5O,MAAQ4O,EAAOtmB,KAAM,CACzC,IAAI0X,EAAO4O,EAAO5O,MAAQ4O,EAAOtmB,IAC7BsmB,EAAOtmB,MAAQsmB,EAAO5O,MACxB1zC,QAAQI,KAAK,qDAEf,IAAIk+H,EAA2B,iBAAT5qF,EAoBtB,OAnBI4qF,IACF5qF,EAAO,CAACA,IAEVA,EAAOA,EAAK/nB,QAAO,SAASqQ,GAM1B,OALyC,IAAzBA,EAAIn9B,QAAQ,WACU,IAAlCm9B,EAAIn9B,QAAQ,mBACe,IAA3Bm9B,EAAIn9B,QAAQ,YACXw/H,GAGHA,GAAU,GACH,GAEuB,IAAzBriG,EAAIn9B,QAAQ,UAAkBs+H,GAAe,QACb,IAAnCnhG,EAAIn9B,QAAQ,4BAGXyjD,EAAOtmB,IACdsmB,EAAO5O,KAAO4qF,EAAW5qF,EAAK,GAAKA,IAC1BA,EAAKliD,WAkNI+sI,CAAiB9vG,EAAO0qB,YAAc,GAAIgkF,GAE9DliI,KAAKujI,cAAgB,GACjB/vG,EAAOgwG,qBACT,IAAK,IAAIl3I,EAAIknC,EAAOgwG,qBAAsBl3I,EAAI,EAAGA,IAC/C0T,KAAKujI,cAAcrgI,KAAK,IAAIhX,EAAOu3I,eAAe,CAChDvlF,WAAY1qB,EAAO0qB,WACnBwlF,aAAclwG,EAAO8qB,2BAIzB9qB,EAAOgwG,qBAAuB,EAGhCxjI,KAAK2jI,QAAUnwG,EAIfxzB,KAAK4rF,aAAe,GAEpB5rF,KAAK4jI,cAAgB/kE,EAASwG,oBAC9BrlE,KAAK6jI,mBAAqB,EAE1B7jI,KAAK8jI,eAAYvjI,EAEjBP,KAAK+jI,WAAY,GAGnB/2I,OAAOC,eAAe8tC,EAAkB7sC,UAAW,mBAAoB,CACrEmsH,cAAc,EACdltH,IAAK,WACH,OAAO6S,KAAK+iI,qBAGhB/1I,OAAOC,eAAe8tC,EAAkB7sC,UAAW,oBAAqB,CACtEmsH,cAAc,EACdltH,IAAK,WACH,OAAO6S,KAAKgjI,sBAKhBjoG,EAAkB7sC,UAAU2/E,eAAiB,KAC7C9yC,EAAkB7sC,UAAU81I,YAAc,KAC1CjpG,EAAkB7sC,UAAU+1I,QAAU,KACtClpG,EAAkB7sC,UAAUg2I,eAAiB,KAC7CnpG,EAAkB7sC,UAAU8/E,uBAAyB,KACrDjzC,EAAkB7sC,UAAUigF,2BAA6B,KACzDpzC,EAAkB7sC,UAAUi2I,wBAA0B,KACtDppG,EAAkB7sC,UAAUk2I,0BAA4B,KACxDrpG,EAAkB7sC,UAAUqgF,oBAAsB,KAClDxzC,EAAkB7sC,UAAUm2I,cAAgB,KAE5CtpG,EAAkB7sC,UAAUu0I,eAAiB,SAAS51I,EAAMw6B,GACtDrnB,KAAK+jI,YAGT/jI,KAAKoiI,cAAc/6G,GACc,mBAAtBrnB,KAAK,KAAOnT,IACrBmT,KAAK,KAAOnT,GAAMw6B,KAItB0T,EAAkB7sC,UAAUo2I,0BAA4B,WACtD,IAAIj9G,EAAQ,IAAIm7G,MAAM,2BACtBxiI,KAAKyiI,eAAe,0BAA2Bp7G,IAGjD0T,EAAkB7sC,UAAUq2I,iBAAmB,WAC7C,OAAOvkI,KAAK2jI,SAGd5oG,EAAkB7sC,UAAUs2I,gBAAkB,WAC5C,OAAOxkI,KAAK6iI,cAGd9nG,EAAkB7sC,UAAUu2I,iBAAmB,WAC7C,OAAOzkI,KAAK8iI,eAKd/nG,EAAkB7sC,UAAUw2I,mBAAqB,SAAStrG,EAAMurG,GAC9D,IAAIC,EAAqB5kI,KAAK4rF,aAAar1F,OAAS,EAChDovE,EAAc,CAChBrnC,MAAO,KACPsnC,YAAa,KACb47D,aAAc,KACd17D,cAAe,KACf26D,kBAAmB,KACnBC,mBAAoB,KACpB36D,UAAW,KACXC,YAAa,KACb5sC,KAAMA,EACNsW,IAAK,KACLu2B,uBAAwB,KACxB4+D,uBAAwB,KACxBtjH,OAAQ,KACRujH,6BAA8B,GAC9BC,aAAa,GAEf,GAAI/kI,KAAKkjI,aAAe0B,EACtBj/D,EAAY67D,aAAexhI,KAAK4rF,aAAa,GAAG41C,aAChD77D,EAAYG,cAAgB9lE,KAAK4rF,aAAa,GAAG9lB,kBAC5C,CACL,IAAI9nD,EAAahe,KAAKglI,8BACtBr/D,EAAY67D,aAAexjH,EAAWwjH,aACtC77D,EAAYG,cAAgB9nD,EAAW8nD,cAKzC,OAHK6+D,GACH3kI,KAAK4rF,aAAa1oF,KAAKyiE,GAElBA,GAGT5qC,EAAkB7sC,UAAUiwC,SAAW,SAASG,EAAO/c,GACrD,GAAIvhB,KAAK+jI,UACP,MAAMlC,EAAU,oBACZ,0DAGN,IAQIl8D,EAJJ,GAJoB3lE,KAAK4rF,aAAa36D,MAAK,SAAS5iC,GAClD,OAAOA,EAAEiwC,QAAUA,KAInB,MAAMujG,EAAU,qBAAsB,yBAIxC,IAAK,IAAIv1I,EAAI,EAAGA,EAAI0T,KAAK4rF,aAAar1F,OAAQjK,IACvC0T,KAAK4rF,aAAat/F,GAAGgyC,OACtBt+B,KAAK4rF,aAAat/F,GAAG8sC,OAASkF,EAAMlF,OACtCusC,EAAc3lE,KAAK4rF,aAAat/F,IAiBpC,OAdKq5E,IACHA,EAAc3lE,KAAK0kI,mBAAmBpmG,EAAMlF,OAG9Cp5B,KAAKilI,+BAEsC,IAAvCjlI,KAAK6iI,aAAaj/H,QAAQ2d,IAC5BvhB,KAAK6iI,aAAa3/H,KAAKqe,GAGzBokD,EAAYrnC,MAAQA,EACpBqnC,EAAYpkD,OAASA,EACrBokD,EAAYI,UAAY,IAAI75E,EAAOmnH,aAAa/0E,EAC5CqnC,EAAYG,eACTH,EAAYI,WAGrBhrC,EAAkB7sC,UAAU88F,UAAY,SAASzpE,GAC/C,IAAI+jC,EAAKtlD,KACT,GAAIkiI,GAAe,MACjB3gH,EAAOue,YAAY1wB,SAAQ,SAASkvB,GAClCgnB,EAAGnnB,SAASG,EAAO/c,UAEhB,CAIL,IAAI2jH,EAAe3jH,EAAOsxD,QAC1BtxD,EAAOue,YAAY1wB,SAAQ,SAASkvB,EAAOsR,GACzC,IAAIu1F,EAAcD,EAAaplG,YAAY8P,GAC3CtR,EAAMjC,iBAAiB,WAAW,SAAShV,GACzC89G,EAAY77D,QAAUjiD,EAAMiiD,cAGhC47D,EAAaplG,YAAY1wB,SAAQ,SAASkvB,GACxCgnB,EAAGnnB,SAASG,EAAO4mG,QAKzBnqG,EAAkB7sC,UAAUsmG,YAAc,SAASjqB,GACjD,GAAIvqE,KAAK+jI,UACP,MAAMlC,EAAU,oBACZ,6DAGN,KAAMt3D,aAAkBr+E,EAAOmnH,cAC7B,MAAM,IAAIx8G,UAAU,0FAItB,IAAI8uE,EAAc3lE,KAAK4rF,aAAa36D,MAAK,SAASzjC,GAChD,OAAOA,EAAEu4E,YAAcwE,KAGzB,IAAK5E,EACH,MAAMk8D,EAAU,qBACZ,8CAEN,IAAItgH,EAASokD,EAAYpkD,OAEzBokD,EAAYI,UAAUniD,OACtB+hD,EAAYI,UAAY,KACxBJ,EAAYrnC,MAAQ,KACpBqnC,EAAYpkD,OAAS,MAMiB,IAHnBvhB,KAAK4rF,aAAapmF,KAAI,SAAShY,GAChD,OAAOA,EAAE+zB,UAEM3d,QAAQ2d,IACrBvhB,KAAK6iI,aAAaj/H,QAAQ2d,IAAW,GACvCvhB,KAAK6iI,aAAa5+H,OAAOjE,KAAK6iI,aAAaj/H,QAAQ2d,GAAS,GAG9DvhB,KAAKilI,+BAGPlqG,EAAkB7sC,UAAUk3I,aAAe,SAAS7jH,GAClD,IAAI+jC,EAAKtlD,KACTuhB,EAAOue,YAAY1wB,SAAQ,SAASkvB,GAClC,IAAIisC,EAASjlB,EAAG+/E,aAAap0G,MAAK,SAAS5iC,GACzC,OAAOA,EAAEiwC,QAAUA,KAEjBisC,GACFjlB,EAAGkvC,YAAYjqB,OAKrBxvC,EAAkB7sC,UAAUm3I,WAAa,WACvC,OAAOrlI,KAAK4rF,aAAal7D,QAAO,SAASi1C,GACvC,QAASA,EAAYI,aAEtBvgE,KAAI,SAASmgE,GACZ,OAAOA,EAAYI,cAIvBhrC,EAAkB7sC,UAAUo3I,aAAe,WACzC,OAAOtlI,KAAK4rF,aAAal7D,QAAO,SAASi1C,GACvC,QAASA,EAAYK,eAEtBxgE,KAAI,SAASmgE,GACZ,OAAOA,EAAYK,gBAKvBjrC,EAAkB7sC,UAAUq3I,mBAAqB,SAAS91D,EACtDyzD,GACF,IAAI59E,EAAKtlD,KACT,GAAIkjI,GAAezzD,EAAgB,EACjC,OAAOzvE,KAAK4rF,aAAa,GAAGhmB,YACvB,GAAI5lE,KAAKujI,cAAchtI,OAC5B,OAAOyJ,KAAKujI,cAAc71G,QAE5B,IAAIk4C,EAAc,IAAI15E,EAAOu3I,eAAe,CAC1CvlF,WAAYl+C,KAAK2jI,QAAQzlF,WACzBwlF,aAAc1jI,KAAK2jI,QAAQrlF,qBAkB7B,OAhBAtxD,OAAOC,eAAe24E,EAAa,QAC/B,CAACr4E,MAAO,MAAO+sH,UAAU,IAG7Bt6G,KAAK4rF,aAAanc,GAAe+1D,wBAA0B,GAC3DxlI,KAAK4rF,aAAanc,GAAeg2D,iBAAmB,SAASp+G,GAC3D,IAAI62D,GAAO72D,EAAMoH,WAAqD,IAAxCzhC,OAAOuY,KAAK8hB,EAAMoH,WAAWl4B,OAG3DqvE,EAAY34B,MAAQixC,EAAM,YAAc,YACuB,OAA3D54B,EAAGsmC,aAAanc,GAAe+1D,yBACjClgF,EAAGsmC,aAAanc,GAAe+1D,wBAAwBtiI,KAAKmkB,IAGhEu+C,EAAYvpC,iBAAiB,iBAC3Br8B,KAAK4rF,aAAanc,GAAeg2D,kBAC5B7/D,GAIT7qC,EAAkB7sC,UAAUw3I,QAAU,SAASh2F,EAAK+/B,GAClD,IAAInqB,EAAKtlD,KACL4lE,EAAc5lE,KAAK4rF,aAAanc,GAAe7J,YACnD,IAAIA,EAAY+/D,iBAAhB,CAGA,IAAIH,EACFxlI,KAAK4rF,aAAanc,GAAe+1D,wBACnCxlI,KAAK4rF,aAAanc,GAAe+1D,wBAA0B,KAC3D5/D,EAAY32B,oBAAoB,iBAC9BjvC,KAAK4rF,aAAanc,GAAeg2D,kBACnC7/D,EAAY+/D,iBAAmB,SAASC,GACtC,KAAItgF,EAAG49E,aAAezzD,EAAgB,GAAtC,CAMA,IAAIpoD,EAAQ,IAAIm7G,MAAM,gBACtBn7G,EAAMoH,UAAY,CAACyhD,OAAQxgC,EAAK+/B,cAAeA,GAE/C,IAAIvgD,EAAO02G,EAAIn3G,UAEXyvD,GAAOhvD,GAAqC,IAA7BliC,OAAOuY,KAAK2pB,GAAM34B,OACrC,GAAI2nF,EAGwB,QAAtBtY,EAAY34B,OAAyC,cAAtB24B,EAAY34B,QAC7C24B,EAAY34B,MAAQ,iBAEjB,CACqB,QAAtB24B,EAAY34B,QACd24B,EAAY34B,MAAQ,aAGtB/d,EAAKN,UAAY,EAEjBM,EAAKnC,MAAQ64C,EAAYC,qBAAqB/F,iBAE9C,IAAI+lE,EAAsBhnE,EAASkB,eAAe7wC,GAClD7H,EAAMoH,UAAYzhC,OAAOgoG,OAAO3tE,EAAMoH,UAClCowC,EAASY,eAAeomE,IAE5Bx+G,EAAMoH,UAAUA,UAAYo3G,EAC5Bx+G,EAAMoH,UAAUq3G,OAAS,WACvB,MAAO,CACLr3G,UAAWpH,EAAMoH,UAAUA,UAC3ByhD,OAAQ7oD,EAAMoH,UAAUyhD,OACxBT,cAAepoD,EAAMoH,UAAUghD,cAC/B3P,iBAAkBz4C,EAAMoH,UAAUqxC,mBAMxC,IAAIT,EAAWR,EAASS,iBAAiBha,EAAGy9E,kBAAkB/wG,KAK5DqtC,EAASh4C,EAAMoH,UAAUghD,gBAJtByO,EAKC,0BAHA,KAAO72D,EAAMoH,UAAUA,UAAY,OAKzC62B,EAAGy9E,kBAAkB/wG,IACjB6sC,EAASO,eAAe9Z,EAAGy9E,kBAAkB/wG,KAC7CqtC,EAASl8D,KAAK,IAClB,IAAI4iI,EAAWzgF,EAAGsmC,aAAa6N,OAAM,SAAS9zB,GAC5C,OAAOA,EAAYC,aACmB,cAAlCD,EAAYC,YAAY34B,SAGD,cAAzBqY,EAAG29E,oBACL39E,EAAG29E,kBAAoB,YACvB39E,EAAGg/E,6BAKApmD,GACH54B,EAAGm9E,eAAe,eAAgBp7G,GAEhC0+G,IACFzgF,EAAGm9E,eAAe,eAAgB,IAAID,MAAM,iBAC5Cl9E,EAAG29E,kBAAoB,WACvB39E,EAAGg/E,+BAKPp4I,EAAOqe,YAAW,WAChBi7H,EAAwBp2H,SAAQ,SAAS/V,GACvCusE,EAAY+/D,iBAAiBtsI,QAE9B,KAIL0hC,EAAkB7sC,UAAU82I,4BAA8B,WACxD,IAAI1/E,EAAKtlD,KACLwhI,EAAe,IAAIt1I,EAAO85I,gBAAgB,MAC9CxE,EAAayE,iBAAmB,WAC9B3gF,EAAG4gF,4BACH5gF,EAAG6gF,0BAGL,IAAIrgE,EAAgB,IAAI55E,EAAOk6I,iBAAiB5E,GAWhD,OAVA17D,EAAcugE,kBAAoB,WAChC/gF,EAAG6gF,0BAELrgE,EAAcpqD,QAAU,WAEtB1uB,OAAOC,eAAe64E,EAAe,QACjC,CAACv4E,MAAO,SAAU+sH,UAAU,IAChCh1D,EAAG6gF,0BAGE,CACL3E,aAAcA,EACd17D,cAAeA,IAMnB/qC,EAAkB7sC,UAAUo4I,6BAA+B,SACvD72D,GACF,IAAI7J,EAAc5lE,KAAK4rF,aAAanc,GAAe7J,YAC/CA,WACKA,EAAY+/D,wBACZ3lI,KAAK4rF,aAAanc,GAAe7J,aAE1C,IAAI47D,EAAexhI,KAAK4rF,aAAanc,GAAe+xD,aAChDA,WACKA,EAAayE,wBACbjmI,KAAK4rF,aAAanc,GAAe+xD,cAE1C,IAAI17D,EAAgB9lE,KAAK4rF,aAAanc,GAAe3J,cACjDA,WACKA,EAAcugE,yBACdvgE,EAAcpqD,eACd1b,KAAK4rF,aAAanc,GAAe3J,gBAK5C/qC,EAAkB7sC,UAAUq4I,YAAc,SAAS5gE,EAC/Ct4D,EAAMm5H,GACR,IAAI/2G,EAAS+wG,EAAsB76D,EAAY86D,kBAC3C96D,EAAY+6D,oBACZrzH,GAAQs4D,EAAYI,YACtBt2C,EAAOi8D,UAAY/lB,EAAYM,uBAC/Bx2C,EAAOyzC,KAAO,CACZoB,MAAOzF,EAASC,WAChB2F,SAAUkB,EAAYvB,eAAeK,UAEnCkB,EAAYk/D,uBAAuBtuI,SACrCk5B,EAAOyzC,KAAKl+C,KAAO2gD,EAAYk/D,uBAAuB,GAAG7/G,MAE3D2gD,EAAYI,UAAU14D,KAAKoiB,IAEzB+2G,GAAQ7gE,EAAYK,aAAev2C,EAAOszC,OAAOxsE,OAAS,IAEnC,UAArBovE,EAAYvsC,MACTusC,EAAYk/D,wBACZ3C,EAAc,OACnBv8D,EAAYk/D,uBAAuBz1H,SAAQ,SAAShhB,UAC3CA,EAAE41E,OAGT2B,EAAYk/D,uBAAuBtuI,OACrCk5B,EAAOi8D,UAAY/lB,EAAYk/D,uBAE/Bp1G,EAAOi8D,UAAY,CAAC,IAEtBj8D,EAAOyzC,KAAO,CACZuB,SAAUkB,EAAYvB,eAAeK,UAEnCkB,EAAYvB,eAAeE,QAC7B70C,EAAOyzC,KAAKoB,MAAQqB,EAAYvB,eAAeE,OAE7CqB,EAAYM,uBAAuB1vE,SACrCk5B,EAAOyzC,KAAKl+C,KAAO2gD,EAAYM,uBAAuB,GAAGjhD,MAE3D2gD,EAAYK,YAAYygE,QAAQh3G,KAIpCsL,EAAkB7sC,UAAU6jF,oBAAsB,SAASjP,GACzD,IAeIzD,EACAtvC,EAhBAu1B,EAAKtlD,KAGT,IAAuD,IAAnD,CAAC,QAAS,UAAU4D,QAAQk/D,EAAY77D,MAC1C,OAAO+1B,QAAQE,OAAO2kG,EAAU,YAC5B,qBAAuB/+D,EAAY77D,KAAO,MAGhD,IAAKq6H,EAAgC,sBACjCx+D,EAAY77D,KAAMq+C,EAAG2oB,iBAAmB3oB,EAAGy+E,UAC7C,OAAO/mG,QAAQE,OAAO2kG,EAAU,oBAC5B,qBAAuB/+D,EAAY77D,KACnC,aAAeq+C,EAAG2oB,iBAKxB,GAAyB,UAArBnL,EAAY77D,KAGdo4D,EAAWR,EAASK,cAAc4D,EAAY9wC,KAC9CjC,EAAcsvC,EAAS3xC,QACvB2xC,EAASjwD,SAAQ,SAASkyD,EAAcmO,GACtC,IAAIh2B,EAAOolB,EAASgE,mBAAmBvB,GACvChc,EAAGsmC,aAAanc,GAAegxD,kBAAoBhnF,KAGrD6L,EAAGsmC,aAAax8E,SAAQ,SAASu2D,EAAa8J,GAC5CnqB,EAAGogF,QAAQ//D,EAAYj2B,IAAK+/B,WAEzB,GAAyB,WAArB3M,EAAY77D,KAAmB,CACxCo4D,EAAWR,EAASK,cAAc5Z,EAAG09E,mBAAmBhxG,KACxDjC,EAAcsvC,EAAS3xC,QACvB,IAAIg5G,EAAY7nE,EAASU,YAAYxvC,EACjC,cAAcx5B,OAAS,EAC3B8oE,EAASjwD,SAAQ,SAASkyD,EAAcmO,GACtC,IAAI9J,EAAcrgB,EAAGsmC,aAAanc,GAC9B7J,EAAcD,EAAYC,YAC1B47D,EAAe77D,EAAY67D,aAC3B17D,EAAgBH,EAAYG,cAC5B26D,EAAoB96D,EAAY86D,kBAChCC,EAAqB/6D,EAAY+6D,mBAMrC,KAHe7hE,EAASuH,WAAW9E,IACgC,IAA/DzC,EAASU,YAAY+B,EAAc,iBAAiB/qE,UAEtCovE,EAAYghE,SAAU,CACtC,IAAIC,EAAsB/nE,EAAS8D,iBAC/BrB,EAAcvxC,GACd82G,EAAuBhoE,EAAS2C,kBAChCF,EAAcvxC,GACd22G,IACFG,EAAqBplE,KAAO,UAGzBnc,EAAG49E,aAAiC,IAAlBzzD,IACrBnqB,EAAGogF,QAAQ//D,EAAYj2B,IAAK+/B,GACD,QAAvB+xD,EAAav0F,OACfu0F,EAAazkH,MAAM6oD,EAAaghE,EAC5BF,EAAY,cAAgB,cAEN,QAAxB5gE,EAAc74B,OAChB64B,EAAc/oD,MAAM8pH,IAKxB,IAAIp3G,EAAS+wG,EAAsBC,EAC/BC,GAIJp7E,EAAGihF,YAAY5gE,EACXl2C,EAAOszC,OAAOxsE,OAAS,GACvB,OAeV,OAVA+uD,EAAGy9E,kBAAoB,CACrB97H,KAAM67D,EAAY77D,KAClB+qB,IAAK8wC,EAAY9wC,KAEM,UAArB8wC,EAAY77D,KACdq+C,EAAGwhF,sBAAsB,oBAEzBxhF,EAAGwhF,sBAAsB,UAGpB9pG,QAAQC,WAGjBlC,EAAkB7sC,UAAU4nF,qBAAuB,SAAShT,GAC1D,IAAIxd,EAAKtlD,KAGT,IAAuD,IAAnD,CAAC,QAAS,UAAU4D,QAAQk/D,EAAY77D,MAC1C,OAAO+1B,QAAQE,OAAO2kG,EAAU,YAC5B,qBAAuB/+D,EAAY77D,KAAO,MAGhD,IAAKq6H,EAAgC,uBACjCx+D,EAAY77D,KAAMq+C,EAAG2oB,iBAAmB3oB,EAAGy+E,UAC7C,OAAO/mG,QAAQE,OAAO2kG,EAAU,oBAC5B,sBAAwB/+D,EAAY77D,KACpC,aAAeq+C,EAAG2oB,iBAGxB,IAAInwC,EAAU,GACdwnB,EAAGw9E,cAAc1zH,SAAQ,SAASmS,GAChCuc,EAAQvc,EAAOra,IAAMqa,KAEvB,IAAIwlH,EAAe,GACf1nE,EAAWR,EAASK,cAAc4D,EAAY9wC,KAC9CjC,EAAcsvC,EAAS3xC,QACvBg5G,EAAY7nE,EAASU,YAAYxvC,EACjC,cAAcx5B,OAAS,EACvB2sI,EAAcrkE,EAASU,YAAYxvC,EACnC,mBAAmBx5B,OAAS,EAChC+uD,EAAG49E,YAAcA,EACjB,IAAI8D,EAAanoE,EAASU,YAAYxvC,EAClC,kBAAkB,GA4UtB,OA1UEu1B,EAAGq9E,0BADDqE,GAC2BA,EAAW5xH,OAAO,IAAIrS,MAAM,KACpDa,QAAQ,YAAc,EAK7By7D,EAASjwD,SAAQ,SAASkyD,EAAcmO,GACtC,IAAIngD,EAAQuvC,EAASG,WAAWsC,GAC5BloC,EAAOylC,EAASsH,QAAQ7E,GAExBqlE,EAAW9nE,EAASuH,WAAW9E,IACgC,IAA/DzC,EAASU,YAAY+B,EAAc,iBAAiB/qE,OACpDmS,EAAW4mB,EAAM,GAAGla,OAAO,GAAGrS,MAAM,KAAK,GAEzC4sB,EAAYkvC,EAASqH,aAAa5E,EAAcvxC,GAChDk3G,EAAapoE,EAAS8F,UAAUrD,GAEhC5xB,EAAMmvB,EAASwC,OAAOC,IAAiBzC,EAASE,qBAGpD,GAAI4nE,GAAsB,gBAATvtG,IAAwC,cAAb1wB,GAC3B,kBAAbA,GAGF48C,EAAGsmC,aAAanc,GAAiB,CAC/B//B,IAAKA,EACLtW,KAAMA,EACN1wB,SAAUA,EACVi+H,UAAU,OARd,CAmBA,IAAIhhE,EACAC,EACA47D,EACA17D,EACAE,EACAC,EACA4+D,EACApE,EAEAniG,GAfCqoG,GAAYrhF,EAAGsmC,aAAanc,IAC7BnqB,EAAGsmC,aAAanc,GAAek3D,WAEjCrhF,EAAGsmC,aAAanc,GAAiBnqB,EAAGo/E,mBAAmBtrG,GAAM,IAc/D,IACIwtG,EACAC,EAFAnG,EAAqB7hE,EAASgE,mBAAmBvB,GAGhDqlE,IACHC,EAAsB/nE,EAAS8D,iBAAiBrB,EAC5CvxC,IACJ82G,EAAuBhoE,EAAS2C,kBAAkBF,EAC9CvxC,IACiB0xC,KAAO,UAE9BojE,EACIhmE,EAAS2E,2BAA2BlC,GAExC,IAAI8C,EAAiBvF,EAASsF,oBAAoB7C,GAE9C4lE,EAAaroE,EAASU,YAAY+B,EAClC,sBAAuBvxC,GAAax5B,OAAS,EAC7C05E,EAAQpR,EAASU,YAAY+B,EAAc,gBAC1C97D,KAAI,SAAS0pB,GACZ,OAAO2vC,EAASY,eAAevwC,MAEhCwB,QAAO,SAASxB,GACf,OAA0B,IAAnBA,EAAKN,aAuBlB,IAnB0B,UAArBk0C,EAAY77D,MAAyC,WAArB67D,EAAY77D,QAC5C0/H,GAAYzD,GAAezzD,EAAgB,GAC5CnqB,EAAGsmC,aAAanc,KAClBnqB,EAAGghF,6BAA6B72D,GAChCnqB,EAAGsmC,aAAanc,GAAe7J,YAC3BtgB,EAAGsmC,aAAa,GAAGhmB,YACvBtgB,EAAGsmC,aAAanc,GAAe+xD,aAC3Bl8E,EAAGsmC,aAAa,GAAG41C,aACvBl8E,EAAGsmC,aAAanc,GAAe3J,cAC3BxgB,EAAGsmC,aAAa,GAAG9lB,cACnBxgB,EAAGsmC,aAAanc,GAAe1J,WACjCzgB,EAAGsmC,aAAanc,GAAe1J,UAAUohE,aACrC7hF,EAAGsmC,aAAa,GAAG9lB,eAErBxgB,EAAGsmC,aAAanc,GAAezJ,aACjC1gB,EAAGsmC,aAAanc,GAAezJ,YAAYmhE,aACvC7hF,EAAGsmC,aAAa,GAAG9lB,gBAGF,UAArBhD,EAAY77D,MAAqB0/H,GAmG9B,GAAyB,WAArB7jE,EAAY77D,OAAsB0/H,EAAU,CAErD/gE,GADAD,EAAcrgB,EAAGsmC,aAAanc,IACJ7J,YAC1B47D,EAAe77D,EAAY67D,aAC3B17D,EAAgBH,EAAYG,cAC5BE,EAAcL,EAAYK,YAC1BC,EAAyBN,EAAYM,uBACrCw6D,EAAoB96D,EAAY86D,kBAEhCn7E,EAAGsmC,aAAanc,GAAeo1D,uBAC3BA,EACJv/E,EAAGsmC,aAAanc,GAAeixD,mBAC3BA,EACJp7E,EAAGsmC,aAAanc,GAAerL,eAAiBA,EAE5C6L,EAAM15E,QAAiC,QAAvBirI,EAAav0F,SAC1By5F,IAAaQ,GACZhE,GAAiC,IAAlBzzD,EAGnBQ,EAAM7gE,SAAQ,SAASqf,GACrB8yG,EAAkB57D,EAAY67D,aAAc/yG,MAH9C+yG,EAAa4F,oBAAoBn3D,IAQhCizD,GAAiC,IAAlBzzD,IACS,QAAvB+xD,EAAav0F,OACfu0F,EAAazkH,MAAM6oD,EAAaghE,EAC5B,eAEsB,QAAxB9gE,EAAc74B,OAChB64B,EAAc/oD,MAAM8pH,KAMCrG,EACvB76D,EAAY86D,kBACZ96D,EAAY+6D,oBAEkB39D,OAAOryC,QAAO,SAAS/jC,GACrD,MAAgC,QAAzBA,EAAEE,KAAK8V,iBACbpM,QACYovE,EAAYM,uBAAuB,GAAGjC,YAC5C2B,EAAYM,uBAAuB,GAAGjC,IAG/C1e,EAAGihF,YAAY5gE,EACG,aAAdh2C,GAA0C,aAAdA,EACd,aAAdA,GAA0C,aAAdA,IAG5Bq2C,GACe,aAAdr2C,GAA0C,aAAdA,SAiBxBg2C,EAAYK,aAhBnB1nC,EAAQ0nC,EAAY1nC,MAChB2oG,GACGnpG,EAAQmpG,EAAW1lH,UACtBuc,EAAQmpG,EAAW1lH,QAAU,IAAIr1B,EAAO0vC,aAE1CumG,EAA6B7jG,EAAOR,EAAQmpG,EAAW1lH,SACvDwlH,EAAa7jI,KAAK,CAACo7B,EAAO0nC,EAAaloC,EAAQmpG,EAAW1lH,YAErDuc,EAAQq2E,UACXr2E,EAAQq2E,QAAU,IAAIjoH,EAAO0vC,aAE/BumG,EAA6B7jG,EAAOR,EAAQq2E,SAC5C4yB,EAAa7jI,KAAK,CAACo7B,EAAO0nC,EAAaloC,EAAQq2E,iBAvKN,EAC7CxuC,EAAcrgB,EAAGsmC,aAAanc,IAC1BnqB,EAAGo/E,mBAAmBtrG,IACdsW,IAAMA,EAEbi2B,EAAYC,cACfD,EAAYC,YAActgB,EAAGigF,mBAAmB91D,EAC5CyzD,IAGFjzD,EAAM15E,QAA6C,QAAnCovE,EAAY67D,aAAav0F,SACvCi6F,GAAgBhE,GAAiC,IAAlBzzD,EAGjCQ,EAAM7gE,SAAQ,SAASqf,GACrB8yG,EAAkB57D,EAAY67D,aAAc/yG,MAH9Ck3C,EAAY67D,aAAa4F,oBAAoBn3D,IAQjDwwD,EAAoBv0I,EAAOwnH,eAAeO,gBAAgB76E,GAItD8oG,EAAc,QAChBzB,EAAkB19D,OAAS09D,EAAkB19D,OAAOryC,QAChD,SAAS8B,GACP,MAAsB,QAAfA,EAAM3lC,SAIrBo5E,EAAyBN,EAAYM,wBAA0B,CAAC,CAC9DjhD,KAAgC,MAAzB,EAAIyqD,EAAgB,KAI7B,IAOQluD,EAPJ8lH,GAAa,EACjB,GAAkB,aAAd13G,GAA0C,aAAdA,GAK9B,GAJA03G,GAAc1hE,EAAYK,YAC1BA,EAAcL,EAAYK,aACtB,IAAI95E,EAAOwnH,eAAe/tC,EAAYG,cAAe1sC,GAErDiuG,EAEF/oG,EAAQ0nC,EAAY1nC,MAEhB2oG,GAAoC,MAAtBA,EAAW1lH,SAElB0lH,GACJnpG,EAAQmpG,EAAW1lH,UACtBuc,EAAQmpG,EAAW1lH,QAAU,IAAIr1B,EAAO0vC,YACxC5uC,OAAOC,eAAe6wC,EAAQmpG,EAAW1lH,QAAS,KAAM,CACtDp0B,IAAK,WACH,OAAO85I,EAAW1lH,WAIxBv0B,OAAOC,eAAeqxC,EAAO,KAAM,CACjCnxC,IAAK,WACH,OAAO85I,EAAW3oG,SAGtB/c,EAASuc,EAAQmpG,EAAW1lH,UAEvBuc,EAAQq2E,UACXr2E,EAAQq2E,QAAU,IAAIjoH,EAAO0vC,aAE/Bra,EAASuc,EAAQq2E,UAEf5yF,IACF4gH,EAA6B7jG,EAAO/c,GACpCokD,EAAYm/D,6BAA6B5hI,KAAKqe,IAEhDwlH,EAAa7jI,KAAK,CAACo7B,EAAO0nC,EAAazkD,SAEhCokD,EAAYK,aAAeL,EAAYK,YAAY1nC,QAC5DqnC,EAAYm/D,6BAA6B11H,SAAQ,SAAS/gB,GACxD,IAAIi5I,EAAcj5I,EAAEyxC,YAAY7O,MAAK,SAASzjC,GAC5C,OAAOA,EAAE0Z,KAAOy+D,EAAYK,YAAY1nC,MAAMp3B,MAE5CogI,GAl0Bd,SAA2ChpG,EAAO/c,GAChDA,EAAOizE,YAAYl2D,GACnB/c,EAAO6gH,cAAc,IAAIl2I,EAAOm2I,sBAAsB,cAClD,CAAC/jG,MAAOA,KAg0BFipG,CAAkCD,EAAaj5I,MAGnDs3E,EAAYm/D,6BAA+B,IAG7Cn/D,EAAY86D,kBAAoBA,EAChC96D,EAAY+6D,mBAAqBA,EACjC/6D,EAAYK,YAAcA,EAC1BL,EAAYvB,eAAiBA,EAC7BuB,EAAYM,uBAAyBA,EACrCN,EAAYk/D,uBAAyBA,EAIrCv/E,EAAGihF,YAAYjhF,EAAGsmC,aAAanc,IAC3B,EACA43D,aA8Ea9mI,IAAjB+kD,EAAGw+E,YACLx+E,EAAGw+E,UAAiC,UAArBhhE,EAAY77D,KAAmB,SAAW,WAG3Dq+C,EAAG09E,mBAAqB,CACtB/7H,KAAM67D,EAAY77D,KAClB+qB,IAAK8wC,EAAY9wC,KAEM,UAArB8wC,EAAY77D,KACdq+C,EAAGwhF,sBAAsB,qBAEzBxhF,EAAGwhF,sBAAsB,UAE3B95I,OAAOuY,KAAKu4B,GAAS1uB,SAAQ,SAAS/C,GACpC,IAAIkV,EAASuc,EAAQzxB,GACrB,GAAIkV,EAAOue,YAAYvpC,OAAQ,CAC7B,IAA0C,IAAtC+uD,EAAGw9E,cAAcl/H,QAAQ2d,GAAgB,CAC3C+jC,EAAGw9E,cAAc5/H,KAAKqe,GACtB,IAAI8F,EAAQ,IAAIm7G,MAAM,aACtBn7G,EAAM9F,OAASA,EACfr1B,EAAOqe,YAAW,WAChB+6C,EAAGm9E,eAAe,YAAap7G,MAInC0/G,EAAa33H,SAAQ,SAASmkB,GAC5B,IAAI+K,EAAQ/K,EAAK,GACb6X,EAAW7X,EAAK,GAChBhS,EAAOra,KAAOqsB,EAAK,GAAGrsB,IAG1Bo7H,EAAah9E,EAAIhnB,EAAO8M,EAAU,CAAC7pB,WAIzCwlH,EAAa33H,SAAQ,SAASmkB,GACxBA,EAAK,IAGT+uG,EAAah9E,EAAI/xB,EAAK,GAAIA,EAAK,GAAI,OAKrCrnC,EAAOqe,YAAW,WACV+6C,GAAMA,EAAGsmC,cAGftmC,EAAGsmC,aAAax8E,SAAQ,SAASu2D,GAC3BA,EAAY67D,cACuB,QAAnC77D,EAAY67D,aAAav0F,OACzB04B,EAAY67D,aAAaE,sBAAsBnrI,OAAS,IAC1DwO,QAAQI,KAAK,sFAEbwgE,EAAY67D,aAAaI,mBAAmB,UAG/C,KAEI5kG,QAAQC,WAGjBlC,EAAkB7sC,UAAUuuB,MAAQ,WAClCzc,KAAK4rF,aAAax8E,SAAQ,SAASu2D,GAM7BA,EAAY67D,cACd77D,EAAY67D,aAAa59G,OAEvB+hD,EAAYG,eACdH,EAAYG,cAAcliD,OAExB+hD,EAAYI,WACdJ,EAAYI,UAAUniD,OAEpB+hD,EAAYK,aACdL,EAAYK,YAAYpiD,UAI5B5jB,KAAK+jI,WAAY,EACjB/jI,KAAK8mI,sBAAsB,WAI7B/rG,EAAkB7sC,UAAU44I,sBAAwB,SAAStpE,GAC3Dx9D,KAAKiuE,eAAiBzQ,EACtB,IAAIn2C,EAAQ,IAAIm7G,MAAM,wBACtBxiI,KAAKyiI,eAAe,uBAAwBp7G,IAI9C0T,EAAkB7sC,UAAU+2I,4BAA8B,WACxD,IAAI3/E,EAAKtlD,KACmB,WAAxBA,KAAKiuE,iBAAwD,IAAzBjuE,KAAK4iI,kBAG7C5iI,KAAK4iI,iBAAkB,EACvB12I,EAAOqe,YAAW,WAChB,GAAI+6C,EAAGs9E,gBAAiB,CACtBt9E,EAAGs9E,iBAAkB,EACrB,IAAIv7G,EAAQ,IAAIm7G,MAAM,qBACtBl9E,EAAGm9E,eAAe,oBAAqBp7G,MAExC,KAIL0T,EAAkB7sC,UAAUg4I,0BAA4B,WACtD,IAAI1oE,EACAgqE,EAAS,CACX,IAAO,EACPz7D,OAAQ,EACR07D,SAAU,EACV79H,UAAW,EACX6sH,UAAW,EACXiR,aAAc,EACdC,OAAQ,GAuBV,GArBA3nI,KAAK4rF,aAAax8E,SAAQ,SAASu2D,GAC7BA,EAAY67D,eAAiB77D,EAAYghE,UAC3Ca,EAAO7hE,EAAY67D,aAAav0F,YAIpCuwB,EAAW,MACPgqE,EAAOG,OAAS,EAClBnqE,EAAW,SACFgqE,EAAOC,SAAW,EAC3BjqE,EAAW,WACFgqE,EAAOE,aAAe,EAC/BlqE,EAAW,eACFgqE,EAAOI,IAAM,EACtBpqE,EAAW,MACFgqE,EAAO59H,UAAY,EAC5B4zD,EAAW,YACFgqE,EAAO/Q,UAAY,IAC5Bj5D,EAAW,aAGTA,IAAax9D,KAAKouE,mBAAoB,CACxCpuE,KAAKouE,mBAAqB5Q,EAC1B,IAAIn2C,EAAQ,IAAIm7G,MAAM,4BACtBxiI,KAAKyiI,eAAe,2BAA4Bp7G,KAKpD0T,EAAkB7sC,UAAUi4I,uBAAyB,WACnD,IAAI3oE,EACAgqE,EAAS,CACX,IAAO,EACPz7D,OAAQ,EACR87D,WAAY,EACZj+H,UAAW,EACX6sH,UAAW,EACXiR,aAAc,EACdC,OAAQ,GAyBV,GAvBA3nI,KAAK4rF,aAAax8E,SAAQ,SAASu2D,GAC7BA,EAAY67D,cAAgB77D,EAAYG,gBACvCH,EAAYghE,WACfa,EAAO7hE,EAAY67D,aAAav0F,SAChCu6F,EAAO7hE,EAAYG,cAAc74B,aAIrCu6F,EAAO59H,WAAa49H,EAAO/Q,UAE3Bj5D,EAAW,MACPgqE,EAAOG,OAAS,EAClBnqE,EAAW,SACFgqE,EAAOK,WAAa,EAC7BrqE,EAAW,aACFgqE,EAAOE,aAAe,EAC/BlqE,EAAW,eACFgqE,EAAOI,IAAM,EACtBpqE,EAAW,MACFgqE,EAAO59H,UAAY,IAC5B4zD,EAAW,aAGTA,IAAax9D,KAAKkuE,gBAAiB,CACrCluE,KAAKkuE,gBAAkB1Q,EACvB,IAAIn2C,EAAQ,IAAIm7G,MAAM,yBACtBxiI,KAAKyiI,eAAe,wBAAyBp7G,KAIjD0T,EAAkB7sC,UAAU2jF,YAAc,WACxC,IAAIvsB,EAAKtlD,KAET,GAAIslD,EAAGy+E,UACL,OAAO/mG,QAAQE,OAAO2kG,EAAU,oBAC5B,yCAGN,IAAIiG,EAAiBxiF,EAAGsmC,aAAal7D,QAAO,SAASljC,GACnD,MAAkB,UAAXA,EAAE4rC,QACR7iC,OACCwxI,EAAiBziF,EAAGsmC,aAAal7D,QAAO,SAASljC,GACnD,MAAkB,UAAXA,EAAE4rC,QACR7iC,OAGCyxI,EAAe7mI,UAAU,GAC7B,GAAI6mI,EAAc,CAEhB,GAAIA,EAAaxyG,WAAawyG,EAAavyG,SACzC,MAAM,IAAI5+B,UACN,6DAEmC0J,IAArCynI,EAAapT,sBAEbkT,GADuC,IAArCE,EAAapT,oBACE,GAC6B,IAArCoT,EAAapT,oBACL,EAEAoT,EAAapT,0BAGOr0H,IAArCynI,EAAanT,sBAEbkT,GADuC,IAArCC,EAAanT,oBACE,GAC6B,IAArCmT,EAAanT,oBACL,EAEAmT,EAAanT,qBAoBpC,IAfAvvE,EAAGsmC,aAAax8E,SAAQ,SAASu2D,GACN,UAArBA,EAAYvsC,OACd0uG,EACqB,IACnBniE,EAAYo/D,aAAc,GAEE,UAArBp/D,EAAYvsC,QACrB2uG,EACqB,IACnBpiE,EAAYo/D,aAAc,MAMzB+C,EAAiB,GAAKC,EAAiB,GACxCD,EAAiB,IACnBxiF,EAAGo/E,mBAAmB,SACtBoD,KAEEC,EAAiB,IACnBziF,EAAGo/E,mBAAmB,SACtBqD,KAIJ,IAAI/1G,EAAM6sC,EAASyG,wBAAwBhgB,EAAGs+E,cAC1Ct+E,EAAGu+E,sBACPv+E,EAAGsmC,aAAax8E,SAAQ,SAASu2D,EAAa8J,GAG5C,IAAInxC,EAAQqnC,EAAYrnC,MACpBlF,EAAOusC,EAAYvsC,KACnBsW,EAAMi2B,EAAYj2B,KAAOmvB,EAASE,qBACtC4G,EAAYj2B,IAAMA,EAEbi2B,EAAYC,cACfD,EAAYC,YAActgB,EAAGigF,mBAAmB91D,EAC5CnqB,EAAG49E,cAGT,IAAIzC,EAAoBv0I,EAAOmnH,aAAaY,gBAAgB76E,GAGxD8oG,EAAc,QAChBzB,EAAkB19D,OAAS09D,EAAkB19D,OAAOryC,QAChD,SAAS8B,GACP,MAAsB,QAAfA,EAAM3lC,SAGrB4zI,EAAkB19D,OAAO3zD,SAAQ,SAASojB,GAGrB,SAAfA,EAAM3lC,WAC0C0T,IAAhDiyB,EAAMouC,WAAW,6BACnBpuC,EAAMouC,WAAW,2BAA6B,KAK5C+E,EAAY+6D,oBACZ/6D,EAAY+6D,mBAAmB39D,QACjC4C,EAAY+6D,mBAAmB39D,OAAO3zD,SAAQ,SAAS64H,GACjDz1G,EAAM3lC,KAAK8V,gBAAkBslI,EAAYp7I,KAAK8V,eAC9C6vB,EAAM4tC,YAAc6nE,EAAY7nE,YAClC5tC,EAAM+tC,qBAAuB0nE,EAAYl1F,mBAKjD0tF,EAAkBz9D,iBAAiB5zD,SAAQ,SAAS8jC,IAC3ByyB,EAAY+6D,oBAC/B/6D,EAAY+6D,mBAAmB19D,kBAAoB,IACtC5zD,SAAQ,SAAS84H,GAC5Bh1F,EAAOtjB,MAAQs4G,EAAQt4G,MACzBsjB,EAAOhsC,GAAKghI,EAAQhhI,UAM1B,IAAI++D,EAAyBN,EAAYM,wBAA0B,CAAC,CAClEjhD,KAAgC,MAAzB,EAAIyqD,EAAgB,KAEzBnxC,GAEE4jG,GAAe,OAAkB,UAAT9oG,IACvB6sC,EAAuB,GAAGjC,MAC7BiC,EAAuB,GAAGjC,IAAM,CAC9Bh/C,KAAMihD,EAAuB,GAAGjhD,KAAO,IAKzC2gD,EAAYo/D,cACdp/D,EAAYK,YAAc,IAAI95E,EAAOwnH,eACjC/tC,EAAYG,cAAe1sC,IAGjCusC,EAAY86D,kBAAoBA,EAChC96D,EAAYM,uBAAyBA,KAIP,eAA5B3gB,EAAGq+E,QAAQn9F,eACbxU,GAAO,kBAAoBszB,EAAGsmC,aAAapmF,KAAI,SAAShY,GACtD,OAAOA,EAAEkiD,OACRvsC,KAAK,KAAO,QAEjB6uB,GAAO,4BAEPszB,EAAGsmC,aAAax8E,SAAQ,SAASu2D,EAAa8J,GAC5Cz9C,GAAO0zC,EAAkBC,EAAaA,EAAY86D,kBAC9C,QAAS96D,EAAYpkD,OAAQ+jC,EAAGw+E,WACpC9xG,GAAO,oBAEH2zC,EAAYC,aAAwC,QAAzBtgB,EAAG29E,mBACX,IAAlBxzD,GAAwBnqB,EAAG49E,cAC9Bv9D,EAAYC,YAAYuiE,qBAAqB/4H,SAAQ,SAAS8f,GAC5DA,EAAKN,UAAY,EACjBoD,GAAO,KAAO6sC,EAASkB,eAAe7wC,GAAQ,UAGV,cAAlCy2C,EAAYC,YAAY34B,QAC1Bjb,GAAO,+BAKb,IAAI3C,EAAO,IAAInjC,EAAOypF,sBAAsB,CAC1C1uE,KAAM,QACN+qB,IAAKA,IAEP,OAAOgL,QAAQC,QAAQ5N,IAGzB0L,EAAkB7sC,UAAU6nF,aAAe,WACzC,IAAIzwB,EAAKtlD,KAET,GAAIslD,EAAGy+E,UACL,OAAO/mG,QAAQE,OAAO2kG,EAAU,oBAC5B,0CAGN,GAA4B,sBAAtBv8E,EAAG2oB,gBACiB,wBAAtB3oB,EAAG2oB,eACL,OAAOjxC,QAAQE,OAAO2kG,EAAU,oBAC5B,+CAAiDv8E,EAAG2oB,iBAG1D,IAAIj8C,EAAM6sC,EAASyG,wBAAwBhgB,EAAGs+E,cAC1Ct+E,EAAGu+E,sBACHv+E,EAAG49E,cACLlxG,GAAO,kBAAoBszB,EAAGsmC,aAAapmF,KAAI,SAAShY,GACtD,OAAOA,EAAEkiD,OACRvsC,KAAK,KAAO,QAEjB6uB,GAAO,4BAEP,IAAIo2G,EAAuBvpE,EAASS,iBAChCha,EAAG09E,mBAAmBhxG,KAAKz7B,OAC/B+uD,EAAGsmC,aAAax8E,SAAQ,SAASu2D,EAAa8J,GAC5C,KAAIA,EAAgB,EAAI24D,GAAxB,CAGA,GAAIziE,EAAYghE,SAkBd,MAjByB,gBAArBhhE,EAAYvsC,KACe,cAAzBusC,EAAYj9D,SACdspB,GAAO,qCAEPA,GAAO,mBAAqB2zC,EAAYj9D,SACpC,0BAEwB,UAArBi9D,EAAYvsC,KACrBpH,GAAO,4DAEuB,UAArB2zC,EAAYvsC,OACrBpH,GAAO,sEAGTA,GAAO,2CAEQ2zC,EAAYj2B,IAAM,QAMjC,IAAIxL,EADN,GAAIyhC,EAAYpkD,OAEW,UAArBokD,EAAYvsC,KACd8K,EAAayhC,EAAYpkD,OAAOqX,iBAAiB,GACnB,UAArB+sC,EAAYvsC,OACrB8K,EAAayhC,EAAYpkD,OAAOuX,iBAAiB,IAE/CoL,GAEEg+F,GAAe,OAA8B,UAArBv8D,EAAYvsC,OACnCusC,EAAYM,uBAAuB,GAAGjC,MACzC2B,EAAYM,uBAAuB,GAAGjC,IAAM,CAC1Ch/C,KAAM2gD,EAAYM,uBAAuB,GAAGjhD,KAAO,IAO3D,IAAI27G,EAAqBH,EACrB76D,EAAY86D,kBACZ96D,EAAY+6D,qBAEHC,EAAmB59D,OAAOryC,QAAO,SAAS/jC,GACrD,MAAgC,QAAzBA,EAAEE,KAAK8V,iBACbpM,QACYovE,EAAYM,uBAAuB,GAAGjC,YAC5C2B,EAAYM,uBAAuB,GAAGjC,IAG/ChyC,GAAO0zC,EAAkBC,EAAag7D,EAClC,SAAUh7D,EAAYpkD,OAAQ+jC,EAAGw+E,WACjCn+D,EAAYvB,gBACZuB,EAAYvB,eAAeI,cAC7BxyC,GAAO,wBAIX,IAAI3C,EAAO,IAAInjC,EAAOypF,sBAAsB,CAC1C1uE,KAAM,SACN+qB,IAAKA,IAEP,OAAOgL,QAAQC,QAAQ5N,IAGzB0L,EAAkB7sC,UAAU6iF,gBAAkB,SAAStiD,GACrD,IACI4wC,EADA/Z,EAAKtlD,KAET,OAAIyuB,QAA2CluB,IAA5BkuB,EAAUghD,gBACzBhhD,EAAUyhD,OACLlzC,QAAQE,OAAO,IAAIrmC,UAAU,qCAI/B,IAAImmC,SAAQ,SAASC,EAASC,GACnC,IAAKooB,EAAG09E,mBACN,OAAO9lG,EAAO2kG,EAAU,oBACpB,2DACC,GAAKpzG,GAAqC,KAAxBA,EAAUA,UAe5B,CACL,IAAIghD,EAAgBhhD,EAAUghD,cAC9B,GAAIhhD,EAAUyhD,OACZ,IAAK,IAAI5jF,EAAI,EAAGA,EAAIg5D,EAAGsmC,aAAar1F,OAAQjK,IAC1C,GAAIg5D,EAAGsmC,aAAat/F,GAAGojD,MAAQjhB,EAAUyhD,OAAQ,CAC/CT,EAAgBnjF,EAChB,MAIN,IAAIq5E,EAAcrgB,EAAGsmC,aAAanc,GAClC,IAAI9J,EAqCF,OAAOzoC,EAAO2kG,EAAU,iBACpB,8BArCJ,GAAIl8D,EAAYghE,SACd,OAAO1pG,IAET,IAAI/N,EAAOliC,OAAOuY,KAAKkpB,EAAUA,WAAWl4B,OAAS,EACjDsoE,EAASY,eAAehxC,EAAUA,WAAa,GAEnD,GAAsB,QAAlBS,EAAKxmB,WAAqC,IAAdwmB,EAAKvB,MAA4B,IAAduB,EAAKvB,MACtD,OAAOsP,IAGT,GAAI/N,EAAKN,WAAgC,IAAnBM,EAAKN,UACzB,OAAOqO,IAIT,IAAsB,IAAlBwyC,GAAwBA,EAAgB,GACxC9J,EAAY67D,eAAiBl8E,EAAGsmC,aAAa,GAAG41C,gBAC7CD,EAAkB57D,EAAY67D,aAActyG,GAC/C,OAAOgO,EAAO2kG,EAAU,iBACpB,8BAKR,IAAIwG,EAAkB55G,EAAUA,UAAUowB,OACJ,IAAlCwpF,EAAgBzkI,QAAQ,QAC1BykI,EAAkBA,EAAgBjzH,OAAO,KAE3CiqD,EAAWR,EAASS,iBAAiBha,EAAG09E,mBAAmBhxG,MAClDy9C,IAAkB,MACtBvgD,EAAKjoB,KAAOohI,EAAkB,qBAC7B,OACN/iF,EAAG09E,mBAAmBhxG,IAClB6sC,EAASO,eAAe9Z,EAAG09E,mBAAmBhxG,KAC9CqtC,EAASl8D,KAAK,SA5DpB,IAAK,IAAIjK,EAAI,EAAGA,EAAIosD,EAAGsmC,aAAar1F,SAC9B+uD,EAAGsmC,aAAa1yF,GAAGytI,WAGvBrhF,EAAGsmC,aAAa1yF,GAAGsoI,aAAaI,mBAAmB,KACnDviE,EAAWR,EAASS,iBAAiBha,EAAG09E,mBAAmBhxG,MAClD94B,IAAM,0BACfosD,EAAG09E,mBAAmBhxG,IAClB6sC,EAASO,eAAe9Z,EAAG09E,mBAAmBhxG,KAC9CqtC,EAASl8D,KAAK,KACdmiD,EAAG49E,cAVmChqI,KAkE9C+jC,QAIJlC,EAAkB7sC,UAAUqiH,SAAW,SAAS+3B,GAC9C,GAAIA,GAAYA,aAAoBp8I,EAAOq8I,iBAAkB,CAC3D,IAAIC,EAAmB,KAUvB,GATAxoI,KAAK4rF,aAAax8E,SAAQ,SAASu2D,GAC7BA,EAAYI,WACZJ,EAAYI,UAAUznC,QAAUgqG,EAClCE,EAAmB7iE,EAAYI,UACtBJ,EAAYK,aACnBL,EAAYK,YAAY1nC,QAAUgqG,IACpCE,EAAmB7iE,EAAYK,iBAG9BwiE,EACH,MAAM3G,EAAU,qBAAsB,qBAExC,OAAO2G,EAAiBj4B,WAG1B,IAAI71B,EAAW,GASf,OARA16E,KAAK4rF,aAAax8E,SAAQ,SAASu2D,GACjC,CAAC,YAAa,cAAe,cAAe,eACxC,iBAAiBv2D,SAAQ,SAASs/E,GAC5B/oB,EAAY+oB,IACdhU,EAASx3E,KAAKyiE,EAAY+oB,GAAQ6hB,kBAIrCvzE,QAAQ+K,IAAI2yC,GAAUhgD,MAAK,SAAS+tG,GACzC,IAAIjvC,EAAU,IAAIj6E,IAMlB,OALAkpH,EAASr5H,SAAQ,SAASoX,GACxBA,EAAMpX,SAAQ,SAAS4xG,GACrBxnB,EAAQp4E,IAAI4/F,EAAK95G,GAAI85G,SAGlBxnB,MAKO,CAAC,eAAgB,iBAAkB,iBACnD,kBAAmB,oBACTpqF,SAAQ,SAASs5H,GAC3B,IAAI3yI,EAAM7J,EAAOw8I,GACjB,GAAI3yI,GAAOA,EAAI7H,WAAa6H,EAAI7H,UAAUqiH,SAAU,CAClD,IAAIo4B,EAAiB5yI,EAAI7H,UAAUqiH,SACnCx6G,EAAI7H,UAAUqiH,SAAW,WACvB,OAAOo4B,EAAe/mG,MAAM5hC,MAC3B06B,MAAK,SAASkuG,GACb,IAAIC,EAAW,IAAItpH,IAKnB,OAJAvyB,OAAOuY,KAAKqjI,GAAax5H,SAAQ,SAASlI,GA1uDpD,IAAsB85G,EA2uDV4nB,EAAY1hI,GAAID,KA1uDnB,CACL6hI,WAAY,cACZC,YAAa,eACbC,cAAe,iBACfC,eAAgB,kBAChBC,gBAAiB,qBANCloB,EA2uD0B4nB,EAAY1hI,IApuDnDD,OAAS+5G,EAAK/5G,KAquDX4hI,EAASznH,IAAIla,EAAI0hI,EAAY1hI,OAExB2hI,UAOf,IAAIpuC,EAAU,CAAC,cAAe,gBA8D9B,OA7DAA,EAAQrrF,SAAQ,SAASs/E,GACvB,IAAIy6C,EAAepuG,EAAkB7sC,UAAUwgG,GAC/C3zD,EAAkB7sC,UAAUwgG,GAAU,WACpC,IAAIvsD,EAAOhhC,UACX,MAAuB,mBAAZghC,EAAK,IACO,mBAAZA,EAAK,GACPgnG,EAAavnG,MAAM5hC,KAAM,CAACmB,UAAU,KAC1Cu5B,MAAK,SAASooC,GACU,mBAAZ3gC,EAAK,IACdA,EAAK,GAAGP,MAAM,KAAM,CAACkhC,OAEtB,SAAS99D,GACa,mBAAZm9B,EAAK,IACdA,EAAK,GAAGP,MAAM,KAAM,CAAC58B,OAIpBmkI,EAAavnG,MAAM5hC,KAAMmB,gBAIpCs5F,EAAU,CAAC,sBAAuB,uBAAwB,oBAClDrrF,SAAQ,SAASs/E,GACvB,IAAIy6C,EAAepuG,EAAkB7sC,UAAUwgG,GAC/C3zD,EAAkB7sC,UAAUwgG,GAAU,WACpC,IAAIvsD,EAAOhhC,UACX,MAAuB,mBAAZghC,EAAK,IACO,mBAAZA,EAAK,GACPgnG,EAAavnG,MAAM5hC,KAAMmB,WAC/Bu5B,MAAK,WACmB,mBAAZyH,EAAK,IACdA,EAAK,GAAGP,MAAM,SAEf,SAAS58B,GACa,mBAAZm9B,EAAK,IACdA,EAAK,GAAGP,MAAM,KAAM,CAAC58B,OAIpBmkI,EAAavnG,MAAM5hC,KAAMmB,eAMpC,CAAC,YAAYiO,SAAQ,SAASs/E,GAC5B,IAAIy6C,EAAepuG,EAAkB7sC,UAAUwgG,GAC/C3zD,EAAkB7sC,UAAUwgG,GAAU,WACpC,IAAIvsD,EAAOhhC,UACX,MAAuB,mBAAZghC,EAAK,GACPgnG,EAAavnG,MAAM5hC,KAAMmB,WAC/Bu5B,MAAK,WACmB,mBAAZyH,EAAK,IACdA,EAAK,GAAGP,MAAM,SAIbunG,EAAavnG,MAAM5hC,KAAMmB,eAI7B45B,I,8BC/zDT,oOA2BA,MAAM9c,EAASF,oBAAUM,GA4CV,SAASqoB,EAChBuzB,EACA/yD,EACA+kE,EACApmC,EACA9f,EACA+f,EACA3+B,GAUJnH,KAAKopI,uBAAgD,IAAxBjiI,EAAQymE,aAQrC5tE,KAAKqpI,iBAAc9oI,EAkBnBP,KAAKspI,gBAAkB,GAUvBtpI,KAAKupI,qBAAsB,EAO3BvpI,KAAKi6D,IAAMA,EAMXj6D,KAAKkH,GAAKA,EAOVlH,KAAK8lC,MAAQA,EASb9lC,KAAKwnC,aAAe,IAAIjoB,IAMxBvf,KAAKgjC,YAAc,IAAIzjB,IAQvBvf,KAAKurF,cAAgB,GAoBrBvrF,KAAKsrF,WAAa,IAAI/rE,IAKtBvf,KAAKwpI,WAAa,KAKlBxpI,KAAKypI,YAAc,KAMnBzpI,KAAKisE,eAAiBA,EAGtBjsE,KAAK0pI,sBAAwB1pI,KAAK0pI,sBAAsB57I,KAAKkS,MAC7DA,KAAKisE,eAAetqD,GAChBm8G,IACA99H,KAAK0pI,uBAET1pI,KAAK2pI,kBAAoB3pI,KAAK2pI,kBAAkB77I,KAAKkS,MACrDA,KAAKisE,eAAetqD,GAChBm8G,IACA99H,KAAK2pI,mBACT3pI,KAAKmH,QAAUA,EAIf,MAAMyiI,EAAkB7jH,GAAe,GAEvC6jH,EAAgBn0G,SAAWm0G,EAAgBn0G,UAAY,GAInDr/B,MAAMC,QAAQuzI,EAAgBn0G,UAC9Bm0G,EAAgBn0G,SAASvyB,KAAK,CAAE2mI,eAAgB7pI,KAAK8lC,QAErD7nB,EAAO9Y,KAAK,iEAGhBnF,KAAKghB,eACC,IAAImZ,IAASW,sBAAsB+K,EAAW+jG,GAIpD,MAAME,EAAwB,CAC1BhgD,IA7MW,IA8MXE,SA7MW,IA8MXH,KAhNW,MAqNf7pF,KAAK2pF,cAAgB3pF,KAAKmH,QAAQkmE,cAAgBrtE,KAAKmH,QAAQkmE,aAAa08D,iBACtE/pI,KAAKmH,QAAQkmE,aAAa08D,iBAC1BD,EAEN9pI,KAAKgqI,SAAW,IAAItgD,IAAS1pF,KAAMA,KAAK2pF,eACxC3pF,KAAKy1H,UAAY,GACjBz1H,KAAKwmB,MAAQ,GACbxmB,KAAKiqI,cAAgB,KAMrBjqI,KAAKmtE,SAAWhmE,EAAQgmE,SAExBntE,KAAKkqI,QAAU,IAAIC,IACnB,MAAMC,EAAYhtH,EAAQ,KAE1Bpd,KAAK0qF,UAAY,IAAI0/C,EACjB,CACIC,YAAa5gD,IAAelzF,OAC5B+zI,wBAAwB,EACxB77D,gBAAiB3uD,IAAQ2uD,oBAEjCzuE,KAAKuqI,eAAiB,IAAIC,IAAexqI,KAAKtJ,YAO9CsJ,KAAKyqI,eAAiB,IAAIC,IAAe1qI,MAMzCA,KAAKwf,aAAey6C,EAAIz6C,aACxBxf,KAAK2qI,YAAc,IAAIC,IAKvB5qI,KAAK6qI,qBAAuB,KAG5B,MAAMC,EAAmBt4G,GACA,iBAAVA,EACAxlC,OAAOo2B,OAAOgQ,GAAenC,KAAK1jC,GAASA,IAAUilC,EAAM7vB,eAG/D,KAIX,IAAI4qE,EAAiBu9D,EACjB9qI,KAAKmH,QAAQomE,gBAAmBvtE,KAAKmH,QAAQumE,YAAct6C,QAK3DtT,IAAQsQ,aAAem9C,IAAmBn6C,QAC1Cm6C,EAAiB,MAIrB,IAAID,EAAgBw9D,EAChB9qI,KAAKmH,QAAQmmE,eAAkBttE,KAAKmH,QAAQsmE,aAAer6C,QAI3Dk6C,IAAkBl6C,QAClBnV,EAAO9Y,KAAK,uDACZmoE,EAAgB,OAGhBC,GAAkBD,KAElBttE,KAAK+qI,gBAAkB,CACnBvpG,OAA0B,OAAlB8rC,EACR3hD,UAAW4S,IACXo+D,SAAUrvB,GAAgCC,IAKlDvtE,KAAKy+H,MAAQ,CAACuM,EAAM9lI,KAChB+Y,EAAOhZ,MAAM+lI,EAAM9lI,GAEnBlF,KAAKy1H,UAAUvyH,KAAK,CAChBwuF,KAAM,IAAIvpF,KACVlB,KAAM+jI,EACNz9I,MAAO2X,GAAQ,MAGvBlF,KAAK6tE,eAAiB,KACtB7tE,KAAKghB,eAAe6sD,eAAiBxmD,IACjCrnB,KAAKy+H,MACD,iBACA7xH,KAAKwL,UAAUiP,EAAMoH,UAAW,KAAM,MAEd,OAAxBzuB,KAAK6tE,gBACL7tE,KAAK6tE,eAAexmD,IAKxBvH,IAAQ01D,aACRx1E,KAAKghB,eAAegjH,YACd38G,GAASrnB,KAAKirI,mBAAmB5jH,EAAM9F,QAC7CvhB,KAAKghB,eAAekjH,eACd78G,GAASrnB,KAAKkrI,qBAAqB7jH,EAAM9F,SAE/CvhB,KAAKghB,eAAeijH,QAAU58G,IAC1B,MAAM9F,EAAS8F,EAAMyW,QAAQ,GAE7B99B,KAAKmrI,kBAAkB5pH,EAAQ8F,EAAMiX,MAAOjX,EAAMs+C,aAClDpkD,EAAO6pH,cAAgBxF,IACnB5lI,KAAKqrI,oBAAoB9pH,EAAQqkH,EAAItnG,SAIjDt+B,KAAKguE,uBAAyB,KAC9BhuE,KAAKghB,eAAegtD,uBAAyB3mD,IACzCrnB,KAAKy+H,MAAM,yBAA0Bz+H,KAAKiuE,gBACN,OAAhCjuE,KAAKguE,wBACLhuE,KAAKguE,uBAAuB3mD,IAGpCrnB,KAAKmuE,2BAA6B,KAClCnuE,KAAKghB,eAAemtD,2BAA6B9mD,IAC7CrnB,KAAKy+H,MAAM,6BAA8Bz+H,KAAKouE,oBACN,OAApCpuE,KAAKmuE,4BACLnuE,KAAKmuE,2BAA2B9mD,IAGxCrnB,KAAKuuE,oBAAsB,KAC3BvuE,KAAKghB,eAAeutD,oBAAsBlnD,IACtCrnB,KAAKy+H,MAAM,uBACsB,OAA7Bz+H,KAAKuuE,qBACLvuE,KAAKuuE,oBAAoBlnD,IAGjCrnB,KAAKqkI,cAAgB,KACrBrkI,KAAKghB,eAAeqjH,cAAgBh9G,IAChCrnB,KAAKy+H,MAAM,iBACgB,OAAvBz+H,KAAKqkI,eACLrkI,KAAKqkI,cAAch9G,IAIvBrnB,KAAKmtE,WACLntE,KAAKiqI,cAAgB/9I,OAAOowC,YAAY,KACpCt8B,KAAKuwG,SAAS/pF,IACV,GAAIA,EAAM/gB,QACqB,mBAAjB+gB,EAAM/gB,OAAuB,CACvC,MAAM+zF,EAAUhzE,EAAM/gB,SAEtB,IAAK,IAAInZ,EAAI,EAAGA,EAAIktG,EAAQjjG,SAAUjK,EAAG,CACrC,MAAM2xC,EAAMu7D,EAAQltG,GAEpB2xC,EAAI34B,QAAQ8J,QAAQviB,IAChBmT,KAAKsrI,aAAartG,EAAKpxC,EAAMoxC,EAAI+iF,KAAKn0H,YAI9C25B,EAAMpX,QAAQhiB,GAAK4S,KAAKsrI,aAAal+I,EAAG,GAAIA,KAEjD,SAIJ,MAGP6wB,EAAO/Y,KAAM,cAAalF,MAY9B0mC,EAAwBx4C,UAAUo9I,aAC5B,SAASpmF,EAAQr4D,EAAM0+I,GACrB,MAAMrkI,EAAM,GAAEg+C,EAAOh+C,MAAMra,IAC3B,IAAIwB,EAAI2R,KAAKwmB,MAAMtf,GACnB,MAAMiN,EAAM,IAAIhM,KAEX9Z,IACD2R,KAAKwmB,MAAMtf,GAAM7Y,EAAI,CACjBstG,UAAWxnF,EACXq3H,QAASr3H,EACTiP,OAAQ,GACRs2G,MAAO,KAGfrrI,EAAE+0B,OAAOlgB,KAAKqoI,GACdl9I,EAAEqrI,MAAMx2H,KAAKiR,EAAI/L,WACb/Z,EAAE+0B,OAAO7sB,OAASyJ,KAAKmtE,WACvB9+E,EAAE+0B,OAAOsK,QACTr/B,EAAEqrI,MAAMhsG,SAEZr/B,EAAEm9I,QAAUr3H,GAMpB,MAAMs3H,EAAU,SAAS3oE,GACrB,OAAI,MAAOA,EACA,GAGH,SAAQA,EAAY77D,WAAW67D,EAAY9wC,OAYvD0U,EAAwBx4C,UAAUmrF,mBAAqB,WACnD,MAAMpsC,EAAQjtC,KAAKghB,eAAeotD,mBAElC,MAAc,cAAVnhC,EACO,YAGJA,GAaXvG,EAAwBx4C,UAAUw9I,0BAA4B,SACtD//G,GACJ,IAAIggH,GAAsB,EAO1B,OALIhgH,IAAc4S,IACdotG,EAAsB3rI,KAAKopI,oBACpBz9G,IAAc4S,MACrBotG,EAAsB3rI,KAAKupI,qBAE3BoC,EACO3rI,KAAK4rI,mBAAmBjgH,GAAa,WAAa,WAGtD,YAQX+a,EAAwBx4C,UAAUo8F,cAAgB,WAC9C,OAAQtqF,KAAKmH,QAAQqmE,kBASzB9mC,EAAwBx4C,UAAUw7I,sBAAwB,SAClD1tE,EACAx9B,GAEJ,IAAKw9B,EAGD,YAFA/9C,EAAOjZ,MAAO,yCAAwChF,MAI1D,MAAMu4D,EAAav4D,KAAKunC,gBAAgBy0B,EAAYz9B,KAEhDg6B,EAAWhiE,QAEXgiE,EAAW,GAAGszE,cAAcrtG,IAWpCkI,EAAwBx4C,UAAUy7I,kBAAoB,SAC9C3tE,EACArwC,EACA2yC,GAEJ,IAAKtC,EAGD,YAFA/9C,EAAOjZ,MAAM,uCAIjB,MAAMs5B,EAAQt+B,KAAKunC,gBAAgBy0B,EAAYrwC,GAE3C2S,EAAM/nC,QAEN+nC,EAAM,GAAGwtG,QAAQxtE,IAWzB53B,EAAwBx4C,UAAUwyH,eAAiB,WAC/C,MAAMD,EAAc,GAgBpB,OAfuBzgH,KAAKghB,eAAeskH,eACtC50G,OAAO0a,GAAYA,EAAS9M,OAAS8M,EAAS9M,MAAMlF,OAASmF,KAEnDnvB,QAAQ0yG,IACnB,MAAM98F,EAAO88F,EAAOiqB,4BAEhB/mH,GAAQA,EAAKzuB,SAKbkqH,EAAYz7F,EAAK,GAAG+F,QAAU/F,EAAK,GAAG2jB,cAIvC83E,GASX/5E,EAAwBx4C,UAAUg5C,eAAiB,SAASvb,GACxD,IAAI0b,EAASjxC,MAAMO,KAAKqJ,KAAKgjC,YAAY5f,UAMzC,YAJkB7iB,IAAdorB,IACA0b,EAASA,EAAO3W,OAAO4N,GAASA,EAAMgJ,YAAc3b,IAGjD0b,GAQXX,EAAwBx4C,UAAU84C,mBAAqB,WACnD,OAAOhnC,KAAKknC,eAAe3I,KAAiB,IAShDmI,EAAwBx4C,UAAU09I,mBAAqB,SAASjgH,GAC5D,IAAKA,EACD,MAAM,IAAIt0B,MAAM,2BAGpB,OAAO2I,KAAKknC,eAAevb,GAAWp1B,OAAS,GAUnDmwC,EAAwBx4C,UAAUq5C,gBAAkB,SAC5Cy0B,EACArwC,GACJ,MAAM6b,EAAe,GACfwkG,EACAhwE,EAAa,CAAEA,GAAeh8D,KAAKwnC,aAAajiC,OAEtD,IAAK,MAAM66H,KAAY4L,EAAW,CAC9B,MAAMC,EAAmBjsI,KAAKwnC,aAAar6C,IAAIizI,GAE/C,GAAK6L,EAOL,IAAK,MAAMpnD,KAAkBonD,EAAiB1mI,OAE1C,IAAKomB,GAAaA,IAAck5D,EAAgB,CAC5C,MAAMqnD,EAAaD,EAAiB9+I,IAAI03F,GAEpCqnD,GACA1kG,EAAatkC,KAAKgpI,IAMlC,OAAO1kG,GASXd,EAAwBx4C,UAAU06C,eAAiB,SAAS5jB,GACxD,GAAoB,iBAATA,EACP,MAAM,IAAI3tB,MAAO,QAAO2tB,qBAE5B,IAAK,MAAMkf,KAAclkC,KAAKgjC,YAAY5f,SACtC,GAAIpjB,KAAKstG,aAAappE,KAAgBlf,EAClC,OAAOkf,EAGf,IAAK,MAAMw4B,KAAe18D,KAAKunC,kBAC3B,GAAIm1B,EAAY6wC,YAAcvoF,EAC1B,OAAO03C,EAIf,OAAO,MASXh2B,EAAwBx4C,UAAU+3H,iBAAmB,SAAS/+G,GAE1D,MAAMilI,EAAgB7tG,GAASA,EAAMqnD,WAAWz+E,KAAOA,EACjDg9B,EAAalkC,KAAKknC,iBAAiBjW,KAAKk7G,GAE9C,GAAIjoG,EACA,OAAOlkC,KAAKstG,aAAappE,GAG7B,MAAMw4B,EAAc18D,KAAKunC,kBAAkBtW,KAAKk7G,GAEhD,OAAIzvE,EACOA,EAAY6wC,UAGhB,MAOX7mE,EAAwBx4C,UAAU+8I,mBAAqB,SAAS1pH,GAC5D,MAAMinB,EAAW1F,IAAI3H,YAAY5Z,GAEjC,IAAKuhB,IAAIyF,iBAAiBC,GAKtB,YAJAvqB,EAAO/Y,KACAlF,KAAF,+DACSwoC,GAMd1oB,IAAQub,oBACR9Z,EAAO6qH,WAAa/kH,IAChBrnB,KAAKmrI,kBAAkB5pH,EAAQ8F,EAAMiX,QAEzC/c,EAAO6pH,cAAgB/jH,IACnBrnB,KAAKqrI,oBAAoB9pH,EAAQ8F,EAAMiX,SAK/C,MAAM+tG,EAAoB9qH,EAAOqX,iBAEjC,IAAK,MAAMgP,KAAcykG,EACrBrsI,KAAKmrI,kBAAkB5pH,EAAQqmB,GAEnC,MAAM0kG,EAAoB/qH,EAAOuX,iBAEjC,IAAK,MAAMy/B,KAAc+zE,EACrBtsI,KAAKmrI,kBAAkB5pH,EAAQg3C,IAgBvC7xB,EAAwBx4C,UAAUi9I,kBAAoB,SAAS5pH,EAAQ+c,EAAOqnC,EAAc,MACxF,MAAMn9B,EAAW1F,IAAI3H,YAAY5Z,GAC3BoK,EAAY2S,EAAMlF,KAExB,IAAKp5B,KAAK8lC,QAAUhD,IAAIyF,iBAAiBC,GAKrC,YAJAvqB,EAAO/Y,KACAlF,KAAF,+DACSwoC,GAOlB,GAHAvqB,EAAO/Y,KAAQlF,KAAF,uBAA8BwoC,EAAU7c,IAGhDA,EAOD,YANAiM,mBACI,IAAIvgC,MACC,oDAAmDmxC,IAOhE,MAAM+jG,EAAYzsH,IAAQ01D,YACpB,IAAItmC,IAAIlvC,KAAKwuE,kBAAkBx8C,KAC/B,IAAIkd,IAAIlvC,KAAKghB,eAAewtD,kBAAkBx8C,KACpD,IAAIw6G,EAEJ,GAAI1sH,IAAQ2uD,kBACR,GAAI9I,GAAeA,EAAYj2B,IAAK,CAChC,MAAMA,EAAMi2B,EAAYj2B,IAExB88F,EAAaD,EAAU9+G,MAAMiD,OAAO+7G,GAAOhgH,IAAQO,SAASy/G,EAAM,SAAQ/8F,SAE1E88F,EAAaD,EAAU9+G,MAAMiD,OAAO+7G,IAChC,MAAM3qF,EAAOr1B,IAAQO,SAASy/G,EAAK,UAEnC,YAAuB,IAAT3qF,GAAwBtZ,IAAasZ,EAAK30B,UAAU,GAAGpqB,MAAM,KAAK,UAIxFypI,EAAaD,EAAU9+G,MAAMiD,OAAO+7G,GAAOA,EAAIt6G,WAAY,KAAIxG,IAGnE,IAAK6gH,EAAWj2I,OAQZ,YAPAqhC,mBACI,IAAIvgC,MACC,2BACGs0B,2CACA6c,MAMhB,IAAIiuC,EAAYhqD,IAAQuD,UAAUw8G,EAAW,GAAI,WAIjD,GAFA/1D,EACMA,EAAU/lD,OAAOlsB,IAA8C,IAAtCA,EAAKZ,QAAS,QAAO4kC,KAC/CiuC,EAAUlgF,OAOX,YANAqhC,mBACI,IAAIvgC,MACC,8BACGmxC,mCAA0C7c,MAQ1D,MAAMmG,EAAU2kD,EAAU,GAAGtpD,UAAU,GAAGpqB,MAAM,KAAK,GAC/C2pI,EAAYnzH,OAAOuY,GACnB66G,EAAkB3sI,KAAKisE,eAAe+xD,aAAa0O,GAEzD,GAAIhzH,MAAMgzH,IAAcA,EAAY,EAOhC,YANA90G,mBACI,IAAIvgC,MACC,iBAAgBy6B,6BACb0W,iBAAwB7c,MAIjC,IAAKghH,EAQR,YAPA/0G,mBACI,IAAIvgC,MACC,4BACGq1I,6BACAlkG,iBAAwB7c,MAMxC1N,EAAOrZ,IAAO5E,KAAF,mBAA0B2sI,EAAiBD,GAEvD,MAAME,EACA5sI,KAAKisE,eAAe8xD,iBAAiB4O,EAAiBhhH,GAE5D,IAAKihH,EAMD,YALAh1G,mBACI,IAAIvgC,MACC,GAAE2I,0CACC2sI,MAKhB,MAAM7nH,EAAQ8nH,EAAc9nH,MACtB0Z,EAAYouG,EAAcpuG,UAEhCx+B,KAAK6sI,mBACDF,EAAiBprH,EAAQ+c,EAAO3S,EAAW6S,EAAWkuG,EAAW5nH,IAkBzE4hB,EAAwBx4C,UAAU2+I,mBAAqB,SAC/CF,EACAprH,EACA+c,EACA3S,EACA6S,EACAxZ,EACAF,GACJ,IAAIgoH,EAAkB9sI,KAAKwnC,aAAar6C,IAAIw/I,GAEvCG,IACDA,EAAkB,IAAIvtH,IACtBvf,KAAKwnC,aAAapmB,IAAIurH,EAAiBG,IAG3C,MAAMC,EAAgBD,EAAgB3/I,IAAIw+B,GAS1C,GAJIohH,GAAiBjtH,IAAQyV,YACzBv1B,KAAKqrI,oBAAoB0B,EAActnD,oBAAqBsnD,EAAcpnD,YAG1EonD,GAAiBA,EAAcpnD,aAAernD,EAO9C,YAJArgB,EAAO/Y,KACAlF,KAAF,qDACM,GAAE2sI,MAAoBhhH,KAG1BohH,GACP9uH,EAAOjZ,MAAO,GAAEhF,qCAAqC2sI,KAAmBhhH,KAG5E,MAAM+wC,EACA,IAAIswE,IACEhtI,KAAKi6D,IACLj6D,KAAKi6D,IAAI13C,WACToqH,EACAprH,EACA+c,EACA3S,EACA6S,EACAxZ,EACAF,EACA9kB,KAAK8lC,OAEjBgnG,EAAgB1rH,IAAIuK,EAAW+wC,GAE/B18D,KAAKwf,aAAa0D,KAAK8V,IAAU3Q,mBAAoBq0C,EAAa18D,OAUtE0mC,EAAwBx4C,UAAUg9I,qBAAuB,SAAS3pH,GAC9D,IAAKuhB,IAAImqG,aAAa1rH,GAAS,CAC3B,MAAMra,EAAK47B,IAAI3H,YAAY5Z,GAK3B,YAHAtD,EAAO/Y,KACF,6DAA4DgC,GAMrE,MAAMolI,EAAoB/qH,EAAOuX,iBAEjC,IAAK,MAAMy/B,KAAc+zE,EACrBtsI,KAAKqrI,oBAAoB9pH,EAAQg3C,GAErC,MAAM8zE,EAAoB9qH,EAAOqX,iBAEjC,IAAK,MAAMgP,KAAcykG,EACrBrsI,KAAKqrI,oBAAoB9pH,EAAQqmB,IAWzClB,EAAwBx4C,UAAUm9I,oBAAsB,SAChD9pH,EACA+c,GACJ,MAAMkK,EAAW1F,IAAI3H,YAAY5Z,GAC3BmlE,EAAUpoD,GAASwE,IAAI1H,WAAWkD,GAExCrgB,EAAO/Y,KAAM,GAAElF,gCAAgCwoC,MAAak+C,KAEvDl+C,EAOAk+C,EAOA1mF,KAAKktI,uBAAuB1kG,EAAUk+C,IAWvCzoE,EAAO9Y,KACF,GAAEnF,0CAA0CwoC,8BAChCk+C,KAnBjB9uD,mBACI,IAAIvgC,MAAS2I,KAAF,+CARf43B,mBACI,IAAIvgC,MAAS2I,KAAF,iDAqCvB0mC,EAAwBx4C,UAAUi/I,oBAAsB,SAChD3kG,EACAk+C,GAEJ,IAAK,MAAMulD,KAAoBjsI,KAAKwnC,aAAapkB,SAC7C,IAAK,MAAM8oH,KAAcD,EAAiB7oH,SAGtC,GAAI8oH,EAAWxmD,eAAiBl9C,GACzB0jG,EAAWrmD,cAAgBa,EAC9B,OAAOwlD,GAiBvBxlG,EAAwBx4C,UAAUg6C,mBAAqB,SAASC,GAC5D,MAAMC,EAAgB,GAChB0kG,EAAkB9sI,KAAKwnC,aAAar6C,IAAIg7C,GAE9C,GAAI2kG,EAAiB,CACjB,MAAMM,EAAoBN,EAAgB3/I,IAAIoxC,KACxC8uG,EAAoBP,EAAgB3/I,IAAIoxC,KAE9C6uG,GAAqBhlG,EAAcllC,KAAKkqI,GACxCC,GAAqBjlG,EAAcllC,KAAKmqI,GAExCrtI,KAAKwnC,aAAa9jB,OAAOykB,GAO7B,OAJAlqB,EAAOhZ,MACF,GAAEjF,kCAAkCmoC,YACjCC,EAAc7xC,UAEf6xC,GAQX1B,EAAwBx4C,UAAUo/I,mBAAqB,SAASC,GAC5DA,EAAYvqH,UACZ,MAAMmI,EAAgBoiH,EAAY5wE,mBAC5BmwE,EAAkB9sI,KAAKwnC,aAAar6C,IAAIg+B,GAEzC2hH,EAGOA,EAAgBppH,OAAO6pH,EAAYjmG,YAC3CrpB,EAAOjZ,MACF,oBAAmBuoI,gCAJxBtvH,EAAOjZ,MACF,+CAA8CmmB,GAKvDnrB,KAAKwf,aAAa0D,KAAK8V,IAAUzQ,qBAAsBglH,IAa3D7mG,EAAwBx4C,UAAUg/I,uBAAyB,SACnD1kG,EACAk+C,GACJ,MAAM6mD,EAAcvtI,KAAKmtI,oBAAoB3kG,EAAUk+C,GAMvD,OAJI6mD,GACAvtI,KAAKstI,mBAAmBC,GAGrBA,GA8GX,MAAMC,EAAiB,SAASn+G,GAC5B,GAAoB,iBAATA,GAA8B,OAATA,GACL,iBAAbA,EAAK2C,IAGf,OAFA/T,EAAO9Y,KAAK,mDAELkqB,EAIX,MAAM+zB,EAAYhmC,EAAQ,IACpBnF,EAAUmrC,EAAUlrC,MAAMmX,EAAK2C,UAEd,IAAZ/Z,QACyB,IAAlBA,EAAQwV,OACfr3B,MAAMC,QAAQ4hB,EAAQwV,QAC7BxV,EAAQwV,MAAMre,QAAQqiB,IASlB,MAAMg8G,EAAa,GACbC,EAAe,GAcrB,QAZgC,IAArBj8G,EAAMX,YACV16B,MAAMC,QAAQo7B,EAAMX,aACvBW,EAAMX,WAAW1hB,QAAQ8hB,SACU,IAApBA,EAAMC,WACU,QAApBD,EAAMC,gBACkB,IAAhBD,EAAMV,OACbi9G,EAAWvqI,KAAKqW,OAAO2X,EAAMV,MAAMztB,MAAM,KAAK,OAM1D3M,MAAMC,QAAQo7B,EAAMjB,OAAQ,CAC5B,IAAIlkC,EAEJ,IAAKA,EAAI,EAAGA,EAAImlC,EAAMjB,MAAMj6B,OAAQjK,IACF,iBAAnBmlC,EAAMjB,MAAMlkC,SACa,IAAtBmlC,EAAMjB,MAAMlkC,GAAG4a,IACtBumI,EAAW7pI,QAAQ6tB,EAAMjB,MAAMlkC,GAAG4a,KAAO,IAC5CwmI,EAAaxqI,KAAKuuB,EAAMjB,MAAMlkC,WACvBmlC,EAAMjB,MAAMlkC,IAI3B,IAAKA,EAAI,EAAGA,EAAImlC,EAAMjB,MAAMj6B,OAAQjK,SACF,IAAnBmlC,EAAMjB,MAAMlkC,IACnBohJ,EAAaxqI,KAAKuuB,EAAMjB,MAAMlkC,IAItCmlC,EAAMjB,MA0BtB,SAAuCimD,EAAY,IAC/C,IAAK32D,IAAQwX,aAAexX,IAAQ21B,qBAAqB,IACrD,OAAOghC,EAGX,IAAIk3D,EAAgB,IAAKl3D,GAsBzB,OApB2BA,EAAU/lD,OAAOiB,GACxCA,EAASr0B,YAAc,WAAaq0B,EAASpkC,QAAU,KACtDiY,IAAImsB,GAAYA,EAASzqB,IAEXkI,QAAQw+H,IAEvB,MAAMC,EAAYF,EAAc18G,KAAKzsB,GACjCA,EAAK0C,KAAO0mI,GAA6B,UAAnBppI,EAAKlH,WAE/BuwI,EAAUtgJ,MAAS,YAAWqgJ,EAG9BD,EACMA,EAAcj9G,OAAOlsB,GAAQA,EAAK0C,KAAO0mI,GAI/CD,EAAczqI,KAAK2qI,KAGhBF,EArDmBG,CAA8BJ,MAKxD,MAAMK,EAAS3qF,EAAUrL,MAAM9/B,GAG/B,OAAO,IAAI09D,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAK+7G,KA2FbrnG,EAAwBx4C,UAAUo/G,aAAe,SAASppE,GACtD,MAAMzT,EAAWzwB,KAAKguI,SAAS9pG,EAAWrB,OAE1C,OAAOpS,GAAYA,EAASD,MAAM,IAatCkW,EAAwBx4C,UAAU+/I,oCAC5B,SAAS5+G,GACP,MAAM2C,EAAMoxB,IAAUlrC,MAAMmX,EAAK2C,KAC3BiC,EAAQjC,EAAIvE,MAAMwD,KAAKnD,GAAwB,UAAfA,EAAM7mB,MAI5CgtB,EAAMnD,WAAamD,EAAMnD,YAAc,GACvC,MAAMo9G,EAAYj6G,EAAMnD,WAAWJ,OAAOQ,GAA6B,QAApBA,EAAMC,WAEzD,GAAI8C,EAAMy2D,WAAaz2D,EAAMw2D,aAAc,CACvC,MAAMj6D,EAAQ,GAad,GAXI09G,GAAaA,EAAU33I,OACvB23I,EAAU9+H,QAAQ8hB,IACdV,EAAMttB,KAAKguB,EAAMV,MAAMztB,MAAM,KAAK,MAGtCkxB,EAAMzD,MAAMphB,QAAQ4V,IACO,SAAnBA,EAAK1nB,WACLkzB,EAAMttB,KAAK8hB,EAAK9d,MAIxB+sB,EAAMnD,WAAWG,KAAKC,GAA6B,QAApBA,EAAMC,WAErC,OAAO9B,EAEX4E,EAAMnD,WAAW5tB,KAAK,CAClBiuB,UAAW,MACXX,MAAOA,EAAMrtB,KAAK,OAI1B,OAAO,IAAIwyE,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKoxB,IAAUrL,MAAM/lB,MAKjC,MAAMm8G,EAAU,CACZlgE,iBACI,OAAOjuE,KAAKghB,eAAeitD,gBAE/BG,qBACI,OAAOpuE,KAAKghB,eAAeotD,oBAE/BS,mBACI,IAAIx/C,EAAOrvB,KAAKghB,eAAe6tD,iBAE/B,OAAKx/C,GAMLrvB,KAAKy+H,MAAM,oCAAqCgN,EAAQp8G,IAGpDvP,IAAQ2uD,mBACRp/C,EAAOrvB,KAAKkqI,QAAQkE,QAAQ/+G,GAC5BrvB,KAAKy+H,MAAM,8CACPgN,EAAQp8G,IAEZA,EAAOrvB,KAAKiuI,oCAAoC5+G,GAChDrvB,KAAKy+H,MAAM,yDACPgN,EAAQp8G,MAERvP,IAAQ4qD,gCACRr7C,EAAOrvB,KAAKyqI,eAAe4D,mCAAmCh/G,GAC9DpR,EAAOhZ,MACH,uDAAwDoqB,IAWhEA,EA3IY,SAASw/C,EAAkB1nE,GAC/C,IAAK0nE,EACD,MAAM,IAAIx3E,MAAM,mCAGpB,MAAMi3I,EAAc,IAAIrrF,IAAiB4rB,EAAiB78C,KACpDu8G,EAAaD,EAAYjrF,YAAY,SAC3C,IAAI4rD,GAAU,EAEVs/B,GAAuC,aAAzBA,EAAW5+G,YACrBxoB,EAAQymE,YACR2gE,EAAW5+G,UAAY,WAEvB4+G,EAAW5+G,UAAY,WAG3Bs/E,GAAU,GAGd,MAAMu/B,EAAaF,EAAYjrF,YAAY,SAO3C,OALImrF,GAAuC,aAAzBA,EAAW7+G,YACzB6+G,EAAW7+G,UAAY,WACvBs/E,GAAU,GAGVA,EACO,IAAIt5B,sBAAsB,CAC7B1uE,KAAM4nE,EAAiB5nE,KACvB+qB,IAAKs8G,EAAY/qF,aAIlBsrB,EA0GQ4/D,CAAgBp/G,EAAMrvB,KAAKmH,UAItCkoB,EAAOrvB,KAAKyqI,eAAeiE,2BAA2Br/G,GAE/CA,IArCHpR,EAAOhZ,MAAM,iDAEN,KAqCfupE,oBACI,IAAIn/C,EAAOrvB,KAAKghB,eAAewtD,kBAE/B,OAAKn/C,GAKLrvB,KAAKy+H,MAAM,qCAAsCgN,EAAQp8G,IAGrDvP,IAAQ2uD,oBACRp/C,EAAOrvB,KAAKkqI,QAAQkE,QAAQ/+G,GAC5BrvB,KAAKy+H,MACD,+CAAgDgN,EAAQp8G,KAGzDA,IAbHpR,EAAOhZ,MAAM,mDAEN,MAq+BnB,SAAS0pI,EAAe7iC,GACpB,OAAOA,EAAe5kE,iBACjBy7C,KAAKn1F,GAAqB,WAAhBA,EAAEgxC,WAx9BrBxxC,OAAOuY,KAAK4oI,GAAS/+H,QAAQ89F,IACzBlgH,OAAOC,eACHy5C,EAAwBx4C,UACxBg/G,EAAM,CACF//G,IAAKghJ,EAAQjhC,OAKzBxmE,EAAwBx4C,UAAU8/I,SAAW,SAASnrG,GAClD,OAAO7iC,KAAKsrF,WAAWn+F,IAAI01C,IAY/B6D,EAAwBx4C,UAAU0gJ,iBAAmB,SAAS9rE,GAC1D,IAAK9iE,KAAK+qI,iBAAmBjrH,IAAQqzF,2BACjC,OAAOrwC,EAGX,MAAMonB,EAAY9mC,IAAUlrC,MAAM4qD,EAAY9wC,KACxCP,EAAQy4D,EAAUz8D,MAAMwD,KAAKvkC,GAAKA,EAAEua,OAASjH,KAAK+qI,gBAAgBp/G,WAiBxE,OAfI3rB,KAAK+qI,gBAAgBvpG,QACrB/U,IAAQ2F,YAAYX,EAAOzxB,KAAK+qI,gBAAgBpuC,UAO5C38F,KAAK+qI,gBAAgBpuC,WAAavpE,QAAsBtT,IAAQC,iBAAmB/f,KAAK8lC,OACxFrZ,IAAQsG,WAAWtB,EAAOzxB,KAAK+qI,gBAAgBpuC,UAAU,IAG7DlwE,IAAQsG,WAAWtB,EAAOzxB,KAAK+qI,gBAAgBpuC,UAG5C,IAAIhnB,sBAAsB,CAC7B1uE,KAAM67D,EAAY77D,KAClB+qB,IAAKoxB,IAAUrL,MAAMmyC,MAU7BxjD,EAAwBx4C,UAAU2gJ,cAAgB,SAASvwG,GACvD,GAAIA,EAAM/Y,UACN,OAAOvlB,KAAKgjC,YAAYjf,IAAIua,EAAMuE,OAGtC,MAAM1X,EAAgBmT,EAAMq+B,mBACtBmwE,EAAkB9sI,KAAKwnC,aAAar6C,IAAIg+B,GAE9C,OAAOwN,QAAQm0G,GAAmBA,EAAgB3/I,IAAImxC,EAAMgJ,aAAehJ,IAS/EoI,EAAwBx4C,UAAUiwC,SAAW,SAASG,EAAO8sC,GAAc,GACvE,MAAMvoC,EAAQvE,EAAMuE,MAIpB,GAFA5kB,EAAO/Y,KAAM,OAAMo5B,SAAat+B,QAE5BA,KAAKgjC,YAAYjf,IAAI8e,GAErB,OAAO7F,QAAQE,OAAO,IAAI7lC,MAAO,GAAEinC,mBAAuBt+B,SAM9D,GAHAA,KAAKgjC,YAAY5hB,IAAIyhB,EAAOvE,GAGxBxe,IAAQ2uD,mBAAqBzuE,KAAK8lC,MAClC9lC,KAAKgqI,SAAS7rG,SAASG,EAAO8sC,OAC3B,CAMH,MAAM0jE,EAAexwG,EAAMmnD,oBAE3B,GAAIqpD,EACA9uI,KAAK+uI,WAAWD,QAGb,IAAKhvH,IAAQ4qD,+BACLpsC,EAAMuK,gBACLvK,EAAMqsC,iBAAmBrsC,EAAMggC,UAC3C,OAAOthC,QAAQE,OAAO,IAAI7lC,MAAO,GAAE2I,8BAA8Bs+B,MAIrE,GAAIxe,IAAQ01D,aAAe11D,IAAQ4qD,+BACxBpsC,EAAMqsC,gBAAkBrsC,EAAMggC,UAAW,CAChD,MAAM7tC,EAAWzwB,KAAKgvI,0BAA0B1wG,GAEhDt+B,KAAKuqI,eAAe0E,eAAex+G,EAASD,MAAM,IAClD,MAAMY,EACAX,EAAS+hB,OAAOvhB,KAAK0xB,GAAqC,QAAxBA,EAAUxxB,WAE9CC,GACApxB,KAAK0qF,UAAUwkD,aAAa99G,EAASZ,OAEzC,MAAM09G,EACAz9G,EAAS+hB,OAAO9hB,OACdiyB,GAAqC,QAAxBA,EAAUxxB,WAE/B,GAAI+8G,EAAW,CACX,MAAMiB,EAAiB,IAAI5vH,IAE3B2uH,EAAU9+H,QAAQ4hB,IACd,MAAMD,EAAcC,EAASR,MAAM,GAC7B4+G,EAAUp+G,EAASR,MAAM,GAE/B2+G,EAAe/tH,IAAI2P,EAAaq+G,KAEpCpvI,KAAK2qI,YAAYuE,aAAaC,KAK1C,IAAIE,EAAeryG,QAAQC,UAO3B,OAJInd,IAAQsQ,cACRi/G,EAAervI,KAAKgqI,SAAS/+C,aAAa3sD,IAGvC+wG,GAWX3oG,EAAwBx4C,UAAUupF,eAAiB,SAASn5C,GACxD,IAAKt+B,KAAKsvI,oBAAoB,iBAAkBhxG,GAE5C,OAAOtB,QAAQE,OAAO,yCAG1Bjf,EAAO/Y,KAAM,UAASo5B,kBAAsBt+B,QAC5C,MAAMuvI,EAAejxG,EAAMmnD,oBAE3B,OAAK8pD,EAODzvH,IAAQ2uD,kBACDzuE,KAAKgqI,SAASvyD,eAAen5C,IAGxCt+B,KAAK+uI,WAAWQ,GAETvyG,QAAQC,SAAQ,KAZnBhf,EAAOjZ,MACF,iBAAgBs5B,kBAAsBt+B,2BAEpCg9B,QAAQE,OAAO,sBAiB9BwJ,EAAwBx4C,UAAU6gJ,WAAa,SAASluG,GACpD7gC,KAAKghB,eAAegqE,UAAUnqD,GAC9B7gC,KAAKurF,cAAcroF,KAAK29B,IAO5B6F,EAAwBx4C,UAAUshJ,cAAgB,SAAS3uG,GACvD7gC,KAAKghB,eAAeokH,aAAavkG,GACjC7gC,KAAKurF,cACCvrF,KAAKurF,cAAc76D,OAAOnP,GAAUA,IAAWsf,IAczD6F,EAAwBx4C,UAAUohJ,oBAAsB,SAChDvpF,EACA7hB,GACJ,MAAMurG,EAAazvI,KAAKgjC,YAAYjf,IAAImgB,EAAWrB,OAOnD,OALK4sG,GACDxxH,EAAOjZ,MACF,GAAE+gD,MAAe7hB,wBAAiClkC,QAGpDyvI,GASX/oG,EAAwBx4C,UAAUwhJ,kBAAoB,SAAS7uG,GAC3D,OAAO7gC,KAAKurF,cAAc3nF,QAAQi9B,IAAgB,GAUtD6F,EAAwBx4C,UAAUsmG,YAAc,SAAStwD,GACrD,MAAMqrG,EAAerrG,EAAWuhD,oBAEhCzlF,KAAKy+H,MACD,eACAv6F,EAAWrB,MAAO0sG,EAAeA,EAAaroI,QAAK3G,GAElDP,KAAKsvI,oBAAoB,eAAgBprG,KAI9ClkC,KAAKgjC,YAAYtf,OAAOwgB,EAAWrB,OACnC7iC,KAAKsrF,WAAW5nE,OAAOwgB,EAAWrB,OAE9B0sG,GACAvvI,KAAKghB,eAAeokH,aAAamK,KAUzC7oG,EAAwBx4C,UAAUyhJ,iBAAmB,SAAShkH,GAC1D,OAAO3rB,KAAKghB,eAAeqkH,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,OAASjwC,EAAEiwC,MAAMlF,OAASzN,IAUlF+a,EAAwBx4C,UAAUm8E,qBAAuB,SAAS/rC,GAC9D,OAAOt+B,KAAKghB,eAAeskH,eAAer0G,KAAK7jC,GAAKA,EAAEkxC,QAAUA,IAUpEoI,EAAwBx4C,UAAUs8E,mBAAqB,SAASlsC,GAC5D,OAAOt+B,KAAKghB,eAAeqkH,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,QAAUA,IAelEoI,EAAwBx4C,UAAUgoF,aAAe,SAASC,EAAUC,GAChE,GAAIt2D,IAAQ2uD,kBACR,OAAOzuE,KAAKgqI,SAAS9zD,aAAaC,EAAUC,GAGvC17C,KAAK,IAAM16B,KAAKsqF,iBAAmBxqE,IAAQ0qE,8BAGpD,IAAI6kD,EAAeryG,QAAQC,UAS3B,OAPIk5C,GACAn2E,KAAKw0F,YAAYre,GAEjBC,IACAi5D,EAAervI,KAAKm+B,SAASi4C,IAG1Bi5D,EAAa30G,KAAK,KAAM,IAWnCgM,EAAwBx4C,UAAUspF,gBAAkB,SAAStzC,GACzD,MAAMqrG,EAAerrG,EAAWuhD,oBAMhC,OAJAzlF,KAAKy+H,MACD,mBACAv6F,EAAWrB,MAAO0sG,EAAeA,EAAaroI,GAAK,MAElDlH,KAAKsvI,oBAAoB,mBAAoBprG,GAK9CpkB,IAAQ2uD,kBACDzuE,KAAKgqI,SAASxyD,gBAAgBtzC,GAGrCqrG,GACAtxH,EAAO/Y,KACF,YAAWg/B,kBAA2BlkC,QAC3CA,KAAKwvI,cAAcD,GAEZvyG,QAAQC,SAAQ,KAG3Bhf,EAAOjZ,MAAO,2CAA0Ck/B,GAEjDlH,QAAQE,OAAO,qBAjBXF,QAAQE,OAAO,0CAoB9BwJ,EAAwBx4C,UAAU6wI,kBAAoB,SAASrlG,EAAOk2G,GAGlE,OAFA5vI,KAAKy+H,MAAM,oBAAqB/kG,EAAOk2G,GAEhC5vI,KAAKghB,eAAe+9G,kBAAkBrlG,EAAOk2G,IAWxDlpG,EAAwBx4C,UAAU2hJ,4BAA8B,SACxDC,GACJ,IAAIC,EAASD,EAAS99G,IAEtB,MAAMg+G,EAAkBD,EAAOnsI,QAAQ,WACjCqsI,EAAgBF,EAAOnsI,QAAQ,mBAAoBosI,GACzD,IAAIE,EAAkBH,EAAO32E,YAAY,gBAEzC,IAAuB,IAAnB62E,IACwB,IAArBC,GACAA,IAAoBD,EACvB,OAAOH,EAGX,MAAMK,EAAcJ,EAAOnsI,QAAQ,OAAQqsI,GACrCG,EAASL,EAAO5iH,UAAU8iH,EAAeE,EAAc,GAE7DJ,EAASA,EAAOnuI,QAAQwuI,EAAQ,IAChCF,EAAkBH,EAAO32E,YAAY,gBACrC,MAAMi3E,EAAgBN,EAAOnsI,QAAQ,OAAQssI,GACvCI,EAAUP,EAAO71G,MAAM,EAAGm2G,GAC1BE,EAAgBH,EAAOvxF,OACvB2xF,EAAUT,EAAO71G,MAAMm2G,GAI7B,OAFAN,EAAU,GAAEO,QAAcC,IAAgBC,IAEnC,IAAI76D,sBAAsB,CAC7B1uE,KAAM6oI,EAAS7oI,KACf+qB,IAAK+9G,KAYbrpG,EAAwBx4C,UAAUuiJ,2BAA6B,SACvD5hE,GACJ,MAAMy/D,EAAc,IAAIrrF,IAAiB4rB,EAAiB78C,KAC1D,IAAI0+G,GAAoB,EACxB,MAAMnC,EAAaD,EAAYjrF,YAAY,SAE3C,GAAIkrF,EAAY,CACZ,MAAMoC,EACA3wI,KAAK0rI,0BAA0BntG,KAEjCgwG,EAAW5+G,YAAcghH,IACzBpC,EAAW5+G,UAAYghH,EACvB1yH,EAAO/Y,KACF,qCAAoCyrI,GACzCD,GAAoB,QAGxBzyH,EAAO9Y,KAAK,oDAGhB,MAAMqpI,EAAaF,EAAYjrF,YAAY,SAE3C,GAAImrF,EAAY,CACZ,MAAMoC,EACA5wI,KAAK0rI,0BAA0BntG,KAEjCiwG,EAAW7+G,YAAcihH,IACzBpC,EAAW7+G,UAAYihH,EACvB3yH,EAAO/Y,KACF,qCAAoC0rI,GACzCF,GAAoB,QAGxBzyH,EAAO9Y,KAAK,mDAGhB,OAAIurI,EACO,IAAI/6D,sBAAsB,CAC7B1uE,KAAM4nE,EAAiB5nE,KACvB+qB,IAAKs8G,EAAY/qF,aAIlBsrB,GAGXnoC,EAAwBx4C,UAAU6jF,oBAAsB,SAASjP,GAC7D,IAAIgtE,EAAWhtE,EAmBf,OAjBA9iE,KAAKy+H,MAAM,oCAAqCgN,EAAQqE,IAGxDA,EAAW9vI,KAAK4uI,iBAAiBkB,GAE7BhwH,IAAQ01D,aACRs6D,EAAW9vI,KAAKywI,2BAA2BX,GAC3CA,EAAW9vI,KAAK6vI,4BAA4BC,KAI5CA,EAAW9vI,KAAKkqI,QAAQ2G,cAAcf,GACtC9vI,KAAKy+H,MACD,oDACAgN,EAAQqE,KAGT,IAAI9yG,QAAQ,CAACC,EAASC,KACzBl9B,KAAKghB,eAAe+wD,oBAAoB+9D,GACnCp1G,KAAK,KACF16B,KAAKy+H,MAAM,gCACX,MAAM+K,EAAa/8G,IAAQwF,SAAS69G,EAAS99G,KAEzCw3G,IAAexpI,KAAKwpI,aACpBxpI,KAAKwpI,WAAaA,EAClBxpI,KAAKwf,aAAa0D,KACd8V,IAAUjQ,oBAAqB/oB,KAAMwpI,IAE7CvsG,KACD9sB,IACCnQ,KAAKy+H,MAAM,+BAAgCtuH,GAC3CnQ,KAAKwf,aAAa0D,KACd8V,IAAUvQ,6BACVtY,EAAKnQ,MACTk9B,EAAO/sB,QAkBvBu2B,EAAwBx4C,UAAU+pF,uBAAyB,SAASqO,GAChEroE,EAAOhZ,MAAO,GAAEjF,+BAA+BsmF,KAC/C,MAAM2oB,EAAUjvG,KAAKopI,sBAAwB9iD,EAI7C,OAFAtmF,KAAKopI,oBAAsB9iD,EAEvBxmE,IAAQ2uD,mBACRzuE,KAAKgqI,SAAS/xD,uBAAuBqO,IAG9B,GAGJ2oB,GAUXvoE,EAAwBx4C,UAAU0lF,oCAAsC,WACpE,IAAK5zE,KAAKghB,eAAeqkH,WAGrB,OAFApnH,EAAOhZ,MAAM,yCAEN+3B,QAAQC,UAEnB,MAAMkpE,EAAkBnmG,KAAKgnC,qBACvB8pG,EAAc9wI,KAAK2vI,iBAAiBpxG,KAE1C,IAAKuyG,EACD,OAAO9zG,QAAQC,UAEnB,MAAM2jC,EAAakwE,EAAYrlD,gBAE/B,IAAK7qB,EAAW8qB,YAAc9qB,EAAW8qB,UAAUn1F,OAC/C,OAAOymC,QAAQC,UAEnB,IAAK,MAAMmuD,KAAYxqB,EAAW8qB,UAC9B,GAAI9qB,EAAW8qB,UAAUv9F,eAAei9F,GAAW,CAC/C,MAAM2lD,EAAa5qC,EAAgB3nE,YAAcC,SApgEvB,qBACC,sBAugE3BxgB,EAAO/Y,KAAM,kDAAiDlF,WAAW+wI,KACzEnwE,EAAW8qB,UAAUN,GAAU4lD,sBAAwBD,EAI/D,OAAOD,EAAYnlD,cAAc/qB,IAYrCl6B,EAAwBx4C,UAAUwlF,cAAgB,WAC9C,IAAK1zE,KAAKghB,eAAeqkH,WAGrB,OAFApnH,EAAOhZ,MAAM,wCAEN+3B,QAAQC,UAEnB,MAAMkpE,EAAkBnmG,KAAKgnC,qBAE7B,IAAKm/D,EACD,OAAOnpE,QAAQC,UAGnB,MAAMuB,EAAY2nE,EAAgB3nE,UAMlC,KAAOx+B,KAAKmH,QAAQkmE,cAAgBrtE,KAAKmH,QAAQkmE,aAAa08D,kBACtDjqH,IAAQ01D,aAAex1E,KAAKmH,QAAQimE,uBAAyB5uC,IAAcC,WAC5E3e,IAAQ2uD,mBACX,OAAOzxC,QAAQC,UAGnB,MAAMg0G,EAAmB9qC,EAAgBlS,iBAClCkS,EAAgBlS,gBAAgB/sF,KAAOi/F,EAAgBzgB,cACxDorD,EAAc9wI,KAAK2vI,iBAAiBpxG,KAE1C,IAAKuyG,EACD,OAAO9zG,QAAQC,UAEnB,MAAM2jC,EAAakwE,EAAYrlD,gBAE/B,IAAM7qB,EAAW8qB,YAAa9qB,EAAW8qB,UAAUn1F,OAC/C,OAAOymC,QAAQC,UAGnB,GAAIj9B,KAAKsqF,iBACL,IAAK,MAAMc,KAAYxqB,EAAW8qB,UAC9B,GAAI9qB,EAAW8qB,UAAUv9F,eAAei9F,GAAW,CAI/C,MAAMuzB,EAAU7+F,IAAQ01D,aACjBh3C,IAAcC,WACdz+B,KAAKmH,QAAQimE,sBACd6jE,EAAmBjxI,KAAK2pF,cAAcE,KArkEhC,IAskEN7pF,KAAKgqI,SAASpgD,2BAA2BwB,GAAUlnB,WAEzDjmD,EAAO/Y,KAAM,GAAElF,iCAAiC2+G,kBACvC3+G,KAAKgqI,SAASpgD,2BAA2BwB,GAAU9+E,KAC5Ds0D,EAAW8qB,UAAUN,GAAUlnB,WAAay6C,OAGjD,CAEH,IAAIA,EAAU3+G,KAAK2pF,cAAcE,KAEjC,GAAIrrD,IAAcC,SAAkB,CAEhC,MAAMyyG,EAAclxI,KAAK6qI,qBACnBz/H,KAAKmM,MAAM4uF,EAAgB9vE,WAAar2B,KAAK6qI,sBAC7C,EACAz/C,EAAWprF,KAAKgqI,SAASpgD,2BAC1B34D,KAAKkgH,GAASA,EAAMpnD,wBAA0BmnD,GAE/C9lD,IACAntE,EAAO/Y,KAAM,GAAElF,iCAAiCorF,EAASlnB,uCACzDy6C,EAAUvzB,EAASlnB,YAG3BtD,EAAW8qB,UAAU,GAAGxnB,WAAay6C,EAGzC,OAAOmyB,EAAYnlD,cAAc/qB,IAGrCl6B,EAAwBx4C,UAAU4nF,qBAAuB,SAAShT,GAO9D,GANA9iE,KAAKy+H,MAAM,qCAAsCgN,EAAQ3oE,IAIzDA,EAAc9iE,KAAK4uI,iBAAiB9rE,GAEhChjD,IAAQ01D,YAAa,CAErB,GAAIx1E,KAAKsqF,gBAAiB,CAItB,MAAM8mD,IAAyBpxI,KAAKmH,QAAQimE,wBAA0BuhE,EAAe3uI,OAGrF8iE,EAAc9iE,KAAK0qF,UAAU2mD,uBAAuBvuE,EAAasuE,GACjEpxI,KAAKy+H,MACD,kDACAgN,EAAQ3oE,IAIhBA,EAAc0qE,EAAe1qE,OAC1B,CACH,MAAMwuE,EAAqBtxI,KAAKghB,eAAewtD,kBAG/C1L,EAAc9iE,KAAKkqI,QAAQ2G,cAAc/tE,EAAawuE,GACtDtxI,KAAKy+H,MACD,gDACAgN,EAAQ3oE,IAER9iE,KAAKsqF,kBAELxnB,EAAc9iE,KAAK0qF,UAAU2mD,uBAAuBvuE,GAGpDA,EAAc9iE,KAAKgqI,SAASz/C,kCAAkCznB,GAC9D9iE,KAAKy+H,MACD,oDACAgN,EAAQ3oE,IAGZA,EAAc9iE,KAAKgqI,SAAS//C,0BAA0BnnB,IAI9D,OAAO,IAAI9lC,QAAQ,CAACC,EAASC,KACzBl9B,KAAKghB,eAAe80D,qBAAqBhT,GACpCpoC,KAAK,KACF16B,KAAKy+H,MAAM,iCACX,MAAMgL,EAAch9G,IAAQwF,SAAS6wC,EAAY9wC,KAE7Cy3G,IAAgBzpI,KAAKypI,cACrBzpI,KAAKypI,YAAcA,EACnBzpI,KAAKwf,aAAa0D,KACd8V,IAAUhQ,qBAAsBhpB,KAAMypI,IAE9CxsG,KACD9sB,IACCnQ,KAAKy+H,MAAM,gCAAiCtuH,GAC5CnQ,KAAKwf,aAAa0D,KACd8V,IAAUtQ,8BACVvY,EACAnQ,MACJk9B,EAAO/sB,QAevBu2B,EAAwBx4C,UAAUylF,yBAA2B,SAASiyC,EAAc,MAChF,GAAIA,EAAc,EACd,MAAM,IAAIvuH,MAAO,wBAAuBuuH,GAI5C,GAAI9lG,IAAQC,gBACR,OAAOid,QAAQC,UAInB,MAAMs0G,EAA4B,OAAhB3rB,EAAuB5lH,KAAK6qI,qBAAuBjlB,EAErE5lH,KAAK6qI,qBAAuB0G,EAE5BtzH,EAAOrZ,IAAK,GAAE5E,8BAA8BuxI,KAE5C,MAAMprC,EAAkBnmG,KAAKgnC,qBAE7B,IAAKm/D,GAAmBA,EAAgB7nC,WAAa6nC,EAAgB3nE,YAAcC,SAC/E,OAAOzB,QAAQC,UAEnB,MAAM6zG,EAAc9wI,KAAK2vI,iBAAiBpxG,KAE1C,IAAKuyG,EACD,OAAO9zG,QAAQC,UAEnB,MAAM2jC,EAAakwE,EAAYrlD,gBAE/B,IAAK7qB,IAAeA,EAAW8qB,YAAc9qB,EAAW8qB,UAAUn1F,OAC9D,OAAOymC,QAAQC,UAGnB,GAAIj9B,KAAKsqF,gBAAiB,CAEtB,MAAMknD,EAAwBxxI,KAAKgqI,SAAS9+C,gCAAgCib,EAAgB7nE,OACvF94B,IAAI0uB,GAAUA,GAAUq9G,GAMvBE,EAAgBzxI,KAAKgqI,SAASpgD,2BAC/BnU,UAAU07D,GAAyC,IAAhCA,EAAMpnD,uBAE1BwnD,EAAY,IAAwB,IAAnBE,IACjBD,EAAsBC,IAAiB,GAE3C,IAAK,MAAMrmD,KAAYxqB,EAAW8qB,UAC1B9qB,EAAW8qB,UAAUv9F,eAAei9F,KACpCxqB,EAAW8qB,UAAUN,GAAU9E,OAASkrD,EAAsBpmD,SAG/DmmD,EAAY,GACnB3wE,EAAW8qB,UAAU,GAAG3B,sBAAwBoc,EAAgB9vE,YAAck7G,EACxEnmI,KAAKmM,MAAM4uF,EAAgB9vE,WAAak7G,GACxC,EACN3wE,EAAW8qB,UAAU,GAAGpF,QAAS,IAEjC1lB,EAAW8qB,UAAU,GAAG3B,2BAAwBxpF,EAChDqgE,EAAW8qB,UAAU,GAAGpF,QAAS,GAKrC,OAFAroE,EAAO/Y,KAAM,GAAElF,8BAA8BuxI,iBAAyB3kI,KAAKwL,UAAUwoD,EAAW8qB,cAEzFolD,EAAYnlD,cAAc/qB,GAAYlmC,KAAK,KAM9C,GALAyrE,EAAgBtT,qBAAuB0+C,EACvCvxI,KAAKwf,aAAa0D,KAAK8V,IAAU7Q,2CAA4Cg+E,GAIzEnmG,KAAK8lC,QAAU9lC,KAAKsqF,gBACpB,OAAOtqF,KAAK0zE,mBAkBxBhtC,EAAwBx4C,UAAUiqF,uBAAyB,SAASmO,GAChEroE,EAAOhZ,MAAO,GAAEjF,+BAA+BsmF,KAC/C,MAAM2oB,EAAUjvG,KAAKupI,sBAAwBjjD,EAI7C,OAFAtmF,KAAKupI,oBAAsBjjD,EAEvBxmE,IAAQ2uD,mBACRzuE,KAAKgqI,SAAS7xD,uBAAuBmO,IAG9B,GAGJ2oB,GAYXvoE,EAAwBx4C,UAAUy9G,UAAY,SAASC,EAAOC,EAAW,IAAK6lC,EAAe,KACzF,IAAK1xI,KAAKqpI,YAAa,CACnB,GAAIrpI,KAAKghB,eAAeqkH,WAAY,CAChC,MAAMt/D,EAAY/lE,KAAKghB,eAAeqkH,aAAap0G,KAAK5iC,GAAKA,EAAEsjJ,MAE/D3xI,KAAKqpI,YAActjE,GAAaA,EAAU4rE,KAC1C3xI,KAAKqpI,aAAeprH,EAAO/Y,KAAQlF,KAAF,4CAGrC,IAAKA,KAAKqpI,YAAa,CACnB,MAAMuI,EAAkBx7I,MAAMO,KAAKqJ,KAAKgjC,YAAY5f,UAAU6N,KAAKzjC,GAAKA,EAAEq7C,gBAEtE7oC,KAAKghB,eAAe6wH,kBAAoBD,IACxC5xI,KAAKqpI,YAAcrpI,KAAKghB,eAAe6wH,iBAAiBD,EAAgBjsD,aAE5E3lF,KAAKqpI,aAAeprH,EAAO/Y,KAAQlF,KAAF,6DAGjCA,KAAKqpI,cACLrpI,KAAKqpI,YAAYyI,aAAe9xI,KAAK+xI,cAAcjkJ,KAAKkS,OAIhE,GAAIA,KAAKqpI,YAAa,CAClB,GAAIrpI,KAAKqpI,YAAY2I,WAOjB,YANAhyI,KAAKspI,gBAAgBpmI,KAAK,CACtB0oG,QACAC,WACA6lC,iBAMR1xI,KAAKqpI,YAAY4I,WAAWrmC,EAAOC,EAAU6lC,QAE7CzzH,EAAO9Y,KAAQnF,KAAF,6CAarB0mC,EAAwBx4C,UAAU6jJ,cAAgB,SAAS1qH,GAGvD,GAAIrnB,KAAKqpI,aAA8B,KAAfhiH,EAAM6qH,MAAelyI,KAAKspI,gBAAgB/yI,OAAQ,CACtE,MAAM,MAAEq1G,EAAF,SAASC,EAAT,aAAmB6lC,GAAiB1xI,KAAKspI,gBAAgB57G,QAE/D1tB,KAAKqpI,YAAY4I,WAAWrmC,EAAOC,EAAU6lC,KAQrDhrG,EAAwBx4C,UAAUmjF,qBAAuB,WACrD,MAAMtuB,EAAUt2B,IAAQ4E,eAExBpT,EAAO/Y,KAAM,GAAElF,qCAAqC+iD,KACpD/iD,KAAKuqI,eAAe0E,eAAelsF,IAOvCrc,EAAwBx4C,UAAUmoF,kBAAoB,WAClDp4D,EAAO/Y,KAAK,gCACZlF,KAAKuqI,eAAe4H,uBAQxBzrG,EAAwBx4C,UAAUuuB,MAAQ,WACtCzc,KAAKy+H,MAAM,QAGXz+H,KAAKisE,eAAex9B,IAChBqvF,IAAoC99H,KAAK2pI,mBAC7C3pI,KAAKisE,eAAex9B,IAChBqvF,IAAyC99H,KAAK0pI,uBAElD,IAAK,MAAM0I,KAAcpyI,KAAKwnC,aAAapkB,SACvC,IAAK,MAAMs5C,KAAe01E,EAAWhvH,SACjCpjB,KAAKstI,mBAAmB5wE,GAGhC18D,KAAKwnC,aAAaqO,QAElB71C,KAAKurF,cAAgB,GAErBvrF,KAAKqpI,YAAc,KACnBrpI,KAAKspI,gBAAkB,GAElBtpI,KAAKi6D,IAAItzB,sBAAsB3mC,OAChCie,EAAOjZ,MAAM,4CAEU,OAAvBhF,KAAKiqI,gBACL/9I,OAAOouC,cAAct6B,KAAKiqI,eAC1BjqI,KAAKiqI,cAAgB,MAEzBhsH,EAAO/Y,KAAM,WAAUlF,WACvBA,KAAKghB,eAAevE,SAGxBiqB,EAAwBx4C,UAAU6nF,aAAe,SAAShwD,GACtD,OAAO/lB,KAAKqyI,sBAAqB,EAAoBtsH,IAGzD2gB,EAAwBx4C,UAAU2jF,YAAc,SAAS9rD,GACrD,OAAO/lB,KAAKqyI,sBAAqB,EAAkBtsH,IAcvD2gB,EAAwBx4C,UAAUmkJ,qBAAuB,SACjDC,EACAvsH,GACJ,MAAM0oF,EAAU6jC,EAAU,QAAU,SAEpCtyI,KAAKy+H,MAAO,SAAQhwB,EAAW7hG,KAAKwL,UAAU2N,EAAa,KAAM,MAEjE,MAAMwsH,EAAgB,CAACC,EAAWC,EAAWC,KACzC,IACI1yI,KAAKy+H,MACA,SAAQhwB,2BAAkCg9B,EAAQ+G,IAEnD1yH,IAAQ01D,cAGHx1E,KAAK4rI,mBAAmBrtG,MACrBv+B,KAAKuqI,eAAeoI,wBACxB3yI,KAAKqxE,uBAITmhE,EAAY,IAAI78D,sBAAsB,CAClC1uE,KAAMurI,EAAUvrI,KAChB+qB,IAAKhyB,KAAKuqI,eAAeqI,gCACrBJ,EAAUxgH,OAGlBhyB,KAAKy+H,MACA,SAAQhwB,wEAETg9B,EAAQ+G,KAMZxyI,KAAKsqF,iBAAmBxqE,IAAQ0qE,gCAC3BxqF,KAAKmH,QAAQimE,uBACdptE,KAAKmH,QAAQimE,uBAAyBuhE,EAAe3uI,SAEzDwyI,EAAYxyI,KAAK0qF,UAAUmoD,sBAAsBL,GACjDxyI,KAAKy+H,MACA,SAAQhwB,EACH,uCACNg9B,EAAQ+G,MAGXxyI,KAAKmH,QAAQ4yC,YAAcj6B,IAAQ0qE,+BAEpCgoD,EAAY,IAAI78D,sBAAsB,CAClC1uE,KAAMurI,EAAUvrI,KAChB+qB,IAAKhyB,KAAK2qI,YAAYmI,eAAeN,EAAUxgH,OAGnDhyB,KAAKy+H,MACA,SAAQhwB,EACF,0CACPg9B,EAAQ+G,KAGhB,MAAMhiG,EAj5ClB,SAAwBnhB,GAKpB,MAAMmhB,EAAU,IAAIjxB,IAMdwzH,EAAY,IAAIxzH,IAEtB,GAAoB,iBAAT8P,GAA8B,OAATA,GACL,iBAAbA,EAAK2C,IAGf,OAFA/T,EAAO9Y,KAAK,mDAELqrC,EAGX,MAAMv4B,EAAUmrC,IAAUlrC,MAAMmX,EAAK2C,KAErC,IAAK57B,MAAMC,QAAQ4hB,EAAQwV,OACvB,OAAO+iB,EAGX,IAAK,MAAM/e,KAASxZ,EAAQwV,MACxB,GAAKr3B,MAAMC,QAAQo7B,EAAMjB,OAAzB,CAIA,GAAIp6B,MAAMC,QAAQo7B,EAAMX,YACpB,IAAK,MAAMI,KAASO,EAAMX,WACtB,QAA+B,IAApBI,EAAMC,gBACa,IAAhBD,EAAMV,MAAuB,CAEvC,MAAMwiH,EACA9hH,EAAMV,MAAMztB,MAAM,KAAKyC,IACrBssB,GAAWxZ,SAASwZ,EAAS,KAC/B8vB,EAAcoxF,EAAW,GAI/B9hH,EAAMV,MAAQwiH,EAGTD,EAAUhvH,IAAI69B,IACfmxF,EAAU3xH,IAAIwgC,EAAa,IAE/BmxF,EAAU5lJ,IAAIy0D,GAAa1+C,KAAKguB,GAI5C,IAAK,MAAMlM,KAAQyM,EAAMjB,MAAO,CAC5B,GAAuB,SAAnBxL,EAAK1nB,UACL,SAGJ,MAAMwkD,EAAO98B,EAAKz3B,MAClB,IAAIkjC,EAAW+f,EAAQrjD,IAAI20D,GAEtBrxB,IACDA,EAAW,CACPD,MAAO,GACPgiB,OAAQ,GACRsP,QAEJtR,EAAQpvB,IAAI0gC,EAAMrxB,IAGtB,MAAM0wB,EAAan8B,EAAK9d,GAIxB,GAFAupB,EAASD,MAAMttB,KAAKi+C,GAEhB4xF,EAAUhvH,IAAIo9B,GAAa,CAC3B,MAAMrwB,EAAaiiH,EAAU5lJ,IAAIg0D,GAEjC,IAAK,MAAMjwB,KAASJ,EAChBL,EAAS+hB,OAAOtvC,KAAKguB,KAMrC,OAAOsf,EA6zCiByiG,CAAeT,GAE/Bv0H,EAAOhZ,MAAM,wBAAyBurC,GACtCxwC,KAAKkzI,sBAAsB1iG,GAE3BiiG,EAAUD,GACZ,MAAOn5I,GACL2G,KAAKy+H,MAAO,SAAQhwB,WAAkBp1G,GACtC2G,KAAKy+H,MAAO,SAAQhwB,WAAkBg9B,EAAQ+G,IAC9Cv0H,EAAOjZ,MAAO,SAAQypG,WAAkBp1G,EAAGoyI,EAAQ+G,IAEnDE,EAASr5I,KAIX85I,EAAgB,CAAChjI,EAAKuiI,KACxB1yI,KAAKy+H,MAAO,SAAQhwB,aAAoBt+F,GACxC,MAAMu1B,EACA4sG,EACIt5G,IAAUrR,oBACVqR,IAAUtR,qBAEpB1nB,KAAKwf,aAAa0D,KAAKwiB,EAAWv1B,EAAKnQ,MAEvC0yI,EAASviI,IAGb,OAAO,IAAI6sB,QAAQ,CAACC,EAASC,KACzB,IAAIk2G,EAGAA,EADAd,EACYtyI,KAAKghB,eAAe6wD,YAAY9rD,GAEhC/lB,KAAKghB,eAAe+0D,aAAahwD,GAGjDqtH,EACK14G,KACG1I,GAAOugH,EAAcvgH,EAAKiL,EAASC,GACnCl4B,GAASmuI,EAAcnuI,EAAOk4B,OAS9CwJ,EAAwBx4C,UAAUs9F,oBAAsB,SAASnqC,GAC7D,OAAIA,GAAWA,EAAQ7O,QAAU6O,EAAQ7O,OAAOj8C,OACrC8qD,EAAQ7O,OAAO,GAAGhiB,MAAM,GACxB6wB,GAAWA,EAAQ7wB,OAAS6wB,EAAQ7wB,MAAMj6B,OAC1C8qD,EAAQ7wB,MAAM,GAGlB,MAUXkW,EAAwBx4C,UAAUglJ,sBAAwB,SAAS1iG,GAC/D,IAAK,MAAMlS,KAASt+B,KAAKgjC,YAAY5f,SAAU,CAC3C,MAAMiwH,EAAY/0G,EAAMy1D,WAExB,GAAIvjD,EAAQzsB,IAAIsvH,GAAY,CACxB,MAAMtwF,EAAUvS,EAAQrjD,IAAIkmJ,GAE5B,IAAKtwF,EAGD,YAFA9kC,EAAOjZ,MAAO,sBAAqBquI,QAAgBrzI,QAIvD,MAAM8iD,EAAU9iD,KAAKsrF,WAAWn+F,IAAImxC,EAAMuE,OACpCywG,EAAatzI,KAAKwrF,oBAAoBzoC,GACtCwwF,EAAavzI,KAAKwrF,oBAAoB1oC,GAGxCwwF,IAAeC,GACI,OAAfA,EACAt1H,EAAO/Y,KACF,8BAA6Bo5B,QAAYt+B,OAC1C+iD,GAEJ9kC,EAAOjZ,MACF,wBAAuBs5B,KAAS+0G,QAAgBrzI,cACtC+iD,GAEnB/iD,KAAKsrF,WAAWlqE,IAAIkd,EAAMuE,MAAOkgB,GAEjC/iD,KAAKwf,aAAa0D,KACd8V,IAAU9Q,yBAA0BoW,EAAOg1G,IAE/Cr1H,EAAOhZ,MACF,kBAAiBquI,UAAmBh1G,KAAS+0G,2BACjBrzI,WAE7Bs+B,EAAMqsC,gBAAmBrsC,EAAMggC,WAIvCrgD,EAAO9Y,KAAM,uCAAsCm5B,WAAe+0G,QAAgBrzI,UAK9F0mC,EAAwBx4C,UAAU6iF,gBAAkB,SAAStiD,GAQzD,OAPAzuB,KAAKy+H,MAAM,kBAAmB7xH,KAAKwL,UAAU,CACzCqW,UAAWA,EAAUA,UACrByhD,OAAQzhD,EAAUyhD,OAClBT,cAAehhD,EAAUghD,cACzB3P,iBAAkBrxC,EAAUqxC,kBAC7B,KAAM,MAEF9/D,KAAKghB,eAAe+vD,gBAAgBtiD,IAY/CiY,EAAwBx4C,UAAUqiH,SAAW,SAAS/kG,EAAUmC,GAIxDmS,IAAQyV,YAAczV,IAAQsQ,aAAetQ,IAAQC,gBAErD/f,KAAKghB,eAAeuvF,WACf71E,KAAKlvB,GACL4xB,MAAMzvB,GAAW,UAMtB3N,KAAKghB,eAAeuvF,SAAS/kG,IAWrCk7B,EAAwBx4C,UAAU8gJ,0BAA4B,SAAS1wG,GACnE,MAAMuE,EAAQvE,EAAMuE,MACpB,IAAIpS,EAAWzwB,KAAKguI,SAASnrG,GAS7B,GAPIpS,GACAxS,EAAOjZ,MAAO,4CAA2C69B,GAMzD7iC,KAAKsqF,mBACAtqF,KAAKmH,QAAQimE,uBACdptE,KAAKmH,QAAQimE,uBAAyBuhE,EAAe3uI,OAAS,CAClEywB,EAAW,CACPD,MAAO,GACPgiB,OAAQ,IAEZ,IAAK,IAAIlmD,EAAI,EAAGA,EAAIm9F,IAAelzF,OAAQjK,IACvCmkC,EAASD,MAAMttB,KAAKupB,IAAQ4E,gBAEhCZ,EAAS+hB,OAAOtvC,KAAK,CACjBstB,MAAOC,EAASD,MAAM0J,QACtB/I,UAAW,aAGfV,EAAW,CACPD,MAAO,CAAE/D,IAAQ4E,gBACjBmhB,OAAQ,IAGhB,IAAKxyC,KAAKmH,QAAQ4yC,WAAY,CAK1B,MAAMy5F,EAAe/iH,EAASD,MAAMj6B,OAEpC,IAAK,IAAIjK,EAAI,EAAGA,EAAIknJ,IAAgBlnJ,EAAG,CACnC,MAAMykC,EAAcN,EAASD,MAAMlkC,GAC7B8iJ,EAAU3iH,IAAQ4E,eAExBZ,EAASD,MAAMttB,KAAKksI,GACpB3+G,EAAS+hB,OAAOtvC,KAAK,CACjBstB,MAAO,CAAEO,EAAaq+G,GACtBj+G,UAAW,SAOvB,OAHAV,EAASqxB,KAAOxjB,EAAMy1D,WACtB/zF,KAAKsrF,WAAWlqE,IAAIyhB,EAAOpS,GAEpBA,GAQXiW,EAAwBx4C,UAAUwI,SAAW,WACzC,MAAQ,OAAMsJ,KAAKkH,UAAUlH,KAAK8lC,Y,mFCjuFtC,wEAOA,MAAM7nB,EAASF,oBAAUM,GAezB,SAASo1H,EAA0BhiH,EAAOiiH,EAAiBtE,GACvDnxH,EAAOhZ,MACF,+BAA8BmqI,EAC5B,iCAAgCsE,EAAgBxsI,IACvD,MAAM6pB,EAAc2iH,EAAgBxsI,GAC9BysI,EAAkBD,EAAgB5xF,KAClC8xF,EAAmBF,EAAgBpvE,MAEnCuvE,EAAkBpiH,EAAM0wB,WAAWpxB,GAErC8iH,IAAoBzE,GAKpByE,IACA51H,EAAOhZ,MACA8rB,EAAF,sCACI8iH,EAAF,mCAIPpiH,EAAM6vB,WAAWuyF,GAEjB51H,EAAOhZ,MAAO,+BAA8B4uI,GAC5C51H,EAAOhZ,MAAMwsB,EAAMgxB,kBAEnBhxB,EAAMixB,qBAAqBmxF,IAE/BpiH,EAAM+vB,iBAAiB,CACnBt6C,GAAIkoI,EACJ9xI,UAAW,QACX/P,MAAOqmJ,IAEXniH,EAAM+vB,iBAAiB,CACnBt6C,GAAIkoI,EACJ9xI,UAAW,OACX/P,MAAOomJ,IAEXliH,EAAMuxB,aAAa,CACf7xB,UAAW,MACXX,MAAQ,GAAEO,KAAeq+G,OA9BzBnxH,EAAOhZ,MAAO,GAAEmqI,iCAAuCr+G,KA0ChD,MAAM65G,EAIjB30I,cAKI+J,KAAK8zI,sBAAwB,IAAIv0H,IAQrCw0H,iBACI/zI,KAAK8zI,sBAAsBj+F,QAS/Bq5F,aAAa8E,GACT/1H,EAAOhZ,MAAM,yBAA0B+uI,GACvCh0I,KAAK8zI,sBAAwBE,EAUjClB,eAAe/C,GACX,MAAMkE,EAAiB,IAAIhxF,IAAiB8sF,GACtCz/G,EAAa2jH,EAAe5wF,YAAY,SAE9C,OAAK/yB,EAMEtwB,KAAKk0I,gBAAgB5jH,GACtB2jH,EAAe1wF,WAAawsF,GAN9B9xH,EAAOhZ,MAAO,sCAAqC8qI,GAE5CA,GAcfmE,gBAAgB5jH,GACZ,GAA6B,aAAzBA,EAAWX,UAGX,OAFA1R,EAAOhZ,MAAM,wDAEN,EAEX,GAAIqrB,EAAWyxB,eAAiB,EAG5B,OAFA9jC,EAAOhZ,MAAM,sDAEN,EAEXgZ,EAAOhZ,MAAM,yBAA0BjF,KAAK8zI,uBAC5C,MAAMK,EAAoB7jH,EAAW+xB,uBAErCpkC,EAAOhZ,MAAM,8BAA+BkvI,EACxC,qCACJ,IAAK,MAAMnvH,KAAQmvH,EAAmB,CAClC,MAAMryF,EAAOxxB,EAAW4wB,iBAAiBl8B,EAAM,QACzCs/C,EAAQh0C,EAAW4wB,iBAAiBl8B,EAAM,SAChD,IAAIovH,EAAuBp0I,KAAK8zI,sBAAsB3mJ,IAAI63B,GAE1D,GAAIovH,EACAn2H,EAAOhZ,MAEA,qDAAa+f,MAASovH,SAC1B,CACHn2H,EAAOhZ,MACF,oDAAmD+f,GAIxD,MAAMqvH,EAA8B/jH,EAAW6xB,WAAWn9B,GAEtDqvH,GACAp2H,EAAOhZ,MACF,cAAaovI,qDAETrvH,GACTovH,EAAuBC,IAEvBD,EAAuB3nH,IAAQ4E,eAC/BpT,EAAOhZ,MAAO,sBAAqBmvI,cACRpvH,IAE/B/G,EAAOhZ,MAAO,oBAAmBmvI,oBACApvH,GACjChlB,KAAK8zI,sBAAsB1yH,IAAI4D,EAAMovH,GAEzCX,EACInjH,EACA,CACIppB,GAAI8d,EACJs/C,QACAxiB,QAEJsyF,GAKR,OAAO,EAQXE,SAASvE,GACL,MAAMkE,EAAiB,IAAIhxF,IAAiB8sF,GACtCz/G,EAAa2jH,EAAe5wF,YAAY,SAE9C,IAAK/yB,EAGD,OAFArS,EAAOhZ,MAAO,sCAAqC8qI,GAE5CA,EAEX,GAA6B,aAAzBz/G,EAAWX,UAGX,OAFA1R,EAAOhZ,MAAM,uDAEN8qI,EAEX,GAAIz/G,EAAWyxB,eAAiB,EAG5B,OAFA9jC,EAAOhZ,MAAM,qDAEN8qI,EAEX,IAAKz/G,EAAW0xB,wBAIZ,OAHA/jC,EAAOhZ,MAAM,0DAGN8qI,EAEX,MAAM7B,EAAY59G,EAAWoxB,WAAW,OAIxCpxB,EAAWsyB,wBAAwB,OAGnC,IAAK,MAAM5xB,KAAYk9G,EAAW,CAC9B,MAAMkB,EAAUruF,YAAmB/vB,GAEnCV,EAAWgxB,WAAW8tF,GAG1B,OAAO6E,EAAe1wF,e,wECjP9B,iEAUA,MAAMtlC,EAASF,oBAAUM,GASV,MAAMmsH,EAOjBv0I,YAAY++E,GACRh1E,KAAKmyI,sBACLnyI,KAAKg1E,UAAYA,EAQrBm9D,sBACInyI,KAAKu0I,kBAAoB,KACzBv0I,KAAKw0I,gBAAiB,EAU1BvF,eAAel+G,GACX,GAA2B,iBAAhBA,EACP,MAAM,IAAI15B,MAAM,kCAEpB2I,KAAKu0I,kBAAoBxjH,EAO7B4hH,uBACI,OAAOh6G,QAAQ34B,KAAKu0I,mBAcxB3B,gCAAgC7C,GAC5B,MAAMkE,EAAiB,IAAIhxF,IAAiB8sF,GACtCz/G,EAAa2jH,EAAe5wF,YAAY,SAE9C,IAAK/yB,EAKD,OAJArS,EAAOhZ,MACAjF,KAAKg1E,UAAP,uCACQ+6D,GAENA,EAGX,GAA6B,aAAzBz/G,EAAWX,UAGP3vB,KAAKu0I,mBAAqBv0I,KAAKw0I,eAC/BlkH,EAAWkxB,iBAAiB,CACxBt6C,GAAIlH,KAAKu0I,kBACTj3I,UAAW,QACX/P,MAAQ,YAAWyS,KAAKu0I,oBAG5Bt2H,EAAO/Y,KACAlF,KAAKg1E,UAAP,oDAGN,CACH,MAAMy/D,EAAiBnkH,EAAW2xB,sBAElC,IAAKwyF,EAKD,OAJAx2H,EAAO/Y,KACAlF,KAAKg1E,UAAP,oDAGE+6D,EAEX,GAAI/vI,KAAKu0I,kBAAmB,CACxBt2H,EAAO/Y,KACAlF,KAAKg1E,UAAP,sCACQy/D,EAAF,gBACEz0I,KAAKu0I,mBAClBjkH,EAAWuyB,YAAY4xF,EAAgBz0I,KAAKu0I,mBAC5C,IAAK,MAAMrjH,KAASZ,EAAWQ,WAC3B,GAAwB,QAApBI,EAAMC,UAAqB,CAC3B,MAAMJ,EAAc+vB,YAAiB5vB,GAC/Bk+G,EAAUruF,YAAmB7vB,GAG/BH,IAAgB0jH,IAChBvjH,EAAMV,MACC,GAAExwB,KAAKu0I,qBAAqBnF,WAK/CpvI,KAAKu0I,kBAAoBE,EACzBx2H,EAAO/Y,KACAlF,KAAKg1E,UAAP,wCACQh1E,KAAKu0I,mBAGtBv0I,KAAKw0I,gBAAiB,EAG1B,OAAOP,EAAe1wF,e,2EC1I9B,gFAMA,MAAMtlC,EAASb,EAAQ,GAAqBW,UAAUM,GAEhD2a,EAAY5b,EAAQ,GAE1B,IAAIs3H,GAA2B,EAC3BC,GAA2B,EAM/B,MAAMC,EAAkB,CACpB,QAAS,UAAW,iBAAkB,UAAW,QAAS,QAAS,aAAc,iBAAkB,YACnG,QAAS,OAAQ,UAAW,aAAc,UAAW,UAAW,WAQrD,MAAM5H,UAAyBroD,IAmB1C1uF,YACQgkE,EACA13C,EACAoqH,EACAprH,EACA+c,EACA3S,EACA6S,EACAxZ,EACAF,EACAghB,GAaJ,GAZAzL,MACI9X,EACAhB,EACA+c,EACA,OAGA3S,EACA6S,GACJx+B,KAAKi6D,IAAMA,EAGS,iBAATj1C,EACP,MAAM,IAAInuB,UAAW,QAAOmuB,qBAEhChlB,KAAKglB,KAAOA,EACZhlB,KAAK2sI,gBAAkBA,EACvB3sI,KAAK8kB,MAAQA,EACb9kB,KAAK8lC,MAAQA,EAEb7nB,EAAOhZ,MAAO,2BAA0BjF,MAKxCA,KAAK60I,aAAe/vH,EAGhB9kB,KAAKi6D,KAAOj6D,KAAKs+B,OACjBt+B,KAAK80I,qBAET90I,KAAK+0I,mBAAqB,GAC1BH,EAAgBxlI,QAAQiY,IACpBrnB,KAAK+0I,mBAAmB1tH,GAASrnB,KAAKg1I,uBAAuBlnJ,KAAKkS,KAAMqnB,KAUhFytH,qBACI90I,KAAKs+B,MAAMjC,iBAAiB,OAAQ,IAAMr8B,KAAKi1I,gBAC/Cj1I,KAAKs+B,MAAMjC,iBAAiB,SAAU,IAAMr8B,KAAKk1I,kBACjDl1I,KAAKs+B,MAAMjC,iBAAiB,QAAS,KACjCpe,EAAOhZ,MAAO,mBAAkBkD,KAAKgM,WAAWnU,UAWxDi1I,eACIh3H,EAAOhZ,MAAO,kBAAiBkD,KAAKgM,WAAWnU,QAE/CA,KAAKi6D,IAAIz6C,aAAa0D,KAAK8V,EAAU1Q,kBAAmBtoB,MAU5Dk1I,iBACIj3H,EAAOhZ,MAAO,oBAAmBkD,KAAKgM,WAAWnU,QAEjDA,KAAKi6D,IAAIz6C,aAAa0D,KAAK8V,EAAUxQ,oBAAqBxoB,MAO9D8rI,QAAQv+I,GACAyS,KAAK8kB,QAAUv3B,IAIfA,IACAyS,KAAK60I,cAAe,GAIpB70I,KAAKuhB,SACLvhB,KAAKuhB,OAAOuD,MAAQv3B,GAGxByS,KAAK8kB,MAAQv3B,EACbyS,KAAKkjB,KAAK05C,qBAAqC58D,OAQnDs+D,UACI,OAAOt+D,KAAK8kB,MAShB63C,mBACI,OAAO38D,KAAK2sI,gBAMhBpnH,UACI,OAAO,EASXgoF,UACI,OAAOvtG,KAAKglB,KAQhB6mH,cAAc5kI,GACNjH,KAAKw+B,YAAcv3B,IAGvBjH,KAAKw+B,UAAYv3B,EACjBjH,KAAKkjB,KAAK05C,0BAA0C31D,IAMxDkuI,gBACI,MAAMluI,EAAOjH,KAAK2qE,eAAiB,QAAU,QAEvCx2D,EAAMjoB,OAAOsuD,YAAYrmC,MAE/BpP,QAAQH,IAAK,iBAAgBqC,OAAWkN,GACxCnU,KAAKuiB,WAAWq7E,qBAAwB32F,EAAF,WAAmBkN,EAKzD,MAAMihI,EAAWlpJ,OAAO4sD,gBAAgB,2BAClCu8F,EAASnpJ,OAAO4sD,gBAAgB,yBAChCw8F,EACC57H,MAAM27H,IAAY37H,MAAM07H,GAAgC,EAApBC,EAASD,EAI9CG,EAAOphI,GACNnU,KAAKuiB,WAAWq7E,qBAAqB,oBAClC59F,KAAKuiB,WAAWq7E,qBAAqB,eACzC03C,EAENt1I,KAAKuiB,WAAWq7E,qBAAwB32F,EAAF,SAAiBsuI,EACvDxwI,QAAQH,IAAK,eAAcqC,OAAWsuI,GAEtCn2H,IAAWoI,cAAc6E,YACrB,CACI,WAAcplB,EACd6d,MAAO9kB,KAAK60I,aACZtnJ,MAAOgoJ,KAYnBrvD,mBAAmBF,GACV0uD,GAA4B10I,KAAK6oC,gBAC9B8rG,GAA4B30I,KAAK2qE,iBAIrC3qE,KAAK6oC,iBACL6rG,GAA2B,GAE3B10I,KAAK2qE,iBACLgqE,GAA2B,GAG/B3uD,EAAU3pD,iBAAiB,UAAWr8B,KAAKm1I,cAAcrnJ,KAAKkS,QAUlEimF,eAAeD,GACX/nE,EAAOhZ,MAAO,2CAA0CjF,MAExD40I,EAAgBxlI,QAAQiY,IACpB2+D,EAAU3pD,iBAAiBhV,EAAOrnB,KAAK+0I,mBAAmB1tH,MAWlE++D,eAAeJ,GACX/nE,EAAOhZ,MAAO,6CAA4CjF,MAE1D40I,EAAgBxlI,QAAQiY,IACpB2+D,EAAU/2C,oBAAoB5nB,EAAOrnB,KAAK+0I,mBAAmB1tH,MASrE2tH,uBAAuB/tI,GACnBgX,EAAOhZ,MAAO,GAAEgC,sDAAyDjH,QAQ7Ew1I,aACI,MAAM,QAAElsE,EAAF,MAAWxkD,EAAX,WAAkB9L,GAAehZ,KAAKs+B,MAE5C,MAAQ,eAActlB,aAAsB8L,eAAmBwkD,IAOnE5yE,WACI,MAAQ,uBAAsBsJ,KAAK28D,6BAA6B38D,KAAKsnC,oBACjEtnC,KAAKutG,mBAAmBvtG,KAAK8lC,kBAAkB9lC,KAAKw1I,oB,4EClUhE,wEAOA,MAAMv3H,EAASF,oBAAUM,GAUV,MAAMqsH,EAOjBz0I,YAAYqtB,GACRtjB,KAAKsjB,IAAMA,EAkBfmyH,+BAA+BnH,GAE3B,MAAMoH,EAAc11I,KAAKsjB,IAAI4jB,eAAe3I,KAE5C,IAAKm3G,EAAYn/I,OACb,OAAO,EACuB,IAAvBm/I,EAAYn/I,QACnB0nB,EAAOjZ,MACAhF,KAAKsjB,IAAP,kEACoCoyH,GAG7C,MAAMplH,EAAag+G,EAAYjrF,YAAY,SAE3C,IAAK/yB,EAKD,OAJArS,EAAOhZ,MACAjF,KAAKsjB,IAAP,4DAGE,EAGX,IAAI67D,GAAW,EAEf,IAAK,MAAM5mB,KAAcm9E,EAAa,CAClC,MAAM5wH,EAAQyzC,EAAW+F,UACnBz9B,EAAc03B,EAAWktB,oBAKzBkwD,EACA90G,GAAe7gC,KAAKsjB,IAAIosH,kBAAkB7uG,GAC1C+0G,EAAgB9wH,IAAU6wH,EAQhC,GANA13H,EAAOhZ,MACF,GAAEjF,KAAKsjB,OAAOi1C,YACXzzC,4BACA6wH,4BACAC,MAEHA,EACD,SAIJ,MAAMC,EACA71I,KAAKsjB,IAAIgnE,gBACLtqF,KAAKsjB,IAAIonE,UAAUorD,UACnB,CAAE91I,KAAKsjB,IAAIinH,eAAegK,mBAEpC,IAAKsB,EAAct/I,OAAQ,CACvB0nB,EAAOjZ,MACF,wBAAuBuzD,QAAiBv4D,KAAKsjB,OAElD,SAGJ67D,GAAW,EAMX7uD,EAAWX,UAAY,WAGvB,MAMMomH,EAAgB,YANFF,EAAc,GAQlC,IAAK,MAAMt0F,KAAWs0F,EAElBvlH,EAAWgxB,WAAWC,GAGtBtjC,EAAOhZ,MACF,GAAEjF,KAAKsjB,6BAA6Bi+B,SACjCgX,KACRjoC,EAAWkxB,iBAAiB,CACxBt6C,GAAIq6C,EACJjkD,UAAW,QACX/P,MAAOwoJ,IAEXzlH,EAAWkxB,iBAAiB,CACxBt6C,GAAIq6C,EACJjkD,UAAW,OACX/P,MAAOgrE,EAAWw7B,aAG1B,GAAI8hD,EAAct/I,OAAS,EAAG,CAC1B,MAAM26B,EAAQ,CACVV,MAAOqlH,EAAc1yI,KAAK,KAC1BguB,UAAW,OAGVb,EAAWmxB,UAAUvwB,EAAMC,UAAWD,EAAMV,SAE7CvS,EAAOhZ,MACF,GAAEjF,KAAKsjB,+BAA+Bi1C,IACvCrnC,GACJZ,EAAW0yB,aAAa9xB,IAQ3BlxB,KAAKsjB,IAAInc,QAAQ4yC,YAClB/5C,KAAKsjB,IAAIqnH,YAAYuJ,gBAAgB5jH,GAI7C,OAAO6uD,EAaX62D,2BAA2B10E,GACvB,MAAM20E,EAAOj2I,KAAKsjB,IAAIpc,GAEtB,IAAK,MAAMyqB,KAAY2vC,EAAa9wC,MAChC,OAAQmB,EAASr0B,WACjB,IAAK,QACL,IAAK,QACL,IAAK,UACDq0B,EAASpkC,MAAQokC,EAASpkC,OAAU,GAAEokC,EAASpkC,SAAS0oJ,IACxD,MACJ,IAAK,OACD,GAAItkH,EAASpkC,MAAO,CAChB,MAAM2oJ,EAAoBvkH,EAASpkC,MAAMwV,MAAM,KAE/C,GAAiC,IAA7BmzI,EAAkB3/I,OAAc,CAChC,MAAMiyC,EAAW0tG,EAAkB,GAC7BxvD,EAAUwvD,EAAkB,GAElCvkH,EAASpkC,MACF,GAAEi7C,KAAYytG,KAAQvvD,KAAWuvD,SAExCh4H,EAAO9Y,KACH,sDACkCwsB,EAASpkC,SAiBnE8gJ,mCAAmCh/G,GAC/B,IAAKA,EACD,MAAM,IAAIh4B,MAAM,mCAGpB,MAAMi3I,EAAc,IAAIrrF,IAAiB5zB,EAAK2C,KAE9C,OAAIhyB,KAAKy1I,+BAA+BnH,GAC7B,IAAI34D,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKs8G,EAAY/qF,aAIlBl0B,EAkBXq/G,2BAA2ByH,GAEvB,IAAKA,IAAgBA,EAAYnkH,MAAQmkH,EAAYlvI,KACjD,OAAOkvI,EAGX,MAAM7H,EAAc,IAAIrrF,IAAiBkzF,EAAYnkH,KAC/CokH,EAAa9H,EAAYjrF,YAAY,SAEvC+yF,GACAp2I,KAAKg2I,2BAA2BI,GAGpC,MAAM9lH,EAAag+G,EAAYjrF,YAAY,SAM3C,OAJI/yB,GACAtwB,KAAKg2I,2BAA2B1lH,GAG7B,IAAIqlD,sBAAsB,CAC7B1uE,KAAMkvI,EAAYlvI,KAClB+qB,IAAKs8G,EAAY/qF,iB,0EC3Q7B,+EAMA,MAAMjtB,EAAclZ,EAAQ,IACtBqhB,EAAYrhB,EAAQ,IACpBu9B,EAAav9B,EAAQ,GAErBa,EAASF,oBAAUM,GAWnBg4H,EAAoB,CACtB,CAAE9/G,MAAO,KACLrC,OAAQ,KACRoiH,OAAQ,EACRliH,IAAK,IACL+W,OAAQ,IACR1wB,IAAK,KACT,CAAE8b,MAAO,KACLrC,OAAQ,IACRoiH,OAAQ,EACRliH,IAAK,KACL+W,OAAQ,KACR1wB,IAAK,KACT,CAAE8b,MAAO,IACLrC,OAAQ,IACRoiH,OAAQ,EACRliH,IAAK,IACL+W,OAAQ,IACR1wB,IAAK,KACT,CAAE8b,MAAO,IACLrC,OAAQ,IACRoiH,OAAQ,EACRliH,IAAK,IACL+W,OAAQ,IACR1wB,IAAK,KACT,CAAE8b,MAAO,IACLrC,OAAQ,IACRoiH,OAAQ,EACRliH,IAAK,IACL+W,OAAQ,IACR1wB,IAAK,KACT,CAAE8b,MAAO,IACLrC,OAAQ,IACRoiH,OAAQ,EACRliH,IAAK,IACL+W,OAAQ,IACR1wB,IAAK,KAab,IAAIs4G,EAAe,IAQfwjB,EAAkB,KAStB,SAASC,EAAU9rD,EAAWr0D,EAAYogH,GAItC,GAAIA,EAAmB,KACnB,OAAO,EAGX,IAAItrG,EAAS,EACTjX,EAAS9oB,KAAKqP,IAAI4b,EAAWnC,OAAQmC,EAAWE,OAEpD,GAAIm0D,EAAW,CAEX,IAAIgsD,EAAkBL,EAAkBplH,KAAKqqB,GAAKA,EAAEpnB,QAAUA,GAE9D,GAAIwiH,EAGA,IAAKxiH,EAASwiH,EAAgBxiH,OAAQA,GAAU,IAAKA,GAAU,EAAG,CAC9D,MAAMyiH,EAAeziH,EAIrB,GAFAwiH,EACML,EAAkBplH,KAAKqqB,GAAKA,EAAEpnB,SAAWyiH,IAC3CD,EAGA,MAFAvrG,GAAUurG,EAAgBvrG,YAMnC,CAGH,MAAMyrG,EAASvgH,EAAWE,MAAQF,EAAWnC,OAGzCiX,EADAyrG,GAAU,MACD,IACFA,GAAU,OACR,KACFA,GAAU,OACR,IAEA,KAMjB,OAAOxrI,KAAKqP,IAAI0wB,EASpB,SAAgBsrG,GACZ,GAAIA,EAAmB,IACnB,OAAOl9H,OAAOqmC,iBAMlB,OAAOmzE,EAAe3nH,KAAKsP,IAAI,KAAM+7H,EAAmB,KAjBhCI,CAAOzrI,KAAKgpB,IAAI,EAAGqiH,EAAmB,OA0BnD,MAAMz3C,EAOjB/oG,YAAYssB,EAAY/C,EAAcrY,GAClCnH,KAAKwf,aAAeA,EAKpBxf,KAAKumH,YAAchkG,EAKnBviB,KAAK82I,YAAc,CACf/3C,kBAAmB,IACnBg4C,YAAQx2I,GAMZP,KAAKg3I,8BAAgC,EAMrCh3I,KAAKi3I,aAAe,GAMpBj3I,KAAKk3I,mBAAqB,EAM1Bl3I,KAAKm3I,mBAAqB,EAO1Bn3I,KAAKo3I,uBAAyB,EAG1BjwI,EAAQqsB,OAAOu/F,cAAgB5rH,EAAQqsB,OAAOu/F,aAAe,IAC7DA,EAAe5rH,EAAQqsB,OAAOu/F,cAKlCxwG,EAAWZ,GACP01H,yBACA,KACIr3I,KAAKs3I,8BAA8B,GACnCt3I,KAAKwf,aAAa0D,KACdq0H,sBACAv3I,KAAK82I,aACT92I,KAAKw3I,yBAGbj1H,EAAW48B,KAAKvb,YACZ+W,EAAW3nD,6BACX,CAACq3G,EAAe7sC,KACP6sC,EAAcvkE,OAAsB,cAAb03B,IACxBx9D,KAAKk3I,kBAAoBhrJ,OAAOsuD,YAAYrmC,SAMxDoO,EAAWZ,GACP01H,4BACA,CAACh7E,EAAa5pC,KAnOC,UAoOPA,EAAQxrB,MACRjH,KAAKy3I,mBACDp7E,EAAYG,QAAS/pC,EAAQrP,UAa7Cb,EAAW4B,WAAWlC,2BAClBjiB,KAAK03I,kBAAkB5pJ,KAAKkS,OAGhCuiB,EAAWZ,GACP01H,qBACA/4G,IACQA,EAAMqsC,iBACFrsC,EAAMggC,UACNt+D,KAAKm3I,mBAAqB,EAE1Bn3I,KAAK23I,4BAIrBp1H,EAAWZ,GACP01H,cACA/4G,IACQA,EAAMqsC,iBAAmBrsC,EAAMggC,WAC/Bt+D,KAAK23I,2BAGjBp1H,EAAW03C,IAAIt4C,GACXqX,6CACAsF,IACIt+B,KAAK82I,YAAYjkD,qBAAuBv0D,EAAMu0D,uBAGtDtwE,EAAWZ,GACP01H,wBACAvsC,IACI9qG,KAAK82I,YAAYhsC,aAAeA,IAGxCvoF,EAAWZ,GACP01H,qBACA/vH,IACItnB,KAAK82I,YAAYc,YACXr+H,QAAQ+N,GAAc,IAAI,mBAS5CqwH,yBACQ33I,KAAKm3I,kBAAoB,IACzBn3I,KAAKm3I,kBAAoBjrJ,OAAOsuD,YAAYrmC,OAapD0jI,4BAA4Br5G,EAAW8/B,EAASw5E,GAI5C,MAAMzhH,EAAaC,EAAYwhH,GAE/B,IACI/4B,EADAg5B,EAAU,IA2Bd,GAtBI/3I,KAAK82I,YAAY/3B,aACjBA,EAAa/+G,KAAK82I,YAAY/3B,WAAWH,OAgBrCtgD,IACAygD,GAAc,KAIlBzgD,IAAYjoC,GAAcmI,IAAcC,EAAUC,SAC/C1+B,KAAKk3I,kBAAoB,GACzBl3I,KAAKm3I,kBAAoB,OAGT52I,IAAfw+G,GACA9gG,EAAOjZ,MAAM,6DAEb+yI,EAAU,KAEVA,EADOh5B,GAAc,EACX,IACHA,GAAc,EACX,GACHA,GAAc,EACX,GACHA,GAAc,EACX,GACHA,GAAc,GACX,GAEA,MAEX,CAIH,MAAMi5B,EAAYh4I,KAAKumH,YAAYjgB,0BAC7Bhc,EACA3xD,QAAQq/G,GAAaA,EAAU1tD,iBAE/B2tD,EACAD,GAAaA,EAAUE,kBACtBF,EAAUE,iBAAiBC,kBAAkB,UAK/CF,GAAsB1B,IACvBv2I,KAAKo3I,sBAAwBlrJ,OAAOsuD,YAAYrmC,MAGhD4+G,EAAewjB,GAEnBA,EAAkB0B,EASlB,IAAI9sG,EACEqrG,EAAUlsD,EAAej0D,EAPNnqC,OAAOsuD,YAAYrmC,MACtC/I,KAAKgpB,IAAIp0B,KAAKm3I,kBACZn3I,KAAKk3I,kBACLl3I,KAAKo3I,wBAMbjsG,EAAS//B,KAAKqP,IAAI,GAAM0wB,EAxVT,MA0VXorG,IACAprG,EAAS//B,KAAKqP,IAAI0wB,EAAQorG,IAG9BwB,EAAU,IAAM/3I,KAAK82I,YAAYn4B,QAAQC,OAASzzE,EAG9C4zE,GAAcA,GAAc,KAC5Bg5B,EAAU3sI,KAAKqP,IAAIs9H,EAAS,KAKpC,GAAI/3I,KAAKg3I,6BAA+B,EAAG,CACvC,MAAMoB,EAAuB,EACvBC,EAAwBr4I,KAAK82I,YAAY/3C,kBACzCu5C,GACCpsJ,OAAOsuD,YAAYrmC,MAAQnU,KAAKg3I,8BAC7B,IAEVe,EACM3sI,KAAKqP,IACHs9H,EACAM,EACOC,EAAcF,GAGjC,OAAOhtI,KAAKqP,IAAI,IAAKs9H,GAOzBT,8BAA8B/pJ,GAC1ByS,KAAK82I,YAAY/3C,kBAAoBxxG,EACrCyS,KAAKg3I,6BAA+B9qJ,OAAOsuD,YAAYrmC,MAO3DqjI,uBAEI,MAAM79I,EAAO,CACTglH,QAAS3+G,KAAK82I,YAAYn4B,QAC1BI,WAAY/+G,KAAK82I,YAAY/3B,WAC7BhgB,kBAAmB/+F,KAAK82I,YAAY/3C,kBACpCg4C,OAAQ/2I,KAAK82I,YAAYC,OACzBjsC,aAAc9qG,KAAK82I,YAAYhsC,aAC/BjY,qBAAsB7yF,KAAK82I,YAAYjkD,qBACvC0xB,eAAgBvkH,KAAK82I,YAAYtyB,qBAGrC,IACIxkH,KAAKumH,YAAY1Y,yBAAyB,CACtC5mG,KApcW,QAqcXmc,OAAQzpB,IACd,MAAON,KAmBbq+I,kBAAkBp0H,EAAK3pB,GAEnB,IAAK2pB,EAAIwiB,MAAO,CACZ,MAAMixG,EACAp9I,EAAKikB,WACAjkB,EAAKikB,UAAUrnB,QAAUoD,EAAKikB,UAAU,GAAGyN,IAEtDrrB,KAAK82I,YAAYC,OAASA,QAAkBx2I,EAKhD,GAAI+iB,IAAQtjB,KAAKumH,YAAYjgB,0BACzB,OAGJ,IAAIz4G,EACJ,MAAM0qJ,GACCv4I,KAAKumH,YAAYtY,0BAClB9H,EACAnmG,KAAKumH,YAAYv/E,qBACjBxI,EACA2nE,EAAkBA,EAAgB3nE,eAAYj+B,EAC9C+9D,GAAU6nC,GAAkBA,EAAgB7nC,UAC5CjoC,EAAa8vE,EACb/6F,KAAKqP,IAAI0rF,EAAgB9vE,WAAY8vE,EAAgBtT,sBAAwB,KAOnF,IAAKhlG,KALAywE,GACDt+D,KAAK23I,yBAIGh+I,EACJA,EAAKxL,eAAeN,KACpBmS,KAAK82I,YAAYjpJ,GAAO8L,EAAK9L,IAKjC0qJ,GACAv4I,KAAKs3I,8BACDt3I,KAAK63I,4BACDr5G,EACA8/B,EACAjoC,IAGZr2B,KAAKwf,aAAa0D,KACdq0H,sBACAv3I,KAAK82I,aACT92I,KAAKw3I,uBAQTC,mBAAmBvwI,EAAIvN,GAEnBqG,KAAKi3I,aAAa/vI,GAAM,CACpBy3G,QAAShlH,EAAKglH,QACdI,WAAYplH,EAAKolH,WACjBhgB,kBAAmBplG,EAAKolG,kBACxBg4C,OAAQp9I,EAAKo9I,OACbjsC,aAAcnxG,EAAKmxG,aACnBjY,qBAAsBl5F,EAAKk5F,qBAC3B0xB,eAAgB5qH,EAAK4qH,gBAGzBvkH,KAAKwf,aAAa0D,KACdq0H,uBACArwI,EACAlH,KAAKi3I,aAAa/vI,IAO1BqpG,WACI,OAAOvwG,KAAK82I,gB,sFC1jBpB,wEAMA,MAAM74H,EAASF,oBAAUM,GAaV,MAAM8vF,EAKjBl4G,YAAYssB,GACRviB,KAAKumH,YAAchkG,EASvBi2H,kBACI,MAAM,iBAAEC,GAAqBz4I,KAAKumH,YAAYp/G,QAAQqsB,OAChDklH,OAAiD,IAArBD,IAAqCA,EACjEjqB,EAA6BxuH,KAAKumH,YAAYpnE,KAAKqvE,6BACnDmqB,EAAyBnqB,IAA+BiqB,EAM9D,GAJAx6H,EAAO/Y,KACA,iCAAqBuzI,qCACajqB,GAErCkqB,IAAwBD,IAAqBjqB,EAM7C,OALAvwG,EAAO/Y,KAAK,kDACZlF,KAAKumH,YAAY/mG,aAAa0D,KAC1BP,oBACA8kF,cAKR,MAAMmxC,EAAgB54I,KAAKumH,YAAYnoB,iBACjCy6C,EAAkBD,GAAiBA,EAAcx/D,wBAElDw/D,EAE0B,cAApBC,EACP56H,EAAO/Y,KAAK,qDAEZ+Y,EAAO/Y,KACA,mEAAa2zI,+BACcF,GAC9BA,EACA34I,KAAKumH,YAAYnoB,iBAAiBvqB,UAC9B,KACI51D,EAAO/Y,KAAK,6CAEhBF,IACIiZ,EAAOjZ,MAAO,8CAA6CA,EAAMP,UAClE,CACC+K,OAAQ,qBACRwkE,kBAAmB,aACnBE,gBAAgB,EAChBJ,sBAAsB,IAG9B9zE,KAAKumH,YAAYnoB,iBAAiB7tB,6BArBtCtyD,EAAO9Y,KAAK,8CA6BpB4X,QAUI/c,KAAKumH,YAAYlnG,KAAK28B,KAAK,MAAOthB,KAC9B,KACS16B,KAAK84I,YACN94I,KAAK+4I,kBAAoB7sJ,OAAOqe,WAAW,KACvCvK,KAAK+4I,uBAAoBx4I,EACzBP,KAAKw4I,mBACN,OAGXxzI,IACIiZ,EAAOjZ,MAAM,8CAA+CA,KAOxE2tD,SACI3yD,KAAK84I,WAAY,EACjB5sJ,OAAOuhB,aAAazN,KAAK+4I,uB,qFCnHjC,qFAoBe,MAAMv2C,UAA+B/iF,IAOhDxpB,YAAYssB,GACR8X,QAEAr6B,KAAKumH,YAAchkG,EACnBviB,KAAKg5I,gBAAkB,KACvBh5I,KAAKi5I,eAAiB,KAEjBn5H,IAAQ0mE,yBACTjkE,EAAW4B,WAAW1C,sBAAsBzhB,KAAKk5I,YAAYprJ,KAAKkS,OAEtEuiB,EAAWZ,GAAGgB,cAAmC3iB,KAAKu2F,YAAYzoG,KAAKkS,OAM3Em5I,uBACI1rI,aAAazN,KAAKg5I,iBAClBh5I,KAAKg5I,gBAAkB,KAU3BI,6BAA6BzwG,GAGzB,MAAM54B,EAAwB,IAAf44B,EAIa,OAAxB3oC,KAAKi5I,gBAA2Bj5I,KAAKi5I,iBAAmBlpI,IACxD/P,KAAKi5I,eAAiBlpI,EACtB/P,KAAKkjB,KAAKg/E,2BAA0CliG,KAAKi5I,iBAUjEI,6BAA6B1wG,GACrB3oC,KAAKs5I,cAIU,IAAf3wG,GAAqB3oC,KAAKg5I,gBAMJ,IAAfrwG,GAAoB3oC,KAAKg5I,iBAChCh5I,KAAKm5I,uBANLn5I,KAAKg5I,gBAAkBzuI,WAAW,KAC9BvK,KAAKs5I,aAAc,EAEnBt5I,KAAKkjB,KAAKg/E,mBAvEA,MAuFtBg3C,YAAY51H,EAAK0B,EAAM2jB,EAAYpjB,GAE/B,IAAKA,IAAYvlB,KAAKu5I,YAClB,OAIJ,MAAMjuD,EAAahoE,EAAIgoE,WAAWn+F,IAAI6S,KAAKu5I,YAAY12G,OAIlDyoD,GAAeA,EAAW96D,MAAMiD,SAASzO,KAM9ChlB,KAAKo5I,6BAA6BzwG,GAClC3oC,KAAKq5I,6BAA6B1wG,IAStC4tD,YAAYj4D,GACJA,EAAMknD,sBAENxlF,KAAKu5I,YAAcj7G,EACnBt+B,KAAKs5I,aAAc,EACnBt5I,KAAKm5I,uBAGDr5H,IAAQ0mE,0BACRloD,EAAM3c,GACFi7C,iBACAj0B,IACI3oC,KAAKq5I,6BAA6B1wG,KAG1CrK,EAAM3c,GACFi7C,4BACAj0B,IACI3oC,KAAKq5I,6BAA6B1wG,GAClC3oC,KAAKo5I,6BAA6BzwG,U,6BCjJ1D,6DAkBe,MAAMm6D,EAMjB7sG,YAAYssB,GACRviB,KAAKuiB,WAAaA,EAElBA,EAAW8Z,iBACP1Z,4BACA3iB,KAAKk5I,YAAYprJ,KAAKkS,OAE1BA,KAAKw5I,SAAWx5I,KAAKuiB,WAAW25C,WASpCg9E,YAAYhyI,EAAIyhC,IAKP3oC,KAAKuiB,WAAW46C,eACdx0B,GAtCiB,IAuChBzhC,IAAOlH,KAAKw5I,UACLx5I,KAAKuiB,WAAW4kB,qBAAqBm3B,WAIpDt+D,KAAKuiB,WAAW03C,IAAIz6C,aAAa0D,KAC7B8V,IAAUtlC,yBACVwT,M,6BCtDZ,8DAuCe,MAAMm7F,UAA0B5iF,eAM3CxpB,cACIokC,QAMAr6B,KAAKy5I,aAAc,EAKnBz5I,KAAK05I,YAAc,GAKnB15I,KAAK25I,eAAiB,GAKtB35I,KAAK45I,SAAU,EAEf55I,KAAK65I,qBAAuB75I,KAAK65I,qBAAqB/rJ,KAAKkS,MAS/D65I,uBACI,MAAMC,EAAWj6F,YAAiB7/C,KAAK05I,aACjCK,EAAgBl6F,YAAiB7/C,KAAK25I,gBAExCG,EAxEoB,IAwEkBC,EAlEd,MAmExB/5I,KAAKkjB,KAAKwzB,oBAEV12C,KAAKg6I,iBAAgB,IAIzBh6I,KAAKqI,QAST4xI,cAAc3xD,EAAU4xD,GACpBl6I,KAAK05I,YAAYx2I,KAAKolF,GACtBtoF,KAAK25I,eAAez2I,KAAKg3I,GAS7BF,gBAAgB1zD,GACZtmF,KAAK45I,QAAUtzD,EACftmF,KAAKkjB,KAAKuzB,wBAAuBz2C,KAAK45I,SAQ1C5iD,gBAAgB14B,GAEZt+D,KAAKg6I,iBAAiB17E,GACtBt+D,KAAKqI,QAQT4zD,WACI,OAAOj8D,KAAK45I,QAQhBvxI,QACIrI,KAAKy5I,aAAc,EACnBz5I,KAAK05I,YAAc,GACnB15I,KAAK25I,eAAiB,GACtBlsI,aAAazN,KAAKm6I,iBAatBrjD,gBAAgBxO,GACZ,GAAKtoF,KAAK45I,QAKV,GAAI55I,KAAKy5I,YAAT,CAEI,MAAMW,EAAiBr6F,YAAqBuoC,EAASI,SAErD1oF,KAAKi6I,cAAc3xD,EAASG,MAAO5oC,YAAiBu6F,SAOxD,GAAI9xD,EAASG,MAvJK,GAuJsB,CACpC,MAAM2xD,EAAiBr6F,YAAqBuoC,EAASI,SAC/CwxD,EAAcr6F,YAAiBu6F,GAEjCF,EArJkB,MAsJlBl6I,KAAKy5I,aAAc,EACnBz5I,KAAKi6I,cAAc3xD,EAASG,MAAOyxD,GAGnCl6I,KAAKm6I,gBAAkB5vI,WAAWvK,KAAK65I,qBApJpB,W,6BClCnC,8DAkCe,MAAM53C,UAA8BxiF,eAK/CxpB,cACIokC,QAMAr6B,KAAKy5I,aAAc,EAKnBz5I,KAAK05I,YAAc,GAKnB15I,KAAK45I,SAAU,EAEf55I,KAAKq6I,mBAAqBr6I,KAAKq6I,mBAAmBvsJ,KAAKkS,MAQ3Dq6I,qBACkBx6F,YAAiB7/C,KAAK05I,aAxDlB,KA2Dd15I,KAAKkjB,KAAK2zB,wBAIV72C,KAAKg6I,iBAAgB,IAIzBh6I,KAAKqI,QAST2xI,gBAAgB1zD,GACZtmF,KAAK45I,QAAUtzD,EACftmF,KAAKkjB,KAAKuzB,wBAAuBz2C,KAAK45I,SAQ1C5iD,gBAAgB14B,GAEZt+D,KAAKg6I,gBAAgB17E,GACrBt+D,KAAKqI,QAQT4zD,WACI,OAAOj8D,KAAK45I,QAYhB9iD,gBAAgBxO,GACPtoF,KAAK45I,UAKN55I,KAAKy5I,YACLz5I,KAAK05I,YAAYx2I,KAAKolF,EAASG,OAO/BH,EAASG,MArHG,KAsHZzoF,KAAKy5I,aAAc,EACnBz5I,KAAK05I,YAAYx2I,KAAKolF,EAASG,OAG/BzoF,KAAKm6I,gBAAkB5vI,WAAWvK,KAAKq6I,mBA/GhB,OAwH/BhyI,QACIrI,KAAKy5I,aAAc,EACnBz5I,KAAK05I,YAAc,GACnBjsI,aAAazN,KAAKm6I,oB,8BCxJ1B,sFASA,MAAMl8H,EAASF,oBAAUM,GAiBzB,MAAMi8H,EAOFrkJ,YAAYomE,EAAa6kC,GAErBlhG,KAAKq8D,YAAcA,EAGnBr8D,KAAKkhG,QAAUA,EAGflhG,KAAKkH,GAAKm1D,EAAYG,QAGtBx8D,KAAKu6I,SAAW,GAIhBv6I,KAAKw6I,cAAgB,EAErBx6I,KAAKy6I,eAAiBz6I,KAAKy6I,eAAe3sJ,KAAKkS,MAC/CA,KAAK06I,YAAc16I,KAAK06I,YAAY5sJ,KAAKkS,MACzCA,KAAK26I,eAAiB36I,KAAK26I,eAAe7sJ,KAAKkS,MAC/CA,KAAK46I,mBAAqB56I,KAAK46I,mBAAmB9sJ,KAAKkS,MACvDA,KAAKwnB,cAAgBxnB,KAAKwnB,cAAc15B,KAAKkS,MAIzCkhG,EAAQ25C,mBACR76I,KAAK06I,cAGT16I,KAAK86I,aAAe5uJ,OAAOowC,YACvBt8B,KAAK06I,YAAax5C,EAAQ65C,gBAC9B/6I,KAAKg7I,kBAAoB9uJ,OAAOqe,WAC5BvK,KAAK46I,mBAAoB56I,KAAKkhG,QAAQ+5C,qBAO9CR,iBACQz6I,KAAK86I,cACL5uJ,OAAOouC,cAAct6B,KAAK86I,cAE1B96I,KAAKg7I,mBACL9uJ,OAAOouC,cAAct6B,KAAKg7I,mBAQlCN,cACI,MAAMQ,EAAYl7I,KAAKw6I,gBACjBW,EAAiB,CACnBl0I,KAzEa,mBA0EbC,GAAIg0I,GAGRl7I,KAAKkhG,QAAQl4D,YAAYmyG,EAAgBn7I,KAAKkH,IAC9ClH,KAAKu6I,SAASW,GAAa,CACvBh0I,GAAIg0I,EACJE,SAAUlvJ,OAAOsuD,YAAYrmC,OAQrCwmI,eAAehoI,GACX,MAAMmmE,EAAU94E,KAAKu6I,SAAS5nI,EAASzL,IAEnC4xE,IACAA,EAAQztD,IAAMn/B,OAAOsuD,YAAYrmC,MAAQ2kE,EAAQsiE,SACjDp7I,KAAKkhG,QAAQ1hF,aAAa0D,KACtBm4H,kBACAr7I,KAAKq8D,YACLyc,EAAQztD,MAGhBrrB,KAAK46I,qBASTA,qBACI,MAAMzmI,EAAMjoB,OAAOsuD,YAAYrmC,MAI/B,IACI2kE,EAASoiE,EADT7vH,EAAMiwH,IAIV,IAAKJ,KAAal7I,KAAKu6I,SACfv6I,KAAKu6I,SAASpsJ,eAAe+sJ,KAC7BpiE,EAAU94E,KAAKu6I,SAASW,GAEpBpiE,EAAQsiE,SAAWjnI,EAAMnU,KAAKkhG,QAAQ+5C,2BAE/Bj7I,KAAKu6I,SAASW,GACdpiE,EAAQztD,MACfA,EAAMjgB,KAAKqP,IAAI4Q,EAAKytD,EAAQztD,OAKpCA,EAAMiwH,KACNt7I,KAAKwnB,cAAc6D,GAQ3B7D,cAAc6D,GACVjM,IAAWoI,cAAc0D,YACrBlrB,KAAKkH,GACLlH,KAAKq8D,YAAY6yC,YAAY,UAC7B7jF,KAmBG,MAAM81E,EAMjBlrG,YAAYssB,EAAYpb,EAAS6hC,GAC7BhpC,KAAKuiB,WAAaA,EAClBviB,KAAKwf,aAAe+C,EAAW/C,aAC/Bxf,KAAKgpC,YAAcA,EAGnBhpC,KAAK+6I,eAAiB,IAGtB/6I,KAAKi7I,oBAAsB,IAG3Bj7I,KAAK+8D,aAAe,GAGpB/8D,KAAK66I,mBAAoB,EAErB1zI,GAAWA,EAAQ+5F,UACyB,iBAAjC/5F,EAAQ+5F,QAAQ45C,eACvB96I,KAAK+6I,eAAiB5zI,EAAQ+5F,QAAQ45C,cAEO,iBAAtC3zI,EAAQ+5F,QAAQ85C,oBACvBh7I,KAAKi7I,oBAAsB9zI,EAAQ+5F,QAAQ85C,mBAI3Ch7I,KAAKi7I,oBAAsB,GAAKj7I,KAAKi7I,oBACnCj7I,KAAK+6I,iBACP/6I,KAAKi7I,oBAAsBj7I,KAAK+6I,iBAGxC98H,EAAO/Y,KACF,uCACGlF,KAAK+6I,qCACL/6I,KAAKi7I,wBAEbj7I,KAAKu7I,kBAAoBv7I,KAAKu7I,kBAAkBztJ,KAAKkS,MACrDuiB,EAAWZ,GACPgB,cACA3iB,KAAKu7I,mBAETv7I,KAAKw7I,gBAAkBx7I,KAAKw7I,gBAAgB1tJ,KAAKkS,MACjDuiB,EAAWZ,GACPgB,YACA3iB,KAAKw7I,iBAETx7I,KAAKy7I,gBAAkBz7I,KAAKy7I,gBAAgB3tJ,KAAKkS,MACjDuiB,EAAWZ,GACPgB,4BACA3iB,KAAKy7I,iBAETz7I,KAAK07I,kBAAoB17I,KAAK07I,kBAAkB5tJ,KAAKkS,MACrDuiB,EAAWZ,GACPgB,sBACA3iB,KAAK07I,mBAObA,oBACI17I,KAAK66I,mBAAoB,EASzB,IAAK,MAAM3zI,KAAMlH,KAAK+8D,aAClB,GAAI/8D,KAAK+8D,aAAa5uE,eAAe+Y,GAAK,CACtC,MAAMy0I,EAAqB37I,KAAK+8D,aAAa71D,GAE7Chb,OAAOqe,WAAWoxI,EAAmBjB,YAAa,MAW9De,gBAAgBp/E,EAAa5pC,GA9PR,qBAiQbA,EAAQxrB,KACRjH,KAAK47I,cAAcv/E,EAAYG,QAAS/pC,GA5P1B,sBA6PPA,EAAQxrB,MACfjH,KAAK26I,eAAet+E,EAAYG,QAAS/pC,GAWjD8oH,kBAAkBr0I,EAAIm1D,GACdr8D,KAAK+6I,gBAAkB,IAIvB/6I,KAAK+8D,aAAa71D,KAClB+W,EAAO/Y,KACF,0CAAyCgC,gBAC9ClH,KAAK+8D,aAAa71D,GAAIuzI,wBACfz6I,KAAK+8D,aAAa71D,IAG7BlH,KAAK+8D,aAAa71D,GAAM,IAAIozI,EAAmBj+E,EAAar8D,OAQhEw7I,gBAAgBt0I,GACRlH,KAAK+6I,gBAAkB,GAIvB/6I,KAAK+8D,aAAa71D,KAClBlH,KAAK+8D,aAAa71D,GAAIuzI,wBACfz6I,KAAK+8D,aAAa71D,IAWjC00I,cAAczwH,EAAe2tD,GAEzB,GAAIA,GAAWA,EAAQ5xE,GAAI,CACvB,MAAMyL,EAAW,CACb1L,KAnTU,oBAoTVC,GAAI4xE,EAAQ5xE,IAGhBlH,KAAKgpC,YAAYr2B,EAAUwY,QAE3BlN,EAAO/Y,KACF,6CAA4CimB,MAUzDwvH,eAAexvH,EAAexY,GAC1B,MAAMgpI,EAAqB37I,KAAK+8D,aAAa5xC,GAEzCwwH,GACAA,EAAmBhB,eAAehoI,GAO1CiR,OACI3F,EAAO/Y,KAAK,oBAEZlF,KAAKuiB,WAAWksB,IACZ9rB,cACA3iB,KAAKu7I,mBACTv7I,KAAKuiB,WAAWksB,IACZ9rB,YACA3iB,KAAKw7I,iBACTx7I,KAAKuiB,WAAWksB,IACZ9rB,4BACA3iB,KAAKy7I,iBACTz7I,KAAKuiB,WAAWksB,IACZ9rB,sBACA3iB,KAAK07I,mBAET,IAAK,MAAMx0I,KAAMlH,KAAK+8D,aACd/8D,KAAK+8D,aAAa5uE,eAAe+Y,IACjClH,KAAK+8D,aAAa71D,GAAIuzI,iBAI9Bz6I,KAAK+8D,aAAe,O,uEC1X5B,gEAMA,MAAM9+C,EAASF,oBAAUM,GAOV,MAAMukF,EAKjB3sG,YAAYssB,GACRviB,KAAKumH,YAAchkG,EASnBviB,KAAK67I,SAAU,EAEf77I,KAAKumH,YAAYlqF,iBACb1Z,cAAmC,IAAM3iB,KAAK87I,kBAClD97I,KAAKumH,YAAYlqF,iBACb1Z,YAAiC,IAAM3iB,KAAK87I,kBAChD97I,KAAKumH,YAAYlqF,iBACb1Z,aAAkC,IAAM3iB,KAAK87I,kBAOrDA,iBACI,MAAMC,EAAY/7I,KAAK67I,QACjBv/E,GACCt8D,KAAKumH,YAAYppD,eACbn9D,KAAKumH,YAAYte,uBAAyB,EAEjD8zC,IAAcz/E,IACdt8D,KAAK67I,QAAUv/E,EACfr+C,EAAOhZ,MAAO,iBAAgB82I,QAAgBz/E,KAC9Ct8D,KAAKumH,YAAY/mG,aAAa0D,KAC1BP,gBAAqCo5H,EAAWz/E,Q,iFCpDhE,qDAUO,MAAMglC,EAOTrrG,YAAYssB,GACRviB,KAAKuiB,WAAaA,EAClBviB,KAAKuiB,WAAWZ,GACZgB,yBACA1K,GAAWjY,KAAKyoE,uBAAuBxwD,IAC3CjY,KAAKuiB,WAAWZ,GACZgB,gCACA,IAAM3iB,KAAKg8I,gCAWnBvzE,uBAAuBwzE,GACnBA,EAAar4G,YACTurC,IAAmBC,iCACnBn3D,IACQA,IAAYjY,KAAKuiB,WAAWuhF,0BAC5B9jG,KAAKg8I,iCAGjBh8I,KAAKk8I,gCACED,EAAa72G,2BAA2BplC,KAAKk8I,gCAGpDD,EAAaroE,sCAGbqoE,EAAaxoE,uBAUjBuoE,+BACI,MAAMG,EAAqBn8I,KAAKo8I,2BAC1B1hE,EAAW,GAEjB,GAAIyhE,GAAsB,EACtB,IAAK,MAAMlkI,KAAWjY,KAAKuiB,WAAWsnD,oBAClC6Q,EAASx3E,KAAK+U,EAAQ07D,yBAAyBwoE,IAIvD,OAAOn/G,QAAQ+K,IAAI2yC,GASvB0hE,2BACI,MAAMC,EAAqBr8I,KAAKuiB,WAAWuhF,yBACrC93B,EAA2BqwE,GAAsBA,EAAmBhtE,8BAE1E,OAAIrvE,KAAKs8I,6BAA+B,GAAKtwE,GAA4B,EAC9D5gE,KAAKqP,IAAIza,KAAKs8I,4BAA6BtwE,GAC3CA,GAA4B,EAC5BA,EAGJhsE,KAAKs8I,4BAOhB9rC,kCAAkCnrE,GAC9BrlC,KAAKk8I,+BAAiC72G,EAEtC,IAAK,MAAMptB,KAAWjY,KAAKuiB,WAAWsnD,oBAClCxkC,GAAkBptB,EAAQmtB,2BAA2BC,GAU7DorE,+BAA+BprE,GAG3B,OAFArlC,KAAKs8I,4BAA8Bj3G,EAE5BrlC,KAAKg8I,kC,4wBC1GpB,MAAM/9H,EAASF,oBAAUM,GA+PV4hF,IAzPf,MAQIhqG,YAAY67G,GAIR9xG,KAAKu8I,UAAY,GAEjBv8I,KAAKw8I,UAAY1qC,EAEjB9xG,KAAKmqH,WAAanqH,KAAKmqH,WAAWr8H,KAAKkS,MAEvCA,KAAKw8I,UAAUh9H,aAAaokB,YACxB+W,IAAWppD,kBAAmByO,KAAKmqH,YAS3CsyB,WAAW11E,GACP,OAAO/mE,KAAKu8I,UAAUx1E,GAe1BojD,YAAW,iBAAEwD,EAAF,SAAoBhnD,IACvB8xB,IAAkBhxB,YAAYd,GAC9B3mE,KAAK08I,qBAAqB/1E,GACnBgnD,GACP3tH,KAAK28I,qBAAqBh2E,GAoBlColC,eAAe5kG,GACX,MAAM8Q,EAAU,IAAIi/E,IAAJ,OACT/vF,GADS,IAEZoN,WAAYvU,KAAKw8I,UAAUjoI,cAG/B,OAAO0D,EAAQ8E,MAAM,CACjBs7E,QAASlxF,EAAQkxF,QACjBC,YAAanxF,EAAQmxF,YACrBC,YAAav4F,KAAKw8I,UAAUjkD,YAC5B/vD,SAAUrhC,EAAQqhC,WAEjB9N,KAAK,KAKG16B,KAAKy8I,WAAWxkI,EAAQs/E,WACzBv3F,KAAK48I,YAAY3kI,GACjBjY,KAAK68I,mBAAmB5kI,IAGrBA,IAEVmlB,MAAMp4B,IACHhF,KAAK68I,mBAAmB5kI,GAEjB+kB,QAAQE,OAAOl4B,KAYlCgnG,cAAcjlC,GACV,MAAM9uD,EAAUjY,KAAKy8I,WAAW11E,GAEhC,OAAI9uD,EACOA,EAAQ2L,KAAK,CAAE20E,YAAav4F,KAAKw8I,UAAUjkD,cAG/Cv7D,QAAQE,OAAO,IAAI7lC,MAAM,2BASpCulJ,YAAY3kI,GACRjY,KAAKu8I,UAAUtkI,EAAQs/E,SAAWt/E,EAYtC6kI,eAAe/1E,EAAWh3D,EAAQtiB,GAC9B,MAAMwqB,EAAU,IAAIi/E,IAAa,CAC7B3iF,WAAYvU,KAAKw8I,UAAUjoI,WAC3BgkF,YAAav4F,KAAKw8I,UAAUjkD,YAC5B9qG,OACAs5E,YACAh3D,WAKJ,OAFA/P,KAAK48I,YAAY3kI,GAEVA,EASX4kI,mBAAmB5kI,EAAS4uD,GACxB7mE,KAAKw8I,UAAUh9H,aAAa0D,KACxBy3B,IAAWhpD,uBAAwBsmB,EAAS4uD,GAUpD61E,qBAAqB/1E,GACjB,MAAMC,EAAc6xB,IAAkB/xB,wBAAwBC,GAE9D,IAAKC,EACD,OAGJ,MAAM,MAAE5hE,EAAF,UAAS6hE,EAAT,cAAoBC,EAApB,UAAmCC,EAAnC,OAA8Ch3D,GAAW62D,EAI/D,IAAI3uD,EAAUjY,KAAKy8I,WAAW11E,GAKzB9uD,GAAsB,QAAXlI,EAaZkI,GACGA,EAAQ4/E,cAAgB9nF,GACxBkI,EAAQq/E,aAAetyF,EAC1BiZ,EAAO9Y,KAAK,uCACRyH,KAAKwL,UAAUwuD,KAKlB3uD,IACDA,EAAUjY,KAAK88I,eAAe/1E,EAAWh3D,EAAQ+2D,IAGrD7uD,EAAQo/E,UAAUtnF,GAEd/K,GACAiT,EAAQggF,SAASjzF,GAGrBhF,KAAK68I,mBAAmB5kI,EAAS4uD,IA/B7B5oD,EAAO9Y,KACH,qCACA,2CAuCZw3I,qBAAqBh2E,GACjB,MAAM,kBAAEO,EAAF,KAAqBz5E,EAArB,UAA2Bs5E,GAC3B0xB,IAAkBzxB,sBAAsBL,GAE9C,IAAKI,EAID,YAHA9oD,EAAO9Y,KACH,2DAKR,IAAI8S,EAAUjY,KAAKy8I,WAAW11E,GAEzB9uD,IACDA,EAAUjY,KAAK88I,eAAe/1E,EAAW,GAAIt5E,IAGjDwqB,EAAQigF,qBAAqBhxB,GAE7BlnE,KAAK68I,mBAAmB5kI,O,kFClQhC,6FASA,MAAMgG,EAASF,oBAAUM,GAWV,MAAMghF,EAOjBppG,YAAYssB,GACRviB,KAAKumH,YAAchkG,EACnBviB,KAAK+8I,sBAAwB,GAC7B/8I,KAAKg9I,sBAAwB,GAC7Bh9I,KAAKi9I,wBAA0B,GAC/Bj9I,KAAKk9I,mCAAqC,GAC1Cl9I,KAAKm9I,0BAA4Bn9I,KAAKm9I,0BAA0BrvJ,KAAKkS,MACrEA,KAAKo9I,4BAA8Bp9I,KAAKo9I,4BAA4BtvJ,KAAKkS,MACzEA,KAAKq9I,eAAiBr9I,KAAKq9I,eAAevvJ,KAAKkS,MAC/CA,KAAKumH,YAAY5kG,GAAG41H,uBAA8Cv3I,KAAKo9I,6BACvEp9I,KAAKumH,YAAYpiG,WAAWlC,2BAA2BjiB,KAAKm9I,2BAC5Dn9I,KAAKumH,YAAY5kG,GAAG01H,YAA4Br3I,KAAKq9I,gBAUzDD,4BAA4BnxH,GAAQ,eAAEs4F,IAClC,MAAM+4B,EAAmBt9I,KAAKk9I,mCAAmCjxH,GAAU,GAAM,EAIjF,GAFAjsB,KAAKk9I,mCAAmCjxH,GAAUqxH,GAEE,IAAhDt9I,KAAKg9I,sBAAsBp5I,QAAQqoB,IAAmBA,KAAUjsB,KAAKi9I,yBAC9D14B,GAAkB,GAAK+4B,EAAkB,EAChD,OAGJ,MAAMjhF,EAAcr8D,KAAKumH,YAAYtpD,mBAAmBhxC,GAExD,GAAIowC,EAAa,CAGb,GAFeA,EAAY4B,qBAAqB1/B,KAErChoC,OAAS,GAAK8lE,EAAY2qD,eAIjC,OAIR,MAAM96F,EAAmBlsB,KAAK+8I,sBAAsB9wH,GAE/C71B,MAAMC,QAAQ61B,KAAqBA,EAAiButE,MAAM9wD,GAA6B,IAAfA,KACzE3oC,KAAKi9I,wBAAwBhxH,GAAU,CACnCE,kBAAmBo4F,EACnBr4F,iBAAkB,KAY9BixH,0BAA0B75H,GAAK,eAAEihG,IACzBjhG,IAAQtjB,KAAKumH,YAAYjgB,4BAI7Bt5G,OAAOuY,KAAKg/G,GAAgBn1G,QAAQ6c,IAChC,IAAoD,IAAhDjsB,KAAKg9I,sBAAsBp5I,QAAQqoB,GACnC,OAGJ,MAAMC,EAAmBlsB,KAAK+8I,sBAAsB9wH,GAE/C71B,MAAMC,QAAQ61B,GAERA,EAAiB31B,QAxFR,GAyFhB21B,EAAiBwB,QAFjB1tB,KAAK+8I,sBAAsB9wH,GAAU,GAKzCjsB,KAAK+8I,sBAAsB9wH,GAAQ/oB,KAAKqhH,EAAet4F,MAI3Dj/B,OAAOuY,KAAKvF,KAAKi9I,yBAAyB7tI,QAAQ6c,IAC9C,MAAM,iBAAEC,EAAF,kBAAoBC,GAAsBnsB,KAAKi9I,wBAAwBhxH,GAI7E,GAFAC,EAAiBhpB,KAAKqhH,EAAet4F,IAnGjB,IAqGhBC,EAAiB31B,OAAoC,CACrD,GAAI21B,EAAiButE,MAAM9wD,QAAoC,IAAfA,GAA6C,IAAfA,GAAmB,CAC7F,MAAM40G,EAAyB3wI,KAAKwL,UAAU8T,GAE9C9M,IAAWoI,cACPwE,YAA8BC,EAAQsxH,EAAwBpxH,IAClElO,EAAO9Y,KAAM,yEACT8mB,0BAA+BsxH,2BAC/BpxH,KACJnsB,KAAKg9I,sBAAsB95I,KAAK+oB,GAChCjsB,KAAKq9I,eAAepxH,UAGjBjsB,KAAKi9I,wBAAwBhxH,OAWhDoxH,eAAepxH,UACJjsB,KAAK+8I,sBAAsB9wH,GAQtCjJ,UACIhjB,KAAKumH,YAAY93E,IAAI8oG,uBAA8Cv3I,KAAKo9I,6BACxEp9I,KAAKumH,YAAY93E,IAAI4oG,YAA4Br3I,KAAKq9I,gBACtDr9I,KAAKumH,YAAYpiG,WAAWjC,8BAA8BliB,KAAKm9I,2BAC/Dn9I,KAAK+8I,2BAAwBx8I,EAC7BP,KAAKi9I,6BAA0B18I,EAC/BP,KAAKg9I,2BAAwBz8I,EAC7BP,KAAKk9I,wCAAqC38I,EAC1CP,KAAKumH,iBAAchmH,M,6FC5J3B,6HAkBA,MAAM0d,EAASF,oBAAUM,GAMzB,MAAMm/H,EAMFvnJ,YAAYpJ,GACRmT,KAAKnT,KAAOA,EACZmT,KAAK4sC,MAAQ,EACb5sC,KAAK0kH,IAAM,EACX1kH,KAAK8hF,QAAU,GAQnB5hC,QAAQP,GACqB,iBAAdA,EACP1hC,EAAOjZ,MACF,GAAEhF,KAAKnT,iCAAiCmT,KAAK4sC,QAC9C+S,GACIjmC,MAAMimC,KACd3/C,KAAK0kH,KAAO/kE,EACZ3/C,KAAK8hF,QAAQ5+E,KAAKy8C,GAClB3/C,KAAK4sC,OAAS,GAStB6wG,YACI,OAAOz9I,KAAK0kH,IAAM1kH,KAAK4sC,MAQ3B8wG,aAAax4F,GACTA,EAAUllD,KAAKnT,KAAP,QAAqBmT,KAAKy9I,YAClCv4F,EAAUllD,KAAKnT,KAAP,YAAyB+f,KAAKwL,UAAUpY,KAAK8hF,SAOzDz5E,QACIrI,KAAK8hF,QAAU,GACf9hF,KAAK0kH,IAAM,EACX1kH,KAAK4sC,MAAQ,GASrB,MAAM+wG,EAQF1nJ,YAAYgpG,EAAqBn5D,EAAO/3C,GAKpCiS,KAAK8lC,MAAQA,EAOb9lC,KAAK49I,GAAK7vJ,EAQViS,KAAK69I,WAAa,EAMlB79I,KAAK89I,QAAU,IAAIN,EAAkB,OAYrCx9I,KAAK+9I,iBAAmB,IAAIx+H,IAO5Bvf,KAAKg+I,qBAAuB/+C,EAU5Bj/F,KAAKi+I,oBAAiB19I,EAEtBP,KAAKk+I,mBAAqB,CAAC56H,EAAKkD,KACxBxmB,KAAK8lC,QAAUxiB,EAAIwiB,OACnB9lC,KAAKm+I,mBAAmB33H,IAIhC,MAAMjE,EAAa08E,EAAoBsnB,YAEvChkG,EAAW4B,WAAWlC,2BAClBjiB,KAAKk+I,oBAEJl+I,KAAK8lC,QACN9lC,KAAK+6D,YAAc7zD,GAAMlH,KAAK+9I,iBAAiBr6H,OAAOxc,GACtDqb,EAAWZ,GAAG01H,YAA4Br3I,KAAK+6D,aAE/C/6D,KAAKo+I,sBACC,CAACl3I,EAAIvN,IAASqG,KAAKq+I,oBAAoBn3I,EAAIvN,GACjD4oB,EAAWZ,GACP41H,uBACAv3I,KAAKo+I,wBASjBD,mBAAmBxkJ,GACf,GAAKA,GAcL,GARImmB,IAAQ6zF,yBACJh6G,EAAKikB,WAAajkB,EAAKikB,UAAUrnB,QACjCyJ,KAAK89I,QAAQ59F,QAAQvmD,EAAKikB,UAAU,GAAGyN,KAI/CrrB,KAAK69I,YAAc,EAEf79I,KAAK69I,YAAc79I,KAAK49I,GAAI,CAC5B,GAAI99H,IAAQ6zF,wBAAyB,CACjC,MAAMpxF,EAAaviB,KAAKg+I,qBAAqBz3B,YAEvC+3B,EAAc,CAChBngG,IAAKn+C,KAAK8lC,MACV,gBAAmBvjB,EAAW0lF,uBAelC,GAZItuG,EAAKikB,WAAajkB,EAAKikB,UAAUrnB,QACjCvJ,OAAOgoG,OAAOspD,EAAa,CACvB,qBACI3kJ,EAAKikB,UAAU,GAAG2jG,mBACtB,sBACI5nH,EAAKikB,UAAU,GAAG6jG,oBACtB,eAAkB9nH,EAAKikB,UAAU,GAAG3W,OAI5CjH,KAAK89I,QAAQJ,aAAaY,GAEtBt+I,KAAK8lC,MAAO,CAEZ,MAAMy4G,EAAgBv+I,KACjBg+I,qBAAqBQ,gBAAgBP,eAErCvkI,MAAM6kI,KAEPD,EAAW,SACLt+I,KAAK89I,QAAQL,YAAcc,OAElC,CAEH,MAAME,EAAez+I,KAAK0+I,yBACpBC,EAAc3+I,KAAK89I,QAAQL,YAEjCz9I,KAAKi+I,eAAiBU,EAAcF,EAE/B/kI,MAAMilI,IAAiBjlI,MAAM+kI,KAE9BH,EAAW,gBAAsBt+I,KAAKi+I,gBAI9C7+H,IAAWoI,cAAcsE,YAAoBwyH,IAGjDt+I,KAAK4+I,uBA5DL3gI,EAAOjZ,MAAM,YAsErB05I,yBACI,IAAI9xG,EAAQ,EAAG83E,EAAM,EAIrB,IAAK,MAAMm6B,KAAa7+I,KAAK+9I,iBAAiB36H,SAAU,CACpD,MAAM07H,EAAMD,EAAUpB,YAEjB/jI,MAAMolI,KACPp6B,GAAOo6B,EACPlyG,GAAS,EACTiyG,EAAUx2I,SAIlB,OAAOq8G,EAAM93E,EAUjByxG,oBAAoBn3I,EAAIvN,GACpB,MAAMolJ,EAAmC,iBAAhBplJ,EAAKo9I,OAC9B,IAAIiI,EAASh/I,KAAK+9I,iBAAiB5wJ,IAAI+Z,IAElC83I,GAAUD,IACXC,EAAS,IAAIxB,EAAqBt2I,EAAF,aAChClH,KAAK+9I,iBAAiB38H,IAAIla,EAAI83I,IAG9BD,EACAC,EAAO9+F,QAAQvmD,EAAKo9I,QACbiI,GACPh/I,KAAK+9I,iBAAiBr6H,OAAOxc,GAQrC03I,iBACI5+I,KAAK89I,QAAQz1I,QACTrI,KAAK+9I,kBACL/9I,KAAK+9I,iBAAiBloG,QAE1B71C,KAAK69I,WAAa,EAMtB76H,UAEI,MAAMT,EAAaviB,KAAKg+I,qBAAqBz3B,YAE7ChkG,EAAW4B,WAAWjC,8BAClBliB,KAAKk+I,oBACJl+I,KAAK8lC,QACNvjB,EAAWksB,IACP8oG,uBACAv3I,KAAKo+I,uBACT77H,EAAWksB,IACP4oG,YACAr3I,KAAK+6D,eAaN,MAAMmkC,EAOjBjpG,YAAYssB,EAAYx0B,GAOpBiS,KAAK49I,GAAK7vJ,EAENA,EAAI,GACJkwB,EAAO/Y,KAAM,0CAAyCnX,aAc1DiS,KAAK69I,WAAa,EAOlB79I,KAAKumH,YAAchkG,EAQnBviB,KAAKi/I,mBACC,IAAIzB,EAAkB,wBAQ5Bx9I,KAAKk/I,qBACC,IAAI1B,EAAkB,0BAQ5Bx9I,KAAKm/I,mBACC,IAAI3B,EAAkB,wBAQ5Bx9I,KAAKo/I,qBACC,IAAI5B,EAAkB,0BAQ5Bx9I,KAAKq/I,gBACC,IAAI7B,EAAkB,oBAQ5Bx9I,KAAKs/I,kBACC,IAAI9B,EAAkB,sBAQ5Bx9I,KAAKu/I,oBACC,IAAI/B,EAAkB,qBAQ5Bx9I,KAAKw/I,iBACC,IAAIhC,EAAkB,sBAQ5Bx9I,KAAKy/I,mBACC,IAAIjC,EAAkB,wBAO5Bx9I,KAAK0/I,cAAgB,IAAIlC,EAAkB,oBAQ3Cx9I,KAAK2/I,oBACC,IAAInC,EAAkB,2BAO5Bx9I,KAAK4/I,aAAe,IAAIpC,EAAkB,mBAQ1Cx9I,KAAK6/I,mBACC,IAAIrC,EAAkB,0BAQ5Bx9I,KAAK8/I,uBACC,IAAItC,EAAkB,iBAQ5Bx9I,KAAK+/I,uBACC,IAAIvC,EAAkB,wBAO5Bx9I,KAAKggJ,sBACC,IAAIxC,EAAkB,gBAQ5Bx9I,KAAKigJ,sBACC,IAAIzC,EAAkB,uBAQ5Bx9I,KAAKkgJ,OAAS,IAAI1C,EAAkB,sBAEpCx9I,KAAKmgJ,2BAAwB5/I,EAE7BP,KAAKogJ,qBAAuBzmJ,IACxBqG,KAAKm+I,mBAAmBxkJ,GACxBqG,KAAKqgJ,kCAAkC1mJ,IAE3C4oB,EAAWZ,GACP41H,sBACAv3I,KAAKogJ,sBAETpgJ,KAAKsgJ,oBAAsB,KACvBriI,EAAOhZ,MAAM,uCACbjF,KAAK4+I,iBACL5+I,KAAKw+I,gBAAgBI,iBACrB5+I,KAAKugJ,gBAAgB3B,kBAEzBr8H,EAAWZ,GACP01H,aACAr3I,KAAKsgJ,qBAETtgJ,KAAKwgJ,uBAAyB,CAACzE,EAAWz/E,MAIpB,IAAdA,IACAr+C,EAAO/Y,KAAK,+BACZlF,KAAKygJ,sBAGbl+H,EAAWZ,GACP01H,gBACAr3I,KAAKwgJ,wBAETxgJ,KAAKw+I,gBACC,IAAIb,EAAmB39I,MAAM,EAAiBjS,GAEpDiS,KAAKugJ,gBACC,IAAI5C,EAAmB39I,MAAM,EAAgBjS,IAtN/CkwB,EAAO/Y,KAAK,uCA+NpBi5I,mBAAmBxkJ,GAEf,IAAKA,EAGD,YAFAskB,EAAOjZ,MAAM,YAKjB,MAAM8gC,EAAQ9lC,KAAKumH,YAAYppD,cACzBujF,EAAW1gJ,KAAKumH,YAAYte,sBAElC,IAAKniE,GAAS46G,EAAW,EAKrB,OAUJ,MAAM/hC,EAAUhlH,EAAKglH,QACfzmF,EAAYv+B,EAAKu+B,UACjB6mF,EAAaplH,EAAKolH,WAClBrnF,EAAY/9B,EAAKklH,UACjBxoF,EAAa18B,EAAK08B,WAExB,GAAKsoF,EAIE,GAAKzmF,EAIL,GAAK6mF,EAIL,GAAKrnF,EAIL,GAAKrB,GA2DZ,GArDAr2B,KAAKi/I,mBAAmB/+F,QAAQy+D,EAAQtpF,MAAMupF,QAC9C5+G,KAAKk/I,qBAAqBh/F,QAAQy+D,EAAQtpF,MAAM0nE,UAEhD/8F,KAAKm/I,mBAAmBj/F,QAAQy+D,EAAQ1qF,MAAM2qF,QAC9C5+G,KAAKo/I,qBAAqBl/F,QAAQy+D,EAAQ1qF,MAAM8oE,UAE5Cj9E,IAAQozF,gCACRlzG,KAAKq/I,gBAAgBn/F,QAAQhoB,EAAU0mF,QACvC5+G,KAAKs/I,kBAAkBp/F,QAAQhoB,EAAU6kE,WAG7C/8F,KAAKw/I,iBAAiBt/F,QAAQ6+D,EAAWH,QACzC5+G,KAAKy/I,mBAAmBv/F,QAAQ6+D,EAAWhiB,UAC3C/8F,KAAKu/I,oBAAoBr/F,QAAQ6+D,EAAWlyB,OAE5C7sF,KAAKkgJ,OAAOhgG,QAAQvmD,EAAKolG,mBAErBrnE,IACA13B,KAAK0/I,cAAcx/F,QACflgD,KAAK2gJ,sBACDjpH,GAAW,EAAoB+G,WACvCz+B,KAAK2/I,oBAAoBz/F,QACrBlgD,KAAK2gJ,sBACDjpH,GAAW,EAAoB+G,YAEvCz+B,KAAK4/I,aAAa1/F,QACdlgD,KAAK2gJ,sBACDjpH,GAAW,EAAkB+G,WACrCz+B,KAAK6/I,mBAAmB3/F,QACpBlgD,KAAK2gJ,sBACDjpH,GAAW,EAAkB+G,aAGrCpI,IACAr2B,KAAK8/I,uBAAuB5/F,QACxBlgD,KAAK4gJ,yBACDvqH,GAAY,EAAoBoI,WAExCz+B,KAAK+/I,uBAAuB7/F,QACxBlgD,KAAK4gJ,yBACDvqH,GAAY,EAAoBoI,YAExCz+B,KAAKggJ,sBAAsB9/F,QACvBlgD,KAAK4gJ,yBACDvqH,GAAY,EAAkBoI,WAEtCz+B,KAAKigJ,sBAAsB//F,QACvBlgD,KAAK4gJ,yBACDvqH,GAAY,EAAkBoI,aAG1Cz+B,KAAK69I,YAAc,EAEf79I,KAAK69I,YAAc79I,KAAK49I,GAAI,CAE5B,MAAMU,EAAc,CAChBngG,IAAKrY,EACL,gBAAmB46G,GAGnB/mJ,EAAKikB,WAAajkB,EAAKikB,UAAUrnB,QACjCvJ,OAAOgoG,OAAOspD,EAAa,CACvB,qBACI3kJ,EAAKikB,UAAU,GAAG2jG,mBACtB,sBACI5nH,EAAKikB,UAAU,GAAG6jG,oBACtB,eAAkB9nH,EAAKikB,UAAU,GAAG3W,OAI5CjH,KAAKi/I,mBAAmBvB,aAAaY,GACrCt+I,KAAKk/I,qBAAqBxB,aAAaY,GAEvCt+I,KAAKm/I,mBAAmBzB,aAAaY,GACrCt+I,KAAKo/I,qBAAqB1B,aAAaY,GAEnCx+H,IAAQozF,gCACRlzG,KAAKq/I,gBAAgB3B,aAAaY,GAClCt+I,KAAKs/I,kBAAkB5B,aAAaY,IAExCt+I,KAAKw/I,iBAAiB9B,aAAaY,GACnCt+I,KAAKy/I,mBAAmB/B,aAAaY,GACrCt+I,KAAKu/I,oBAAoB7B,aAAaY,GAEtCt+I,KAAK0/I,cAAchC,aAAaY,GAC3B5kI,MAAM1Z,KAAK2/I,oBAAoBlC,cAChCz9I,KAAK2/I,oBAAoBjC,aAAaY,GAE1Ct+I,KAAK4/I,aAAalC,aAAaY,GAC1B5kI,MAAM1Z,KAAK6/I,mBAAmBpC,cAC/Bz9I,KAAK6/I,mBAAmBnC,aAAaY,GAGzCt+I,KAAK8/I,uBAAuBpC,aAAaY,GACpC5kI,MAAM1Z,KAAK+/I,uBAAuBtC,cACnCz9I,KAAK+/I,uBAAuBrC,aAAaY,GAE7Ct+I,KAAKggJ,sBAAsBtC,aAAaY,GACnC5kI,MAAM1Z,KAAKigJ,sBAAsBxC,cAClCz9I,KAAKigJ,sBAAsBvC,aAAaY,GAG5Ct+I,KAAKkgJ,OAAOxC,aAAaY,GAEzBl/H,IAAWoI,cAAcsE,YAAoBwyH,IAE7Ct+I,KAAK4+I,uBA/GL3gI,EAAOjZ,MAAM,sBAJbiZ,EAAOjZ,MAAM,uBAJbiZ,EAAOjZ,MAAM,wBAJbiZ,EAAOjZ,MAAM,uBAJbiZ,EAAOjZ,MAAM,gBA8IrB47I,yBAAyBC,EAAiBt7H,EAASiZ,GAC/C,IAAIsiH,EAAgB,EAChBlxC,EAAY,EAChB,MAAMmxC,EAAO/gJ,KAAKumH,YAAYrqD,WAE9B,IAAK,MAAM8kF,KAAUh0J,OAAOuY,KAAKs7I,GAC7B,GAAIt7H,EAAUy7H,IAAWD,EAAOC,IAAWD,EAAM,CAC7C,MAAM1kF,EACA92C,EACI,KACAvlB,KAAKumH,YAAYtpD,mBAAmB+jF,GACxCC,EAAmBJ,EAAgBG,GAGzC,IAAKz7H,GAAW82C,IAAgB4kF,EAAkB,CAC9C,MAAMC,EAAgBlhJ,KAAKmhJ,6BACvBF,EAAkB5kF,EAAa79B,GAE9B9kB,MAAMwnI,KACPJ,GAAiBI,EACjBtxC,GAAa,IAM7B,OAAOkxC,EAAgBlxC,EAc3BuxC,6BAA6BC,EAAQ/kF,EAAa79B,GAC9C,IAAIhO,EAAQxjC,OAAOuY,KAAK67I,GAAQ57I,IAAIwf,GAAQzL,OAAOyL,IAC/C6W,EAAc,KAIlB,MAAMvY,EAAMtjB,KAAKumH,YAAYjgB,0BAEzBjqC,GACAxgC,EAAcwgC,EAAY4B,qBAAqB1/B,KAC3C1C,IACArL,EACMA,EAAME,OACJ1L,GAAQ6W,EAAY5K,KAChBqN,IACKA,EAAMggC,WACAhgC,EAAMivE,YAAcvoF,GACpBsZ,EAAME,YAAcA,OAG/C3C,EAAc77B,KAAKumH,YAAYr/E,eAAe3I,KAC9C/N,EACMA,EAAME,OACJ1L,GAAQ6W,EAAY5K,KAChBqN,IACKA,EAAMggC,WACAh7C,EAAIgqF,aAAahvE,KAAWtZ,GAC5BsZ,EAAME,YAAcA,KAG/C,IAAIsiH,EAAgB,EAChBO,EAAgB,EAEpB,IAAK,MAAMr8H,KAAQwL,EAAO,CACtB,MAAM8wH,EACA/nI,OAAO6nI,EAAOp8H,GAAMkP,QAAU3a,OAAO6nI,EAAOp8H,GAAMuR,QAGnD7c,MAAM4nI,IAAmBA,EAAiB,IAC3CR,GAAiBQ,EACjBD,GAAiB,GAIzB,OAAOP,EAAgBO,EAa3BV,sBAAsBjpH,EAAWnS,EAASiZ,GACtC,IAAI+iH,EAAa,EACb3xC,EAAY,EAChB,MAAMmxC,EAAO/gJ,KAAKumH,YAAYrqD,WAE9B,IAAK,MAAM8kF,KAAUh0J,OAAOuY,KAAKmyB,GAC7B,GAAInS,EAAUy7H,IAAWD,EAAOC,IAAWD,EAAM,CAC7C,MAAM1kF,EACA92C,EACI,KAAOvlB,KAAKumH,YAAYtpD,mBAAmB+jF,GAC/CQ,EAAY9pH,EAAUspH,GAG5B,IAAKz7H,GAAW82C,IAAgBmlF,EAAW,CACvC,MAAMC,EACAzhJ,KAAK0hJ,0BACHF,EAAWnlF,EAAa79B,GAE3B9kB,MAAM+nI,KACPF,GAAcE,EACd7xC,GAAa,IAM7B,OAAO2xC,EAAa3xC,EAcxB8xC,0BAA0BN,EAAQ/kF,EAAa79B,GAC3C,IAAIhO,EAAQxjC,OAAOuY,KAAK67I,GAAQ57I,IAAIwf,GAAQzL,OAAOyL,IAC/C6W,EAAc,KAIlB,MAAMvY,EAAMtjB,KAAKumH,YAAYjgB,0BAEzBjqC,GACAxgC,EAAcwgC,EAAY4B,qBAAqB1/B,KAC3C1C,IACArL,EACMA,EAAME,OACJ1L,GAAQ6W,EAAY5K,KAChBqN,IAAUA,EAAMggC,WACThgC,EAAMivE,YAAcvoF,GACpBsZ,EAAME,YAAcA,OAG3C3C,EAAc77B,KAAKumH,YAAYr/E,eAAe3I,KAC9C/N,EACMA,EAAME,OACJ1L,GAAQ6W,EAAY5K,KAChBqN,IAAUA,EAAMggC,WACTh7C,EAAIgqF,aAAahvE,KAAWtZ,GAC5BsZ,EAAME,YAAcA,KAG3C,IAAI+iH,EAAa,EACbF,EAAgB,EAEpB,IAAK,MAAMr8H,KAAQwL,EAAO,CACtB,MAAMmxH,EAAcpoI,OAAO6nI,EAAOp8H,KAG7BtL,MAAMioI,IAAgBA,EAAc,IACrCJ,GAAcI,EACdN,GAAiB,GAIzB,OAAOE,EAAaF,EAUxBhB,kCAAkC1mJ,GAC9B,IAAKA,IAASA,EAAKikB,YAAcjkB,EAAKikB,UAAUrnB,OAC5C,OAEJ,MAAMqrJ,EAAiB,CACnBzjG,IAAKxkD,EAAKikB,UAAU,GAAGugC,IACvB,qBAAwBxkD,EAAKikB,UAAU,GAAG2jG,mBAC1C,sBAAyB5nH,EAAKikB,UAAU,GAAG6jG,oBAC3C,eAAkB9nH,EAAKikB,UAAU,GAAG3W,MAGnCjH,KAAKmgJ,uBAA0BpjE,IAAQ6kE,EAAgB5hJ,KAAKmgJ,yBAC7DngJ,KAAKmgJ,sBAAwByB,EAC7BxiI,IAAWoI,cAAcuE,YAA0B61H,KAW3DnB,oBACIzgJ,KAAK4+I,iBACL5+I,KAAKw+I,gBAAgBI,iBAOzBA,iBACI5+I,KAAKi/I,mBAAmB52I,QACxBrI,KAAKk/I,qBAAqB72I,QAE1BrI,KAAKm/I,mBAAmB92I,QACxBrI,KAAKo/I,qBAAqB/2I,QAE1BrI,KAAKq/I,gBAAgBh3I,QACrBrI,KAAKs/I,kBAAkBj3I,QAEvBrI,KAAKw/I,iBAAiBn3I,QACtBrI,KAAKy/I,mBAAmBp3I,QACxBrI,KAAKu/I,oBAAoBl3I,QAEzBrI,KAAK0/I,cAAcr3I,QACnBrI,KAAK2/I,oBAAoBt3I,QACzBrI,KAAK4/I,aAAav3I,QAClBrI,KAAK6/I,mBAAmBx3I,QAExBrI,KAAK8/I,uBAAuBz3I,QAC5BrI,KAAK+/I,uBAAuB13I,QAC5BrI,KAAKggJ,sBAAsB33I,QAC3BrI,KAAKigJ,sBAAsB53I,QAE3BrI,KAAKkgJ,OAAO73I,QAEZrI,KAAK69I,WAAa,EAMtB76H,UACIhjB,KAAKumH,YAAY93E,IACb4oG,aACAr3I,KAAKsgJ,qBACTtgJ,KAAKumH,YAAY93E,IACb8oG,sBACAv3I,KAAKogJ,sBACTpgJ,KAAKumH,YAAY93E,IACb4oG,gBACAr3I,KAAKwgJ,wBACTxgJ,KAAKw+I,gBAAgBx7H,UACrBhjB,KAAKugJ,gBAAgBv9H,c,qFCx/B7B,8EASe,MAAMw8E,EAQjBvpG,YAAYssB,GACRviB,KAAKwmB,MAAQ,CACTi5B,MAAO,GAIPoiG,kBAAmB,MAGvB,MAAMr8H,EAASjD,EAAW25C,WAE1Bl8D,KAAKwmB,MAAMi5B,MAAMj6B,GAAU,IAAIs8H,IAAat8H,EAAQ,MAAM,GAC1DxlB,KAAKuiB,WAAaA,EAElBA,EAAW8Z,iBACP1Z,2BACA3iB,KAAK+hJ,mBAAmBj0J,KAAKkS,OACjCuiB,EAAW8Z,iBACP1Z,cACA3iB,KAAKgiJ,YAAYl0J,KAAKkS,OAC1BuiB,EAAW8Z,iBACP1Z,YACA3iB,KAAKiiJ,aAAan0J,KAAKkS,OAC3BuiB,EAAW8Z,iBACP1Z,uBACA3iB,KAAKkiJ,qBAAqBp0J,KAAKkS,OAC/BuiB,EAAWlD,MACXkD,EAAWlD,KAAKukB,YACZ+W,IAAWpoD,uBACXyN,KAAKmiJ,aAAar0J,KAAKkS,OAanC+hJ,mBAAmBF,GACf,MAAMO,EACApiJ,KAAKwmB,MAAMi5B,MAAMz/C,KAAKwmB,MAAMq7H,mBAC5BQ,EAAqBriJ,KAAKwmB,MAAMi5B,MAAMoiG,GAE5CO,GAAsBA,EAAmBl2D,oBAAmB,GAC5Dm2D,GAAsBA,EAAmBn2D,oBAAmB,GAC5DlsF,KAAKwmB,MAAMq7H,kBAAoBA,EAWnCG,YAAYx8H,EAAQ62C,GACZA,EAAYgrC,YAIXrnG,KAAKwmB,MAAMi5B,MAAMj6B,KAClBxlB,KAAKwmB,MAAMi5B,MAAMj6B,GAAU,IAAIs8H,IAAat8H,EAAQ62C,EAAYkwB,mBAYxE01D,aAAaz8H,GACT,MAAM88H,EAAYtiJ,KAAKwmB,MAAMi5B,MAAMj6B,GAE/B88H,GACAA,EAAUv1D,gBAYlBm1D,qBAAqB18H,EAAQgnE,GACzB,MAAM81D,EAAYtiJ,KAAKwmB,MAAMi5B,MAAMj6B,GAE/B88H,GACAA,EAAUt2D,eAAeQ,GAWjC+jB,WACI,OAAOvwG,KAAKwmB,MAAMi5B,MAStB0iG,aAAaI,GACT,IAAK,MAAM/8H,KAAU+8H,EAAU,CAC3B,IAAIC,EACJ,MAAMC,EAAiBziJ,KAAKuiB,WAAW06C,mBAAmBz3C,GAGrDi9H,GAAmBA,EAAep7C,aAC/BrnG,KAAKwmB,MAAMi5B,MAAMj6B,IACjBg9H,EAAuBxiJ,KAAKwmB,MAAMi5B,MAAMj6B,GAEnCg9H,EAAqBj2D,kBACtBi2D,EACKx2D,eAAeu2D,EAAS/8H,GAAQqmE,eAGzC22D,EAAuB,IAAIV,IACvBt8H,EAAQ+8H,EAAS/8H,GAAQqmE,aAC7B7rF,KAAKwmB,MAAMi5B,MAAMj6B,GAAUg9H,EAC3BA,EAAqBz1D,kBAI7By1D,EAAqBr2D,yBACfo2D,EAAS/8H,GAAQ2mE,6B,gBC/JnC,MAAM+P,EAAgB9+E,EAAQ,IACxBslI,EAAgBtlI,EAClB,KAiBJ,SAAS2oF,IAEL/lG,KAAK2iJ,cAAgB,IAAIzmD,EAGzBl8F,KAAK4iJ,qBAAuB,IAAIF,EAGhC1iJ,KAAKw8H,QAAU,KAIfx8H,KAAK27F,UAAY,KAGjB37F,KAAK6iJ,cAAgB,KAIrB7iJ,KAAKwL,SAAW,KAIhBxL,KAAKw5F,QAAU,GAGfx5F,KAAKitC,MAzCY,SA6CjBjtC,KAAK8iJ,WAAa,EA+DtB,SAASC,EAAaj9C,EAAa9vB,GAO/B,GANAjxE,QAAQH,IACJ,0FAC2BoxE,EAAOgtE,UAAUzsJ,QAI5Cy/E,EAAOgtE,UAAUzsJ,OAAS,EAAG,CAC7B,IAAIy5D,EAASgmB,EAAO2lB,UAAUsnD,qBACxBn9C,EAAYnK,UAAUsnD,qBAIxBjzF,EAAS,IACTA,EAAS,GAGb,IAAIp/B,EAAQ,IAEZolD,EAAOgtE,UAAU5zI,QAAQ8zI,IACrBA,EAAWC,OAASnzF,EACpBkzF,EAAWhlE,KAAOluB,EAClBp/B,GAAYsyH,EAAWE,KAAb,MAEdxyH,GAAS,IACT7rB,QAAQH,IAAIgsB,GAKZolD,EAAOgtE,UAAUn2J,KAAOmpF,EAAOnpF,KAInCi5G,EAAYtM,QAAQt2F,KAAK8yE,EAAOgtE,WAChCl9C,EAAY02B,UACZz3H,QAAQH,IAAK,oBAAmBkhG,EAAY02B,SAG5C12B,EAAYu9C,aAkHhB,SAASC,EAAmBC,GACxB,IAAK,IAAIj3J,EAAI,EAAGA,EAAIi3J,EAAoBhtJ,OAAQjK,IACN,IAAlCi3J,EAAoBj3J,GAAGiK,QACvBgtJ,EAAoBt/I,OAAO3X,EAAG,GAItC,OAAOi3J,EAAoBhtJ,OAAS,EAvNxCwvG,EAAY73G,UAAU6uB,MAAQ,WAC1B,GAtDiB,WAsDb/c,KAAKitC,MACL,MAAM,IAAI51C,MACL,4FAEG2I,KAAKitC,gBAEjBjtC,KAAKitC,MA3De,YA4DpBjtC,KAAK2iJ,cAAc5lI,QACnB/c,KAAK27F,UAAY,IAAIxzF,MASzB49F,EAAY73G,UAAU01B,KAAO,SAAcpY,GACvC,GAvEoB,cAuEhBxL,KAAKitC,MACL,MAAM,IAAI51C,MACL,8FAEG2I,KAAKitC,gBAIjBloC,QAAQH,IAAI,8CACZ5E,KAAK2iJ,cAAc/+H,OAGnB,MAAM4/H,EAAWT,EAAaj1J,KAAK,KAAMkS,MAEzCA,KAAK2iJ,cAAcxlD,sBAAsB/tF,QAAQq0I,IAC7CzjJ,KAAK4iJ,qBAAqBv1I,KAAKo2I,EAAiBD,GAChDxjJ,KAAKw8H,YAITx8H,KAAKitC,MA1FkB,eA6FvBjtC,KAAKwL,SAAWA,GA2DpBu6F,EAAY73G,UAAUm1J,WAAa,WAxJR,iBAyJnBrjJ,KAAKitC,OAAiD,IAAjBjtC,KAAKw8H,SAG1Cx8H,KAAK0jJ,SAQb39C,EAAY73G,UAAUw1J,MAAQ,WAC1B3+I,QAAQH,IACH,sDACG5E,KAAKw5F,QAAQjjG,QACrByJ,KAAK6iJ,cAAgB,GAOrB,MAAMc,EAAS3jJ,KAAKw5F,QAGdoqD,EAAiB,GASvB,IANAN,EAAmBK,GAGnBA,EAAOv0I,QAAQwhB,GA6FnB,SAA+BA,EAAOwyH,GAClC,GAAqB,IAAjBxyH,EAAMr6B,OACNq6B,EAAM1tB,KAAKkgJ,OACR,CACH,GAAIxyH,EAAMA,EAAMr6B,OAAS,GAAG4sJ,OAASC,EAAKD,MAGtC,YAFAvyH,EAAM1tB,KAAKkgJ,GAKf,IAAK,IAAI92J,EAAI,EAAGA,EAAIskC,EAAMr6B,OAAQjK,IAC9B,GAAI82J,EAAKD,MAAQvyH,EAAMtkC,GAAG62J,MAGtB,YAFAvyH,EAAM3sB,OAAO3X,EAAG,EAAG82J,GAK3BxyH,EAAM1tB,KAAKkgJ,IA9GSS,CAAsBD,EAAgBhzH,IAGvD0yH,EAAmBK,IAAS,CAE/B,IAAIG,EAAkBH,EAAO,GAE7BA,EAAOv0I,QAAQ4zI,IACPA,EAAU,GAAGG,MAAQW,EAAgB,GAAGX,QACxCW,EAAkBd,KAK1B,IAAIe,EAAYD,EAAgBp2H,QAMhC,IAJA1tB,KAAKgkJ,oBAAoBD,EAAWD,EAAgBj3J,MAI7Ci3J,EAAgBvtJ,OAAS,GAAG,CAC/B,IAAI0tJ,GAAe,EACnB,MAAMC,EAAgBJ,EAAgB,GAAGX,MASzC,GAPAQ,EAAOv0I,QAAQ4zI,IACPA,EAAU,GAAGG,MAAQe,IACrBD,GAAe,KAKnBA,EACA,MAGJF,EAAYD,EAAgBp2H,QAC5B1tB,KAAKgkJ,oBAAoBD,EAAW,OAM5C/jJ,KAAKitC,MAjOc,WAkOfjtC,KAAKwL,UACLxL,KAAKwL,SAASxL,KAAK6iJ,gBAU3B98C,EAAY73G,UAAU81J,oBAAsB,SAASZ,EAAMv2J,GACnDA,UACAmT,KAAK6iJ,eAAkB,KAAIh2J,KAC3BmT,KAAK8iJ,WAAaj2J,EAAK0J,OAAS,GAEhCyJ,KAAK8iJ,WAAaM,EAAKA,KAAK7sJ,OA/OJ,KAgPxByJ,KAAK6iJ,eAAiB,SACtB7iJ,KAAK8iJ,WAAa,GAEtB9iJ,KAAK6iJ,eAAkB,IAAGO,EAAKA,KAC/BpjJ,KAAK8iJ,YAAcM,EAAKA,KAAK7sJ,OAAS,GAwD1CwvG,EAAY73G,UAAUiwC,SAAW,SAASG,GACtCt+B,KAAK2iJ,cAAcxkH,SAASG,IAOhCynE,EAAY73G,UAAUsmG,YAAc,SAASl2D,GACzCt+B,KAAK2iJ,cAAcnuD,YAAYl2D,IAQnCynE,EAAY73G,UAAUi2J,iBAAmB,WACrC,GAjUmB,aAiUfnkJ,KAAKitC,MACL,MAAM,IAAI51C,MACL,qGAEG2I,KAAKitC,gBAGjB,OAAOjtC,KAAK6iJ,eAMhB98C,EAAY73G,UAAUmvI,SAAW,WAC7B,OAAOr9H,KAAKitC,OAOhB84D,EAAY73G,UAAUma,MAAQ,WAC1BrI,KAAKitC,MA1VY,SA2VjBjtC,KAAKw8H,QAAU,KACfx8H,KAAK6iJ,cAAgB,KACrB7iJ,KAAK27F,UAAY,KACjB37F,KAAKwL,SAAW,KAChBxL,KAAKw5F,QAAU,GACfx5F,KAAK8iJ,WAAa,GAGtB/2J,EAAOD,QAAUi6G,G,8BCvWjB,yDAEA,MAAM9nF,EAASb,EAAQ,GAAqBW,UAAUM,GAUvC,SAAS8/E,EAAmB57E,GAEvCviB,KAAK0gF,SAAW,GAEhB1gF,KAAKuiB,WAAaA,EAClBviB,KAAKuiB,WAAWqiF,mBACZ,WAAY5kG,KAAKokJ,gBAAgBt2J,KAAKkS,OAG9Cm+F,EAAmBjwG,UAAUk2J,gBACvB,SAAS1jE,EAAU2jE,EAAa32C,GAC9B,IAAK1tG,KAAKuiB,WAAWkrF,SAASC,GAK1B,YAJAzvF,EAAO9Y,KACF,8CAA6Cu7E,EAC9CgtB,GAKR,MAAM9oG,EAAM,GAEZ87E,EAASkY,SAASxpF,QAAQwf,IAEtB,MAAM/hC,EAAO+hC,EAAU3xB,WAAWpQ,KAC5BknD,EAAUnlB,EAAUrhC,MAEtByS,KAAK0gF,SAAS7zF,KAAUknD,IACxB/zC,KAAK0gF,SAAS7zF,GAAQknD,EACtB91B,EAAO/Y,KAAM,OAAMrY,cAAiBknD,KAEpCnvC,EAAI1B,KAAK,CACLgE,GAAI,oBACJ0nB,UAAW/hC,EACXknD,eAMRnvC,EAAIrO,OAAS,GACb6oB,IAAWkH,QAAQ1Z,KAAKwL,UAAUxT,KAW9Cu5F,EAAmBjwG,UAAUo2J,oBAAsB,SAASC,GACxD,OAAOvkJ,KAAK0gF,SAAS6jE,M,kFChEzB,0FACA,MAAMtmI,EAASF,oBAAUM,GAUV,MAAM0hF,EAOjB9pG,YAAY67G,GACR9xG,KAAK8xG,SAAWA,EAChB9xG,KAAKwf,aAAesyF,EAAStyF,aAC7BvB,EAAOhZ,MAAM,uBACbjF,KAAK29C,SAAW,GAEhB39C,KAAKwkJ,2BAA6BxkJ,KAAKykJ,oBAAoB32J,KAAKkS,MAKhE8xG,EAAShN,oBAAoB,uBACzB9kG,KAAK0kJ,oBAAoB52J,KAAKkS,OAStC0kJ,oBAAoBxjJ,GAChB,MAAMjE,EAAaiE,EAAKjE,WAExB,IAAKA,EACD,OAGJghB,EAAOhZ,MAAM,+BAAgChI,GAE7C,MAAMugE,EAAWvgE,EAAWgwC,MAE5B,GAAIuwB,IAAax9D,KAAKitC,MAItB,OAAQuwB,GACR,KAAKmnF,WACL,KAAKA,YACL,KAAKA,gBACL,KAAKA,iBACL,KAAKA,eAAwB,CACzB,MAAMjlF,EAAUziE,EAAW2nJ,WAE3B,IAAKllF,EACD,OAIJ,MAAMznD,EAAUjY,KAAK29C,SAAS+hB,GAE1BznD,EACAA,EAAQ4sI,SAASrnF,EAAUvgE,EAAW6nJ,gBAEtC7mI,EAAO9Y,KAAK,kCAAmCu6D,KAc3DgxC,wBAAwBC,EAAY9kB,GAChC,GAAI7rF,KAAK29C,SAASgzD,GAId,OAHA1yF,EAAO9Y,KAAK,uDACRwrG,GAEG,IAAIt5G,MAAMstJ,wBAGrB,MAAM1sI,EAAU,IAAI8sI,IAChBp0C,EAAY9kB,EAAa7rF,KAAK8xG,UAMlC,OAJA75F,EAAQ+sI,iBAAiBhlJ,KAAKwkJ,4BAE9BxkJ,KAAK29C,SAASgzD,GAAc14F,EAErBA,EASXwsI,oBAAoBp9H,GAChB,MAAMq4C,EAAUr4C,EAAMq4C,QAEtB,GAAIr4C,EAAMm2C,WAAamnF,aAChBt9H,EAAMm2C,WAAamnF,eAAwB,CAC9C,MAAM1sI,EAAUjY,KAAK29C,SAAS+hB,GAE9B,IAAKznD,EAID,YAHAgG,EAAOjZ,MAAM,6CACT06D,GAKRznD,EAAQgtI,oBAAoBjlJ,KAAKwkJ,mCAC1BxkJ,KAAK29C,SAAS+hB,GAGzB1/D,KAAKwf,aAAa0D,KACdy3B,IAAW5nD,mCACXs0B,O,6EClIZ,gFAOA,MAAMpJ,EAASF,oBAAUM,GAYV,MAAM0mI,UAA+B3qH,IAWhDnkC,YAAY06G,EAAY9kB,EAAaimB,GACjCz3E,QAEAr6B,KAAK2wG,WAAaA,EAClB3wG,KAAK6rF,YAAcA,EACnB7rF,KAAK8xG,SAAWA,EAShB9xG,KAAKitC,WAAQ1sC,EAMjBqjB,OACQ5jB,KAAKitC,QAAU2jE,aACZ5wG,KAAKitC,QAAU2jE,eAMtB5wG,KAAKklJ,aAAa,QALdjnI,EAAO9Y,KAAK,mDAWpB4X,QAGQ/c,KAAKitC,QAAU2jE,YACZ5wG,KAAKitC,QAAU2jE,aACf5wG,KAAKitC,QAAU2jE,iBACf5wG,KAAKitC,QAAU2jE,iBAMtB5wG,KAAKklJ,aAAa,SALdjnI,EAAO9Y,KAAK,yCAgBpB0/I,SAASrnF,EAAU2nF,GACf,GAAI3nF,IAAax9D,KAAKitC,MAClB,OAGJ,MAAMm4G,EAAWplJ,KAAKitC,MAEtBjtC,KAAKitC,MAAQuwB,EACbx9D,KAAKwf,aAAa0D,KAlFJ,gBAmFV,CACIw8C,QAAS1/D,KAAK2wG,WACdw0C,gBACAC,WACA5nF,SAAUx9D,KAAKitC,MACf4+C,YAAa7rF,KAAK6rF,cAW9Bm5D,iBAAiBtjI,GACb1hB,KAAK4jC,YApGS,gBAoGkBliB,GAQpCujI,oBAAoBvjI,GAChB1hB,KAAK8hB,eA7GS,gBA6GqBJ,GASvCwjI,aAAa16H,GACT,MAAMvtB,EAAa,CACf,MAAS,kCACT,OAAUutB,EACVo6H,WAAY5kJ,KAAK2wG,YAGrB1zG,EAAWooJ,YAAcrlJ,KAAK6rF,YAE9B,MAAM54E,EAAKtX,cAAI,CACXotC,GAAI/oC,KAAK8xG,SAASvZ,YAClBtxF,KAAM,QACLta,EAAE,QAASsQ,GACXkJ,KAEL8X,EAAOhZ,MAASulB,EAAF,wBAAiCvX,EAAG/M,UAClDlG,KAAK8xG,SAASv9F,WAAWnG,OACrB6E,EACA,OACAjO,IACIiZ,EAAOjZ,MACF,aAAYwlB,kCAAwCxlB,GACzDhF,KAAK6kJ,SAASj0C,sB,yFCzJ9B,gFAOA,MAAM3yF,EAASF,oBAAUM,GAYV,SAASinI,IAEpB,OAAO,IAAItoH,QAAQC,IACf6F,IAAIvI,iBAAiBI,IACjB,MAAM4qH,EAAe5qH,EAAQjK,OAAO8I,GAA0B,eAAhBA,EAAOJ,MAC/CosH,EAAqB,GAG3B,IAAK,MAAMC,KAAaF,EAAc,CAClC,MAAMG,EAAgB5iH,IAAIlF,+BAA+B,CAAEjD,QAAS,CAAE,SAClE9D,YAAa4uH,EAAU9vH,WAAY+E,KAAK2M,IAIxC,MAAM/I,EAAQ+I,EAAO,GACfo1D,EAAiBn+D,EAAMmnD,oBAO7B,OALArmE,IAAWkC,gBAAgBm7E,EAAgBn+D,EAAMoK,cAAc56C,KAAKwwC,IACpEA,EAAMjC,iBAAiBugC,sBAAsC,KACzDx9C,IAAWuE,eAAe84E,KAGvBn+D,IAGXknH,EAAmBtiJ,KAAKwiJ,GAG5B1oH,QAAQo+C,WAAWoqE,GAAoB9qH,KAAKirH,IACxC,MAAMC,EAAqBD,EAAaj1H,OAAOtiC,GAAkB,cAAbA,EAAE2hB,QAChD81I,EAAmBF,EAAaj1H,OAAOtiC,GAAkB,aAAbA,EAAE2hB,QAG9CilB,EAAmB4wH,EAAmBpgJ,IAAIpX,GAAKA,EAAEb,OACjDu4J,EAAgBD,EAAiBrgJ,IAAIpX,GAAKA,EAAEb,OAElD,IAAK,MAAMiiB,KAAUs2I,EACjB7nI,EAAOjZ,MAAM,8CAA+CwK,GAIhE,IAAK,MAAMgqB,KAAUxE,EACjBwE,EAAO7X,GAAGi7C,4BAA4Cj0B,IAK9CA,EAAa,OACbo9G,EAAkB/wH,GAClBiI,EAAQ,CAAEtH,SAAU6D,EAAO7D,SACvBqwH,YAAaxsH,EAAO8E,MAAM5E,WAM1CnvB,WAAW,KACPw7I,EAAkB/wH,GAClBiI,EAAQ,CACJtH,SAAU,GACVqwH,YAAa,MApEX,WAoF1B,SAASD,EAAkB7sH,GACvB,IAAK,MAAMM,KAAUN,EACjBM,EAAOyvD,gB,kvCCrFf,MAAMhrE,EAASF,oBAAUM,GAOV,MAAM4nI,EAmBjBhwJ,YAAYkR,EAAU,IAClB,MAAM,gBACF++I,GAEA/+I,EADGg/I,EAFP,EAGIh/I,EAHJ,qBAUAnH,KAAKqxD,SAAL,GACIxrB,UAAWqgH,GACJA,EAAgB7mI,KAAK9K,WAAW+9B,OAAOqiF,cAC3CwxB,GAQPnmJ,KAAKomJ,gBAAkB,KAGvBpmJ,KAAKqmJ,cAAgBrmJ,KAAKqmJ,cAAcv4J,KAAKkS,MAC7CA,KAAKsmJ,eAAiBtmJ,KAAKsmJ,eAAex4J,KAAKkS,MAC/CA,KAAKumJ,gBAAkBvmJ,KAAKumJ,gBAAgBz4J,KAAKkS,MAiBrDwmJ,eAAe/hJ,GACX,MAAM2rG,EAAU3rG,EAAQ9N,KAExB,IAAKy5G,EACD,OAMJ,GAAIpwG,KAAKomJ,iBACFpmJ,KAAKomJ,gBAAgBK,eAAiBr2C,EAOzC,YANApwG,KAAKqmJ,cACDj2C,EACAvsD,IAAQvZ,iBACR,YAMR,MAAMr3B,EAAKjT,KAAK0mJ,oBAAoBjiJ,EAAQ9K,KAAKsZ,IAC3C0zI,EAAU1zI,GAAMA,EAAGge,KAAK,UACxBzG,EAASm8H,GAAWA,EAAQplJ,KAAK,UAEnCipB,IAAWq5B,IAAQE,WACnB/jD,KAAKomJ,gBAAkBpmJ,KAAK4mJ,sBAAsBx2C,EAAS,CACvDhlC,aAAa,EACby7E,cAAc,KAMlB7mJ,KAAKomJ,iBACLpmJ,KAAKomJ,gBAAgBI,eAAeG,GAKpCn8H,IAAWq5B,IAAQvZ,kBAChB9f,IAAWq5B,IAAQI,aACnBz5B,IAAWq5B,IAAQG,WACtBhkD,KAAK8mJ,uBAeb/pI,MAAMqzF,EAASptE,EAAc,IACzBhjC,KAAKomJ,gBAAkBpmJ,KAAK4mJ,sBAAsBx2C,EAAS,CACvDhlC,aAAa,EACby7E,cAAc,IAGlB7mJ,KAAKomJ,gBAAgBrpI,MAAMimB,GAQ/Bpf,OACQ5jB,KAAKomJ,iBACLpmJ,KAAKomJ,gBAAgBxiI,OAGzB5jB,KAAKomJ,gBAAkB,KAW3BM,oBAAoBK,GAChB,IACI,MAAMC,GAAS,IAAI/kJ,WAAYC,gBAAgB6kJ,EAAK,YAEpD,OAAOt0G,EAAEu0G,GACX,MAAO3tJ,GAGL,OAFA4kB,EAAOjZ,MAAM,kDAEN,MAef4hJ,sBAAsBx2C,EAASjpG,EAAU,IACrC,IAAKipG,EACD,MAAM,IAAI/4G,MAAM,mDAGpB,MAAM41E,EAAY,EAAH,CACXpnC,UAAW7lC,KAAKqxD,SAASxrB,UACzB+pF,QAAS5vH,KAAKqmJ,cACdY,eAAgBjnJ,KAAKumJ,gBACrBW,cAAelnJ,KAAKsmJ,eACpBl2C,WACGjpG,GAGP,OAAO,IAAIggJ,IAAkBl6E,GAiBjCo5E,cAAcj2C,EAASzlF,EAAWE,EAAU,IACxC5M,EAAOjZ,MACH,oCAAqCorG,EAASzlF,EAAWE,GAE7D,MAAM5X,EAAKtX,cAAI,CACXotC,GAAIqnE,EACJnpG,KAAM,QAELta,EAAE,SAAU,CACTsZ,MAAO,oBACPukB,OAAQG,IAEXh+B,EAAE,WACFa,EAAEq9B,GACF1kB,KAELnG,KAAKsmJ,eAAel2C,EAASn9F,GAEzBjT,KAAKomJ,iBACFpmJ,KAAKomJ,gBAAgBK,eAAiBr2C,GACzCpwG,KAAK8mJ,uBAebP,gBAAgBa,GACZ,IAAKpnJ,KAAKqxD,SAAS41F,eAIf,OAHAhpI,EAAOjZ,MAAM,gDACboiJ,EAAiBpkI,UAKrB,MAAMqkI,EAAUD,EAAiBz8E,eACjC,IAAInsC,EAEA6oH,IACA7oH,EAAYx+B,KAAKqxD,SAASi2F,sBACpB7oH,IAAUC,QAAUD,IAAUE,QAKxC,MAAMkC,EAAcumH,EAAiB3hE,oBAC/B8hE,EAAmBzkH,IAAI0kH,qBACzB,CACI,CACI7xH,SACK,SAAQ31B,KAAKomJ,gBAAgBK,aAClC96H,UAAW07H,EAAU9oH,IAAkBA,IACvCF,WAAY,QACZ9c,OAAQsf,EACRvC,MAAOuC,EAAY/H,iBAAiB,GACpC0F,eAIZx+B,KAAKqxD,SAAS41F,eAAeM,EAAiB,IAWlDjB,eAAel2C,EAASn9F,GACpB,GAAKjT,KAAKqxD,SAAS61F,cAInB,IACI,MAAMO,GACA,IAAIC,eAAgBC,kBAAkB10I,EAAG/M,UAAY+M,GAE3DjT,KAAKqxD,SAAS61F,cAAc92C,EAAS,CAAEn9F,GAAIw0I,IAC7C,MAAOpuJ,GACL4kB,EAAOjZ,MAAM,mDAUrB8hJ,uBACI9mJ,KAAK4jB,OAEL5jB,KAAKqxD,SAASu2F,oBACP5nJ,KAAKqxD,SAASu2F,yB,84BC7T7B,MAAM3pI,EAASF,oBAAUM,GASV,MAAM8oI,EAoBjBlxJ,YAAYkR,EAAU,IAClBnH,KAAKqxD,SAAL,GACIxrB,UAAW,GACXulC,aAAa,EACby8E,cAAc,EACdhB,cAAc,GACX1/I,GASPnH,KAAK+pG,QAAU,GAOf/pG,KAAKomJ,gBAAkB,KAGvBpmJ,KAAK2b,SAAW3b,KAAK2b,SAAS7tB,KAAKkS,MACnCA,KAAKumJ,gBAAkBvmJ,KAAKumJ,gBAAgBz4J,KAAKkS,MACjDA,KAAKsmJ,eAAiBtmJ,KAAKsmJ,eAAex4J,KAAKkS,MASnDymJ,aACI,OAAOzmJ,KAAKqxD,SAAS++C,QAUzBo2C,eAAeG,GACX,OAAQA,EAAQplJ,KAAK,WACrB,KAAKsiD,IAAQC,OACT9jD,KAAK8nJ,iBAAiBnB,GACtB,MAEJ,KAAK9iG,IAAQE,SACT/jD,KAAK+nJ,mBAAmBpB,GACxB,MAEJ,KAAK9iG,IAAQG,UACThkD,KAAKgoJ,oBAAoBrB,GACzB,MAEJ,KAAK9iG,IAAQhxD,eACTmN,KAAKioJ,iBAAiBtB,IAa9B5pI,MAAMimB,EAAc,IACZhjC,KAAKomJ,kBAITpmJ,KAAK+pG,QAAU/pG,KAAK+pG,QAAQ/vG,OAAOgpC,GAEnChjC,KAAKomJ,gBAAkBpmJ,KAAK4mJ,wBAE5B5mJ,KAAKomJ,gBAAgBz0E,OAAO3uC,IAShCpf,OACQ5jB,KAAKomJ,iBACLpmJ,KAAKomJ,gBAAgBvyE,YAGzB7zE,KAAKgoJ,sBAUTpB,wBAOI,MAWMsB,EAAiB,CAGnBt+I,WAAW,EACX0oC,OAAQ,CACJuhC,UAAW,QAEfzlE,OAAQpO,KAAKsmJ,eAIbjqH,iBAAkB,IAAM,QAWtB8rH,EAAgB,EAAH,CACfjqG,WAAY1F,KACTx4C,KAAKqxD,SAASxrB,WAgCfuiH,EAAW,CACbtjD,oBAAqB,OACrBhsD,gBAAiB,GACjBt5B,aAAc,CAAE0D,KApBJmE,IACZ,OAAQA,GACR,KAAKszB,IAAWxrD,sBAChB,KAAKwrD,IAAW3rD,kBACZgR,KAAK2b,SAASkoC,IAAQvZ,iBAAkBjjB,MAiB5CipG,qBAAsB,KAGX,IAEXtrB,uBAAwB,QAe5BhlG,KAAKqoJ,KAAO,IAAIvlH,IARO,GAQa,IAOpC9iC,KAAKqoJ,KAAKzkH,YACN5K,IAAU3Q,mBACVroB,KAAKumJ,iBAGT,MAAMz6C,EAAiB,IAAIlhC,SACvBrqE,OACAA,EACAP,KAAKqxD,SAAS++C,QACd83C,EACA,CACItzB,oBAAqB50H,KAAKqxD,SAASw2F,aACnChzB,oBAAqB70H,KAAKqxD,SAASw1F,cAEvCsB,GACA,EACAnoJ,KAAKqxD,SAAS+Z,aASlB,OAFA0gC,EAAeplD,WAAW0hG,EAAUpoJ,KAAKqoJ,KAzHtB,IA2HZv8C,EAYXnwF,SAASgP,EAAWE,EAAU,IAC1B7qB,KAAKqxD,SAASu+D,QAAQ5vH,KAAKqxD,SAAS++C,QAASzlF,EAAWE,GAY5D07H,gBAAgBa,GACZpnJ,KAAK+pG,QAAQ7mG,KAAKkkJ,GAElBpnJ,KAAKqxD,SAAS41F,eAAeG,GAWjCd,eAAerzI,GACXjT,KAAKqxD,SAAS61F,cAAclnJ,KAAKqxD,SAAS++C,QAASn9F,GAYvD60I,iBAAiBnB,GACR3mJ,KAAKomJ,gBAMVpmJ,KAAKomJ,gBAAgBn0E,UAAU00E,GAL3B1oI,EAAOjZ,MAAM,sDAgBrB+iJ,mBAAmBpB,GACX3mJ,KAAKomJ,gBACLnoI,EAAOjZ,MAAM,sDAKjBhF,KAAKomJ,gBAAkBpmJ,KAAK4mJ,wBAE5B5mJ,KAAKomJ,gBAAgB90E,YACjBq1E,EACA,OACA,IAAM3mJ,KAAK2b,SACP3b,KAAKqxD,SAAS++C,QACdvsD,IAAQvZ,iBACR,4BAYZ09G,sBACIhoJ,KAAK+pG,QAAQ36F,QAAQkvB,GAASA,EAAMtb,WACpChjB,KAAK+pG,QAAU,GAEX/pG,KAAKomJ,iBACLpmJ,KAAKomJ,gBAAgBjyE,eAGrBn0E,KAAKqoJ,OACLroJ,KAAKqoJ,KAAKvmI,eACNkX,IAAU3Q,mBACVroB,KAAKumJ,iBAGTvmJ,KAAKqoJ,KAAKxkH,WAYlBokH,iBAAiBtB,GACb3mJ,KAAKomJ,gBAAgB31E,iBAAiBk2E,O,wFCjZ/B,KACX3hJ,MAAO,CACHsjJ,KAAM,OACN5qJ,MAAO,QACP6qJ,oBAAqB,sBACrBC,oBAAqB,uBAEzB/6J,KAAM,CACFg7J,KAAM,OACNhsJ,OAAQ,UAEZsT,OAAQ,CACJ00E,IAAK,MACLD,GAAI,KACJ9gC,QAAS,a,6BCdjB,6DAQA,MACM/X,EAAU,IAAIlsB,IACpB,IAAImoD,GAAe,EACf8gF,EAAM,KAkHK,KACX99I,KAhDGzI,eAAoBgF,GACvB,GAAIygE,EACA,MAAM,IAAIvwE,MAAM,oCAOpB,aArEJ,WACI,IAAIyoB,IAAQC,gBAIZ,OAAO,IAAIid,QAAQC,IACfjd,IAAWC,WACPE,KACY,GACE,OACI5f,EACC08B,KAsDrB0rH,GAEND,EAAM,IAAIx8J,OAAOo6D,UAtCrB,SAAqBn/C,GACjB,OAAO,IAAI61B,QAAQ,CAACC,EAASC,KACzB,IAAK/1B,EAAQyY,0BAA2B,CACpC,MAAMgpI,EAAQzhJ,EAAQyX,YAChBiqI,EAAY1hJ,EAAQ0X,gBACpB2G,EAASre,EAAQs5F,cAAgBt5F,EAAQu6F,uBAAyBhB,IAASC,kBAEjF+nD,EAAIhiG,WAAWkiG,EAAOC,EAAWrjI,EAAQ,CAACzV,EAAQtL,KAC/B,YAAXsL,GACA24I,EAAI/mI,GAhDK,qBAgDoB,IAAIwgB,KAC7BwJ,EAAQzoB,KAjDH,wBAiDiCif,KAE1CylC,GAAe,EACf3qC,KAEAC,EAAO,CACHntB,SACAtL,aAGT,KAAM,CAAEqkJ,oBAAoB,OAoBhCC,CAAY5hJ,IAwCnB6hJ,QAxBG,WACH,OAAKphF,EAIE,IAAI5qC,QAAQ,CAACC,EAASC,KACzByO,EAAQhqB,GArGa,qBAqGY,CAAC5R,EAAQ0iB,KACvB,YAAX1iB,EACAktB,EAAQxK,GAERyK,EAAO,CACHntB,SACA0iB,cAMZi2H,EAAIO,oBAhBGjsH,QAAQE,OAAO,oB,cCzG9B,MAAMgsH,EAAW,CAsBbC,gBAAe,CAACC,EAAYvsG,EAAUwsG,IAGf,iBAFPD,EAGD,KAHCA,EAMDxnJ,QAAQ,SAAUi7C,GACxBj7C,QAAQ,iBAAiC,IAAhBynJ,IAItCt9J,EAAOD,QAAUo9J,G,8BClCjB,iEAQA,MAAMjrI,EAASF,oBAAUM,GAMV,MAAMirI,EAIjBrzJ,cACI+J,KAAKupJ,UAAW,EAChBvpJ,KAAKwpJ,cAAgB,GACrBxpJ,KAAKypJ,gBAAkB,GAQ3BC,eAAenoI,GACNA,EAAOqX,kBACR3a,EAAO9Y,KAAK,mDAGhBnF,KAAKwpJ,cAActmJ,KAAKqe,GAU5BxE,QAEI,GAAI/c,KAAKupJ,SACL,OAAOvpJ,KAAK2pJ,UAAUpoI,OAK1B,GAFAvhB,KAAKonF,cAAgBC,eAEhBrnF,KAAKwpJ,cAAcjzJ,OAGpB,OAFA0nB,EAAO9Y,KAAK,8DAEL,KAGXnF,KAAKupJ,UAAW,EAEhBvpJ,KAAK2pJ,UAAY3pJ,KAAKonF,cAAcwiE,+BAEpC,IAAK,MAAMroI,KAAUvhB,KAAKwpJ,cAAe,CACrC,MAAMK,EAAY7pJ,KAAKonF,cAAczF,wBAAwBpgE,GAE7DsoI,EAAUp+I,QAAQzL,KAAK2pJ,WAGvB3pJ,KAAKypJ,gBAAgBvmJ,KAAK2mJ,GAG9B,OAAO7pJ,KAAK2pJ,UAAUpoI,OAQ1BlZ,QACIrI,KAAKupJ,UAAW,EAChBvpJ,KAAKwpJ,cAAgB,GAGrB,IAAK,MAAMK,KAAa7pJ,KAAKypJ,gBACzBI,EAAUt6I,aAGdvP,KAAKypJ,gBAAkB,GAEnBzpJ,KAAKonF,gBACLpnF,KAAKonF,mBAAgB7mF,O,kJCYlB,MA/ED,SAAS0X,EAAS23H,GAgC5B,YA/BuB,IAAZ33H,QAAoD,IAAlBA,EAAQwV,OAAyBr3B,MAAMC,QAAQ4hB,EAAQwV,QAChGxV,EAAQwV,MAAMre,QAAQqiB,IACdA,EAAM24D,SAAW34D,EAAM24D,QAAQ7zF,SAC/Bk7B,EAAMjB,MAAQ,GACdiB,EAAM24D,QAAQh7E,QAAQ2b,IAClB/9B,OAAOuY,KAAKwlB,GAAQ3b,QAAQ9R,IACN,OAAdA,GAGJm0B,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI6jB,EAAO7jB,GACX5J,YACA/P,MAAOw9B,EAAOztB,gBAInBm0B,EAAM24D,SAIb34D,EAAMX,YAAcW,EAAMX,WAAWv6B,QACrCk7B,EAAMX,WAAW1hB,QAAQyiB,SACU,IAApBA,EAAUrB,OAClBp6B,MAAMC,QAAQw7B,EAAUrB,SACvBqB,EAAUrB,MAAQqB,EAAUrB,MAAMrtB,KAAK,UAOpDigD,IAAUrL,MAAM9/B,EAAS23H,IA+CrB,EArCD,SAAS59G,GACnB,MAAM/Z,EAAUmrC,IAAUlrC,MAAM8Z,GAiChC,YA/BuB,IAAZ/Z,QAAoD,IAAlBA,EAAQwV,OAAyBr3B,MAAMC,QAAQ4hB,EAAQwV,QAChGxV,EAAQwV,MAAMre,QAAQqiB,SAES,IAAhBA,EAAMjB,OAAyBp6B,MAAMC,QAAQo7B,EAAMjB,SAC1DiB,EAAM24D,QAAU,GAChB34D,EAAMjB,MAAMphB,QAAQ4V,IAChB,MAAM8kI,EAAQr4H,EAAM24D,QAAQ3U,UAAU1qD,GAAUA,EAAO7jB,KAAO8d,EAAK9d,IAEnE,GAAI4iJ,GAAS,EACTr4H,EAAM24D,QAAQ0/D,GAAO9kI,EAAK1nB,WAAa0nB,EAAKz3B,UACzC,CACH,MAAM+tC,EAAM,CAAEp0B,GAAI8d,EAAK9d,IAEvBo0B,EAAItW,EAAK1nB,WAAa0nB,EAAKz3B,MAC3BkkC,EAAM24D,QAAQlnF,KAAKo4B,aAGpB7J,EAAMjB,YAIe,IAArBiB,EAAMX,YAA8B16B,MAAMC,QAAQo7B,EAAMX,aAC/DW,EAAMX,WAAW1hB,QAAQyiB,IACU,iBAApBA,EAAUrB,QACjBqB,EAAUrB,MAAQqB,EAAUrB,MAAMztB,MAAM,UAOrDkV,GClFX,MAAM8xI,EAAc,CAAE,QAAS,QAAS,QAClCC,EAAen4H,GAAaA,EAAUZ,KAAKg5H,GAAyB,QAAlBA,EAAI94H,WACtD+4H,EAAer4H,GAAaA,EAAUZ,KAAKg5H,GAAyB,QAAlBA,EAAI94H,WAW5D,SAASg5H,EAAmB14H,EAAOL,EAAUg5H,EAAcC,GACvD,IAAK54H,IAAUL,EACX,OAEJ,MAAMk5H,EAAiBhvH,GAAO+uH,EAAWp5H,KAAKlG,GAAUA,EAAO7jB,GAAGxQ,aAAe4kC,GAEjFlK,EAASZ,MAAMphB,QAAQksB,IACnB7J,EAAM24D,QAAQlnF,KAAKonJ,EAAehvH,IAGlC,MAAMivH,EAAkBH,EAAa9xI,SAASgjB,EAAK,KAAKrK,KAAKg5H,GAAyB,QAAlBA,EAAI94H,WAExE,GAAIo5H,EAAiB,CACjB,MAAMC,EAAcD,EAAgB/5H,MAAMS,KAAK5iC,GAAKA,IAAMitC,GAE1D7J,EAAM24D,QAAQlnF,KAAKonJ,EAAeE,IAClC/4H,EAAMX,WAAW5tB,KAAKqnJ,MAK9B94H,EAAMX,WAAW5tB,KAAKkuB,GAgB1B,SAASq5H,EAAkBh5H,EAAOzM,EAAMolI,EAAcC,GAClD,IAAK54H,IAAUzM,EACX,OAMJ,GAJAyM,EAAM24D,QAAU,GAChB34D,EAAMX,WAAa,IAGds5H,EAAaplI,EAAK9d,IAInB,OAHAuqB,EAAM24D,QAAQlnF,KAAK8hB,QACnByM,EAAMqwB,KAAO98B,EAAK88B,MAItB,MAGM1wB,EAAW44H,EAAaI,EAAaplI,EAAK9d,KAC1C8pB,EAAWk5H,EAAaE,EAAaplI,EAAK9d,KAGhD,GAAIkqB,EACA+4H,EAAmB14H,EAAOL,EAAUg5H,EAAcC,QAC/C,GAAIr5H,EAAU,CAEjB,MAAM05H,EAAY15H,EAASR,MAAMS,KAAK5iC,GAAKA,IAAM22B,GAC3C2lI,EAAYX,EAAaI,EAAaM,IAExCC,EACAR,EAAmB14H,EAAOk5H,EAAWP,EAAcC,IAGnDr5H,EAASR,MAAMphB,QAAQksB,IACnB7J,EAAM24D,QAAQlnF,KAnBHo4B,IAAO+uH,EAAWp5H,KAAKlG,GAAUA,EAAO7jB,GAAGxQ,aAAe4kC,GAmBlDgvH,CAAehvH,MAEtC7J,EAAMX,WAAW5tB,KAAK8tB,IAK9BS,EAAMqwB,KAAOrwB,EAAM24D,QAAQ,GAAGtoC,KAsBlC,SAAS8oG,EAA0B5lI,EAAMolI,EAAcS,GASnD,IAAKA,EAAO55H,KARcnD,KAClBA,EAAMs8D,SACCt8D,EAAMs8D,QAAQzH,KAAK53D,GAAUA,EAAO7jB,KAAO8d,EAAK9d,KAM1B,CAGjC,IAAKkjJ,EAAaplI,EAAK9d,IACnB,OAAO,EAEX,MAAMkqB,EAAW44H,EAAaI,EAAaplI,EAAK9d,KAC1C8pB,EAAWk5H,EAAaE,EAAaplI,EAAK9d,KAEhD,GAAIkqB,EACA,OAAOy5H,EAAOloE,KAAK70D,GAASA,EAAMs8D,SAC3Bt8D,EAAMs8D,QAAQzH,KAAKrnD,GAAOA,EAAIp0B,GAAGxQ,aAAe06B,EAASZ,MAAM,KACnE,GAAIQ,GAAYhM,EAAK9d,GAAGxQ,aAAes6B,EAASR,MAAM,GAAI,CAG7D,OAAOo6H,EAFW,CAAE1jJ,GAAI8pB,EAASR,MAAM,IAEK45H,EAAcS,GAI9D,OAAO,EAGX,OAAO,EAiCJ,MAAM1gB,EAMTiE,QAAQtrE,GACJ,IAAKA,GAA0C,iBAApBA,EAAY9wC,IAGnC,OAFAjtB,QAAQI,KAAK,mDAEN29D,EAIX,MAAM7qD,EAAUmrC,EAAgB0f,EAAY9wC,KAG5C,IAAK/Z,EAAQwV,QAAUxV,EAAQwV,MAAMl3B,OAGjC,OAFAwO,QAAQI,KAAK,iCAEN29D,EAIX,GAAI7qD,EAAQwV,MAAMgsE,MAAM/sG,IAAqC,IAAhCq9J,EAAYnmJ,QAAQlX,EAAEgjD,MAG/C,OAFA3qC,QAAQI,KAAK,uDAEN29D,EAGX,MAAMr1C,EAAQ,GACRq9H,EAAe7yI,EAAQwV,MAE7BxV,EAAQwV,MAAQ,GAChBq9H,EAAa17I,QAAQqiB,IACjB,MAAMxqB,EAAOwqB,EAAMxqB,KAEnB,GAAa,gBAATA,EAIA,OAHAwqB,EAAMie,IAAM,YACZjiB,EAAMgE,EAAMie,KAAOje,GAIvB,QAA2B,IAAhBhE,EAAMxmB,GAAuB,CACpC,MAAM8jJ,EAAQ7qH,IAAUzO,GAGpBs5H,EAAM3gE,SAAWh0F,MAAMC,QAAQ00J,EAAM3gE,UACrC2gE,EAAM3gE,QAAQh7E,QAAQ2b,IAClB0G,EAAMqwB,KAAO/2B,EAAO+2B,KAAOrwB,EAAMqwB,YAAc/2B,EAAO+2B,OAUzDipG,EAAMj6H,YAAeW,EAAMqwB,OAC5BipG,EAAMj6H,WAAa,WAEhBi6H,EAAMjpG,KACbipG,EAAMr7G,IAAMzoC,EACZwmB,EAAMxmB,GAAQ8jJ,OACPt5H,EAAMqwB,OAETrwB,EAAM24D,SAAWh0F,MAAMC,QAAQo7B,EAAM24D,WACrC38D,EAAMxmB,GAAMmjF,QAAU38D,EAAMxmB,GAAMmjF,QAAQpwF,OAAOy3B,EAAM24D,eAE3B,IAArB34D,EAAMX,YAA8B16B,MAAMC,QAAQo7B,EAAMX,cAC/DrD,EAAMxmB,GAAM6pB,WAAarD,EAAMxmB,GAAM6pB,WAAW92B,OAAOy3B,EAAMX,gBAIzE7Y,EAAQwV,MAAQzgC,OAAOo2B,OAAOqK,GAG9B,MAAMu9H,EAAS,GAEfh+J,OAAOo2B,OAAOqK,GAAOre,QAAQ0e,IACD,aAApBA,EAAM6B,WACNq7H,EAAO9nJ,KAAK4qB,EAAM4hB,OAK1Bz3B,EAAQu6B,OAAOpjC,QAAQ8hB,IACA,WAAfA,EAAMjqB,OACNiqB,EAAM+5H,KAAOD,EAAO7nJ,KAAK,QAKjC8U,EAAQizI,aAAe,CACnBC,SAAU,MACVvyG,MAAO,KAEX,MAAMm1F,EAAS3qF,EAAgBnrC,GAE/B,OAAO,IAAI09D,sBAAsB,CAC7B1uE,KAAM67D,EAAY77D,KAClB+qB,IAAK+7G,IAYb8C,cAAc/tE,EAAasoF,EAAU,MACjC,IAAKtoF,GAA0C,iBAApBA,EAAY9wC,IAGnC,OAFAjtB,QAAQI,KAAK,mDAEN29D,EAIX,MAAM7qD,EAAUmrC,EAAgB0f,EAAY9wC,KAG5C,IAAK/Z,EAAQwV,QAAUxV,EAAQwV,MAAMl3B,OAGjC,OAFAwO,QAAQI,KAAK,iCAEN29D,EAIX,GAAI7qD,EAAQwV,MAAMl3B,OAAS,GAAK0hB,EAAQwV,MAAMgsE,MAAM/sG,IAAqC,IAAhCq9J,EAAYnmJ,QAAQlX,EAAEgjD,MAG3E,OAFA3qC,QAAQI,KAAK,6CAEN29D,EAEX,MAAMuoF,EAAcD,EAAUhoG,EAAgBgoG,EAAQp5H,KAAO,KACvDvE,EAAQ,GAEdxV,EAAQwV,MAAMre,QAAQqiB,IAClB,MAAMxqB,EAAOwqB,EAAMxqB,KAEnB,GAAa,gBAATA,EAAwB,CACxB,IAAKokJ,IAAgBA,EAAY59H,MAAO,CACpC,MAAM69H,EAAWprH,IAAUzO,GAK3B,OAHA65H,EAAS57G,IAAM1iD,OAAOuY,KAAKkoB,GAAOl3B,OAAOG,gBACzC+2B,EAAMgE,EAAMie,KAAO47G,GAIvB,MAAMC,EAAeF,EAAY59H,MAAMgoD,UAAU/oF,GAAKA,EAAEua,OAASA,GAOjE,YALIskJ,IACAF,EAAY59H,MAAM89H,GAAgB95H,EAClC45H,EAAY59H,MAAM89H,GAAc77G,IAAM67G,IAO9C,MAAMC,EA3LlB,SAA8BpB,GAC1B,MAAMoB,EAAa,GAEnB,OAAKpB,GAAiBh0J,MAAMC,QAAQ+zJ,IAGpCA,EAAah7I,QAAQ8hB,IACbA,EAAMV,OAASp6B,MAAMC,QAAQ66B,EAAMV,QACnCU,EAAMV,MAAMphB,QAAQ4V,SACgB,IAArBwmI,EAAWxmI,KAClBwmI,EAAWxmI,GAAQ,IAEvBwmI,EAAWxmI,GAAM9hB,KAAKguB,OAK3Bs6H,GAbIA,EAuLgBC,CAAqBh6H,EAAMX,YAEzCW,EAAM24D,SAGX34D,EAAM24D,QAAQh7E,QAAQ,CAAC4V,EAAM4qB,KAGzB,IAAK5qB,EAAK88B,KACN,OAIJ,IAAKupG,IAAgBA,EAAY59H,MAAO,CACpC,GAAIm9H,EAA0B5lI,EAAMwmI,EAAYx+J,OAAOo2B,OAAOqK,IAC1D,OAEJ,MAAM69H,EAAWprH,IAAUzO,GAU3B,OARA65H,EAAS57G,IAAM1iD,OAAOuY,KAAKkoB,GAAOl3B,OAAOG,WACzC40J,EAAS37H,UAAYigB,GAEK,aAApBne,EAAM9B,UADN,WAC8C,WACpD27H,EAASI,gBAAanrJ,EACtBkqJ,EAAkBa,EAAUtmI,EAAMwmI,EAAY/5H,EAAM24D,cACpD38D,EAAM69H,EAAS57G,KAAO47G,GAM1B,GAAIV,EAA0B5lI,EAAMwmI,EAAYH,EAAY59H,OACxD,OAIJ,MAAMk+H,EAAcN,EAAY59H,MAC3BgoD,UAAUm2E,GAAUA,EAAOj8H,WACA,aAArBi8H,EAAOj8H,WACPi8H,EAAO3kJ,OAASA,GAE3B,GAAI0kJ,GAAe,EACfN,EAAY59H,MAAMk+H,GAAah8H,UAAY,WAC3C86H,EAAkBY,EAAY59H,MAAMk+H,GAAc3mI,EAAMwmI,EAAY/5H,EAAM24D,aACvE,CACH,MAAMkhE,EAAWprH,IAAUzO,GAE3B65H,EAAS57G,IAAM27G,EAAY59H,MAAMl3B,OAAOG,WACxC40J,EAAS37H,UAAY,WACrB86H,EAAkBa,EAAUtmI,EAAMwmI,EAAY/5H,EAAM24D,SACpDihE,EAAY59H,MAAMvqB,KAAKooJ,QAInCrzI,EAAQwV,MAAQ49H,EAAcA,EAAY59H,MAAQzgC,OAAOo2B,OAAOqK,GAChE,MAAMw9H,EAAO,GAEbhzI,EAAQwV,MAAMre,QAAQqiB,IAClBw5H,EAAK/nJ,KAAKuuB,EAAMie,OAIpBz3B,EAAQu6B,OAAOpjC,QAAQ8hB,IACA,WAAfA,EAAMjqB,OACNiqB,EAAM+5H,KAAOA,EAAK9nJ,KAAK,QAK/B8U,EAAQizI,aAAe,CACnBC,SAAU,MACVvyG,MAAO,KAIX3gC,EAAQw5G,OAAOnrD,iBACf,MAAMksE,EAAYpvF,EAAgBnrC,GAElC,OAAO,IAAI09D,sBAAsB,CAC7B1uE,KAAM67D,EAAY77D,KAClB+qB,IAAKwgH,O,6BChbjB,IAAI5oE,EAAoC,oBAAXD,QAA0BA,OAAOC,iBAAmBD,OAAOC,gBAAgB97E,KAAK67E,SAA+B,oBAAbkiF,UAAgE,mBAA7BA,SAASjiF,iBAAkCiiF,SAASjiF,gBAAgB97E,KAAK+9J,UACvOC,EAAQ,IAAI9gG,WAAW,IACZ,SAAS+gG,IACtB,IAAKniF,EACH,MAAM,IAAIvyE,MAAM,4GAGlB,OAAOuyE,EAAgBkiF,GCNzB,IAFA,IAAIE,EAAY,GAEP,EAAI,EAAG,EAAI,MAAO,EACzBA,EAAU9oJ,MAAM,EAAI,KAAOxM,SAAS,IAAI0e,OAAO,IAWlC,MARf,SAAqB62I,EAAKj8F,GACxB,IAAI1jE,EAAI0jE,GAAU,EACdk8F,EAAMF,EAGV,OAAQE,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM4/J,EAAID,EAAI3/J,EAAI,IAAM,IAAM4/J,EAAID,EAAI3/J,EAAI,KAAO4/J,EAAID,EAAI3/J,EAAI,KAAO4/J,EAAID,EAAI3/J,EAAI,KAAO4/J,EAAID,EAAI3/J,EAAI,KAAO4/J,EAAID,EAAI3/J,EAAI,KAAO4/J,EAAID,EAAI3/J,EAAI,MAAMqW,eCaxT,IAzBf,SAAYwE,EAAS8kJ,EAAKj8F,GACD,iBAAZ7oD,IACT8kJ,EAAkB,WAAZ9kJ,EAAuB,IAAI6jD,WAAW,IAAM,KAClD7jD,EAAU,MAIZ,IAAIglJ,GADJhlJ,EAAUA,GAAW,IACFkE,SAAWlE,EAAQ4kJ,KAAOA,KAK7C,GAHAI,EAAK,GAAe,GAAVA,EAAK,GAAY,GAC3BA,EAAK,GAAe,GAAVA,EAAK,GAAY,IAEvBF,EAAK,CAGP,IAFA,IAAIlvI,EAAQizC,GAAU,EAEb1jE,EAAI,EAAGA,EAAI,KAAMA,EACxB2/J,EAAIlvI,EAAQzwB,GAAK6/J,EAAK7/J,GAGxB,OAAO2/J,EAGT,OAAO,EAAYE,K,gBCvBrBpgK,EAAOD,QAAUsxB,EAAQ,KAAiB+2F,S,6BCF1C,4VA0CA,MAAMl2F,EAASd,IAAOY,UAAUM,GA+ChC,SAAS+tI,EAAkCjlJ,GACvC,MAAMlK,EAAa,CACf,gBACIkK,EAAQwzB,QAAQlH,SAAS,SAC7B,gBACItsB,EAAQwzB,QAAQlH,SAAS,SAC7B,yBACItsB,EAAQwzB,QAAQlH,SAAS,YAOjC,OAJIx2B,EAAWovJ,kBACXpvJ,EAAWo5B,WAAalvB,EAAQkvB,YAG7Bp5B,EAiCIqvJ,UAVf,SAAkCvgK,GAC9B,MACkC,iBAAvBG,OAAOqgK,YACRv/J,OAAOgoG,OAAO,GAAI9oG,OAAOqgK,YAAaxgK,GACtCA,EAMCugK,CAAyB,CAEpCv4G,QAAS,aAETspD,oBASA4oD,2BAEAuG,UAAW,CACPtuF,4BAA6B3E,IAC7BkzF,UAAWC,IACXC,WAAY/7C,EACZ1K,oBAAqB2lB,GAEzBt/E,OAAQ,CACJhqB,WAAYI,EACZpO,WAAYknC,EACZmxG,UAAW1qD,EACX5jE,MAAOs+B,EACPniC,aAAcs+D,EACdgG,kBAAmBw4C,EACnBr2C,QAASm6C,GAEb7jI,OAAQ,CACJ+K,WAAYklF,EACZlzF,WAAYwnC,EACZzd,MAAOuQ,GAEXg+G,WAAY,CACRjnI,qBAEJknI,UAAW3vI,IAAOM,OAClBgd,aAAcsyH,IACdlsI,UAAWzB,IAAWyB,UACtBjW,KAAKzD,EAAU,IACXu5F,IAAS91F,KAAKzD,EAAQo5C,iBACtBnhC,IAAWxU,KAAKzD,GAIXjb,OAAO4sD,kBACR5sD,OAAO4sD,gBAAkB,KAGU,IAAnC3xC,EAAQ6lJ,yBACR/uI,EAAO9Y,KAAK,kCACZnF,KAAK6gB,UAAUmC,WAGf7b,EAAQ8lJ,4BACRr1H,IAAqB7pB,WACjB/N,KAAKktJ,wBAAwBp/J,KAAKkS,OAK1C,MAAMmtJ,EAAShmJ,EAAQu3C,eAEvB,GAAIyuG,GAAUngK,OAAOuY,KAAK4nJ,GAAQ52J,OAAS,EAAG,CAC1C,MAAM87G,EAAY,GAElB,IAAK,MAAM9wG,KAAQ4rJ,EACXA,EAAOh/J,eAAeoT,KACtB8wG,EAAU9wG,GAAQ4rJ,EAAO5rJ,IAIjC8wG,EAAUnrG,GAAK,kBACfkY,IAAWkH,QAAQ1Z,KAAKwL,UAAUi6F,IAGtC,GAAIryG,KAAK+zC,QAAS,CACd,MAAMs+D,EAAY,CACdnrG,GAAI,oBACJ0nB,UAAW,iBACXmlB,QAAS/zC,KAAK+zC,SAGlB30B,IAAWkH,QAAQ1Z,KAAKwL,UAAUi6F,IAGtC,OAAOvvE,IAAIl4B,KAAKzD,IAQpB85B,wBAAuB,IACZ6B,IAAI7B,0BAWfmsH,kBAAiB,IACNtqH,IAAIsqH,oBAGfhvI,YAAYvZ,GACRsY,IAAOiB,YAAYvZ,IAWvBqZ,gBAAgBrZ,EAAOqC,GACnBiW,IAAOe,gBAAgBrZ,EAAOqC,IASlCmmJ,sBAAsBC,GAClBnwI,IAAOQ,mBAAmB2vI,IAS9BC,yBAAyBD,GACrBnwI,IAAOU,sBAAsByvI,IAUjCE,oBAAoBrmJ,GAChBgW,IAAOW,iBAAiB3W,IAwC5Bg9B,kBACQh9B,EAAU,GAAIsmJ,EAAkCC,GACpD,IAAIC,GAAmB,EAkBvB,OAhByC,IAArCF,GACAvhK,OAAOqe,WAAW,KACTojJ,GACDZ,IAAkBnzD,UACdb,6BACAj5E,IAAQm1B,YArSa,KA0ShC/oD,OAAO4sD,kBACR5sD,OAAO4sD,gBAAkB,IAE7B5sD,OAAO4sD,gBAAgB,2BACjB5sD,OAAOsuD,YAAYrmC,MAElB2uB,IAAIlF,+BAA+Bz2B,GACrCuzB,KAAK2M,IAWF,GAVAsmH,GAAmB,EAEnBzhK,OAAO4sD,gBAAgB,yBACjB5sD,OAAOsuD,YAAYrmC,MAEzBiL,IAAWoI,cACP+D,YACI,UACA6gI,EAAkCjlJ,MAErC27B,IAAI37B,QAAQsZ,mBACb,IAAK,IAAIn0B,EAAI,EAAGA,EAAI+6C,EAAO9wC,OAAQjK,IAAK,CACpC,MAAMgyC,EAAQ+I,EAAO/6C,GACfshK,EAAUtvH,EAAMmnD,oBAElBnnD,EAAMgJ,YAAc/I,MACpBnf,IAAWkC,gBAAgBssI,EACvBtvH,EAAMoK,cAAc56C,KAAKwwC,IAC7BA,EAAMjC,iBACFugC,sBACA,KACIx9C,IAAWuE,eAAeiqI,MAO9C,MAAMC,EACA/qH,IAAI1B,oCAEV,GAAIysH,EACA,IAAK,IAAIvhK,EAAI,EAAGA,EAAI+6C,EAAO9wC,OAAQjK,IAAK,CACtB+6C,EAAO/6C,GAEfknG,+BACFq6D,GAMZ,IAAK,MAAMvvH,KAAS+I,EACZ/I,EAAMr3B,OAASs3B,KACQ,YAApBD,EAAME,WACTx+B,KAAK8tJ,0BAA0BxvH,EAAMA,MAAO,UAIpD,OAAO+I,IAEVjK,MAAMp4B,IAGH,GAFA2oJ,GAAmB,EAEf3oJ,EAAMnY,OAASgiD,2BACX/uB,IAAQ+a,iBAAkB,CAC9B,MAAMkzH,EAAgB5mJ,EAAQkvB,YAAc,MACtC23H,EAjW1B,SAA4B33H,GACxB,IAAKC,IAAYD,GACb,OAAO,KAEX,MAAM6tB,EAAQ5tB,IAAYD,GAAY6tB,MACtC,IAAIjmB,EAAM,KACNgwH,EAAU,KAed,OAbAjhK,OAAOuY,KAAK+wB,KAAalnB,QAAQhiB,IAC7B,MAAMG,EAAQ+oC,IAAYlpC,KAErB6wC,GAAQA,EAAIimB,MAAQ32D,EAAM22D,OAAS32D,EAAM22D,MAAQA,KAClD+pG,EAAU7gK,EACV6wC,EAAM1wC,KAIV0gK,IAAY53H,IACZ43H,EAAU,MAGPA,EA4U+BC,CAAmBH,GAEzC,GAAsB,OAAlBC,EAeA,OAdA7mJ,EAAQkvB,WAAa23H,EAErB/vI,EAAOhZ,MACH,0CACA+oJ,GAEJ5uI,IAAWoI,cAAc+D,YACrB,UACA,CACI,eAAkBwiI,EAClB,eAAkBC,EAClBx+I,OAAQ,4BAGTxP,KAAKmkC,kBACRh9B,OACA5G,EACAmtJ,GAAmB1gK,OAAOgoG,OAAO,GAAI7tF,IAK7C,GAAIumJ,GACG1oJ,EAAM6gB,IAAIE,aACV/gB,EAAM6gB,IAAIE,YAAYkO,OACtBjvB,EAAM6gB,IAAIE,YAAYkO,MAAMuB,WAC5BxwB,EAAM6gB,IAAIE,YAAYkO,MAAMuB,UAAUI,SAGzC,OAFA83H,EAAgBh4H,oBAAiBn1B,EAE1BP,KAAKmkC,kBAAkBupH,GAItC,GAAI1oJ,EAAMnY,OACEgiD,8BAA8C,CAItD,MAAMwjE,EAAY,CACdnrG,GAAI,8BACJzC,QAASO,EAAMP,SAGnB2a,IAAWkH,QAAQ1Z,KAAKwL,UAAUi6F,IAElCjzF,IAAWoI,cACP+D,YACI,UACA,CACI/b,OAAQ,0CAEjB,GAAIxK,EAAMnY,OAASgiD,YAA4B,CAElD,MAAMwjE,EAAY,CACdnrG,GAAI,2BACJ6I,OAAQ/K,EAAM6gB,IAAI8U,SAGtBvb,IAAWkH,QAAQ1Z,KAAKwL,UAAUi6F,IAElC,MAAMp1G,EACAmvJ,EAAkCjlJ,GAExClK,EAAWuS,OAAS,mBACpBvS,EAAW09B,QAAU31B,EAAM6gB,IAAI8U,QAAQx3B,KAAK,KAC5Cic,IAAWoI,cACP+D,YAAwB,QAAStuB,QAClC,CAEHmiB,IAAWuG,uBAAuB3gB,GAElC,MAAM/H,EACAmvJ,EAAkCjlJ,GAExClK,EAAWuS,OAASxK,EAAMnY,KAC1BuyB,IAAWoI,cACP+D,YAAwB,QAAStuB,IAMzC,OAHA/Q,OAAO4sD,gBAAgB,yBACjB5sD,OAAOsuD,YAAYrmC,MAElB6oB,QAAQE,OAAOl4B,MAoBlCmpJ,sBAAqB,CAACC,EAAoB9mE,EAAYT,IAC3CF,IAAgB/4F,OAAOwgK,EAAoB9mE,EAAYT,GASlEwnE,iBAAgB,IACL,IAAI/E,IAQfhE,qBAAoB,IACTA,cAWXppH,wBAII,OAHAje,EAAO9Y,KAAK,yFAGLnF,KAAKy6B,aAAayB,yBAY7ByE,wBAAwBC,GAIpB,OAHA3iB,EAAO9Y,KAAK,2FAGLnF,KAAKy6B,aAAakG,wBAAwBC,IAUrD+4D,gCACI,OAAO35F,KAAKy6B,aAAak/D,iCAQ7B20D,uBAAsB,IACXlvI,IAAWoB,oBACXygE,IAAoBK,wBAS/B/mD,iBAAiB/uB,GACbyS,EAAO9Y,KAAK,oFAEZnF,KAAKy6B,aAAaF,iBAAiB/uB,IAWvC0hJ,wBAAwBzoJ,EAASsmB,EAAQwjI,EAAQC,EAAOxpJ,GACpDiZ,EAAOjZ,MACF,mBAAkBP,EAClB,WAAUsmB,EACV,SAAQwjI,EACR,WAAUC,EACX,eAAgBxpJ,GACpBoa,IAAW+H,kBAAkBniB,IASjCypJ,gBAAe,SAAE/vF,IACbF,IAAYG,kBAAkB,CAAED,cAUpCovF,0BAA0BxvH,EAAOowH,GACzB,gBAAiBpwH,GACjBA,EAAMqwH,YAAcD,EAChBpwH,EAAMqwH,cAAgBD,GACtBzwI,EAAOhZ,MAAM,oCAGjBgZ,EAAOhZ,MAAM,yDAIrB2pJ,gBAQAC,KAAM,CACF3F,aACAlpI,eACAF,gB,6CCtoBR,IAAI3C,EAASC,EAAQ,IAgDrB,SAASC,EAAayxI,EAAY3nJ,GAC9BnH,KAAK8uJ,WAAaA,EAClB9uJ,KAAK+uJ,oBAAmB5nJ,IAAWA,EAAQ4nJ,mBAAmB5nJ,EAAQ4nJ,iBACtE/uJ,KAAKgvJ,cAAgB7nJ,GAAWA,EAAQ6nJ,cAAgB7nJ,EAAQ6nJ,cAAe,IAC/EhvJ,KAAKivJ,eAAiB9nJ,GAAWA,EAAQ8nJ,eAAiB9nJ,EAAQ8nJ,eAAiB,IAGnFjiK,OAAOuY,KAAK4X,EAAOM,QAAQrO,QAC3B,SAAU8/I,GAENlvJ,KADiBmd,EAAOM,OAAOyxI,IACZ,WACflvJ,KAAKmvJ,KAAKvtH,MAAM5hC,KAAMmB,YACxBrT,KAAKkS,KAAMkvJ,IACfphK,KAAKkS,OAMPA,KAAKovJ,oBAAsB,KAM3BpvJ,KAAK6/E,MAAQ,GAKb7/E,KAAKqvJ,SAAW,EAMhBrvJ,KAAKsvJ,YAAc,GAiBvBjyI,EAAanvB,UAAUkqB,UAAY,SAAUm3I,GACzC,IACI,OAAO3iJ,KAAKwL,UAAUm3I,GACxB,MAAOvqJ,GACL,MAAO,iCAmBfqY,EAAanvB,UAAUshK,iBAAmB,SAC1CN,GAEI,IADA,IAAIpqJ,EAAM,GACDxY,EAAI,EAAG6L,EAAMgJ,UAAU5K,OAAQjK,EAAI6L,EAAK7L,IAAK,CAClD,IAAI8U,EAAMD,UAAU7U,IAEf0T,KAAK+uJ,kBAAoBG,IAAa/xI,EAAOM,OAAO/f,OACtC,iBAAR0D,IACPA,EAAMpB,KAAKoY,UAAUhX,IAEzB0D,GAAO1D,EACH9U,IAAM6L,EAAM,IACZ2M,GAAO,KAGf,OAAOA,EAAIvO,OAASuO,EAAM,MAS9BuY,EAAanvB,UAAUihK,KAAO,WAG1B,IAAI3mE,EAAYrnF,UAAU,GACtB2D,EAAM9E,KAAKwvJ,iBAAiB5tH,MAAM5hC,KAAMmB,WAC5C,GAAI2D,EAAK,CAEL,IAAI2qJ,EAAczvJ,KAAK6/E,MAAM7/E,KAAK6/E,MAAMtpF,OAAS,GAC7Cm5J,EAAkBD,GAAeA,EAAY9tJ,KAC7C+tJ,IAAoB5qJ,EACpB2qJ,EAAY7iH,OAAS,GAErB5sC,KAAK6/E,MAAM38E,KAAK,CACZvB,KAAMmD,EACN0jF,UAAWA,EACX57C,MAAO,IAEX5sC,KAAKqvJ,UAAYvqJ,EAAIvO,QAIzByJ,KAAKqvJ,UAAYrvJ,KAAKivJ,gBACtBjvJ,KAAK2vJ,QAAO,GAAkB,IAQtCtyI,EAAanvB,UAAU6uB,MAAQ,WAC3B/c,KAAK4vJ,8BAQTvyI,EAAanvB,UAAU0hK,2BAA6B,WAC5C5vJ,KAAKovJ,sBACLljK,OAAOuhB,aAAazN,KAAKovJ,qBACzBpvJ,KAAKovJ,oBAAsB,MAG/BpvJ,KAAKovJ,oBAAsBljK,OAAOqe,WAC9BvK,KAAK2vJ,OAAO7hK,KACRkS,MAAM,GAA0B,GACpCA,KAAKgvJ,gBAOb3xI,EAAanvB,UAAUsf,MAAQ,WAC3BxN,KAAK2vJ,QACD,GACA,IAcRtyI,EAAanvB,UAAUyhK,OAAS,SAASE,EAAOC,GAExC9vJ,KAAKqvJ,SAAW,IAAMrvJ,KAAK8uJ,WAAWiB,WAAaF,KAG/C7vJ,KAAK8uJ,WAAWiB,WAEZ/vJ,KAAKsvJ,YAAY/4J,SACjByJ,KAAKsvJ,YAAYlgJ,QACb,SAAU4gJ,GACNhwJ,KAAK8uJ,WAAWmB,UAAUD,IAC5BliK,KAAKkS,OAGXA,KAAKsvJ,YAAc,IAGvBtvJ,KAAK8uJ,WAAWmB,UAAUjwJ,KAAK6/E,QAE/B7/E,KAAKsvJ,YAAYpsJ,KAAKlD,KAAK6/E,OAG/B7/E,KAAK6/E,MAAQ,GACb7/E,KAAKqvJ,SAAW,GAGhBS,GACA9vJ,KAAK4vJ,8BAQbvyI,EAAanvB,UAAU01B,KAAO,WAE1B5jB,KAAK2vJ,QAAO,GAA0B,IAG1C5jK,EAAOD,QAAUuxB,G,eClRjB,YACAtxB,EAAOD,QAAUokK,I,+BCDjB;;;;;;;;GAsBElwJ,MAAQ9T,OAXNF,EAAO,QAAD,4BAcR,WAEF,IAAImkK,EAAmB,kCAGnBC,EAAgBlkK,OAAO6uB,SAAW7uB,OAAO6uB,SAASkiF,KAAO,KACzDozD,EAAUD,GAAcA,EAAYxuJ,QAAQ,OAAQ,IAAIA,QAAQ,QAAS,KAAc,KAGvF0uJ,EAAUj1J,SAASyV,qBAAqB,UAGxCy/I,EAA2B,eAAiBD,EAAQ,IAAMj1J,SAASwF,cAAc,WAGjF2vJ,GAActkK,OAAO6rH,OAAqC,mBAA5B7rH,OAAO6rH,MAAMrhH,WAG3C+5J,EAAiC,kBAAmBp1J,SAIpD,oBAAqBhE,OAASA,MAAMq5J,kBAAoBpV,MAC/BjkJ,MAAMq5J,gBACjCr5J,MAAMq5J,gBAAkBpV,KAO1B,IAAIqV,GAAyB,EACzBC,GAAwB,EA+O5B,SAASC,IAIP,GAAuB,IAAnBP,EAAQ/5J,OACV,OAAO,KAGT,IAAIjK,EAAG+M,EAAG+K,EAAO28B,EAAKg4B,EAClB+3F,EAAkB,GAClBC,EAAiBF,EAAwBE,gBAAkB,EAQ/D,IAAKzkK,EAAI,EAAGA,EAAIgkK,EAAQ/5J,OAAQjK,IAC1BkkK,GAAcD,EACZJ,EAAiBj+I,KAAKo+I,EAAQhkK,GAAG0sB,aACnC83I,EAAgB5tJ,KAAKotJ,EAAQhkK,IAI/BwkK,EAAgB5tJ,KAAKotJ,EAAQhkK,IAQjC,GAJA+M,EAAI,IAAIhC,MACJs5J,IACFvsJ,EAAQ/K,EAAE+K,QAEPA,GAASwsJ,EACZ,IACE,MAAMv3J,EAER,MAAO8W,GAEL/L,EAAQ+L,EAAI/L,MAiEhB,GA7DIA,KAEF20D,EAzQJ,SAA0Bh4B,EAAK+vH,GAC7B,IAAIxkK,EACAysE,EAAS,KAIb,GAFA+3F,EAAkBA,GAAmBR,EAElB,iBAARvvH,GAAoBA,EAC7B,IAAKz0C,EAAIwkK,EAAgBv6J,OAAQjK,KAC/B,GAAIwkK,EAAgBxkK,GAAGgvC,MAAQyF,EAAK,CAGlCg4B,EAAS+3F,EAAgBxkK,GACzB,MAIN,OAAOysE,EAyPIi4F,CADTjwH,EAvMJ,SAASkwH,EAAsB7sJ,EAAO2sJ,GACpC,IAAI19I,EACA0tB,EAAM,KACNmwH,EAA0C,iBAAnBH,EA+B3B,OA9BAA,EAAiBG,EAAgB9lJ,KAAKozG,MAAMuyC,GAAkB,EACzC,iBAAV3sJ,GAAsBA,IAC3B8sJ,EACF79I,EAAUjP,EAAMkP,MAAM,+GAGtBD,EAAUjP,EAAMkP,MAAM,kNAELD,EAAQ,KACvBA,EAAUjP,EAAMkP,MAAM,kHAItBD,GAAWA,EAAQ,KAGnB0tB,EAFEgwH,EAAiB,EAEbE,EADW7sJ,EAAM81B,MAAM91B,EAAMR,QAAQyP,EAAQ,IAAMA,EAAQ,GAAG9c,QACvBw6J,EAAiB,GAGxD19I,EAAQ,KAWb0tB,EAqKCkwH,CAAsB7sJ,EAAO2sJ,GACJD,KAEhBT,GAAWtvH,IAAQsvH,IAiB9Bt3F,EA5OR,SAA6B+3F,GAC3B,IAAIxkK,EAAG6L,EACH4gE,EAAS,KAEb,IAAKzsE,EAAI,EAAG6L,GADZ24J,EAAkBA,GAAmBR,GACH/5J,OAAQjK,EAAI6L,EAAK7L,IACjD,IAAKwkK,EAAgBxkK,GAAG8lD,aAAa,OAAQ,CAC3C,GAAI2mB,EAAQ,CACVA,EAAS,KACT,MAEFA,EAAS+3F,EAAgBxkK,GAG7B,OAAOysE,EA+NQo4F,CAAoBL,IAU9B/3F,GAK4B,IAA3B+3F,EAAgBv6J,SAClBwiE,EAAS+3F,EAAgB,IAIxB/3F,GAeC03F,IACF13F,EAAS19D,SAAS+1J,gBAIjBr4F,GAeCy3F,GAAcD,EAChB,IAAKjkK,EAAIwkK,EAAgBv6J,OAAQjK,KAC/B,GAAsC,gBAAlCwkK,EAAgBxkK,GAAG0sB,WAA8B,CACnD+/C,EAAS+3F,EAAgBxkK,GACzB,MAkCR,OA5BKysE,IAyBHA,EAAS+3F,EAAgBA,EAAgBv6J,OAAS,IAAM,MAGnDwiE,GA3YT,WACE,IACE,IAAI5oD,EAAM,IAAI9Y,MAEd,MADAs5J,EAA8C,iBAAdxgJ,EAAI/L,SAAwB+L,EAAI/L,MAC1D+L,EAER,MAAOkhJ,GACLT,EAAmD,iBAApBS,EAAUjtJ,SAAwBitJ,EAAUjtJ,QAP/E,GA+YAysJ,EAAwBE,eAAiB,EAOrC,IAAIn4F,EAA4Bi4F,EAShC,OARAj4F,EAAuB04F,KAAST,EAChCj4F,EAAuB24F,IA1R3B,WAME,OAAO,MAqRL34F,EAAuB64D,OArL3B,WAIE,OAAO,MAuLE74D,IA9cD,gC,cCPV7sE,EAAOD,QAJW,CACdo7B,UAAW,U,0CCCfzrB,UAAQsK,oBAAoB,QAC5B,CACIyO,YAAa,KACb6vE,YAAc,GACdC,UAAY,GACZktE,OAAS,GAOT5mJ,KAAM,SAAS6mJ,GAEfzxJ,KAAKwU,YAAci9I,EACfzxJ,KAAKqkF,YAAc,GACnBrkF,KAAKskF,UAAc,GACnBtkF,KAAKwxJ,OAAc,GAEnBC,EAAK1jJ,WAAW/N,KAAK0xJ,aAAa5jK,KAAKkS,MAAOvE,UAAQK,GAAGO,WAAY,KAAM,MAAO,KAAM,MAExFo1J,EAAK1jJ,WAAW/N,KAAK2xJ,cAAc7jK,KAAKkS,MAAOvE,UAAQK,GAAGQ,YAAa,KAAM,MAAO,KAAM,OAa9Fs1J,YAAa,SAAS3tE,EAAUh9E,EAAMpa,EAAMglK,GAExC,IAAK,IAAIvlK,EAAE,EAAGA,EAAE0T,KAAKqkF,YAAY9tF,OAAQjK,IAErC,GAAI0T,KAAKqkF,YAAY/3F,GAAG23F,UAAYA,GAChCjkF,KAAKqkF,YAAY/3F,GAAG2a,MAAQA,GAC5BjH,KAAKqkF,YAAY/3F,GAAGO,MAAQA,GAC5BmT,KAAKqkF,YAAY/3F,GAAGulK,MAAQA,EAE5B,OAAO,EAIf,OADA7xJ,KAAKqkF,YAAYnhF,KAAK,CAAC+gF,SAAUA,EAAUh9E,KAAMA,EAAMpa,KAAMA,EAAMglK,KAAMA,KAClE,GAUX/3G,WAAY,SAASg4G,GAEjB,IAAK,IAAIxlK,EAAE,EAAGA,EAAE0T,KAAKskF,UAAU/tF,OAAQjK,IAElC,GAAI0T,KAAKskF,UAAUh4F,IAAMwlK,EACrB,OAAO,EAGhB,OADA9xJ,KAAKskF,UAAUphF,KAAK4uJ,IACb,GAUXluE,cAAe,SAASkuE,GAEpB,IAAK,IAAIxlK,EAAE,EAAGA,EAAE0T,KAAKskF,UAAU/tF,OAAQjK,IAElC,GAAI0T,KAAKskF,UAAUh4F,KAAOwlK,EAEtB,OADA9xJ,KAAKskF,UAAUrgF,OAAO3X,EAAE,IACjB,EAGhB,OAAO,GAaXylK,QAAS,SAASpuJ,EAAK9W,EAAMqU,EAAM8wJ,GAE/B,QAAI9wJ,IAAS8wJ,IAEbhyJ,KAAKwxJ,OAAOtuJ,KAAK,CAACS,IAAKA,EAAK9W,KAAMA,EAAMqU,KAAMA,EAAM8wJ,UAAWA,IACxD,KAUX9sJ,KAAM,SAASvB,EAAKzC,EAAMwmD,EAAS1iD,EAAO4I,GAEtC,IAAIpS,EAAQ,CAACyK,MAAOxK,UAAQK,GAAGO,YAC3B6E,IACA1F,EAAM0F,KAAOA,GAEjB,IAAIgE,EAAOvJ,MAAI,CAAChF,KAAKqJ,KAAKwU,YAAY7Q,IACrBolC,GAAGplC,EAAKsD,KAAK,QAAQta,EAAE,QAAS6O,GACjDwE,KAAKwU,YAAYpG,OAAOlJ,EAAMwiD,EAAS1iD,EAAO4I,IAUlDqkJ,MAAO,SAAStuJ,EAAKzC,EAAMwmD,EAAS1iD,EAAO4I,GAEvC,IAAIpS,EAAQ,CAACyK,MAAOxK,UAAQK,GAAGQ,aAC3B4E,IACA1F,EAAM0F,KAAOA,GAEjB,IAAI+wJ,EAAQt2J,MAAI,CAAChF,KAAKqJ,KAAKwU,YAAY7Q,IACtBolC,GAAGplC,EAAKsD,KAAK,QAAQta,EAAE,QAAS6O,GACjDwE,KAAKwU,YAAYpG,OAAO6jJ,EAAOvqG,EAAS1iD,EAAO4I,IAKnDskJ,eAAgB,SAASlkJ,EAAQmkJ,GAE7B,IAAIjrJ,EAAQ8G,EAAOpL,aAAa,MAC5BjM,EAAOqX,EAAOpL,aAAa,QAC3BwvJ,EAAWz2J,MAAI,CAACsL,KAAM,SAAUC,GAAIA,IAMxC,OAJa,OAATvQ,GACAy7J,EAAS52J,MAAM,CAACutC,GAAIpyC,IAGjBy7J,EAASzlK,EAAE,QAASwlK,IAM/BT,aAAc,SAAS1jJ,GAEnB,IAEI1hB,EAFA4U,EAAO8M,EAAO8C,qBAAqB,SAAS,GAAGlO,aAAa,QAC5DpH,EAAQ,CAACyK,MAAOxK,UAAQK,GAAGO,YAE3B6E,IAEA1F,EAAM0F,KAAOA,GAEjB,IAAIkxJ,EAAWpyJ,KAAKkyJ,eAAelkJ,EAAQxS,GAC3C,IAAKlP,EAAE,EAAGA,EAAE0T,KAAKqkF,YAAY9tF,OAAQjK,IAEjCkP,EAAQ,CAACyoF,SAAUjkF,KAAKqkF,YAAY/3F,GAAG23F,SAC1Bh9E,KAAUjH,KAAKqkF,YAAY/3F,GAAG2a,MACvCjH,KAAKqkF,YAAY/3F,GAAGO,OACpB2O,EAAM3O,KAAOmT,KAAKqkF,YAAY/3F,GAAGO,MACjCmT,KAAKqkF,YAAY/3F,GAAGulK,OACpBr2J,EAAM,YAAcwE,KAAKqkF,YAAY/3F,GAAGulK,MAC5CO,EAASzlK,EAAE,WAAY6O,GAAO2K,KAElC,IAAK7Z,EAAE,EAAGA,EAAE0T,KAAKskF,UAAU/tF,OAAQjK,IAE/B8lK,EAASzlK,EAAE,UAAW,CAAC,IAAMqT,KAAKskF,UAAUh4F,KAAK6Z,KAGrD,OADAnG,KAAKwU,YAAYnH,KAAK+kJ,EAAS/sJ,SACxB,GAKXssJ,cAAe,SAAS3jJ,GAEpB,IAEIikJ,EAAO3lK,EAFP6lK,EAAc,CAAClsJ,MAAOxK,UAAQK,GAAGQ,aACjC4E,EAAO8M,EAAO8C,qBAAqB,SAAS,GAAGlO,aAAa,QAEhE,GAAI1B,GAIA,IAFAixJ,EAAYjxJ,KAAOA,EACnB+wJ,EAAQ,GACH3lK,EAAI,EAAGA,EAAI0T,KAAKwxJ,OAAOj7J,OAAQjK,IAEhC,GAAI0T,KAAKwxJ,OAAOllK,GAAG4U,MAAQA,EAC3B,CACI+wJ,EAAQjyJ,KAAKwxJ,OAAOllK,GAAG0lK,UAAUhkJ,GACjC,YAMRikJ,EAAQjyJ,KAAKwxJ,OAEjB,IAAIY,EAAWpyJ,KAAKkyJ,eAAelkJ,EAAQmkJ,GAC3C,IAAK7lK,EAAI,EAAGA,EAAI2lK,EAAM17J,OAAQjK,IAC9B,CACI,IAAIkP,EAAQ,CAACmI,IAAMsuJ,EAAM3lK,GAAGqX,KACxBsuJ,EAAM3lK,GAAGO,OACT2O,EAAM3O,KAAOolK,EAAM3lK,GAAGO,MACtBolK,EAAM3lK,GAAG4U,OACT1F,EAAM0F,KAAO+wJ,EAAM3lK,GAAG4U,MAC1BkxJ,EAASzlK,EAAE,OAAQ6O,GAAO2K,KAG9B,OADAnG,KAAKwU,YAAYnH,KAAK+kJ,EAAS/sJ,SACxB,K,kDCpNf5J,UAAQsK,oBAAoB,mBAAoB,CAK/CssJ,SAAS,EAOTC,oCAAoC,EAQpCC,wBAAyB,EAMzBC,GAAI,KAMJC,IAAK,gBAMLC,4BAA4B,EAQ5BC,gCAAiC,KAQjCC,+BAAgC,KAMhCC,0BAA2B,KAQ3BC,mBAAoB,KAMpBC,gBAAiB,KAMjBC,iBAAkB,KAKlBC,8BAA+B,EAM/BC,cAAc,EAKdC,uBAAwB,GAOxBC,6BAA8B,GAE9BC,8BAA+B,SAAS3xI,GACvC1hB,KAAKozJ,6BAA6BlwJ,KAAKwe,IAGxC8f,OAAQ,SAASx2B,GAChB,IAAKhL,KAAKkzJ,aACT,MAAM,IAAI77J,MAAM,sDACV,GAAI2I,KAAK0mH,oBAAsBjrH,UAAQgC,OAAOM,UACpD,MAAM,IAAI1G,MAAM,sDAEjB2I,KAAKwyJ,GAAGnlJ,KAAK9R,SAAO,SAAU,CAAE0K,MAAOjG,KAAKyyJ,IAAKznJ,YACjDhL,KAAKwyJ,GAAGhlJ,QACRxN,KAAKwyJ,GAAGznJ,SAGTqoD,eAAgB,WACf,OAAOpzD,KAAKszJ,cAGb,cACC,OAAOtzJ,KAAKkzJ,cAGbloJ,OAAQ,WACP,IAAKhL,KAAKozD,iBACT,MAAM,IAAI/7D,MAAM,mBAEjB,GAAI2I,KAAK0mH,oBAAsBjrH,UAAQgC,OAAOO,aAC7C,MAAM,IAAI3G,MAAM,yDAGjB2I,KAAKwyJ,GAAGrrJ,QAAQwM,yBAA0B,EAC1C3T,KAAKuzJ,WAAY,EAEjBvzJ,KAAKwzJ,iBAAiB5xH,MAAM5hC,KAAKwyJ,GAAIxyJ,KAAKyzJ,eAG3CC,uBAAwB,WACvB,GAAI1zJ,KAAK0mH,oBAAsBjrH,UAAQgC,OAAOM,UAC7C,MAAM,IAAI1G,MAAM,sEAEjB2I,KAAKizJ,8BAAgC,EACrCjzJ,KAAKwyJ,GAAGnlJ,KAAK9R,SAAO,IAAK,CAAE0K,MAAOjG,KAAKyyJ,QAGxCkB,mBAAoB,WACnB,OAAO3zJ,KAAK6yJ,2BAGbe,mBAAoB,WACnB,OAAO5zJ,KAAK4yJ,gCAGbhoJ,KAAM,SAAS6mJ,GACdzxJ,KAAKwyJ,GAAKf,EACVh2J,UAAQ+D,aAAa,KAAMQ,KAAKyyJ,KAGhCzyJ,KAAK8yJ,mBAAqB9yJ,KAAKwyJ,GAAGvlJ,UAClCjN,KAAKwyJ,GAAGvlJ,UAAYjN,KAAKiN,UAAUnf,KAAKkS,MAExCA,KAAKwzJ,iBAAmBxzJ,KAAKwyJ,GAAG/mJ,QAChCzL,KAAKwyJ,GAAG/mJ,QAAUzL,KAAK6zJ,sBAAsB/lK,KAAKkS,MAElDA,KAAK8zJ,mCAAqC9zJ,KAAKwyJ,GAAG9+I,2BAClD1T,KAAKwyJ,GAAG9+I,2BAA6B1T,KAAK0T,2BAA2B5lB,KAAKkS,MAE1EA,KAAK+zJ,sBAAwB/zJ,KAAKwyJ,GAAG1iJ,cACrC9P,KAAKwyJ,GAAG1iJ,cAAgB9P,KAAKg0J,uBAAuBlmK,KAAKkS,MAEzDA,KAAKi0J,oBAAsBj0J,KAAKwyJ,GAAGjjJ,WACnCvP,KAAKwyJ,GAAGjjJ,WAAavP,KAAKk0J,qBAAqBpmK,KAAKkS,OAGrDk0J,qBAAsB,WACrBl0J,KAAKszJ,kBAAe/yJ,EACpBP,KAAKi0J,oBAAoBryH,MAAM5hC,KAAKwyJ,GAAIrxJ,YAGzC6yJ,uBAAwB,WACnBh0J,KAAKozD,mBACHpzD,KAAKuzJ,WACNvzJ,KAAKwyJ,GAAG5oJ,YAAc5J,KAAKwyJ,GAAG3oJ,gBAClC7J,KAAKm0J,aAAe,CACnBhrJ,SAAUnJ,KAAKwyJ,GAAGrpJ,SAClBD,cAAelJ,KAAKwyJ,GAAGtpJ,cACvBE,aAAcpJ,KAAKwyJ,GAAGppJ,aACtBC,eAAgBrJ,KAAKwyJ,GAAGnpJ,eACxBC,UAAWtJ,KAAKwyJ,GAAGlpJ,UACnBC,YAAavJ,KAAKwyJ,GAAGjpJ,aAEtBvJ,KAAKo0J,WAAap0J,KAAKwyJ,GAAG7uJ,IAE1B3D,KAAKqyJ,SAAW52J,UAAQwJ,MAAM,0CAA4CjF,KAAKm0J,aAAahrJ,SAAS5S,SAKtGyJ,KAAKwyJ,GAAGvoJ,MAAQ,GAEhBjK,KAAK+zJ,sBAAsBnyH,MAAM5hC,KAAKwyJ,GAAIrxJ,YAG3C0yJ,sBAAuB,WACtB7zJ,KAAKyzJ,aAAetyJ,UAEpBnB,KAAKwzJ,iBAAiB5xH,MAAM5hC,KAAKwyJ,GAAIrxJ,YAGtCuS,2BAA4B,SAAShU,GAGpC,OAFAM,KAAKkzJ,aAAexzJ,EAAK4R,uBAAuBtR,KAAKyyJ,IAAK,MAAMl8J,OAAS,EAElEyJ,KAAK8zJ,mCAAmClyH,MAAM5hC,KAAKwyJ,GAAIrxJ,YAG/D+O,cAAe,SAAUH,GAExB,GADA/P,KAAK0mH,kBAAoB32G,EACpB/P,KAAKozD,kBACLrjD,IAAWtU,UAAQgC,OAAOM,WAAagS,IAAWtU,UAAQgC,OAAOO,aAqC/D,GAAI+R,IAAWtU,UAAQgC,OAAOY,aAAe,CACnD2B,KAAKwyJ,GAAG7uJ,IAAM3D,KAAKo0J,WAGnB,IAAK,MAAMnmK,KAAY+R,KAAKm0J,aAC3Bn0J,KAAKwyJ,GAAGvkK,GAAY+R,KAAKm0J,aAAalmK,GAIvC+R,KAAKwyJ,GAAGnlJ,KAAK9R,SAAO,SAAU,CAC7B0K,MAAOjG,KAAKyyJ,IACZ7rJ,EAAG5G,KAAK4yJ,+BACRyB,OAAQr0J,KAAKszJ,gBAEdtzJ,KAAKwyJ,GAAGhlJ,aACEuC,IAAWtU,UAAQgC,OAAOC,QACpCsC,KAAKqyJ,SAAW52J,UAAQwJ,MAAM,oCAC9BjF,KAAKszJ,kBAAe/yJ,QArDpBP,KAAKqyJ,SAAW52J,UAAQwJ,MAAM,kBAE9BjF,KAAK2yJ,gCAAkC,EACvC3yJ,KAAK4yJ,+BAAiC,EAEtC5yJ,KAAK6yJ,0BAA4B,EAEjC7yJ,KAAK0yJ,4BAA6B,EAClC1yJ,KAAKizJ,8BAAgC,EAGrCjzJ,KAAKuzJ,WAAY,EAEbxjJ,IAAWtU,UAAQgC,OAAOO,eAC7BgC,KAAKkzJ,cAAe,GAGrBlzJ,KAAKmzJ,uBAAyB,GAE1BnzJ,KAAK+yJ,iBACR/yJ,KAAKwyJ,GAAGrkJ,cAAcnO,KAAK+yJ,iBAGxB/yJ,KAAKgzJ,kBACRhzJ,KAAKwyJ,GAAGrkJ,cAAcnO,KAAKgzJ,kBAG5BhzJ,KAAK+yJ,gBAAkB/yJ,KAAKwyJ,GAAGzkJ,WAAW/N,KAAKs0J,4BAA4BxmK,KAAKkS,MAAOA,KAAKyyJ,IAAK,KACjGzyJ,KAAKu0J,YAAcv0J,KAAKwyJ,GAAGzkJ,WAAW/N,KAAKw0J,iBAAiB1mK,KAAKkS,MAAOA,KAAKyyJ,IAAK,KAClFzyJ,KAAKgzJ,iBAAmBhzJ,KAAKwyJ,GAAGzkJ,WAAW/N,KAAKy0J,uBAAuB3mK,KAAKkS,OAG5EA,KAAK00J,gBAAkB10J,KAAKwyJ,GAAGrgJ,eAAenS,KAAK20J,eAAe7mK,KAAKkS,MAAOA,KAAKyyJ,IAAK,WACxFzyJ,KAAK40J,qBAAuB50J,KAAKwyJ,GAAGrgJ,eAAenS,KAAK60J,oBAAoB/mK,KAAKkS,MAAOA,KAAKyyJ,IAAK,UAClGzyJ,KAAK80J,gBAAmB90J,KAAKwyJ,GAAGrgJ,eAAenS,KAAK+0J,eAAejnK,KAAKkS,MAAOA,KAAKyyJ,IAAI,YA8B1FxlJ,UAAW,SAASvN,GAOnB,OANIjE,UAAQwE,WAAWP,EAAM,OAC5BjE,UAAQwE,WAAWP,EAAM,aACzBjE,UAAQwE,WAAWP,EAAM,aACzBM,KAAKg1J,4BAA4Bt1J,GAG3BM,KAAK8yJ,mBAAmBrmK,KAAKuT,KAAKwyJ,GAAI9yJ,IAG9Ci1J,eAAgB,SAASj1J,GAOxB,OANAM,KAAK0yJ,4BAA6B,EAElC1yJ,KAAKszJ,aAA+C,SAAhC5zJ,EAAKkD,aAAa,WAAwBlD,EAAKkD,aAAa,MAEhF5C,KAAKwyJ,GAAGxnJ,UAED,GAGR6pJ,oBAAqB,SAASn1J,GAC7B,MAAMsF,EAAQtF,GAAQA,EAAKu1J,mBAAqBv1J,EAAKu1J,kBAAkB90J,QAKvE,OAHAH,KAAKwyJ,GAAGtmJ,qBAAqBzQ,UAAQgC,OAAOC,MAAOsH,EAAOtF,GAC1DM,KAAKwyJ,GAAG1iJ,iBAED,GAGRilJ,eAAgB,SAASr1J,GAExB,IAAIw1J,EAAe58I,SAAS5Y,EAAKkD,aAAa,MAQ9C,GAPA5C,KAAKm1J,2BAA2BD,EAAcl1J,KAAK2yJ,iCAEnD3yJ,KAAKuzJ,WAAY,EACjBvzJ,KAAKwyJ,GAAGvpJ,SAAU,EAClBjJ,KAAKwyJ,GAAG7oJ,eAAgB,EACxB3J,KAAKwyJ,GAAGxoJ,UAAW,EAEfhK,KAAKmzJ,uBAAuB58J,OAAS,EAAG,CAC3CyJ,KAAKqyJ,SAAW52J,UAAQwJ,MAAM,oCAAqCjF,KAAKmzJ,wBACxE,IAAI,MAAMnlJ,KAAUhO,KAAKmzJ,uBACxBnzJ,KAAKwyJ,GAAGnlJ,KAAKW,QAGdhO,KAAKqyJ,SAAW52J,UAAQwJ,MAAM,+BAAgCjF,KAAKmzJ,wBAKpE,OAFAnzJ,KAAKwyJ,GAAGtmJ,qBAAqBzQ,UAAQgC,OAAOM,UAAW,OAEhD,GAGR02J,uBAAwB,SAAS/0J,GAShC,OARIjE,UAAQwE,WAAWP,EAAM,OAASjE,UAAQwE,WAAWP,EAAM,aAAejE,UAAQwE,WAAWP,EAAM,cACtGM,KAAKo1J,kCAEDp1J,KAAKsyJ,oCACRtyJ,KAAKq1J,4BAIA,GAGRF,2BAA4B,SAASG,EAAsBC,GAC1D,IAAIC,EAAQF,EAAuBC,EAE/BC,EAAQ,GACXx1J,KAAKy1J,YAAY,uDAAyDH,EAAuB,gBAAkBC,GAGhHC,EAAQx1J,KAAKmzJ,uBAAuB58J,QACvCyJ,KAAKy1J,YAAY,8FAAgGD,EAAQ,kCAAoCx1J,KAAKmzJ,uBAAuB58J,OAAS,WAAa++J,EAAuB,gBAAkBC,GAGzP,IAAI,IAAIjpK,EAAI,EAAGA,EAAIkpK,EAAOlpK,IAEzB,IADA,IAAI0hB,EAAShO,KAAKmzJ,uBAAuBzlI,QAChCx0B,EAAI,EAAGA,EAAI8G,KAAKozJ,6BAA6B78J,OAAQ2C,IAC7D8G,KAAKozJ,6BAA6Bl6J,GAAG8U,GAInChO,KAAKqyJ,SAAWryJ,KAAKmzJ,uBAAuB58J,OAAS,GACxDkF,UAAQ0J,KAAK,4BAA6BnF,KAAKmzJ,wBAGhDnzJ,KAAK2yJ,gCAAkC2C,EAEnCt1J,KAAKuyJ,wBAA0B,IAClCvyJ,KAAKizJ,8BAAgC,IAIvCqB,4BAA6B,WAG5B,OAFAt0J,KAAKq1J,2BAEE,GAGRb,iBAAkB,SAAS90J,GAC1B,IAAIw1J,EAAe58I,SAAS5Y,EAAKkD,aAAa,MAG9C,OAFA5C,KAAKm1J,2BAA2BD,EAAcl1J,KAAK2yJ,kCAE5C,GAGR0C,wBAAyB,WACpBr1J,KAAK0yJ,4BACR1yJ,KAAKwyJ,GAAGnlJ,KAAK9R,SAAO,IAAK,CAAE0K,MAAOjG,KAAKyyJ,IAAK7rJ,EAAG5G,KAAK4yJ,mCAItDoC,4BAA6B,SAASt1J,GACrC,GAAIM,KAAK0yJ,2BAA4B,CACpC,IAAmD,IAA/C1yJ,KAAKmzJ,uBAAuBvvJ,QAAQlE,GAEvC,OAGDM,KAAKmzJ,uBAAuBjwJ,KAAKxD,GACjCM,KAAK6yJ,4BAED7yJ,KAAKuyJ,wBAA0B,IAClCvyJ,KAAKizJ,gCAEDjzJ,KAAKizJ,gCAAkCjzJ,KAAKuyJ,yBAE/ChoJ,WAAW,KACNvK,KAAK0mH,oBAAsBjrH,UAAQgC,OAAOM,WAC7CiC,KAAK0zJ,0BAEJ,MAMP0B,gCAAiC,WAC5Bp1J,KAAK0yJ,4BACR1yJ,KAAK4yJ,kCAIP6C,YAAa,SAAS3wJ,GAErB,MADArJ,UAAQuJ,MAAMF,GACR,IAAIzN,MAAMyN,M,yBC9blB,gBAAI4wJ,OAA2B,IAAX9/J,GAA0BA,GACjB,oBAATC,MAAwBA,MAChC3J,OACR01C,EAAQyJ,SAASn9C,UAAU0zC,MAiB/B,SAAS+zH,EAAQzuJ,EAAI0uJ,GACnB51J,KAAKsmH,IAAMp/G,EACXlH,KAAK61J,SAAWD,EAflB9pK,EAAQye,WAAa,WACnB,OAAO,IAAIorJ,EAAQ/zH,EAAMn1C,KAAK8d,WAAYmrJ,EAAOv0J,WAAYsM,eAE/D3hB,EAAQwwC,YAAc,WACpB,OAAO,IAAIq5H,EAAQ/zH,EAAMn1C,KAAK6vC,YAAao5H,EAAOv0J,WAAYm5B,gBAEhExuC,EAAQ2hB,aACR3hB,EAAQwuC,cAAgB,SAAS1sB,GAC3BA,GACFA,EAAQ6O,SAQZk5I,EAAQznK,UAAU4nK,MAAQH,EAAQznK,UAAU6nK,IAAM,aAClDJ,EAAQznK,UAAUuuB,MAAQ,WACxBzc,KAAK61J,SAASppK,KAAKipK,EAAO11J,KAAKsmH,MAIjCx6H,EAAQkqK,OAAS,SAASziI,EAAM0iI,GAC9BxoJ,aAAa8lB,EAAK2iI,gBAClB3iI,EAAK9pB,aAAewsJ,GAGtBnqK,EAAQqqK,SAAW,SAAS5iI,GAC1B9lB,aAAa8lB,EAAK2iI,gBAClB3iI,EAAK9pB,cAAgB,GAGvB3d,EAAQsqK,aAAetqK,EAAQw6F,OAAS,SAAS/yD,GAC/C9lB,aAAa8lB,EAAK2iI,gBAElB,IAAID,EAAQ1iI,EAAK9pB,aACbwsJ,GAAS,IACX1iI,EAAK2iI,eAAiB3rJ,YAAW,WAC3BgpB,EAAK8iI,YACP9iI,EAAK8iI,eACNJ,KAKP74I,EAAQ,KAIRtxB,EAAQ0qI,aAAgC,oBAAT3gI,MAAwBA,KAAK2gI,mBAClB,IAAX5gI,GAA0BA,EAAO4gI,cACxCx2H,MAAQA,KAAKw2H,aACrC1qI,EAAQwqK,eAAkC,oBAATzgK,MAAwBA,KAAKygK,qBAClB,IAAX1gK,GAA0BA,EAAO0gK,gBACxCt2J,MAAQA,KAAKs2J,iB,mCC9DtC,wBAAU1gK,EAAQ2K,GACf,aAEA,IAAI3K,EAAO4gI,aAAX,CAIA,IAII+/B,EA6HIv0J,EAZAk+H,EArBAs2B,EACAC,EAjGJC,EAAa,EACbC,EAAgB,GAChBC,GAAwB,EACxBt2J,EAAM1K,EAAOyF,SAoJbw7J,EAAW7pK,OAAO4gD,gBAAkB5gD,OAAO4gD,eAAeh4C,GAC9DihK,EAAWA,GAAYA,EAAStsJ,WAAassJ,EAAWjhK,EAGf,qBAArC,GAAGc,SAASjK,KAAKmJ,EAAOo+D,SApFxBuiG,EAAoB,SAASO,GACzB9iG,EAAQssB,UAAS,WAAcy2E,EAAaD,QAIpD,WAGI,GAAIlhK,EAAOo+G,cAAgBp+G,EAAOohK,cAAe,CAC7C,IAAIC,GAA4B,EAC5BC,EAAethK,EAAOkmB,UAM1B,OALAlmB,EAAOkmB,UAAY,WACfm7I,GAA4B,GAEhCrhK,EAAOo+G,YAAY,GAAI,KACvBp+G,EAAOkmB,UAAYo7I,EACZD,GAwEJE,GAIAvhK,EAAOwhK,iBA9CVl3B,EAAU,IAAIk3B,gBACVC,MAAMv7I,UAAY,SAASuL,GAE/B0vI,EADa1vI,EAAM1tB,OAIvB48J,EAAoB,SAASO,GACzB52B,EAAQo3B,MAAMtjD,YAAY8iD,KA2CvBx2J,GAAO,uBAAwBA,EAAIO,cAAc,WAtCpDmB,EAAO1B,EAAI0W,gBACfu/I,EAAoB,SAASO,GAGzB,IAAI/9F,EAASz4D,EAAIO,cAAc,UAC/Bk4D,EAAO1hD,mBAAqB,WACxB0/I,EAAaD,GACb/9F,EAAO1hD,mBAAqB,KAC5BrV,EAAKu1J,YAAYx+F,GACjBA,EAAS,MAEb/2D,EAAKpB,YAAYm4D,KAKrBw9F,EAAoB,SAASO,GACzBvsJ,WAAWwsJ,EAAc,EAAGD,KAlD5BN,EAAgB,gBAAkBprJ,KAAKC,SAAW,IAClDorJ,EAAkB,SAASpvI,GACvBA,EAAM0D,SAAWn1B,GACK,iBAAfyxB,EAAM1tB,MACyB,IAAtC0tB,EAAM1tB,KAAKiK,QAAQ4yJ,IACnBO,GAAc1vI,EAAM1tB,KAAKugC,MAAMs8H,EAAcjgK,UAIjDX,EAAOymC,iBACPzmC,EAAOymC,iBAAiB,UAAWo6H,GAAiB,GAEpD7gK,EAAOo8H,YAAY,YAAaykC,GAGpCF,EAAoB,SAASO,GACzBlhK,EAAOo+G,YAAYwiD,EAAgBM,EAAQ,OAgEnDD,EAASrgC,aA1KT,SAAsBhrH,GAEI,mBAAbA,IACTA,EAAW,IAAI6/B,SAAS,GAAK7/B,IAI/B,IADA,IAAI22B,EAAO,IAAI/rC,MAAM+K,UAAU5K,OAAS,GAC/BjK,EAAI,EAAGA,EAAI61C,EAAK5rC,OAAQjK,IAC7B61C,EAAK71C,GAAK6U,UAAU7U,EAAI,GAG5B,IAAIwxH,EAAO,CAAEtyG,SAAUA,EAAU22B,KAAMA,GAGvC,OAFAw0H,EAAcD,GAAc54C,EAC5By4C,EAAkBG,GACXA,KA6JTG,EAASP,eAAiBA,EA1J1B,SAASA,EAAeQ,UACbH,EAAcG,GAyBzB,SAASC,EAAaD,GAGlB,GAAIF,EAGArsJ,WAAWwsJ,EAAc,EAAGD,OACzB,CACH,IAAIh5C,EAAO64C,EAAcG,GACzB,GAAIh5C,EAAM,CACN84C,GAAwB,EACxB,KAjCZ,SAAa94C,GACT,IAAItyG,EAAWsyG,EAAKtyG,SAChB22B,EAAO27E,EAAK37E,KAChB,OAAQA,EAAK5rC,QACb,KAAK,EACDiV,IACA,MACJ,KAAK,EACDA,EAAS22B,EAAK,IACd,MACJ,KAAK,EACD32B,EAAS22B,EAAK,GAAIA,EAAK,IACvB,MACJ,KAAK,EACD32B,EAAS22B,EAAK,GAAIA,EAAK,GAAIA,EAAK,IAChC,MACJ,QACI32B,EAASo2B,WAnDpB,EAmDqCO,IAiBlBp6B,CAAI+1G,GADR,QAGIw4C,EAAeQ,GACfF,GAAwB,MAvE3C,CAyLiB,oBAAT/gK,UAAyC,IAAXD,EAAyBoK,KAAOpK,EAASC,Q,wCCzLhF,IAAI2hK,EAAa,SAAU3mH,GACzB,OAAO/3C,OAAOygB,OAAOs3B,MAAQA,EAAIt3B,OAAOs3B,GAAKA,GAgB3C4mH,EAAW,SAAU1hK,EAAKglB,EAAU43B,GACtC,IAAI+kH,EAAa3hK,EAAIlJ,MAAQkJ,EAAIuP,MAC7BvP,EAAImN,OAAS6X,EAAShlB,EAAImN,MAC5B6X,EAAShlB,EAAImN,MAAQ,GAEdw0J,IAAe38I,EAAShlB,EAAIlJ,QACnCkuB,EAAShlB,EAAIlJ,MAAQ,IAEvB,IAAI8qK,EAAc5hK,EAAImN,KACpB,GACAw0J,EAAa38I,EAAShlB,EAAIlJ,MAAQkuB,GAvBf,SAAUzH,EAAOyH,EAAUzV,EAAOsyJ,GACvD,GAAIA,IAAYtyJ,EACdyV,EAAS68I,GAAWJ,EAAWlkJ,EAAM,SAGrC,IAAK,IAAIhnB,EAAI,EAAGA,EAAIgZ,EAAM/O,OAAQjK,GAAK,EACnB,MAAdgnB,EAAMhnB,EAAE,KACVyuB,EAASzV,EAAMhZ,IAAMkrK,EAAWlkJ,EAAMhnB,EAAE,KAkB9CurK,CAAiBllH,EAAQr/B,MAAMvd,EAAIglG,KAAM48D,EAAa5hK,EAAIuP,MAAOvP,EAAIlJ,MAEjEkJ,EAAImN,MACN6X,EAAShlB,EAAImN,MAAMA,KAAKy0J,IAIxB78D,EAAU19E,EAAQ,IAClB06I,EAAYhtG,OAAO58D,UAAUgkB,KAAKpkB,KAAK,iBAE3ChC,EAAQosB,MAAQ,SAAU8Z,GACxB,IAAI/Z,EAAU,GACVwV,EAAQ,GACR1S,EAAW9C,EAoBf,OAjBA+Z,EAAIjvB,MAAM,gBAAgB2tB,OAAOonI,GAAW1oJ,SAAQ,SAAU7iB,GAC5D,IAAI0a,EAAO1a,EAAE,GACTomD,EAAUpmD,EAAE2tC,MAAM,GACT,MAATjzB,IACFwmB,EAAMvqB,KAAK,CAACqvB,IAAK,GAAIe,KAAM,KAC3BvY,EAAW0S,EAAMA,EAAMl3B,OAAO,IAGhC,IAAK,IAAI2C,EAAI,EAAGA,GAAK4hG,EAAQ7zF,IAAS,IAAI1Q,OAAQ2C,GAAK,EAAG,CACxD,IAAInD,EAAM+kG,EAAQ7zF,GAAM/N,GACxB,GAAInD,EAAIglG,IAAI7oF,KAAKygC,GACf,OAAO8kH,EAAS1hK,EAAKglB,EAAU43B,OAKrC16B,EAAQwV,MAAQA,EACTxV,GAGT,IAAI8/I,EAAe,SAAUC,EAAKC,GAChC,IAAI5pK,EAAI4pK,EAAKl1J,MAAM,QAAS,GAI5B,OAHiB,IAAb1U,EAAEkI,SACJyhK,EAAI3pK,EAAE,IAAMmpK,EAAWnpK,EAAE,KAEpB2pK,GAGTlsK,EAAQmsD,YAAc,SAAU7gD,GAC9B,OAAOA,EAAI2L,MAAM,SAAS2C,OAAOqyJ,EAAc,KAIjDjsK,EAAQksD,gBAAkBlsD,EAAQmsD,YAElCnsD,EAAQosD,cAAgB,SAAU9gD,GAChC,OAAOA,EAAI2L,MAAM,KAAKyC,IAAI+T,SAG5BztB,EAAQqsD,sBAAwB,SAAU/gD,GAGxC,IAFA,IAAI24E,EAAa,GACb/rE,EAAQ5M,EAAI2L,MAAM,KAAKyC,IAAIgyJ,GACtBlrK,EAAI,EAAGA,EAAI0X,EAAMzN,OAAQjK,GAAK,EACrCyjF,EAAW7sE,KAAK,CACd0rB,UAAW5qB,EAAM1X,GACjBuiC,GAAI7qB,EAAM1X,EAAI,GACdqhC,KAAM3pB,EAAM1X,EAAI,KAGpB,OAAOyjF,GAGTjkF,EAAQssD,qBAAuB,SAAUhhD,GACvC,OAAOA,EAAI2L,MAAM,KAAKyC,KAAI,SAAU+tB,GAClC,OAAOA,EAAKpG,UAAU,EAAGoG,EAAKh9B,OAAO,GAAGwM,MAAM,KAAK2C,OAAOqyJ,EAAc,QAI5EjsK,EAAQusD,yBAA2B,SAAUjhD,GAC3C,OAAOA,EAAI2L,MAAM,KAAKyC,KAAI,SAAU+b,GAClC,OAAOA,EAAOxe,MAAM,KAAKyC,KAAI,SAAUw1F,GACrC,IAAIk9D,EAAMnuJ,GAAS,EASnB,MAPkB,MAAdixF,EAAO,GACTk9D,EAAOV,EAAWx8D,IAElBk9D,EAAOV,EAAWx8D,EAAO7tE,UAAU,EAAG6tE,EAAOzkG,SAC7CwT,GAAS,GAGJ,CACLmuJ,KAAMA,EACNnuJ,OAAQA,W,gBCrHhB,IAAI+wF,EAAU19E,EAAQ,IAGlB+6I,EAAe,WACfn9D,EAAS,SAAUo9D,GACrB,IAAI9rK,EAAI,EACJ61C,EAAOhhC,UACPhJ,EAAMgqC,EAAK5rC,OACf,OAAO6hK,EAAUx2J,QAAQu2J,GAAc,SAAUnhK,GAC/C,GAAI1K,GAAK6L,EACP,OAAOnB,EAET,IAAIoK,EAAM+gC,EAAK71C,GAEf,OADAA,GAAK,EACG0K,GACR,IAAK,KACH,MAAO,IACT,IAAK,KACH,OAAO8B,OAAOsI,GAChB,IAAK,KACH,OAAOmY,OAAOnY,GAChB,IAAK,KACH,MAAO,QAMTi3J,EAAW,SAAUpxJ,EAAMlR,EAAKglB,GAClC,IAIIonB,EAAO,CAACl7B,EAAO,KAJTlR,EAAIilG,kBAAkB3vD,SAC7Bt1C,EAAIilG,OAAOjlG,EAAImN,KAAO6X,EAAWA,EAAShlB,EAAIlJ,OAC/CkJ,EAAIilG,SAGN,GAAIjlG,EAAIuP,MACN,IAAK,IAAIhZ,EAAI,EAAGA,EAAIyJ,EAAIuP,MAAM/O,OAAQjK,GAAK,EAAG,CAC5C,IAAIyB,EAAIgI,EAAIuP,MAAMhZ,GACdyJ,EAAIlJ,KACNs1C,EAAKj/B,KAAK6X,EAAShlB,EAAIlJ,MAAMkB,IAG7Bo0C,EAAKj/B,KAAK6X,EAAShlB,EAAIuP,MAAMhZ,UAKjC61C,EAAKj/B,KAAK6X,EAAShlB,EAAIlJ,OAEzB,OAAOmuG,EAAOp5D,MAAM,KAAMO,IAKxBm2H,EAAoB,CACtB,IAAK,IAAK,IAAK,IACf,IAAK,IAAK,IAAK,IACf,IAAK,IAAK,IAAK,IAAK,KAElBC,EAAoB,CAAC,IAAK,IAAK,IAAK,KAGxCxsK,EAAOD,QAAU,SAAUmsB,EAAS23H,GAClCA,EAAOA,GAAQ,GAEQ,MAAnB33H,EAAQ87B,UACV97B,EAAQ87B,QAAU,GAEA,MAAhB97B,EAAQprB,OACVorB,EAAQprB,KAAO,KAEjBorB,EAAQwV,MAAMre,SAAQ,SAAUqiB,GACR,MAAlBA,EAAMkB,WACRlB,EAAMkB,SAAW,OAIrB,IAAI6lI,EAAa5oB,EAAK4oB,YAAcF,EAChCG,EAAa7oB,EAAK6oB,YAAcF,EAChCvmI,EAAM,GAkCV,OA/BAwmI,EAAWppJ,SAAQ,SAAUnI,GAC3B6zF,EAAQ7zF,GAAMmI,SAAQ,SAAUrZ,GAC1BA,EAAIlJ,QAAQorB,GAAgC,MAArBA,EAAQliB,EAAIlJ,MACrCmlC,EAAI9uB,KAAKm1J,EAASpxJ,EAAMlR,EAAKkiB,IAEtBliB,EAAImN,QAAQ+U,GAAgC,MAArBA,EAAQliB,EAAImN,OAC1C+U,EAAQliB,EAAImN,MAAMkM,SAAQ,SAAUlP,GAClC8xB,EAAI9uB,KAAKm1J,EAASpxJ,EAAMlR,EAAKmK,aAOrC+X,EAAQwV,MAAMre,SAAQ,SAAUqiB,GAC9BO,EAAI9uB,KAAKm1J,EAAS,IAAKv9D,EAAQpuG,EAAE,GAAI+kC,IAErCgnI,EAAWrpJ,SAAQ,SAAUnI,GAC3B6zF,EAAQ7zF,GAAMmI,SAAQ,SAAUrZ,GAC1BA,EAAIlJ,QAAQ4kC,GAA4B,MAAnBA,EAAM17B,EAAIlJ,MACjCmlC,EAAI9uB,KAAKm1J,EAASpxJ,EAAMlR,EAAK07B,IAEtB17B,EAAImN,QAAQuuB,GAA4B,MAAnBA,EAAM17B,EAAImN,OACtCuuB,EAAM17B,EAAImN,MAAMkM,SAAQ,SAAUlP,GAChC8xB,EAAI9uB,KAAKm1J,EAASpxJ,EAAMlR,EAAKmK,gBAOhC8xB,EAAI7uB,KAAK,QAAU,S,gBCjG5B,IAAIigD,EAAYhmC,EAAQ,IACpBs7I,EAAiBt7I,EAAQ,KACzBu7I,EAAaD,EAAeC,WAC5BC,EAAaF,EAAeE,WAQhC,SAASpnI,EAAkBC,EAAOzM,EAAM0M,GACpC,OAAOD,EACFjB,MACAE,QAAO,SAASD,GAAY,OAAOA,EAASvpB,KAAO8d,KACnD0L,QAAO,SAASD,GAAY,OAAOA,EAASnzB,YAAco0B,KAC1DlsB,KAAI,SAASirB,GAAY,OAAOA,EAASljC,SAAU,GAK5D,SAAS68I,EAAUjjI,GAEfnH,KAAKmH,QAAUA,GAAoB,GAE9BnH,KAAKmH,QAAQkjI,cACdrqI,KAAKmH,QAAQkjI,YAnBO,GAqBxBtlI,QAAQH,IAAI,uBAAyB5E,KAAKmH,QAAQkjI,YAAc,WAMhErqI,KAAK81I,UAAY,GAiBrB,SAAS+iB,EAAa5gJ,EAASuS,GACZ,MAAXvS,GAAoB7hB,MAAMC,QAAQ4hB,EAAQwV,QAI9CxV,EAAQwV,MAAMre,SAAQ,SAAUqiB,GACT,UAAfA,EAAMxqB,MACNujB,EAAOiH,MAKnB,SAASqnI,EAAoBzpI,GAEzB,OAAOA,GAAgB,MAARA,GACRA,EAAKpoB,MAAqB,IAAbooB,EAAKpoB,MAClBooB,EAAK2C,KAAmB,IAAZ3C,EAAK2C,IAkJ5Bo4G,EAAUl8I,UAAU6lJ,eAAiB,WACjC/zI,KAAK81I,UAAY,IAWrB1L,EAAUl8I,UAAUghJ,aAAe,SAAS1+G,GACxCxwB,KAAK81I,UAAYtlH,GAYrB45G,EAAUl8I,UAAU6qK,gBAAkB,SAAUtnI,GAC5C,IAAIL,EAAWK,EAAMX,YACjBW,EAAMX,WAAWG,MAAK,SAASC,GAAS,MAA2B,QAApBA,EAAMC,aACzD,OAAIC,EACOA,EAASZ,MACXztB,MAAM,KACNyC,KAAI,SAASssB,GAAW,OAAOxZ,SAASwZ,MAEtC,CAACL,EAAMjB,MAAM,GAAGtpB,KAI/BkjI,EAAUl8I,UAAU8qK,sBAAwB,SAAUC,EAAaC,GAE/D,IADA,IAAI1oH,EAAU,GACLlkD,EAAI,EAAGA,EAAI2sK,EAAY1iK,SAAUjK,EAAG,CACzC,IAAI6sK,EAAUF,EAAY3sK,GACtB8sK,EAAUF,EAAY5sK,IAAM,KAChCkkD,EAAQ2oH,GAAWC,EAEvB,OAAO5oH,GAGX45F,EAAUl8I,UAAUmrK,2BAA6B,SAAS5nI,GACtD1sB,QAAQH,IAAI,sCAAuC5E,KAAK81I,WACxD,IAAIwjB,EAAct5J,KAAK+4J,gBAAgBtnI,GACvC1sB,QAAQH,IAAI,sCAAuC00J,GACnD,IAAIC,EAAU/nI,EAAiBC,EAAO6nI,EAAY,GAAI,QAClDE,EAAWhoI,EAAiBC,EAAO6nI,EAAY,GAAI,SACnDG,EAAiBz5J,KAAKg5J,sBAAsBM,EAAat5J,KAAK81I,WAClE/wI,QAAQH,IAAI,uCAAwC60J,GAGpD,IAAIC,EAAa15J,KAAK81I,UACjBplH,QAAO,SAAS1L,GAAQ,OAAwD,IAAjDh4B,OAAOo2B,OAAOq2I,GAAgB71J,QAAQohB,MA2B1E,OA1BAjgB,QAAQH,IAAI,oCAAqC80J,GAGjDjoI,EAAMjB,MAAMphB,SAAQ,SAAS4V,GACrBy0I,EAAez0I,EAAK9d,MACpB8d,EAAK9d,GAAKuyJ,EAAez0I,EAAK9d,QAItCwyJ,EAAWtqJ,SAAQ,SAAS4V,GACxByM,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI8d,EACJ1nB,UAAW,OACX/P,MAAOgsK,IAEX9nI,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI8d,EACJ1nB,UAAW,QACX/P,MAAOisK,OAGf/nI,EAAMX,WAAaW,EAAMX,YAAc,GACvCW,EAAMX,WAAW5tB,KAAK,CAClBiuB,UAAW,MACXX,MAAOxwB,KAAK81I,UAAU3yI,KAAK,OAExBsuB,GAGX24G,EAAUl8I,UAAUyrK,oBAAsB,SAASloI,EAAOV,GACtD,IAAI6oI,EAAsB,SAASnoI,EAAOzM,GACtCyM,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI8d,EACJ1nB,UAAW,QACX/P,MAAOqmJ,IAEXniH,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI8d,EACJ1nB,UAAW,OACX/P,MAAOomJ,KAGXA,EAAkBniH,EAAiBC,EAAOV,EAAa,QACvD6iH,EAAmBpiH,EAAiBC,EAAOV,EAAa,SAKxD/wB,KAAKmH,QAAQsnE,kBAAoBklE,IACjCA,EAAkBliH,EAAMqwB,KACLrwB,EAAMjB,MACZphB,QAAQ4V,IACjByM,EAAMjB,MAAMttB,KAAK,CACbgE,GAAI8d,EAAK9d,GACT5J,UAAW,OACX/P,MAAOomJ,OAOnB,IADA,IAAIkmB,EAAW,GACNvtK,EAAI,EAAGA,EAAI0T,KAAKmH,QAAQkjI,YAAc,IAAK/9I,EAAG,CACnD,IAAIwtK,EA/RD1uJ,KAAKmM,MAAM,WAAAnM,KAAKC,UADb,EAiSNuuJ,EAAoBnoI,EAAOqoI,GAC3BD,EAAS32J,KAAK42J,GAOlB,OALAroI,EAAMX,WAAaW,EAAMX,YAAc,GACvCW,EAAMX,WAAW5tB,KAAK,CAClBiuB,UAAW,MACXX,MAAOO,EAAc,IAAM8oI,EAAS12J,KAAK,OAEtCsuB,GAoBX24G,EAAUl8I,UAAU6rK,kBAAoB,SAAStoI,GAG7C,IAAIV,EACAR,EAAWkB,EAAMjB,OAASiB,EAAMjB,MAC/BhrB,KAAI,SAASirB,GAAY,OAAOA,EAASvpB,MACzCwpB,QAAO,SAAS1L,EAAM2L,EAAOC,GAC1B,OAAOA,EAAMhtB,QAAQohB,KAAU2L,KAElCp6B,QAAU,EACXs6B,EAAaY,EAAMX,YAAcW,EAAMX,WAAWv6B,QAAW,EAEjE,GAAiB,IAAbg6B,GAAkBA,EAAW,EAE7B,OAAOkB,EAEX,GAAgB,GAAZlB,GAA+B,IAAdM,EAEjB,OAAOY,EAGX,GAAiB,IAAblB,EACAQ,EAAcU,EAAMjB,MAAM,GAAGtpB,OAC1B,CAGH,IAAI8pB,EAAWS,EAAMX,WAAWJ,QAAO,SAASQ,GAAS,MAA2B,QAApBA,EAAMC,aAAwB,GAC9F,IAAIH,EAIA,OAAOS,EAHPV,EAAczY,SAAS0Y,EAASR,MAAMztB,MAAM,KAAK,IAsBzD,OAhBAgC,QAAQH,IAAI,qCAAsC5E,KAAK81I,WACvD/wI,QAAQH,IAAI,qCAAuCmsB,IAEY,IAAzC/wB,KAAK81I,UAAUlyI,QAAQmtB,IAGzChsB,QAAQH,IAAI,2EAEZ6sB,EAAQzxB,KAAKq5J,2BAA2B5nI,KAExC1sB,QAAQH,IAAI,2EAEZ6sB,EAAQzxB,KAAK25J,oBAAoBloI,EAAOV,IAG5C/wB,KAAK81I,UAAY91I,KAAK+4J,gBAAgBtnI,GAC/BA,GAaX24G,EAAUl8I,UAAUmjJ,uBAAyB,SAAUhiH,EAAM+hH,GAEzD,IAAK0nB,EAAoBzpI,GACrB,OAAOA,EAGX,IAAIpX,EAAUmrC,EAAUlrC,MAAMmX,EAAK2C,KAE/Bn8B,EAAOmK,KAqBX,OApBA64J,EAAa5gJ,GAAS,SAAUwZ,GAGxB57B,EAAKsR,QAAQmjI,uBAjXzB,SAAgC74G,GAE5B,GAAKA,GAAUr7B,MAAMC,QAAQo7B,EAAMX,YASnC,IALA,IAAIs5D,EAAUuuE,EAAWlnI,GACrByyB,EAAQ,GAGRhrD,EAAIu4B,EAAMX,WAAWv6B,OAClB2C,KAEH,GAAsC,QAAlCu4B,EAAMX,WAAW53B,GAAGi4B,UAAxB,CAMA,IAFA,IAAI6oI,EAAiBvoI,EAAMX,WAAW53B,GAAGs3B,MAAMztB,MAAM,KAE5CzW,EAAI,EAAGA,EAAI0tK,EAAezjK,OAAQjK,IAAK,CAE5C,IAAI04B,EAAOg1I,EAAe1tK,GAC1B43D,EAAMhhD,KAAK8hB,GAEX,IAAIhhB,EAAQomF,EAAQplE,GAAM88B,KAAK/+C,MAAM,KACrCqnF,EAAQplE,GAAM88B,KAAO,CAAC99C,EAAM,GAAI,IAAK1X,EAAG,IAAK0X,EAAM,GAAI,IAAK1X,GAAG6W,KAAK,IACpEinF,EAAQplE,GAAMs/C,MAAQ,CAAC8lB,EAAQplE,GAAMs/C,MAAO,IAAKh4E,GAAG6W,KAAK,IAGzDsuB,EAAMX,WAAW1hB,SAAQ,SAAU6qJ,GAC/B,GAA+B,QAA3BA,EAAa9oI,UAAjB,CAIA,IAAI+oI,EAAeD,EAAazpI,MAAMztB,MAAM,MACR,IAAhCm3J,EAAat2J,QAAQohB,IAKzBk1I,EAAa9qJ,SAAQ,SAAU+qJ,GAC3B/vE,EAAQ+vE,GAAar4G,KAAOsoC,EAAQplE,GAAM88B,KAC1CsoC,EAAQ+vE,GAAa71F,MAAQ8lB,EAAQplE,GAAMs/C,MACvC61F,IAAgBn1I,GAChBk/B,EAAMhhD,KAAKi3J,UAQ3B1oI,EAAMjB,MAAQooI,EAAWxuE,EAASlmC,GAClCzyB,EAAMX,WAAW7sB,OAAO/K,EAAG,IA6TvBoxI,CAAuB74G,GAzTnC,SAAgCA,GAE5B,GAAKA,GAAUr7B,MAAMC,QAAQo7B,EAAMX,YAAnC,CAMA,IAAIs5D,EAAUuuE,EAAWlnI,GAGzBA,EAAMX,WAAW1hB,SAAQ,SAAUgrJ,GAC/B,GAAiC,QAA7BA,EAAejpI,UAAnB,CAIApsB,QAAQG,KAAK,wBAA0Bk1J,EAAe5pI,OAEtD4pI,EAAeC,MAAO,EAKtB,IAHA,IAAIL,EAAiBI,EAAe5pI,MAAMztB,MAAM,KAGvCzW,EAAI,EAAGA,EAAI0tK,EAAezjK,OAAQjK,IAAK,CAE5C,IAAI04B,EAAOg1I,EAAe1tK,UACnB89F,EAAQplE,GAGfyM,EAAMX,WAAW1hB,SAAQ,SAAU6qJ,GAC/B,GAA+B,QAA3BA,EAAa9oI,UAAjB,CAIA,IAAI+oI,EAAeD,EAAazpI,MAAMztB,MAAM,MACR,IAAhCm3J,EAAat2J,QAAQohB,KAKzBk1I,EAAa9qJ,SAAQ,SAAU+qJ,UACpB/vE,EAAQ+vE,MAInBF,EAAaI,MAAO,YAOhC5oI,EAAMjB,MAAQooI,EAAWxuE,GAIzB,IADA,IAAI99F,EAAImlC,EAAMX,WAAWv6B,OAClBjK,KACCmlC,EAAMX,WAAWxkC,GAAG+tK,MACpB5oI,EAAMX,WAAW7sB,OAAO3X,EAAG,QAvD/ByY,QAAQG,KAAK,6DAwTTo1J,CAAuB7oI,IAOtB57B,EAAKsR,QAAQsnE,iBAAmB2iE,EA3P7C,SAA8B3/G,GACrBA,IAIAr7B,MAAMC,QAAQo7B,EAAM8oI,WACrB9oI,EAAM8oI,QAAU,IAGf9oI,EAAM8oI,QAAQ53E,MACX,SAAUr2F,GAAK,MAAmB,6BAAZA,EAAEiB,UAC5BkkC,EAAM8oI,QAAQr3J,KAAK,CAAC,MAAS,8BAiPzBs3J,CAAqB/oI,GApQjC,SAA8BA,GACrBA,QAAsC,IAAtBA,EAAMgpI,cAI3BhpI,EAAMgpI,iBAAcl6J,GAiQZm6J,CAAqBjpI,MAItB,IAAIkkD,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKoxB,EAAUrL,MAAM9/B,MAW7BmyH,EAAUl8I,UAAU2kJ,sBAAwB,SAAUxjH,GAElD,IAAKypI,EAAoBzpI,GACrB,OAAOA,EAGX,IAAIpX,EAAUmrC,EAAUlrC,MAAMmX,EAAK2C,KAE/Bn8B,EAAOmK,KASX,OARA64J,EAAa5gJ,GAAS,SAAUwZ,GACL,YAAnBA,EAAM9B,WAA8C,YAAnB8B,EAAM9B,WAI3C95B,EAAKkkK,kBAAkBtoI,MAGpB,IAAIkkD,sBAAsB,CAC7B1uE,KAAMooB,EAAKpoB,KACX+qB,IAAKoxB,EAAUrL,MAAM9/B,MAM7BlsB,EAAOD,QAAUs+I,G,cCrejBt+I,EAAQ8sK,WAAa,SAASxuE,EAASlmC,GACrC,IAAI1zB,EAAQ,GAGZ,QAAuB,IAAZ45D,GACyB,IAAhCp9F,OAAOuY,KAAK6kF,GAAS7zF,OAAc,CAEhCH,MAAMC,QAAQ6tD,KACjBA,EAAQ,IAIV,IAAK,IAAI53D,EAAI,EAAGA,EAAI43D,EAAM3tD,OAAQjK,IAAK,CACrC,IAAI04B,EAAOk/B,EAAM53D,GACby+B,EAASq/D,EAAQplE,GACrBh4B,OAAOuY,KAAKwlB,GAAQ3b,SAAQ,SAAU9R,GACpCkzB,EAAMttB,KAAK,CACTgE,GAAI8d,EACJ1nB,UAAWA,EACX/P,MAAOw9B,EAAOztB,QAMpBtQ,OAAOuY,KAAK6kF,GAASh7E,SAAQ,SAAU4V,GAErC,GADAA,EAAO1M,SAAS0M,KACZk/B,EAAMtgD,QAAQohB,IAAS,GAA3B,CAKA,IAAI+F,EAASq/D,EAAQplE,GACrBh4B,OAAOuY,KAAKwlB,GAAQ3b,SAAQ,SAAU9R,GACpCkzB,EAAMttB,KAAK,CACTgE,GAAI8d,EACJ1nB,UAAWA,EACX/P,MAAOw9B,EAAOztB,YAMtB,OAAOkzB,GAGT1kC,EAAQ6sK,WAAa,SAAUlnI,GAC7B,IAAI24D,EAAU,GASd,YAP2B,IAAhB34D,EAAMjB,OAAyBp6B,MAAMC,QAAQo7B,EAAMjB,QAC5DiB,EAAMjB,MAAMphB,SAAQ,SAAU4V,GACvBolE,EAAQplE,EAAK9d,MAChBkjF,EAAQplE,EAAK9d,IAAM,IACrBkjF,EAAQplE,EAAK9d,IAAI8d,EAAK1nB,WAAa0nB,EAAKz3B,SAGrC68F,I,cC3DTr+F,EAAOD,QATiB,SAASmzE,EAAMpyE,EAAM8uG,EAAWqnD,GACpDhjJ,KAAKi/D,KAAOA,EACZj/D,KAAKnT,KAAOA,EACZmT,KAAK27F,UAAYA,EACjB37F,KAAKgjJ,UAAYA,I,gBCdrB,MAAM2X,EAAOv9I,EAAQ,KAEfulI,EAAgBvlI,EAAQ,IACxBw9I,EAAuBx9I,EAAQ,KAK/BslI,EAAgB,WAElB1iJ,KAAK+gC,IAiHT,WACI,MAAMt8B,EAAU,2DAEhB,QAAyBlE,IAArBizB,OAAOqnI,UACP91J,QAAQH,IAAIH,OACT,CACH,MAAMq2J,EAAWtnI,OAAOqnI,UAExB,QAA0Bt6J,IAAtBu6J,EAASrnI,UAA0BqnI,EAASrnI,SAAS,YACrD,OAAOqnI,EAEX/1J,QAAQH,IAAIH,IA5HLs2J,IAMfrY,EAAcx0J,UAAYlB,OAAOY,OAAOgtK,EAAqB1sK,WAK7Dw0J,EAAczsJ,YAAcysJ,EAS5BA,EAAcx0J,UAAUwsJ,YAAc,SAASsgB,EAAexvJ,GAC1DzG,QAAQH,IAAK,6BAA4B5E,KAAK+gC,KAC9Ch8B,QAAQH,IAAK,8BAA6Bo2J,GAC1C,MAAMliF,EAAU,IAAI3hE,eAEpB2hE,EAAQzhE,mBAAqB,WACzB,GAAIyhE,EAAQ9/D,aAAe7B,eAAe8jJ,MAChB,MAAnBniF,EAAQ/oE,OACXvE,EAASstE,EAAQ7jE,mBACd,GAAI6jE,EAAQ9/D,aAAe7B,eAAe8jJ,KAC7C,MAAM,IAAI5jK,MACL,yDACGyhF,EAAQ/oE,SAKxB+oE,EAAQ/+D,KAAK,OAAQ/Z,KAAK+gC,KAC1B+3C,EAAQ7+D,iBAAiB,eACrB0oI,EAAc5mD,4BAClBjjB,EAAQzrE,KAAK2tJ,GACbj2J,QAAQH,IAAK,QAAOo2J,IASxBtY,EAAcx0J,UAAUgtK,eAAiB,SAASvoJ,GAC9C,MAAMlN,EAASmH,KAAKsL,MAAMvF,GAAUwoJ,QAKpC11J,EAAOioB,QACP,MAAMkD,EAAQ,GAOd,OALAnrB,EAAO2J,QACHg0I,GACIA,EAAKgY,QACExqI,EAAM1tB,KAAK,IAAIy3J,EAAKvX,EAAKA,KAAMA,EAAKrmI,MAAOqmI,EAAKllE,OAExDttD,GAQX8xH,EAAcx0J,UAAUmtK,OAAS,SAAS1oJ,GAItC,GAHA5N,QAAQH,IAAK,wBAAuB+N,EAASjc,YAGrB,iBAAbic,EACP,OAAO,EAIX,IAAI2sC,EAEJ,IACIA,EAAO1yC,KAAKsL,MAAMvF,GACpB,MAAO3N,GAGL,OAFAD,QAAQH,IAAII,IAEL,EAIX,QAAqBzE,IAAjB++C,EAAK67G,QACL,OAAO,EAIX,MAAMvqI,EAAQ0uB,EAAK67G,QAEnB,SAAMvqI,EAAM,KAAMA,EAAM,GAAG,gBA8B/B7kC,EAAOD,QAAU42J,G,cCvIjB,MAAMiY,EAAO,SAASvX,EAAMD,EAAOjlE,GAC/Bl+E,KAAKojJ,KAAOA,EACZpjJ,KAAKmjJ,MAAQA,EACbnjJ,KAAKk+E,IAAMA,GAOfy8E,EAAKzsK,UAAUotK,QAAU,WACrB,OAAOt7J,KAAKojJ,MAOhBuX,EAAKzsK,UAAUqtK,aAAe,WAC1B,OAAOv7J,KAAKmjJ,OAOhBwX,EAAKzsK,UAAUstK,WAAa,WACxB,OAAOx7J,KAAKk+E,KAGhBnyF,EAAOD,QAAU6uK,G,cChCjB,MAAMC,EAAuB,WACzB,MAAM,IAAIvjK,MAAM,0DAapBujK,EAAqB1sK,UAAUmf,KAAO,SAAco2I,EAAiBj4I,GACjExL,KAAK06I,YAAY+I,EAAgBxkF,KAAMtsD,IAC/B3S,KAAKq7J,OAAO1oJ,GACZ8wI,EAAgBT,UAAYhjJ,KAAKk7J,eAAevoJ,IAEhD5N,QAAQH,IAAI,wDACZ6+I,EAAgBT,UAAY,IAEhCx3I,EAASi4I,MAcjBmX,EAAqB1sK,UAAUwsJ,YAAc,SAAS+gB,EAAWjwJ,GAC7D,MAAM,IAAInU,MAAM,iDAmBpBujK,EAAqB1sK,UAAUgtK,eAAiB,SAASvoJ,GACrD,MAAM,IAAItb,MAAM,4CAUpBujK,EAAqB1sK,UAAUmtK,OAAS,SAAS1oJ,GAC7C,MAAM,IAAItb,MAAM,4CAGpBtL,EAAOD,QAAU8uK,G,olECjEjB,IAAIc,GAAe,EACfC,GAAuB,EAUpB,SAASC,EAAeC,EAAU5D,EAAMhwH,GAC7C,MAAM30B,EAAQuoJ,EAASvoJ,MAAM2kJ,GAC7B,OAAO3kJ,GAASA,EAAM/c,QAAU0xC,GAAO3vB,SAAShF,EAAM20B,GAAM,IAMvD,SAAS6zH,EAAwB5vK,EAAQ6vK,EAAiBtoJ,GAC/D,IAAKvnB,EAAO6uC,kBACV,OAEF,MAAMtyB,EAAQvc,EAAO6uC,kBAAkB7sC,UACjC8tK,EAAyBvzJ,EAAM4zB,iBACrC5zB,EAAM4zB,iBAAmB,SAAS4/H,EAAiBvhC,GACjD,GAAIuhC,IAAoBF,EACtB,OAAOC,EAAuBp6H,MAAM5hC,KAAMmB,WAE5C,MAAM04H,EAAmBxgI,IACvB,MAAM6iK,EAAgBzoJ,EAAQpa,GAC1B6iK,GACFxhC,EAAGwhC,IAKP,OAFAl8J,KAAKm8J,UAAYn8J,KAAKm8J,WAAa,GACnCn8J,KAAKm8J,UAAUzhC,GAAMb,EACdmiC,EAAuBp6H,MAAM5hC,KAAM,CAACi8J,EACzCpiC,KAGJ,MAAMuiC,EAA4B3zJ,EAAMwmC,oBACxCxmC,EAAMwmC,oBAAsB,SAASgtH,EAAiBvhC,GACpD,GAAIuhC,IAAoBF,IAAoB/7J,KAAKm8J,YACzCn8J,KAAKm8J,UAAUzhC,GACrB,OAAO0hC,EAA0Bx6H,MAAM5hC,KAAMmB,WAE/C,MAAMk7J,EAAcr8J,KAAKm8J,UAAUzhC,GAEnC,cADO16H,KAAKm8J,UAAUzhC,GACf0hC,EAA0Bx6H,MAAM5hC,KAAM,CAACi8J,EAC5CI,KAGJrvK,OAAOC,eAAewb,EAAO,KAAOszJ,EAAiB,CACnD5uK,MACE,OAAO6S,KAAK,MAAQ+7J,IAEtB36I,IAAIs5G,GACE16H,KAAK,MAAQ+7J,KACf/7J,KAAKivC,oBAAoB8sH,EACrB/7J,KAAK,MAAQ+7J,WACV/7J,KAAK,MAAQ+7J,IAElBrhC,GACF16H,KAAKq8B,iBAAiB0/H,EAClB/7J,KAAK,MAAQ+7J,GAAmBrhC,IAGxCxtI,YAAY,EACZmtH,cAAc,IAIX,SAASiiD,EAAWC,GACzB,MAAoB,kBAATA,EACF,IAAIllK,MAAM,yBAA2BklK,EACxC,4BAENb,EAAea,EACPA,EAAQ,8BACZ,8BAOC,SAASC,EAAgBD,GAC9B,MAAoB,kBAATA,EACF,IAAIllK,MAAM,yBAA2BklK,EACxC,4BAENZ,GAAwBY,EACjB,oCAAsCA,EAAO,WAAa,YAG5D,SAAS33J,IACd,GAAsB,iBAAX1Y,OAAqB,CAC9B,GAAIwvK,EACF,OAEqB,oBAAZ32J,SAAkD,mBAAhBA,QAAQH,KACnDG,QAAQH,IAAIg9B,MAAM78B,QAAS5D,YAQ1B,SAASs7J,EAAWC,EAAWC,GAC/BhB,GAGL52J,QAAQI,KAAKu3J,EAAY,8BAAgCC,EACrD,aASC,SAASC,EAAc1wK,GAC5B,MAAM,UAACsuC,GAAatuC,EAGduZ,EAAS,CAACqa,QAAS,KAAMi0B,QAAS,MAGxC,QAAsB,IAAX7nD,IAA2BA,EAAOsuC,UAE3C,OADA/0B,EAAOqa,QAAU,iBACVra,EAGT,GAAI+0B,EAAUqiI,gBACZp3J,EAAOqa,QAAU,UACjBra,EAAOsuC,QAAU6nH,EAAephI,EAAUsZ,UACtC,mBAAoB,QACnB,GAAItZ,EAAUsiI,qBACW,IAA3B5wK,EAAO6wK,iBAA6B7wK,EAAO8wK,0BAC1C9wK,EAAOu3I,eAKXh+H,EAAOqa,QAAU,SACjBra,EAAOsuC,QAAU6nH,EAAephI,EAAUsZ,UACtC,wBAAyB,QACxB,GAAItZ,EAAUC,cACjBD,EAAUsZ,UAAUxgC,MAAM,sBAC5B7N,EAAOqa,QAAU,OACjBra,EAAOsuC,QAAU6nH,EAAephI,EAAUsZ,UACtC,qBAAsB,OACrB,KAAI5nD,EAAO6uC,oBACdP,EAAUsZ,UAAUxgC,MAAM,wBAQ5B,OADA7N,EAAOqa,QAAU,2BACVra,EAPPA,EAAOqa,QAAU,SACjBra,EAAOsuC,QAAU6nH,EAAephI,EAAUsZ,UACtC,uBAAwB,GAC5BruC,EAAOw3J,oBAAsB/wK,EAAOknH,mBAChC,qBAAsBlnH,EAAOknH,kBAAkBllH,UAMrD,OAAOuX,EAST,SAASooD,EAASqvG,GAChB,MAA+C,oBAAxClwK,OAAOkB,UAAUwI,SAASjK,KAAKywK,GAQjC,SAASC,EAAcxjK,GAC5B,OAAKk0D,EAASl0D,GAIP3M,OAAOuY,KAAK5L,GAAM+L,QAAO,SAASmkD,EAAah8D,GACpD,MAAMoN,EAAQ4yD,EAASl0D,EAAK9L,IACtBN,EAAQ0N,EAAQkiK,EAAcxjK,EAAK9L,IAAQ8L,EAAK9L,GAChDuvK,EAAgBniK,IAAUjO,OAAOuY,KAAKhY,GAAOgJ,OACnD,YAAcgK,IAAVhT,GAAuB6vK,EAClBvzG,EAEF78D,OAAOgoG,OAAOnrC,EAAa,CAAC,CAACh8D,GAAMN,MACzC,IAXMoM,EAgCJ,SAAS0jK,EAAY53J,EAAQ64B,EAAOg/H,GACzC,MAAMC,EAAkBD,EAAW,eAAiB,cAC9CE,EAAiB,IAAIj+I,IAC3B,GAAc,OAAV+e,EACF,OAAOk/H,EAET,MAAMC,EAAa,GAcnB,OAbAh4J,EAAO2J,QAAQ7hB,IACM,UAAfA,EAAM0Z,MACN1Z,EAAMy4H,kBAAoB1nF,EAAMp3B,IAClCu2J,EAAWv6J,KAAK3V,KAGpBkwK,EAAWruJ,QAAQsuJ,IACjBj4J,EAAO2J,QAAQoX,IACTA,EAAMvf,OAASs2J,GAAmB/2I,EAAMkgE,UAAYg3E,EAAUx2J,IAhCjE,SAASy2J,EAAUn3I,EAAO2hC,EAAMy1G,GAChCz1G,IAAQy1G,EAAU75I,IAAIokC,EAAKjhD,MAGhC02J,EAAUx8I,IAAI+mC,EAAKjhD,GAAIihD,GACvBn7D,OAAOuY,KAAK4iD,GAAM/4C,QAAQviB,IACpBA,EAAK2uD,SAAS,MAChBmiH,EAAUn3I,EAAOA,EAAMr5B,IAAIg7D,EAAKt7D,IAAQ+wK,GAC/B/wK,EAAK2uD,SAAS,QACvB2M,EAAKt7D,GAAMuiB,QAAQlI,IACjBy2J,EAAUn3I,EAAOA,EAAMr5B,IAAI+Z,GAAK02J,QAuBhCD,CAAUl4J,EAAQ+gB,EAAOg3I,OAIxBA,EC/OT,MAAMnL,EAAU53J,EAET,SAASojK,EAAiB3xK,GAC/B,MAAMsuC,EAAYtuC,GAAUA,EAAOsuC,UAEnC,IAAKA,EAAUC,aACb,OAGF,MAAMqjI,EAAiBrjK,EAAoBvO,GAErC6xK,EAAuB,SAASpxK,GACpC,GAAiB,iBAANA,GAAkBA,EAAE6oC,WAAa7oC,EAAE8oC,SAC5C,OAAO9oC,EAET,MAAMqxK,EAAK,GA4CX,OA3CAhxK,OAAOuY,KAAK5Y,GAAGyiB,QAAQvhB,IACrB,GAAY,YAARA,GAA6B,aAARA,GAA8B,gBAARA,EAC7C,OAEF,MAAMT,EAAuB,iBAAXT,EAAEkB,GAAqBlB,EAAEkB,GAAO,CAACsmC,MAAOxnC,EAAEkB,SAC5C0S,IAAZnT,EAAE6wK,OAA0C,iBAAZ7wK,EAAE6wK,QACpC7wK,EAAEqtB,IAAMrtB,EAAEgnC,IAAMhnC,EAAE6wK,OAEpB,MAAMC,EAAW,SAAS1+F,EAAQ3yE,GAChC,OAAI2yE,EACKA,EAAS3yE,EAAK8L,OAAO,GAAGqnE,cAAgBnzE,EAAKqtC,MAAM,GAE3C,aAATrtC,EAAuB,WAAaA,GAE9C,QAAgB0T,IAAZnT,EAAE+mC,MAAqB,CACzB6pI,EAAGvoI,SAAWuoI,EAAGvoI,UAAY,GAC7B,IAAI0oI,EAAK,GACc,iBAAZ/wK,EAAE+mC,OACXgqI,EAAGD,EAAS,MAAOrwK,IAAQT,EAAE+mC,MAC7B6pI,EAAGvoI,SAASvyB,KAAKi7J,GACjBA,EAAK,GACLA,EAAGD,EAAS,MAAOrwK,IAAQT,EAAE+mC,MAC7B6pI,EAAGvoI,SAASvyB,KAAKi7J,KAEjBA,EAAGD,EAAS,GAAIrwK,IAAQT,EAAE+mC,MAC1B6pI,EAAGvoI,SAASvyB,KAAKi7J,SAGL59J,IAAZnT,EAAE6wK,OAA0C,iBAAZ7wK,EAAE6wK,OACpCD,EAAGxoI,UAAYwoI,EAAGxoI,WAAa,GAC/BwoI,EAAGxoI,UAAU0oI,EAAS,GAAIrwK,IAAQT,EAAE6wK,OAEpC,CAAC,MAAO,OAAO7uJ,QAAQgvJ,SACN79J,IAAXnT,EAAEgxK,KACJJ,EAAGxoI,UAAYwoI,EAAGxoI,WAAa,GAC/BwoI,EAAGxoI,UAAU0oI,EAASE,EAAKvwK,IAAQT,EAAEgxK,QAKzCzxK,EAAE0xK,WACJL,EAAGvoI,UAAYuoI,EAAGvoI,UAAY,IAAIz7B,OAAOrN,EAAE0xK,WAEtCL,GAGHM,EAAmB,SAASv4I,EAAanmB,GAC7C,GAAIk+J,EAAe/pH,SAAW,GAC5B,OAAOn0C,EAAKmmB,GAGd,IADAA,EAAcnZ,KAAKsL,MAAMtL,KAAKwL,UAAU2N,MACQ,iBAAtBA,EAAYsP,MAAoB,CACxD,MAAMkpI,EAAQ,SAASxoK,EAAK2B,EAAGC,GACzBD,KAAK3B,KAAS4B,KAAK5B,KACrBA,EAAI4B,GAAK5B,EAAI2B,UACN3B,EAAI2B,KAIf6mK,GADAx4I,EAAcnZ,KAAKsL,MAAMtL,KAAKwL,UAAU2N,KACtBsP,MAAO,kBAAmB,uBAC5CkpI,EAAMx4I,EAAYsP,MAAO,mBAAoB,wBAC7CtP,EAAYsP,MAAQ0oI,EAAqBh4I,EAAYsP,OAEvD,GAAItP,GAA4C,iBAAtBA,EAAYkO,MAAoB,CAExD,IAAIuqI,EAAOz4I,EAAYkO,MAAM4B,WAC7B2oI,EAAOA,IAA0B,iBAATA,EAAqBA,EAAO,CAACrqI,MAAOqqI,IAC5D,MAAMC,EAA6BX,EAAe/pH,QAAU,GAE5D,GAAKyqH,IAAwB,SAAfA,EAAKP,OAAmC,gBAAfO,EAAKP,OACf,SAAfO,EAAKrqI,OAAmC,gBAAfqqI,EAAKrqI,UACtCqG,EAAUC,aAAaikI,0BACvBlkI,EAAUC,aAAaikI,0BAA0B7oI,YAChD4oI,GAA6B,CAElC,IAAIprJ,EAMJ,UAPO0S,EAAYkO,MAAM4B,WAEN,gBAAf2oI,EAAKP,OAA0C,gBAAfO,EAAKrqI,MACvC9gB,EAAU,CAAC,OAAQ,QACK,SAAfmrJ,EAAKP,OAAmC,SAAfO,EAAKrqI,QACvC9gB,EAAU,CAAC,UAETA,EAEF,OAAOmnB,EAAUC,aAAaF,mBAC7BG,KAAKC,IAEJ,IAAIgkI,GADJhkI,EAAUA,EAAQjK,OAAO9jC,GAAgB,eAAXA,EAAEwsC,OACdnI,KAAKrkC,GAAKymB,EAAQsvE,KAAKrvE,GACvC1mB,EAAE8sC,MAAM/2B,cAAc8wB,SAASngB,KAUjC,OATKqrJ,GAAOhkI,EAAQpkC,QAAU8c,EAAQogB,SAAS,UAC7CkrI,EAAMhkI,EAAQA,EAAQpkC,OAAS,IAE7BooK,IACF54I,EAAYkO,MAAM0B,SAAW6oI,EAAKP,MAAQ,CAACA,MAAOU,EAAIhpI,UACZ,CAACxB,MAAOwqI,EAAIhpI,WAExD5P,EAAYkO,MAAQ8pI,EAAqBh4I,EAAYkO,OACrDo+H,EAAQ,WAAazlJ,KAAKwL,UAAU2N,IAC7BnmB,EAAKmmB,KAIlBA,EAAYkO,MAAQ8pI,EAAqBh4I,EAAYkO,OAGvD,OADAo+H,EAAQ,WAAazlJ,KAAKwL,UAAU2N,IAC7BnmB,EAAKmmB,IAGR64I,EAAa,SAASvlK,GAC1B,OAAIykK,EAAe/pH,SAAW,GACrB16C,EAEF,CACLxM,KAAM,CACJgyK,sBAAuB,kBACvBC,yBAA0B,kBAC1B/8B,kBAAmB,kBACnBg9B,qBAAsB,gBACtBC,4BAA6B,uBAC7BC,gBAAiB,mBACjBC,+BAAgC,kBAChCC,wBAAyB,kBACzBC,gBAAiB,aACjBC,mBAAoB,aACpBC,mBAAoB,cACpBjmK,EAAExM,OAASwM,EAAExM,KACf4X,QAASpL,EAAEoL,QACXqqC,WAAYz1C,EAAEy1C,YAAcz1C,EAAEysB,eAC9BpvB,WACE,OAAOsJ,KAAKnT,MAAQmT,KAAKyE,SAAW,MAAQzE,KAAKyE,WAmBvD,GALA+1B,EAAU2C,aATY,SAASpX,EAAaxS,EAAWq8G,GACrD0uC,EAAiBv4I,EAAap5B,IAC5B6tC,EAAUsiI,mBAAmBnwK,EAAG4mB,EAAWla,IACrCu2H,GACFA,EAAQgvC,EAAWvlK,SAKYvL,KAAK0sC,GAKxCA,EAAUC,aAAa0C,aAAc,CACvC,MAAMoiI,EAAmB/kI,EAAUC,aAAa0C,aAC5CrvC,KAAK0sC,EAAUC,cACnBD,EAAUC,aAAa0C,aAAe,SAAS/Y,GAC7C,OAAOk6I,EAAiBl6I,EAAIz3B,GAAK4yK,EAAiB5yK,GAAG+tC,KAAKnZ,IACxD,GAAI50B,EAAE0oC,QAAU9T,EAAOqX,iBAAiBriC,QACpC5J,EAAEsnC,QAAU1S,EAAOuX,iBAAiBviC,OAItC,MAHAgrB,EAAOue,YAAY1wB,QAAQkvB,IACzBA,EAAM1a,SAEF,IAAI47I,aAAa,GAAI,iBAE7B,OAAOj+I,GACNloB,GAAK2jC,QAAQE,OAAO0hI,EAAWvlK,QCjLjC,SAASomK,EAAoBvzK,EAAQwzK,GACtCxzK,EAAOsuC,UAAUC,cACnB,oBAAqBvuC,EAAOsuC,UAAUC,cAGlCvuC,EAAOsuC,UAAUC,eAKI,mBAAhBilI,EAKXxzK,EAAOsuC,UAAUC,aAAa29B,gBAC5B,SAAyBryC,GACvB,OAAO25I,EAAY35I,GAChB2U,KAAK9E,IACJ,MAAM+pI,EAAiB55I,EAAYkO,OAASlO,EAAYkO,MAAMsC,MACxDqpI,EAAkB75I,EAAYkO,OAClClO,EAAYkO,MAAMC,OACd2rI,EAAqB95I,EAAYkO,OACrClO,EAAYkO,MAAMyD,UAcpB,OAbA3R,EAAYkO,MAAQ,CAClBuB,UAAW,CACTyC,kBAAmB,UACnBK,oBAAqB1C,EACrBQ,aAAcypI,GAAsB,IAGpCF,IACF55I,EAAYkO,MAAMuB,UAAUkB,SAAWipI,GAErCC,IACF75I,EAAYkO,MAAMuB,UAAUmB,UAAYipI,GAEnC1zK,EAAOsuC,UAAUC,aAAa0C,aAAapX,MA1BxDhhB,QAAQC,MAAM,gECLX,SAAS86J,EAAgB5zK,GAC9BA,EAAO0vC,YAAc1vC,EAAO0vC,aAAe1vC,EAAO6zK,kBAG7C,SAASC,EAAY9zK,GAC1B,GAAsB,iBAAXA,GAAuBA,EAAO6uC,qBAAuB,YAC5D7uC,EAAO6uC,kBAAkB7sC,WAAY,CACvClB,OAAOC,eAAef,EAAO6uC,kBAAkB7sC,UAAW,UAAW,CACnEf,MACE,OAAO6S,KAAKigK,UAEd7+I,IAAIk6B,GACEt7C,KAAKigK,UACPjgK,KAAKivC,oBAAoB,QAASjvC,KAAKigK,UAEzCjgK,KAAKq8B,iBAAiB,QAASr8B,KAAKigK,SAAW3kH,IAEjDpuD,YAAY,EACZmtH,cAAc,IAEhB,MAAM6lD,EACFh0K,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACvC5pF,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACjC,WAuCE,OAtCK91E,KAAKmgK,eACRngK,KAAKmgK,aAAgB9mK,IAGnBA,EAAEkoB,OAAO8a,iBAAiB,WAAY+jI,IACpC,IAAIh1H,EAEFA,EADEl/C,EAAO6uC,kBAAkB7sC,UAAUo3I,aAC1BtlI,KAAKslI,eACbr0G,KAAK7jC,GAAKA,EAAEkxC,OAASlxC,EAAEkxC,MAAMp3B,KAAOk5J,EAAG9hI,MAAMp3B,IAErC,CAACo3B,MAAO8hI,EAAG9hI,OAGxB,MAAMjX,EAAQ,IAAIm7G,MAAM,SACxBn7G,EAAMiX,MAAQ8hI,EAAG9hI,MACjBjX,EAAM+jB,SAAWA,EACjB/jB,EAAMs+C,YAAc,CAACv6B,YACrB/jB,EAAMyW,QAAU,CAACzkC,EAAEkoB,QACnBvhB,KAAKoiI,cAAc/6G,KAErBhuB,EAAEkoB,OAAOue,YAAY1wB,QAAQkvB,IAC3B,IAAI8M,EAEFA,EADEl/C,EAAO6uC,kBAAkB7sC,UAAUo3I,aAC1BtlI,KAAKslI,eACbr0G,KAAK7jC,GAAKA,EAAEkxC,OAASlxC,EAAEkxC,MAAMp3B,KAAOo3B,EAAMp3B,IAElC,CAACo3B,SAEd,MAAMjX,EAAQ,IAAIm7G,MAAM,SACxBn7G,EAAMiX,MAAQA,EACdjX,EAAM+jB,SAAWA,EACjB/jB,EAAMs+C,YAAc,CAACv6B,YACrB/jB,EAAMyW,QAAU,CAACzkC,EAAEkoB,QACnBvhB,KAAKoiI,cAAc/6G,MAGvBrnB,KAAKq8B,iBAAiB,YAAar8B,KAAKmgK,eAEnCD,EAAyBt+H,MAAM5hC,KAAMmB,iBAMhD1G,EAA8BvO,EAAQ,QAASmN,IACxCA,EAAEssE,aACL34E,OAAOC,eAAeoM,EAAG,cACvB,CAAC9L,MAAO,CAAC69C,SAAU/xC,EAAE+xC,YAElB/xC,IAKN,SAASgnK,EAAuBn0K,GAErC,GAAsB,iBAAXA,GAAuBA,EAAO6uC,qBACnC,eAAgB7uC,EAAO6uC,kBAAkB7sC,YAC3C,qBAAsBhC,EAAO6uC,kBAAkB7sC,UAAW,CAC5D,MAAMoyK,EAAqB,SAASh7G,EAAIhnB,GACtC,MAAO,CACLA,QACA,WAQE,YAPmB/9B,IAAfP,KAAKugK,QACY,UAAfjiI,EAAMlF,KACRp5B,KAAKugK,MAAQj7G,EAAGusF,iBAAiBvzG,GAEjCt+B,KAAKugK,MAAQ,MAGVvgK,KAAKugK,OAEdC,IAAKl7G,IAKT,IAAKp5D,EAAO6uC,kBAAkB7sC,UAAUm3I,WAAY,CAClDn5I,EAAO6uC,kBAAkB7sC,UAAUm3I,WAAa,WAE9C,OADArlI,KAAKygK,SAAWzgK,KAAKygK,UAAY,GAC1BzgK,KAAKygK,SAASvmI,SAEvB,MAAMwmI,EAAex0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SACxDjyC,EAAO6uC,kBAAkB7sC,UAAUiwC,SACjC,SAAkBG,EAAO/c,GACvB,IAAIgpD,EAASm2F,EAAa9+H,MAAM5hC,KAAMmB,WAKtC,OAJKopE,IACHA,EAAS+1F,EAAmBtgK,KAAMs+B,GAClCt+B,KAAKygK,SAASv9J,KAAKqnE,IAEdA,GAGX,MAAMo2F,EAAkBz0K,EAAO6uC,kBAAkB7sC,UAAUsmG,YAC3DtoG,EAAO6uC,kBAAkB7sC,UAAUsmG,YACjC,SAAqBjqB,GACnBo2F,EAAgB/+H,MAAM5hC,KAAMmB,WAC5B,MAAMyuC,EAAM5vC,KAAKygK,SAAS78J,QAAQ2mE,IACrB,IAAT36B,GACF5vC,KAAKygK,SAASx8J,OAAO2rC,EAAK,IAIlC,MAAMgxH,EAAgB10K,EAAO6uC,kBAAkB7sC,UAAU88F,UACzD9+F,EAAO6uC,kBAAkB7sC,UAAU88F,UAAY,SAAmBzpE,GAChEvhB,KAAKygK,SAAWzgK,KAAKygK,UAAY,GACjCG,EAAch/H,MAAM5hC,KAAM,CAACuhB,IAC3BA,EAAOue,YAAY1wB,QAAQkvB,IACzBt+B,KAAKygK,SAASv9J,KAAKo9J,EAAmBtgK,KAAMs+B,OAIhD,MAAMuiI,EAAmB30K,EAAO6uC,kBAAkB7sC,UAAUk3I,aAC5Dl5I,EAAO6uC,kBAAkB7sC,UAAUk3I,aACjC,SAAsB7jH,GACpBvhB,KAAKygK,SAAWzgK,KAAKygK,UAAY,GACjCI,EAAiBj/H,MAAM5hC,KAAM,CAACuhB,IAE9BA,EAAOue,YAAY1wB,QAAQkvB,IACzB,MAAMisC,EAASvqE,KAAKygK,SAASxvI,KAAK5iC,GAAKA,EAAEiwC,QAAUA,GAC/CisC,GACFvqE,KAAKygK,SAASx8J,OAAOjE,KAAKygK,SAAS78J,QAAQ2mE,GAAS,WAIvD,GAAsB,iBAAXr+E,GAAuBA,EAAO6uC,mBACrC,eAAgB7uC,EAAO6uC,kBAAkB7sC,WACzC,qBAAsBhC,EAAO6uC,kBAAkB7sC,WAC/ChC,EAAOmnH,gBACL,SAAUnnH,EAAOmnH,aAAanlH,WAAY,CACrD,MAAM4yK,EAAiB50K,EAAO6uC,kBAAkB7sC,UAAUm3I,WAC1Dn5I,EAAO6uC,kBAAkB7sC,UAAUm3I,WAAa,WAC9C,MAAMh0F,EAAUyvH,EAAel/H,MAAM5hC,KAAM,IAE3C,OADAqxC,EAAQjiC,QAAQm7D,GAAUA,EAAOi2F,IAAMxgK,MAChCqxC,GAGTrkD,OAAOC,eAAef,EAAOmnH,aAAanlH,UAAW,OAAQ,CAC3Df,MAQE,YAPmBoT,IAAfP,KAAKugK,QACiB,UAApBvgK,KAAKs+B,MAAMlF,KACbp5B,KAAKugK,MAAQvgK,KAAKwgK,IAAI3uB,iBAAiB7xI,KAAKs+B,OAE5Ct+B,KAAKugK,MAAQ,MAGVvgK,KAAKugK,UAMb,SAASQ,EAAa70K,GAC3B,IAAKA,EAAO6uC,kBACV,OAGF,MAAMimI,EAAe90K,EAAO6uC,kBAAkB7sC,UAAUqiH,SACxDrkH,EAAO6uC,kBAAkB7sC,UAAUqiH,SAAW,WAC5C,MAAO+3B,EAAU24B,EAAQC,GAAS//J,UAIlC,GAAIA,UAAU5K,OAAS,GAAyB,mBAAb+xI,EACjC,OAAO04B,EAAap/H,MAAM5hC,KAAMmB,WAKlC,GAA4B,IAAxB6/J,EAAazqK,SAAsC,IAArB4K,UAAU5K,QACpB,mBAAb+xI,GACT,OAAO04B,EAAap/H,MAAM5hC,KAAM,IAGlC,MAAMmhK,EAAkB,SAASxuJ,GAC/B,MAAMyuJ,EAAiB,GAiBvB,OAhBgBzuJ,EAASlN,SACjB2J,QAAQ81C,IACd,MAAMm8G,EAAgB,CACpBn6J,GAAIg+C,EAAOh+C,GACXshF,UAAWtjC,EAAOsjC,UAClBvhF,KAAM,CACJgiI,eAAgB,kBAChBC,gBAAiB,oBACjBhkF,EAAOj+C,OAASi+C,EAAOj+C,MAE3Bi+C,EAAO5/C,QAAQ8J,QAAQviB,IACrBw0K,EAAcx0K,GAAQq4D,EAAO87D,KAAKn0H,KAEpCu0K,EAAeC,EAAcn6J,IAAMm6J,IAG9BD,GAIHE,EAAe,SAAS96I,GAC5B,OAAO,IAAIjH,IAAIvyB,OAAOuY,KAAKihB,GAAOhhB,IAAI3X,GAAO,CAACA,EAAK24B,EAAM34B,OAG3D,GAAIsT,UAAU5K,QAAU,EAAG,CACzB,MAAMgrK,EAA0B,SAAS5uJ,GACvCsuJ,EAAOK,EAAaH,EAAgBxuJ,MAGtC,OAAOquJ,EAAap/H,MAAM5hC,KAAM,CAACuhK,EAC/Bj5B,IAIJ,OAAO,IAAItrG,QAAQ,CAACC,EAASC,KAC3B8jI,EAAap/H,MAAM5hC,KAAM,CACvB,SAAS2S,GACPsqB,EAAQqkI,EAAaH,EAAgBxuJ,MACpCuqB,MACJxC,KAAKumI,EAAQC,IAIb,SAASM,EAA2Bt1K,GACzC,KAAwB,iBAAXA,GAAuBA,EAAO6uC,mBACvC7uC,EAAOmnH,cAAgBnnH,EAAOwnH,gBAChC,OAIF,KAAM,aAAcxnH,EAAOmnH,aAAanlH,WAAY,CAClD,MAAM4yK,EAAiB50K,EAAO6uC,kBAAkB7sC,UAAUm3I,WACtDy7B,IACF50K,EAAO6uC,kBAAkB7sC,UAAUm3I,WAAa,WAC9C,MAAMh0F,EAAUyvH,EAAel/H,MAAM5hC,KAAM,IAE3C,OADAqxC,EAAQjiC,QAAQm7D,GAAUA,EAAOi2F,IAAMxgK,MAChCqxC,IAIX,MAAMqvH,EAAex0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SACpDuiI,IACFx0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SAAW,WAC5C,MAAMosC,EAASm2F,EAAa9+H,MAAM5hC,KAAMmB,WAExC,OADAopE,EAAOi2F,IAAMxgK,KACNuqE,IAGXr+E,EAAOmnH,aAAanlH,UAAUqiH,SAAW,WACvC,MAAMhmC,EAASvqE,KACf,OAAOA,KAAKwgK,IAAIjwD,WAAW71E,KAAKj1B,GAK9BhL,EAAkBgL,EAAQ8kE,EAAOjsC,OAAO,KAK9C,KAAM,aAAcpyC,EAAOwnH,eAAexlH,WAAY,CACpD,MAAMuzK,EAAmBv1K,EAAO6uC,kBAAkB7sC,UAAUo3I,aACxDm8B,IACFv1K,EAAO6uC,kBAAkB7sC,UAAUo3I,aACjC,WACE,MAAMo8B,EAAYD,EAAiB7/H,MAAM5hC,KAAM,IAE/C,OADA0hK,EAAUtyJ,QAAQg8B,GAAYA,EAASo1H,IAAMxgK,MACtC0hK,IAGbjnK,EAA8BvO,EAAQ,QAASmN,IAC7CA,EAAE+xC,SAASo1H,IAAMnnK,EAAEsoK,WACZtoK,IAETnN,EAAOwnH,eAAexlH,UAAUqiH,SAAW,WACzC,MAAMnlE,EAAWprC,KACjB,OAAOA,KAAKwgK,IAAIjwD,WAAW71E,KAAKj1B,GAC9BhL,EAAkBgL,EAAQ2lC,EAAS9M,OAAO,KAIhD,KAAM,aAAcpyC,EAAOmnH,aAAanlH,cACpC,aAAchC,EAAOwnH,eAAexlH,WACtC,OAIF,MAAM8yK,EAAe90K,EAAO6uC,kBAAkB7sC,UAAUqiH,SACxDrkH,EAAO6uC,kBAAkB7sC,UAAUqiH,SAAW,WAC5C,GAAIpvG,UAAU5K,OAAS,GACnB4K,UAAU,aAAcjV,EAAOq8I,iBAAkB,CACnD,MAAMjqG,EAAQn9B,UAAU,GACxB,IAAIopE,EACAn/B,EACAj7B,EAoBJ,OAnBAnQ,KAAKqlI,aAAaj2H,QAAQ/gB,IACpBA,EAAEiwC,QAAUA,IACVisC,EACFp6D,GAAM,EAENo6D,EAASl8E,KAIf2R,KAAKslI,eAAel2H,QAAQhiB,IACtBA,EAAEkxC,QAAUA,IACV8M,EACFj7B,GAAM,EAENi7B,EAAWh+C,GAGRA,EAAEkxC,QAAUA,IAEjBnuB,GAAQo6D,GAAUn/B,EACbpO,QAAQE,OAAO,IAAIsiI,aACxB,4DACA,uBACOj1F,EACFA,EAAOgmC,WACLnlE,EACFA,EAASmlE,WAEXvzE,QAAQE,OAAO,IAAIsiI,aACxB,gDACA,uBAEJ,OAAOwB,EAAap/H,MAAM5hC,KAAMmB,YAI7B,SAASygK,EAAkC11K,GAIhDA,EAAO6uC,kBAAkB7sC,UAAUs2I,gBACjC,WAEE,OADAxkI,KAAK6hK,qBAAuB7hK,KAAK6hK,sBAAwB,GAClD70K,OAAOuY,KAAKvF,KAAK6hK,sBACrBr8J,IAAIgjC,GAAYxoC,KAAK6hK,qBAAqBr5H,GAAU,KAG3D,MAAMk4H,EAAex0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SACxDjyC,EAAO6uC,kBAAkB7sC,UAAUiwC,SACjC,SAAkBG,EAAO/c,GACvB,IAAKA,EACH,OAAOm/I,EAAa9+H,MAAM5hC,KAAMmB,WAElCnB,KAAK6hK,qBAAuB7hK,KAAK6hK,sBAAwB,GAEzD,MAAMt3F,EAASm2F,EAAa9+H,MAAM5hC,KAAMmB,WAMxC,OALKnB,KAAK6hK,qBAAqBtgJ,EAAOra,KAE+B,IAA1DlH,KAAK6hK,qBAAqBtgJ,EAAOra,IAAItD,QAAQ2mE,IACtDvqE,KAAK6hK,qBAAqBtgJ,EAAOra,IAAIhE,KAAKqnE,GAF1CvqE,KAAK6hK,qBAAqBtgJ,EAAOra,IAAM,CAACqa,EAAQgpD,GAI3CA,GAGX,MAAMq2F,EAAgB10K,EAAO6uC,kBAAkB7sC,UAAU88F,UACzD9+F,EAAO6uC,kBAAkB7sC,UAAU88F,UAAY,SAAmBzpE,GAChEvhB,KAAK6hK,qBAAuB7hK,KAAK6hK,sBAAwB,GAEzDtgJ,EAAOue,YAAY1wB,QAAQkvB,IAEzB,GADsBt+B,KAAKqlI,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,QAAUA,GAE5D,MAAM,IAAIkhI,aAAa,wBACnB,wBAGR,MAAMsC,EAAkB9hK,KAAKqlI,aAC7Bu7B,EAAch/H,MAAM5hC,KAAMmB,WAC1B,MAAM4gK,EAAa/hK,KAAKqlI,aACrB30G,OAAOsxI,IAAqD,IAAxCF,EAAgBl+J,QAAQo+J,IAC/ChiK,KAAK6hK,qBAAqBtgJ,EAAOra,IAAM,CAACqa,GAAQvnB,OAAO+nK,IAGzD,MAAMlB,EAAmB30K,EAAO6uC,kBAAkB7sC,UAAUk3I,aAC5Dl5I,EAAO6uC,kBAAkB7sC,UAAUk3I,aACjC,SAAsB7jH,GAGpB,OAFAvhB,KAAK6hK,qBAAuB7hK,KAAK6hK,sBAAwB,UAClD7hK,KAAK6hK,qBAAqBtgJ,EAAOra,IACjC25J,EAAiBj/H,MAAM5hC,KAAMmB,YAGxC,MAAMw/J,EAAkBz0K,EAAO6uC,kBAAkB7sC,UAAUsmG,YAC3DtoG,EAAO6uC,kBAAkB7sC,UAAUsmG,YACjC,SAAqBjqB,GAanB,OAZAvqE,KAAK6hK,qBAAuB7hK,KAAK6hK,sBAAwB,GACrDt3F,GACFv9E,OAAOuY,KAAKvF,KAAK6hK,sBAAsBzyJ,QAAQo5B,IAC7C,MAAMoH,EAAM5vC,KAAK6hK,qBAAqBr5H,GAAU5kC,QAAQ2mE,IAC3C,IAAT36B,GACF5vC,KAAK6hK,qBAAqBr5H,GAAUvkC,OAAO2rC,EAAK,GAEC,IAA/C5vC,KAAK6hK,qBAAqBr5H,GAAUjyC,eAC/ByJ,KAAK6hK,qBAAqBr5H,KAIhCm4H,EAAgB/+H,MAAM5hC,KAAMmB,YAIlC,SAAS8gK,EAAwB/1K,GACtC,IAAKA,EAAO6uC,kBACV,OAEF,MAAM+iI,EAAiBrjK,EAAoBvO,GAE3C,GAAIA,EAAO6uC,kBAAkB7sC,UAAUiwC,UACnC2/H,EAAe/pH,SAAW,GAC5B,OAAO6tH,EAAkC11K,GAK3C,MAAMg2K,EAAsBh2K,EAAO6uC,kBAAkB7sC,UAChDs2I,gBACLt4I,EAAO6uC,kBAAkB7sC,UAAUs2I,gBACjC,WACE,MAAM29B,EAAgBD,EAAoBtgI,MAAM5hC,MAEhD,OADAA,KAAKoiK,gBAAkBpiK,KAAKoiK,iBAAmB,GACxCD,EAAc38J,IAAI+b,GAAUvhB,KAAKoiK,gBAAgB7gJ,EAAOra,MAGnE,MAAM05J,EAAgB10K,EAAO6uC,kBAAkB7sC,UAAU88F,UACzD9+F,EAAO6uC,kBAAkB7sC,UAAU88F,UAAY,SAAmBzpE,GAahE,GAZAvhB,KAAKqiK,SAAWriK,KAAKqiK,UAAY,GACjCriK,KAAKoiK,gBAAkBpiK,KAAKoiK,iBAAmB,GAE/C7gJ,EAAOue,YAAY1wB,QAAQkvB,IAEzB,GADsBt+B,KAAKqlI,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,QAAUA,GAE5D,MAAM,IAAIkhI,aAAa,wBACnB,yBAKHx/J,KAAKoiK,gBAAgB7gJ,EAAOra,IAAK,CACpC,MAAMo7J,EAAY,IAAIp2K,EAAO0vC,YAAYra,EAAOue,aAChD9/B,KAAKqiK,SAAS9gJ,EAAOra,IAAMo7J,EAC3BtiK,KAAKoiK,gBAAgBE,EAAUp7J,IAAMqa,EACrCA,EAAS+gJ,EAEX1B,EAAch/H,MAAM5hC,KAAM,CAACuhB,KAG7B,MAAMs/I,EAAmB30K,EAAO6uC,kBAAkB7sC,UAAUk3I,aA6D5D,SAASm9B,EAAwBj9G,EAAIwd,GACnC,IAAI9wC,EAAM8wC,EAAY9wC,IAOtB,OANAhlC,OAAOuY,KAAK+/C,EAAG88G,iBAAmB,IAAIhzJ,QAAQozJ,IAC5C,MAAMC,EAAiBn9G,EAAG88G,gBAAgBI,GACpCE,EAAiBp9G,EAAG+8G,SAASI,EAAev7J,IAClD8qB,EAAMA,EAAIpwB,QAAQ,IAAIkpD,OAAO43G,EAAex7J,GAAI,KAC5Cu7J,EAAev7J,MAEd,IAAIyuE,sBAAsB,CAC/B1uE,KAAM67D,EAAY77D,KAClB+qB,QAGJ,SAAS2wI,EAAwBr9G,EAAIwd,GACnC,IAAI9wC,EAAM8wC,EAAY9wC,IAOtB,OANAhlC,OAAOuY,KAAK+/C,EAAG88G,iBAAmB,IAAIhzJ,QAAQozJ,IAC5C,MAAMC,EAAiBn9G,EAAG88G,gBAAgBI,GACpCE,EAAiBp9G,EAAG+8G,SAASI,EAAev7J,IAClD8qB,EAAMA,EAAIpwB,QAAQ,IAAIkpD,OAAO23G,EAAev7J,GAAI,KAC5Cw7J,EAAex7J,MAEd,IAAIyuE,sBAAsB,CAC/B1uE,KAAM67D,EAAY77D,KAClB+qB,QAnFJ9lC,EAAO6uC,kBAAkB7sC,UAAUk3I,aACjC,SAAsB7jH,GACpBvhB,KAAKqiK,SAAWriK,KAAKqiK,UAAY,GACjCriK,KAAKoiK,gBAAkBpiK,KAAKoiK,iBAAmB,GAE/CvB,EAAiBj/H,MAAM5hC,KAAM,CAAEA,KAAKqiK,SAAS9gJ,EAAOra,KAAOqa,WACpDvhB,KAAKoiK,gBAAiBpiK,KAAKqiK,SAAS9gJ,EAAOra,IAC9ClH,KAAKqiK,SAAS9gJ,EAAOra,IAAIA,GAAKqa,EAAOra,WAClClH,KAAKqiK,SAAS9gJ,EAAOra,KAGhChb,EAAO6uC,kBAAkB7sC,UAAUiwC,SACjC,SAAkBG,EAAO/c,GACvB,GAA4B,WAAxBvhB,KAAKiuE,eACP,MAAM,IAAIuxF,aACR,sDACA,qBAEJ,MAAM1hI,EAAU,GAAG5D,MAAMztC,KAAK0U,UAAW,GACzC,GAAuB,IAAnB28B,EAAQvnC,SACPunC,EAAQ,GAAGgC,YAAY7O,KAAKzjC,GAAKA,IAAM8wC,GAG1C,MAAM,IAAIkhI,aACR,gHAEA,qBAGJ,MAAMoD,EAAgB5iK,KAAKqlI,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,QAAUA,GAC9D,GAAIskI,EACF,MAAM,IAAIpD,aAAa,wBACnB,sBAGNx/J,KAAKqiK,SAAWriK,KAAKqiK,UAAY,GACjCriK,KAAKoiK,gBAAkBpiK,KAAKoiK,iBAAmB,GAC/C,MAAMS,EAAY7iK,KAAKqiK,SAAS9gJ,EAAOra,IACvC,GAAI27J,EAKFA,EAAU1kI,SAASG,GAGnBtB,QAAQC,UAAUvC,KAAK,KACrB16B,KAAKoiI,cAAc,IAAII,MAAM,4BAE1B,CACL,MAAM8/B,EAAY,IAAIp2K,EAAO0vC,YAAY,CAAC0C,IAC1Ct+B,KAAKqiK,SAAS9gJ,EAAOra,IAAMo7J,EAC3BtiK,KAAKoiK,gBAAgBE,EAAUp7J,IAAMqa,EACrCvhB,KAAKgrF,UAAUs3E,GAEjB,OAAOtiK,KAAKqlI,aAAap0G,KAAK5iC,GAAKA,EAAEiwC,QAAUA,IA+BnD,CAAC,cAAe,gBAAgBlvB,SAAQ,SAASs/E,GAC/C,MAAMy6C,EAAej9I,EAAO6uC,kBAAkB7sC,UAAUwgG,GAClDo0E,EAAY,CAAC,CAACp0E,KAClB,MAAMvsD,EAAOhhC,UAGb,OAFqBA,UAAU5K,QACH,mBAAjB4K,UAAU,GAEZgoI,EAAavnG,MAAM5hC,KAAM,CAC7B8iE,IACC,MAAMzzC,EAAOkzI,EAAwBviK,KAAM8iE,GAC3C3gC,EAAK,GAAGP,MAAM,KAAM,CAACvS,KAEtBlf,IACKgyB,EAAK,IACPA,EAAK,GAAGP,MAAM,KAAMzxB,IAErBhP,UAAU,KAGVgoI,EAAavnG,MAAM5hC,KAAMmB,WAC/Bu5B,KAAKooC,GAAey/F,EAAwBviK,KAAM8iE,MAErD52E,EAAO6uC,kBAAkB7sC,UAAUwgG,GAAUo0E,EAAUp0E,MAGzD,MAAMq0E,EACF72K,EAAO6uC,kBAAkB7sC,UAAU6jF,oBACvC7lF,EAAO6uC,kBAAkB7sC,UAAU6jF,oBACjC,WACE,OAAK5wE,UAAU5K,QAAW4K,UAAU,GAAG8F,MAGvC9F,UAAU,GAAKwhK,EAAwB3iK,KAAMmB,UAAU,IAChD4hK,EAAwBnhI,MAAM5hC,KAAMmB,YAHlC4hK,EAAwBnhI,MAAM5hC,KAAMmB,YAQjD,MAAM6hK,EAAuBh2K,OAAOi2K,yBAChC/2K,EAAO6uC,kBAAkB7sC,UAAW,oBACxClB,OAAOC,eAAef,EAAO6uC,kBAAkB7sC,UAC3C,mBAAoB,CAClBf,MACE,MAAM21E,EAAckgG,EAAqB71K,IAAIy0C,MAAM5hC,MACnD,MAAyB,KAArB8iE,EAAY77D,KACP67D,EAEFy/F,EAAwBviK,KAAM8iE,MAI7C52E,EAAO6uC,kBAAkB7sC,UAAUsmG,YACjC,SAAqBjqB,GACnB,GAA4B,WAAxBvqE,KAAKiuE,eACP,MAAM,IAAIuxF,aACR,sDACA,qBAIJ,IAAKj1F,EAAOi2F,IACV,MAAM,IAAIhB,aAAa,yFAC2B,aAGpD,KADgBj1F,EAAOi2F,MAAQxgK,MAE7B,MAAM,IAAIw/J,aAAa,6CACnB,sBAKN,IAAIj+I,EADJvhB,KAAKqiK,SAAWriK,KAAKqiK,UAAY,GAEjCr1K,OAAOuY,KAAKvF,KAAKqiK,UAAUjzJ,QAAQ8zJ,IAChBljK,KAAKqiK,SAASa,GAAUpjI,YACtC7O,KAAKqN,GAASisC,EAAOjsC,QAAUA,KAEhC/c,EAASvhB,KAAKqiK,SAASa,MAIvB3hJ,IACgC,IAA9BA,EAAOue,YAAYvpC,OAGrByJ,KAAKolI,aAAaplI,KAAKoiK,gBAAgB7gJ,EAAOra,KAG9Cqa,EAAOizE,YAAYjqB,EAAOjsC,OAE5Bt+B,KAAKoiI,cAAc,IAAII,MAAM,wBAK9B,SAAS2gC,EAAmBj3K,GACjC,MAAM4xK,EAAiBrjK,EAAoBvO,GAM3C,IAJKA,EAAO6uC,mBAAqB7uC,EAAO8wK,0BAEtC9wK,EAAO6uC,kBAAoB7uC,EAAO8wK,0BAE/B9wK,EAAO6uC,kBACV,OAGF,MAAMqoI,EAC0D,IAA9Dl3K,EAAO6uC,kBAAkB7sC,UAAU6iF,gBAAgBx6E,OAGjDunK,EAAe/pH,QAAU,IAC3B,CAAC,sBAAuB,uBAAwB,mBAC3C3kC,SAAQ,SAASs/E,GAChB,MAAMy6C,EAAej9I,EAAO6uC,kBAAkB7sC,UAAUwgG,GAClDo0E,EAAY,CAAC,CAACp0E,KAIlB,OAHAvtF,UAAU,GAAK,IAAiB,oBAAXutF,EACjBxiG,EAAO0kF,gBACP1kF,EAAOypF,uBAAuBx0E,UAAU,IACrCgoI,EAAavnG,MAAM5hC,KAAMmB,aAElCjV,EAAO6uC,kBAAkB7sC,UAAUwgG,GAAUo0E,EAAUp0E,MAK/D,MAAM20E,EACFn3K,EAAO6uC,kBAAkB7sC,UAAU6iF,gBACvC7kF,EAAO6uC,kBAAkB7sC,UAAU6iF,gBACjC,WACE,OAAKqyF,GAAiCjiK,UAAU,GAQ5C28J,EAAe/pH,QAAU,IAC3B5yC,UAAU,IAAiC,KAA3BA,UAAU,GAAGstB,UACtBuO,QAAQC,UAEVomI,EAAsBzhI,MAAM5hC,KAAMmB,YAXnCA,UAAU,IACZA,UAAU,GAAGygC,MAAM,MAEd5E,QAAQC,YAYhB,SAASqmI,EAAqBp3K,GACnCuO,EAA8BvO,EAAQ,oBAAqBmN,IAEzD,GAA0B,WADfA,EAAE8xC,OACN8iC,eAGP,OAAO50E,I,sBCxsBJ,SAASwkK,EAAiB3xK,GAC/B,MAAMsuC,EAAYtuC,GAAUA,EAAOsuC,UAc7B+kI,EAAmB/kI,EAAUC,aAAa0C,aAC5CrvC,KAAK0sC,EAAUC,cACnBD,EAAUC,aAAa0C,aAAe,SAASxwC,GAC7C,OAAO4yK,EAAiB5yK,GAAGywC,MAAM/jC,GAAK2jC,QAAQE,OAf7B,SAAS7jC,GAC1B,MAAO,CACLxM,KAAM,CAACgyK,sBAAuB,mBAAmBxlK,EAAExM,OAASwM,EAAExM,KAC9D4X,QAASpL,EAAEoL,QACXqqC,WAAYz1C,EAAEy1C,WACdp4C,WACE,OAAOsJ,KAAKnT,OASqC+xK,CAAWvlK,MClB7D,SAASomK,EAAoBvzK,GAC5B,oBAAqBA,EAAOsuC,WAG5BtuC,EAAOsuC,UAAUC,eAGnBvuC,EAAOsuC,UAAUC,cACnB,oBAAqBvuC,EAAOsuC,UAAUC,eAGxCvuC,EAAOsuC,UAAUC,aAAa29B,gBAC5BlsE,EAAOsuC,UAAU49B,gBAAgBtqE,KAAK5B,EAAOsuC,aCL1C,SAAS2oI,EAAmBj3K,GACjC,MAAM4xK,EAAiBrjK,EAAoBvO,GAE3C,GAAIA,EAAOu3I,iBACJv3I,EAAO0kF,kBACV1kF,EAAO0kF,gBAAkB,SAAyBzuC,GAChD,OAAOA,IAGNj2C,EAAOypF,wBACVzpF,EAAOypF,sBAAwB,SAA+BxzC,GAC5D,OAAOA,IAMP27H,EAAe/pH,QAAU,OAAO,CAClC,MAAMwvH,EAAiBv2K,OAAOi2K,yBAC1B/2K,EAAOq8I,iBAAiBr6I,UAAW,WACvClB,OAAOC,eAAef,EAAOq8I,iBAAiBr6I,UAAW,UAAW,CAClEkzB,IAAI7zB,GACFg2K,EAAeniJ,IAAI30B,KAAKuT,KAAMzS,GAC9B,MAAMssD,EAAK,IAAI2oF,MAAM,WACrB3oF,EAAGyvB,QAAU/7E,EACbyS,KAAKoiI,cAAcvoF,MAQvB3tD,EAAOmnH,gBAAkB,SAAUnnH,EAAOmnH,aAAanlH,YACzDlB,OAAOC,eAAef,EAAOmnH,aAAanlH,UAAW,OAAQ,CAC3Df,MAQE,YAPmBoT,IAAfP,KAAKugK,QACiB,UAApBvgK,KAAKs+B,MAAMlF,KACbp5B,KAAKugK,MAAQ,IAAIr0K,EAAOs3K,cAAcxjK,MACT,UAApBA,KAAKs+B,MAAMlF,OACpBp5B,KAAKugK,MAAQ,OAGVvgK,KAAKugK,SAMdr0K,EAAOs3K,gBAAkBt3K,EAAOu3K,gBAClCv3K,EAAOu3K,cAAgBv3K,EAAOs3K,eAGhC,MAAME,EAAwBC,IAAsBz3K,EAChD4xK,EAAe/pH,SACnB7nD,EAAO6uC,kBAAoB,SAA2BvH,GAMpD,OALIA,GAAUA,EAAO0qB,aACnB1qB,EAAO0qB,WC1DN,SAA0BA,EAAYgkF,GAC3C,IAAIkB,GAAU,EAEd,OADAllF,EAAatxC,KAAKsL,MAAMtL,KAAKwL,UAAU8lC,KACrBxtB,OAAO22B,IACvB,GAAIA,IAAWA,EAAO5O,MAAQ4O,EAAOtmB,KAAM,CACzC,IAAI0X,EAAO4O,EAAO5O,MAAQ4O,EAAOtmB,IAC7BsmB,EAAOtmB,MAAQsmB,EAAO5O,MACxBh+C,EAAiB,mBAAoB,qBAEvC,MAAM4oI,EAA2B,iBAAT5qF,EAsBxB,OArBI4qF,IACF5qF,EAAO,CAACA,IAEVA,EAAOA,EAAK/nB,OAAOqQ,IAEjB,GAA6B,IAAzBA,EAAIn9B,QAAQ,SACd,OAAO,EAGT,MAAMggK,EAAY7iI,EAAI5O,WAAW,UAC5B4O,EAAI5O,WAAW,WAChB4O,EAAItN,SAAS,iBACjB,OAAImwI,IAAcxgC,GAChBA,GAAU,GACH,GAEFwgC,IAAcxgC,WAGhB/7E,EAAOtmB,IACdsmB,EAAO5O,KAAO4qF,EAAW5qF,EAAK,GAAKA,IAC1BA,EAAKliD,UD2BM+sI,CAAiB9vG,EAAO0qB,WAC1C4/G,EAAe/pH,SACjBt5C,EAAU,+BAAgC+4B,EAAO0qB,aAE5C,IAAIwlH,EAAsBlwI,IAEnCtnC,EAAO6uC,kBAAkB7sC,UAAYw1K,EAAsBx1K,UAGtD,SAAS21K,EAAiB33K,GAE3BA,EAAOmnH,gBACL,iBAAkBnnH,EAAOmnH,aAAanlH,aAC1ChC,EAAOmnH,aAAanlH,UAAUgoF,aAC1BhqF,EAAOmnH,aAAanlH,UAAU41K,UE5E/B,SAASjG,EAAiB3xK,GAC/B,MAAM4xK,EAAiBrjK,EAAoBvO,GACrCsuC,EAAYtuC,GAAUA,EAAOsuC,UAC7B+tG,EAAmBr8I,GAAUA,EAAOq8I,iBAS1C,GAPA/tG,EAAU2C,aAAe,SAASpX,EAAaxS,EAAWq8G,GAExDn1H,EAAiB,yBACb,uCACJ+/B,EAAUC,aAAa0C,aAAapX,GAAa2U,KAAKnnB,EAAWq8G,MAG7DkuC,EAAe/pH,QAAU,IAC3B,oBAAqBvZ,EAAUC,aAAaikI,2BAA4B,CAC1E,MAAMH,EAAQ,SAASxoK,EAAK2B,EAAGC,GACzBD,KAAK3B,KAAS4B,KAAK5B,KACrBA,EAAI4B,GAAK5B,EAAI2B,UACN3B,EAAI2B,KAITqsK,EAAqBvpI,EAAUC,aAAa0C,aAC9CrvC,KAAK0sC,EAAUC,cAUnB,GATAD,EAAUC,aAAa0C,aAAe,SAASxwC,GAM7C,MALiB,iBAANA,GAAqC,iBAAZA,EAAE0oC,QACpC1oC,EAAIigB,KAAKsL,MAAMtL,KAAKwL,UAAUzrB,IAC9B4xK,EAAM5xK,EAAE0oC,MAAO,kBAAmB,sBAClCkpI,EAAM5xK,EAAE0oC,MAAO,mBAAoB,wBAE9B0uI,EAAmBp3K,IAGxB47I,GAAoBA,EAAiBr6I,UAAUw0C,YAAa,CAC9D,MAAMshI,EAAoBz7B,EAAiBr6I,UAAUw0C,YACrD6lG,EAAiBr6I,UAAUw0C,YAAc,WACvC,MAAM3sC,EAAMiuK,EAAkBpiI,MAAM5hC,KAAMmB,WAG1C,OAFAo9J,EAAMxoK,EAAK,qBAAsB,mBACjCwoK,EAAMxoK,EAAK,sBAAuB,oBAC3BA,GAIX,GAAIwyI,GAAoBA,EAAiBr6I,UAAU6xC,iBAAkB,CACnE,MAAMkkI,EACJ17B,EAAiBr6I,UAAU6xC,iBAC7BwoG,EAAiBr6I,UAAU6xC,iBAAmB,SAASpzC,GAMrD,MALkB,UAAdqT,KAAKo5B,MAAiC,iBAANzsC,IAClCA,EAAIigB,KAAKsL,MAAMtL,KAAKwL,UAAUzrB,IAC9B4xK,EAAM5xK,EAAG,kBAAmB,sBAC5B4xK,EAAM5xK,EAAG,mBAAoB,wBAExBs3K,EAAuBriI,MAAM5hC,KAAM,CAACrT,OCrD5C,SAAS8yK,EAAoBvzK,EAAQg4K,GACtCh4K,EAAOsuC,UAAUC,cACnB,oBAAqBvuC,EAAOsuC,UAAUC,cAGlCvuC,EAAOsuC,UAAUC,eAGvBvuC,EAAOsuC,UAAUC,aAAa29B,gBAC5B,SAAyBryC,GACvB,IAAMA,IAAeA,EAAYkO,MAAQ,CACvC,MAAM9jB,EAAM,IAAIqvJ,aAAa,0DAK7B,OAHArvJ,EAAItjB,KAAO,gBAEXsjB,EAAIwM,KAAO,EACJqgB,QAAQE,OAAO/sB,GAOxB,OAL0B,IAAtB4V,EAAYkO,MACdlO,EAAYkO,MAAQ,CAACwD,YAAaysI,GAElCn+I,EAAYkO,MAAMwD,YAAcysI,EAE3Bh4K,EAAOsuC,UAAUC,aAAa0C,aAAapX,KCnBjD,SAASi6I,EAAY9zK,GACJ,iBAAXA,GAAuBA,EAAOi4K,eACpC,aAAcj4K,EAAOi4K,cAAcj2K,aAClC,gBAAiBhC,EAAOi4K,cAAcj2K,YAC1ClB,OAAOC,eAAef,EAAOi4K,cAAcj2K,UAAW,cAAe,CACnEf,MACE,MAAO,CAACi+C,SAAUprC,KAAKorC,aAMxB,SAAS+3H,EAAmBj3K,GACjC,MAAM4xK,EAAiBrjK,EAAoBvO,GAE3C,GAAsB,iBAAXA,IACLA,EAAO6uC,oBAAqB7uC,EAAOk4K,qBACvC,OAwBF,IAtBKl4K,EAAO6uC,mBAAqB7uC,EAAOk4K,uBAEtCl4K,EAAO6uC,kBAAoB7uC,EAAOk4K,sBAGhCtG,EAAe/pH,QAAU,IAE3B,CAAC,sBAAuB,uBAAwB,mBAC3C3kC,SAAQ,SAASs/E,GAChB,MAAMy6C,EAAej9I,EAAO6uC,kBAAkB7sC,UAAUwgG,GAClDo0E,EAAY,CAAC,CAACp0E,KAIlB,OAHAvtF,UAAU,GAAK,IAAiB,oBAAXutF,EACjBxiG,EAAO0kF,gBACP1kF,EAAOypF,uBAAuBx0E,UAAU,IACrCgoI,EAAavnG,MAAM5hC,KAAMmB,aAElCjV,EAAO6uC,kBAAkB7sC,UAAUwgG,GAAUo0E,EAAUp0E,MAM3DovE,EAAe/pH,QAAU,GAAI,CAC/B,MAAMsvH,EACFn3K,EAAO6uC,kBAAkB7sC,UAAU6iF,gBACvC7kF,EAAO6uC,kBAAkB7sC,UAAU6iF,gBACnC,WACE,OAAK5vE,UAAU,GAQXA,UAAU,IAAiC,KAA3BA,UAAU,GAAGstB,UACxBuO,QAAQC,UAEVomI,EAAsBzhI,MAAM5hC,KAAMmB,YAVnCA,UAAU,IACZA,UAAU,GAAGygC,MAAM,MAEd5E,QAAQC,YAWrB,MAAMonI,EAAmB,CACvBv7B,WAAY,cACZC,YAAa,eACbC,cAAe,iBACfC,eAAgB,kBAChBC,gBAAiB,oBAGbo7B,EAAiBp4K,EAAO6uC,kBAAkB7sC,UAAUqiH,SAC1DrkH,EAAO6uC,kBAAkB7sC,UAAUqiH,SAAW,WAC5C,MAAO+3B,EAAU24B,EAAQC,GAAS//J,UAClC,OAAOmjK,EAAe1iI,MAAM5hC,KAAM,CAACsoI,GAAY,OAC5C5tG,KAAKlU,IACJ,GAAIs3I,EAAe/pH,QAAU,KAAOktH,EAGlC,IACEz6I,EAAMpX,QAAQ4xG,IACZA,EAAK/5G,KAAOo9J,EAAiBrjD,EAAK/5G,OAAS+5G,EAAK/5G,OAElD,MAAO5N,GACP,GAAe,cAAXA,EAAExM,KACJ,MAAMwM,EAGRmtB,EAAMpX,QAAQ,CAAC4xG,EAAM10H,KACnBk6B,EAAMpF,IAAI90B,EAAGU,OAAOgoG,OAAO,GAAIgsB,EAAM,CACnC/5G,KAAMo9J,EAAiBrjD,EAAK/5G,OAAS+5G,EAAK/5G,UAKlD,OAAOuf,IAERkU,KAAKumI,EAAQC,IAIb,SAASqD,EAAmBr4K,GACjC,GAAwB,iBAAXA,IAAuBA,EAAO6uC,oBACvC7uC,EAAOmnH,aACT,OAEF,GAAInnH,EAAOmnH,cAAgB,aAAcnnH,EAAOmnH,aAAanlH,UAC3D,OAEF,MAAM4yK,EAAiB50K,EAAO6uC,kBAAkB7sC,UAAUm3I,WACtDy7B,IACF50K,EAAO6uC,kBAAkB7sC,UAAUm3I,WAAa,WAC9C,MAAMh0F,EAAUyvH,EAAel/H,MAAM5hC,KAAM,IAE3C,OADAqxC,EAAQjiC,QAAQm7D,GAAUA,EAAOi2F,IAAMxgK,MAChCqxC,IAIX,MAAMqvH,EAAex0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SACpDuiI,IACFx0K,EAAO6uC,kBAAkB7sC,UAAUiwC,SAAW,WAC5C,MAAMosC,EAASm2F,EAAa9+H,MAAM5hC,KAAMmB,WAExC,OADAopE,EAAOi2F,IAAMxgK,KACNuqE,IAGXr+E,EAAOmnH,aAAanlH,UAAUqiH,SAAW,WACvC,OAAOvwG,KAAKs+B,MAAQt+B,KAAKwgK,IAAIjwD,SAASvwG,KAAKs+B,OACvCtB,QAAQC,QAAQ,IAAI1d,MAIrB,SAASilJ,EAAqBt4K,GACnC,GAAwB,iBAAXA,IAAuBA,EAAO6uC,oBACvC7uC,EAAOmnH,aACT,OAEF,GAAInnH,EAAOmnH,cAAgB,aAAcnnH,EAAOwnH,eAAexlH,UAC7D,OAEF,MAAMuzK,EAAmBv1K,EAAO6uC,kBAAkB7sC,UAAUo3I,aACxDm8B,IACFv1K,EAAO6uC,kBAAkB7sC,UAAUo3I,aAAe,WAChD,MAAMo8B,EAAYD,EAAiB7/H,MAAM5hC,KAAM,IAE/C,OADA0hK,EAAUtyJ,QAAQg8B,GAAYA,EAASo1H,IAAMxgK,MACtC0hK,IAGXjnK,EAA8BvO,EAAQ,QAASmN,IAC7CA,EAAE+xC,SAASo1H,IAAMnnK,EAAEsoK,WACZtoK,IAETnN,EAAOwnH,eAAexlH,UAAUqiH,SAAW,WACzC,OAAOvwG,KAAKwgK,IAAIjwD,SAASvwG,KAAKs+B,QAI3B,SAASmmI,EAAiBv4K,GAC1BA,EAAO6uC,qBACR,iBAAkB7uC,EAAO6uC,kBAAkB7sC,aAG/ChC,EAAO6uC,kBAAkB7sC,UAAUk3I,aACjC,SAAsB7jH,GACpB9mB,EAAiB,eAAgB,eACjCuF,KAAKqlI,aAAaj2H,QAAQm7D,IACpBA,EAAOjsC,OAAS/c,EAAOue,YAAYrM,SAAS82C,EAAOjsC,QACrDt+B,KAAKw0F,YAAYjqB,OAMpB,SAASm6F,EAAmBx4K,GAG7BA,EAAOy4K,cAAgBz4K,EAAO04K,iBAChC14K,EAAO04K,eAAiB14K,EAAOy4K,aAI5B,SAASE,EAAmB34K,GAIjC,GAAwB,iBAAXA,IAAuBA,EAAO6uC,kBACzC,OAEF,MAAM+pI,EAAqB54K,EAAO6uC,kBAAkB7sC,UAAU48F,eAC1Dg6E,IACF54K,EAAO6uC,kBAAkB7sC,UAAU48F,eACjC,WACE9qF,KAAK+kK,sBAAwB,GAC7B,MAAMC,EAAiB7jK,UAAU,GAC3B8jK,EAAqBD,GACD,kBAAmBA,EACzCC,GAEFD,EAAen6E,cAAcz7E,QAAS81J,IACpC,GAAI,QAASA,EAAe,CAE1B,IADiB,oBACHhzJ,KAAKgzJ,EAAc54J,KAC/B,MAAM,IAAIzV,UAAU,+BAGxB,GAAI,0BAA2BquK,KACvBljF,WAAWkjF,EAAcn7E,wBAA0B,GACvD,MAAM,IAAIp8C,WAAW,2CAGzB,GAAI,iBAAkBu3H,KACdljF,WAAWkjF,EAAcC,eAAiB,GAC9C,MAAM,IAAIx3H,WAAW,kCAK7B,MAAMg4B,EAAcm/F,EAAmBljI,MAAM5hC,KAAMmB,WACnD,GAAI8jK,EAAoB,CAQtB,MAAM,OAAC16F,GAAU5E,EACXl2C,EAAS86C,EAAOkhB,gBAChB,cAAeh8D,IACnBA,EAAOi8D,UAAYs5E,EAAen6E,cAClC7qF,KAAK+kK,sBAAsB7hK,KACzBqnE,EAAOohB,cAAcl8D,GACpB2N,MAAM,UAIb,OAAOuoC,IAKR,SAASy/F,EAAgBl5K,GAI9B,GAAwB,iBAAXA,IAAuBA,EAAO6uC,kBACzC,OAEF,MAAMsqI,EAAkBn5K,EAAO6uC,kBAAkB7sC,UAAU2jF,YAC3D3lF,EAAO6uC,kBAAkB7sC,UAAU2jF,YAAc,WAC/C,OAAI7xE,KAAK+kK,uBAAyB/kK,KAAK+kK,sBAAsBxuK,OACpDymC,QAAQ+K,IAAI/nC,KAAK+kK,uBACvBrqI,KAAK,IACG2qI,EAAgBzjI,MAAM5hC,KAAMmB,YAEpCmkK,QAAQ,KACPtlK,KAAK+kK,sBAAwB,KAG1BM,EAAgBzjI,MAAM5hC,KAAMmB,YAIhC,SAASokK,EAAiBr5K,GAI/B,GAAwB,iBAAXA,IAAuBA,EAAO6uC,kBACzC,OAEF,MAAMyqI,EAAmBt5K,EAAO6uC,kBAAkB7sC,UAAU6nF,aAC5D7pF,EAAO6uC,kBAAkB7sC,UAAU6nF,aAAe,WAChD,OAAI/1E,KAAK+kK,uBAAyB/kK,KAAK+kK,sBAAsBxuK,OACpDymC,QAAQ+K,IAAI/nC,KAAK+kK,uBACvBrqI,KAAK,IACG8qI,EAAiB5jI,MAAM5hC,KAAMmB,YAErCmkK,QAAQ,KACPtlK,KAAK+kK,sBAAwB,KAG1BS,EAAiB5jI,MAAM5hC,KAAMmB,YC3RjC,SAASskK,EAAoBv5K,GAClC,GAAsB,iBAAXA,GAAwBA,EAAO6uC,kBAA1C,CAYA,GATM,oBAAqB7uC,EAAO6uC,kBAAkB7sC,YAClDhC,EAAO6uC,kBAAkB7sC,UAAUs2I,gBACjC,WAIE,OAHKxkI,KAAK0lK,gBACR1lK,KAAK0lK,cAAgB,IAEhB1lK,KAAK0lK,kBAGZ,cAAex5K,EAAO6uC,kBAAkB7sC,WAAY,CACxD,MAAMy3K,EAAYz5K,EAAO6uC,kBAAkB7sC,UAAUiwC,SACrDjyC,EAAO6uC,kBAAkB7sC,UAAU88F,UAAY,SAAmBzpE,GAC3DvhB,KAAK0lK,gBACR1lK,KAAK0lK,cAAgB,IAElB1lK,KAAK0lK,cAAcjyI,SAASlS,IAC/BvhB,KAAK0lK,cAAcxiK,KAAKqe,GAI1BA,EAAOqX,iBAAiBxpB,QAAQkvB,GAASqnI,EAAUl5K,KAAKuT,KAAMs+B,EAC5D/c,IACFA,EAAOuX,iBAAiB1pB,QAAQkvB,GAASqnI,EAAUl5K,KAAKuT,KAAMs+B,EAC5D/c,KAGJr1B,EAAO6uC,kBAAkB7sC,UAAUiwC,SACjC,SAAkBG,GAChB,MAAM/c,EAASpgB,UAAU,GAQzB,OAPIogB,IACGvhB,KAAK0lK,cAEE1lK,KAAK0lK,cAAcjyI,SAASlS,IACtCvhB,KAAK0lK,cAAcxiK,KAAKqe,GAFxBvhB,KAAK0lK,cAAgB,CAACnkJ,IAKnBokJ,EAAU/jI,MAAM5hC,KAAMmB,YAG7B,iBAAkBjV,EAAO6uC,kBAAkB7sC,YAC/ChC,EAAO6uC,kBAAkB7sC,UAAUk3I,aACjC,SAAsB7jH,GACfvhB,KAAK0lK,gBACR1lK,KAAK0lK,cAAgB,IAEvB,MAAM/0I,EAAQ3wB,KAAK0lK,cAAc9hK,QAAQ2d,GACzC,IAAe,IAAXoP,EACF,OAEF3wB,KAAK0lK,cAAczhK,OAAO0sB,EAAO,GACjC,MAAM0W,EAAS9lB,EAAOue,YACtB9/B,KAAKqlI,aAAaj2H,QAAQm7D,IACpBljC,EAAO5T,SAAS82C,EAAOjsC,QACzBt+B,KAAKw0F,YAAYjqB,QAOtB,SAASq7F,EAAqB15K,GACnC,GAAsB,iBAAXA,GAAwBA,EAAO6uC,oBAGpC,qBAAsB7uC,EAAO6uC,kBAAkB7sC,YACnDhC,EAAO6uC,kBAAkB7sC,UAAUu2I,iBACjC,WACE,OAAOzkI,KAAK6lK,eAAiB7lK,KAAK6lK,eAAiB,OAGnD,gBAAiB35K,EAAO6uC,kBAAkB7sC,YAAY,CAC1DlB,OAAOC,eAAef,EAAO6uC,kBAAkB7sC,UAAW,cAAe,CACvEf,MACE,OAAO6S,KAAK8lK,cAEd1kJ,IAAIk6B,GACEt7C,KAAK8lK,eACP9lK,KAAKivC,oBAAoB,YAAajvC,KAAK8lK,cAC3C9lK,KAAKivC,oBAAoB,QAASjvC,KAAK+lK,mBAEzC/lK,KAAKq8B,iBAAiB,YAAar8B,KAAK8lK,aAAexqH,GACvDt7C,KAAKq8B,iBAAiB,QAASr8B,KAAK+lK,iBAAoB1sK,IACtDA,EAAEykC,QAAQ1uB,QAAQmS,IAIhB,GAHKvhB,KAAK6lK,iBACR7lK,KAAK6lK,eAAiB,IAEpB7lK,KAAK6lK,eAAepyI,SAASlS,GAC/B,OAEFvhB,KAAK6lK,eAAe3iK,KAAKqe,GACzB,MAAM8F,EAAQ,IAAIm7G,MAAM,aACxBn7G,EAAM9F,OAASA,EACfvhB,KAAKoiI,cAAc/6G,UAK3B,MAAM64I,EACJh0K,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACrC5pF,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACjC,WACE,MAAMxwB,EAAKtlD,KAiBX,OAhBKA,KAAK+lK,kBACR/lK,KAAKq8B,iBAAiB,QAASr8B,KAAK+lK,iBAAmB,SAAS1sK,GAC9DA,EAAEykC,QAAQ1uB,QAAQmS,IAIhB,GAHK+jC,EAAGugH,iBACNvgH,EAAGugH,eAAiB,IAElBvgH,EAAGugH,eAAejiK,QAAQ2d,IAAW,EACvC,OAEF+jC,EAAGugH,eAAe3iK,KAAKqe,GACvB,MAAM8F,EAAQ,IAAIm7G,MAAM,aACxBn7G,EAAM9F,OAASA,EACf+jC,EAAG88E,cAAc/6G,OAIhB64I,EAAyBt+H,MAAM0jB,EAAInkD,aAK3C,SAAS6kK,EAAiB95K,GAC/B,GAAsB,iBAAXA,IAAwBA,EAAO6uC,kBACxC,OAEF,MAAM7sC,EAAYhC,EAAO6uC,kBAAkB7sC,UACrCm3K,EAAkBn3K,EAAU2jF,YAC5B2zF,EAAmBt3K,EAAU6nF,aAC7BhE,EAAsB7jF,EAAU6jF,oBAChC+D,EAAuB5nF,EAAU4nF,qBACjC/E,EAAkB7iF,EAAU6iF,gBAElC7iF,EAAU2jF,YACR,SAAqByjB,EAAiB6+B,GACpC,MAAMhtH,EAAWhG,UAAU5K,QAAU,EAAK4K,UAAU,GAAKA,UAAU,GAC7D4gC,EAAUsjI,EAAgBzjI,MAAM5hC,KAAM,CAACmH,IAC7C,OAAKgtH,GAGLpyF,EAAQrH,KAAK46D,EAAiB6+B,GACvBn3F,QAAQC,WAHN8E,GAMb7zC,EAAU6nF,aACR,SAAsBuf,EAAiB6+B,GACrC,MAAMhtH,EAAWhG,UAAU5K,QAAU,EAAK4K,UAAU,GAAKA,UAAU,GAC7D4gC,EAAUyjI,EAAiB5jI,MAAM5hC,KAAM,CAACmH,IAC9C,OAAKgtH,GAGLpyF,EAAQrH,KAAK46D,EAAiB6+B,GACvBn3F,QAAQC,WAHN8E,GAMb,IAAIkkI,EAAe,SAASnjG,EAAawyB,EAAiB6+B,GACxD,MAAMpyF,EAAUgwC,EAAoBnwC,MAAM5hC,KAAM,CAAC8iE,IACjD,OAAKqxD,GAGLpyF,EAAQrH,KAAK46D,EAAiB6+B,GACvBn3F,QAAQC,WAHN8E,GAKX7zC,EAAU6jF,oBAAsBk0F,EAEhCA,EAAe,SAASnjG,EAAawyB,EAAiB6+B,GACpD,MAAMpyF,EAAU+zC,EAAqBl0C,MAAM5hC,KAAM,CAAC8iE,IAClD,OAAKqxD,GAGLpyF,EAAQrH,KAAK46D,EAAiB6+B,GACvBn3F,QAAQC,WAHN8E,GAKX7zC,EAAU4nF,qBAAuBmwF,EAEjCA,EAAe,SAASx3I,EAAW6mE,EAAiB6+B,GAClD,MAAMpyF,EAAUgvC,EAAgBnvC,MAAM5hC,KAAM,CAACyuB,IAC7C,OAAK0lG,GAGLpyF,EAAQrH,KAAK46D,EAAiB6+B,GACvBn3F,QAAQC,WAHN8E,GAKX7zC,EAAU6iF,gBAAkBk1F,EAGvB,SAASpI,EAAiB3xK,GAC/B,MAAMsuC,EAAYtuC,GAAUA,EAAOsuC,UAEnC,GAAIA,EAAUC,cAAgBD,EAAUC,aAAa0C,aAAc,CAEjE,MAAM1C,EAAeD,EAAUC,aACzByrI,EAAgBzrI,EAAa0C,aAAarvC,KAAK2sC,GACrDD,EAAUC,aAAa0C,aAAgBpX,GAC9BmgJ,EAAcC,EAAgBpgJ,KAIpCyU,EAAU2C,cAAgB3C,EAAUC,cACvCD,EAAUC,aAAa0C,eACvB3C,EAAU2C,aAAe,SAAsBpX,EAAa20G,EAAI0rC,GAC9D5rI,EAAUC,aAAa0C,aAAapX,GACnC2U,KAAKggG,EAAI0rC,IACVt4K,KAAK0sC,IAIJ,SAAS2rI,EAAgBpgJ,GAC9B,OAAIA,QAAqCxlB,IAAtBwlB,EAAYkO,MACtBjnC,OAAOgoG,OAAO,GACnBjvE,EACA,CAACkO,MAAOx5B,EAAoBsrB,EAAYkO,SAIrClO,EAGF,SAASsgJ,GAAqBn6K,GAEnC,MAAMo6K,EAAqBp6K,EAAO6uC,kBAClC7uC,EAAO6uC,kBACL,SAA2BwrI,EAAUhqI,GACnC,GAAIgqI,GAAYA,EAASroH,WAAY,CACnC,MAAMsoH,EAAgB,GACtB,IAAK,IAAIl6K,EAAI,EAAGA,EAAIi6K,EAASroH,WAAW3nD,OAAQjK,IAAK,CACnD,IAAI+6D,EAASk/G,EAASroH,WAAW5xD,IAC5B+6D,EAAOl5D,eAAe,SACvBk5D,EAAOl5D,eAAe,QACxBsM,EAAiB,mBAAoB,qBACrC4sD,EAASz6C,KAAKsL,MAAMtL,KAAKwL,UAAUivC,IACnCA,EAAO5O,KAAO4O,EAAOtmB,WACdsmB,EAAOtmB,IACdylI,EAActjK,KAAKmkD,IAEnBm/G,EAActjK,KAAKqjK,EAASroH,WAAW5xD,IAG3Ci6K,EAASroH,WAAasoH,EAExB,OAAO,IAAIF,EAAmBC,EAAUhqI,IAE5CrwC,EAAO6uC,kBAAkB7sC,UAAYo4K,EAAmBp4K,UAEpD,wBAAyBhC,EAAO6uC,mBAClC/tC,OAAOC,eAAef,EAAO6uC,kBAAmB,sBAAuB,CACrE5tC,IAAG,IACMm5K,EAAmBG,sBAM3B,SAASC,GAA0Bx6K,GAElB,iBAAXA,GAAuBA,EAAOi4K,eACrC,aAAcj4K,EAAOi4K,cAAcj2K,aACjC,gBAAiBhC,EAAOi4K,cAAcj2K,YAC1ClB,OAAOC,eAAef,EAAOi4K,cAAcj2K,UAAW,cAAe,CACnEf,MACE,MAAO,CAACi+C,SAAUprC,KAAKorC,aAMxB,SAASu7H,GAAsBz6K,GACpC,MAAMm5K,EAAkBn5K,EAAO6uC,kBAAkB7sC,UAAU2jF,YAC3D3lF,EAAO6uC,kBAAkB7sC,UAAU2jF,YACjC,SAAqBm2D,GACnB,GAAIA,EAAc,MACgC,IAArCA,EAAapT,sBAEtBoT,EAAapT,sBACToT,EAAapT,qBAEnB,MAAMgyC,EAAmB5mK,KAAK+qF,kBAAkB95D,KAAK00C,GACf,UAApCA,EAAYv6B,SAAS9M,MAAMlF,OACY,IAArC4uG,EAAapT,qBAAiCgyC,EACb,aAA/BA,EAAiBj3I,UACfi3I,EAAiBC,aACnBD,EAAiBC,aAAa,YAE9BD,EAAiBj3I,UAAY,WAES,aAA/Bi3I,EAAiBj3I,YACtBi3I,EAAiBC,aACnBD,EAAiBC,aAAa,YAE9BD,EAAiBj3I,UAAY,aAGa,IAArCq4G,EAAapT,qBACnBgyC,GACH5mK,KAAK8qF,eAAe,cAG0B,IAArCk9C,EAAanT,sBAEtBmT,EAAanT,sBACTmT,EAAanT,qBAEnB,MAAMiyC,EAAmB9mK,KAAK+qF,kBAAkB95D,KAAK00C,GACf,UAApCA,EAAYv6B,SAAS9M,MAAMlF,OACY,IAArC4uG,EAAanT,qBAAiCiyC,EACb,aAA/BA,EAAiBn3I,UACfm3I,EAAiBD,aACnBC,EAAiBD,aAAa,YAE9BC,EAAiBn3I,UAAY,WAES,aAA/Bm3I,EAAiBn3I,YACtBm3I,EAAiBD,aACnBC,EAAiBD,aAAa,YAE9BC,EAAiBn3I,UAAY,aAGa,IAArCq4G,EAAanT,qBACnBiyC,GACH9mK,KAAK8qF,eAAe,SAGxB,OAAOu6E,EAAgBzjI,MAAM5hC,KAAMmB,Y,wBCrUlC,SAAS4lK,GAAoB76K,GAGlC,IAAKA,EAAO0kF,iBAAoB1kF,EAAO0kF,iBAAmB,eACtD1kF,EAAO0kF,gBAAgB1iF,UACzB,OAGF,MAAM84K,EAAwB96K,EAAO0kF,gBACrC1kF,EAAO0kF,gBAAkB,SAAyBzuC,GAQhD,GANoB,iBAATA,GAAqBA,EAAK1T,WACA,IAAjC0T,EAAK1T,UAAU7qB,QAAQ,SACzBu+B,EAAOv1B,KAAKsL,MAAMtL,KAAKwL,UAAU+pB,KAC5B1T,UAAY0T,EAAK1T,UAAUrZ,OAAO,IAGrC+sB,EAAK1T,WAAa0T,EAAK1T,UAAUl4B,OAAQ,CAE3C,MAAM0wK,EAAkB,IAAID,EAAsB7kI,GAC5C+kI,EAAkBroG,KAASY,eAAet9B,EAAK1T,WAC/C04I,EAAqBn6K,OAAOgoG,OAAOiyE,EACrCC,GAWJ,OARAC,EAAmBrhC,OAAS,WAC1B,MAAO,CACLr3G,UAAW04I,EAAmB14I,UAC9ByhD,OAAQi3F,EAAmBj3F,OAC3BT,cAAe03F,EAAmB13F,cAClC3P,iBAAkBqnG,EAAmBrnG,mBAGlCqnG,EAET,OAAO,IAAIH,EAAsB7kI,IAEnCj2C,EAAO0kF,gBAAgB1iF,UAAY84K,EAAsB94K,UAIzDuM,EAA8BvO,EAAQ,eAAgBmN,IAChDA,EAAEo1B,WACJzhC,OAAOC,eAAeoM,EAAG,YAAa,CACpC9L,MAAO,IAAIrB,EAAO0kF,gBAAgBv3E,EAAEo1B,WACpC6rF,SAAU,UAGPjhH,IAIJ,SAAS+tK,GAAmBl7K,GACjC,IAAKA,EAAO6uC,kBACV,OAEF,MAAM+iI,EAAiBrjK,EAAoBvO,GAErC,SAAUA,EAAO6uC,kBAAkB7sC,WACvClB,OAAOC,eAAef,EAAO6uC,kBAAkB7sC,UAAW,OAAQ,CAChEf,MACE,YAA6B,IAAf6S,KAAKqnK,MAAwB,KAAOrnK,KAAKqnK,SAK7D,MAAMC,EAAoB,SAASxkG,GACjC,IAAKA,IAAgBA,EAAY9wC,IAC/B,OAAO,EAET,MAAMqtC,EAAWR,KAASK,cAAc4D,EAAY9wC,KAEpD,OADAqtC,EAAS3xC,QACF2xC,EAASsjB,KAAKrhB,IACnB,MAAM7vC,EAAQotC,KAASrxC,WAAW8zC,GAClC,OAAO7vC,GAAwB,gBAAfA,EAAM2H,OACqB,IAApC3H,EAAM/oB,SAAS9E,QAAQ,WAI5B2jK,EAA0B,SAASzkG,GAEvC,MAAMxvD,EAAQwvD,EAAY9wC,IAAI1e,MAAM,mCACpC,GAAc,OAAVA,GAAkBA,EAAM/c,OAAS,EACnC,OAAQ,EAEV,MAAMw9C,EAAUz7B,SAAShF,EAAM,GAAI,IAEnC,OAAOygC,GAAYA,GAAW,EAAIA,GAG9ByzH,EAA2B,SAASC,GAKxC,IAAIC,EAAwB,MAwB5B,MAvB+B,YAA3B5J,EAAeh+I,UAKb4nJ,EAJA5J,EAAe/pH,QAAU,IACF,IAArB0zH,EAGsB,MAIA,WAEjB3J,EAAe/pH,QAAU,GAML,KAA3B+pH,EAAe/pH,QAAiB,MAAQ,MAGlB,YAGrB2zH,GAGHC,EAAoB,SAAS7kG,EAAa2kG,GAG9C,IAAIziG,EAAiB,MAKU,YAA3B84F,EAAeh+I,SACgB,KAA3Bg+I,EAAe/pH,UACrBixB,EAAiB,OAGnB,MAAM1xD,EAAQurD,KAASU,YAAYuD,EAAY9wC,IAC7C,uBAUF,OATI1e,EAAM/c,OAAS,EACjByuE,EAAiB1sD,SAAShF,EAAM,GAAG8B,OAAO,IAAK,IACX,YAA3B0oJ,EAAeh+I,UACO,IAArB2nJ,IAIVziG,EAAiB,YAEZA,GAGHk7F,EACFh0K,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACvC5pF,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACjC,WAKE,GAJA91E,KAAKqnK,MAAQ,KAIkB,WAA3BvJ,EAAeh+I,SAAwBg+I,EAAe/pH,SAAW,GAAI,CACvE,MAAM,aAACxN,GAAgBvmC,KAAKukI,mBACP,WAAjBh+F,GACFv5C,OAAOC,eAAe+S,KAAM,OAAQ,CAClC7S,MACE,YAA6B,IAAf6S,KAAKqnK,MAAwB,KAAOrnK,KAAKqnK,OAEzDn6K,YAAY,EACZmtH,cAAc,IAKpB,GAAIitD,EAAkBnmK,UAAU,IAAK,CAEnC,MAAMivB,EAAYm3I,EAAwBpmK,UAAU,IAG9CymK,EAAaJ,EAAyBp3I,GAGtCy3I,EAAYF,EAAkBxmK,UAAU,GAAIivB,GAGlD,IAAI40C,EAEFA,EADiB,IAAf4iG,GAAkC,IAAdC,EACLtuJ,OAAOuuJ,kBACA,IAAfF,GAAkC,IAAdC,EACZz8J,KAAKgpB,IAAIwzI,EAAYC,GAErBz8J,KAAKqP,IAAImtJ,EAAYC,GAKxC,MAAMh1H,EAAO,GACb7lD,OAAOC,eAAe4lD,EAAM,iBAAkB,CAC5C1lD,IAAG,IACM63E,IAGXhlE,KAAKqnK,MAAQx0H,EAGf,OAAOqtH,EAAyBt+H,MAAM5hC,KAAMmB,YAI3C,SAAS4mK,GAAuB77K,GACrC,IAAMA,EAAO6uC,qBACT,sBAAuB7uC,EAAO6uC,kBAAkB7sC,WAClD,OAOF,SAAS85K,EAAWC,EAAI3iH,GACtB,MAAM4iH,EAAsBD,EAAG56J,KAC/B46J,EAAG56J,KAAO,WACR,MAAM1T,EAAOwH,UAAU,GACjB5K,EAASoD,EAAKpD,QAAUoD,EAAKspB,MAAQtpB,EAAKs1D,WAChD,GAAsB,SAAlBg5G,EAAGjvJ,YACHssC,EAAGzS,MAAQt8C,EAAS+uD,EAAGzS,KAAKmyB,eAC9B,MAAM,IAAInuE,UAAU,4CAClByuD,EAAGzS,KAAKmyB,eAAiB,WAE7B,OAAOkjG,EAAoBtmI,MAAMqmI,EAAI9mK,YAGzC,MAAMgnK,EACJj8K,EAAO6uC,kBAAkB7sC,UAAU6wI,kBACrC7yI,EAAO6uC,kBAAkB7sC,UAAU6wI,kBACjC,WACE,MAAMqpC,EAAcD,EAAsBvmI,MAAM5hC,KAAMmB,WAEtD,OADA6mK,EAAWI,EAAapoK,MACjBooK,GAEX3tK,EAA8BvO,EAAQ,cAAemN,IACnD2uK,EAAW3uK,EAAE6mI,QAAS7mI,EAAE8xC,QACjB9xC,IAYJ,SAASgvK,GAAoBn8K,GAClC,IAAKA,EAAO6uC,mBACR,oBAAqB7uC,EAAO6uC,kBAAkB7sC,UAChD,OAEF,MAAMua,EAAQvc,EAAO6uC,kBAAkB7sC,UACvClB,OAAOC,eAAewb,EAAO,kBAAmB,CAC9Ctb,MACE,MAAO,CACLspI,UAAW,YACXgR,SAAU,cACVznI,KAAKouE,qBAAuBpuE,KAAKouE,oBAErClhF,YAAY,EACZmtH,cAAc,IAEhBrtH,OAAOC,eAAewb,EAAO,0BAA2B,CACtDtb,MACE,OAAO6S,KAAKsoK,0BAA4B,MAE1ClnJ,IAAIs5G,GACE16H,KAAKsoK,2BACPtoK,KAAKivC,oBAAoB,wBACrBjvC,KAAKsoK,iCACFtoK,KAAKsoK,0BAEV5tC,GACF16H,KAAKq8B,iBAAiB,wBAClBr8B,KAAKsoK,yBAA2B5tC,IAGxCxtI,YAAY,EACZmtH,cAAc,IAGhB,CAAC,sBAAuB,wBAAwBjrG,QAASs/E,IACvD,MAAM65E,EAAa9/J,EAAMimF,GACzBjmF,EAAMimF,GAAU,WAcd,OAbK1uF,KAAKwoK,6BACRxoK,KAAKwoK,2BAA6BnvK,IAChC,MAAMisD,EAAKjsD,EAAE8xC,OACb,GAAIma,EAAGmjH,uBAAyBnjH,EAAG4oB,gBAAiB,CAClD5oB,EAAGmjH,qBAAuBnjH,EAAG4oB,gBAC7B,MAAMw6F,EAAW,IAAIlmC,MAAM,wBAAyBnpI,GACpDisD,EAAG88E,cAAcsmC,GAEnB,OAAOrvK,GAET2G,KAAKq8B,iBAAiB,2BACpBr8B,KAAKwoK,6BAEFD,EAAW3mI,MAAM5hC,KAAMmB,cAK7B,SAASwnK,GAAuBz8K,GAErC,IAAKA,EAAO6uC,kBACV,OAEF,MAAM+iI,EAAiBrjK,EAAoBvO,GAC3C,GAA+B,WAA3B4xK,EAAeh+I,SAAwBg+I,EAAe/pH,SAAW,GACnE,OAEF,MAAM60H,EAAY18K,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACrD5pF,EAAO6uC,kBAAkB7sC,UAAU4nF,qBACnC,SAA8BzmD,GAM5B,OALIA,GAAQA,EAAK2C,MAAuD,IAAhD3C,EAAK2C,IAAIpuB,QAAQ,4BACvCyrB,EAAK2C,IAAM3C,EAAK2C,IAAIjvB,MAAM,MAAM2tB,OAAQlsB,GACf,yBAAhBA,EAAKq6C,QACX17C,KAAK,OAEHylK,EAAUhnI,MAAM5hC,KAAMmB,YCnUjC,MAAM0nK,GCIC,UAAwB,OAAC38K,GAAU,GAAIib,EAAU,CACtD2hK,YAAY,EACZC,aAAa,EACbC,UAAU,EACVC,YAAY,IAGZ,MAAM5W,EAAU53J,EACVqjK,EAAiBrjK,EAAoBvO,GAErC28K,EAAU,CACd/K,iBACAoL,aACAtN,eAAgBnhK,EAChB6hK,WAAY7hK,EACZ+hK,gBAAiB/hK,GAInB,OAAQqjK,EAAeh+I,SACrB,IAAK,SACH,IAAKqpJ,IAAeA,IACfhiK,EAAQ2hK,WAEX,OADAzW,EAAQ,wDACDwW,EAETxW,EAAQ,+BAERwW,EAAQO,YAAcD,EAEtBA,EAA4Bj9K,GAC5Bi9K,EAA2Bj9K,GAC3Bi9K,EAA8Bj9K,GAC9Bi9K,EAAuBj9K,GACvBi9K,EAAmCj9K,GACnCi9K,EAAkCj9K,GAClCi9K,EAAwBj9K,GACxBi9K,EAAsCj9K,GACtCi9K,EAAgCj9K,GAEhCg9K,GAA+Bh9K,GAC/Bg9K,GAA+Bh9K,GAC/Bg9K,GAA8Bh9K,GAC9Bg9K,GAAkCh9K,GAClCg9K,GAAkCh9K,GAClC,MACF,IAAK,UACH,IAAKm9K,IAAgBA,IAChBliK,EAAQ4hK,YAEX,OADA1W,EAAQ,yDACDwW,EAETxW,EAAQ,gCAERwW,EAAQO,YAAcC,EAEtBA,EAA6Bn9K,GAC7Bm9K,EAA+Bn9K,GAC/Bm9K,EAAwBn9K,GACxBm9K,EAA6Bn9K,GAC7Bm9K,EAA+Bn9K,GAC/Bm9K,EAAiCn9K,GACjCm9K,EAA+Bn9K,GAC/Bm9K,EAA+Bn9K,GAC/Bm9K,EAA4Bn9K,GAC5Bm9K,EAA6Bn9K,GAE7Bg9K,GAA+Bh9K,GAC/Bg9K,GAA+Bh9K,GAC/Bg9K,GAA8Bh9K,GAC9Bg9K,GAAkCh9K,GAClC,MACF,IAAK,OACH,IAAKo9K,IAAaA,IAAgCniK,EAAQ6hK,SAExD,OADA3W,EAAQ,yDACDwW,EAETxW,EAAQ,6BAERwW,EAAQO,YAAcE,EAEtBA,EAA0Bp9K,GAC1Bo9K,EAA6Bp9K,GAC7Bo9K,EAA4Bp9K,GAC5Bo9K,EAA0Bp9K,GAI1Bg9K,GAA8Bh9K,GAC9Bg9K,GAAkCh9K,GAClC,MACF,IAAK,SACH,IAAKq9K,IAAepiK,EAAQ8hK,WAE1B,OADA5W,EAAQ,wDACDwW,EAETxW,EAAQ,+BAERwW,EAAQO,YAAcG,EAEtBA,GAAgCr9K,GAChCq9K,GAAiCr9K,GACjCq9K,EAA4Br9K,GAC5Bq9K,EAA+Br9K,GAC/Bq9K,EAAgCr9K,GAChCq9K,GAAqCr9K,GACrCq9K,EAA4Br9K,GAE5Bg9K,GAA+Bh9K,GAC/Bg9K,GAA8Bh9K,GAC9Bg9K,GAAkCh9K,GAClCg9K,GAAkCh9K,GAClC,MACF,QACEmmK,EAAQ,wBAIZ,OAAOwW,ED1HOW,CAAe,CAACt9K,gBACjB28K","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 = 147);\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 /**\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 PARTCIPANT_FEATURES_CHANGED: 'xmpp.partcipant_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 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, \"&\");\n text = text.replace(/</g, \"<\");\n text = text.replace(/>/g, \">\");\n text = text.replace(/'/g, \"'\");\n text = text.replace(/\"/g, \""\");\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(/\\&/g, \"&\");\n text = text.replace(/</g, \"<\");\n text = text.replace(/>/g, \">\");\n text = text.replace(/'/g, \"'\");\n text = text.replace(/"/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","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 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 GRANTED_PERMISSIONS: 'rtc.granted_permissions',\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","/**\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","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 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.GRANTED_PERMISSIONS, 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(\n 'list of media devices has changed:',\n 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, devicesReceived);\n\n eventEmitter.emit(RTCEvents.DEVICE_LIST_CHANGED, devicesReceived);\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 } 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 * Represents a default implementation of setting a <tt>MediaStream</tt> as the\n * source of a video element that tries to be browser-agnostic through feature\n * checking. Note though that it was not completely clear from the predating\n * browser-specific implementations what "videoSrc" was because one\n * implementation of {@link RTCUtils#getVideoSrc} would return\n * <tt>MediaStream</tt> (e.g. Firefox), another a <tt>string</tt> representation\n * of the <tt>URL</tt> of the <tt>MediaStream</tt> (e.g. Chrome) and the return\n * value was only used by {@link RTCUIHelper#getVideoId} which itself did not\n * appear to be used anywhere. Generally, the implementation will try to follow\n * the related standards i.e. work with the <tt>srcObject</tt> and <tt>src</tt>\n * properties of the specified <tt>element</tt> taking into account vender\n * prefixes.\n *\n * @param element the element whose video source/src is to be set to the\n * specified <tt>stream</tt>\n * @param {MediaStream} stream the <tt>MediaStream</tt> to set as the video\n * source/src of <tt>element</tt>\n */\nfunction defaultSetVideoSrc(element, stream) {\n // srcObject\n let srcObjectPropertyName = 'srcObject';\n\n if (!(srcObjectPropertyName in element)) {\n srcObjectPropertyName = 'mozSrcObject';\n if (!(srcObjectPropertyName in element)) {\n srcObjectPropertyName = null;\n }\n }\n if (srcObjectPropertyName) {\n element[srcObjectPropertyName] = stream;\n\n return;\n }\n\n // src\n let src;\n\n if (stream) {\n src = stream.jitsiObjectURL;\n\n // Save the created URL for stream so we can reuse it and not keep\n // creating URLs.\n if (!src) {\n stream.jitsiObjectURL = src = URL.createObjectURL(stream);\n }\n }\n element.src = src || '';\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.isChromiumBased() // this is chrome < 61\n || browser.isReactNative()) {\n\n this.RTCPeerConnectionType = RTCPeerConnection;\n\n this.attachMediaStream\n = wrapAttachMediaStream((element, stream) => {\n defaultSetVideoSrc(element, stream);\n\n return element;\n });\n\n this.getStreamID = function({ id }) {\n // A. MediaStreams from FF endpoints have the characters '{' and\n // '}' that make jQuery choke.\n // B. 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\n if (!MediaStream.prototype.getVideoTracks) {\n MediaStream.prototype.getVideoTracks = function() {\n return this.videoTracks;\n };\n }\n if (!MediaStream.prototype.getAudioTracks) {\n MediaStream.prototype.getAudioTracks = function() {\n return this.audioTracks;\n };\n }\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.splice(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 * @returns {Promise} Returns a media stream on success or a JitsiTrackError\n * on failure.\n **/\n getUserMediaWithConstraints(um, options = {}) {\n const constraints = getConstraints(um, options);\n\n logger.info('Get media constraints', constraints);\n\n return new Promise((resolve, reject) => {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(stream => {\n logger.log('onUserMediaSuccess');\n updateGrantedPermissions(um, stream);\n resolve(stream);\n })\n .catch(error => {\n logger.warn('Failed to get access to local media. '\n + ` ${error} ${constraints} `);\n updateGrantedPermissions(um, undefined);\n reject(new JitsiTrackError(error, constraints, um));\n });\n });\n }\n\n /**\n * Acquires a media stream via getUserMedia that\n * matches the given constraints\n *\n * @param {array} umDevices which devices to acquire (e.g. audio, video)\n * @param {Object} constraints - Stream specifications to use.\n * @returns {Promise}\n */\n _newGetUserMediaWithConstraints(umDevices, constraints = {}) {\n return new Promise((resolve, reject) => {\n navigator.mediaDevices.getUserMedia(constraints)\n .then(stream => {\n logger.log('onUserMediaSuccess');\n updateGrantedPermissions(umDevices, stream);\n resolve(stream);\n })\n .catch(error => {\n logger.warn('Failed to get access to local media. '\n + ` ${error} ${constraints} `);\n updateGrantedPermissions(umDevices, undefined);\n reject(new JitsiTrackError(error, constraints, umDevices));\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#_newGetUserMediaWithConstraints.\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() || !browser.supportsVideo()) {\n return Promise.reject(\n 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 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 = options.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 } = options;\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(options);\n\n const constraints = {\n video: {\n ...gumOptions,\n deviceId: matchingDevice.deviceId\n }\n };\n\n return this._newGetUserMediaWithConstraints(\n requestedDevices, constraints)\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 = options.devices || [ 'audio', 'video' ];\n const requestedCaptureDevices = umDevices.filter(device =>\n device === 'audio'\n || (device === 'video' && browser.supportsVideo()));\n\n if (!requestedCaptureDevices.length) {\n return Promise.resolve();\n }\n\n const constraints = newGetConstraints(\n requestedCaptureDevices, options);\n\n logger.info('Got media constraints: ', constraints);\n\n return this._newGetUserMediaWithConstraints(\n requestedCaptureDevices, constraints);\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: options.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: options.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 // if we have done createObjectURL, lets clean it\n const url = mediaStream.jitsiObjectURL;\n\n if (url) {\n delete mediaStream.jitsiObjectURL;\n URL.revokeObjectURL(url);\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 ID of currently pinned participant or <tt>null</tt> if\n * no user is pinned.\n * @type {string|null}\n * @private\n */\n this._pinnedEndpoint = null;\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.sendPinnedEndpointMessage(\n this._pinnedEndpoint);\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 + `pinned(${this._pinnedEndpoint})`\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(`Received remote max frame height of ${senderVideoConstraints} on the bridge channel`);\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 * Elects the participant with the given id to be the pinned participant in\n * order to always receive video for this participant (even when last n is\n * enabled).\n * @param {stirng} id The user id.\n * @throws NetworkError or InvalidStateError or Error if the operation\n * fails.\n */\n pinEndpoint(id) {\n // Cache the value if channel is missing, till we open it.\n this._pinnedEndpoint = id;\n if (this._channel && this._channel.isOpen()) {\n this._channel.sendPinnedEndpointMessage(id);\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 // 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 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","/* 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 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 * 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","/**\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 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;\nmodule.exports.once = once;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nfunction checkListener(listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n}\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction _getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return _getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n checkListener(listener);\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = _getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n if (arguments.length === 0)\n return this.listener.call(this.target);\n return this.listener.apply(this.target, arguments);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n checkListener(listener);\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n checkListener(listener);\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n checkListener(listener);\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\nfunction once(emitter, name) {\n return new Promise(function (resolve, reject) {\n function eventListener() {\n if (errorListener !== undefined) {\n emitter.removeListener('error', errorListener);\n }\n resolve([].slice.call(arguments));\n };\n var errorListener;\n\n // Adding an error listener is not optional because\n // if an error is thrown on an event emitter we cannot\n // guarantee that the actual event we are waiting will\n // be fired. The result could be a silent way to create\n // memory or file descriptor leaks, which is something\n // we should avoid.\n if (name !== 'error') {\n errorListener = function errorListener(err) {\n emitter.removeListener(name, eventListener);\n reject(err);\n };\n\n emitter.once('error', errorListener);\n }\n\n emitter.once(name, eventListener);\n });\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.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","import EventEmitter from 'events';\n\n/**\n * The class implements basic event operations - add/remove listener.\n * NOTE: The purpose of the class is to be extended in order to add\n * this functionality to other classes.\n */\nexport default class Listenable {\n /**\n * Creates new instance.\n * @param {EventEmitter} eventEmitter\n * @constructor\n */\n constructor(eventEmitter = new EventEmitter()) {\n this.eventEmitter = eventEmitter;\n\n // aliases for addListener/removeListener\n this.addEventListener = this.on = this.addListener;\n this.removeEventListener = this.off = this.removeListener;\n }\n\n /**\n * Adds new listener.\n * @param {String} eventName the name of the event\n * @param {Function} listener the listener.\n * @returns {Function} - The unsubscribe function.\n */\n addListener(eventName, listener) {\n this.eventEmitter.addListener(eventName, listener);\n\n return () => this.removeEventListener(eventName, listener);\n }\n\n /**\n * Removes listener.\n * @param {String} eventName the name of the event that triggers the\n * listener\n * @param {Function} listener the listener.\n */\n removeListener(eventName, listener) {\n this.eventEmitter.removeListener(eventName, listener);\n }\n}\n","/* global $ */\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","// 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 * 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","/**\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","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 * 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 $ */\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.enableWebsocketResume - True to enable stream resumption.\n * @param {number} [options.websocketKeepAlive] - See {@link XmppConnection} constructor.\n * @returns {XmppConnection}\n */\nfunction createConnection({ enableWebsocketResume, serviceUrl = '/http-bind', token, websocketKeepAlive }) {\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 });\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:stun.l.google.com:19302' },\n { urls: 'stun:stun1.l.google.com:19302' },\n { urls: 'stun:stun2.l.google.com:19302' }\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 *\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 {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 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 });\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 because of https://bugzilla.mozilla.org/show_bug.cgi?id=1668028.\n if (!(this.options.disableRtx || browser.isFirefox())) {\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 // 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('https://jitsi.org/meet/e2ee');\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 // Schedule ping ?\n const pingJid = this.connection.domain;\n\n // FIXME no need to do it again on stream resume\n this.caps.getFeaturesAndIdentities(pingJid)\n .then(({ features, identities }) => {\n if (!features.has(Strophe.NS.PING)) {\n logger.error(\n `Ping NOT supported by ${pingJid} - 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\n .ping(this.connection.domain, 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 the 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","/* 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","/**\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 * 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","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 };\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.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=240000] - The websocket keep alive interval. It's 4 minutes by\n * default with jitter. Pass -1 to disable. The actual interval equation is:\n * jitterDelay = (interval * 0.2) + (0.8 * interval * Math.random())\n * The keep alive is HTTP GET request to the {@link options.serviceUrl}.\n */\n constructor({ enableWebsocketResume, websocketKeepAlive, serviceUrl }) {\n super();\n this._options = {\n enableWebsocketResume: typeof enableWebsocketResume === 'undefined' ? true : enableWebsocketResume,\n websocketKeepAlive: typeof websocketKeepAlive === 'undefined' ? 4 * 60 * 1000 : Number(websocketKeepAlive)\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 onPingThresholdExceeded: () => this._onPingErrorThresholdExceeded()\n }));\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 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 this._maybeStartWSKeepAlive();\n this._processDeferredIQs();\n this._resumeTask.cancel();\n this.ping.startInterval(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\n = /* base */ (websocketKeepAlive * 0.2) + /* jitter */ (Math.random() * 0.8 * websocketKeepAlive);\n\n logger.debug(`Scheduling next WebSocket keep-alive in ${intervalWithJitter}ms`);\n\n this._wsKeepAlive = setTimeout(() => {\n const url = this.service.replace('wss://', 'https://').replace('ws://', 'http://');\n\n fetch(url).catch(\n error => {\n logger.error(`Websocket Keep alive failed for url: ${url}`, { error });\n })\n .then(() => this._maybeStartWSKeepAlive());\n }, intervalWithJitter);\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 * 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","/**\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","\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.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/**\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","/* 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/**\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\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.USER_JOINED,\n this._onParticipantJoined.bind(this));\n this.conference.on(\n JitsiConferenceEvents.USER_LEFT,\n this._onParticipantLeft.bind(this));\n this.conference.on(\n JitsiConferenceEvents.CONFERENCE_JOINED,\n () => {\n this._conferenceJoined = true;\n });\n\n // 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.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 setEnabled(enabled) {\n if (enabled === this._enabled) {\n return;\n }\n\n this._enabled = enabled;\n\n if (!this._initialized && enabled) {\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 * 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 * 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 CodecMimeType from '../../service/RTC/CodecMimeType';\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} p2p.disableH264 - Described in the config.js[1].\n * @property {boolean} p2p.preferH264 - Described in the config.js[1].\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\n // codec preference options for jvb connection.\n if (pcOptions.videoQuality) {\n pcOptions.disabledCodec = pcOptions.videoQuality.disabledCodec;\n pcOptions.preferredCodec = pcOptions.videoQuality.preferredCodec;\n }\n\n if (this.isP2P) {\n // simulcast needs to be disabled for P2P (121) calls\n pcOptions.disableSimulcast = true;\n pcOptions.disableH264 = options.p2p && options.p2p.disableH264;\n pcOptions.preferH264 = options.p2p && options.p2p.preferH264;\n\n // codec preference options for p2p.\n if (options.p2p) {\n // Do not negotiate H246 codec when insertable streams is used because of issues like this -\n // https://bugs.chromium.org/p/webrtc/issues/detail?id=11886\n pcOptions.disabledCodec = options.enableInsertableStreams\n ? CodecMimeType.H264\n : options.p2p.disabledCodec;\n pcOptions.preferredCodec = options.p2p.preferredCodec;\n }\n\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 pcOptions.preferH264 = options.preferH264;\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\n // restored.\n if (this.peerconnection.signalingState === 'stable') {\n if (this.isReconnect) {\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 /* 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 /* 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 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 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","/**\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 * 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 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 initialized!');\n this._init.resolve();\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 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 logger.debug(`Olm session established with ${pId}`);\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","'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 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 */\n addFeature(feature, submit = false) {\n this.disco.addFeature(feature);\n this._generateVersion();\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 */\n removeFeature(feature, submit = false) {\n this.disco.removeFeature(feature);\n this._generateVersion();\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 * 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\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.PARTCIPANT_FEATURES_CHANGED,\n 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;\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 the\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 const height = localTrack.getSettings().height;\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 * 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 }\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 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 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._grantedPermissions = {};\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 RTC.addListener(\n RTCEvents.GRANTED_PERMISSIONS,\n grantedPermissions =>\n this._handleGrantedPermissions(grantedPermissions));\n\n // Test if the W3C Permissions API is implemented and the 'camera' and\n // 'microphone' permissions are implemented. (Testing for at least one\n // of them seems sufficient).\n this._permissionsApiSupported = new Promise(resolve => {\n if (!navigator.permissions) {\n resolve(false);\n\n return;\n }\n\n navigator.permissions.query({ name: VIDEO_PERMISSION_NAME })\n .then(() => resolve(true), () => resolve(false));\n });\n }\n\n /**\n * Updated the local granted 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} grantedPermissions - Array with the permissions\n * which were granted.\n */\n _handleGrantedPermissions(grantedPermissions) {\n this._grantedPermissions = {\n ...this._grantedPermissions,\n ...grantedPermissions\n };\n }\n\n /**\n * Gathers data and sends it to statistics.\n * @param deviceID the device id to log\n * @param devices list of devices\n */\n _logOutputDevice(deviceID, devices) {\n const device\n = devices.find(\n d => d.kind === 'audiooutput' && d.deviceId === deviceID);\n\n if (device) {\n Statistics.sendActiveDeviceListEvent(\n RTC.getEventDataForActiveDevice(device));\n }\n }\n\n /**\n * Executes callback with list of media devices connected.\n * @param {function} callback\n */\n enumerateDevices(callback) {\n RTC.enumerateDevices(callback);\n }\n\n /**\n * Checks if its possible to enumerate available cameras/micropones.\n * @returns {Promise<boolean>} a Promise which will be resolved only once\n * the WebRTC stack is ready, either with true if the device listing is\n * available available or with false otherwise.\n */\n isDeviceListAvailable() {\n return RTC.isDeviceListAvailable();\n }\n\n /**\n * Returns true if changing the input (camera / microphone) or output\n * (audio) device is supported and false if not.\n * @param {string} [deviceType] - type of device to change. Default is\n * undefined or 'input', 'output' - for audio output device change.\n * @returns {boolean} true if available, false otherwise.\n */\n isDeviceChangeAvailable(deviceType) {\n return RTC.isDeviceChangeAvailable(deviceType);\n }\n\n /**\n * Checks if the permission for the given device was granted.\n *\n * @param {'audio'|'video'} [type] - type of devices to check,\n * undefined stands for both 'audio' and 'video' together\n * @returns {Promise<boolean>}\n */\n isDevicePermissionGranted(type) {\n return new Promise(resolve => {\n // Shortcut: first check if we already know the permission was\n // granted.\n if (type in this._grantedPermissions) {\n resolve(this._grantedPermissions[type]);\n\n return;\n }\n\n // Check using the Permissions API.\n this._permissionsApiSupported.then(supported => {\n if (!supported) {\n // Workaround on Safari for audio input device\n // selection to work. Safari doesn't support the\n // permissions query.\n if (browser.isSafari()) {\n resolve(true);\n\n return;\n }\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 // The status attribute is deprecated, and state\n // should be used instead, but check both for now\n // for backwards compatibility.\n const grantStatus = permissionStatus.state\n || permissionStatus.status;\n\n return grantStatus === PERMISSION_GRANTED_STATUS;\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 /**\n * Returns whether or not the current browser can support capturing video,\n * be it camera or desktop, and displaying received video.\n *\n * @returns {boolean}\n */\n supportsVideo() {\n // Defer to the browser capabilities to allow exposure of the api to the\n // consumer but prevent other files from having to import\n // JitsiMediaDevices.\n return browser.supportsVideo();\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 < 1) {\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 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 = createEmptyStream();\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 * Creates a empty MediaStream object which can be used\n * to add MediaStreamTracks to\n * @returns MediaStream\n */\nfunction createEmptyStream() {\n if (typeof MediaStream !== 'undefined') {\n return new MediaStream();\n }\n throw new Error('cannot create a clean mediaStream');\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 return this.xmpp.caps.addFeature(feature, submit);\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 return this.xmpp.caps.removeFeature(feature, submit);\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 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 { JITSI_MEET_MUC_TYPE } from './modules/xmpp/xmpp';\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 * @param {*} [options.config.openBridgeChannel] Which kind of communication to\n * open with the videobridge. Values can be \"datachannel\", \"websocket\", true\n * (treat it as \"datachannel\"), undefined (treat it as \"datachannel\") and false\n * (don't open any channel).\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 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\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 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 if (track.isVideoTrack()) {\n // Ensure there's exactly 1 local video track in the conference.\n const localVideoTrack = this.rtc.getLocalVideoTrack();\n\n if (localVideoTrack) {\n // Don't be excessively harsh and severe if the API client happens\n // to attempt to add the same local video track twice.\n if (track === localVideoTrack) {\n return Promise.resolve(track);\n }\n\n return Promise.reject(new Error(\n 'cannot add second video track to the conference'));\n\n }\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 * Elects the participant with the given id to be the pinned participant in\n * order to always receive video for this participant (even when last n is\n * enabled).\n * @param participantId the identifier of the participant\n * @throws NetworkError or InvalidStateError or Error if the operation fails.\n */\nJitsiConference.prototype.pinParticipant = function(participantId) {\n this.rtc.pinEndpoint(participantId);\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 */\nJitsiConference.prototype.onMemberJoined = function(\n jid, nick, role, isHidden, statsID, status, identity, botType) {\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 this.participants[id] = participant;\n this.eventEmitter.emit(\n JitsiConferenceEvents.USER_JOINED,\n id,\n participant);\n\n this._updateFeatures(participant);\n\n this._maybeStartOrStopP2P();\n this._maybeSetSITimeout();\n};\n\n/* eslint-enable max-params */\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('http://jitsi.org/protocol/jigasi')) {\n participant.setProperty('features_jigasi', true);\n }\n\n if (features.has('https://jitsi.org/meet/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 }\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 let bridgeChannelType;\n\n switch (this.options.config.openBridgeChannel) {\n case 'datachannel':\n case true:\n case undefined:\n bridgeChannelType = 'datachannel';\n break;\n case 'websocket':\n bridgeChannelType = 'websocket';\n break;\n }\n\n if (bridgeChannelType === 'datachannel') {\n this.rtc.initializeBridgeChannel(pc, null);\n } else if (bridgeChannelType === 'websocket' && wsUrl) {\n this.rtc.initializeBridgeChannel(null, wsUrl);\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_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 'octo-enabled'\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') !== 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 // send some analytics events\n chatRoom.addListener(XMPPEvents.MUC_JOINED,\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 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.PARTCIPANT_FEATURES_CHANGED,\n this.xmppListeners[XMPPEvents.PARTCIPANT_FEATURES_CHANGED]);\n delete this.xmppListeners[XMPPEvents.PARTCIPANT_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 => {\n const participant\n = conference.getParticipantById(\n Strophe.getResourceFromJid(from));\n\n if (participant) {\n conference.eventEmitter.emit(\n JitsiConferenceEvents.PARTCIPANT_FEATURES_CHANGED,\n participant);\n }\n };\n\n conference.xmpp.caps.addListener(\n XMPPEvents.PARTCIPANT_FEATURES_CHANGED,\n featuresChangedListener);\n this.xmppListeners[XMPPEvents.PARTCIPANT_FEATURES_CHANGED]\n = 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// 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 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()\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 * Returns whether or not the current browser can support capturing video,\n * be it camera or desktop, and displaying received video.\n *\n * @returns {boolean}\n */\n supportsVideo() {\n return true;\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 // eslint-disable-next-line max-len\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)\n .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 const REQUIRED_CHROME_VERSION = 61;\n\n if (this.isChrome()) {\n return !this.isVersionLessThan(REQUIRED_CHROME_VERSION);\n }\n\n if (this.isFirefox() || this.isSafari()) {\n return true;\n }\n\n if (this.isChromiumBased()) {\n return this._getChromiumBasedVersion() >= REQUIRED_CHROME_VERSION;\n }\n\n return false;\n }\n\n /**\n * Checks if the browser uses webrtc-adapter. All browsers using the new\n * getUserMedia flow and Edge.\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() && this._getChromiumBasedVersion() >= 65;\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.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];\nKEYS_BY_BROWSER_TYPE[browsers.REACT_NATIVE]\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 // eslint-disable-next-line no-throw-literal\n throw `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();\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 // eslint-disable-next-line no-throw-literal\n throw `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 case browsers.REACT_NATIVE:\n // The implementation provided by react-native-webrtc follows the\n // Objective-C WebRTC API: RTCStatsReport has a values property of type\n // Array in which each element is a key-value pair.\n itemStatByKey = function(item, key) {\n let value;\n\n item.values.some(pair => {\n if (pair.hasOwnProperty(key)) {\n value = pair[key];\n\n return true;\n }\n\n return false;\n\n });\n\n return value;\n };\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.length && videoCodec.length) {\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 // eslint-disable-next-line no-throw-literal\n throw `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 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 // 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 }\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 * @param {int} timeout the timeout in ms for reply from the participant.\n * @returns {Promise<Set<String>, Error>}\n */\n getFeatures(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","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\n // adds previd param only if missing\n if (search.indexOf('previd=') === -1) {\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 * Ping every 10 sec\n */\nconst PING_INTERVAL = 10000;\n\n/**\n * Ping timeout error after 5 sec of waiting.\n */\nconst PING_TIMEOUT = 5000;\n\n/**\n * 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_THRESHOLD = 2;\n\n/**\n * The number of timestamps of send pings to keep.\n * The current value is 2 minutes.\n * @type {number} number of timestamps.\n */\nconst PING_TIMESTAMPS_TO_KEEP = 120000 / PING_INTERVAL;\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 * Contructs 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 * @constructor\n */\n constructor({ onPingThresholdExceeded }) {\n super();\n this.failedPings = 0;\n this.pingExecIntervals = new Array(PING_TIMESTAMPS_TO_KEEP);\n this._onPingThresholdExceeded = onPingThresholdExceeded;\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 * @param interval task interval in ms.\n */\n startInterval(remoteJid, interval = PING_INTERVAL) {\n clearInterval(this.intervalId);\n this.intervalId = window.setInterval(() => {\n this.ping(remoteJid, () => {\n this.failedPings = 0;\n }, error => {\n this.failedPings += 1;\n const errmsg = `Ping ${error ? 'error' : 'timeout'}`;\n\n if (this.failedPings >= PING_THRESHOLD) {\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 }, PING_TIMEOUT);\n }, interval);\n logger.info(`XMPP pings will be sent every ${interval} 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 > PING_TIMESTAMPS_TO_KEEP) {\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 -= PING_INTERVAL;\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 { $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 * @param {Object} customJoinPresenceExtensions - Key values object to be used\n * for the initial presence, they key will be an xmpp node and its text is the value,\n * and those will be added to the initial <x xmlns='http://jabber.org/protocol/muc'/>\n * @returns {Promise} - resolved when join completes. At the time of this\n * writing it's never rejected.\n */\n join(password, customJoinPresenceExtensions) {\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, customJoinPresenceExtensions);\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 * @param customJoinPresenceExtensions - Object of key values to be added to the initial presence only.\n */\n sendPresence(fromJoin, customJoinPresenceExtensions) {\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 (customJoinPresenceExtensions) {\n Object.keys(customJoinPresenceExtensions).forEach(key => {\n pres.c(key).t(customJoinPresenceExtensions[key]).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 '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, jid);\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\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 memberOfThis.isFocus = true;\n this._initFocus(from, jid);\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\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 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 * Initialize some properties when the focus participant is verified.\n * @param from jid of the focus\n * @param mucJid the jid of the focus in the muc\n */\n _initFocus(from, mucJid) {\n this.focusMucJid = from;\n\n logger.info(`Ignore focus: ${from}, real JID: ${mucJid}`);\n this.xmpp.caps.getFeatures(mucJid, 15000).then(features => {\n this.focusFeatures = features;\n logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);\n }, error => {\n logger.error('Failed to discover Jicofo features', error && error.message);\n });\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.focusFeatures\n ? this.focusFeatures.has('https://jitsi.org/meet/jicofo/terminate-restart')\n : false;\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 videoTypeNode = filterNodeFromPresenceJSON(pres, 'videoType');\n\n if (videoTypeNode.length > 0) {\n data.videoType = videoTypeNode[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 && jid === this.mainRoom.roomjid) {\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 // Sip gateway can be enabled by configuring Jigasi host in config.js or\n // it will be enabled automatically if focus detects the component through\n // service discovery.\n this.sipGatewayEnabled\n = this.options.connection.hosts\n && this.options.connection.hosts.call_control !== undefined;\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 if (this.options.connection.enforcedBridge !== undefined) {\n elem.c(\n 'property', {\n name: 'enforcedBridge',\n value: this.options.connection.enforcedBridge\n }).up();\n }\n\n // Tell the focus we have Jigasi configured\n if (this.options.connection.hosts !== undefined\n && this.options.connection.hosts.call_control !== undefined) {\n elem.c(\n 'property', {\n name: 'call_control',\n value: this.options.connection.hosts.call_control\n }).up();\n }\n if (config.channelLastN !== undefined) {\n elem.c(\n 'property', {\n name: 'channelLastN',\n value: config.channelLastN\n }).up();\n }\n elem.c(\n 'property', {\n name: 'disableRtx',\n value: Boolean(config.disableRtx)\n }).up();\n\n if (config.enableTcc !== undefined) {\n elem.c(\n 'property', {\n name: 'enableTcc',\n value: Boolean(config.enableTcc)\n }).up();\n }\n if (config.enableRemb !== undefined) {\n elem.c(\n 'property', {\n name: 'enableRemb',\n value: Boolean(config.enableRemb)\n }).up();\n }\n if (config.enableOpusRed === true) {\n elem.c(\n 'property', {\n name: 'enableOpusRed',\n value: true\n }).up();\n }\n if (config.minParticipants !== undefined) {\n elem.c(\n 'property', {\n name: 'minParticipants',\n value: config.minParticipants\n }).up();\n }\n\n elem.c(\n 'property', {\n name: 'enableLipSync',\n value: this.options.connection.enableLipSync === true\n }).up();\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 if (config.testing && config.testing.octo\n && typeof config.testing.octo.probability === 'number') {\n if (Math.random() < config.testing.octo.probability) {\n elem.c(\n 'property', {\n name: 'octo',\n value: true\n }).up();\n }\n }\n\n let openSctp;\n\n switch (this.options.conference.openBridgeChannel) {\n case 'datachannel':\n case true:\n case undefined:\n openSctp = true;\n break;\n case 'websocket':\n openSctp = false;\n break;\n }\n\n elem.c(\n 'property', {\n name: 'openSctp',\n value: openSctp\n }).up();\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 if (this.options.conference.useRoomAsSharedDocumentName !== undefined) {\n elem.c(\n 'property', {\n name: 'useRoomAsSharedDocumentName',\n value: this.options.conference.useRoomAsSharedDocumentName\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 focus has auto-detected Jigasi component(this will be also\n // included if we have passed our host from the config)\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 //\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.connection.domain })\n .c('services', { xmlns: 'urn:xmpp:extdisco:1' }),\n 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 const username = el.attr('username');\n\n // https://code.google.com/p/webrtc/issues/detail\n // ?id=1508\n\n if (username) {\n const match\n = navigator.userAgent.match(\n /Chrom(e|ium)\\/([0-9]+)\\./);\n\n if (match && parseInt(match[2], 10) < 28) {\n dict.urls += `${username}@`;\n } else {\n // only works in M28\n dict.username = username;\n }\n }\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 }, err => {\n logger.warn('getting turn credentials failed', err);\n logger.warn('is mod_turncredentials or similar installed?');\n });\n\n // implement push?\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","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(\n 'At least peerconnection or wsUrl must be given');\n } else if (peerconnection && wsUrl) {\n throw new TypeError(\n '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 const jsonObject = {\n colibriClass: 'LastNChangedEvent',\n lastN: value\n };\n\n this._send(jsonObject);\n logger.log(`Channel lastN set to: ${value}`);\n }\n\n /**\n * Sends a \"pinned endpoint changed\" message via the channel.\n * @param {string} endpointId The id of the pinned endpoint.\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 sendPinnedEndpointMessage(endpointId) {\n logger.log(\n 'sending pinned changed notification to the bridge for endpoint ',\n endpointId);\n\n this._send({\n colibriClass: 'PinnedEndpointChangedEvent',\n pinnedEndpoint: endpointId || null\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(\n 'sending selected changed notification to the bridge for endpoints',\n 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 a ReceiverVideoConstraint message with '\n + `a maxFrameHeight of ${maxFrameHeightPixels} pixels`);\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(\n 'Failed to parse channel message as JSON: ',\n 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(\n 'Channel new dominant speaker event: ',\n dominantSpeakerEndpoint);\n emitter.emit(\n RTCEvents.DOMINANT_SPEAKER_CHANGED,\n dominantSpeakerEndpoint);\n break;\n }\n case 'EndpointConnectivityStatusChangeEvent': {\n const endpoint = obj.endpoint;\n const isActive = obj.active === 'true';\n\n logger.info(\n `Endpoint connection status changed: ${endpoint} active ? ${\n isActive}`);\n emitter.emit(RTCEvents.ENDPOINT_CONN_STATUS_CHANGED,\n endpoint, isActive);\n\n break;\n }\n case 'EndpointMessage': {\n emitter.emit(\n RTCEvents.ENDPOINT_MESSAGE_RECEIVED, obj.from,\n obj.msgPayload);\n\n break;\n }\n case 'LastNEndpointsChangeEvent': {\n // The new/latest list of last-n endpoint IDs.\n const lastNEndpoints = obj.lastNEndpoints;\n\n logger.info('Channel new last-n event: ',\n lastNEndpoints, obj);\n emitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED,\n lastNEndpoints, obj);\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 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 DESKSTOP_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 // We currently support preferring/disabling video codecs only.\n const 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 // Set the codec preference that will be applied on the SDP based on the config.js settings.\n let preferredCodec = getCodecMimeType(\n this.options.preferredCodec || (this.options.preferH264 && CodecMimeType.H264)\n );\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() && preferredCodec === CodecMimeType.VP9) {\n preferredCodec = null;\n }\n\n // Determine the codec that needs to be disabled based on config.js settings.\n let disabledCodec = getCodecMimeType(\n this.options.disabledCodec || (this.options.disableH264 && CodecMimeType.H264)\n );\n\n // Make sure we don't disable VP8 since it is a mandatory codec.\n if (disabledCodec === CodecMimeType.VP8) {\n logger.warn('Disabling VP8 is not permitted, setting is ignored!');\n disabledCodec = null;\n }\n\n if (preferredCodec || disabledCodec) {\n // If both enable and disable are set for the same codec, disable setting will prevail.\n this.codecPreference = {\n enable: disabledCodec === null,\n mediaType: MediaType.VIDEO,\n mimeType: disabledCodec ? disabledCodec : preferredCodec\n };\n }\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 * 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 } 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 * 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\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 if (!this.peerconnection.getSenders) {\n logger.debug('Browser doesn\\'t support RTCRtpSender');\n\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\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 || (browser.usesPlanB() && this.options.capScreenshareBitrate && videoType === VideoType.DESKTOP)\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 // On chromium, set a max bitrate of 500 Kbps for screenshare when\n // capScreenshareBitrate is enabled through config.js and presenter\n // is not turned on.\n const bitrate = browser.usesPlanB()\n && videoType === VideoType.DESKTOP\n && this.options.capScreenshareBitrate\n ? presenterEnabled ? this.videoBitrates.high : DESKSTOP_SHARE_RATE\n : this.tpcUtils.localStreamEncodingsConfig[encoding].maxBitrate;\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\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 // Determine if \"x-google-conference\" needs to be added to the remote description.\n // We need to add that flag for camera tracks always and for desktop tracks only when\n // capScreenshareBitrate is disabled.\n const enableConferenceFlag = !(this.options.capScreenshareBitrate && !hasCameraTrack(this));\n\n // eslint-disable-next-line no-param-reassign\n description = this.simulcast.mungeRemoteDescription(description, enableConferenceFlag);\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 logger.log(`${this} senderVideoMaxHeight: ${newHeight}`);\n\n const localVideoTrack = this.getLocalVideoTrack();\n\n if (!localVideoTrack || localVideoTrack.isMuted() || localVideoTrack.videoType !== VideoType.CAMERA) {\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 } else if (newHeight > 0) {\n parameters.encodings[0].scaleResolutionDownBy = localVideoTrack.resolution >= newHeight\n ? Math.floor(localVideoTrack.resolution / newHeight)\n : 1;\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\n/**\n * Checks if a camera track has been added to the peerconnection\n * @param {TraceablePeerConnection} peerConnection\n * @return {boolean} <tt>true</tt> if the peerconnection has\n * a camera track for its video source <tt>false</tt> otherwise.\n */\nfunction hasCameraTrack(peerConnection) {\n return peerConnection.getLocalTracks()\n .some(t => t.videoType === 'camera');\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\n // desktop tracks only when the testing flag for maxbitrates\n // in config.js is disabled.\n if (this.isSimulcastOn() && browser.usesSdpMungingForSimulcast()\n && (!this.options.capScreenshareBitrate\n || (this.options.capScreenshareBitrate && hasCameraTrack(this)))) {\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\n // desktop tracks only when the testing flag for maxbitrates\n // in config.js is disabled.\n if (this.isSimulcastOn()\n && (!this.options.capScreenshareBitrate\n || (this.options.capScreenshareBitrate && hasCameraTrack(this)))) {\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 { enableIceRestart } = this._conference.options.config;\n const explicitlyDisabled = typeof enableIceRestart !== 'undefined' && !enableIceRestart;\n const supportsRestartByTerminate = this._conference.room.supportsRestartByTerminate();\n const useTerminateForRestart = supportsRestartByTerminate && !enableIceRestart;\n\n logger.info('ICE failed,'\n + ` enableIceRestart: ${enableIceRestart},`\n + ` supports restart by terminate: ${supportsRestartByTerminate}`);\n\n if (explicitlyDisabled || (!enableIceRestart && !supportsRestartByTerminate)) {\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 the 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 if (!options.disableThirdPartyRequests) {\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/**\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 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":""} |