diff --git a/dist/naf-janus-adapter.js b/dist/naf-janus-adapter.js index 50c5781..7e324c2 100644 --- a/dist/naf-janus-adapter.js +++ b/dist/naf-janus-adapter.js @@ -362,7 +362,7 @@ module.exports = { // SDP helpers. -var SDPUtils = {}; +const SDPUtils = {}; // Generate an alphanumeric identifier for cname or mids. // TODO: use UUIDs instead? https://gist.github.com/jed/982883 @@ -375,43 +375,39 @@ SDPUtils.localCName = SDPUtils.generateIdentifier(); // Splits SDP into lines, dealing with both CRLF and LF. SDPUtils.splitLines = function(blob) { - return blob.trim().split('\n').map(function(line) { - return line.trim(); - }); + return blob.trim().split('\n').map(line => line.trim()); }; // Splits SDP into sessionpart and mediasections. Ensures CRLF. SDPUtils.splitSections = function(blob) { - var parts = blob.split('\nm='); - return parts.map(function(part, index) { - return (index > 0 ? 'm=' + part : part).trim() + '\r\n'; - }); + const parts = blob.split('\nm='); + return parts.map((part, index) => (index > 0 ? + 'm=' + part : part).trim() + '\r\n'); }; -// returns the session description. +// Returns the session description. SDPUtils.getDescription = function(blob) { - var sections = SDPUtils.splitSections(blob); + const sections = SDPUtils.splitSections(blob); return sections && sections[0]; }; -// returns the individual media sections. +// Returns the individual media sections. SDPUtils.getMediaSections = function(blob) { - var sections = SDPUtils.splitSections(blob); + const sections = SDPUtils.splitSections(blob); sections.shift(); return sections; }; // Returns lines that start with a certain prefix. SDPUtils.matchPrefix = function(blob, prefix) { - return SDPUtils.splitLines(blob).filter(function(line) { - return line.indexOf(prefix) === 0; - }); + return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0); }; // Parses an ICE candidate line. Sample input: // candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8 // rport 55996" +// Input can be prefixed with a=. SDPUtils.parseCandidate = function(line) { - var parts; + let parts; // Parse both variants. if (line.indexOf('a=candidate:') === 0) { parts = line.substring(12).split(' '); @@ -419,19 +415,19 @@ SDPUtils.parseCandidate = function(line) { parts = line.substring(10).split(' '); } - var candidate = { + const candidate = { foundation: parts[0], - component: parseInt(parts[1], 10), + component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1], protocol: parts[2].toLowerCase(), priority: parseInt(parts[3], 10), ip: parts[4], address: parts[4], // address is an alias for ip. port: parseInt(parts[5], 10), // skip parts[6] == 'typ' - type: parts[7] + type: parts[7], }; - for (var i = 8; i < parts.length; i += 2) { + for (let i = 8; i < parts.length; i += 2) { switch (parts[i]) { case 'raddr': candidate.relatedAddress = parts[i + 1]; @@ -443,11 +439,13 @@ SDPUtils.parseCandidate = function(line) { candidate.tcpType = parts[i + 1]; break; case 'ufrag': - candidate.ufrag = parts[i + 1]; // for backward compability. + candidate.ufrag = parts[i + 1]; // for backward compatibility. candidate.usernameFragment = parts[i + 1]; break; - default: // extension handling, in particular ufrag - candidate[parts[i]] = parts[i + 1]; + default: // extension handling, in particular ufrag. Don't overwrite. + if (candidate[parts[i]] === undefined) { + candidate[parts[i]] = parts[i + 1]; + } break; } } @@ -455,16 +453,25 @@ SDPUtils.parseCandidate = function(line) { }; // Translates a candidate object into SDP candidate attribute. +// This does not include the a= prefix! SDPUtils.writeCandidate = function(candidate) { - var sdp = []; + const sdp = []; sdp.push(candidate.foundation); - sdp.push(candidate.component); + + const component = candidate.component; + if (component === 'rtp') { + sdp.push(1); + } else if (component === 'rtcp') { + sdp.push(2); + } else { + sdp.push(component); + } sdp.push(candidate.protocol.toUpperCase()); sdp.push(candidate.priority); sdp.push(candidate.address || candidate.ip); sdp.push(candidate.port); - var type = candidate.type; + const type = candidate.type; sdp.push('typ'); sdp.push(type); if (type !== 'host' && candidate.relatedAddress && @@ -486,17 +493,18 @@ SDPUtils.writeCandidate = function(candidate) { }; // Parses an ice-options line, returns an array of option tags. +// Sample input: // a=ice-options:foo bar SDPUtils.parseIceOptions = function(line) { return line.substr(14).split(' '); }; -// Parses an rtpmap line, returns RTCRtpCoddecParameters. Sample input: +// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input: // a=rtpmap:111 opus/48000/2 SDPUtils.parseRtpMap = function(line) { - var parts = line.substr(9).split(' '); - var parsed = { - payloadType: parseInt(parts.shift(), 10) // was: id + let parts = line.substr(9).split(' '); + const parsed = { + payloadType: parseInt(parts.shift(), 10), // was: id }; parts = parts[0].split('/'); @@ -509,31 +517,31 @@ SDPUtils.parseRtpMap = function(line) { return parsed; }; -// Generate an a=rtpmap line from RTCRtpCodecCapability or +// Generates a rtpmap line from RTCRtpCodecCapability or // RTCRtpCodecParameters. SDPUtils.writeRtpMap = function(codec) { - var pt = codec.payloadType; + let pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } - var channels = codec.channels || codec.numChannels || 1; + const channels = codec.channels || codec.numChannels || 1; return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate + (channels !== 1 ? '/' + channels : '') + '\r\n'; }; -// Parses an a=extmap line (headerextension from RFC 5285). Sample input: +// Parses a extmap line (headerextension from RFC 5285). Sample input: // a=extmap:2 urn:ietf:params:rtp-hdrext:toffset // a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset SDPUtils.parseExtmap = function(line) { - var parts = line.substr(9).split(' '); + const parts = line.substr(9).split(' '); return { id: parseInt(parts[0], 10), direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv', - uri: parts[1] + uri: parts[1], }; }; -// Generates a=extmap line from RTCRtpHeaderExtensionParameters or +// Generates an extmap line from RTCRtpHeaderExtensionParameters or // RTCRtpHeaderExtension. SDPUtils.writeExtmap = function(headerExtension) { return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) + @@ -543,31 +551,31 @@ SDPUtils.writeExtmap = function(headerExtension) { ' ' + headerExtension.uri + '\r\n'; }; -// Parses an ftmp line, returns dictionary. Sample input: +// Parses a fmtp line, returns dictionary. Sample input: // a=fmtp:96 vbr=on;cng=on // Also deals with vbr=on; cng=on SDPUtils.parseFmtp = function(line) { - var parsed = {}; - var kv; - var parts = line.substr(line.indexOf(' ') + 1).split(';'); - for (var j = 0; j < parts.length; j++) { + const parsed = {}; + let kv; + const parts = line.substr(line.indexOf(' ') + 1).split(';'); + for (let j = 0; j < parts.length; j++) { kv = parts[j].trim().split('='); parsed[kv[0].trim()] = kv[1]; } return parsed; }; -// Generates an a=ftmp line from RTCRtpCodecCapability or RTCRtpCodecParameters. +// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters. SDPUtils.writeFmtp = function(codec) { - var line = ''; - var pt = codec.payloadType; + let line = ''; + let pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } if (codec.parameters && Object.keys(codec.parameters).length) { - var params = []; - Object.keys(codec.parameters).forEach(function(param) { - if (codec.parameters[param]) { + const params = []; + Object.keys(codec.parameters).forEach(param => { + if (codec.parameters[param] !== undefined) { params.push(param + '=' + codec.parameters[param]); } else { params.push(param); @@ -578,25 +586,26 @@ SDPUtils.writeFmtp = function(codec) { return line; }; -// Parses an rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: +// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input: // a=rtcp-fb:98 nack rpsi SDPUtils.parseRtcpFb = function(line) { - var parts = line.substr(line.indexOf(' ') + 1).split(' '); + const parts = line.substr(line.indexOf(' ') + 1).split(' '); return { type: parts.shift(), - parameter: parts.join(' ') + parameter: parts.join(' '), }; }; + // Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters. SDPUtils.writeRtcpFb = function(codec) { - var lines = ''; - var pt = codec.payloadType; + let lines = ''; + let pt = codec.payloadType; if (codec.preferredPayloadType !== undefined) { pt = codec.preferredPayloadType; } if (codec.rtcpFeedback && codec.rtcpFeedback.length) { // FIXME: special handling for trr-int? - codec.rtcpFeedback.forEach(function(fb) { + codec.rtcpFeedback.forEach(fb => { lines += 'a=rtcp-fb:' + pt + ' ' + fb.type + (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') + '\r\n'; @@ -605,14 +614,14 @@ SDPUtils.writeRtcpFb = function(codec) { return lines; }; -// Parses an RFC 5576 ssrc media attribute. Sample input: +// Parses a RFC 5576 ssrc media attribute. Sample input: // a=ssrc:3735928559 cname:something SDPUtils.parseSsrcMedia = function(line) { - var sp = line.indexOf(' '); - var parts = { - ssrc: parseInt(line.substr(7, sp - 7), 10) + const sp = line.indexOf(' '); + const parts = { + ssrc: parseInt(line.substr(7, sp - 7), 10), }; - var colon = line.indexOf(':', sp); + const colon = line.indexOf(':', sp); if (colon > -1) { parts.attribute = line.substr(sp + 1, colon - sp - 1); parts.value = line.substr(colon + 1); @@ -622,30 +631,31 @@ SDPUtils.parseSsrcMedia = function(line) { return parts; }; +// Parse a ssrc-group line (see RFC 5576). Sample input: +// a=ssrc-group:semantics 12 34 SDPUtils.parseSsrcGroup = function(line) { - var parts = line.substr(13).split(' '); + const parts = line.substr(13).split(' '); return { semantics: parts.shift(), - ssrcs: parts.map(function(ssrc) { - return parseInt(ssrc, 10); - }) + ssrcs: parts.map(ssrc => parseInt(ssrc, 10)), }; }; // Extracts the MID (RFC 5888) from a media section. -// returns the MID or undefined if no mid line was found. +// Returns the MID or undefined if no mid line was found. SDPUtils.getMid = function(mediaSection) { - var mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; + const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0]; if (mid) { return mid.substr(6); } }; +// Parses a fingerprint line for DTLS-SRTP. SDPUtils.parseFingerprint = function(line) { - var parts = line.substr(14).split(' '); + const parts = line.substr(14).split(' '); return { algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge. - value: parts[1] + value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572. }; }; @@ -653,20 +663,19 @@ SDPUtils.parseFingerprint = function(line) { // FIXME: for consistency with other functions this should only // get the fingerprint line as input. See also getIceParameters. SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) { - var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + const lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=fingerprint:'); - // Note: a=setup line is ignored since we use the 'auto' role. - // Note2: 'algorithm' is not case sensitive except in Edge. + // Note: a=setup line is ignored since we use the 'auto' role in Edge. return { role: 'auto', - fingerprints: lines.map(SDPUtils.parseFingerprint) + fingerprints: lines.map(SDPUtils.parseFingerprint), }; }; // Serializes DTLS parameters to SDP. SDPUtils.writeDtlsParameters = function(params, setupType) { - var sdp = 'a=setup:' + setupType + '\r\n'; - params.fingerprints.forEach(function(fp) { + let sdp = 'a=setup:' + setupType + '\r\n'; + params.fingerprints.forEach(fp => { sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n'; }); return sdp; @@ -675,7 +684,7 @@ SDPUtils.writeDtlsParameters = function(params, setupType) { // Parses a=crypto lines into // https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members SDPUtils.parseCryptoLine = function(line) { - var parts = line.substr(9).split(' '); + const parts = line.substr(9).split(' '); return { tag: parseInt(parts[0], 10), cryptoSuite: parts[1], @@ -700,7 +709,7 @@ SDPUtils.parseCryptoKeyParams = function(keyParams) { if (keyParams.indexOf('inline:') !== 0) { return null; } - var parts = keyParams.substr(7).split('|'); + const parts = keyParams.substr(7).split('|'); return { keyMethod: 'inline', keySalt: parts[0], @@ -719,9 +728,9 @@ SDPUtils.writeCryptoKeyParams = function(keyParams) { : ''); }; -// Extracts all SDES paramters. +// Extracts all SDES parameters. SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) { - var lines = SDPUtils.matchPrefix(mediaSection + sessionpart, + const lines = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=crypto:'); return lines.map(SDPUtils.parseCryptoLine); }; @@ -730,9 +739,9 @@ SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) { // FIXME: for consistency with other functions this should only // get the ice-ufrag and ice-pwd lines as input. SDPUtils.getIceParameters = function(mediaSection, sessionpart) { - var ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, + const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-ufrag:')[0]; - var pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, + const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart, 'a=ice-pwd:')[0]; if (!(ufrag && pwd)) { return null; @@ -745,27 +754,31 @@ SDPUtils.getIceParameters = function(mediaSection, sessionpart) { // Serializes ICE parameters to SDP. SDPUtils.writeIceParameters = function(params) { - return 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + + let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\r\n' + 'a=ice-pwd:' + params.password + '\r\n'; + if (params.iceLite) { + sdp += 'a=ice-lite\r\n'; + } + return sdp; }; // Parses the SDP media section and returns RTCRtpParameters. SDPUtils.parseRtpParameters = function(mediaSection) { - var description = { + const description = { codecs: [], headerExtensions: [], fecMechanisms: [], - rtcp: [] + rtcp: [], }; - var lines = SDPUtils.splitLines(mediaSection); - var mline = lines[0].split(' '); - for (var i = 3; i < mline.length; i++) { // find all codecs from mline[3..] - var pt = mline[i]; - var rtpmapline = SDPUtils.matchPrefix( + const lines = SDPUtils.splitLines(mediaSection); + const mline = lines[0].split(' '); + for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..] + const pt = mline[i]; + const rtpmapline = SDPUtils.matchPrefix( mediaSection, 'a=rtpmap:' + pt + ' ')[0]; if (rtpmapline) { - var codec = SDPUtils.parseRtpMap(rtpmapline); - var fmtps = SDPUtils.matchPrefix( + const codec = SDPUtils.parseRtpMap(rtpmapline); + const fmtps = SDPUtils.matchPrefix( mediaSection, 'a=fmtp:' + pt + ' '); // Only the first a=fmtp: is considered. codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {}; @@ -784,7 +797,7 @@ SDPUtils.parseRtpParameters = function(mediaSection) { } } } - SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(function(line) { + SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => { description.headerExtensions.push(SDPUtils.parseExtmap(line)); }); // FIXME: parse rtcp. @@ -794,13 +807,13 @@ SDPUtils.parseRtpParameters = function(mediaSection) { // Generates parts of the SDP media section describing the capabilities / // parameters. SDPUtils.writeRtpDescription = function(kind, caps) { - var sdp = ''; + let sdp = ''; // Build the mline. sdp += 'm=' + kind + ' '; sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs. sdp += ' UDP/TLS/RTP/SAVPF '; - sdp += caps.codecs.map(function(codec) { + sdp += caps.codecs.map(codec => { if (codec.preferredPayloadType !== undefined) { return codec.preferredPayloadType; } @@ -811,13 +824,13 @@ SDPUtils.writeRtpDescription = function(kind, caps) { sdp += 'a=rtcp:9 IN IP4 0.0.0.0\r\n'; // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb. - caps.codecs.forEach(function(codec) { + caps.codecs.forEach(codec => { sdp += SDPUtils.writeRtpMap(codec); sdp += SDPUtils.writeFmtp(codec); sdp += SDPUtils.writeRtcpFb(codec); }); - var maxptime = 0; - caps.codecs.forEach(function(codec) { + let maxptime = 0; + caps.codecs.forEach(codec => { if (codec.maxptime > maxptime) { maxptime = codec.maxptime; } @@ -825,10 +838,9 @@ SDPUtils.writeRtpDescription = function(kind, caps) { if (maxptime > 0) { sdp += 'a=maxptime:' + maxptime + '\r\n'; } - sdp += 'a=rtcp-mux\r\n'; if (caps.headerExtensions) { - caps.headerExtensions.forEach(function(extension) { + caps.headerExtensions.forEach(extension => { sdp += SDPUtils.writeExtmap(extension); }); } @@ -839,38 +851,32 @@ SDPUtils.writeRtpDescription = function(kind, caps) { // Parses the SDP media section and returns an array of // RTCRtpEncodingParameters. SDPUtils.parseRtpEncodingParameters = function(mediaSection) { - var encodingParameters = []; - var description = SDPUtils.parseRtpParameters(mediaSection); - var hasRed = description.fecMechanisms.indexOf('RED') !== -1; - var hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; + const encodingParameters = []; + const description = SDPUtils.parseRtpParameters(mediaSection); + const hasRed = description.fecMechanisms.indexOf('RED') !== -1; + const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1; // filter a=ssrc:... cname:, ignore PlanB-msid - var ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(parts) { - return parts.attribute === 'cname'; - }); - var primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; - var secondarySsrc; - - var flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') - .map(function(line) { - var parts = line.substr(17).split(' '); - return parts.map(function(part) { - return parseInt(part, 10); - }); + const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(line => SDPUtils.parseSsrcMedia(line)) + .filter(parts => parts.attribute === 'cname'); + const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc; + let secondarySsrc; + + const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID') + .map(line => { + const parts = line.substr(17).split(' '); + return parts.map(part => parseInt(part, 10)); }); if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) { secondarySsrc = flows[0][1]; } - description.codecs.forEach(function(codec) { + description.codecs.forEach(codec => { if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) { - var encParam = { + let encParam = { ssrc: primarySsrc, - codecPayloadType: parseInt(codec.parameters.apt, 10) + codecPayloadType: parseInt(codec.parameters.apt, 10), }; if (primarySsrc && secondarySsrc) { encParam.rtx = {ssrc: secondarySsrc}; @@ -880,7 +886,7 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { encParam = JSON.parse(JSON.stringify(encParam)); encParam.fec = { ssrc: primarySsrc, - mechanism: hasUlpfec ? 'red+ulpfec' : 'red' + mechanism: hasUlpfec ? 'red+ulpfec' : 'red', }; encodingParameters.push(encParam); } @@ -888,12 +894,12 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { }); if (encodingParameters.length === 0 && primarySsrc) { encodingParameters.push({ - ssrc: primarySsrc + ssrc: primarySsrc, }); } // we support both b=AS and b=TIAS but interpret AS as TIAS. - var bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); + let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b='); if (bandwidth.length) { if (bandwidth[0].indexOf('b=TIAS:') === 0) { bandwidth = parseInt(bandwidth[0].substr(7), 10); @@ -904,7 +910,7 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { } else { bandwidth = undefined; } - encodingParameters.forEach(function(params) { + encodingParameters.forEach(params => { params.maxBitrate = bandwidth; }); } @@ -913,17 +919,13 @@ SDPUtils.parseRtpEncodingParameters = function(mediaSection) { // parses http://draft.ortc.org/#rtcrtcpparameters* SDPUtils.parseRtcpParameters = function(mediaSection) { - var rtcpParameters = {}; + const rtcpParameters = {}; - // Gets the first SSRC. Note tha with RTX there might be multiple + // Gets the first SSRC. Note that with RTX there might be multiple // SSRCs. - var remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(obj) { - return obj.attribute === 'cname'; - })[0]; + const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(line => SDPUtils.parseSsrcMedia(line)) + .filter(obj => obj.attribute === 'cname')[0]; if (remoteSsrc) { rtcpParameters.cname = remoteSsrc.value; rtcpParameters.ssrc = remoteSsrc.ssrc; @@ -931,34 +933,46 @@ SDPUtils.parseRtcpParameters = function(mediaSection) { // Edge uses the compound attribute instead of reducedSize // compound is !reducedSize - var rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); + const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize'); rtcpParameters.reducedSize = rsize.length > 0; rtcpParameters.compound = rsize.length === 0; // parses the rtcp-mux attrіbute. // Note that Edge does not support unmuxed RTCP. - var mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); + const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux'); rtcpParameters.mux = mux.length > 0; return rtcpParameters; }; +SDPUtils.writeRtcpParameters = function(rtcpParameters) { + let sdp = ''; + if (rtcpParameters.reducedSize) { + sdp += 'a=rtcp-rsize\r\n'; + } + if (rtcpParameters.mux) { + sdp += 'a=rtcp-mux\r\n'; + } + if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) { + sdp += 'a=ssrc:' + rtcpParameters.ssrc + + ' cname:' + rtcpParameters.cname + '\r\n'; + } + return sdp; +}; + + // parses either a=msid: or a=ssrc:... msid lines and returns // the id of the MediaStream and MediaStreamTrack. SDPUtils.parseMsid = function(mediaSection) { - var parts; - var spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); + let parts; + const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:'); if (spec.length === 1) { parts = spec[0].substr(7).split(' '); return {stream: parts[0], track: parts[1]}; } - var planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') - .map(function(line) { - return SDPUtils.parseSsrcMedia(line); - }) - .filter(function(msidParts) { - return msidParts.attribute === 'msid'; - }); + const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:') + .map(line => SDPUtils.parseSsrcMedia(line)) + .filter(msidParts => msidParts.attribute === 'msid'); if (planB.length > 0) { parts = planB[0].value.split(' '); return {stream: parts[0], track: parts[1]}; @@ -969,32 +983,32 @@ SDPUtils.parseMsid = function(mediaSection) { // parses draft-ietf-mmusic-sctp-sdp-26 first and falls back // to draft-ietf-mmusic-sctp-sdp-05 SDPUtils.parseSctpDescription = function(mediaSection) { - var mline = SDPUtils.parseMLine(mediaSection); - var maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); - var maxMessageSize; + const mline = SDPUtils.parseMLine(mediaSection); + const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:'); + let maxMessageSize; if (maxSizeLine.length > 0) { maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10); } if (isNaN(maxMessageSize)) { maxMessageSize = 65536; } - var sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); + const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:'); if (sctpPort.length > 0) { return { port: parseInt(sctpPort[0].substr(12), 10), protocol: mline.fmt, - maxMessageSize: maxMessageSize + maxMessageSize, }; } - var sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); + const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:'); if (sctpMapLines.length > 0) { - var parts = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:')[0] + const parts = sctpMapLines[0] .substr(10) .split(' '); return { port: parseInt(parts[0], 10), protocol: parts[1], - maxMessageSize: maxMessageSize + maxMessageSize, }; } }; @@ -1005,18 +1019,18 @@ SDPUtils.parseSctpDescription = function(mediaSection) { // as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line // protocol of DTLS/SCTP -- without UDP/ or TCP/) SDPUtils.writeSctpDescription = function(media, sctp) { - var output = []; + let output = []; if (media.protocol !== 'DTLS/SCTP') { output = [ 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\r\n', 'c=IN IP4 0.0.0.0\r\n', - 'a=sctp-port:' + sctp.port + '\r\n' + 'a=sctp-port:' + sctp.port + '\r\n', ]; } else { output = [ 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\r\n', 'c=IN IP4 0.0.0.0\r\n', - 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n' + 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\r\n', ]; } if (sctp.maxMessageSize !== undefined) { @@ -1033,20 +1047,20 @@ SDPUtils.generateSessionId = function() { return Math.random().toString().substr(2, 21); }; -// Write boilder plate for start of SDP +// Write boiler plate for start of SDP // sessId argument is optional - if not supplied it will // be generated randomly // sessVersion is optional and defaults to 2 // sessUser is optional and defaults to 'thisisadapterortc' SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) { - var sessionId; - var version = sessVer !== undefined ? sessVer : 2; + let sessionId; + const version = sessVer !== undefined ? sessVer : 2; if (sessId) { sessionId = sessId; } else { sessionId = SDPUtils.generateSessionId(); } - var user = sessUser || 'thisisadapterortc'; + const user = sessUser || 'thisisadapterortc'; // FIXME: sess-id should be an NTP timestamp. return 'v=0\r\n' + 'o=' + user + ' ' + sessionId + ' ' + version + @@ -1055,65 +1069,11 @@ SDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) { 't=0 0\r\n'; }; -SDPUtils.writeMediaSection = function(transceiver, caps, type, stream) { - var sdp = SDPUtils.writeRtpDescription(transceiver.kind, caps); - - // Map ICE parameters (ufrag, pwd) to SDP. - sdp += SDPUtils.writeIceParameters( - transceiver.iceGatherer.getLocalParameters()); - - // Map DTLS parameters to SDP. - sdp += SDPUtils.writeDtlsParameters( - transceiver.dtlsTransport.getLocalParameters(), - type === 'offer' ? 'actpass' : 'active'); - - sdp += 'a=mid:' + transceiver.mid + '\r\n'; - - if (transceiver.direction) { - sdp += 'a=' + transceiver.direction + '\r\n'; - } else if (transceiver.rtpSender && transceiver.rtpReceiver) { - sdp += 'a=sendrecv\r\n'; - } else if (transceiver.rtpSender) { - sdp += 'a=sendonly\r\n'; - } else if (transceiver.rtpReceiver) { - sdp += 'a=recvonly\r\n'; - } else { - sdp += 'a=inactive\r\n'; - } - - if (transceiver.rtpSender) { - // spec. - var msid = 'msid:' + stream.id + ' ' + - transceiver.rtpSender.track.id + '\r\n'; - sdp += 'a=' + msid; - - // for Chrome. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' ' + msid; - if (transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' ' + msid; - sdp += 'a=ssrc-group:FID ' + - transceiver.sendEncodingParameters[0].ssrc + ' ' + - transceiver.sendEncodingParameters[0].rtx.ssrc + - '\r\n'; - } - } - // FIXME: this should be written by writeRtpDescription. - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - if (transceiver.rtpSender && transceiver.sendEncodingParameters[0].rtx) { - sdp += 'a=ssrc:' + transceiver.sendEncodingParameters[0].rtx.ssrc + - ' cname:' + SDPUtils.localCName + '\r\n'; - } - return sdp; -}; - // Gets the direction from the mediaSection or the sessionpart. SDPUtils.getDirection = function(mediaSection, sessionpart) { // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv. - var lines = SDPUtils.splitLines(mediaSection); - for (var i = 0; i < lines.length; i++) { + const lines = SDPUtils.splitLines(mediaSection); + for (let i = 0; i < lines.length; i++) { switch (lines[i]) { case 'a=sendrecv': case 'a=sendonly': @@ -1131,8 +1091,8 @@ SDPUtils.getDirection = function(mediaSection, sessionpart) { }; SDPUtils.getKind = function(mediaSection) { - var lines = SDPUtils.splitLines(mediaSection); - var mline = lines[0].split(' '); + const lines = SDPUtils.splitLines(mediaSection); + const mline = lines[0].split(' '); return mline[0].substr(2); }; @@ -1141,26 +1101,26 @@ SDPUtils.isRejected = function(mediaSection) { }; SDPUtils.parseMLine = function(mediaSection) { - var lines = SDPUtils.splitLines(mediaSection); - var parts = lines[0].substr(2).split(' '); + const lines = SDPUtils.splitLines(mediaSection); + const parts = lines[0].substr(2).split(' '); return { kind: parts[0], port: parseInt(parts[1], 10), protocol: parts[2], - fmt: parts.slice(3).join(' ') + fmt: parts.slice(3).join(' '), }; }; SDPUtils.parseOLine = function(mediaSection) { - var line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; - var parts = line.substr(2).split(' '); + const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0]; + const parts = line.substr(2).split(' '); return { username: parts[0], sessionId: parts[1], sessionVersion: parseInt(parts[2], 10), netType: parts[3], addressType: parts[4], - address: parts[5] + address: parts[5], }; }; @@ -1169,8 +1129,8 @@ SDPUtils.isValidSDP = function(blob) { if (typeof blob !== 'string' || blob.length === 0) { return false; } - var lines = SDPUtils.splitLines(blob); - for (var i = 0; i < lines.length; i++) { + const lines = SDPUtils.splitLines(blob); + for (let i = 0; i < lines.length; i++) { if (lines[i].length < 2 || lines[i].charAt(1) !== '=') { return false; } @@ -2292,4 +2252,4 @@ module.exports = JanusAdapter; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL21pbmlqYW51cy9taW5pamFudXMuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3NkcC9zZHAuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL2luZGV4LmpzIl0sIm5hbWVzIjpbIm1qIiwicmVxdWlyZSIsIkphbnVzU2Vzc2lvbiIsInByb3RvdHlwZSIsInNlbmRPcmlnaW5hbCIsInNlbmQiLCJ0eXBlIiwic2lnbmFsIiwiY2F0Y2giLCJlIiwibWVzc2FnZSIsImluZGV4T2YiLCJjb25zb2xlIiwiZXJyb3IiLCJOQUYiLCJjb25uZWN0aW9uIiwiYWRhcHRlciIsInJlY29ubmVjdCIsInNkcFV0aWxzIiwiZGVidWciLCJsb2ciLCJ3YXJuIiwiaXNTYWZhcmkiLCJ0ZXN0IiwibmF2aWdhdG9yIiwidXNlckFnZW50IiwiU1VCU0NSSUJFX1RJTUVPVVRfTVMiLCJkZWJvdW5jZSIsImZuIiwiY3VyciIsIlByb21pc2UiLCJyZXNvbHZlIiwiYXJncyIsIkFycmF5Iiwic2xpY2UiLCJjYWxsIiwiYXJndW1lbnRzIiwidGhlbiIsIl8iLCJhcHBseSIsInJhbmRvbVVpbnQiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJOdW1iZXIiLCJNQVhfU0FGRV9JTlRFR0VSIiwidW50aWxEYXRhQ2hhbm5lbE9wZW4iLCJkYXRhQ2hhbm5lbCIsInJlamVjdCIsInJlYWR5U3RhdGUiLCJyZXNvbHZlciIsInJlamVjdG9yIiwiY2xlYXIiLCJyZW1vdmVFdmVudExpc3RlbmVyIiwiYWRkRXZlbnRMaXN0ZW5lciIsImlzSDI2NFZpZGVvU3VwcG9ydGVkIiwidmlkZW8iLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJjYW5QbGF5VHlwZSIsIk9QVVNfUEFSQU1FVEVSUyIsInVzZWR0eCIsInN0ZXJlbyIsIkRFRkFVTFRfUEVFUl9DT05ORUNUSU9OX0NPTkZJRyIsImljZVNlcnZlcnMiLCJ1cmxzIiwiV1NfTk9STUFMX0NMT1NVUkUiLCJKYW51c0FkYXB0ZXIiLCJjb25zdHJ1Y3RvciIsInJvb20iLCJjbGllbnRJZCIsImpvaW5Ub2tlbiIsInNlcnZlclVybCIsIndlYlJ0Y09wdGlvbnMiLCJwZWVyQ29ubmVjdGlvbkNvbmZpZyIsIndzIiwic2Vzc2lvbiIsInJlbGlhYmxlVHJhbnNwb3J0IiwidW5yZWxpYWJsZVRyYW5zcG9ydCIsImluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSIsInJlY29ubmVjdGlvbkRlbGF5IiwicmVjb25uZWN0aW9uVGltZW91dCIsIm1heFJlY29ubmVjdGlvbkF0dGVtcHRzIiwicmVjb25uZWN0aW9uQXR0ZW1wdHMiLCJwdWJsaXNoZXIiLCJvY2N1cGFudHMiLCJsZWZ0T2NjdXBhbnRzIiwiU2V0IiwibWVkaWFTdHJlYW1zIiwibG9jYWxNZWRpYVN0cmVhbSIsInBlbmRpbmdNZWRpYVJlcXVlc3RzIiwiTWFwIiwiYmxvY2tlZENsaWVudHMiLCJmcm96ZW5VcGRhdGVzIiwidGltZU9mZnNldHMiLCJzZXJ2ZXJUaW1lUmVxdWVzdHMiLCJhdmdUaW1lT2Zmc2V0Iiwib25XZWJzb2NrZXRPcGVuIiwiYmluZCIsIm9uV2Vic29ja2V0Q2xvc2UiLCJvbldlYnNvY2tldE1lc3NhZ2UiLCJvbkRhdGFDaGFubmVsTWVzc2FnZSIsIm9uRGF0YSIsInNldFNlcnZlclVybCIsInVybCIsInNldEFwcCIsImFwcCIsInNldFJvb20iLCJyb29tTmFtZSIsInNldEpvaW5Ub2tlbiIsInNldENsaWVudElkIiwic2V0V2ViUnRjT3B0aW9ucyIsIm9wdGlvbnMiLCJzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyIsInNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMiLCJzdWNjZXNzTGlzdGVuZXIiLCJmYWlsdXJlTGlzdGVuZXIiLCJjb25uZWN0U3VjY2VzcyIsImNvbm5lY3RGYWlsdXJlIiwic2V0Um9vbU9jY3VwYW50TGlzdGVuZXIiLCJvY2N1cGFudExpc3RlbmVyIiwib25PY2N1cGFudHNDaGFuZ2VkIiwic2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMiLCJvcGVuTGlzdGVuZXIiLCJjbG9zZWRMaXN0ZW5lciIsIm1lc3NhZ2VMaXN0ZW5lciIsIm9uT2NjdXBhbnRDb25uZWN0ZWQiLCJvbk9jY3VwYW50RGlzY29ubmVjdGVkIiwib25PY2N1cGFudE1lc3NhZ2UiLCJzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMiLCJyZWNvbm5lY3RpbmdMaXN0ZW5lciIsInJlY29ubmVjdGVkTGlzdGVuZXIiLCJyZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyIiwib25SZWNvbm5lY3RpbmciLCJvblJlY29ubmVjdGVkIiwib25SZWNvbm5lY3Rpb25FcnJvciIsImNvbm5lY3QiLCJ3ZWJzb2NrZXRDb25uZWN0aW9uIiwiV2ViU29ja2V0IiwidGltZW91dE1zIiwid3NPbk9wZW4iLCJhbGwiLCJ1cGRhdGVUaW1lT2Zmc2V0IiwiZGlzY29ubmVjdCIsImNsZWFyVGltZW91dCIsInJlbW92ZUFsbE9jY3VwYW50cyIsImNvbm4iLCJjbG9zZSIsImRpc3Bvc2UiLCJkZWxheWVkUmVjb25uZWN0VGltZW91dCIsImlzRGlzY29ubmVjdGVkIiwiY3JlYXRlIiwiY3JlYXRlUHVibGlzaGVyIiwiYWRkT2NjdXBhbnRQcm9taXNlcyIsImkiLCJpbml0aWFsT2NjdXBhbnRzIiwibGVuZ3RoIiwib2NjdXBhbnRJZCIsInB1c2giLCJhZGRPY2N1cGFudCIsImV2ZW50IiwiY29kZSIsInNldFRpbWVvdXQiLCJFcnJvciIsInBlcmZvcm1EZWxheWVkUmVjb25uZWN0IiwicmVjZWl2ZSIsIkpTT04iLCJwYXJzZSIsImRhdGEiLCJyZW1vdmVPY2N1cGFudCIsImRlbGV0ZSIsInN1YnNjcmliZXIiLCJjcmVhdGVTdWJzY3JpYmVyIiwic2V0TWVkaWFTdHJlYW0iLCJtZWRpYVN0cmVhbSIsIk9iamVjdCIsImdldE93blByb3BlcnR5TmFtZXMiLCJhZGQiLCJoYXMiLCJtc2ciLCJnZXQiLCJhdWRpbyIsImFzc29jaWF0ZSIsImhhbmRsZSIsImV2Iiwic2VuZFRyaWNrbGUiLCJjYW5kaWRhdGUiLCJpY2VDb25uZWN0aW9uU3RhdGUiLCJvZmZlciIsImNyZWF0ZU9mZmVyIiwiY29uZmlndXJlUHVibGlzaGVyU2RwIiwiZml4U2FmYXJpSWNlVUZyYWciLCJsb2NhbCIsIm8iLCJzZXRMb2NhbERlc2NyaXB0aW9uIiwicmVtb3RlIiwiaiIsInNlbmRKc2VwIiwiciIsInNldFJlbW90ZURlc2NyaXB0aW9uIiwianNlcCIsIm9uIiwiYW5zd2VyIiwiY29uZmlndXJlU3Vic2NyaWJlclNkcCIsImNyZWF0ZUFuc3dlciIsImEiLCJKYW51c1BsdWdpbkhhbmRsZSIsIlJUQ1BlZXJDb25uZWN0aW9uIiwiYXR0YWNoIiwid2VicnRjdXAiLCJyZWxpYWJsZUNoYW5uZWwiLCJjcmVhdGVEYXRhQ2hhbm5lbCIsIm9yZGVyZWQiLCJ1bnJlbGlhYmxlQ2hhbm5lbCIsIm1heFJldHJhbnNtaXRzIiwiZ2V0VHJhY2tzIiwiZm9yRWFjaCIsImFkZFRyYWNrIiwidHJhY2siLCJwbHVnaW5kYXRhIiwicm9vbV9pZCIsInVzZXJfaWQiLCJib2R5IiwiZGlzcGF0Y2hFdmVudCIsIkN1c3RvbUV2ZW50IiwiZGV0YWlsIiwiYnkiLCJzZW5kSm9pbiIsIm5vdGlmaWNhdGlvbnMiLCJzdWNjZXNzIiwiZXJyIiwicmVzcG9uc2UiLCJ1c2VycyIsImluY2x1ZGVzIiwic2RwIiwicmVwbGFjZSIsImxpbmUiLCJwdCIsInBhcmFtZXRlcnMiLCJhc3NpZ24iLCJwYXJzZUZtdHAiLCJ3cml0ZUZtdHAiLCJwYXlsb2FkVHlwZSIsIm1heFJldHJpZXMiLCJ3ZWJydGNGYWlsZWQiLCJsZWZ0SW50ZXJ2YWwiLCJzZXRJbnRlcnZhbCIsImNsZWFySW50ZXJ2YWwiLCJ0aW1lb3V0IiwibWVkaWEiLCJfaU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlciIsIk1lZGlhU3RyZWFtIiwicmVjZWl2ZXJzIiwiZ2V0UmVjZWl2ZXJzIiwicmVjZWl2ZXIiLCJzdWJzY3JpYmUiLCJzZW5kTWVzc2FnZSIsImtpbmQiLCJ0b2tlbiIsInRvZ2dsZUZyZWV6ZSIsImZyb3plbiIsInVuZnJlZXplIiwiZnJlZXplIiwiZmx1c2hQZW5kaW5nVXBkYXRlcyIsImRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UiLCJuZXR3b3JrSWQiLCJsIiwiZCIsImdldFBlbmRpbmdEYXRhIiwiZGF0YVR5cGUiLCJvd25lciIsImdldFBlbmRpbmdEYXRhRm9yTmV0d29ya0lkIiwic291cmNlIiwic3RvcmVNZXNzYWdlIiwic3RvcmVTaW5nbGVNZXNzYWdlIiwiaW5kZXgiLCJ1bmRlZmluZWQiLCJzZXQiLCJzdG9yZWRNZXNzYWdlIiwic3RvcmVkRGF0YSIsImlzT3V0ZGF0ZWRNZXNzYWdlIiwibGFzdE93bmVyVGltZSIsImlzQ29udGVtcG9yYW5lb3VzTWVzc2FnZSIsImNyZWF0ZWRXaGlsZUZyb3plbiIsImlzRmlyc3RTeW5jIiwiY29tcG9uZW50cyIsImVuYWJsZWQiLCJzaG91bGRTdGFydENvbm5lY3Rpb25UbyIsImNsaWVudCIsInN0YXJ0U3RyZWFtQ29ubmVjdGlvbiIsImNsb3NlU3RyZWFtQ29ubmVjdGlvbiIsImdldENvbm5lY3RTdGF0dXMiLCJhZGFwdGVycyIsIklTX0NPTk5FQ1RFRCIsIk5PVF9DT05ORUNURUQiLCJjbGllbnRTZW50VGltZSIsIkRhdGUiLCJub3ciLCJyZXMiLCJmZXRjaCIsImxvY2F0aW9uIiwiaHJlZiIsIm1ldGhvZCIsImNhY2hlIiwicHJlY2lzaW9uIiwic2VydmVyUmVjZWl2ZWRUaW1lIiwiaGVhZGVycyIsImdldFRpbWUiLCJjbGllbnRSZWNlaXZlZFRpbWUiLCJzZXJ2ZXJUaW1lIiwidGltZU9mZnNldCIsInJlZHVjZSIsImFjYyIsIm9mZnNldCIsImdldFNlcnZlclRpbWUiLCJnZXRNZWRpYVN0cmVhbSIsImF1ZGlvUHJvbWlzZSIsInZpZGVvUHJvbWlzZSIsInByb21pc2UiLCJzdHJlYW0iLCJhdWRpb1N0cmVhbSIsImdldEF1ZGlvVHJhY2tzIiwidmlkZW9TdHJlYW0iLCJnZXRWaWRlb1RyYWNrcyIsInNldExvY2FsTWVkaWFTdHJlYW0iLCJleGlzdGluZ1NlbmRlcnMiLCJnZXRTZW5kZXJzIiwibmV3U2VuZGVycyIsInRyYWNrcyIsInQiLCJzZW5kZXIiLCJmaW5kIiwicyIsInJlcGxhY2VUcmFjayIsInRvTG93ZXJDYXNlIiwicmVtb3ZlVHJhY2siLCJlbmFibGVNaWNyb3Bob25lIiwic2VuZERhdGEiLCJzdHJpbmdpZnkiLCJ3aG9tIiwic2VuZERhdGFHdWFyYW50ZWVkIiwiYnJvYWRjYXN0RGF0YSIsImJyb2FkY2FzdERhdGFHdWFyYW50ZWVkIiwia2ljayIsInBlcm1zVG9rZW4iLCJibG9jayIsInVuYmxvY2siLCJyZWdpc3RlciIsIm1vZHVsZSIsImV4cG9ydHMiXSwibWFwcGluZ3MiOiI7UUFBQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTs7O1FBR0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDBDQUEwQyxnQ0FBZ0M7UUFDMUU7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSx3REFBd0Qsa0JBQWtCO1FBQzFFO1FBQ0EsaURBQWlELGNBQWM7UUFDL0Q7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBLHlDQUF5QyxpQ0FBaUM7UUFDMUUsZ0hBQWdILG1CQUFtQixFQUFFO1FBQ3JJO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMkJBQTJCLDBCQUEwQixFQUFFO1FBQ3ZELGlDQUFpQyxlQUFlO1FBQ2hEO1FBQ0E7UUFDQTs7UUFFQTtRQUNBLHNEQUFzRCwrREFBK0Q7O1FBRXJIO1FBQ0E7OztRQUdBO1FBQ0E7Ozs7Ozs7Ozs7OztBQ2xGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxp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Q0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCLGtCQUFrQixPQUFPO0FBQzFDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw0Q0FBNEM7QUFDNUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1AsS0FBSztBQUNMO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QjtBQUN4QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBLEdBQUc7QUFDSDtBQUNBLEdBQUc7QUFDSDtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCLGtCQUFrQjtBQUNuQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixrQkFBa0I7QUFD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T0FBdEIsRUFBK0J4UixZQUEvQjs7QUFFQXlSLE9BQU9DLE9BQVAsR0FBaUIxUixZQUFqQixDIiwiZmlsZSI6Im5hZi1qYW51cy1hZGFwdGVyLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9zcmMvaW5kZXguanNcIik7XG4iLCIvKipcbiAqIFJlcHJlc2VudHMgYSBoYW5kbGUgdG8gYSBzaW5nbGUgSmFudXMgcGx1Z2luIG9uIGEgSmFudXMgc2Vzc2lvbi4gRWFjaCBXZWJSVEMgY29ubmVjdGlvbiB0byB0aGUgSmFudXMgc2VydmVyIHdpbGwgYmVcbiAqIGFzc29jaWF0ZWQgd2l0aCBhIHNpbmdsZSBoYW5kbGUuIE9uY2UgYXR0YWNoZWQgdG8gdGhlIHNlcnZlciwgdGhpcyBoYW5kbGUgd2lsbCBiZSBnaXZlbiBhIHVuaXF1ZSBJRCB3aGljaCBzaG91bGQgYmVcbiAqIHVzZWQgdG8gYXNzb2NpYXRlIGl0IHdpdGggZnV0dXJlIHNpZ25hbGxpbmcgbWVzc2FnZXMuXG4gKlxuICogU2VlIGh0dHBzOi8vamFudXMuY29uZi5tZWV0ZWNoby5jb20vZG9jcy9yZXN0Lmh0bWwjaGFuZGxlcy5cbiAqKi9cbmZ1bmN0aW9uIEphbnVzUGx1Z2luSGFuZGxlKHNlc3Npb24pIHtcbiAgdGhpcy5zZXNzaW9uID0gc2Vzc2lvbjtcbiAgdGhpcy5pZCA9IHVuZGVmaW5lZDtcbn1cblxuLyoqIEF0dGFjaGVzIHRoaXMgaGFuZGxlIHRvIHRoZSBKYW51cyBzZXJ2ZXIgYW5kIHNldHMgaXRzIElELiAqKi9cbkphbnVzUGx1Z2luSGFuZGxlLnByb3RvdHlwZS5hdHRhY2ggPSBmdW5jdGlvbihwbHVnaW4pIHtcbiAgdmFyIHBheWxvYWQgPSB7IHBsdWdpbjogcGx1Z2luLCBcImZvcmNlLWJ1bmRsZVwiOiB0cnVlLCBcImZvcmNlLXJ0Y3AtbXV4XCI6IHRydWUgfTtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5zZW5kKFwiYXR0YWNoXCIsIHBheWxvYWQpLnRoZW4ocmVzcCA9PiB7XG4gICAgdGhpcy5pZCA9IHJlc3AuZGF0YS5pZDtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKiogRGV0YWNoZXMgdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLmRldGFjaCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwiZGV0YWNoXCIpO1xufTtcblxuLyoqIFJlZ2lzdGVycyBhIGNhbGxiYWNrIHRvIGJlIGZpcmVkIHVwb24gdGhlIHJlY2VwdGlvbiBvZiBhbnkgaW5jb21pbmcgSmFudXMgc2lnbmFscyBmb3IgdGhpcyBwbHVnaW4gaGFuZGxlIHdpdGggdGhlXG4gKiBgamFudXNgIGF0dHJpYnV0ZSBlcXVhbCB0byBgZXZgLlxuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLm9uID0gZnVuY3Rpb24oZXYsIGNhbGxiYWNrKSB7XG4gIHJldHVybiB0aGlzLnNlc3Npb24ub24oZXYsIHNpZ25hbCA9PiB7XG4gICAgaWYgKHNpZ25hbC5zZW5kZXIgPT0gdGhpcy5pZCkge1xuICAgICAgY2FsbGJhY2soc2lnbmFsKTtcbiAgICB9XG4gIH0pO1xufTtcblxuLyoqXG4gKiBTZW5kcyBhIHNpZ25hbCBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuIFNpZ25hbHMgc2hvdWxkIGJlIEpTT04tc2VyaWFsaXphYmxlIG9iamVjdHMuIFJldHVybnMgYSBwcm9taXNlIHRoYXQgd2lsbFxuICogYmUgcmVzb2x2ZWQgb3IgcmVqZWN0ZWQgd2hlbiBhIHJlc3BvbnNlIHRvIHRoaXMgc2lnbmFsIGlzIHJlY2VpdmVkLCBvciB3aGVuIG5vIHJlc3BvbnNlIGlzIHJlY2VpdmVkIHdpdGhpbiB0aGVcbiAqIHNlc3Npb24gdGltZW91dC5cbiAqKi9cbkphbnVzUGx1Z2luSGFuZGxlLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24odHlwZSwgc2lnbmFsKSB7XG4gIHJldHVybiB0aGlzLnNlc3Npb24uc2VuZCh0eXBlLCBPYmplY3QuYXNzaWduKHsgaGFuZGxlX2lkOiB0aGlzLmlkIH0sIHNpZ25hbCkpO1xufTtcblxuLyoqIFNlbmRzIGEgcGx1Z2luLXNwZWNpZmljIG1lc3NhZ2UgYXNzb2NpYXRlZCB3aXRoIHRoaXMgaGFuZGxlLiAqKi9cbkphbnVzUGx1Z2luSGFuZGxlLnByb3RvdHlwZS5zZW5kTWVzc2FnZSA9IGZ1bmN0aW9uKGJvZHkpIHtcbiAgcmV0dXJuIHRoaXMuc2VuZChcIm1lc3NhZ2VcIiwgeyBib2R5OiBib2R5IH0pO1xufTtcblxuLyoqIFNlbmRzIGEgSlNFUCBvZmZlciBvciBhbnN3ZXIgYXNzb2NpYXRlZCB3aXRoIHRoaXMgaGFuZGxlLiAqKi9cbkphbnVzUGx1Z2luSGFuZGxlLnByb3RvdHlwZS5zZW5kSnNlcCA9IGZ1bmN0aW9uKGpzZXApIHtcbiAgcmV0dXJuIHRoaXMuc2VuZChcIm1lc3NhZ2VcIiwgeyBib2R5OiB7fSwganNlcDoganNlcCB9KTtcbn07XG5cbi8qKiBTZW5kcyBhbiBJQ0UgdHJpY2tsZSBjYW5kaWRhdGUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgaGFuZGxlLiAqKi9cbkphbnVzUGx1Z2luSGFuZGxlLnByb3RvdHlwZS5zZW5kVHJpY2tsZSA9IGZ1bmN0aW9uKGNhbmRpZGF0ZSkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwidHJpY2tsZVwiLCB7IGNhbmRpZGF0ZTogY2FuZGlkYXRlIH0pO1xufTtcblxuLyoqXG4gKiBSZXByZXNlbnRzIGEgSmFudXMgc2Vzc2lvbiAtLSBhIEphbnVzIGNvbnRleHQgZnJvbSB3aXRoaW4gd2hpY2ggeW91IGNhbiBvcGVuIG11bHRpcGxlIGhhbmRsZXMgYW5kIGNvbm5lY3Rpb25zLiBPbmNlXG4gKiBjcmVhdGVkLCB0aGlzIHNlc3Npb24gd2lsbCBiZSBnaXZlbiBhIHVuaXF1ZSBJRCB3aGljaCBzaG91bGQgYmUgdXNlZCB0byBhc3NvY2lhdGUgaXQgd2l0aCBmdXR1cmUgc2lnbmFsbGluZyBtZXNzYWdlcy5cbiAqXG4gKiBTZWUgaHR0cHM6Ly9qYW51cy5jb25mLm1lZXRlY2hvLmNvbS9kb2NzL3Jlc3QuaHRtbCNzZXNzaW9ucy5cbiAqKi9cbmZ1bmN0aW9uIEphbnVzU2Vzc2lvbihvdXRwdXQsIG9wdGlvbnMpIHtcbiAgdGhpcy5vdXRwdXQgPSBvdXRwdXQ7XG4gIHRoaXMuaWQgPSB1bmRlZmluZWQ7XG4gIHRoaXMubmV4dFR4SWQgPSAwO1xuICB0aGlzLnR4bnMgPSB7fTtcbiAgdGhpcy5ldmVudEhhbmRsZXJzID0ge307XG4gIHRoaXMub3B0aW9ucyA9IE9iamVjdC5hc3NpZ24oe1xuICAgIHZlcmJvc2U6IGZhbHNlLFxuICAgIHRpbWVvdXRNczogMTAwMDAsXG4gICAga2VlcGFsaXZlTXM6IDMwMDAwXG4gIH0sIG9wdGlvbnMpO1xufVxuXG4vKiogQ3JlYXRlcyB0aGlzIHNlc3Npb24gb24gdGhlIEphbnVzIHNlcnZlciBhbmQgc2V0cyBpdHMgSUQuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5jcmVhdGUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuc2VuZChcImNyZWF0ZVwiKS50aGVuKHJlc3AgPT4ge1xuICAgIHRoaXMuaWQgPSByZXNwLmRhdGEuaWQ7XG4gICAgcmV0dXJuIHJlc3A7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBEZXN0cm95cyB0aGlzIHNlc3Npb24uIE5vdGUgdGhhdCB1cG9uIGRlc3RydWN0aW9uLCBKYW51cyB3aWxsIGFsc28gY2xvc2UgdGhlIHNpZ25hbGxpbmcgdHJhbnNwb3J0IChpZiBhcHBsaWNhYmxlKSBhbmRcbiAqIGFueSBvcGVuIFdlYlJUQyBjb25uZWN0aW9ucy5cbiAqKi9cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuZGVzdHJveSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwiZGVzdHJveVwiKS50aGVuKChyZXNwKSA9PiB7XG4gICAgdGhpcy5kaXNwb3NlKCk7XG4gICAgcmV0dXJuIHJlc3A7XG4gIH0pO1xufTtcblxuLyoqXG4gKiBEaXNwb3NlcyBvZiB0aGlzIHNlc3Npb24gaW4gYSB3YXkgc3VjaCB0aGF0IG5vIGZ1cnRoZXIgaW5jb21pbmcgc2lnbmFsbGluZyBtZXNzYWdlcyB3aWxsIGJlIHByb2Nlc3NlZC5cbiAqIE91dHN0YW5kaW5nIHRyYW5zYWN0aW9ucyB3aWxsIGJlIHJlamVjdGVkLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5kaXNwb3NlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2tpbGxLZWVwYWxpdmUoKTtcbiAgdGhpcy5ldmVudEhhbmRsZXJzID0ge307XG4gIGZvciAodmFyIHR4SWQgaW4gdGhpcy50eG5zKSB7XG4gICAgaWYgKHRoaXMudHhucy5oYXNPd25Qcm9wZXJ0eSh0eElkKSkge1xuICAgICAgdmFyIHR4biA9IHRoaXMudHhuc1t0eElkXTtcbiAgICAgIGNsZWFyVGltZW91dCh0eG4udGltZW91dCk7XG4gICAgICB0eG4ucmVqZWN0KG5ldyBFcnJvcihcIkphbnVzIHNlc3Npb24gd2FzIGRpc3Bvc2VkLlwiKSk7XG4gICAgICBkZWxldGUgdGhpcy50eG5zW3R4SWRdO1xuICAgIH1cbiAgfVxufTtcblxuLyoqXG4gKiBXaGV0aGVyIHRoaXMgc2lnbmFsIHJlcHJlc2VudHMgYW4gZXJyb3IsIGFuZCB0aGUgYXNzb2NpYXRlZCBwcm9taXNlIChpZiBhbnkpIHNob3VsZCBiZSByZWplY3RlZC5cbiAqIFVzZXJzIHNob3VsZCBvdmVycmlkZSB0aGlzIHRvIGhhbmRsZSBhbnkgY3VzdG9tIHBsdWdpbi1zcGVjaWZpYyBlcnJvciBjb252ZW50aW9ucy5cbiAqKi9cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuaXNFcnJvciA9IGZ1bmN0aW9uKHNpZ25hbCkge1xuICByZXR1cm4gc2lnbmFsLmphbnVzID09PSBcImVycm9yXCI7XG59O1xuXG4vKiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgdG8gYmUgZmlyZWQgdXBvbiB0aGUgcmVjZXB0aW9uIG9mIGFueSBpbmNvbWluZyBKYW51cyBzaWduYWxzIGZvciB0aGlzIHNlc3Npb24gd2l0aCB0aGVcbiAqIGBqYW51c2AgYXR0cmlidXRlIGVxdWFsIHRvIGBldmAuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLm9uID0gZnVuY3Rpb24oZXYsIGNhbGxiYWNrKSB7XG4gIHZhciBoYW5kbGVycyA9IHRoaXMuZXZlbnRIYW5kbGVyc1tldl07XG4gIGlmIChoYW5kbGVycyA9PSBudWxsKSB7XG4gICAgaGFuZGxlcnMgPSB0aGlzLmV2ZW50SGFuZGxlcnNbZXZdID0gW107XG4gIH1cbiAgaGFuZGxlcnMucHVzaChjYWxsYmFjayk7XG59O1xuXG4vKipcbiAqIENhbGxiYWNrIGZvciByZWNlaXZpbmcgSlNPTiBzaWduYWxsaW5nIG1lc3NhZ2VzIHBlcnRpbmVudCB0byB0aGlzIHNlc3Npb24uIElmIHRoZSBzaWduYWxzIGFyZSByZXNwb25zZXMgdG8gcHJldmlvdXNseVxuICogc2VudCBzaWduYWxzLCB0aGUgcHJvbWlzZXMgZm9yIHRoZSBvdXRnb2luZyBzaWduYWxzIHdpbGwgYmUgcmVzb2x2ZWQgb3IgcmVqZWN0ZWQgYXBwcm9wcmlhdGVseSB3aXRoIHRoaXMgc2lnbmFsIGFzIGFuXG4gKiBhcmd1bWVudC5cbiAqXG4gKiBFeHRlcm5hbCBjYWxsZXJzIHNob3VsZCBjYWxsIHRoaXMgZnVuY3Rpb24gZXZlcnkgdGltZSBhIG5ldyBzaWduYWwgYXJyaXZlcyBvbiB0aGUgdHJhbnNwb3J0OyBmb3IgZXhhbXBsZSwgaW4gYVxuICogV2ViU29ja2V0J3MgYG1lc3NhZ2VgIGV2ZW50LCBvciB3aGVuIGEgbmV3IGRhdHVtIHNob3dzIHVwIGluIGFuIEhUVFAgbG9uZy1wb2xsaW5nIHJlc3BvbnNlLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5yZWNlaXZlID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIGlmICh0aGlzLm9wdGlvbnMudmVyYm9zZSkge1xuICAgIHRoaXMuX2xvZ0luY29taW5nKHNpZ25hbCk7XG4gIH1cbiAgaWYgKHNpZ25hbC5zZXNzaW9uX2lkICE9IHRoaXMuaWQpIHtcbiAgICBjb25zb2xlLndhcm4oXCJJbmNvcnJlY3Qgc2Vzc2lvbiBJRCByZWNlaXZlZCBpbiBKYW51cyBzaWduYWxsaW5nIG1lc3NhZ2U6IHdhcyBcIiArIHNpZ25hbC5zZXNzaW9uX2lkICsgXCIsIGV4cGVjdGVkIFwiICsgdGhpcy5pZCArIFwiLlwiKTtcbiAgfVxuXG4gIHZhciByZXNwb25zZVR5cGUgPSBzaWduYWwuamFudXM7XG4gIHZhciBoYW5kbGVycyA9IHRoaXMuZXZlbnRIYW5kbGVyc1tyZXNwb25zZVR5cGVdO1xuICBpZiAoaGFuZGxlcnMgIT0gbnVsbCkge1xuICAgIGZvciAodmFyIGkgPSAwOyBpIDwgaGFuZGxlcnMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGhhbmRsZXJzW2ldKHNpZ25hbCk7XG4gICAgfVxuICB9XG5cbiAgaWYgKHNpZ25hbC50cmFuc2FjdGlvbiAhPSBudWxsKSB7XG4gICAgdmFyIHR4biA9IHRoaXMudHhuc1tzaWduYWwudHJhbnNhY3Rpb25dO1xuICAgIGlmICh0eG4gPT0gbnVsbCkge1xuICAgICAgLy8gdGhpcyBpcyBhIHJlc3BvbnNlIHRvIGEgdHJhbnNhY3Rpb24gdGhhdCB3YXNuJ3QgY2F1c2VkIHZpYSBKYW51c1Nlc3Npb24uc2VuZCwgb3IgYSBwbHVnaW4gcmVwbGllZCB0d2ljZSB0byBhXG4gICAgICAvLyBzaW5nbGUgcmVxdWVzdCwgb3IgdGhlIHNlc3Npb24gd2FzIGRpc3Bvc2VkLCBvciBzb21ldGhpbmcgZWxzZSB0aGF0IGlzbid0IHVuZGVyIG91ciBwdXJ2aWV3OyB0aGF0J3MgZmluZVxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmIChyZXNwb25zZVR5cGUgPT09IFwiYWNrXCIgJiYgdHhuLnR5cGUgPT0gXCJtZXNzYWdlXCIpIHtcbiAgICAgIC8vIHRoaXMgaXMgYW4gYWNrIG9mIGFuIGFzeW5jaHJvbm91c2x5LXByb2Nlc3NlZCBwbHVnaW4gcmVxdWVzdCwgd2Ugc2hvdWxkIHdhaXQgdG8gcmVzb2x2ZSB0aGUgcHJvbWlzZSB1bnRpbCB0aGVcbiAgICAgIC8vIGFjdHVhbCByZXNwb25zZSBjb21lcyBpblxuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNsZWFyVGltZW91dCh0eG4udGltZW91dCk7XG5cbiAgICBkZWxldGUgdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl07XG4gICAgKHRoaXMuaXNFcnJvcihzaWduYWwpID8gdHhuLnJlamVjdCA6IHR4bi5yZXNvbHZlKShzaWduYWwpO1xuICB9XG59O1xuXG4vKipcbiAqIFNlbmRzIGEgc2lnbmFsIGFzc29jaWF0ZWQgd2l0aCB0aGlzIHNlc3Npb24sIGJlZ2lubmluZyBhIG5ldyB0cmFuc2FjdGlvbi4gUmV0dXJucyBhIHByb21pc2UgdGhhdCB3aWxsIGJlIHJlc29sdmVkIG9yXG4gKiByZWplY3RlZCB3aGVuIGEgcmVzcG9uc2UgaXMgcmVjZWl2ZWQgaW4gdGhlIHNhbWUgdHJhbnNhY3Rpb24sIG9yIHdoZW4gbm8gcmVzcG9uc2UgaXMgcmVjZWl2ZWQgd2l0aGluIHRoZSBzZXNzaW9uXG4gKiB0aW1lb3V0LlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24odHlwZSwgc2lnbmFsKSB7XG4gIHNpZ25hbCA9IE9iamVjdC5hc3NpZ24oeyB0cmFuc2FjdGlvbjogKHRoaXMubmV4dFR4SWQrKykudG9TdHJpbmcoKSB9LCBzaWduYWwpO1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIHZhciB0aW1lb3V0ID0gbnVsbDtcbiAgICBpZiAodGhpcy5vcHRpb25zLnRpbWVvdXRNcykge1xuICAgICAgdGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICBkZWxldGUgdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl07XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoXCJTaWduYWxsaW5nIHRyYW5zYWN0aW9uIHdpdGggdHhpZCBcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiIHRpbWVkIG91dC5cIikpO1xuICAgICAgfSwgdGhpcy5vcHRpb25zLnRpbWVvdXRNcyk7XG4gICAgfVxuICAgIHRoaXMudHhuc1tzaWduYWwudHJhbnNhY3Rpb25dID0geyByZXNvbHZlOiByZXNvbHZlLCByZWplY3Q6IHJlamVjdCwgdGltZW91dDogdGltZW91dCwgdHlwZTogdHlwZSB9O1xuICAgIHRoaXMuX3RyYW5zbWl0KHR5cGUsIHNpZ25hbCk7XG4gIH0pO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fdHJhbnNtaXQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IGphbnVzOiB0eXBlIH0sIHNpZ25hbCk7XG5cbiAgaWYgKHRoaXMuaWQgIT0gbnVsbCkgeyAvLyB0aGlzLmlkIGlzIHVuZGVmaW5lZCBpbiB0aGUgc3BlY2lhbCBjYXNlIHdoZW4gd2UncmUgc2VuZGluZyB0aGUgc2Vzc2lvbiBjcmVhdGUgbWVzc2FnZVxuICAgIHNpZ25hbCA9IE9iamVjdC5hc3NpZ24oeyBzZXNzaW9uX2lkOiB0aGlzLmlkIH0sIHNpZ25hbCk7XG4gIH1cblxuICBpZiAodGhpcy5vcHRpb25zLnZlcmJvc2UpIHtcbiAgICB0aGlzLl9sb2dPdXRnb2luZyhzaWduYWwpO1xuICB9XG5cbiAgdGhpcy5vdXRwdXQoSlNPTi5zdHJpbmdpZnkoc2lnbmFsKSk7XG4gIHRoaXMuX3Jlc2V0S2VlcGFsaXZlKCk7XG59O1xuXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLl9sb2dPdXRnb2luZyA9IGZ1bmN0aW9uKHNpZ25hbCkge1xuICB2YXIga2luZCA9IHNpZ25hbC5qYW51cztcbiAgaWYgKGtpbmQgPT09IFwibWVzc2FnZVwiICYmIHNpZ25hbC5qc2VwKSB7XG4gICAga2luZCA9IHNpZ25hbC5qc2VwLnR5cGU7XG4gIH1cbiAgdmFyIG1lc3NhZ2UgPSBcIj4gT3V0Z29pbmcgSmFudXMgXCIgKyAoa2luZCB8fCBcInNpZ25hbFwiKSArIFwiICgjXCIgKyBzaWduYWwudHJhbnNhY3Rpb24gKyBcIik6IFwiO1xuICBjb25zb2xlLmRlYnVnKFwiJWNcIiArIG1lc3NhZ2UsIFwiY29sb3I6ICMwNDBcIiwgc2lnbmFsKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX2xvZ0luY29taW5nID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHZhciBraW5kID0gc2lnbmFsLmphbnVzO1xuICB2YXIgbWVzc2FnZSA9IHNpZ25hbC50cmFuc2FjdGlvbiA/XG4gICAgICBcIjwgSW5jb21pbmcgSmFudXMgXCIgKyAoa2luZCB8fCBcInNpZ25hbFwiKSArIFwiICgjXCIgKyBzaWduYWwudHJhbnNhY3Rpb24gKyBcIik6IFwiIDpcbiAgICAgIFwiPCBJbmNvbWluZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCI6IFwiO1xuICBjb25zb2xlLmRlYnVnKFwiJWNcIiArIG1lc3NhZ2UsIFwiY29sb3I6ICMwMDRcIiwgc2lnbmFsKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX3NlbmRLZWVwYWxpdmUgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIHRoaXMuc2VuZChcImtlZXBhbGl2ZVwiKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX2tpbGxLZWVwYWxpdmUgPSBmdW5jdGlvbigpIHtcbiAgY2xlYXJUaW1lb3V0KHRoaXMua2VlcGFsaXZlVGltZW91dCk7XG59O1xuXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLl9yZXNldEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICB0aGlzLl9raWxsS2VlcGFsaXZlKCk7XG4gIGlmICh0aGlzLm9wdGlvbnMua2VlcGFsaXZlTXMpIHtcbiAgICB0aGlzLmtlZXBhbGl2ZVRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuX3NlbmRLZWVwYWxpdmUoKS5jYXRjaChlID0+IGNvbnNvbGUuZXJyb3IoXCJFcnJvciByZWNlaXZlZCBmcm9tIGtlZXBhbGl2ZTogXCIsIGUpKTtcbiAgICB9LCB0aGlzLm9wdGlvbnMua2VlcGFsaXZlTXMpO1xuICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgSmFudXNQbHVnaW5IYW5kbGUsXG4gIEphbnVzU2Vzc2lvblxufTtcbiIsIi8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG4vLyBTRFAgaGVscGVycy5cbnZhciBTRFBVdGlscyA9IHt9O1xuXG4vLyBHZW5lcmF0ZSBhbiBhbHBoYW51bWVyaWMgaWRlbnRpZmllciBmb3IgY25hbWUgb3IgbWlkcy5cbi8vIFRPRE86IHVzZSBVVUlEcyBpbnN0ZWFkPyBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9qZWQvOTgyODgzXG5TRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIgPSBmdW5jdGlvbigpIHtcbiAgcmV0dXJuIE1hdGgucmFuZG9tKCkudG9TdHJpbmcoMzYpLnN1YnN0cigyLCAxMCk7XG59O1xuXG4vLyBUaGUgUlRDUCBDTkFNRSB1c2VkIGJ5IGFsbCBwZWVyY29ubmVjdGlvbnMgZnJvbSB0aGUgc2FtZSBKUy5cblNEUFV0aWxzLmxvY2FsQ05hbWUgPSBTRFBVdGlscy5nZW5lcmF0ZUlkZW50aWZpZXIoKTtcblxuLy8gU3BsaXRzIFNEUCBpbnRvIGxpbmVzLCBkZWFsaW5nIHdpdGggYm90aCBDUkxGIGFuZCBMRi5cblNEUFV0aWxzLnNwbGl0TGluZXMgPSBmdW5jdGlvbihibG9iKSB7XG4gIHJldHVybiBibG9iLnRyaW0oKS5zcGxpdCgnXFxuJykubWFwKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICByZXR1cm4gbGluZS50cmltKCk7XG4gIH0pO1xufTtcbi8vIFNwbGl0cyBTRFAgaW50byBzZXNzaW9ucGFydCBhbmQgbWVkaWFzZWN0aW9ucy4gRW5zdXJlcyBDUkxGLlxuU0RQVXRpbHMuc3BsaXRTZWN0aW9ucyA9IGZ1bmN0aW9uKGJsb2IpIHtcbiAgdmFyIHBhcnRzID0gYmxvYi5zcGxpdCgnXFxubT0nKTtcbiAgcmV0dXJuIHBhcnRzLm1hcChmdW5jdGlvbihwYXJ0LCBpbmRleCkge1xuICAgIHJldHVybiAoaW5kZXggPiAwID8gJ209JyArIHBhcnQgOiBwYXJ0KS50cmltKCkgKyAnXFxyXFxuJztcbiAgfSk7XG59O1xuXG4vLyByZXR1cm5zIHRoZSBzZXNzaW9uIGRlc2NyaXB0aW9uLlxuU0RQVXRpbHMuZ2V0RGVzY3JpcHRpb24gPSBmdW5jdGlvbihibG9iKSB7XG4gIHZhciBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHJldHVybiBzZWN0aW9ucyAmJiBzZWN0aW9uc1swXTtcbn07XG5cbi8vIHJldHVybnMgdGhlIGluZGl2aWR1YWwgbWVkaWEgc2VjdGlvbnMuXG5TRFBVdGlscy5nZXRNZWRpYVNlY3Rpb25zID0gZnVuY3Rpb24oYmxvYikge1xuICB2YXIgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGJsb2IpO1xuICBzZWN0aW9ucy5zaGlmdCgpO1xuICByZXR1cm4gc2VjdGlvbnM7XG59O1xuXG4vLyBSZXR1cm5zIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCBhIGNlcnRhaW4gcHJlZml4LlxuU0RQVXRpbHMubWF0Y2hQcmVmaXggPSBmdW5jdGlvbihibG9iLCBwcmVmaXgpIHtcbiAgcmV0dXJuIFNEUFV0aWxzLnNwbGl0TGluZXMoYmxvYikuZmlsdGVyKGZ1bmN0aW9uKGxpbmUpIHtcbiAgICByZXR1cm4gbGluZS5pbmRleE9mKHByZWZpeCkgPT09IDA7XG4gIH0pO1xufTtcblxuLy8gUGFyc2VzIGFuIElDRSBjYW5kaWRhdGUgbGluZS4gU2FtcGxlIGlucHV0OlxuLy8gY2FuZGlkYXRlOjcwMjc4NjM1MCAyIHVkcCA0MTgxOTkwMiA4LjguOC44IDYwNzY5IHR5cCByZWxheSByYWRkciA4LjguOC44XG4vLyBycG9ydCA1NTk5NlwiXG5TRFBVdGlscy5wYXJzZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgdmFyIHBhcnRzO1xuICAvLyBQYXJzZSBib3RoIHZhcmlhbnRzLlxuICBpZiAobGluZS5pbmRleE9mKCdhPWNhbmRpZGF0ZTonKSA9PT0gMCkge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTIpLnNwbGl0KCcgJyk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMCkuc3BsaXQoJyAnKTtcbiAgfVxuXG4gIHZhciBjYW5kaWRhdGUgPSB7XG4gICAgZm91bmRhdGlvbjogcGFydHNbMF0sXG4gICAgY29tcG9uZW50OiBwYXJzZUludChwYXJ0c1sxXSwgMTApLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXS50b0xvd2VyQ2FzZSgpLFxuICAgIHByaW9yaXR5OiBwYXJzZUludChwYXJ0c1szXSwgMTApLFxuICAgIGlwOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s0XSwgLy8gYWRkcmVzcyBpcyBhbiBhbGlhcyBmb3IgaXAuXG4gICAgcG9ydDogcGFyc2VJbnQocGFydHNbNV0sIDEwKSxcbiAgICAvLyBza2lwIHBhcnRzWzZdID09ICd0eXAnXG4gICAgdHlwZTogcGFydHNbN11cbiAgfTtcblxuICBmb3IgKHZhciBpID0gODsgaSA8IHBhcnRzLmxlbmd0aDsgaSArPSAyKSB7XG4gICAgc3dpdGNoIChwYXJ0c1tpXSkge1xuICAgICAgY2FzZSAncmFkZHInOlxuICAgICAgICBjYW5kaWRhdGUucmVsYXRlZEFkZHJlc3MgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAncnBvcnQnOlxuICAgICAgICBjYW5kaWRhdGUucmVsYXRlZFBvcnQgPSBwYXJzZUludChwYXJ0c1tpICsgMV0sIDEwKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0Y3B0eXBlJzpcbiAgICAgICAgY2FuZGlkYXRlLnRjcFR5cGUgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndWZyYWcnOlxuICAgICAgICBjYW5kaWRhdGUudWZyYWcgPSBwYXJ0c1tpICsgMV07IC8vIGZvciBiYWNrd2FyZCBjb21wYWJpbGl0eS5cbiAgICAgICAgY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gZXh0ZW5zaW9uIGhhbmRsaW5nLCBpbiBwYXJ0aWN1bGFyIHVmcmFnXG4gICAgICAgIGNhbmRpZGF0ZVtwYXJ0c1tpXV0gPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2FuZGlkYXRlO1xufTtcblxuLy8gVHJhbnNsYXRlcyBhIGNhbmRpZGF0ZSBvYmplY3QgaW50byBTRFAgY2FuZGlkYXRlIGF0dHJpYnV0ZS5cblNEUFV0aWxzLndyaXRlQ2FuZGlkYXRlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gIHZhciBzZHAgPSBbXTtcbiAgc2RwLnB1c2goY2FuZGlkYXRlLmZvdW5kYXRpb24pO1xuICBzZHAucHVzaChjYW5kaWRhdGUuY29tcG9uZW50KTtcbiAgc2RwLnB1c2goY2FuZGlkYXRlLnByb3RvY29sLnRvVXBwZXJDYXNlKCkpO1xuICBzZHAucHVzaChjYW5kaWRhdGUucHJpb3JpdHkpO1xuICBzZHAucHVzaChjYW5kaWRhdGUuYWRkcmVzcyB8fCBjYW5kaWRhdGUuaXApO1xuICBzZHAucHVzaChjYW5kaWRhdGUucG9ydCk7XG5cbiAgdmFyIHR5cGUgPSBjYW5kaWRhdGUudHlwZTtcbiAgc2RwLnB1c2goJ3R5cCcpO1xuICBzZHAucHVzaCh0eXBlKTtcbiAgaWYgKHR5cGUgIT09ICdob3N0JyAmJiBjYW5kaWRhdGUucmVsYXRlZEFkZHJlc3MgJiZcbiAgICAgIGNhbmRpZGF0ZS5yZWxhdGVkUG9ydCkge1xuICAgIHNkcC5wdXNoKCdyYWRkcicpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS5yZWxhdGVkQWRkcmVzcyk7XG4gICAgc2RwLnB1c2goJ3Jwb3J0Jyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KTtcbiAgfVxuICBpZiAoY2FuZGlkYXRlLnRjcFR5cGUgJiYgY2FuZGlkYXRlLnByb3RvY29sLnRvTG93ZXJDYXNlKCkgPT09ICd0Y3AnKSB7XG4gICAgc2RwLnB1c2goJ3RjcHR5cGUnKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUudGNwVHlwZSk7XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS51c2VybmFtZUZyYWdtZW50IHx8IGNhbmRpZGF0ZS51ZnJhZykge1xuICAgIHNkcC5wdXNoKCd1ZnJhZycpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS51c2VybmFtZUZyYWdtZW50IHx8IGNhbmRpZGF0ZS51ZnJhZyk7XG4gIH1cbiAgcmV0dXJuICdjYW5kaWRhdGU6JyArIHNkcC5qb2luKCcgJyk7XG59O1xuXG4vLyBQYXJzZXMgYW4gaWNlLW9wdGlvbnMgbGluZSwgcmV0dXJucyBhbiBhcnJheSBvZiBvcHRpb24gdGFncy5cbi8vIGE9aWNlLW9wdGlvbnM6Zm9vIGJhclxuU0RQVXRpbHMucGFyc2VJY2VPcHRpb25zID0gZnVuY3Rpb24obGluZSkge1xuICByZXR1cm4gbGluZS5zdWJzdHIoMTQpLnNwbGl0KCcgJyk7XG59O1xuXG4vLyBQYXJzZXMgYW4gcnRwbWFwIGxpbmUsIHJldHVybnMgUlRDUnRwQ29kZGVjUGFyYW1ldGVycy4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydHBtYXA6MTExIG9wdXMvNDgwMDAvMlxuU0RQVXRpbHMucGFyc2VSdHBNYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIHZhciBwYXJzZWQgPSB7XG4gICAgcGF5bG9hZFR5cGU6IHBhcnNlSW50KHBhcnRzLnNoaWZ0KCksIDEwKSAvLyB3YXM6IGlkXG4gIH07XG5cbiAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuXG4gIHBhcnNlZC5uYW1lID0gcGFydHNbMF07XG4gIHBhcnNlZC5jbG9ja1JhdGUgPSBwYXJzZUludChwYXJ0c1sxXSwgMTApOyAvLyB3YXM6IGNsb2NrcmF0ZVxuICBwYXJzZWQuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgLy8gbGVnYWN5IGFsaWFzLCBnb3QgcmVuYW1lZCBiYWNrIHRvIGNoYW5uZWxzIGluIE9SVEMuXG4gIHBhcnNlZC5udW1DaGFubmVscyA9IHBhcnNlZC5jaGFubmVscztcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlIGFuIGE9cnRwbWFwIGxpbmUgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3Jcbi8vIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwTWFwID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICB2YXIgY2hhbm5lbHMgPSBjb2RlYy5jaGFubmVscyB8fCBjb2RlYy5udW1DaGFubmVscyB8fCAxO1xuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNoYW5uZWxzICE9PSAxID8gJy8nICsgY2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhbiBhPWV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuLy8gYT1leHRtYXA6Mi9zZW5kb25seSB1cm46aWV0ZjpwYXJhbXM6cnRwLWhkcmV4dDp0b2Zmc2V0XG5TRFBVdGlscy5wYXJzZUV4dG1hcCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBpZDogcGFyc2VJbnQocGFydHNbMF0sIDEwKSxcbiAgICBkaXJlY3Rpb246IHBhcnRzWzBdLmluZGV4T2YoJy8nKSA+IDAgPyBwYXJ0c1swXS5zcGxpdCgnLycpWzFdIDogJ3NlbmRyZWN2JyxcbiAgICB1cmk6IHBhcnRzWzFdXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZXMgYT1leHRtYXAgbGluZSBmcm9tIFJUQ1J0cEhlYWRlckV4dGVuc2lvblBhcmFtZXRlcnMgb3Jcbi8vIFJUQ1J0cEhlYWRlckV4dGVuc2lvbi5cblNEUFV0aWxzLndyaXRlRXh0bWFwID0gZnVuY3Rpb24oaGVhZGVyRXh0ZW5zaW9uKSB7XG4gIHJldHVybiAnYT1leHRtYXA6JyArIChoZWFkZXJFeHRlbnNpb24uaWQgfHwgaGVhZGVyRXh0ZW5zaW9uLnByZWZlcnJlZElkKSArXG4gICAgICAoaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvbiAmJiBoZWFkZXJFeHRlbnNpb24uZGlyZWN0aW9uICE9PSAnc2VuZHJlY3YnXG4gICAgICAgID8gJy8nICsgaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvblxuICAgICAgICA6ICcnKSArXG4gICAgICAnICcgKyBoZWFkZXJFeHRlbnNpb24udXJpICsgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgYW4gZnRtcCBsaW5lLCByZXR1cm5zIGRpY3Rpb25hcnkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9Zm10cDo5NiB2YnI9b247Y25nPW9uXG4vLyBBbHNvIGRlYWxzIHdpdGggdmJyPW9uOyBjbmc9b25cblNEUFV0aWxzLnBhcnNlRm10cCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgdmFyIHBhcnNlZCA9IHt9O1xuICB2YXIga3Y7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKGxpbmUuaW5kZXhPZignICcpICsgMSkuc3BsaXQoJzsnKTtcbiAgZm9yICh2YXIgaiA9IDA7IGogPCBwYXJ0cy5sZW5ndGg7IGorKykge1xuICAgIGt2ID0gcGFydHNbal0udHJpbSgpLnNwbGl0KCc9Jyk7XG4gICAgcGFyc2VkW2t2WzBdLnRyaW0oKV0gPSBrdlsxXTtcbiAgfVxuICByZXR1cm4gcGFyc2VkO1xufTtcblxuLy8gR2VuZXJhdGVzIGFuIGE9ZnRtcCBsaW5lIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlRm10cCA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIHZhciBsaW5lID0gJyc7XG4gIHZhciBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgaWYgKGNvZGVjLnBhcmFtZXRlcnMgJiYgT2JqZWN0LmtleXMoY29kZWMucGFyYW1ldGVycykubGVuZ3RoKSB7XG4gICAgdmFyIHBhcmFtcyA9IFtdO1xuICAgIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmZvckVhY2goZnVuY3Rpb24ocGFyYW0pIHtcbiAgICAgIGlmIChjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSkge1xuICAgICAgICBwYXJhbXMucHVzaChwYXJhbSArICc9JyArIGNvZGVjLnBhcmFtZXRlcnNbcGFyYW1dKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHBhcmFtcy5wdXNoKHBhcmFtKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBsaW5lICs9ICdhPWZtdHA6JyArIHB0ICsgJyAnICsgcGFyYW1zLmpvaW4oJzsnKSArICdcXHJcXG4nO1xuICB9XG4gIHJldHVybiBsaW5lO1xufTtcblxuLy8gUGFyc2VzIGFuIHJ0Y3AtZmIgbGluZSwgcmV0dXJucyBSVENQUnRjcEZlZWRiYWNrIG9iamVjdC4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydGNwLWZiOjk4IG5hY2sgcnBzaVxuU0RQVXRpbHMucGFyc2VSdGNwRmIgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKGxpbmUuaW5kZXhPZignICcpICsgMSkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICB0eXBlOiBwYXJ0cy5zaGlmdCgpLFxuICAgIHBhcmFtZXRlcjogcGFydHMuam9pbignICcpXG4gIH07XG59O1xuLy8gR2VuZXJhdGUgYT1ydGNwLWZiIGxpbmVzIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yIFJUQ1J0cENvZGVjUGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRjcEZiID0gZnVuY3Rpb24oY29kZWMpIHtcbiAgdmFyIGxpbmVzID0gJyc7XG4gIHZhciBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgaWYgKGNvZGVjLnJ0Y3BGZWVkYmFjayAmJiBjb2RlYy5ydGNwRmVlZGJhY2subGVuZ3RoKSB7XG4gICAgLy8gRklYTUU6IHNwZWNpYWwgaGFuZGxpbmcgZm9yIHRyci1pbnQ/XG4gICAgY29kZWMucnRjcEZlZWRiYWNrLmZvckVhY2goZnVuY3Rpb24oZmIpIHtcbiAgICAgIGxpbmVzICs9ICdhPXJ0Y3AtZmI6JyArIHB0ICsgJyAnICsgZmIudHlwZSArXG4gICAgICAoZmIucGFyYW1ldGVyICYmIGZiLnBhcmFtZXRlci5sZW5ndGggPyAnICcgKyBmYi5wYXJhbWV0ZXIgOiAnJykgK1xuICAgICAgICAgICdcXHJcXG4nO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBsaW5lcztcbn07XG5cbi8vIFBhcnNlcyBhbiBSRkMgNTU3NiBzc3JjIG1lZGlhIGF0dHJpYnV0ZS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjOjM3MzU5Mjg1NTkgY25hbWU6c29tZXRoaW5nXG5TRFBVdGlscy5wYXJzZVNzcmNNZWRpYSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgdmFyIHNwID0gbGluZS5pbmRleE9mKCcgJyk7XG4gIHZhciBwYXJ0cyA9IHtcbiAgICBzc3JjOiBwYXJzZUludChsaW5lLnN1YnN0cig3LCBzcCAtIDcpLCAxMClcbiAgfTtcbiAgdmFyIGNvbG9uID0gbGluZS5pbmRleE9mKCc6Jywgc3ApO1xuICBpZiAoY29sb24gPiAtMSkge1xuICAgIHBhcnRzLmF0dHJpYnV0ZSA9IGxpbmUuc3Vic3RyKHNwICsgMSwgY29sb24gLSBzcCAtIDEpO1xuICAgIHBhcnRzLnZhbHVlID0gbGluZS5zdWJzdHIoY29sb24gKyAxKTtcbiAgfSBlbHNlIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cihzcCArIDEpO1xuICB9XG4gIHJldHVybiBwYXJ0cztcbn07XG5cblNEUFV0aWxzLnBhcnNlU3NyY0dyb3VwID0gZnVuY3Rpb24obGluZSkge1xuICB2YXIgcGFydHMgPSBsaW5lLnN1YnN0cigxMykuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBzZW1hbnRpY3M6IHBhcnRzLnNoaWZ0KCksXG4gICAgc3NyY3M6IHBhcnRzLm1hcChmdW5jdGlvbihzc3JjKSB7XG4gICAgICByZXR1cm4gcGFyc2VJbnQoc3NyYywgMTApO1xuICAgIH0pXG4gIH07XG59O1xuXG4vLyBFeHRyYWN0cyB0aGUgTUlEIChSRkMgNTg4OCkgZnJvbSBhIG1lZGlhIHNlY3Rpb24uXG4vLyByZXR1cm5zIHRoZSBNSUQgb3IgdW5kZWZpbmVkIGlmIG5vIG1pZCBsaW5lIHdhcyBmb3VuZC5cblNEUFV0aWxzLmdldE1pZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICB2YXIgbWlkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1taWQ6JylbMF07XG4gIGlmIChtaWQpIHtcbiAgICByZXR1cm4gbWlkLnN1YnN0cig2KTtcbiAgfVxufTtcblxuU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoMTQpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgYWxnb3JpdGhtOiBwYXJ0c1swXS50b0xvd2VyQ2FzZSgpLCAvLyBhbGdvcml0aG0gaXMgY2FzZS1zZW5zaXRpdmUgaW4gRWRnZS5cbiAgICB2YWx1ZTogcGFydHNbMV1cbiAgfTtcbn07XG5cbi8vIEV4dHJhY3RzIERUTFMgcGFyYW1ldGVycyBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgZmluZ2VycHJpbnQgbGluZSBhcyBpbnB1dC4gU2VlIGFsc28gZ2V0SWNlUGFyYW1ldGVycy5cblNEUFV0aWxzLmdldER0bHNQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCkge1xuICB2YXIgbGluZXMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1maW5nZXJwcmludDonKTtcbiAgLy8gTm90ZTogYT1zZXR1cCBsaW5lIGlzIGlnbm9yZWQgc2luY2Ugd2UgdXNlIHRoZSAnYXV0bycgcm9sZS5cbiAgLy8gTm90ZTI6ICdhbGdvcml0aG0nIGlzIG5vdCBjYXNlIHNlbnNpdGl2ZSBleGNlcHQgaW4gRWRnZS5cbiAgcmV0dXJuIHtcbiAgICByb2xlOiAnYXV0bycsXG4gICAgZmluZ2VycHJpbnRzOiBsaW5lcy5tYXAoU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludClcbiAgfTtcbn07XG5cbi8vIFNlcmlhbGl6ZXMgRFRMUyBwYXJhbWV0ZXJzIHRvIFNEUC5cblNEUFV0aWxzLndyaXRlRHRsc1BhcmFtZXRlcnMgPSBmdW5jdGlvbihwYXJhbXMsIHNldHVwVHlwZSkge1xuICB2YXIgc2RwID0gJ2E9c2V0dXA6JyArIHNldHVwVHlwZSArICdcXHJcXG4nO1xuICBwYXJhbXMuZmluZ2VycHJpbnRzLmZvckVhY2goZnVuY3Rpb24oZnApIHtcbiAgICBzZHAgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIGZwLmFsZ29yaXRobSArICcgJyArIGZwLnZhbHVlICsgJ1xcclxcbic7XG4gIH0pO1xuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIGE9Y3J5cHRvIGxpbmVzIGludG9cbi8vICAgaHR0cHM6Ly9yYXdnaXQuY29tL2Fib2JhL2VkZ2VydGMvbWFzdGVyL21zb3J0Yy1yczQuaHRtbCNkaWN0aW9uYXJ5LXJ0Y3NydHBzZGVzcGFyYW1ldGVycy1tZW1iZXJzXG5TRFBVdGlscy5wYXJzZUNyeXB0b0xpbmUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdGFnOiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgIGNyeXB0b1N1aXRlOiBwYXJ0c1sxXSxcbiAgICBrZXlQYXJhbXM6IHBhcnRzWzJdLFxuICAgIHNlc3Npb25QYXJhbXM6IHBhcnRzLnNsaWNlKDMpLFxuICB9O1xufTtcblxuU0RQVXRpbHMud3JpdGVDcnlwdG9MaW5lID0gZnVuY3Rpb24ocGFyYW1ldGVycykge1xuICByZXR1cm4gJ2E9Y3J5cHRvOicgKyBwYXJhbWV0ZXJzLnRhZyArICcgJyArXG4gICAgcGFyYW1ldGVycy5jcnlwdG9TdWl0ZSArICcgJyArXG4gICAgKHR5cGVvZiBwYXJhbWV0ZXJzLmtleVBhcmFtcyA9PT0gJ29iamVjdCdcbiAgICAgID8gU0RQVXRpbHMud3JpdGVDcnlwdG9LZXlQYXJhbXMocGFyYW1ldGVycy5rZXlQYXJhbXMpXG4gICAgICA6IHBhcmFtZXRlcnMua2V5UGFyYW1zKSArXG4gICAgKHBhcmFtZXRlcnMuc2Vzc2lvblBhcmFtcyA/ICcgJyArIHBhcmFtZXRlcnMuc2Vzc2lvblBhcmFtcy5qb2luKCcgJykgOiAnJykgK1xuICAgICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIHRoZSBjcnlwdG8ga2V5IHBhcmFtZXRlcnMgaW50b1xuLy8gICBodHRwczovL3Jhd2dpdC5jb20vYWJvYmEvZWRnZXJ0Yy9tYXN0ZXIvbXNvcnRjLXJzNC5odG1sI3J0Y3NydHBrZXlwYXJhbSpcblNEUFV0aWxzLnBhcnNlQ3J5cHRvS2V5UGFyYW1zID0gZnVuY3Rpb24oa2V5UGFyYW1zKSB7XG4gIGlmIChrZXlQYXJhbXMuaW5kZXhPZignaW5saW5lOicpICE9PSAwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgdmFyIHBhcnRzID0ga2V5UGFyYW1zLnN1YnN0cig3KS5zcGxpdCgnfCcpO1xuICByZXR1cm4ge1xuICAgIGtleU1ldGhvZDogJ2lubGluZScsXG4gICAga2V5U2FsdDogcGFydHNbMF0sXG4gICAgbGlmZVRpbWU6IHBhcnRzWzFdLFxuICAgIG1raVZhbHVlOiBwYXJ0c1syXSA/IHBhcnRzWzJdLnNwbGl0KCc6JylbMF0gOiB1bmRlZmluZWQsXG4gICAgbWtpTGVuZ3RoOiBwYXJ0c1syXSA/IHBhcnRzWzJdLnNwbGl0KCc6JylbMV0gOiB1bmRlZmluZWQsXG4gIH07XG59O1xuXG5TRFBVdGlscy53cml0ZUNyeXB0b0tleVBhcmFtcyA9IGZ1bmN0aW9uKGtleVBhcmFtcykge1xuICByZXR1cm4ga2V5UGFyYW1zLmtleU1ldGhvZCArICc6J1xuICAgICsga2V5UGFyYW1zLmtleVNhbHQgK1xuICAgIChrZXlQYXJhbXMubGlmZVRpbWUgPyAnfCcgKyBrZXlQYXJhbXMubGlmZVRpbWUgOiAnJykgK1xuICAgIChrZXlQYXJhbXMubWtpVmFsdWUgJiYga2V5UGFyYW1zLm1raUxlbmd0aFxuICAgICAgPyAnfCcgKyBrZXlQYXJhbXMubWtpVmFsdWUgKyAnOicgKyBrZXlQYXJhbXMubWtpTGVuZ3RoXG4gICAgICA6ICcnKTtcbn07XG5cbi8vIEV4dHJhY3RzIGFsbCBTREVTIHBhcmFtdGVycy5cblNEUFV0aWxzLmdldENyeXB0b1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIHZhciBsaW5lcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiArIHNlc3Npb25wYXJ0LFxuICAgICdhPWNyeXB0bzonKTtcbiAgcmV0dXJuIGxpbmVzLm1hcChTRFBVdGlscy5wYXJzZUNyeXB0b0xpbmUpO1xufTtcblxuLy8gUGFyc2VzIElDRSBpbmZvcm1hdGlvbiBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgaWNlLXVmcmFnIGFuZCBpY2UtcHdkIGxpbmVzIGFzIGlucHV0LlxuU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgdmFyIHVmcmFnID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9aWNlLXVmcmFnOicpWzBdO1xuICB2YXIgcHdkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9aWNlLXB3ZDonKVswXTtcbiAgaWYgKCEodWZyYWcgJiYgcHdkKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiB7XG4gICAgdXNlcm5hbWVGcmFnbWVudDogdWZyYWcuc3Vic3RyKDEyKSxcbiAgICBwYXNzd29yZDogcHdkLnN1YnN0cigxMCksXG4gIH07XG59O1xuXG4vLyBTZXJpYWxpemVzIElDRSBwYXJhbWV0ZXJzIHRvIFNEUC5cblNEUFV0aWxzLndyaXRlSWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHBhcmFtcykge1xuICByZXR1cm4gJ2E9aWNlLXVmcmFnOicgKyBwYXJhbXMudXNlcm5hbWVGcmFnbWVudCArICdcXHJcXG4nICtcbiAgICAgICdhPWljZS1wd2Q6JyArIHBhcmFtcy5wYXNzd29yZCArICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBhbmQgcmV0dXJucyBSVENSdHBQYXJhbWV0ZXJzLlxuU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIHZhciBkZXNjcmlwdGlvbiA9IHtcbiAgICBjb2RlY3M6IFtdLFxuICAgIGhlYWRlckV4dGVuc2lvbnM6IFtdLFxuICAgIGZlY01lY2hhbmlzbXM6IFtdLFxuICAgIHJ0Y3A6IFtdXG4gIH07XG4gIHZhciBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgdmFyIG1saW5lID0gbGluZXNbMF0uc3BsaXQoJyAnKTtcbiAgZm9yICh2YXIgaSA9IDM7IGkgPCBtbGluZS5sZW5ndGg7IGkrKykgeyAvLyBmaW5kIGFsbCBjb2RlY3MgZnJvbSBtbGluZVszLi5dXG4gICAgdmFyIHB0ID0gbWxpbmVbaV07XG4gICAgdmFyIHJ0cG1hcGxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRwbWFwOicgKyBwdCArICcgJylbMF07XG4gICAgaWYgKHJ0cG1hcGxpbmUpIHtcbiAgICAgIHZhciBjb2RlYyA9IFNEUFV0aWxzLnBhcnNlUnRwTWFwKHJ0cG1hcGxpbmUpO1xuICAgICAgdmFyIGZtdHBzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoXG4gICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9Zm10cDonICsgcHQgKyAnICcpO1xuICAgICAgLy8gT25seSB0aGUgZmlyc3QgYT1mbXRwOjxwdD4gaXMgY29uc2lkZXJlZC5cbiAgICAgIGNvZGVjLnBhcmFtZXRlcnMgPSBmbXRwcy5sZW5ndGggPyBTRFBVdGlscy5wYXJzZUZtdHAoZm10cHNbMF0pIDoge307XG4gICAgICBjb2RlYy5ydGNwRmVlZGJhY2sgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgICAgbWVkaWFTZWN0aW9uLCAnYT1ydGNwLWZiOicgKyBwdCArICcgJylcbiAgICAgICAgLm1hcChTRFBVdGlscy5wYXJzZVJ0Y3BGYik7XG4gICAgICBkZXNjcmlwdGlvbi5jb2RlY3MucHVzaChjb2RlYyk7XG4gICAgICAvLyBwYXJzZSBGRUMgbWVjaGFuaXNtcyBmcm9tIHJ0cG1hcCBsaW5lcy5cbiAgICAgIHN3aXRjaCAoY29kZWMubmFtZS50b1VwcGVyQ2FzZSgpKSB7XG4gICAgICAgIGNhc2UgJ1JFRCc6XG4gICAgICAgIGNhc2UgJ1VMUEZFQyc6XG4gICAgICAgICAgZGVzY3JpcHRpb24uZmVjTWVjaGFuaXNtcy5wdXNoKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6IC8vIG9ubHkgUkVEIGFuZCBVTFBGRUMgYXJlIHJlY29nbml6ZWQgYXMgRkVDIG1lY2hhbmlzbXMuXG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9XG4gIFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9ZXh0bWFwOicpLmZvckVhY2goZnVuY3Rpb24obGluZSkge1xuICAgIGRlc2NyaXB0aW9uLmhlYWRlckV4dGVuc2lvbnMucHVzaChTRFBVdGlscy5wYXJzZUV4dG1hcChsaW5lKSk7XG4gIH0pO1xuICAvLyBGSVhNRTogcGFyc2UgcnRjcC5cbiAgcmV0dXJuIGRlc2NyaXB0aW9uO1xufTtcblxuLy8gR2VuZXJhdGVzIHBhcnRzIG9mIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBkZXNjcmliaW5nIHRoZSBjYXBhYmlsaXRpZXMgL1xuLy8gcGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihraW5kLCBjYXBzKSB7XG4gIHZhciBzZHAgPSAnJztcblxuICAvLyBCdWlsZCB0aGUgbWxpbmUuXG4gIHNkcCArPSAnbT0nICsga2luZCArICcgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLmxlbmd0aCA+IDAgPyAnOScgOiAnMCc7IC8vIHJlamVjdCBpZiBubyBjb2RlY3MuXG4gIHNkcCArPSAnIFVEUC9UTFMvUlRQL1NBVlBGICc7XG4gIHNkcCArPSBjYXBzLmNvZGVjcy5tYXAoZnVuY3Rpb24oY29kZWMpIHtcbiAgICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICAgIH1cbiAgICByZXR1cm4gY29kZWMucGF5bG9hZFR5cGU7XG4gIH0pLmpvaW4oJyAnKSArICdcXHJcXG4nO1xuXG4gIHNkcCArPSAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbic7XG4gIHNkcCArPSAnYT1ydGNwOjkgSU4gSVA0IDAuMC4wLjBcXHJcXG4nO1xuXG4gIC8vIEFkZCBhPXJ0cG1hcCBsaW5lcyBmb3IgZWFjaCBjb2RlYy4gQWxzbyBmbXRwIGFuZCBydGNwLWZiLlxuICBjYXBzLmNvZGVjcy5mb3JFYWNoKGZ1bmN0aW9uKGNvZGVjKSB7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlUnRwTWFwKGNvZGVjKTtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVGbXRwKGNvZGVjKTtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVSdGNwRmIoY29kZWMpO1xuICB9KTtcbiAgdmFyIG1heHB0aW1lID0gMDtcbiAgY2Fwcy5jb2RlY3MuZm9yRWFjaChmdW5jdGlvbihjb2RlYykge1xuICAgIGlmIChjb2RlYy5tYXhwdGltZSA+IG1heHB0aW1lKSB7XG4gICAgICBtYXhwdGltZSA9IGNvZGVjLm1heHB0aW1lO1xuICAgIH1cbiAgfSk7XG4gIGlmIChtYXhwdGltZSA+IDApIHtcbiAgICBzZHAgKz0gJ2E9bWF4cHRpbWU6JyArIG1heHB0aW1lICsgJ1xcclxcbic7XG4gIH1cbiAgc2RwICs9ICdhPXJ0Y3AtbXV4XFxyXFxuJztcblxuICBpZiAoY2Fwcy5oZWFkZXJFeHRlbnNpb25zKSB7XG4gICAgY2Fwcy5oZWFkZXJFeHRlbnNpb25zLmZvckVhY2goZnVuY3Rpb24oZXh0ZW5zaW9uKSB7XG4gICAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVFeHRtYXAoZXh0ZW5zaW9uKTtcbiAgICB9KTtcbiAgfVxuICAvLyBGSVhNRTogd3JpdGUgZmVjTWVjaGFuaXNtcy5cbiAgcmV0dXJuIHNkcDtcbn07XG5cbi8vIFBhcnNlcyB0aGUgU0RQIG1lZGlhIHNlY3Rpb24gYW5kIHJldHVybnMgYW4gYXJyYXkgb2Zcbi8vIFJUQ1J0cEVuY29kaW5nUGFyYW1ldGVycy5cblNEUFV0aWxzLnBhcnNlUnRwRW5jb2RpbmdQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIHZhciBlbmNvZGluZ1BhcmFtZXRlcnMgPSBbXTtcbiAgdmFyIGRlc2NyaXB0aW9uID0gU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG4gIHZhciBoYXNSZWQgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1JFRCcpICE9PSAtMTtcbiAgdmFyIGhhc1VscGZlYyA9IGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMuaW5kZXhPZignVUxQRkVDJykgIT09IC0xO1xuXG4gIC8vIGZpbHRlciBhPXNzcmM6Li4uIGNuYW1lOiwgaWdub3JlIFBsYW5CLW1zaWRcbiAgdmFyIHNzcmNzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChmdW5jdGlvbihsaW5lKSB7XG4gICAgICByZXR1cm4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSk7XG4gICAgfSlcbiAgICAuZmlsdGVyKGZ1bmN0aW9uKHBhcnRzKSB7XG4gICAgICByZXR1cm4gcGFydHMuYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgIH0pO1xuICB2YXIgcHJpbWFyeVNzcmMgPSBzc3Jjcy5sZW5ndGggPiAwICYmIHNzcmNzWzBdLnNzcmM7XG4gIHZhciBzZWNvbmRhcnlTc3JjO1xuXG4gIHZhciBmbG93cyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYy1ncm91cDpGSUQnKVxuICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgdmFyIHBhcnRzID0gbGluZS5zdWJzdHIoMTcpLnNwbGl0KCcgJyk7XG4gICAgICByZXR1cm4gcGFydHMubWFwKGZ1bmN0aW9uKHBhcnQpIHtcbiAgICAgICAgcmV0dXJuIHBhcnNlSW50KHBhcnQsIDEwKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICBpZiAoZmxvd3MubGVuZ3RoID4gMCAmJiBmbG93c1swXS5sZW5ndGggPiAxICYmIGZsb3dzWzBdWzBdID09PSBwcmltYXJ5U3NyYykge1xuICAgIHNlY29uZGFyeVNzcmMgPSBmbG93c1swXVsxXTtcbiAgfVxuXG4gIGRlc2NyaXB0aW9uLmNvZGVjcy5mb3JFYWNoKGZ1bmN0aW9uKGNvZGVjKSB7XG4gICAgaWYgKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSA9PT0gJ1JUWCcgJiYgY29kZWMucGFyYW1ldGVycy5hcHQpIHtcbiAgICAgIHZhciBlbmNQYXJhbSA9IHtcbiAgICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgICAgIGNvZGVjUGF5bG9hZFR5cGU6IHBhcnNlSW50KGNvZGVjLnBhcmFtZXRlcnMuYXB0LCAxMClcbiAgICAgIH07XG4gICAgICBpZiAocHJpbWFyeVNzcmMgJiYgc2Vjb25kYXJ5U3NyYykge1xuICAgICAgICBlbmNQYXJhbS5ydHggPSB7c3NyYzogc2Vjb25kYXJ5U3NyY307XG4gICAgICB9XG4gICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICBpZiAoaGFzUmVkKSB7XG4gICAgICAgIGVuY1BhcmFtID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShlbmNQYXJhbSkpO1xuICAgICAgICBlbmNQYXJhbS5mZWMgPSB7XG4gICAgICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgICAgICAgbWVjaGFuaXNtOiBoYXNVbHBmZWMgPyAncmVkK3VscGZlYycgOiAncmVkJ1xuICAgICAgICB9O1xuICAgICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcbiAgaWYgKGVuY29kaW5nUGFyYW1ldGVycy5sZW5ndGggPT09IDAgJiYgcHJpbWFyeVNzcmMpIHtcbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaCh7XG4gICAgICBzc3JjOiBwcmltYXJ5U3NyY1xuICAgIH0pO1xuICB9XG5cbiAgLy8gd2Ugc3VwcG9ydCBib3RoIGI9QVMgYW5kIGI9VElBUyBidXQgaW50ZXJwcmV0IEFTIGFzIFRJQVMuXG4gIHZhciBiYW5kd2lkdGggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdiPScpO1xuICBpZiAoYmFuZHdpZHRoLmxlbmd0aCkge1xuICAgIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1USUFTOicpID09PSAwKSB7XG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyKDcpLCAxMCk7XG4gICAgfSBlbHNlIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1BUzonKSA9PT0gMCkge1xuICAgICAgLy8gdXNlIGZvcm11bGEgZnJvbSBKU0VQIHRvIGNvbnZlcnQgYj1BUyB0byBUSUFTIHZhbHVlLlxuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cig1KSwgMTApICogMTAwMCAqIDAuOTVcbiAgICAgICAgICAtICg1MCAqIDQwICogOCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJhbmR3aWR0aCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgZW5jb2RpbmdQYXJhbWV0ZXJzLmZvckVhY2goZnVuY3Rpb24ocGFyYW1zKSB7XG4gICAgICBwYXJhbXMubWF4Qml0cmF0ZSA9IGJhbmR3aWR0aDtcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gZW5jb2RpbmdQYXJhbWV0ZXJzO1xufTtcblxuLy8gcGFyc2VzIGh0dHA6Ly9kcmFmdC5vcnRjLm9yZy8jcnRjcnRjcHBhcmFtZXRlcnMqXG5TRFBVdGlscy5wYXJzZVJ0Y3BQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIHZhciBydGNwUGFyYW1ldGVycyA9IHt9O1xuXG4gIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhIHdpdGggUlRYIHRoZXJlIG1pZ2h0IGJlIG11bHRpcGxlXG4gIC8vIFNTUkNzLlxuICB2YXIgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgIH0pXG4gICAgLmZpbHRlcihmdW5jdGlvbihvYmopIHtcbiAgICAgIHJldHVybiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnO1xuICAgIH0pWzBdO1xuICBpZiAocmVtb3RlU3NyYykge1xuICAgIHJ0Y3BQYXJhbWV0ZXJzLmNuYW1lID0gcmVtb3RlU3NyYy52YWx1ZTtcbiAgICBydGNwUGFyYW1ldGVycy5zc3JjID0gcmVtb3RlU3NyYy5zc3JjO1xuICB9XG5cbiAgLy8gRWRnZSB1c2VzIHRoZSBjb21wb3VuZCBhdHRyaWJ1dGUgaW5zdGVhZCBvZiByZWR1Y2VkU2l6ZVxuICAvLyBjb21wb3VuZCBpcyAhcmVkdWNlZFNpemVcbiAgdmFyIHJzaXplID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1ydGNwLXJzaXplJyk7XG4gIHJ0Y3BQYXJhbWV0ZXJzLnJlZHVjZWRTaXplID0gcnNpemUubGVuZ3RoID4gMDtcbiAgcnRjcFBhcmFtZXRlcnMuY29tcG91bmQgPSByc2l6ZS5sZW5ndGggPT09IDA7XG5cbiAgLy8gcGFyc2VzIHRoZSBydGNwLW11eCBhdHRy0ZZidXRlLlxuICAvLyBOb3RlIHRoYXQgRWRnZSBkb2VzIG5vdCBzdXBwb3J0IHVubXV4ZWQgUlRDUC5cbiAgdmFyIG11eCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1tdXgnKTtcbiAgcnRjcFBhcmFtZXRlcnMubXV4ID0gbXV4Lmxlbmd0aCA+IDA7XG5cbiAgcmV0dXJuIHJ0Y3BQYXJhbWV0ZXJzO1xufTtcblxuLy8gcGFyc2VzIGVpdGhlciBhPW1zaWQ6IG9yIGE9c3NyYzouLi4gbXNpZCBsaW5lcyBhbmQgcmV0dXJuc1xuLy8gdGhlIGlkIG9mIHRoZSBNZWRpYVN0cmVhbSBhbmQgTWVkaWFTdHJlYW1UcmFjay5cblNEUFV0aWxzLnBhcnNlTXNpZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICB2YXIgcGFydHM7XG4gIHZhciBzcGVjID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1tc2lkOicpO1xuICBpZiAoc3BlYy5sZW5ndGggPT09IDEpIHtcbiAgICBwYXJ0cyA9IHNwZWNbMF0uc3Vic3RyKDcpLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtzdHJlYW06IHBhcnRzWzBdLCB0cmFjazogcGFydHNbMV19O1xuICB9XG4gIHZhciBwbGFuQiA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgIC5tYXAoZnVuY3Rpb24obGluZSkge1xuICAgICAgcmV0dXJuIFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpO1xuICAgIH0pXG4gICAgLmZpbHRlcihmdW5jdGlvbihtc2lkUGFydHMpIHtcbiAgICAgIHJldHVybiBtc2lkUGFydHMuYXR0cmlidXRlID09PSAnbXNpZCc7XG4gICAgfSk7XG4gIGlmIChwbGFuQi5sZW5ndGggPiAwKSB7XG4gICAgcGFydHMgPSBwbGFuQlswXS52YWx1ZS5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7c3RyZWFtOiBwYXJ0c1swXSwgdHJhY2s6IHBhcnRzWzFdfTtcbiAgfVxufTtcblxuLy8gU0NUUFxuLy8gcGFyc2VzIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTI2IGZpcnN0IGFuZCBmYWxscyBiYWNrXG4vLyB0byBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0wNVxuU0RQVXRpbHMucGFyc2VTY3RwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgdmFyIG1saW5lID0gU0RQVXRpbHMucGFyc2VNTGluZShtZWRpYVNlY3Rpb24pO1xuICB2YXIgbWF4U2l6ZUxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1heC1tZXNzYWdlLXNpemU6Jyk7XG4gIHZhciBtYXhNZXNzYWdlU2l6ZTtcbiAgaWYgKG1heFNpemVMaW5lLmxlbmd0aCA+IDApIHtcbiAgICBtYXhNZXNzYWdlU2l6ZSA9IHBhcnNlSW50KG1heFNpemVMaW5lWzBdLnN1YnN0cigxOSksIDEwKTtcbiAgfVxuICBpZiAoaXNOYU4obWF4TWVzc2FnZVNpemUpKSB7XG4gICAgbWF4TWVzc2FnZVNpemUgPSA2NTUzNjtcbiAgfVxuICB2YXIgc2N0cFBvcnQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNjdHAtcG9ydDonKTtcbiAgaWYgKHNjdHBQb3J0Lmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4ge1xuICAgICAgcG9ydDogcGFyc2VJbnQoc2N0cFBvcnRbMF0uc3Vic3RyKDEyKSwgMTApLFxuICAgICAgcHJvdG9jb2w6IG1saW5lLmZtdCxcbiAgICAgIG1heE1lc3NhZ2VTaXplOiBtYXhNZXNzYWdlU2l6ZVxuICAgIH07XG4gIH1cbiAgdmFyIHNjdHBNYXBMaW5lcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c2N0cG1hcDonKTtcbiAgaWYgKHNjdHBNYXBMaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgdmFyIHBhcnRzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zY3RwbWFwOicpWzBdXG4gICAgICAuc3Vic3RyKDEwKVxuICAgICAgLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgICBwcm90b2NvbDogcGFydHNbMV0sXG4gICAgICBtYXhNZXNzYWdlU2l6ZTogbWF4TWVzc2FnZVNpemVcbiAgICB9O1xuICB9XG59O1xuXG4vLyBTQ1RQXG4vLyBvdXRwdXRzIHRoZSBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0yNiB2ZXJzaW9uIHRoYXQgYWxsIGJyb3dzZXJzXG4vLyBzdXBwb3J0IGJ5IG5vdyByZWNlaXZpbmcgaW4gdGhpcyBmb3JtYXQsIHVubGVzcyB3ZSBvcmlnaW5hbGx5IHBhcnNlZFxuLy8gYXMgdGhlIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTA1IGZvcm1hdCAoaW5kaWNhdGVkIGJ5IHRoZSBtLWxpbmVcbi8vIHByb3RvY29sIG9mIERUTFMvU0NUUCAtLSB3aXRob3V0IFVEUC8gb3IgVENQLylcblNEUFV0aWxzLndyaXRlU2N0cERlc2NyaXB0aW9uID0gZnVuY3Rpb24obWVkaWEsIHNjdHApIHtcbiAgdmFyIG91dHB1dCA9IFtdO1xuICBpZiAobWVkaWEucHJvdG9jb2wgIT09ICdEVExTL1NDVFAnKSB7XG4gICAgb3V0cHV0ID0gW1xuICAgICAgJ209JyArIG1lZGlhLmtpbmQgKyAnIDkgJyArIG1lZGlhLnByb3RvY29sICsgJyAnICsgc2N0cC5wcm90b2NvbCArICdcXHJcXG4nLFxuICAgICAgJ2M9SU4gSVA0IDAuMC4wLjBcXHJcXG4nLFxuICAgICAgJ2E9c2N0cC1wb3J0OicgKyBzY3RwLnBvcnQgKyAnXFxyXFxuJ1xuICAgIF07XG4gIH0gZWxzZSB7XG4gICAgb3V0cHV0ID0gW1xuICAgICAgJ209JyArIG1lZGlhLmtpbmQgKyAnIDkgJyArIG1lZGlhLnByb3RvY29sICsgJyAnICsgc2N0cC5wb3J0ICsgJ1xcclxcbicsXG4gICAgICAnYz1JTiBJUDQgMC4wLjAuMFxcclxcbicsXG4gICAgICAnYT1zY3RwbWFwOicgKyBzY3RwLnBvcnQgKyAnICcgKyBzY3RwLnByb3RvY29sICsgJyA2NTUzNVxcclxcbidcbiAgICBdO1xuICB9XG4gIGlmIChzY3RwLm1heE1lc3NhZ2VTaXplICE9PSB1bmRlZmluZWQpIHtcbiAgICBvdXRwdXQucHVzaCgnYT1tYXgtbWVzc2FnZS1zaXplOicgKyBzY3RwLm1heE1lc3NhZ2VTaXplICsgJ1xcclxcbicpO1xuICB9XG4gIHJldHVybiBvdXRwdXQuam9pbignJyk7XG59O1xuXG4vLyBHZW5lcmF0ZSBhIHNlc3Npb24gSUQgZm9yIFNEUC5cbi8vIGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9kcmFmdC1pZXRmLXJ0Y3dlYi1qc2VwLTIwI3NlY3Rpb24tNS4yLjFcbi8vIHJlY29tbWVuZHMgdXNpbmcgYSBjcnlwdG9ncmFwaGljYWxseSByYW5kb20gK3ZlIDY0LWJpdCB2YWx1ZVxuLy8gYnV0IHJpZ2h0IG5vdyB0aGlzIHNob3VsZCBiZSBhY2NlcHRhYmxlIGFuZCB3aXRoaW4gdGhlIHJpZ2h0IHJhbmdlXG5TRFBVdGlscy5nZW5lcmF0ZVNlc3Npb25JZCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygpLnN1YnN0cigyLCAyMSk7XG59O1xuXG4vLyBXcml0ZSBib2lsZGVyIHBsYXRlIGZvciBzdGFydCBvZiBTRFBcbi8vIHNlc3NJZCBhcmd1bWVudCBpcyBvcHRpb25hbCAtIGlmIG5vdCBzdXBwbGllZCBpdCB3aWxsXG4vLyBiZSBnZW5lcmF0ZWQgcmFuZG9tbHlcbi8vIHNlc3NWZXJzaW9uIGlzIG9wdGlvbmFsIGFuZCBkZWZhdWx0cyB0byAyXG4vLyBzZXNzVXNlciBpcyBvcHRpb25hbCBhbmQgZGVmYXVsdHMgdG8gJ3RoaXNpc2FkYXB0ZXJvcnRjJ1xuU0RQVXRpbHMud3JpdGVTZXNzaW9uQm9pbGVycGxhdGUgPSBmdW5jdGlvbihzZXNzSWQsIHNlc3NWZXIsIHNlc3NVc2VyKSB7XG4gIHZhciBzZXNzaW9uSWQ7XG4gIHZhciB2ZXJzaW9uID0gc2Vzc1ZlciAhPT0gdW5kZWZpbmVkID8gc2Vzc1ZlciA6IDI7XG4gIGlmIChzZXNzSWQpIHtcbiAgICBzZXNzaW9uSWQgPSBzZXNzSWQ7XG4gIH0gZWxzZSB7XG4gICAgc2Vzc2lvbklkID0gU0RQVXRpbHMuZ2VuZXJhdGVTZXNzaW9uSWQoKTtcbiAgfVxuICB2YXIgdXNlciA9IHNlc3NVc2VyIHx8ICd0aGlzaXNhZGFwdGVyb3J0Yyc7XG4gIC8vIEZJWE1FOiBzZXNzLWlkIHNob3VsZCBiZSBhbiBOVFAgdGltZXN0YW1wLlxuICByZXR1cm4gJ3Y9MFxcclxcbicgK1xuICAgICAgJ289JyArIHVzZXIgKyAnICcgKyBzZXNzaW9uSWQgKyAnICcgKyB2ZXJzaW9uICtcbiAgICAgICAgJyBJTiBJUDQgMTI3LjAuMC4xXFxyXFxuJyArXG4gICAgICAncz0tXFxyXFxuJyArXG4gICAgICAndD0wIDBcXHJcXG4nO1xufTtcblxuU0RQVXRpbHMud3JpdGVNZWRpYVNlY3Rpb24gPSBmdW5jdGlvbih0cmFuc2NlaXZlciwgY2FwcywgdHlwZSwgc3RyZWFtKSB7XG4gIHZhciBzZHAgPSBTRFBVdGlscy53cml0ZVJ0cERlc2NyaXB0aW9uKHRyYW5zY2VpdmVyLmtpbmQsIGNhcHMpO1xuXG4gIC8vIE1hcCBJQ0UgcGFyYW1ldGVycyAodWZyYWcsIHB3ZCkgdG8gU0RQLlxuICBzZHAgKz0gU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzKFxuICAgIHRyYW5zY2VpdmVyLmljZUdhdGhlcmVyLmdldExvY2FsUGFyYW1ldGVycygpKTtcblxuICAvLyBNYXAgRFRMUyBwYXJhbWV0ZXJzIHRvIFNEUC5cbiAgc2RwICs9IFNEUFV0aWxzLndyaXRlRHRsc1BhcmFtZXRlcnMoXG4gICAgdHJhbnNjZWl2ZXIuZHRsc1RyYW5zcG9ydC5nZXRMb2NhbFBhcmFtZXRlcnMoKSxcbiAgICB0eXBlID09PSAnb2ZmZXInID8gJ2FjdHBhc3MnIDogJ2FjdGl2ZScpO1xuXG4gIHNkcCArPSAnYT1taWQ6JyArIHRyYW5zY2VpdmVyLm1pZCArICdcXHJcXG4nO1xuXG4gIGlmICh0cmFuc2NlaXZlci5kaXJlY3Rpb24pIHtcbiAgICBzZHAgKz0gJ2E9JyArIHRyYW5zY2VpdmVyLmRpcmVjdGlvbiArICdcXHJcXG4nO1xuICB9IGVsc2UgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlciAmJiB0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgIHNkcCArPSAnYT1zZW5kcmVjdlxcclxcbic7XG4gIH0gZWxzZSBpZiAodHJhbnNjZWl2ZXIucnRwU2VuZGVyKSB7XG4gICAgc2RwICs9ICdhPXNlbmRvbmx5XFxyXFxuJztcbiAgfSBlbHNlIGlmICh0cmFuc2NlaXZlci5ydHBSZWNlaXZlcikge1xuICAgIHNkcCArPSAnYT1yZWN2b25seVxcclxcbic7XG4gIH0gZWxzZSB7XG4gICAgc2RwICs9ICdhPWluYWN0aXZlXFxyXFxuJztcbiAgfVxuXG4gIGlmICh0cmFuc2NlaXZlci5ydHBTZW5kZXIpIHtcbiAgICAvLyBzcGVjLlxuICAgIHZhciBtc2lkID0gJ21zaWQ6JyArIHN0cmVhbS5pZCArICcgJyArXG4gICAgICAgIHRyYW5zY2VpdmVyLnJ0cFNlbmRlci50cmFjay5pZCArICdcXHJcXG4nO1xuICAgIHNkcCArPSAnYT0nICsgbXNpZDtcblxuICAgIC8vIGZvciBDaHJvbWUuXG4gICAgc2RwICs9ICdhPXNzcmM6JyArIHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnNbMF0uc3NyYyArXG4gICAgICAgICcgJyArIG1zaWQ7XG4gICAgaWYgKHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnNbMF0ucnR4KSB7XG4gICAgICBzZHAgKz0gJ2E9c3NyYzonICsgdHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVyc1swXS5ydHguc3NyYyArXG4gICAgICAgICAgJyAnICsgbXNpZDtcbiAgICAgIHNkcCArPSAnYT1zc3JjLWdyb3VwOkZJRCAnICtcbiAgICAgICAgICB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnNzcmMgKyAnICcgK1xuICAgICAgICAgIHRyYW5zY2VpdmVyLnNlbmRFbmNvZGluZ1BhcmFtZXRlcnNbMF0ucnR4LnNzcmMgK1xuICAgICAgICAgICdcXHJcXG4nO1xuICAgIH1cbiAgfVxuICAvLyBGSVhNRTogdGhpcyBzaG91bGQgYmUgd3JpdHRlbiBieSB3cml0ZVJ0cERlc2NyaXB0aW9uLlxuICBzZHAgKz0gJ2E9c3NyYzonICsgdHJhbnNjZWl2ZXIuc2VuZEVuY29kaW5nUGFyYW1ldGVyc1swXS5zc3JjICtcbiAgICAgICcgY25hbWU6JyArIFNEUFV0aWxzLmxvY2FsQ05hbWUgKyAnXFxyXFxuJztcbiAgaWYgKHRyYW5zY2VpdmVyLnJ0cFNlbmRlciAmJiB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnJ0eCkge1xuICAgIHNkcCArPSAnYT1zc3JjOicgKyB0cmFuc2NlaXZlci5zZW5kRW5jb2RpbmdQYXJhbWV0ZXJzWzBdLnJ0eC5zc3JjICtcbiAgICAgICAgJyBjbmFtZTonICsgU0RQVXRpbHMubG9jYWxDTmFtZSArICdcXHJcXG4nO1xuICB9XG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBHZXRzIHRoZSBkaXJlY3Rpb24gZnJvbSB0aGUgbWVkaWFTZWN0aW9uIG9yIHRoZSBzZXNzaW9ucGFydC5cblNEUFV0aWxzLmdldERpcmVjdGlvbiA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgLy8gTG9vayBmb3Igc2VuZHJlY3YsIHNlbmRvbmx5LCByZWN2b25seSwgaW5hY3RpdmUsIGRlZmF1bHQgdG8gc2VuZHJlY3YuXG4gIHZhciBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIHN3aXRjaCAobGluZXNbaV0pIHtcbiAgICAgIGNhc2UgJ2E9c2VuZHJlY3YnOlxuICAgICAgY2FzZSAnYT1zZW5kb25seSc6XG4gICAgICBjYXNlICdhPXJlY3Zvbmx5JzpcbiAgICAgIGNhc2UgJ2E9aW5hY3RpdmUnOlxuICAgICAgICByZXR1cm4gbGluZXNbaV0uc3Vic3RyKDIpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gRklYTUU6IFdoYXQgc2hvdWxkIGhhcHBlbiBoZXJlP1xuICAgIH1cbiAgfVxuICBpZiAoc2Vzc2lvbnBhcnQpIHtcbiAgICByZXR1cm4gU0RQVXRpbHMuZ2V0RGlyZWN0aW9uKHNlc3Npb25wYXJ0KTtcbiAgfVxuICByZXR1cm4gJ3NlbmRyZWN2Jztcbn07XG5cblNEUFV0aWxzLmdldEtpbmQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICByZXR1cm4gbWxpbmVbMF0uc3Vic3RyKDIpO1xufTtcblxuU0RQVXRpbHMuaXNSZWplY3RlZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICByZXR1cm4gbWVkaWFTZWN0aW9uLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcbn07XG5cblNEUFV0aWxzLnBhcnNlTUxpbmUgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgdmFyIGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICB2YXIgcGFydHMgPSBsaW5lc1swXS5zdWJzdHIoMikuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBraW5kOiBwYXJ0c1swXSxcbiAgICBwb3J0OiBwYXJzZUludChwYXJ0c1sxXSwgMTApLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXSxcbiAgICBmbXQ6IHBhcnRzLnNsaWNlKDMpLmpvaW4oJyAnKVxuICB9O1xufTtcblxuU0RQVXRpbHMucGFyc2VPTGluZSA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICB2YXIgbGluZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ289JylbMF07XG4gIHZhciBwYXJ0cyA9IGxpbmUuc3Vic3RyKDIpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdXNlcm5hbWU6IHBhcnRzWzBdLFxuICAgIHNlc3Npb25JZDogcGFydHNbMV0sXG4gICAgc2Vzc2lvblZlcnNpb246IHBhcnNlSW50KHBhcnRzWzJdLCAxMCksXG4gICAgbmV0VHlwZTogcGFydHNbM10sXG4gICAgYWRkcmVzc1R5cGU6IHBhcnRzWzRdLFxuICAgIGFkZHJlc3M6IHBhcnRzWzVdXG4gIH07XG59O1xuXG4vLyBhIHZlcnkgbmFpdmUgaW50ZXJwcmV0YXRpb24gb2YgYSB2YWxpZCBTRFAuXG5TRFBVdGlscy5pc1ZhbGlkU0RQID0gZnVuY3Rpb24oYmxvYikge1xuICBpZiAodHlwZW9mIGJsb2IgIT09ICdzdHJpbmcnIHx8IGJsb2IubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG4gIHZhciBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMoYmxvYik7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAobGluZXNbaV0ubGVuZ3RoIDwgMiB8fCBsaW5lc1tpXS5jaGFyQXQoMSkgIT09ICc9Jykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICAvLyBUT0RPOiBjaGVjayB0aGUgbW9kaWZpZXIgYSBiaXQgbW9yZS5cbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbmlmICh0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0Jykge1xuICBtb2R1bGUuZXhwb3J0cyA9IFNEUFV0aWxzO1xufVxuIiwidmFyIG1qID0gcmVxdWlyZShcIm1pbmlqYW51c1wiKTtcbm1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZE9yaWdpbmFsID0gbWouSmFudXNTZXNzaW9uLnByb3RvdHlwZS5zZW5kO1xubWouSmFudXNTZXNzaW9uLnByb3RvdHlwZS5zZW5kID0gZnVuY3Rpb24odHlwZSwgc2lnbmFsKSB7XG4gIHJldHVybiB0aGlzLnNlbmRPcmlnaW5hbCh0eXBlLCBzaWduYWwpLmNhdGNoKChlKSA9PiB7XG4gICAgaWYgKGUubWVzc2FnZSAmJiBlLm1lc3NhZ2UuaW5kZXhPZihcInRpbWVkIG91dFwiKSA+IC0xKSB7XG4gICAgICBjb25zb2xlLmVycm9yKFwid2ViIHNvY2tldCB0aW1lZCBvdXRcIik7XG4gICAgICBOQUYuY29ubmVjdGlvbi5hZGFwdGVyLnJlY29ubmVjdCgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyhlKTtcbiAgICB9XG4gIH0pO1xufVxuXG52YXIgc2RwVXRpbHMgPSByZXF1aXJlKFwic2RwXCIpO1xuLy92YXIgZGVidWcgPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjpkZWJ1Z1wiKTtcbi8vdmFyIHdhcm4gPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjp3YXJuXCIpO1xuLy92YXIgZXJyb3IgPSByZXF1aXJlKFwiZGVidWdcIikoXCJuYWYtamFudXMtYWRhcHRlcjplcnJvclwiKTtcbnZhciBkZWJ1ZyA9IGNvbnNvbGUubG9nO1xudmFyIHdhcm4gPSBjb25zb2xlLndhcm47XG52YXIgZXJyb3IgPSBjb25zb2xlLmVycm9yO1xudmFyIGlzU2FmYXJpID0gL14oKD8hY2hyb21lfGFuZHJvaWQpLikqc2FmYXJpL2kudGVzdChuYXZpZ2F0b3IudXNlckFnZW50KTtcblxuY29uc3QgU1VCU0NSSUJFX1RJTUVPVVRfTVMgPSAxNTAwMDtcblxuZnVuY3Rpb24gZGVib3VuY2UoZm4pIHtcbiAgdmFyIGN1cnIgPSBQcm9taXNlLnJlc29sdmUoKTtcbiAgcmV0dXJuIGZ1bmN0aW9uKCkge1xuICAgIHZhciBhcmdzID0gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoYXJndW1lbnRzKTtcbiAgICBjdXJyID0gY3Vyci50aGVuKF8gPT4gZm4uYXBwbHkodGhpcywgYXJncykpO1xuICB9O1xufVxuXG5mdW5jdGlvbiByYW5kb21VaW50KCkge1xuICByZXR1cm4gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVIpO1xufVxuXG5mdW5jdGlvbiB1bnRpbERhdGFDaGFubmVsT3BlbihkYXRhQ2hhbm5lbCkge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGlmIChkYXRhQ2hhbm5lbC5yZWFkeVN0YXRlID09PSBcIm9wZW5cIikge1xuICAgICAgcmVzb2x2ZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgcmVzb2x2ZXIsIHJlamVjdG9yO1xuXG4gICAgICBjb25zdCBjbGVhciA9ICgpID0+IHtcbiAgICAgICAgZGF0YUNoYW5uZWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgcmVzb2x2ZXIpO1xuICAgICAgICBkYXRhQ2hhbm5lbC5yZW1vdmVFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgcmVqZWN0b3IpO1xuICAgICAgfTtcblxuICAgICAgcmVzb2x2ZXIgPSAoKSA9PiB7XG4gICAgICAgIGNsZWFyKCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH07XG4gICAgICByZWplY3RvciA9ICgpID0+IHtcbiAgICAgICAgY2xlYXIoKTtcbiAgICAgICAgcmVqZWN0KCk7XG4gICAgICB9O1xuXG4gICAgICBkYXRhQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwib3BlblwiLCByZXNvbHZlcik7XG4gICAgICBkYXRhQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwiZXJyb3JcIiwgcmVqZWN0b3IpO1xuICAgIH1cbiAgfSk7XG59XG5cbmNvbnN0IGlzSDI2NFZpZGVvU3VwcG9ydGVkID0gKCgpID0+IHtcbiAgY29uc3QgdmlkZW8gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwidmlkZW9cIik7XG4gIHJldHVybiB2aWRlby5jYW5QbGF5VHlwZSgndmlkZW8vbXA0OyBjb2RlY3M9XCJhdmMxLjQyRTAxRSwgbXA0YS40MC4yXCInKSAhPT0gXCJcIjtcbn0pKCk7XG5cbmNvbnN0IE9QVVNfUEFSQU1FVEVSUyA9IHtcbiAgLy8gaW5kaWNhdGVzIHRoYXQgd2Ugd2FudCB0byBlbmFibGUgRFRYIHRvIGVsaWRlIHNpbGVuY2UgcGFja2V0c1xuICB1c2VkdHg6IDEsXG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHByZWZlciB0byByZWNlaXZlIG1vbm8gYXVkaW8gKGltcG9ydGFudCBmb3Igdm9pcCBwcm9maWxlKVxuICBzdGVyZW86IDAsXG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHByZWZlciB0byBzZW5kIG1vbm8gYXVkaW8gKGltcG9ydGFudCBmb3Igdm9pcCBwcm9maWxlKVxuICBcInNwcm9wLXN0ZXJlb1wiOiAwXG59O1xuXG5jb25zdCBERUZBVUxUX1BFRVJfQ09OTkVDVElPTl9DT05GSUcgPSB7XG4gIGljZVNlcnZlcnM6IFt7IHVybHM6IFwic3R1bjpzdHVuMS5sLmdvb2dsZS5jb206MTkzMDJcIiB9LCB7IHVybHM6IFwic3R1bjpzdHVuMi5sLmdvb2dsZS5jb206MTkzMDJcIiB9XVxufTtcblxuY29uc3QgV1NfTk9STUFMX0NMT1NVUkUgPSAxMDAwO1xuXG5jbGFzcyBKYW51c0FkYXB0ZXIge1xuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLnJvb20gPSBudWxsO1xuICAgIC8vIFdlIGV4cGVjdCB0aGUgY29uc3VtZXIgdG8gc2V0IGEgY2xpZW50IGlkIGJlZm9yZSBjb25uZWN0aW5nLlxuICAgIHRoaXMuY2xpZW50SWQgPSBudWxsO1xuICAgIHRoaXMuam9pblRva2VuID0gbnVsbDtcblxuICAgIHRoaXMuc2VydmVyVXJsID0gbnVsbDtcbiAgICB0aGlzLndlYlJ0Y09wdGlvbnMgPSB7fTtcbiAgICB0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnID0gbnVsbDtcbiAgICB0aGlzLndzID0gbnVsbDtcbiAgICB0aGlzLnNlc3Npb24gPSBudWxsO1xuICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQgPSBcImRhdGFjaGFubmVsXCI7XG4gICAgdGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0ID0gXCJkYXRhY2hhbm5lbFwiO1xuXG4gICAgLy8gSW4gdGhlIGV2ZW50IHRoZSBzZXJ2ZXIgcmVzdGFydHMgYW5kIGFsbCBjbGllbnRzIGxvc2UgY29ubmVjdGlvbiwgcmVjb25uZWN0IHdpdGhcbiAgICAvLyBzb21lIHJhbmRvbSBqaXR0ZXIgYWRkZWQgdG8gcHJldmVudCBzaW11bHRhbmVvdXMgcmVjb25uZWN0aW9uIHJlcXVlc3RzLlxuICAgIHRoaXMuaW5pdGlhbFJlY29ubmVjdGlvbkRlbGF5ID0gMTAwMCAqIE1hdGgucmFuZG9tKCk7XG4gICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSA9IHRoaXMuaW5pdGlhbFJlY29ubmVjdGlvbkRlbGF5O1xuICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IG51bGw7XG4gICAgdGhpcy5tYXhSZWNvbm5lY3Rpb25BdHRlbXB0cyA9IDEwO1xuICAgIHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMgPSAwO1xuXG4gICAgdGhpcy5wdWJsaXNoZXIgPSBudWxsO1xuICAgIHRoaXMub2NjdXBhbnRzID0ge307XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzID0gbmV3IFNldCgpO1xuICAgIHRoaXMubWVkaWFTdHJlYW1zID0ge307XG4gICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtID0gbnVsbDtcbiAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzID0gbmV3IE1hcCgpO1xuXG4gICAgdGhpcy5ibG9ja2VkQ2xpZW50cyA9IG5ldyBNYXAoKTtcbiAgICB0aGlzLmZyb3plblVwZGF0ZXMgPSBuZXcgTWFwKCk7XG5cbiAgICB0aGlzLnRpbWVPZmZzZXRzID0gW107XG4gICAgdGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPSAwO1xuICAgIHRoaXMuYXZnVGltZU9mZnNldCA9IDA7XG5cbiAgICB0aGlzLm9uV2Vic29ja2V0T3BlbiA9IHRoaXMub25XZWJzb2NrZXRPcGVuLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbldlYnNvY2tldENsb3NlID0gdGhpcy5vbldlYnNvY2tldENsb3NlLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UgPSB0aGlzLm9uV2Vic29ja2V0TWVzc2FnZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25EYXRhQ2hhbm5lbE1lc3NhZ2UgPSB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlLmJpbmQodGhpcyk7XG4gICAgdGhpcy5vbkRhdGEgPSB0aGlzLm9uRGF0YS5iaW5kKHRoaXMpO1xuICB9XG5cbiAgc2V0U2VydmVyVXJsKHVybCkge1xuICAgIHRoaXMuc2VydmVyVXJsID0gdXJsO1xuICB9XG5cbiAgc2V0QXBwKGFwcCkge31cblxuICBzZXRSb29tKHJvb21OYW1lKSB7XG4gICAgdGhpcy5yb29tID0gcm9vbU5hbWU7XG4gIH1cblxuICBzZXRKb2luVG9rZW4oam9pblRva2VuKSB7XG4gICAgdGhpcy5qb2luVG9rZW4gPSBqb2luVG9rZW47XG4gIH1cblxuICBzZXRDbGllbnRJZChjbGllbnRJZCkge1xuICAgIHRoaXMuY2xpZW50SWQgPSBjbGllbnRJZDtcbiAgfVxuXG4gIHNldFdlYlJ0Y09wdGlvbnMob3B0aW9ucykge1xuICAgIHRoaXMud2ViUnRjT3B0aW9ucyA9IG9wdGlvbnM7XG4gIH1cblxuICBzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyhwZWVyQ29ubmVjdGlvbkNvbmZpZykge1xuICAgIHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgPSBwZWVyQ29ubmVjdGlvbkNvbmZpZztcbiAgfVxuXG4gIHNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMoc3VjY2Vzc0xpc3RlbmVyLCBmYWlsdXJlTGlzdGVuZXIpIHtcbiAgICB0aGlzLmNvbm5lY3RTdWNjZXNzID0gc3VjY2Vzc0xpc3RlbmVyO1xuICAgIHRoaXMuY29ubmVjdEZhaWx1cmUgPSBmYWlsdXJlTGlzdGVuZXI7XG4gIH1cblxuICBzZXRSb29tT2NjdXBhbnRMaXN0ZW5lcihvY2N1cGFudExpc3RlbmVyKSB7XG4gICAgdGhpcy5vbk9jY3VwYW50c0NoYW5nZWQgPSBvY2N1cGFudExpc3RlbmVyO1xuICB9XG5cbiAgc2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMob3Blbkxpc3RlbmVyLCBjbG9zZWRMaXN0ZW5lciwgbWVzc2FnZUxpc3RlbmVyKSB7XG4gICAgdGhpcy5vbk9jY3VwYW50Q29ubmVjdGVkID0gb3Blbkxpc3RlbmVyO1xuICAgIHRoaXMub25PY2N1cGFudERpc2Nvbm5lY3RlZCA9IGNsb3NlZExpc3RlbmVyO1xuICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UgPSBtZXNzYWdlTGlzdGVuZXI7XG4gIH1cblxuICBzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMocmVjb25uZWN0aW5nTGlzdGVuZXIsIHJlY29ubmVjdGVkTGlzdGVuZXIsIHJlY29ubmVjdGlvbkVycm9yTGlzdGVuZXIpIHtcbiAgICAvLyBvblJlY29ubmVjdGluZyBpcyBjYWxsZWQgd2l0aCB0aGUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcyB1bnRpbCB0aGUgbmV4dCByZWNvbm5lY3Rpb24gYXR0ZW1wdFxuICAgIHRoaXMub25SZWNvbm5lY3RpbmcgPSByZWNvbm5lY3RpbmdMaXN0ZW5lcjtcbiAgICAvLyBvblJlY29ubmVjdGVkIGlzIGNhbGxlZCB3aGVuIHRoZSBjb25uZWN0aW9uIGhhcyBiZWVuIHJlZXN0YWJsaXNoZWRcbiAgICB0aGlzLm9uUmVjb25uZWN0ZWQgPSByZWNvbm5lY3RlZExpc3RlbmVyO1xuICAgIC8vIG9uUmVjb25uZWN0aW9uRXJyb3IgaXMgY2FsbGVkIHdpdGggYW4gZXJyb3Igd2hlbiBtYXhSZWNvbm5lY3Rpb25BdHRlbXB0cyBoYXMgYmVlbiByZWFjaGVkXG4gICAgdGhpcy5vblJlY29ubmVjdGlvbkVycm9yID0gcmVjb25uZWN0aW9uRXJyb3JMaXN0ZW5lcjtcbiAgfVxuXG4gIGNvbm5lY3QoKSB7XG4gICAgZGVidWcoYGNvbm5lY3RpbmcgdG8gJHt0aGlzLnNlcnZlclVybH1gKTtcblxuICAgIGNvbnN0IHdlYnNvY2tldENvbm5lY3Rpb24gPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0aGlzLndzID0gbmV3IFdlYlNvY2tldCh0aGlzLnNlcnZlclVybCwgXCJqYW51cy1wcm90b2NvbFwiKTtcblxuICAgICAgdGhpcy5zZXNzaW9uID0gbmV3IG1qLkphbnVzU2Vzc2lvbih0aGlzLndzLnNlbmQuYmluZCh0aGlzLndzKSwgeyB0aW1lb3V0TXM6IDQwMDAwIH0pO1xuXG4gICAgICB0aGlzLndzLmFkZEV2ZW50TGlzdGVuZXIoXCJjbG9zZVwiLCB0aGlzLm9uV2Vic29ja2V0Q2xvc2UpO1xuICAgICAgdGhpcy53cy5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCB0aGlzLm9uV2Vic29ja2V0TWVzc2FnZSk7XG5cbiAgICAgIHRoaXMud3NPbk9wZW4gPSAoKSA9PiB7XG4gICAgICAgIHRoaXMud3MucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgdGhpcy53c09uT3Blbik7XG4gICAgICAgIHRoaXMub25XZWJzb2NrZXRPcGVuKClcbiAgICAgICAgICAudGhlbihyZXNvbHZlKVxuICAgICAgICAgIC5jYXRjaChyZWplY3QpO1xuICAgICAgfTtcblxuICAgICAgdGhpcy53cy5hZGRFdmVudExpc3RlbmVyKFwib3BlblwiLCB0aGlzLndzT25PcGVuKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBQcm9taXNlLmFsbChbd2Vic29ja2V0Q29ubmVjdGlvbiwgdGhpcy51cGRhdGVUaW1lT2Zmc2V0KCldKTtcbiAgfVxuXG4gIGRpc2Nvbm5lY3QoKSB7XG4gICAgZGVidWcoYGRpc2Nvbm5lY3RpbmdgKTtcblxuICAgIGNsZWFyVGltZW91dCh0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQpO1xuXG4gICAgdGhpcy5yZW1vdmVBbGxPY2N1cGFudHMoKTtcbiAgICB0aGlzLmxlZnRPY2N1cGFudHMgPSBuZXcgU2V0KCk7XG5cbiAgICBpZiAodGhpcy5wdWJsaXNoZXIpIHtcbiAgICAgIC8vIENsb3NlIHRoZSBwdWJsaXNoZXIgcGVlciBjb25uZWN0aW9uLiBXaGljaCBhbHNvIGRldGFjaGVzIHRoZSBwbHVnaW4gaGFuZGxlLlxuICAgICAgdGhpcy5wdWJsaXNoZXIuY29ubi5jbG9zZSgpO1xuICAgICAgdGhpcy5wdWJsaXNoZXIgPSBudWxsO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnNlc3Npb24pIHtcbiAgICAgIHRoaXMuc2Vzc2lvbi5kaXNwb3NlKCk7XG4gICAgICB0aGlzLnNlc3Npb24gPSBudWxsO1xuICAgIH1cblxuICAgIGlmICh0aGlzLndzKSB7XG4gICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHRoaXMud3NPbk9wZW4pO1xuICAgICAgdGhpcy53cy5yZW1vdmVFdmVudExpc3RlbmVyKFwiY2xvc2VcIiwgdGhpcy5vbldlYnNvY2tldENsb3NlKTtcbiAgICAgIHRoaXMud3MucmVtb3ZlRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UpO1xuICAgICAgdGhpcy53cy5jbG9zZSgpO1xuICAgICAgdGhpcy53cyA9IG51bGw7XG4gICAgfVxuXG4gICAgLy8gTm93IHRoYXQgYWxsIFJUQ1BlZXJDb25uZWN0aW9uIGNsb3NlZCwgYmUgc3VyZSB0byBub3QgY2FsbFxuICAgIC8vIHJlY29ubmVjdCgpIGFnYWluIHZpYSBwZXJmb3JtRGVsYXllZFJlY29ubmVjdCBpZiBwcmV2aW91c1xuICAgIC8vIFJUQ1BlZXJDb25uZWN0aW9uIHdhcyBpbiB0aGUgZmFpbGVkIHN0YXRlLlxuICAgIGlmICh0aGlzLmRlbGF5ZWRSZWNvbm5lY3RUaW1lb3V0KSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCk7XG4gICAgICB0aGlzLmRlbGF5ZWRSZWNvbm5lY3RUaW1lb3V0ID0gbnVsbDtcbiAgICB9XG4gIH1cblxuICBpc0Rpc2Nvbm5lY3RlZCgpIHtcbiAgICByZXR1cm4gdGhpcy53cyA9PT0gbnVsbDtcbiAgfVxuXG4gIGFzeW5jIG9uV2Vic29ja2V0T3BlbigpIHtcbiAgICAvLyBDcmVhdGUgdGhlIEphbnVzIFNlc3Npb25cbiAgICBhd2FpdCB0aGlzLnNlc3Npb24uY3JlYXRlKCk7XG5cbiAgICAvLyBBdHRhY2ggdGhlIFNGVSBQbHVnaW4gYW5kIGNyZWF0ZSBhIFJUQ1BlZXJDb25uZWN0aW9uIGZvciB0aGUgcHVibGlzaGVyLlxuICAgIC8vIFRoZSBwdWJsaXNoZXIgc2VuZHMgYXVkaW8gYW5kIG9wZW5zIHR3byBiaWRpcmVjdGlvbmFsIGRhdGEgY2hhbm5lbHMuXG4gICAgLy8gT25lIHJlbGlhYmxlIGRhdGFjaGFubmVsIGFuZCBvbmUgdW5yZWxpYWJsZS5cbiAgICB0aGlzLnB1Ymxpc2hlciA9IGF3YWl0IHRoaXMuY3JlYXRlUHVibGlzaGVyKCk7XG5cbiAgICAvLyBDYWxsIHRoZSBuYWYgY29ubmVjdFN1Y2Nlc3MgY2FsbGJhY2sgYmVmb3JlIHdlIHN0YXJ0IHJlY2VpdmluZyBXZWJSVEMgbWVzc2FnZXMuXG4gICAgdGhpcy5jb25uZWN0U3VjY2Vzcyh0aGlzLmNsaWVudElkKTtcblxuICAgIGNvbnN0IGFkZE9jY3VwYW50UHJvbWlzZXMgPSBbXTtcblxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5wdWJsaXNoZXIuaW5pdGlhbE9jY3VwYW50cy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3Qgb2NjdXBhbnRJZCA9IHRoaXMucHVibGlzaGVyLmluaXRpYWxPY2N1cGFudHNbaV07XG4gICAgICBpZiAob2NjdXBhbnRJZCA9PT0gdGhpcy5jbGllbnRJZCkgY29udGludWU7IC8vIEhhcHBlbnMgZHVyaW5nIG5vbi1ncmFjZWZ1bCByZWNvbm5lY3RzIGR1ZSB0byB6b21iaWUgc2Vzc2lvbnNcbiAgICAgIGFkZE9jY3VwYW50UHJvbWlzZXMucHVzaCh0aGlzLmFkZE9jY3VwYW50KG9jY3VwYW50SWQpKTtcbiAgICB9XG5cbiAgICBhd2FpdCBQcm9taXNlLmFsbChhZGRPY2N1cGFudFByb21pc2VzKTtcbiAgfVxuXG4gIG9uV2Vic29ja2V0Q2xvc2UoZXZlbnQpIHtcbiAgICAvLyBUaGUgY29ubmVjdGlvbiB3YXMgY2xvc2VkIHN1Y2Nlc3NmdWxseS4gRG9uJ3QgdHJ5IHRvIHJlY29ubmVjdC5cbiAgICBpZiAoZXZlbnQuY29kZSA9PT0gV1NfTk9STUFMX0NMT1NVUkUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zb2xlLndhcm4oXCJKYW51cyB3ZWJzb2NrZXQgY2xvc2VkIHVuZXhwZWN0ZWRseS5cIik7XG4gICAgaWYgKHRoaXMub25SZWNvbm5lY3RpbmcpIHtcbiAgICAgIHRoaXMub25SZWNvbm5lY3RpbmcodGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gICAgfVxuXG4gICAgdGhpcy5yZWNvbm5lY3Rpb25UaW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB0aGlzLnJlY29ubmVjdCgpLCB0aGlzLnJlY29ubmVjdGlvbkRlbGF5KTtcbiAgfVxuXG4gIHJlY29ubmVjdCgpIHtcbiAgICAvLyBEaXNwb3NlIG9mIGFsbCBuZXR3b3JrZWQgZW50aXRpZXMgYW5kIG90aGVyIHJlc291cmNlcyB0aWVkIHRvIHRoZSBzZXNzaW9uLlxuICAgIHRoaXMuZGlzY29ubmVjdCgpO1xuXG4gICAgdGhpcy5jb25uZWN0KClcbiAgICAgIC50aGVuKCgpID0+IHtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSA9IHRoaXMuaW5pdGlhbFJlY29ubmVjdGlvbkRlbGF5O1xuICAgICAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzID0gMDtcblxuICAgICAgICBpZiAodGhpcy5vblJlY29ubmVjdGVkKSB7XG4gICAgICAgICAgdGhpcy5vblJlY29ubmVjdGVkKCk7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuY2F0Y2goZXJyb3IgPT4ge1xuICAgICAgICB0aGlzLnJlY29ubmVjdGlvbkRlbGF5ICs9IDEwMDA7XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMrKztcblxuICAgICAgICBpZiAodGhpcy5yZWNvbm5lY3Rpb25BdHRlbXB0cyA+IHRoaXMubWF4UmVjb25uZWN0aW9uQXR0ZW1wdHMgJiYgdGhpcy5vblJlY29ubmVjdGlvbkVycm9yKSB7XG4gICAgICAgICAgcmV0dXJuIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvcihcbiAgICAgICAgICAgIG5ldyBFcnJvcihcIkNvbm5lY3Rpb24gY291bGQgbm90IGJlIHJlZXN0YWJsaXNoZWQsIGV4Y2VlZGVkIG1heGltdW0gbnVtYmVyIG9mIHJlY29ubmVjdGlvbiBhdHRlbXB0cy5cIilcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc29sZS53YXJuKFwiRXJyb3IgZHVyaW5nIHJlY29ubmVjdCwgcmV0cnlpbmcuXCIpO1xuICAgICAgICBjb25zb2xlLndhcm4oZXJyb3IpO1xuXG4gICAgICAgIGlmICh0aGlzLm9uUmVjb25uZWN0aW5nKSB7XG4gICAgICAgICAgdGhpcy5vblJlY29ubmVjdGluZyh0aGlzLnJlY29ubmVjdGlvbkRlbGF5KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWNvbm5lY3QoKSwgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gICAgICB9KTtcbiAgfVxuXG4gIHBlcmZvcm1EZWxheWVkUmVjb25uZWN0KCkge1xuICAgIGlmICh0aGlzLmRlbGF5ZWRSZWNvbm5lY3RUaW1lb3V0KSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCk7XG4gICAgfVxuXG4gICAgdGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCA9IG51bGw7XG4gICAgICB0aGlzLnJlY29ubmVjdCgpO1xuICAgIH0sIDEwMDAwKTtcbiAgfVxuXG4gIG9uV2Vic29ja2V0TWVzc2FnZShldmVudCkge1xuICAgIHRoaXMuc2Vzc2lvbi5yZWNlaXZlKEpTT04ucGFyc2UoZXZlbnQuZGF0YSkpO1xuICB9XG5cbiAgYXN5bmMgYWRkT2NjdXBhbnQob2NjdXBhbnRJZCkge1xuICAgIGlmICh0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXSkge1xuICAgICAgdGhpcy5yZW1vdmVPY2N1cGFudChvY2N1cGFudElkKTtcbiAgICB9XG5cbiAgICB0aGlzLmxlZnRPY2N1cGFudHMuZGVsZXRlKG9jY3VwYW50SWQpO1xuXG4gICAgdmFyIHN1YnNjcmliZXIgPSBhd2FpdCB0aGlzLmNyZWF0ZVN1YnNjcmliZXIob2NjdXBhbnRJZCk7XG5cbiAgICBpZiAoIXN1YnNjcmliZXIpIHJldHVybjtcblxuICAgIHRoaXMub2NjdXBhbnRzW29jY3VwYW50SWRdID0gc3Vic2NyaWJlcjtcblxuICAgIHRoaXMuc2V0TWVkaWFTdHJlYW0ob2NjdXBhbnRJZCwgc3Vic2NyaWJlci5tZWRpYVN0cmVhbSk7XG5cbiAgICAvLyBDYWxsIHRoZSBOZXR3b3JrZWQgQUZyYW1lIGNhbGxiYWNrcyBmb3IgdGhlIG5ldyBvY2N1cGFudC5cbiAgICB0aGlzLm9uT2NjdXBhbnRDb25uZWN0ZWQob2NjdXBhbnRJZCk7XG4gICAgdGhpcy5vbk9jY3VwYW50c0NoYW5nZWQodGhpcy5vY2N1cGFudHMpO1xuXG4gICAgcmV0dXJuIHN1YnNjcmliZXI7XG4gIH1cblxuICByZW1vdmVBbGxPY2N1cGFudHMoKSB7XG4gICAgZm9yIChjb25zdCBvY2N1cGFudElkIG9mIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKHRoaXMub2NjdXBhbnRzKSkge1xuICAgICAgdGhpcy5yZW1vdmVPY2N1cGFudChvY2N1cGFudElkKTtcbiAgICB9XG4gIH1cblxuICByZW1vdmVPY2N1cGFudChvY2N1cGFudElkKSB7XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzLmFkZChvY2N1cGFudElkKTtcblxuICAgIGlmICh0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXSkge1xuICAgICAgLy8gQ2xvc2UgdGhlIHN1YnNjcmliZXIgcGVlciBjb25uZWN0aW9uLiBXaGljaCBhbHNvIGRldGFjaGVzIHRoZSBwbHVnaW4gaGFuZGxlLlxuICAgICAgdGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0uY29ubi5jbG9zZSgpO1xuICAgICAgZGVsZXRlIHRoaXMub2NjdXBhbnRzW29jY3VwYW50SWRdO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm1lZGlhU3RyZWFtc1tvY2N1cGFudElkXSkge1xuICAgICAgZGVsZXRlIHRoaXMubWVkaWFTdHJlYW1zW29jY3VwYW50SWRdO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29uc3QgbXNnID0gXCJUaGUgdXNlciBkaXNjb25uZWN0ZWQgYmVmb3JlIHRoZSBtZWRpYSBzdHJlYW0gd2FzIHJlc29sdmVkLlwiO1xuICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQob2NjdXBhbnRJZCkuYXVkaW8ucmVqZWN0KG1zZyk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChvY2N1cGFudElkKS52aWRlby5yZWplY3QobXNnKTtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZGVsZXRlKG9jY3VwYW50SWQpO1xuICAgIH1cblxuICAgIC8vIENhbGwgdGhlIE5ldHdvcmtlZCBBRnJhbWUgY2FsbGJhY2tzIGZvciB0aGUgcmVtb3ZlZCBvY2N1cGFudC5cbiAgICB0aGlzLm9uT2NjdXBhbnREaXNjb25uZWN0ZWQob2NjdXBhbnRJZCk7XG4gICAgdGhpcy5vbk9jY3VwYW50c0NoYW5nZWQodGhpcy5vY2N1cGFudHMpO1xuICB9XG5cbiAgYXNzb2NpYXRlKGNvbm4sIGhhbmRsZSkge1xuICAgIGNvbm4uYWRkRXZlbnRMaXN0ZW5lcihcImljZWNhbmRpZGF0ZVwiLCBldiA9PiB7XG4gICAgICBoYW5kbGUuc2VuZFRyaWNrbGUoZXYuY2FuZGlkYXRlIHx8IG51bGwpLmNhdGNoKGUgPT4gZXJyb3IoXCJFcnJvciB0cmlja2xpbmcgSUNFOiAlb1wiLCBlKSk7XG4gICAgfSk7XG4gICAgY29ubi5hZGRFdmVudExpc3RlbmVyKFwiaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlXCIsIGV2ID0+IHtcbiAgICAgIGlmIChjb25uLmljZUNvbm5lY3Rpb25TdGF0ZSA9PT0gXCJjb25uZWN0ZWRcIikge1xuICAgICAgICBjb25zb2xlLmxvZyhcIklDRSBzdGF0ZSBjaGFuZ2VkIHRvIGNvbm5lY3RlZFwiKTtcbiAgICAgIH1cbiAgICAgIGlmIChjb25uLmljZUNvbm5lY3Rpb25TdGF0ZSA9PT0gXCJkaXNjb25uZWN0ZWRcIikge1xuICAgICAgICBjb25zb2xlLndhcm4oXCJJQ0Ugc3RhdGUgY2hhbmdlZCB0byBkaXNjb25uZWN0ZWRcIik7XG4gICAgICB9XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiZmFpbGVkXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiSUNFIGZhaWx1cmUgZGV0ZWN0ZWQuIFJlY29ubmVjdGluZyBpbiAxMHMuXCIpO1xuICAgICAgICB0aGlzLnBlcmZvcm1EZWxheWVkUmVjb25uZWN0KCk7XG4gICAgICB9XG4gICAgfSlcblxuICAgIC8vIHdlIGhhdmUgdG8gZGVib3VuY2UgdGhlc2UgYmVjYXVzZSBqYW51cyBnZXRzIGFuZ3J5IGlmIHlvdSBzZW5kIGl0IGEgbmV3IFNEUCBiZWZvcmVcbiAgICAvLyBpdCdzIGZpbmlzaGVkIHByb2Nlc3NpbmcgYW4gZXhpc3RpbmcgU0RQLiBpbiBhY3R1YWxpdHksIGl0IHNlZW1zIGxpa2UgdGhpcyBpcyBtYXliZVxuICAgIC8vIHRvbyBsaWJlcmFsIGFuZCB3ZSBuZWVkIHRvIHdhaXQgc29tZSBhbW91bnQgb2YgdGltZSBhZnRlciBhbiBvZmZlciBiZWZvcmUgc2VuZGluZyBhbm90aGVyLFxuICAgIC8vIGJ1dCB3ZSBkb24ndCBjdXJyZW50bHkga25vdyBhbnkgZ29vZCB3YXkgb2YgZGV0ZWN0aW5nIGV4YWN0bHkgaG93IGxvbmcgOihcbiAgICBjb25uLmFkZEV2ZW50TGlzdGVuZXIoXG4gICAgICBcIm5lZ290aWF0aW9ubmVlZGVkXCIsXG4gICAgICBkZWJvdW5jZShldiA9PiB7XG4gICAgICAgIGRlYnVnKFwiU2VuZGluZyBuZXcgb2ZmZXIgZm9yIGhhbmRsZTogJW9cIiwgaGFuZGxlKTtcbiAgICAgICAgdmFyIG9mZmVyID0gY29ubi5jcmVhdGVPZmZlcigpLnRoZW4odGhpcy5jb25maWd1cmVQdWJsaXNoZXJTZHApLnRoZW4odGhpcy5maXhTYWZhcmlJY2VVRnJhZyk7XG4gICAgICAgIHZhciBsb2NhbCA9IG9mZmVyLnRoZW4obyA9PiBjb25uLnNldExvY2FsRGVzY3JpcHRpb24obykpO1xuICAgICAgICB2YXIgcmVtb3RlID0gb2ZmZXI7XG5cbiAgICAgICAgcmVtb3RlID0gcmVtb3RlXG4gICAgICAgICAgLnRoZW4odGhpcy5maXhTYWZhcmlJY2VVRnJhZylcbiAgICAgICAgICAudGhlbihqID0+IGhhbmRsZS5zZW5kSnNlcChqKSlcbiAgICAgICAgICAudGhlbihyID0+IGNvbm4uc2V0UmVtb3RlRGVzY3JpcHRpb24oci5qc2VwKSk7XG4gICAgICAgIHJldHVybiBQcm9taXNlLmFsbChbbG9jYWwsIHJlbW90ZV0pLmNhdGNoKGUgPT4gZXJyb3IoXCJFcnJvciBuZWdvdGlhdGluZyBvZmZlcjogJW9cIiwgZSkpO1xuICAgICAgfSlcbiAgICApO1xuICAgIGhhbmRsZS5vbihcbiAgICAgIFwiZXZlbnRcIixcbiAgICAgIGRlYm91bmNlKGV2ID0+IHtcbiAgICAgICAgdmFyIGpzZXAgPSBldi5qc2VwO1xuICAgICAgICBpZiAoanNlcCAmJiBqc2VwLnR5cGUgPT0gXCJvZmZlclwiKSB7XG4gICAgICAgICAgZGVidWcoXCJBY2NlcHRpbmcgbmV3IG9mZmVyIGZvciBoYW5kbGU6ICVvXCIsIGhhbmRsZSk7XG4gICAgICAgICAgdmFyIGFuc3dlciA9IGNvbm5cbiAgICAgICAgICAgIC5zZXRSZW1vdGVEZXNjcmlwdGlvbih0aGlzLmNvbmZpZ3VyZVN1YnNjcmliZXJTZHAoanNlcCkpXG4gICAgICAgICAgICAudGhlbihfID0+IGNvbm4uY3JlYXRlQW5zd2VyKCkpXG4gICAgICAgICAgICAudGhlbih0aGlzLmZpeFNhZmFyaUljZVVGcmFnKTtcbiAgICAgICAgICB2YXIgbG9jYWwgPSBhbnN3ZXIudGhlbihhID0+IGNvbm4uc2V0TG9jYWxEZXNjcmlwdGlvbihhKSk7XG4gICAgICAgICAgdmFyIHJlbW90ZSA9IGFuc3dlci50aGVuKGogPT4gaGFuZGxlLnNlbmRKc2VwKGopKTtcbiAgICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoW2xvY2FsLCByZW1vdGVdKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgbmVnb3RpYXRpbmcgYW5zd2VyOiAlb1wiLCBlKSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gc29tZSBvdGhlciBraW5kIG9mIGV2ZW50LCBub3RoaW5nIHRvIGRvXG4gICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVB1Ymxpc2hlcigpIHtcbiAgICB2YXIgaGFuZGxlID0gbmV3IG1qLkphbnVzUGx1Z2luSGFuZGxlKHRoaXMuc2Vzc2lvbik7XG4gICAgdmFyIGNvbm4gPSBuZXcgUlRDUGVlckNvbm5lY3Rpb24odGhpcy5wZWVyQ29ubmVjdGlvbkNvbmZpZyB8fCBERUZBVUxUX1BFRVJfQ09OTkVDVElPTl9DT05GSUcpO1xuXG4gICAgZGVidWcoXCJwdWIgd2FpdGluZyBmb3Igc2Z1XCIpO1xuICAgIGF3YWl0IGhhbmRsZS5hdHRhY2goXCJqYW51cy5wbHVnaW4uc2Z1XCIpO1xuXG4gICAgdGhpcy5hc3NvY2lhdGUoY29ubiwgaGFuZGxlKTtcblxuICAgIGRlYnVnKFwicHViIHdhaXRpbmcgZm9yIGRhdGEgY2hhbm5lbHMgJiB3ZWJydGN1cFwiKTtcbiAgICB2YXIgd2VicnRjdXAgPSBuZXcgUHJvbWlzZShyZXNvbHZlID0+IGhhbmRsZS5vbihcIndlYnJ0Y3VwXCIsIHJlc29sdmUpKTtcblxuICAgIC8vIFVucmVsaWFibGUgZGF0YWNoYW5uZWw6IHNlbmRpbmcgYW5kIHJlY2VpdmluZyBjb21wb25lbnQgdXBkYXRlcy5cbiAgICAvLyBSZWxpYWJsZSBkYXRhY2hhbm5lbDogc2VuZGluZyBhbmQgcmVjaWV2aW5nIGVudGl0eSBpbnN0YW50aWF0aW9ucy5cbiAgICB2YXIgcmVsaWFibGVDaGFubmVsID0gY29ubi5jcmVhdGVEYXRhQ2hhbm5lbChcInJlbGlhYmxlXCIsIHsgb3JkZXJlZDogdHJ1ZSB9KTtcbiAgICB2YXIgdW5yZWxpYWJsZUNoYW5uZWwgPSBjb25uLmNyZWF0ZURhdGFDaGFubmVsKFwidW5yZWxpYWJsZVwiLCB7XG4gICAgICBvcmRlcmVkOiBmYWxzZSxcbiAgICAgIG1heFJldHJhbnNtaXRzOiAwXG4gICAgfSk7XG5cbiAgICByZWxpYWJsZUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgZSA9PiB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlKGUsIFwiamFudXMtcmVsaWFibGVcIikpO1xuICAgIHVucmVsaWFibGVDaGFubmVsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGUgPT4gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZShlLCBcImphbnVzLXVucmVsaWFibGVcIikpO1xuXG4gICAgYXdhaXQgd2VicnRjdXA7XG4gICAgYXdhaXQgdW50aWxEYXRhQ2hhbm5lbE9wZW4ocmVsaWFibGVDaGFubmVsKTtcbiAgICBhd2FpdCB1bnRpbERhdGFDaGFubmVsT3Blbih1bnJlbGlhYmxlQ2hhbm5lbCk7XG5cbiAgICAvLyBkb2luZyB0aGlzIGhlcmUgaXMgc29ydCBvZiBhIGhhY2sgYXJvdW5kIGNocm9tZSByZW5lZ290aWF0aW9uIHdlaXJkbmVzcyAtLVxuICAgIC8vIGlmIHdlIGRvIGl0IHByaW9yIHRvIHdlYnJ0Y3VwLCBjaHJvbWUgb24gZ2VhciBWUiB3aWxsIHNvbWV0aW1lcyBwdXQgYVxuICAgIC8vIHJlbmVnb3RpYXRpb24gb2ZmZXIgaW4gZmxpZ2h0IHdoaWxlIHRoZSBmaXJzdCBvZmZlciB3YXMgc3RpbGwgYmVpbmdcbiAgICAvLyBwcm9jZXNzZWQgYnkgamFudXMuIHdlIHNob3VsZCBmaW5kIHNvbWUgbW9yZSBwcmluY2lwbGVkIHdheSB0byBmaWd1cmUgb3V0XG4gICAgLy8gd2hlbiBqYW51cyBpcyBkb25lIGluIHRoZSBmdXR1cmUuXG4gICAgaWYgKHRoaXMubG9jYWxNZWRpYVN0cmVhbSkge1xuICAgICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2godHJhY2sgPT4ge1xuICAgICAgICBjb25uLmFkZFRyYWNrKHRyYWNrLCB0aGlzLmxvY2FsTWVkaWFTdHJlYW0pO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIGFsbCBvZiB0aGUgam9pbiBhbmQgbGVhdmUgZXZlbnRzLlxuICAgIGhhbmRsZS5vbihcImV2ZW50XCIsIGV2ID0+IHtcbiAgICAgIHZhciBkYXRhID0gZXYucGx1Z2luZGF0YS5kYXRhO1xuICAgICAgaWYgKGRhdGEuZXZlbnQgPT0gXCJqb2luXCIgJiYgZGF0YS5yb29tX2lkID09IHRoaXMucm9vbSkge1xuICAgICAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgICAgIC8vIERvbid0IGNyZWF0ZSBhIG5ldyBSVENQZWVyQ29ubmVjdGlvbiwgYWxsIFJUQ1BlZXJDb25uZWN0aW9uIHdpbGwgYmUgY2xvc2VkIGluIGxlc3MgdGhhbiAxMHMuXG4gICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuYWRkT2NjdXBhbnQoZGF0YS51c2VyX2lkKTtcbiAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudCA9PSBcImxlYXZlXCIgJiYgZGF0YS5yb29tX2lkID09IHRoaXMucm9vbSkge1xuICAgICAgICB0aGlzLnJlbW92ZU9jY3VwYW50KGRhdGEudXNlcl9pZCk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJibG9ja2VkXCIpIHtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGRhdGEuYnkgfSB9KSk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJ1bmJsb2NrZWRcIikge1xuICAgICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwidW5ibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBkYXRhLmJ5IH0gfSkpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09PSBcImRhdGFcIikge1xuICAgICAgICB0aGlzLm9uRGF0YShKU09OLnBhcnNlKGRhdGEuYm9keSksIFwiamFudXMtZXZlbnRcIik7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBkZWJ1ZyhcInB1YiB3YWl0aW5nIGZvciBqb2luXCIpO1xuXG4gICAgLy8gU2VuZCBqb2luIG1lc3NhZ2UgdG8gamFudXMuIExpc3RlbiBmb3Igam9pbi9sZWF2ZSBtZXNzYWdlcy4gQXV0b21hdGljYWxseSBzdWJzY3JpYmUgdG8gYWxsIHVzZXJzJyBXZWJSVEMgZGF0YS5cbiAgICB2YXIgbWVzc2FnZSA9IGF3YWl0IHRoaXMuc2VuZEpvaW4oaGFuZGxlLCB7XG4gICAgICBub3RpZmljYXRpb25zOiB0cnVlLFxuICAgICAgZGF0YTogdHJ1ZVxuICAgIH0pO1xuXG4gICAgaWYgKCFtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5zdWNjZXNzKSB7XG4gICAgICBjb25zdCBlcnIgPSBtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5lcnJvcjtcbiAgICAgIGNvbnNvbGUuZXJyb3IoZXJyKTtcbiAgICAgIC8vIFdlIG1heSBnZXQgaGVyZSBiZWNhdXNlIG9mIGFuIGV4cGlyZWQgSldULlxuICAgICAgLy8gQ2xvc2UgdGhlIGNvbm5lY3Rpb24gb3Vyc2VsZiBvdGhlcndpc2UgamFudXMgd2lsbCBjbG9zZSBpdCBhZnRlclxuICAgICAgLy8gc2Vzc2lvbl90aW1lb3V0IGJlY2F1c2Ugd2UgZGlkbid0IHNlbmQgYW55IGtlZXBhbGl2ZSBhbmQgdGhpcyB3aWxsXG4gICAgICAvLyB0cmlnZ2VyIGEgZGVsYXllZCByZWNvbm5lY3QgYmVjYXVzZSBvZiB0aGUgaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlXG4gICAgICAvLyBsaXN0ZW5lciBmb3IgZmFpbHVyZSBzdGF0ZS5cbiAgICAgIC8vIEV2ZW4gaWYgdGhlIGFwcCBjb2RlIGNhbGxzIGRpc2Nvbm5lY3QgaW4gY2FzZSBvZiBlcnJvciwgZGlzY29ubmVjdFxuICAgICAgLy8gd29uJ3QgY2xvc2UgdGhlIHBlZXIgY29ubmVjdGlvbiBiZWNhdXNlIHRoaXMucHVibGlzaGVyIGlzIG5vdCBzZXQuXG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuXG4gICAgdmFyIGluaXRpYWxPY2N1cGFudHMgPSBtZXNzYWdlLnBsdWdpbmRhdGEuZGF0YS5yZXNwb25zZS51c2Vyc1t0aGlzLnJvb21dIHx8IFtdO1xuXG4gICAgaWYgKGluaXRpYWxPY2N1cGFudHMuaW5jbHVkZXModGhpcy5jbGllbnRJZCkpIHtcbiAgICAgIGNvbnNvbGUud2FybihcIkphbnVzIHN0aWxsIGhhcyBwcmV2aW91cyBzZXNzaW9uIGZvciB0aGlzIGNsaWVudC4gUmVjb25uZWN0aW5nIGluIDEwcy5cIik7XG4gICAgICB0aGlzLnBlcmZvcm1EZWxheWVkUmVjb25uZWN0KCk7XG4gICAgfVxuXG4gICAgZGVidWcoXCJwdWJsaXNoZXIgcmVhZHlcIik7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhhbmRsZSxcbiAgICAgIGluaXRpYWxPY2N1cGFudHMsXG4gICAgICByZWxpYWJsZUNoYW5uZWwsXG4gICAgICB1bnJlbGlhYmxlQ2hhbm5lbCxcbiAgICAgIGNvbm5cbiAgICB9O1xuICB9XG5cbiAgY29uZmlndXJlUHVibGlzaGVyU2RwKGpzZXApIHtcbiAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL2E9Zm10cDooMTA5fDExMSkuKlxcclxcbi9nLCAobGluZSwgcHQpID0+IHtcbiAgICAgIGNvbnN0IHBhcmFtZXRlcnMgPSBPYmplY3QuYXNzaWduKHNkcFV0aWxzLnBhcnNlRm10cChsaW5lKSwgT1BVU19QQVJBTUVURVJTKTtcbiAgICAgIHJldHVybiBzZHBVdGlscy53cml0ZUZtdHAoeyBwYXlsb2FkVHlwZTogcHQsIHBhcmFtZXRlcnM6IHBhcmFtZXRlcnMgfSk7XG4gICAgfSk7XG4gICAgcmV0dXJuIGpzZXA7XG4gIH1cblxuICBjb25maWd1cmVTdWJzY3JpYmVyU2RwKGpzZXApIHtcbiAgICAvLyB0b2RvOiBjb25zaWRlciBjbGVhbmluZyB1cCB0aGVzZSBoYWNrcyB0byB1c2Ugc2RwdXRpbHNcbiAgICBpZiAoIWlzSDI2NFZpZGVvU3VwcG9ydGVkKSB7XG4gICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKFwiSGVhZGxlc3NDaHJvbWVcIikgIT09IC0xKSB7XG4gICAgICAgIC8vIEhlYWRsZXNzQ2hyb21lIChlLmcuIHB1cHBldGVlcikgZG9lc24ndCBzdXBwb3J0IHdlYnJ0YyB2aWRlbyBzdHJlYW1zLCBzbyB3ZSByZW1vdmUgdGhvc2UgbGluZXMgZnJvbSB0aGUgU0RQLlxuICAgICAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL209dmlkZW9bXl0qbT0vLCBcIm09XCIpO1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFRPRE86IEhhY2sgdG8gZ2V0IHZpZGVvIHdvcmtpbmcgb24gQ2hyb21lIGZvciBBbmRyb2lkLiBodHRwczovL2dyb3Vwcy5nb29nbGUuY29tL2ZvcnVtLyMhdG9waWMvbW96aWxsYS5kZXYubWVkaWEvWWUyOXZ1TVRwbzhcbiAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5pbmRleE9mKFwiQW5kcm9pZFwiKSA9PT0gLTEpIHtcbiAgICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZShcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcblwiLFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuYT1ydGNwLWZiOjEwNyB0cmFuc3BvcnQtY2NcXHJcXG5hPWZtdHA6MTA3IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MmUwMWZcXHJcXG5cIlxuICAgICAgKTtcbiAgICB9IGVsc2Uge1xuICAgICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuXCIsXG4gICAgICAgIFwiYT1ydGNwLWZiOjEwNyBnb29nLXJlbWJcXHJcXG5hPXJ0Y3AtZmI6MTA3IHRyYW5zcG9ydC1jY1xcclxcbmE9Zm10cDoxMDcgbGV2ZWwtYXN5bW1ldHJ5LWFsbG93ZWQ9MTtwYWNrZXRpemF0aW9uLW1vZGU9MTtwcm9maWxlLWxldmVsLWlkPTQyMDAxZlxcclxcblwiXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4ganNlcDtcbiAgfVxuXG4gIGFzeW5jIGZpeFNhZmFyaUljZVVGcmFnKGpzZXApIHtcbiAgICAvLyBTYWZhcmkgcHJvZHVjZXMgYSBcXG4gaW5zdGVhZCBvZiBhbiBcXHJcXG4gZm9yIHRoZSBpY2UtdWZyYWcuIFNlZSBodHRwczovL2dpdGh1Yi5jb20vbWVldGVjaG8vamFudXMtZ2F0ZXdheS9pc3N1ZXMvMTgxOFxuICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZSgvW15cXHJdXFxuYT1pY2UtdWZyYWcvZywgXCJcXHJcXG5hPWljZS11ZnJhZ1wiKTtcbiAgICByZXR1cm4ganNlcFxuICB9XG5cbiAgYXN5bmMgY3JlYXRlU3Vic2NyaWJlcihvY2N1cGFudElkLCBtYXhSZXRyaWVzID0gNSkge1xuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBiZWZvcmUgc3Vic2NyaXB0aW9uIG5lZ290YXRpb24uXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgdmFyIGhhbmRsZSA9IG5ldyBtai5KYW51c1BsdWdpbkhhbmRsZSh0aGlzLnNlc3Npb24pO1xuICAgIHZhciBjb25uID0gbmV3IFJUQ1BlZXJDb25uZWN0aW9uKHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgfHwgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHKTtcblxuICAgIGRlYnVnKG9jY3VwYW50SWQgKyBcIjogc3ViIHdhaXRpbmcgZm9yIHNmdVwiKTtcbiAgICBhd2FpdCBoYW5kbGUuYXR0YWNoKFwiamFudXMucGx1Z2luLnNmdVwiKTtcblxuICAgIHRoaXMuYXNzb2NpYXRlKGNvbm4sIGhhbmRsZSk7XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YiB3YWl0aW5nIGZvciBqb2luXCIpO1xuXG4gICAgaWYgKHRoaXMubGVmdE9jY3VwYW50cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IGNhbmNlbGxlZCBvY2N1cGFudCBjb25uZWN0aW9uLCBvY2N1cGFudCBsZWZ0IGFmdGVyIGF0dGFjaFwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGxldCB3ZWJydGNGYWlsZWQgPSBmYWxzZTtcblxuICAgIGNvbnN0IHdlYnJ0Y3VwID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiB7XG4gICAgICBjb25zdCBsZWZ0SW50ZXJ2YWwgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgfVxuICAgICAgfSwgMTAwMCk7XG5cbiAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICB3ZWJydGNGYWlsZWQgPSB0cnVlO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9LCBTVUJTQ1JJQkVfVElNRU9VVF9NUyk7XG5cbiAgICAgIGhhbmRsZS5vbihcIndlYnJ0Y3VwXCIsICgpID0+IHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgICAgICBjbGVhckludGVydmFsKGxlZnRJbnRlcnZhbCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuXG4gICAgLy8gU2VuZCBqb2luIG1lc3NhZ2UgdG8gamFudXMuIERvbid0IGxpc3RlbiBmb3Igam9pbi9sZWF2ZSBtZXNzYWdlcy4gU3Vic2NyaWJlIHRvIHRoZSBvY2N1cGFudCdzIG1lZGlhLlxuICAgIC8vIEphbnVzIHNob3VsZCBzZW5kIHVzIGFuIG9mZmVyIGZvciB0aGlzIG9jY3VwYW50J3MgbWVkaWEgaW4gcmVzcG9uc2UgdG8gdGhpcy5cbiAgICBhd2FpdCB0aGlzLnNlbmRKb2luKGhhbmRsZSwgeyBtZWRpYTogb2NjdXBhbnRJZCB9KTtcblxuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBhZnRlciBqb2luXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWIgd2FpdGluZyBmb3Igd2VicnRjdXBcIik7XG4gICAgYXdhaXQgd2VicnRjdXA7XG5cbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgZHVyaW5nIG9yIGFmdGVyIHdlYnJ0Y3VwXCIpO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgaWYgKHdlYnJ0Y0ZhaWxlZCkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgaWYgKG1heFJldHJpZXMgPiAwKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IHdlYnJ0YyB1cCB0aW1lZCBvdXQsIHJldHJ5aW5nXCIpO1xuICAgICAgICByZXR1cm4gdGhpcy5jcmVhdGVTdWJzY3JpYmVyKG9jY3VwYW50SWQsIG1heFJldHJpZXMgLSAxKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IHdlYnJ0YyB1cCB0aW1lZCBvdXRcIik7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmIChpc1NhZmFyaSAmJiAhdGhpcy5faU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlcikge1xuICAgICAgLy8gSEFDSzogdGhlIGZpcnN0IHBlZXIgb24gU2FmYXJpIGR1cmluZyBwYWdlIGxvYWQgY2FuIGZhaWwgdG8gd29yayBpZiB3ZSBkb24ndFxuICAgICAgLy8gd2FpdCBzb21lIHRpbWUgYmVmb3JlIGNvbnRpbnVpbmcgaGVyZS4gU2VlOiBodHRwczovL2dpdGh1Yi5jb20vbW96aWxsYS9odWJzL3B1bGwvMTY5MlxuICAgICAgYXdhaXQgKG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIDMwMDApKSk7XG4gICAgICB0aGlzLl9pT1NIYWNrRGVsYXllZEluaXRpYWxQZWVyID0gdHJ1ZTtcbiAgICB9XG5cbiAgICB2YXIgbWVkaWFTdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICB2YXIgcmVjZWl2ZXJzID0gY29ubi5nZXRSZWNlaXZlcnMoKTtcbiAgICByZWNlaXZlcnMuZm9yRWFjaChyZWNlaXZlciA9PiB7XG4gICAgICBpZiAocmVjZWl2ZXIudHJhY2spIHtcbiAgICAgICAgbWVkaWFTdHJlYW0uYWRkVHJhY2socmVjZWl2ZXIudHJhY2spO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGlmIChtZWRpYVN0cmVhbS5nZXRUcmFja3MoKS5sZW5ndGggPT09IDApIHtcbiAgICAgIG1lZGlhU3RyZWFtID0gbnVsbDtcbiAgICB9XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YnNjcmliZXIgcmVhZHlcIik7XG4gICAgcmV0dXJuIHtcbiAgICAgIGhhbmRsZSxcbiAgICAgIG1lZGlhU3RyZWFtLFxuICAgICAgY29ublxuICAgIH07XG4gIH1cblxuICBzZW5kSm9pbihoYW5kbGUsIHN1YnNjcmliZSkge1xuICAgIHJldHVybiBoYW5kbGUuc2VuZE1lc3NhZ2Uoe1xuICAgICAga2luZDogXCJqb2luXCIsXG4gICAgICByb29tX2lkOiB0aGlzLnJvb20sXG4gICAgICB1c2VyX2lkOiB0aGlzLmNsaWVudElkLFxuICAgICAgc3Vic2NyaWJlLFxuICAgICAgdG9rZW46IHRoaXMuam9pblRva2VuXG4gICAgfSk7XG4gIH1cblxuICB0b2dnbGVGcmVlemUoKSB7XG4gICAgaWYgKHRoaXMuZnJvemVuKSB7XG4gICAgICB0aGlzLnVuZnJlZXplKCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZnJlZXplKCk7XG4gICAgfVxuICB9XG5cbiAgZnJlZXplKCkge1xuICAgIHRoaXMuZnJvemVuID0gdHJ1ZTtcbiAgfVxuXG4gIHVuZnJlZXplKCkge1xuICAgIHRoaXMuZnJvemVuID0gZmFsc2U7XG4gICAgdGhpcy5mbHVzaFBlbmRpbmdVcGRhdGVzKCk7XG4gIH1cblxuICBkYXRhRm9yVXBkYXRlTXVsdGlNZXNzYWdlKG5ldHdvcmtJZCwgbWVzc2FnZSkge1xuICAgIC8vIFwiZFwiIGlzIGFuIGFycmF5IG9mIGVudGl0eSBkYXRhcywgd2hlcmUgZWFjaCBpdGVtIGluIHRoZSBhcnJheSByZXByZXNlbnRzIGEgdW5pcXVlIGVudGl0eSBhbmQgY29udGFpbnNcbiAgICAvLyBtZXRhZGF0YSBmb3IgdGhlIGVudGl0eSwgYW5kIGFuIGFycmF5IG9mIGNvbXBvbmVudHMgdGhhdCBoYXZlIGJlZW4gdXBkYXRlZCBvbiB0aGUgZW50aXR5LlxuICAgIC8vIFRoaXMgbWV0aG9kIGZpbmRzIHRoZSBkYXRhIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGdpdmVuIG5ldHdvcmtJZC5cbiAgICBmb3IgKGxldCBpID0gMCwgbCA9IG1lc3NhZ2UuZGF0YS5kLmxlbmd0aDsgaSA8IGw7IGkrKykge1xuICAgICAgY29uc3QgZGF0YSA9IG1lc3NhZ2UuZGF0YS5kW2ldO1xuXG4gICAgICBpZiAoZGF0YS5uZXR3b3JrSWQgPT09IG5ldHdvcmtJZCkge1xuICAgICAgICByZXR1cm4gZGF0YTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGdldFBlbmRpbmdEYXRhKG5ldHdvcmtJZCwgbWVzc2FnZSkge1xuICAgIGlmICghbWVzc2FnZSkgcmV0dXJuIG51bGw7XG5cbiAgICBsZXQgZGF0YSA9IG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIiA/IHRoaXMuZGF0YUZvclVwZGF0ZU11bHRpTWVzc2FnZShuZXR3b3JrSWQsIG1lc3NhZ2UpIDogbWVzc2FnZS5kYXRhO1xuXG4gICAgLy8gSWdub3JlIG1lc3NhZ2VzIHJlbGF0aW5nIHRvIHVzZXJzIHdobyBoYXZlIGRpc2Nvbm5lY3RlZCBzaW5jZSBmcmVlemluZywgdGhlaXIgZW50aXRpZXNcbiAgICAvLyB3aWxsIGhhdmUgYWxlYWR5IGJlZW4gcmVtb3ZlZCBieSBOQUYuXG4gICAgLy8gTm90ZSB0aGF0IGRlbGV0ZSBtZXNzYWdlcyBoYXZlIG5vIFwib3duZXJcIiBzbyB3ZSBoYXZlIHRvIGNoZWNrIGZvciB0aGF0IGFzIHdlbGwuXG4gICAgaWYgKGRhdGEub3duZXIgJiYgIXRoaXMub2NjdXBhbnRzW2RhdGEub3duZXJdKSByZXR1cm4gbnVsbDtcblxuICAgIC8vIElnbm9yZSBtZXNzYWdlcyBmcm9tIHVzZXJzIHRoYXQgd2UgbWF5IGhhdmUgYmxvY2tlZCB3aGlsZSBmcm96ZW4uXG4gICAgaWYgKGRhdGEub3duZXIgJiYgdGhpcy5ibG9ja2VkQ2xpZW50cy5oYXMoZGF0YS5vd25lcikpIHJldHVybiBudWxsO1xuXG4gICAgcmV0dXJuIGRhdGFcbiAgfVxuXG4gIC8vIFVzZWQgZXh0ZXJuYWxseVxuICBnZXRQZW5kaW5nRGF0YUZvck5ldHdvcmtJZChuZXR3b3JrSWQpIHtcbiAgICByZXR1cm4gdGhpcy5nZXRQZW5kaW5nRGF0YShuZXR3b3JrSWQsIHRoaXMuZnJvemVuVXBkYXRlcy5nZXQobmV0d29ya0lkKSk7XG4gIH1cblxuICBmbHVzaFBlbmRpbmdVcGRhdGVzKCkge1xuICAgIGZvciAoY29uc3QgW25ldHdvcmtJZCwgbWVzc2FnZV0gb2YgdGhpcy5mcm96ZW5VcGRhdGVzKSB7XG4gICAgICBsZXQgZGF0YSA9IHRoaXMuZ2V0UGVuZGluZ0RhdGEobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICAgIGlmICghZGF0YSkgY29udGludWU7XG5cbiAgICAgIC8vIE92ZXJyaWRlIHRoZSBkYXRhIHR5cGUgb24gXCJ1bVwiIG1lc3NhZ2VzIHR5cGVzLCBzaW5jZSB3ZSBleHRyYWN0IGVudGl0eSB1cGRhdGVzIGZyb20gXCJ1bVwiIG1lc3NhZ2VzIGludG9cbiAgICAgIC8vIGluZGl2aWR1YWwgZnJvemVuVXBkYXRlcyBpbiBzdG9yZVNpbmdsZU1lc3NhZ2UuXG4gICAgICBjb25zdCBkYXRhVHlwZSA9IG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIiA/IFwidVwiIDogbWVzc2FnZS5kYXRhVHlwZTtcblxuICAgICAgdGhpcy5vbk9jY3VwYW50TWVzc2FnZShudWxsLCBkYXRhVHlwZSwgZGF0YSwgbWVzc2FnZS5zb3VyY2UpO1xuICAgIH1cbiAgICB0aGlzLmZyb3plblVwZGF0ZXMuY2xlYXIoKTtcbiAgfVxuXG4gIHN0b3JlTWVzc2FnZShtZXNzYWdlKSB7XG4gICAgaWYgKG1lc3NhZ2UuZGF0YVR5cGUgPT09IFwidW1cIikgeyAvLyBVcGRhdGVNdWx0aVxuICAgICAgZm9yIChsZXQgaSA9IDAsIGwgPSBtZXNzYWdlLmRhdGEuZC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgICAgdGhpcy5zdG9yZVNpbmdsZU1lc3NhZ2UobWVzc2FnZSwgaSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuc3RvcmVTaW5nbGVNZXNzYWdlKG1lc3NhZ2UpO1xuICAgIH1cbiAgfVxuXG4gIHN0b3JlU2luZ2xlTWVzc2FnZShtZXNzYWdlLCBpbmRleCkge1xuICAgIGNvbnN0IGRhdGEgPSBpbmRleCAhPT0gdW5kZWZpbmVkID8gbWVzc2FnZS5kYXRhLmRbaW5kZXhdIDogbWVzc2FnZS5kYXRhO1xuICAgIGNvbnN0IGRhdGFUeXBlID0gbWVzc2FnZS5kYXRhVHlwZTtcbiAgICBjb25zdCBzb3VyY2UgPSBtZXNzYWdlLnNvdXJjZTtcblxuICAgIGNvbnN0IG5ldHdvcmtJZCA9IGRhdGEubmV0d29ya0lkO1xuXG4gICAgaWYgKCF0aGlzLmZyb3plblVwZGF0ZXMuaGFzKG5ldHdvcmtJZCkpIHtcbiAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5zZXQobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3Qgc3RvcmVkTWVzc2FnZSA9IHRoaXMuZnJvemVuVXBkYXRlcy5nZXQobmV0d29ya0lkKTtcbiAgICAgIGNvbnN0IHN0b3JlZERhdGEgPSBzdG9yZWRNZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyB0aGlzLmRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UobmV0d29ya0lkLCBzdG9yZWRNZXNzYWdlKSA6IHN0b3JlZE1lc3NhZ2UuZGF0YTtcblxuICAgICAgLy8gQXZvaWQgdXBkYXRpbmcgY29tcG9uZW50cyBpZiB0aGUgZW50aXR5IGRhdGEgcmVjZWl2ZWQgZGlkIG5vdCBjb21lIGZyb20gdGhlIGN1cnJlbnQgb3duZXIuXG4gICAgICBjb25zdCBpc091dGRhdGVkTWVzc2FnZSA9IGRhdGEubGFzdE93bmVyVGltZSA8IHN0b3JlZERhdGEubGFzdE93bmVyVGltZTtcbiAgICAgIGNvbnN0IGlzQ29udGVtcG9yYW5lb3VzTWVzc2FnZSA9IGRhdGEubGFzdE93bmVyVGltZSA9PT0gc3RvcmVkRGF0YS5sYXN0T3duZXJUaW1lO1xuICAgICAgaWYgKGlzT3V0ZGF0ZWRNZXNzYWdlIHx8IChpc0NvbnRlbXBvcmFuZW91c01lc3NhZ2UgJiYgc3RvcmVkRGF0YS5vd25lciA+IGRhdGEub3duZXIpKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKGRhdGFUeXBlID09PSBcInJcIikge1xuICAgICAgICBjb25zdCBjcmVhdGVkV2hpbGVGcm96ZW4gPSBzdG9yZWREYXRhICYmIHN0b3JlZERhdGEuaXNGaXJzdFN5bmM7XG4gICAgICAgIGlmIChjcmVhdGVkV2hpbGVGcm96ZW4pIHtcbiAgICAgICAgICAvLyBJZiB0aGUgZW50aXR5IHdhcyBjcmVhdGVkIGFuZCBkZWxldGVkIHdoaWxlIGZyb3plbiwgZG9uJ3QgYm90aGVyIGNvbnZleWluZyBhbnl0aGluZyB0byB0aGUgY29uc3VtZXIuXG4gICAgICAgICAgdGhpcy5mcm96ZW5VcGRhdGVzLmRlbGV0ZShuZXR3b3JrSWQpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIERlbGV0ZSBtZXNzYWdlcyBvdmVycmlkZSBhbnkgb3RoZXIgbWVzc2FnZXMgZm9yIHRoaXMgZW50aXR5XG4gICAgICAgICAgdGhpcy5mcm96ZW5VcGRhdGVzLnNldChuZXR3b3JrSWQsIG1lc3NhZ2UpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBtZXJnZSBpbiBjb21wb25lbnQgdXBkYXRlc1xuICAgICAgICBpZiAoc3RvcmVkRGF0YS5jb21wb25lbnRzICYmIGRhdGEuY29tcG9uZW50cykge1xuICAgICAgICAgIE9iamVjdC5hc3NpZ24oc3RvcmVkRGF0YS5jb21wb25lbnRzLCBkYXRhLmNvbXBvbmVudHMpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgb25EYXRhQ2hhbm5lbE1lc3NhZ2UoZSwgc291cmNlKSB7XG4gICAgdGhpcy5vbkRhdGEoSlNPTi5wYXJzZShlLmRhdGEpLCBzb3VyY2UpO1xuICB9XG5cbiAgb25EYXRhKG1lc3NhZ2UsIHNvdXJjZSkge1xuICAgIGlmIChkZWJ1Zy5lbmFibGVkKSB7XG4gICAgICBkZWJ1ZyhgREMgaW46ICR7bWVzc2FnZX1gKTtcbiAgICB9XG5cbiAgICBpZiAoIW1lc3NhZ2UuZGF0YVR5cGUpIHJldHVybjtcblxuICAgIG1lc3NhZ2Uuc291cmNlID0gc291cmNlO1xuXG4gICAgaWYgKHRoaXMuZnJvemVuKSB7XG4gICAgICB0aGlzLnN0b3JlTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5vbk9jY3VwYW50TWVzc2FnZShudWxsLCBtZXNzYWdlLmRhdGFUeXBlLCBtZXNzYWdlLmRhdGEsIG1lc3NhZ2Uuc291cmNlKTtcbiAgICB9XG4gIH1cblxuICBzaG91bGRTdGFydENvbm5lY3Rpb25UbyhjbGllbnQpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIHN0YXJ0U3RyZWFtQ29ubmVjdGlvbihjbGllbnQpIHt9XG5cbiAgY2xvc2VTdHJlYW1Db25uZWN0aW9uKGNsaWVudCkge31cblxuICBnZXRDb25uZWN0U3RhdHVzKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMub2NjdXBhbnRzW2NsaWVudElkXSA/IE5BRi5hZGFwdGVycy5JU19DT05ORUNURUQgOiBOQUYuYWRhcHRlcnMuTk9UX0NPTk5FQ1RFRDtcbiAgfVxuXG4gIGFzeW5jIHVwZGF0ZVRpbWVPZmZzZXQoKSB7XG4gICAgaWYgKHRoaXMuaXNEaXNjb25uZWN0ZWQoKSkgcmV0dXJuO1xuXG4gICAgY29uc3QgY2xpZW50U2VudFRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgY29uc3QgcmVzID0gYXdhaXQgZmV0Y2goZG9jdW1lbnQubG9jYXRpb24uaHJlZiwge1xuICAgICAgbWV0aG9kOiBcIkhFQURcIixcbiAgICAgIGNhY2hlOiBcIm5vLWNhY2hlXCJcbiAgICB9KTtcblxuICAgIGNvbnN0IHByZWNpc2lvbiA9IDEwMDA7XG4gICAgY29uc3Qgc2VydmVyUmVjZWl2ZWRUaW1lID0gbmV3IERhdGUocmVzLmhlYWRlcnMuZ2V0KFwiRGF0ZVwiKSkuZ2V0VGltZSgpICsgcHJlY2lzaW9uIC8gMjtcbiAgICBjb25zdCBjbGllbnRSZWNlaXZlZFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGNvbnN0IHNlcnZlclRpbWUgPSBzZXJ2ZXJSZWNlaXZlZFRpbWUgKyAoY2xpZW50UmVjZWl2ZWRUaW1lIC0gY2xpZW50U2VudFRpbWUpIC8gMjtcbiAgICBjb25zdCB0aW1lT2Zmc2V0ID0gc2VydmVyVGltZSAtIGNsaWVudFJlY2VpdmVkVGltZTtcblxuICAgIHRoaXMuc2VydmVyVGltZVJlcXVlc3RzKys7XG5cbiAgICBpZiAodGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPD0gMTApIHtcbiAgICAgIHRoaXMudGltZU9mZnNldHMucHVzaCh0aW1lT2Zmc2V0KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy50aW1lT2Zmc2V0c1t0aGlzLnNlcnZlclRpbWVSZXF1ZXN0cyAlIDEwXSA9IHRpbWVPZmZzZXQ7XG4gICAgfVxuXG4gICAgdGhpcy5hdmdUaW1lT2Zmc2V0ID0gdGhpcy50aW1lT2Zmc2V0cy5yZWR1Y2UoKGFjYywgb2Zmc2V0KSA9PiAoYWNjICs9IG9mZnNldCksIDApIC8gdGhpcy50aW1lT2Zmc2V0cy5sZW5ndGg7XG5cbiAgICBpZiAodGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgPiAxMCkge1xuICAgICAgZGVidWcoYG5ldyBzZXJ2ZXIgdGltZSBvZmZzZXQ6ICR7dGhpcy5hdmdUaW1lT2Zmc2V0fW1zYCk7XG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMudXBkYXRlVGltZU9mZnNldCgpLCA1ICogNjAgKiAxMDAwKTsgLy8gU3luYyBjbG9jayBldmVyeSA1IG1pbnV0ZXMuXG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudXBkYXRlVGltZU9mZnNldCgpO1xuICAgIH1cbiAgfVxuXG4gIGdldFNlcnZlclRpbWUoKSB7XG4gICAgcmV0dXJuIERhdGUubm93KCkgKyB0aGlzLmF2Z1RpbWVPZmZzZXQ7XG4gIH1cblxuICBnZXRNZWRpYVN0cmVhbShjbGllbnRJZCwgdHlwZSA9IFwiYXVkaW9cIikge1xuICAgIGlmICh0aGlzLm1lZGlhU3RyZWFtc1tjbGllbnRJZF0pIHtcbiAgICAgIGRlYnVnKGBBbHJlYWR5IGhhZCAke3R5cGV9IGZvciAke2NsaWVudElkfWApO1xuICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh0aGlzLm1lZGlhU3RyZWFtc1tjbGllbnRJZF1bdHlwZV0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBkZWJ1ZyhgV2FpdGluZyBvbiAke3R5cGV9IGZvciAke2NsaWVudElkfWApO1xuICAgICAgaWYgKCF0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmhhcyhjbGllbnRJZCkpIHtcbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5zZXQoY2xpZW50SWQsIHt9KTtcblxuICAgICAgICBjb25zdCBhdWRpb1Byb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLmF1ZGlvID0geyByZXNvbHZlLCByZWplY3QgfTtcbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHZpZGVvUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkudmlkZW8gPSB7IHJlc29sdmUsIHJlamVjdCB9O1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkuYXVkaW8ucHJvbWlzZSA9IGF1ZGlvUHJvbWlzZTtcbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLnZpZGVvLnByb21pc2UgPSB2aWRlb1Byb21pc2U7XG5cbiAgICAgICAgYXVkaW9Qcm9taXNlLmNhdGNoKGUgPT4gY29uc29sZS53YXJuKGAke2NsaWVudElkfSBnZXRNZWRpYVN0cmVhbSBBdWRpbyBFcnJvcmAsIGUpKTtcbiAgICAgICAgdmlkZW9Qcm9taXNlLmNhdGNoKGUgPT4gY29uc29sZS53YXJuKGAke2NsaWVudElkfSBnZXRNZWRpYVN0cmVhbSBWaWRlbyBFcnJvcmAsIGUpKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZClbdHlwZV0ucHJvbWlzZTtcbiAgICB9XG4gIH1cblxuICBzZXRNZWRpYVN0cmVhbShjbGllbnRJZCwgc3RyZWFtKSB7XG4gICAgLy8gU2FmYXJpIGRvZXNuJ3QgbGlrZSBpdCB3aGVuIHlvdSB1c2Ugc2luZ2xlIGEgbWl4ZWQgbWVkaWEgc3RyZWFtIHdoZXJlIG9uZSBvZiB0aGUgdHJhY2tzIGlzIGluYWN0aXZlLCBzbyB3ZVxuICAgIC8vIHNwbGl0IHRoZSB0cmFja3MgaW50byB0d28gc3RyZWFtcy5cbiAgICBjb25zdCBhdWRpb1N0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuICAgIHRyeSB7XG4gICAgc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiBhdWRpb1N0cmVhbS5hZGRUcmFjayh0cmFjaykpO1xuXG4gICAgfSBjYXRjaChlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYCR7Y2xpZW50SWR9IHNldE1lZGlhU3RyZWFtIEF1ZGlvIEVycm9yYCwgZSk7XG4gICAgfVxuICAgIGNvbnN0IHZpZGVvU3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKCk7XG4gICAgdHJ5IHtcbiAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHZpZGVvU3RyZWFtLmFkZFRyYWNrKHRyYWNrKSk7XG5cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zb2xlLndhcm4oYCR7Y2xpZW50SWR9IHNldE1lZGlhU3RyZWFtIFZpZGVvIEVycm9yYCwgZSk7XG4gICAgfVxuXG4gICAgdGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdID0geyBhdWRpbzogYXVkaW9TdHJlYW0sIHZpZGVvOiB2aWRlb1N0cmVhbSB9O1xuXG4gICAgLy8gUmVzb2x2ZSB0aGUgcHJvbWlzZSBmb3IgdGhlIHVzZXIncyBtZWRpYSBzdHJlYW0gaWYgaXQgZXhpc3RzLlxuICAgIGlmICh0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmhhcyhjbGllbnRJZCkpIHtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS5hdWRpby5yZXNvbHZlKGF1ZGlvU3RyZWFtKTtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS52aWRlby5yZXNvbHZlKHZpZGVvU3RyZWFtKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBzZXRMb2NhbE1lZGlhU3RyZWFtKHN0cmVhbSkge1xuICAgIC8vIG91ciBqb2IgaGVyZSBpcyB0byBtYWtlIHN1cmUgdGhlIGNvbm5lY3Rpb24gd2luZHMgdXAgd2l0aCBSVFAgc2VuZGVycyBzZW5kaW5nIHRoZSBzdHVmZiBpbiB0aGlzIHN0cmVhbSxcbiAgICAvLyBhbmQgbm90IHRoZSBzdHVmZiB0aGF0IGlzbid0IGluIHRoaXMgc3RyZWFtLiBzdHJhdGVneSBpcyB0byByZXBsYWNlIGV4aXN0aW5nIHRyYWNrcyBpZiB3ZSBjYW4sIGFkZCB0cmFja3NcbiAgICAvLyB0aGF0IHdlIGNhbid0IHJlcGxhY2UsIGFuZCBkaXNhYmxlIHRyYWNrcyB0aGF0IGRvbid0IGV4aXN0IGFueW1vcmUuXG5cbiAgICAvLyBub3RlIHRoYXQgd2UgZG9uJ3QgZXZlciByZW1vdmUgYSB0cmFjayBmcm9tIHRoZSBzdHJlYW0gLS0gc2luY2UgSmFudXMgZG9lc24ndCBzdXBwb3J0IFVuaWZpZWQgUGxhbiwgd2UgYWJzb2x1dGVseVxuICAgIC8vIGNhbid0IHdpbmQgdXAgd2l0aCBhIFNEUCB0aGF0IGhhcyA+MSBhdWRpbyBvciA+MSB2aWRlbyB0cmFja3MsIGV2ZW4gaWYgb25lIG9mIHRoZW0gaXMgaW5hY3RpdmUgKHdoYXQgeW91IGdldCBpZlxuICAgIC8vIHlvdSByZW1vdmUgYSB0cmFjayBmcm9tIGFuIGV4aXN0aW5nIHN0cmVhbS4pXG4gICAgaWYgKHRoaXMucHVibGlzaGVyICYmIHRoaXMucHVibGlzaGVyLmNvbm4pIHtcbiAgICAgIGNvbnN0IGV4aXN0aW5nU2VuZGVycyA9IHRoaXMucHVibGlzaGVyLmNvbm4uZ2V0U2VuZGVycygpO1xuICAgICAgY29uc3QgbmV3U2VuZGVycyA9IFtdO1xuICAgICAgY29uc3QgdHJhY2tzID0gc3RyZWFtLmdldFRyYWNrcygpO1xuXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRyYWNrcy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCB0ID0gdHJhY2tzW2ldO1xuICAgICAgICBjb25zdCBzZW5kZXIgPSBleGlzdGluZ1NlbmRlcnMuZmluZChzID0+IHMudHJhY2sgIT0gbnVsbCAmJiBzLnRyYWNrLmtpbmQgPT0gdC5raW5kKTtcblxuICAgICAgICBpZiAoc2VuZGVyICE9IG51bGwpIHtcbiAgICAgICAgICBpZiAoc2VuZGVyLnJlcGxhY2VUcmFjaykge1xuICAgICAgICAgICAgYXdhaXQgc2VuZGVyLnJlcGxhY2VUcmFjayh0KTtcblxuICAgICAgICAgICAgLy8gV29ya2Fyb3VuZCBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xNTc2NzcxXG4gICAgICAgICAgICBpZiAodC5raW5kID09PSBcInZpZGVvXCIgJiYgdC5lbmFibGVkICYmIG5hdmlnYXRvci51c2VyQWdlbnQudG9Mb3dlckNhc2UoKS5pbmRleE9mKCdmaXJlZm94JykgPiAtMSkge1xuICAgICAgICAgICAgICB0LmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0LmVuYWJsZWQgPSB0cnVlLCAxMDAwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gRmFsbGJhY2sgZm9yIGJyb3dzZXJzIHRoYXQgZG9uJ3Qgc3VwcG9ydCByZXBsYWNlVHJhY2suIEF0IHRoaXMgdGltZSBvZiB0aGlzIHdyaXRpbmdcbiAgICAgICAgICAgIC8vIG1vc3QgYnJvd3NlcnMgc3VwcG9ydCBpdCwgYW5kIHRlc3RpbmcgdGhpcyBjb2RlIHBhdGggc2VlbXMgdG8gbm90IHdvcmsgcHJvcGVybHlcbiAgICAgICAgICAgIC8vIGluIENocm9tZSBhbnltb3JlLlxuICAgICAgICAgICAgc3RyZWFtLnJlbW92ZVRyYWNrKHNlbmRlci50cmFjayk7XG4gICAgICAgICAgICBzdHJlYW0uYWRkVHJhY2sodCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIG5ld1NlbmRlcnMucHVzaChzZW5kZXIpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG5ld1NlbmRlcnMucHVzaCh0aGlzLnB1Ymxpc2hlci5jb25uLmFkZFRyYWNrKHQsIHN0cmVhbSkpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBleGlzdGluZ1NlbmRlcnMuZm9yRWFjaChzID0+IHtcbiAgICAgICAgaWYgKCFuZXdTZW5kZXJzLmluY2x1ZGVzKHMpKSB7XG4gICAgICAgICAgcy50cmFjay5lbmFibGVkID0gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgICB0aGlzLmxvY2FsTWVkaWFTdHJlYW0gPSBzdHJlYW07XG4gICAgdGhpcy5zZXRNZWRpYVN0cmVhbSh0aGlzLmNsaWVudElkLCBzdHJlYW0pO1xuICB9XG5cbiAgZW5hYmxlTWljcm9waG9uZShlbmFibGVkKSB7XG4gICAgaWYgKHRoaXMucHVibGlzaGVyICYmIHRoaXMucHVibGlzaGVyLmNvbm4pIHtcbiAgICAgIHRoaXMucHVibGlzaGVyLmNvbm4uZ2V0U2VuZGVycygpLmZvckVhY2gocyA9PiB7XG4gICAgICAgIGlmIChzLnRyYWNrLmtpbmQgPT0gXCJhdWRpb1wiKSB7XG4gICAgICAgICAgcy50cmFjay5lbmFibGVkID0gZW5hYmxlZDtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgc2VuZERhdGEoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwic2VuZERhdGEgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0KSB7XG4gICAgICAgIGNhc2UgXCJ3ZWJzb2NrZXRcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImRhdGFcIiwgYm9keTogSlNPTi5zdHJpbmdpZnkoeyBkYXRhVHlwZSwgZGF0YSB9KSwgd2hvbTogY2xpZW50SWQgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnVucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHNlbmREYXRhR3VhcmFudGVlZChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpIHtcbiAgICBpZiAoIXRoaXMucHVibGlzaGVyKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJzZW5kRGF0YUd1YXJhbnRlZWQgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSksIHdob206IGNsaWVudElkIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5yZWxpYWJsZUNoYW5uZWwuc2VuZChKU09OLnN0cmluZ2lmeSh7IGNsaWVudElkLCBkYXRhVHlwZSwgZGF0YSB9KSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhpcy5yZWxpYWJsZVRyYW5zcG9ydChjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGJyb2FkY2FzdERhdGEoZGF0YVR5cGUsIGRhdGEpIHtcbiAgICBpZiAoIXRoaXMucHVibGlzaGVyKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJicm9hZGNhc3REYXRhIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnVucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBkYXRhVHlwZSwgZGF0YSB9KSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdGhpcy51bnJlbGlhYmxlVHJhbnNwb3J0KHVuZGVmaW5lZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGJyb2FkY2FzdERhdGFHdWFyYW50ZWVkKGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwiYnJvYWRjYXN0RGF0YUd1YXJhbnRlZWQgY2FsbGVkIHdpdGhvdXQgYSBwdWJsaXNoZXJcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIHN3aXRjaCAodGhpcy5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkgfSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJkYXRhY2hhbm5lbFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQodW5kZWZpbmVkLCBkYXRhVHlwZSwgZGF0YSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAga2ljayhjbGllbnRJZCwgcGVybXNUb2tlbikge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImtpY2tcIiwgcm9vbV9pZDogdGhpcy5yb29tLCB1c2VyX2lkOiBjbGllbnRJZCwgdG9rZW46IHBlcm1zVG9rZW4gfSkudGhlbigoKSA9PiB7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwia2lja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBjbGllbnRJZCB9IH0pKTtcbiAgICB9KTtcbiAgfVxuXG4gIGJsb2NrKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiYmxvY2tcIiwgd2hvbTogY2xpZW50SWQgfSkudGhlbigoKSA9PiB7XG4gICAgICB0aGlzLmJsb2NrZWRDbGllbnRzLnNldChjbGllbnRJZCwgdHJ1ZSk7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwiYmxvY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogY2xpZW50SWQgfSB9KSk7XG4gICAgfSk7XG4gIH1cblxuICB1bmJsb2NrKGNsaWVudElkKSB7XG4gICAgcmV0dXJuIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwidW5ibG9ja1wiLCB3aG9tOiBjbGllbnRJZCB9KS50aGVuKCgpID0+IHtcbiAgICAgIHRoaXMuYmxvY2tlZENsaWVudHMuZGVsZXRlKGNsaWVudElkKTtcbiAgICAgIGRvY3VtZW50LmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoXCJ1bmJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGNsaWVudElkIH0gfSkpO1xuICAgIH0pO1xuICB9XG59XG5cbk5BRi5hZGFwdGVycy5yZWdpc3RlcihcImphbnVzXCIsIEphbnVzQWRhcHRlcik7XG5cbm1vZHVsZS5leHBvcnRzID0gSmFudXNBZGFwdGVyO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL21pbmlqYW51cy9taW5pamFudXMuanMiLCJ3ZWJwYWNrOi8vLy4vbm9kZV9tb2R1bGVzL3NkcC9zZHAuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL2luZGV4LmpzIl0sIm5hbWVzIjpbIm1qIiwicmVxdWlyZSIsIkphbnVzU2Vzc2lvbiIsInByb3RvdHlwZSIsInNlbmRPcmlnaW5hbCIsInNlbmQiLCJ0eXBlIiwic2lnbmFsIiwiY2F0Y2giLCJlIiwibWVzc2FnZSIsImluZGV4T2YiLCJjb25zb2xlIiwiZXJyb3IiLCJOQUYiLCJjb25uZWN0aW9uIiwiYWRhcHRlciIsInJlY29ubmVjdCIsInNkcFV0aWxzIiwiZGVidWciLCJsb2ciLCJ3YXJuIiwiaXNTYWZhcmkiLCJ0ZXN0IiwibmF2aWdhdG9yIiwidXNlckFnZW50IiwiU1VCU0NSSUJFX1RJTUVPVVRfTVMiLCJkZWJvdW5jZSIsImZuIiwiY3VyciIsIlByb21pc2UiLCJyZXNvbHZlIiwiYXJncyIsIkFycmF5Iiwic2xpY2UiLCJjYWxsIiwiYXJndW1lbnRzIiwidGhlbiIsIl8iLCJhcHBseSIsInJhbmRvbVVpbnQiLCJNYXRoIiwiZmxvb3IiLCJyYW5kb20iLCJOdW1iZXIiLCJNQVhfU0FGRV9JTlRFR0VSIiwidW50aWxEYXRhQ2hhbm5lbE9wZW4iLCJkYXRhQ2hhbm5lbCIsInJlamVjdCIsInJlYWR5U3RhdGUiLCJyZXNvbHZlciIsInJlamVjdG9yIiwiY2xlYXIiLCJyZW1vdmVFdmVudExpc3RlbmVyIiwiYWRkRXZlbnRMaXN0ZW5lciIsImlzSDI2NFZpZGVvU3VwcG9ydGVkIiwidmlkZW8iLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJjYW5QbGF5VHlwZSIsIk9QVVNfUEFSQU1FVEVSUyIsInVzZWR0eCIsInN0ZXJlbyIsIkRFRkFVTFRfUEVFUl9DT05ORUNUSU9OX0NPTkZJRyIsImljZVNlcnZlcnMiLCJ1cmxzIiwiV1NfTk9STUFMX0NMT1NVUkUiLCJKYW51c0FkYXB0ZXIiLCJjb25zdHJ1Y3RvciIsInJvb20iLCJjbGllbnRJZCIsImpvaW5Ub2tlbiIsInNlcnZlclVybCIsIndlYlJ0Y09wdGlvbnMiLCJwZWVyQ29ubmVjdGlvbkNvbmZpZyIsIndzIiwic2Vzc2lvbiIsInJlbGlhYmxlVHJhbnNwb3J0IiwidW5yZWxpYWJsZVRyYW5zcG9ydCIsImluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSIsInJlY29ubmVjdGlvbkRlbGF5IiwicmVjb25uZWN0aW9uVGltZW91dCIsIm1heFJlY29ubmVjdGlvbkF0dGVtcHRzIiwicmVjb25uZWN0aW9uQXR0ZW1wdHMiLCJwdWJsaXNoZXIiLCJvY2N1cGFudHMiLCJsZWZ0T2NjdXBhbnRzIiwiU2V0IiwibWVkaWFTdHJlYW1zIiwibG9jYWxNZWRpYVN0cmVhbSIsInBlbmRpbmdNZWRpYVJlcXVlc3RzIiwiTWFwIiwiYmxvY2tlZENsaWVudHMiLCJmcm96ZW5VcGRhdGVzIiwidGltZU9mZnNldHMiLCJzZXJ2ZXJUaW1lUmVxdWVzdHMiLCJhdmdUaW1lT2Zmc2V0Iiwib25XZWJzb2NrZXRPcGVuIiwiYmluZCIsIm9uV2Vic29ja2V0Q2xvc2UiLCJvbldlYnNvY2tldE1lc3NhZ2UiLCJvbkRhdGFDaGFubmVsTWVzc2FnZSIsIm9uRGF0YSIsInNldFNlcnZlclVybCIsInVybCIsInNldEFwcCIsImFwcCIsInNldFJvb20iLCJyb29tTmFtZSIsInNldEpvaW5Ub2tlbiIsInNldENsaWVudElkIiwic2V0V2ViUnRjT3B0aW9ucyIsIm9wdGlvbnMiLCJzZXRQZWVyQ29ubmVjdGlvbkNvbmZpZyIsInNldFNlcnZlckNvbm5lY3RMaXN0ZW5lcnMiLCJzdWNjZXNzTGlzdGVuZXIiLCJmYWlsdXJlTGlzdGVuZXIiLCJjb25uZWN0U3VjY2VzcyIsImNvbm5lY3RGYWlsdXJlIiwic2V0Um9vbU9jY3VwYW50TGlzdGVuZXIiLCJvY2N1cGFudExpc3RlbmVyIiwib25PY2N1cGFudHNDaGFuZ2VkIiwic2V0RGF0YUNoYW5uZWxMaXN0ZW5lcnMiLCJvcGVuTGlzdGVuZXIiLCJjbG9zZWRMaXN0ZW5lciIsIm1lc3NhZ2VMaXN0ZW5lciIsIm9uT2NjdXBhbnRDb25uZWN0ZWQiLCJvbk9jY3VwYW50RGlzY29ubmVjdGVkIiwib25PY2N1cGFudE1lc3NhZ2UiLCJzZXRSZWNvbm5lY3Rpb25MaXN0ZW5lcnMiLCJyZWNvbm5lY3RpbmdMaXN0ZW5lciIsInJlY29ubmVjdGVkTGlzdGVuZXIiLCJyZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyIiwib25SZWNvbm5lY3RpbmciLCJvblJlY29ubmVjdGVkIiwib25SZWNvbm5lY3Rpb25FcnJvciIsImNvbm5lY3QiLCJ3ZWJzb2NrZXRDb25uZWN0aW9uIiwiV2ViU29ja2V0IiwidGltZW91dE1zIiwid3NPbk9wZW4iLCJhbGwiLCJ1cGRhdGVUaW1lT2Zmc2V0IiwiZGlzY29ubmVjdCIsImNsZWFyVGltZW91dCIsInJlbW92ZUFsbE9jY3VwYW50cyIsImNvbm4iLCJjbG9zZSIsImRpc3Bvc2UiLCJkZWxheWVkUmVjb25uZWN0VGltZW91dCIsImlzRGlzY29ubmVjdGVkIiwiY3JlYXRlIiwiY3JlYXRlUHVibGlzaGVyIiwiYWRkT2NjdXBhbnRQcm9taXNlcyIsImkiLCJpbml0aWFsT2NjdXBhbnRzIiwibGVuZ3RoIiwib2NjdXBhbnRJZCIsInB1c2giLCJhZGRPY2N1cGFudCIsImV2ZW50IiwiY29kZSIsInNldFRpbWVvdXQiLCJFcnJvciIsInBlcmZvcm1EZWxheWVkUmVjb25uZWN0IiwicmVjZWl2ZSIsIkpTT04iLCJwYXJzZSIsImRhdGEiLCJyZW1vdmVPY2N1cGFudCIsImRlbGV0ZSIsInN1YnNjcmliZXIiLCJjcmVhdGVTdWJzY3JpYmVyIiwic2V0TWVkaWFTdHJlYW0iLCJtZWRpYVN0cmVhbSIsIk9iamVjdCIsImdldE93blByb3BlcnR5TmFtZXMiLCJhZGQiLCJoYXMiLCJtc2ciLCJnZXQiLCJhdWRpbyIsImFzc29jaWF0ZSIsImhhbmRsZSIsImV2Iiwic2VuZFRyaWNrbGUiLCJjYW5kaWRhdGUiLCJpY2VDb25uZWN0aW9uU3RhdGUiLCJvZmZlciIsImNyZWF0ZU9mZmVyIiwiY29uZmlndXJlUHVibGlzaGVyU2RwIiwiZml4U2FmYXJpSWNlVUZyYWciLCJsb2NhbCIsIm8iLCJzZXRMb2NhbERlc2NyaXB0aW9uIiwicmVtb3RlIiwiaiIsInNlbmRKc2VwIiwiciIsInNldFJlbW90ZURlc2NyaXB0aW9uIiwianNlcCIsIm9uIiwiYW5zd2VyIiwiY29uZmlndXJlU3Vic2NyaWJlclNkcCIsImNyZWF0ZUFuc3dlciIsImEiLCJKYW51c1BsdWdpbkhhbmRsZSIsIlJUQ1BlZXJDb25uZWN0aW9uIiwiYXR0YWNoIiwid2VicnRjdXAiLCJyZWxpYWJsZUNoYW5uZWwiLCJjcmVhdGVEYXRhQ2hhbm5lbCIsIm9yZGVyZWQiLCJ1bnJlbGlhYmxlQ2hhbm5lbCIsIm1heFJldHJhbnNtaXRzIiwiZ2V0VHJhY2tzIiwiZm9yRWFjaCIsImFkZFRyYWNrIiwidHJhY2siLCJwbHVnaW5kYXRhIiwicm9vbV9pZCIsInVzZXJfaWQiLCJib2R5IiwiZGlzcGF0Y2hFdmVudCIsIkN1c3RvbUV2ZW50IiwiZGV0YWlsIiwiYnkiLCJzZW5kSm9pbiIsIm5vdGlmaWNhdGlvbnMiLCJzdWNjZXNzIiwiZXJyIiwicmVzcG9uc2UiLCJ1c2VycyIsImluY2x1ZGVzIiwic2RwIiwicmVwbGFjZSIsImxpbmUiLCJwdCIsInBhcmFtZXRlcnMiLCJhc3NpZ24iLCJwYXJzZUZtdHAiLCJ3cml0ZUZtdHAiLCJwYXlsb2FkVHlwZSIsIm1heFJldHJpZXMiLCJ3ZWJydGNGYWlsZWQiLCJsZWZ0SW50ZXJ2YWwiLCJzZXRJbnRlcnZhbCIsImNsZWFySW50ZXJ2YWwiLCJ0aW1lb3V0IiwibWVkaWEiLCJfaU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlciIsIk1lZGlhU3RyZWFtIiwicmVjZWl2ZXJzIiwiZ2V0UmVjZWl2ZXJzIiwicmVjZWl2ZXIiLCJzdWJzY3JpYmUiLCJzZW5kTWVzc2FnZSIsImtpbmQiLCJ0b2tlbiIsInRvZ2dsZUZyZWV6ZSIsImZyb3plbiIsInVuZnJlZXplIiwiZnJlZXplIiwiZmx1c2hQZW5kaW5nVXBkYXRlcyIsImRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UiLCJuZXR3b3JrSWQiLCJsIiwiZCIsImdldFBlbmRpbmdEYXRhIiwiZGF0YVR5cGUiLCJvd25lciIsImdldFBlbmRpbmdEYXRhRm9yTmV0d29ya0lkIiwic291cmNlIiwic3RvcmVNZXNzYWdlIiwic3RvcmVTaW5nbGVNZXNzYWdlIiwiaW5kZXgiLCJ1bmRlZmluZWQiLCJzZXQiLCJzdG9yZWRNZXNzYWdlIiwic3RvcmVkRGF0YSIsImlzT3V0ZGF0ZWRNZXNzYWdlIiwibGFzdE93bmVyVGltZSIsImlzQ29udGVtcG9yYW5lb3VzTWVzc2FnZSIsImNyZWF0ZWRXaGlsZUZyb3plbiIsImlzRmlyc3RTeW5jIiwiY29tcG9uZW50cyIsImVuYWJsZWQiLCJzaG91bGRTdGFydENvbm5lY3Rpb25UbyIsImNsaWVudCIsInN0YXJ0U3RyZWFtQ29ubmVjdGlvbiIsImNsb3NlU3RyZWFtQ29ubmVjdGlvbiIsImdldENvbm5lY3RTdGF0dXMiLCJhZGFwdGVycyIsIklTX0NPTk5FQ1RFRCIsIk5PVF9DT05ORUNURUQiLCJjbGllbnRTZW50VGltZSIsIkRhdGUiLCJub3ciLCJyZXMiLCJmZXRjaCIsImxvY2F0aW9uIiwiaHJlZiIsIm1ldGhvZCIsImNhY2hlIiwicHJlY2lzaW9uIiwic2VydmVyUmVjZWl2ZWRUaW1lIiwiaGVhZGVycyIsImdldFRpbWUiLCJjbGllbnRSZWNlaXZlZFRpbWUiLCJzZXJ2ZXJUaW1lIiwidGltZU9mZnNldCIsInJlZHVjZSIsImFjYyIsIm9mZnNldCIsImdldFNlcnZlclRpbWUiLCJnZXRNZWRpYVN0cmVhbSIsImF1ZGlvUHJvbWlzZSIsInZpZGVvUHJvbWlzZSIsInByb21pc2UiLCJzdHJlYW0iLCJhdWRpb1N0cmVhbSIsImdldEF1ZGlvVHJhY2tzIiwidmlkZW9TdHJlYW0iLCJnZXRWaWRlb1RyYWNrcyIsInNldExvY2FsTWVkaWFTdHJlYW0iLCJleGlzdGluZ1NlbmRlcnMiLCJnZXRTZW5kZXJzIiwibmV3U2VuZGVycyIsInRyYWNrcyIsInQiLCJzZW5kZXIiLCJmaW5kIiwicyIsInJlcGxhY2VUcmFjayIsInRvTG93ZXJDYXNlIiwicmVtb3ZlVHJhY2siLCJlbmFibGVNaWNyb3Bob25lIiwic2VuZERhdGEiLCJzdHJpbmdpZnkiLCJ3aG9tIiwic2VuZERhdGFHdWFyYW50ZWVkIiwiYnJvYWRjYXN0RGF0YSIsImJyb2FkY2FzdERhdGFHdWFyYW50ZWVkIiwia2ljayIsInBlcm1zVG9rZW4iLCJibG9jayIsInVuYmxvY2siLCJyZWdpc3RlciIsIm1vZHVsZSIsImV4cG9ydHMiXSwibWFwcGluZ3MiOiI7UUFBQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTs7O1FBR0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDBDQUEwQyxnQ0FBZ0M7UUFDMUU7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSx3REFBd0Qsa0JBQWtCO1FBQzFFO1FBQ0EsaURBQWlELGNBQWM7UUFDL0Q7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBLHlDQUF5QyxpQ0FBaUM7UUFDMUUsZ0hBQWdILG1CQUFtQixFQUFFO1FBQ3JJO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMkJBQTJCLDBCQUEwQixFQUFFO1FBQ3ZELGlDQUFpQyxlQUFlO1FBQ2hEO1FBQ0E7UUFDQTs7UUFFQTtRQUNBLHNEQUFzRCwrREFBK0Q7O1FBRXJIO1FBQ0E7OztRQUdBO1FBQ0E7Ozs7Ozs7Ozs7OztBQ2xGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxp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R0FBRztBQUNIO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBLDRDQUE0QztBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxvQkFBb0I7QUFDcEIsMEJBQTBCO0FBQzFCO0FBQ0E7QUFDQTtBQUNBLDJEQUEyRDtBQUMzRCxpQkFBaUIsa0JBQWtCO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsaURBQWlEO0FBQ2pEO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUIsa0JBQWtCLE9BQU87QUFDMUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLDRDQUE0QztBQUM1QztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHOztBQUVIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLHdCQUF3QjtBQUN4QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FBR0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxZQUFZO0FBQ1o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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LENBQWVtRyxNQUFmLENBQXNCeUUsV0FBdEIsQ0FBa0MsRUFBRUMsTUFBTSxPQUFSLEVBQWlCa0YsTUFBTTdRLFFBQXZCLEVBQWxDLEVBQXFFakMsSUFBckUsQ0FBMEUsTUFBTTtBQUNyRixXQUFLdUQsY0FBTCxDQUFvQnlMLEdBQXBCLENBQXdCL00sUUFBeEIsRUFBa0MsSUFBbEM7QUFDQWIsZUFBU3FLLElBQVQsQ0FBY0MsYUFBZCxDQUE0QixJQUFJQyxXQUFKLENBQWdCLFNBQWhCLEVBQTJCLEVBQUVDLFFBQVEsRUFBRTNKLFVBQVVBLFFBQVosRUFBVixFQUEzQixDQUE1QjtBQUNELEtBSE0sQ0FBUDtBQUlEOztBQUVEb1IsVUFBUXBSLFFBQVIsRUFBa0I7QUFDaEIsV0FBTyxLQUFLYyxTQUFMLENBQWVtRyxNQUFmLENBQXNCeUUsV0FBdEIsQ0FBa0MsRUFBRUMsTUFBTSxTQUFSLEVBQW1Ca0YsTUFBTTdRLFFBQXpCLEVBQWxDLEVBQXVFakMsSUFBdkUsQ0FBNEUsTUFBTTtBQUN2RixXQUFLdUQsY0FBTCxDQUFvQjhFLE1BQXBCLENBQTJCcEcsUUFBM0I7QUFDQWIsZUFBU3FLLElBQVQsQ0FBY0MsYUFBZCxDQUE0QixJQUFJQyxXQUFKLENBQWdCLFdBQWhCLEVBQTZCLEVBQUVDLFFBQVEsRUFBRTNKLFVBQVVBLFFBQVosRUFBVixFQUE3QixDQUE1QjtBQUNELEtBSE0sQ0FBUDtBQUlEO0FBMTlCZ0I7O0FBNjlCbkJ4RCxJQUFJc1IsUUFBSixDQUFhdUQsUUFBYixDQUFzQixPQUF0QixFQUErQnhSLFlBQS9COztBQUVBeVIsT0FBT0MsT0FBUCxHQUFpQjFSLFlBQWpCLEMiLCJmaWxlIjoibmFmLWphbnVzLWFkYXB0ZXIuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gXCIuL3NyYy9pbmRleC5qc1wiKTtcbiIsIi8qKlxuICogUmVwcmVzZW50cyBhIGhhbmRsZSB0byBhIHNpbmdsZSBKYW51cyBwbHVnaW4gb24gYSBKYW51cyBzZXNzaW9uLiBFYWNoIFdlYlJUQyBjb25uZWN0aW9uIHRvIHRoZSBKYW51cyBzZXJ2ZXIgd2lsbCBiZVxuICogYXNzb2NpYXRlZCB3aXRoIGEgc2luZ2xlIGhhbmRsZS4gT25jZSBhdHRhY2hlZCB0byB0aGUgc2VydmVyLCB0aGlzIGhhbmRsZSB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZVxuICogdXNlZCB0byBhc3NvY2lhdGUgaXQgd2l0aCBmdXR1cmUgc2lnbmFsbGluZyBtZXNzYWdlcy5cbiAqXG4gKiBTZWUgaHR0cHM6Ly9qYW51cy5jb25mLm1lZXRlY2hvLmNvbS9kb2NzL3Jlc3QuaHRtbCNoYW5kbGVzLlxuICoqL1xuZnVuY3Rpb24gSmFudXNQbHVnaW5IYW5kbGUoc2Vzc2lvbikge1xuICB0aGlzLnNlc3Npb24gPSBzZXNzaW9uO1xuICB0aGlzLmlkID0gdW5kZWZpbmVkO1xufVxuXG4vKiogQXR0YWNoZXMgdGhpcyBoYW5kbGUgdG8gdGhlIEphbnVzIHNlcnZlciBhbmQgc2V0cyBpdHMgSUQuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLmF0dGFjaCA9IGZ1bmN0aW9uKHBsdWdpbikge1xuICB2YXIgcGF5bG9hZCA9IHsgcGx1Z2luOiBwbHVnaW4sIFwiZm9yY2UtYnVuZGxlXCI6IHRydWUsIFwiZm9yY2UtcnRjcC1tdXhcIjogdHJ1ZSB9O1xuICByZXR1cm4gdGhpcy5zZXNzaW9uLnNlbmQoXCJhdHRhY2hcIiwgcGF5bG9hZCkudGhlbihyZXNwID0+IHtcbiAgICB0aGlzLmlkID0gcmVzcC5kYXRhLmlkO1xuICAgIHJldHVybiByZXNwO1xuICB9KTtcbn07XG5cbi8qKiBEZXRhY2hlcyB0aGlzIGhhbmRsZS4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUuZGV0YWNoID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXRhY2hcIik7XG59O1xuXG4vKiogUmVnaXN0ZXJzIGEgY2FsbGJhY2sgdG8gYmUgZmlyZWQgdXBvbiB0aGUgcmVjZXB0aW9uIG9mIGFueSBpbmNvbWluZyBKYW51cyBzaWduYWxzIGZvciB0aGlzIHBsdWdpbiBoYW5kbGUgd2l0aCB0aGVcbiAqIGBqYW51c2AgYXR0cmlidXRlIGVxdWFsIHRvIGBldmAuXG4gKiovXG5KYW51c1BsdWdpbkhhbmRsZS5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5vbihldiwgc2lnbmFsID0+IHtcbiAgICBpZiAoc2lnbmFsLnNlbmRlciA9PSB0aGlzLmlkKSB7XG4gICAgICBjYWxsYmFjayhzaWduYWwpO1xuICAgIH1cbiAgfSk7XG59O1xuXG4vKipcbiAqIFNlbmRzIGEgc2lnbmFsIGFzc29jaWF0ZWQgd2l0aCB0aGlzIGhhbmRsZS4gU2lnbmFscyBzaG91bGQgYmUgSlNPTi1zZXJpYWxpemFibGUgb2JqZWN0cy4gUmV0dXJucyBhIHByb21pc2UgdGhhdCB3aWxsXG4gKiBiZSByZXNvbHZlZCBvciByZWplY3RlZCB3aGVuIGEgcmVzcG9uc2UgdG8gdGhpcyBzaWduYWwgaXMgcmVjZWl2ZWQsIG9yIHdoZW4gbm8gcmVzcG9uc2UgaXMgcmVjZWl2ZWQgd2l0aGluIHRoZVxuICogc2Vzc2lvbiB0aW1lb3V0LlxuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgcmV0dXJuIHRoaXMuc2Vzc2lvbi5zZW5kKHR5cGUsIE9iamVjdC5hc3NpZ24oeyBoYW5kbGVfaWQ6IHRoaXMuaWQgfSwgc2lnbmFsKSk7XG59O1xuXG4vKiogU2VuZHMgYSBwbHVnaW4tc3BlY2lmaWMgbWVzc2FnZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRNZXNzYWdlID0gZnVuY3Rpb24oYm9keSkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IGJvZHkgfSk7XG59O1xuXG4vKiogU2VuZHMgYSBKU0VQIG9mZmVyIG9yIGFuc3dlciBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRKc2VwID0gZnVuY3Rpb24oanNlcCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwibWVzc2FnZVwiLCB7IGJvZHk6IHt9LCBqc2VwOiBqc2VwIH0pO1xufTtcblxuLyoqIFNlbmRzIGFuIElDRSB0cmlja2xlIGNhbmRpZGF0ZSBhc3NvY2lhdGVkIHdpdGggdGhpcyBoYW5kbGUuICoqL1xuSmFudXNQbHVnaW5IYW5kbGUucHJvdG90eXBlLnNlbmRUcmlja2xlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJ0cmlja2xlXCIsIHsgY2FuZGlkYXRlOiBjYW5kaWRhdGUgfSk7XG59O1xuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBKYW51cyBzZXNzaW9uIC0tIGEgSmFudXMgY29udGV4dCBmcm9tIHdpdGhpbiB3aGljaCB5b3UgY2FuIG9wZW4gbXVsdGlwbGUgaGFuZGxlcyBhbmQgY29ubmVjdGlvbnMuIE9uY2VcbiAqIGNyZWF0ZWQsIHRoaXMgc2Vzc2lvbiB3aWxsIGJlIGdpdmVuIGEgdW5pcXVlIElEIHdoaWNoIHNob3VsZCBiZSB1c2VkIHRvIGFzc29jaWF0ZSBpdCB3aXRoIGZ1dHVyZSBzaWduYWxsaW5nIG1lc3NhZ2VzLlxuICpcbiAqIFNlZSBodHRwczovL2phbnVzLmNvbmYubWVldGVjaG8uY29tL2RvY3MvcmVzdC5odG1sI3Nlc3Npb25zLlxuICoqL1xuZnVuY3Rpb24gSmFudXNTZXNzaW9uKG91dHB1dCwgb3B0aW9ucykge1xuICB0aGlzLm91dHB1dCA9IG91dHB1dDtcbiAgdGhpcy5pZCA9IHVuZGVmaW5lZDtcbiAgdGhpcy5uZXh0VHhJZCA9IDA7XG4gIHRoaXMudHhucyA9IHt9O1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgdGhpcy5vcHRpb25zID0gT2JqZWN0LmFzc2lnbih7XG4gICAgdmVyYm9zZTogZmFsc2UsXG4gICAgdGltZW91dE1zOiAxMDAwMCxcbiAgICBrZWVwYWxpdmVNczogMzAwMDBcbiAgfSwgb3B0aW9ucyk7XG59XG5cbi8qKiBDcmVhdGVzIHRoaXMgc2Vzc2lvbiBvbiB0aGUgSmFudXMgc2VydmVyIGFuZCBzZXRzIGl0cyBJRC4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmNyZWF0ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwiY3JlYXRlXCIpLnRoZW4ocmVzcCA9PiB7XG4gICAgdGhpcy5pZCA9IHJlc3AuZGF0YS5pZDtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERlc3Ryb3lzIHRoaXMgc2Vzc2lvbi4gTm90ZSB0aGF0IHVwb24gZGVzdHJ1Y3Rpb24sIEphbnVzIHdpbGwgYWxzbyBjbG9zZSB0aGUgc2lnbmFsbGluZyB0cmFuc3BvcnQgKGlmIGFwcGxpY2FibGUpIGFuZFxuICogYW55IG9wZW4gV2ViUlRDIGNvbm5lY3Rpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5kZXN0cm95ID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiB0aGlzLnNlbmQoXCJkZXN0cm95XCIpLnRoZW4oKHJlc3ApID0+IHtcbiAgICB0aGlzLmRpc3Bvc2UoKTtcbiAgICByZXR1cm4gcmVzcDtcbiAgfSk7XG59O1xuXG4vKipcbiAqIERpc3Bvc2VzIG9mIHRoaXMgc2Vzc2lvbiBpbiBhIHdheSBzdWNoIHRoYXQgbm8gZnVydGhlciBpbmNvbWluZyBzaWduYWxsaW5nIG1lc3NhZ2VzIHdpbGwgYmUgcHJvY2Vzc2VkLlxuICogT3V0c3RhbmRpbmcgdHJhbnNhY3Rpb25zIHdpbGwgYmUgcmVqZWN0ZWQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLmRpc3Bvc2UgPSBmdW5jdGlvbigpIHtcbiAgdGhpcy5fa2lsbEtlZXBhbGl2ZSgpO1xuICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7fTtcbiAgZm9yICh2YXIgdHhJZCBpbiB0aGlzLnR4bnMpIHtcbiAgICBpZiAodGhpcy50eG5zLmhhc093blByb3BlcnR5KHR4SWQpKSB7XG4gICAgICB2YXIgdHhuID0gdGhpcy50eG5zW3R4SWRdO1xuICAgICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcbiAgICAgIHR4bi5yZWplY3QobmV3IEVycm9yKFwiSmFudXMgc2Vzc2lvbiB3YXMgZGlzcG9zZWQuXCIpKTtcbiAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbdHhJZF07XG4gICAgfVxuICB9XG59O1xuXG4vKipcbiAqIFdoZXRoZXIgdGhpcyBzaWduYWwgcmVwcmVzZW50cyBhbiBlcnJvciwgYW5kIHRoZSBhc3NvY2lhdGVkIHByb21pc2UgKGlmIGFueSkgc2hvdWxkIGJlIHJlamVjdGVkLlxuICogVXNlcnMgc2hvdWxkIG92ZXJyaWRlIHRoaXMgdG8gaGFuZGxlIGFueSBjdXN0b20gcGx1Z2luLXNwZWNpZmljIGVycm9yIGNvbnZlbnRpb25zLlxuICoqL1xuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5pc0Vycm9yID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHJldHVybiBzaWduYWwuamFudXMgPT09IFwiZXJyb3JcIjtcbn07XG5cbi8qKiBSZWdpc3RlcnMgYSBjYWxsYmFjayB0byBiZSBmaXJlZCB1cG9uIHRoZSByZWNlcHRpb24gb2YgYW55IGluY29taW5nIEphbnVzIHNpZ25hbHMgZm9yIHRoaXMgc2Vzc2lvbiB3aXRoIHRoZVxuICogYGphbnVzYCBhdHRyaWJ1dGUgZXF1YWwgdG8gYGV2YC5cbiAqKi9cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUub24gPSBmdW5jdGlvbihldiwgY2FsbGJhY2spIHtcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW2V2XTtcbiAgaWYgKGhhbmRsZXJzID09IG51bGwpIHtcbiAgICBoYW5kbGVycyA9IHRoaXMuZXZlbnRIYW5kbGVyc1tldl0gPSBbXTtcbiAgfVxuICBoYW5kbGVycy5wdXNoKGNhbGxiYWNrKTtcbn07XG5cbi8qKlxuICogQ2FsbGJhY2sgZm9yIHJlY2VpdmluZyBKU09OIHNpZ25hbGxpbmcgbWVzc2FnZXMgcGVydGluZW50IHRvIHRoaXMgc2Vzc2lvbi4gSWYgdGhlIHNpZ25hbHMgYXJlIHJlc3BvbnNlcyB0byBwcmV2aW91c2x5XG4gKiBzZW50IHNpZ25hbHMsIHRoZSBwcm9taXNlcyBmb3IgdGhlIG91dGdvaW5nIHNpZ25hbHMgd2lsbCBiZSByZXNvbHZlZCBvciByZWplY3RlZCBhcHByb3ByaWF0ZWx5IHdpdGggdGhpcyBzaWduYWwgYXMgYW5cbiAqIGFyZ3VtZW50LlxuICpcbiAqIEV4dGVybmFsIGNhbGxlcnMgc2hvdWxkIGNhbGwgdGhpcyBmdW5jdGlvbiBldmVyeSB0aW1lIGEgbmV3IHNpZ25hbCBhcnJpdmVzIG9uIHRoZSB0cmFuc3BvcnQ7IGZvciBleGFtcGxlLCBpbiBhXG4gKiBXZWJTb2NrZXQncyBgbWVzc2FnZWAgZXZlbnQsIG9yIHdoZW4gYSBuZXcgZGF0dW0gc2hvd3MgdXAgaW4gYW4gSFRUUCBsb25nLXBvbGxpbmcgcmVzcG9uc2UuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnJlY2VpdmUgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgaWYgKHRoaXMub3B0aW9ucy52ZXJib3NlKSB7XG4gICAgdGhpcy5fbG9nSW5jb21pbmcoc2lnbmFsKTtcbiAgfVxuICBpZiAoc2lnbmFsLnNlc3Npb25faWQgIT0gdGhpcy5pZCkge1xuICAgIGNvbnNvbGUud2FybihcIkluY29ycmVjdCBzZXNzaW9uIElEIHJlY2VpdmVkIGluIEphbnVzIHNpZ25hbGxpbmcgbWVzc2FnZTogd2FzIFwiICsgc2lnbmFsLnNlc3Npb25faWQgKyBcIiwgZXhwZWN0ZWQgXCIgKyB0aGlzLmlkICsgXCIuXCIpO1xuICB9XG5cbiAgdmFyIHJlc3BvbnNlVHlwZSA9IHNpZ25hbC5qYW51cztcbiAgdmFyIGhhbmRsZXJzID0gdGhpcy5ldmVudEhhbmRsZXJzW3Jlc3BvbnNlVHlwZV07XG4gIGlmIChoYW5kbGVycyAhPSBudWxsKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBoYW5kbGVycy5sZW5ndGg7IGkrKykge1xuICAgICAgaGFuZGxlcnNbaV0oc2lnbmFsKTtcbiAgICB9XG4gIH1cblxuICBpZiAoc2lnbmFsLnRyYW5zYWN0aW9uICE9IG51bGwpIHtcbiAgICB2YXIgdHhuID0gdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl07XG4gICAgaWYgKHR4biA9PSBudWxsKSB7XG4gICAgICAvLyB0aGlzIGlzIGEgcmVzcG9uc2UgdG8gYSB0cmFuc2FjdGlvbiB0aGF0IHdhc24ndCBjYXVzZWQgdmlhIEphbnVzU2Vzc2lvbi5zZW5kLCBvciBhIHBsdWdpbiByZXBsaWVkIHR3aWNlIHRvIGFcbiAgICAgIC8vIHNpbmdsZSByZXF1ZXN0LCBvciB0aGUgc2Vzc2lvbiB3YXMgZGlzcG9zZWQsIG9yIHNvbWV0aGluZyBlbHNlIHRoYXQgaXNuJ3QgdW5kZXIgb3VyIHB1cnZpZXc7IHRoYXQncyBmaW5lXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHJlc3BvbnNlVHlwZSA9PT0gXCJhY2tcIiAmJiB0eG4udHlwZSA9PSBcIm1lc3NhZ2VcIikge1xuICAgICAgLy8gdGhpcyBpcyBhbiBhY2sgb2YgYW4gYXN5bmNocm9ub3VzbHktcHJvY2Vzc2VkIHBsdWdpbiByZXF1ZXN0LCB3ZSBzaG91bGQgd2FpdCB0byByZXNvbHZlIHRoZSBwcm9taXNlIHVudGlsIHRoZVxuICAgICAgLy8gYWN0dWFsIHJlc3BvbnNlIGNvbWVzIGluXG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY2xlYXJUaW1lb3V0KHR4bi50aW1lb3V0KTtcblxuICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAodGhpcy5pc0Vycm9yKHNpZ25hbCkgPyB0eG4ucmVqZWN0IDogdHhuLnJlc29sdmUpKHNpZ25hbCk7XG4gIH1cbn07XG5cbi8qKlxuICogU2VuZHMgYSBzaWduYWwgYXNzb2NpYXRlZCB3aXRoIHRoaXMgc2Vzc2lvbiwgYmVnaW5uaW5nIGEgbmV3IHRyYW5zYWN0aW9uLiBSZXR1cm5zIGEgcHJvbWlzZSB0aGF0IHdpbGwgYmUgcmVzb2x2ZWQgb3JcbiAqIHJlamVjdGVkIHdoZW4gYSByZXNwb25zZSBpcyByZWNlaXZlZCBpbiB0aGUgc2FtZSB0cmFuc2FjdGlvbiwgb3Igd2hlbiBubyByZXNwb25zZSBpcyByZWNlaXZlZCB3aXRoaW4gdGhlIHNlc3Npb25cbiAqIHRpbWVvdXQuXG4gKiovXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnNlbmQgPSBmdW5jdGlvbih0eXBlLCBzaWduYWwpIHtcbiAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHRyYW5zYWN0aW9uOiAodGhpcy5uZXh0VHhJZCsrKS50b1N0cmluZygpIH0sIHNpZ25hbCk7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgdmFyIHRpbWVvdXQgPSBudWxsO1xuICAgIGlmICh0aGlzLm9wdGlvbnMudGltZW91dE1zKSB7XG4gICAgICB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGRlbGV0ZSB0aGlzLnR4bnNbc2lnbmFsLnRyYW5zYWN0aW9uXTtcbiAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihcIlNpZ25hbGxpbmcgdHJhbnNhY3Rpb24gd2l0aCB0eGlkIFwiICsgc2lnbmFsLnRyYW5zYWN0aW9uICsgXCIgdGltZWQgb3V0LlwiKSk7XG4gICAgICB9LCB0aGlzLm9wdGlvbnMudGltZW91dE1zKTtcbiAgICB9XG4gICAgdGhpcy50eG5zW3NpZ25hbC50cmFuc2FjdGlvbl0gPSB7IHJlc29sdmU6IHJlc29sdmUsIHJlamVjdDogcmVqZWN0LCB0aW1lb3V0OiB0aW1lb3V0LCB0eXBlOiB0eXBlIH07XG4gICAgdGhpcy5fdHJhbnNtaXQodHlwZSwgc2lnbmFsKTtcbiAgfSk7XG59O1xuXG5KYW51c1Nlc3Npb24ucHJvdG90eXBlLl90cmFuc21pdCA9IGZ1bmN0aW9uKHR5cGUsIHNpZ25hbCkge1xuICBzaWduYWwgPSBPYmplY3QuYXNzaWduKHsgamFudXM6IHR5cGUgfSwgc2lnbmFsKTtcblxuICBpZiAodGhpcy5pZCAhPSBudWxsKSB7IC8vIHRoaXMuaWQgaXMgdW5kZWZpbmVkIGluIHRoZSBzcGVjaWFsIGNhc2Ugd2hlbiB3ZSdyZSBzZW5kaW5nIHRoZSBzZXNzaW9uIGNyZWF0ZSBtZXNzYWdlXG4gICAgc2lnbmFsID0gT2JqZWN0LmFzc2lnbih7IHNlc3Npb25faWQ6IHRoaXMuaWQgfSwgc2lnbmFsKTtcbiAgfVxuXG4gIGlmICh0aGlzLm9wdGlvbnMudmVyYm9zZSkge1xuICAgIHRoaXMuX2xvZ091dGdvaW5nKHNpZ25hbCk7XG4gIH1cblxuICB0aGlzLm91dHB1dChKU09OLnN0cmluZ2lmeShzaWduYWwpKTtcbiAgdGhpcy5fcmVzZXRLZWVwYWxpdmUoKTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX2xvZ091dGdvaW5nID0gZnVuY3Rpb24oc2lnbmFsKSB7XG4gIHZhciBraW5kID0gc2lnbmFsLmphbnVzO1xuICBpZiAoa2luZCA9PT0gXCJtZXNzYWdlXCIgJiYgc2lnbmFsLmpzZXApIHtcbiAgICBraW5kID0gc2lnbmFsLmpzZXAudHlwZTtcbiAgfVxuICB2YXIgbWVzc2FnZSA9IFwiPiBPdXRnb2luZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzA0MFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fbG9nSW5jb21pbmcgPSBmdW5jdGlvbihzaWduYWwpIHtcbiAgdmFyIGtpbmQgPSBzaWduYWwuamFudXM7XG4gIHZhciBtZXNzYWdlID0gc2lnbmFsLnRyYW5zYWN0aW9uID9cbiAgICAgIFwiPCBJbmNvbWluZyBKYW51cyBcIiArIChraW5kIHx8IFwic2lnbmFsXCIpICsgXCIgKCNcIiArIHNpZ25hbC50cmFuc2FjdGlvbiArIFwiKTogXCIgOlxuICAgICAgXCI8IEluY29taW5nIEphbnVzIFwiICsgKGtpbmQgfHwgXCJzaWduYWxcIikgKyBcIjogXCI7XG4gIGNvbnNvbGUuZGVidWcoXCIlY1wiICsgbWVzc2FnZSwgXCJjb2xvcjogIzAwNFwiLCBzaWduYWwpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fc2VuZEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gdGhpcy5zZW5kKFwia2VlcGFsaXZlXCIpO1xufTtcblxuSmFudXNTZXNzaW9uLnByb3RvdHlwZS5fa2lsbEtlZXBhbGl2ZSA9IGZ1bmN0aW9uKCkge1xuICBjbGVhclRpbWVvdXQodGhpcy5rZWVwYWxpdmVUaW1lb3V0KTtcbn07XG5cbkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuX3Jlc2V0S2VlcGFsaXZlID0gZnVuY3Rpb24oKSB7XG4gIHRoaXMuX2tpbGxLZWVwYWxpdmUoKTtcbiAgaWYgKHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcykge1xuICAgIHRoaXMua2VlcGFsaXZlVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgdGhpcy5fc2VuZEtlZXBhbGl2ZSgpLmNhdGNoKGUgPT4gY29uc29sZS5lcnJvcihcIkVycm9yIHJlY2VpdmVkIGZyb20ga2VlcGFsaXZlOiBcIiwgZSkpO1xuICAgIH0sIHRoaXMub3B0aW9ucy5rZWVwYWxpdmVNcyk7XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBKYW51c1BsdWdpbkhhbmRsZSxcbiAgSmFudXNTZXNzaW9uXG59O1xuIiwiLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbi8vIFNEUCBoZWxwZXJzLlxuY29uc3QgU0RQVXRpbHMgPSB7fTtcblxuLy8gR2VuZXJhdGUgYW4gYWxwaGFudW1lcmljIGlkZW50aWZpZXIgZm9yIGNuYW1lIG9yIG1pZHMuXG4vLyBUT0RPOiB1c2UgVVVJRHMgaW5zdGVhZD8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamVkLzk4Mjg4M1xuU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKDM2KS5zdWJzdHIoMiwgMTApO1xufTtcblxuLy8gVGhlIFJUQ1AgQ05BTUUgdXNlZCBieSBhbGwgcGVlcmNvbm5lY3Rpb25zIGZyb20gdGhlIHNhbWUgSlMuXG5TRFBVdGlscy5sb2NhbENOYW1lID0gU0RQVXRpbHMuZ2VuZXJhdGVJZGVudGlmaWVyKCk7XG5cbi8vIFNwbGl0cyBTRFAgaW50byBsaW5lcywgZGVhbGluZyB3aXRoIGJvdGggQ1JMRiBhbmQgTEYuXG5TRFBVdGlscy5zcGxpdExpbmVzID0gZnVuY3Rpb24oYmxvYikge1xuICByZXR1cm4gYmxvYi50cmltKCkuc3BsaXQoJ1xcbicpLm1hcChsaW5lID0+IGxpbmUudHJpbSgpKTtcbn07XG4vLyBTcGxpdHMgU0RQIGludG8gc2Vzc2lvbnBhcnQgYW5kIG1lZGlhc2VjdGlvbnMuIEVuc3VyZXMgQ1JMRi5cblNEUFV0aWxzLnNwbGl0U2VjdGlvbnMgPSBmdW5jdGlvbihibG9iKSB7XG4gIGNvbnN0IHBhcnRzID0gYmxvYi5zcGxpdCgnXFxubT0nKTtcbiAgcmV0dXJuIHBhcnRzLm1hcCgocGFydCwgaW5kZXgpID0+IChpbmRleCA+IDAgP1xuICAgICdtPScgKyBwYXJ0IDogcGFydCkudHJpbSgpICsgJ1xcclxcbicpO1xufTtcblxuLy8gUmV0dXJucyB0aGUgc2Vzc2lvbiBkZXNjcmlwdGlvbi5cblNEUFV0aWxzLmdldERlc2NyaXB0aW9uID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHJldHVybiBzZWN0aW9ucyAmJiBzZWN0aW9uc1swXTtcbn07XG5cbi8vIFJldHVybnMgdGhlIGluZGl2aWR1YWwgbWVkaWEgc2VjdGlvbnMuXG5TRFBVdGlscy5nZXRNZWRpYVNlY3Rpb25zID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBzZWN0aW9ucyA9IFNEUFV0aWxzLnNwbGl0U2VjdGlvbnMoYmxvYik7XG4gIHNlY3Rpb25zLnNoaWZ0KCk7XG4gIHJldHVybiBzZWN0aW9ucztcbn07XG5cbi8vIFJldHVybnMgbGluZXMgdGhhdCBzdGFydCB3aXRoIGEgY2VydGFpbiBwcmVmaXguXG5TRFBVdGlscy5tYXRjaFByZWZpeCA9IGZ1bmN0aW9uKGJsb2IsIHByZWZpeCkge1xuICByZXR1cm4gU0RQVXRpbHMuc3BsaXRMaW5lcyhibG9iKS5maWx0ZXIobGluZSA9PiBsaW5lLmluZGV4T2YocHJlZml4KSA9PT0gMCk7XG59O1xuXG4vLyBQYXJzZXMgYW4gSUNFIGNhbmRpZGF0ZSBsaW5lLiBTYW1wbGUgaW5wdXQ6XG4vLyBjYW5kaWRhdGU6NzAyNzg2MzUwIDIgdWRwIDQxODE5OTAyIDguOC44LjggNjA3NjkgdHlwIHJlbGF5IHJhZGRyIDguOC44Ljhcbi8vIHJwb3J0IDU1OTk2XCJcbi8vIElucHV0IGNhbiBiZSBwcmVmaXhlZCB3aXRoIGE9LlxuU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cztcbiAgLy8gUGFyc2UgYm90aCB2YXJpYW50cy5cbiAgaWYgKGxpbmUuaW5kZXhPZignYT1jYW5kaWRhdGU6JykgPT09IDApIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEyKS5zcGxpdCgnICcpO1xuICB9IGVsc2Uge1xuICAgIHBhcnRzID0gbGluZS5zdWJzdHJpbmcoMTApLnNwbGl0KCcgJyk7XG4gIH1cblxuICBjb25zdCBjYW5kaWRhdGUgPSB7XG4gICAgZm91bmRhdGlvbjogcGFydHNbMF0sXG4gICAgY29tcG9uZW50OiB7MTogJ3J0cCcsIDI6ICdydGNwJ31bcGFydHNbMV1dIHx8IHBhcnRzWzFdLFxuICAgIHByb3RvY29sOiBwYXJ0c1syXS50b0xvd2VyQ2FzZSgpLFxuICAgIHByaW9yaXR5OiBwYXJzZUludChwYXJ0c1szXSwgMTApLFxuICAgIGlwOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s0XSwgLy8gYWRkcmVzcyBpcyBhbiBhbGlhcyBmb3IgaXAuXG4gICAgcG9ydDogcGFyc2VJbnQocGFydHNbNV0sIDEwKSxcbiAgICAvLyBza2lwIHBhcnRzWzZdID09ICd0eXAnXG4gICAgdHlwZTogcGFydHNbN10sXG4gIH07XG5cbiAgZm9yIChsZXQgaSA9IDg7IGkgPCBwYXJ0cy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHN3aXRjaCAocGFydHNbaV0pIHtcbiAgICAgIGNhc2UgJ3JhZGRyJzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3Jwb3J0JzpcbiAgICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0ID0gcGFyc2VJbnQocGFydHNbaSArIDFdLCAxMCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAndGNwdHlwZSc6XG4gICAgICAgIGNhbmRpZGF0ZS50Y3BUeXBlID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3VmcmFnJzpcbiAgICAgICAgY2FuZGlkYXRlLnVmcmFnID0gcGFydHNbaSArIDFdOyAvLyBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eS5cbiAgICAgICAgY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgPSBwYXJ0c1tpICsgMV07XG4gICAgICAgIGJyZWFrO1xuICAgICAgZGVmYXVsdDogLy8gZXh0ZW5zaW9uIGhhbmRsaW5nLCBpbiBwYXJ0aWN1bGFyIHVmcmFnLiBEb24ndCBvdmVyd3JpdGUuXG4gICAgICAgIGlmIChjYW5kaWRhdGVbcGFydHNbaV1dID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBjYW5kaWRhdGVbcGFydHNbaV1dID0gcGFydHNbaSArIDFdO1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2FuZGlkYXRlO1xufTtcblxuLy8gVHJhbnNsYXRlcyBhIGNhbmRpZGF0ZSBvYmplY3QgaW50byBTRFAgY2FuZGlkYXRlIGF0dHJpYnV0ZS5cbi8vIFRoaXMgZG9lcyBub3QgaW5jbHVkZSB0aGUgYT0gcHJlZml4IVxuU0RQVXRpbHMud3JpdGVDYW5kaWRhdGUgPSBmdW5jdGlvbihjYW5kaWRhdGUpIHtcbiAgY29uc3Qgc2RwID0gW107XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5mb3VuZGF0aW9uKTtcblxuICBjb25zdCBjb21wb25lbnQgPSBjYW5kaWRhdGUuY29tcG9uZW50O1xuICBpZiAoY29tcG9uZW50ID09PSAncnRwJykge1xuICAgIHNkcC5wdXNoKDEpO1xuICB9IGVsc2UgaWYgKGNvbXBvbmVudCA9PT0gJ3J0Y3AnKSB7XG4gICAgc2RwLnB1c2goMik7XG4gIH0gZWxzZSB7XG4gICAgc2RwLnB1c2goY29tcG9uZW50KTtcbiAgfVxuICBzZHAucHVzaChjYW5kaWRhdGUucHJvdG9jb2wudG9VcHBlckNhc2UoKSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wcmlvcml0eSk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5hZGRyZXNzIHx8IGNhbmRpZGF0ZS5pcCk7XG4gIHNkcC5wdXNoKGNhbmRpZGF0ZS5wb3J0KTtcblxuICBjb25zdCB0eXBlID0gY2FuZGlkYXRlLnR5cGU7XG4gIHNkcC5wdXNoKCd0eXAnKTtcbiAgc2RwLnB1c2godHlwZSk7XG4gIGlmICh0eXBlICE9PSAnaG9zdCcgJiYgY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzICYmXG4gICAgICBjYW5kaWRhdGUucmVsYXRlZFBvcnQpIHtcbiAgICBzZHAucHVzaCgncmFkZHInKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUucmVsYXRlZEFkZHJlc3MpO1xuICAgIHNkcC5wdXNoKCdycG9ydCcpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS5yZWxhdGVkUG9ydCk7XG4gIH1cbiAgaWYgKGNhbmRpZGF0ZS50Y3BUeXBlICYmIGNhbmRpZGF0ZS5wcm90b2NvbC50b0xvd2VyQ2FzZSgpID09PSAndGNwJykge1xuICAgIHNkcC5wdXNoKCd0Y3B0eXBlJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnRjcFR5cGUpO1xuICB9XG4gIGlmIChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpIHtcbiAgICBzZHAucHVzaCgndWZyYWcnKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUudXNlcm5hbWVGcmFnbWVudCB8fCBjYW5kaWRhdGUudWZyYWcpO1xuICB9XG4gIHJldHVybiAnY2FuZGlkYXRlOicgKyBzZHAuam9pbignICcpO1xufTtcblxuLy8gUGFyc2VzIGFuIGljZS1vcHRpb25zIGxpbmUsIHJldHVybnMgYW4gYXJyYXkgb2Ygb3B0aW9uIHRhZ3MuXG4vLyBTYW1wbGUgaW5wdXQ6XG4vLyBhPWljZS1vcHRpb25zOmZvbyBiYXJcblNEUFV0aWxzLnBhcnNlSWNlT3B0aW9ucyA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgcmV0dXJuIGxpbmUuc3Vic3RyKDE0KS5zcGxpdCgnICcpO1xufTtcblxuLy8gUGFyc2VzIGEgcnRwbWFwIGxpbmUsIHJldHVybnMgUlRDUnRwQ29kZGVjUGFyYW1ldGVycy4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydHBtYXA6MTExIG9wdXMvNDgwMDAvMlxuU0RQVXRpbHMucGFyc2VSdHBNYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGxldCBwYXJ0cyA9IGxpbmUuc3Vic3RyKDkpLnNwbGl0KCcgJyk7XG4gIGNvbnN0IHBhcnNlZCA9IHtcbiAgICBwYXlsb2FkVHlwZTogcGFyc2VJbnQocGFydHMuc2hpZnQoKSwgMTApLCAvLyB3YXM6IGlkXG4gIH07XG5cbiAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnLycpO1xuXG4gIHBhcnNlZC5uYW1lID0gcGFydHNbMF07XG4gIHBhcnNlZC5jbG9ja1JhdGUgPSBwYXJzZUludChwYXJ0c1sxXSwgMTApOyAvLyB3YXM6IGNsb2NrcmF0ZVxuICBwYXJzZWQuY2hhbm5lbHMgPSBwYXJ0cy5sZW5ndGggPT09IDMgPyBwYXJzZUludChwYXJ0c1syXSwgMTApIDogMTtcbiAgLy8gbGVnYWN5IGFsaWFzLCBnb3QgcmVuYW1lZCBiYWNrIHRvIGNoYW5uZWxzIGluIE9SVEMuXG4gIHBhcnNlZC5udW1DaGFubmVscyA9IHBhcnNlZC5jaGFubmVscztcbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIHJ0cG1hcCBsaW5lIGZyb20gUlRDUnRwQ29kZWNDYXBhYmlsaXR5IG9yXG4vLyBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0cE1hcCA9IGZ1bmN0aW9uKGNvZGVjKSB7XG4gIGxldCBwdCA9IGNvZGVjLnBheWxvYWRUeXBlO1xuICBpZiAoY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGUgIT09IHVuZGVmaW5lZCkge1xuICAgIHB0ID0gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gIH1cbiAgY29uc3QgY2hhbm5lbHMgPSBjb2RlYy5jaGFubmVscyB8fCBjb2RlYy5udW1DaGFubmVscyB8fCAxO1xuICByZXR1cm4gJ2E9cnRwbWFwOicgKyBwdCArICcgJyArIGNvZGVjLm5hbWUgKyAnLycgKyBjb2RlYy5jbG9ja1JhdGUgK1xuICAgICAgKGNoYW5uZWxzICE9PSAxID8gJy8nICsgY2hhbm5lbHMgOiAnJykgKyAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyBhIGV4dG1hcCBsaW5lIChoZWFkZXJleHRlbnNpb24gZnJvbSBSRkMgNTI4NSkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9ZXh0bWFwOjIgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuLy8gYT1leHRtYXA6Mi9zZW5kb25seSB1cm46aWV0ZjpwYXJhbXM6cnRwLWhkcmV4dDp0b2Zmc2V0XG5TRFBVdGlscy5wYXJzZUV4dG1hcCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cig5KS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGlkOiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgIGRpcmVjdGlvbjogcGFydHNbMF0uaW5kZXhPZignLycpID4gMCA/IHBhcnRzWzBdLnNwbGl0KCcvJylbMV0gOiAnc2VuZHJlY3YnLFxuICAgIHVyaTogcGFydHNbMV0sXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZXMgYW4gZXh0bWFwIGxpbmUgZnJvbSBSVENSdHBIZWFkZXJFeHRlbnNpb25QYXJhbWV0ZXJzIG9yXG4vLyBSVENSdHBIZWFkZXJFeHRlbnNpb24uXG5TRFBVdGlscy53cml0ZUV4dG1hcCA9IGZ1bmN0aW9uKGhlYWRlckV4dGVuc2lvbikge1xuICByZXR1cm4gJ2E9ZXh0bWFwOicgKyAoaGVhZGVyRXh0ZW5zaW9uLmlkIHx8IGhlYWRlckV4dGVuc2lvbi5wcmVmZXJyZWRJZCkgK1xuICAgICAgKGhlYWRlckV4dGVuc2lvbi5kaXJlY3Rpb24gJiYgaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvbiAhPT0gJ3NlbmRyZWN2J1xuICAgICAgICA/ICcvJyArIGhlYWRlckV4dGVuc2lvbi5kaXJlY3Rpb25cbiAgICAgICAgOiAnJykgK1xuICAgICAgJyAnICsgaGVhZGVyRXh0ZW5zaW9uLnVyaSArICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIGEgZm10cCBsaW5lLCByZXR1cm5zIGRpY3Rpb25hcnkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9Zm10cDo5NiB2YnI9b247Y25nPW9uXG4vLyBBbHNvIGRlYWxzIHdpdGggdmJyPW9uOyBjbmc9b25cblNEUFV0aWxzLnBhcnNlRm10cCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFyc2VkID0ge307XG4gIGxldCBrdjtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cihsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCc7Jyk7XG4gIGZvciAobGV0IGogPSAwOyBqIDwgcGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICBrdiA9IHBhcnRzW2pdLnRyaW0oKS5zcGxpdCgnPScpO1xuICAgIHBhcnNlZFtrdlswXS50cmltKCldID0ga3ZbMV07XG4gIH1cbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIGZtdHAgbGluZSBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZUZtdHAgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZSA9ICcnO1xuICBsZXQgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5wYXJhbWV0ZXJzICYmIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmxlbmd0aCkge1xuICAgIGNvbnN0IHBhcmFtcyA9IFtdO1xuICAgIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmZvckVhY2gocGFyYW0gPT4ge1xuICAgICAgaWYgKGNvZGVjLnBhcmFtZXRlcnNbcGFyYW1dICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcGFyYW1zLnB1c2gocGFyYW0gKyAnPScgKyBjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwYXJhbXMucHVzaChwYXJhbSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgbGluZSArPSAnYT1mbXRwOicgKyBwdCArICcgJyArIHBhcmFtcy5qb2luKCc7JykgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gbGluZTtcbn07XG5cbi8vIFBhcnNlcyBhIHJ0Y3AtZmIgbGluZSwgcmV0dXJucyBSVENQUnRjcEZlZWRiYWNrIG9iamVjdC4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydGNwLWZiOjk4IG5hY2sgcnBzaVxuU0RQVXRpbHMucGFyc2VSdGNwRmIgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIobGluZS5pbmRleE9mKCcgJykgKyAxKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHR5cGU6IHBhcnRzLnNoaWZ0KCksXG4gICAgcGFyYW1ldGVyOiBwYXJ0cy5qb2luKCcgJyksXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZSBhPXJ0Y3AtZmIgbGluZXMgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3IgUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdGNwRmIgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZXMgPSAnJztcbiAgbGV0IHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICBpZiAoY29kZWMucnRjcEZlZWRiYWNrICYmIGNvZGVjLnJ0Y3BGZWVkYmFjay5sZW5ndGgpIHtcbiAgICAvLyBGSVhNRTogc3BlY2lhbCBoYW5kbGluZyBmb3IgdHJyLWludD9cbiAgICBjb2RlYy5ydGNwRmVlZGJhY2suZm9yRWFjaChmYiA9PiB7XG4gICAgICBsaW5lcyArPSAnYT1ydGNwLWZiOicgKyBwdCArICcgJyArIGZiLnR5cGUgK1xuICAgICAgKGZiLnBhcmFtZXRlciAmJiBmYi5wYXJhbWV0ZXIubGVuZ3RoID8gJyAnICsgZmIucGFyYW1ldGVyIDogJycpICtcbiAgICAgICAgICAnXFxyXFxuJztcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbGluZXM7XG59O1xuXG4vLyBQYXJzZXMgYSBSRkMgNTU3NiBzc3JjIG1lZGlhIGF0dHJpYnV0ZS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjOjM3MzU5Mjg1NTkgY25hbWU6c29tZXRoaW5nXG5TRFBVdGlscy5wYXJzZVNzcmNNZWRpYSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3Qgc3AgPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgY29uc3QgcGFydHMgPSB7XG4gICAgc3NyYzogcGFyc2VJbnQobGluZS5zdWJzdHIoNywgc3AgLSA3KSwgMTApLFxuICB9O1xuICBjb25zdCBjb2xvbiA9IGxpbmUuaW5kZXhPZignOicsIHNwKTtcbiAgaWYgKGNvbG9uID4gLTEpIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cihzcCArIDEsIGNvbG9uIC0gc3AgLSAxKTtcbiAgICBwYXJ0cy52YWx1ZSA9IGxpbmUuc3Vic3RyKGNvbG9uICsgMSk7XG4gIH0gZWxzZSB7XG4gICAgcGFydHMuYXR0cmlidXRlID0gbGluZS5zdWJzdHIoc3AgKyAxKTtcbiAgfVxuICByZXR1cm4gcGFydHM7XG59O1xuXG4vLyBQYXJzZSBhIHNzcmMtZ3JvdXAgbGluZSAoc2VlIFJGQyA1NTc2KS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjLWdyb3VwOnNlbWFudGljcyAxMiAzNFxuU0RQVXRpbHMucGFyc2VTc3JjR3JvdXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoMTMpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgc2VtYW50aWNzOiBwYXJ0cy5zaGlmdCgpLFxuICAgIHNzcmNzOiBwYXJ0cy5tYXAoc3NyYyA9PiBwYXJzZUludChzc3JjLCAxMCkpLFxuICB9O1xufTtcblxuLy8gRXh0cmFjdHMgdGhlIE1JRCAoUkZDIDU4ODgpIGZyb20gYSBtZWRpYSBzZWN0aW9uLlxuLy8gUmV0dXJucyB0aGUgTUlEIG9yIHVuZGVmaW5lZCBpZiBubyBtaWQgbGluZSB3YXMgZm91bmQuXG5TRFBVdGlscy5nZXRNaWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbWlkID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1taWQ6JylbMF07XG4gIGlmIChtaWQpIHtcbiAgICByZXR1cm4gbWlkLnN1YnN0cig2KTtcbiAgfVxufTtcblxuLy8gUGFyc2VzIGEgZmluZ2VycHJpbnQgbGluZSBmb3IgRFRMUy1TUlRQLlxuU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cigxNCkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBhbGdvcml0aG06IHBhcnRzWzBdLnRvTG93ZXJDYXNlKCksIC8vIGFsZ29yaXRobSBpcyBjYXNlLXNlbnNpdGl2ZSBpbiBFZGdlLlxuICAgIHZhbHVlOiBwYXJ0c1sxXS50b1VwcGVyQ2FzZSgpLCAvLyB0aGUgZGVmaW5pdGlvbiBpcyB1cHBlci1jYXNlIGluIFJGQyA0NTcyLlxuICB9O1xufTtcblxuLy8gRXh0cmFjdHMgRFRMUyBwYXJhbWV0ZXJzIGZyb20gU0RQIG1lZGlhIHNlY3Rpb24gb3Igc2Vzc2lvbnBhcnQuXG4vLyBGSVhNRTogZm9yIGNvbnNpc3RlbmN5IHdpdGggb3RoZXIgZnVuY3Rpb25zIHRoaXMgc2hvdWxkIG9ubHlcbi8vICAgZ2V0IHRoZSBmaW5nZXJwcmludCBsaW5lIGFzIGlucHV0LiBTZWUgYWxzbyBnZXRJY2VQYXJhbWV0ZXJzLlxuU0RQVXRpbHMuZ2V0RHRsc1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9ZmluZ2VycHJpbnQ6Jyk7XG4gIC8vIE5vdGU6IGE9c2V0dXAgbGluZSBpcyBpZ25vcmVkIHNpbmNlIHdlIHVzZSB0aGUgJ2F1dG8nIHJvbGUgaW4gRWRnZS5cbiAgcmV0dXJuIHtcbiAgICByb2xlOiAnYXV0bycsXG4gICAgZmluZ2VycHJpbnRzOiBsaW5lcy5tYXAoU0RQVXRpbHMucGFyc2VGaW5nZXJwcmludCksXG4gIH07XG59O1xuXG4vLyBTZXJpYWxpemVzIERUTFMgcGFyYW1ldGVycyB0byBTRFAuXG5TRFBVdGlscy53cml0ZUR0bHNQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zLCBzZXR1cFR5cGUpIHtcbiAgbGV0IHNkcCA9ICdhPXNldHVwOicgKyBzZXR1cFR5cGUgKyAnXFxyXFxuJztcbiAgcGFyYW1zLmZpbmdlcnByaW50cy5mb3JFYWNoKGZwID0+IHtcbiAgICBzZHAgKz0gJ2E9ZmluZ2VycHJpbnQ6JyArIGZwLmFsZ29yaXRobSArICcgJyArIGZwLnZhbHVlICsgJ1xcclxcbic7XG4gIH0pO1xuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIGE9Y3J5cHRvIGxpbmVzIGludG9cbi8vICAgaHR0cHM6Ly9yYXdnaXQuY29tL2Fib2JhL2VkZ2VydGMvbWFzdGVyL21zb3J0Yy1yczQuaHRtbCNkaWN0aW9uYXJ5LXJ0Y3NydHBzZGVzcGFyYW1ldGVycy1tZW1iZXJzXG5TRFBVdGlscy5wYXJzZUNyeXB0b0xpbmUgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoOSkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICB0YWc6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgY3J5cHRvU3VpdGU6IHBhcnRzWzFdLFxuICAgIGtleVBhcmFtczogcGFydHNbMl0sXG4gICAgc2Vzc2lvblBhcmFtczogcGFydHMuc2xpY2UoMyksXG4gIH07XG59O1xuXG5TRFBVdGlscy53cml0ZUNyeXB0b0xpbmUgPSBmdW5jdGlvbihwYXJhbWV0ZXJzKSB7XG4gIHJldHVybiAnYT1jcnlwdG86JyArIHBhcmFtZXRlcnMudGFnICsgJyAnICtcbiAgICBwYXJhbWV0ZXJzLmNyeXB0b1N1aXRlICsgJyAnICtcbiAgICAodHlwZW9mIHBhcmFtZXRlcnMua2V5UGFyYW1zID09PSAnb2JqZWN0J1xuICAgICAgPyBTRFBVdGlscy53cml0ZUNyeXB0b0tleVBhcmFtcyhwYXJhbWV0ZXJzLmtleVBhcmFtcylcbiAgICAgIDogcGFyYW1ldGVycy5rZXlQYXJhbXMpICtcbiAgICAocGFyYW1ldGVycy5zZXNzaW9uUGFyYW1zID8gJyAnICsgcGFyYW1ldGVycy5zZXNzaW9uUGFyYW1zLmpvaW4oJyAnKSA6ICcnKSArXG4gICAgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgdGhlIGNyeXB0byBrZXkgcGFyYW1ldGVycyBpbnRvXG4vLyAgIGh0dHBzOi8vcmF3Z2l0LmNvbS9hYm9iYS9lZGdlcnRjL21hc3Rlci9tc29ydGMtcnM0Lmh0bWwjcnRjc3J0cGtleXBhcmFtKlxuU0RQVXRpbHMucGFyc2VDcnlwdG9LZXlQYXJhbXMgPSBmdW5jdGlvbihrZXlQYXJhbXMpIHtcbiAgaWYgKGtleVBhcmFtcy5pbmRleE9mKCdpbmxpbmU6JykgIT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBwYXJ0cyA9IGtleVBhcmFtcy5zdWJzdHIoNykuc3BsaXQoJ3wnKTtcbiAgcmV0dXJuIHtcbiAgICBrZXlNZXRob2Q6ICdpbmxpbmUnLFxuICAgIGtleVNhbHQ6IHBhcnRzWzBdLFxuICAgIGxpZmVUaW1lOiBwYXJ0c1sxXSxcbiAgICBta2lWYWx1ZTogcGFydHNbMl0gPyBwYXJ0c1syXS5zcGxpdCgnOicpWzBdIDogdW5kZWZpbmVkLFxuICAgIG1raUxlbmd0aDogcGFydHNbMl0gPyBwYXJ0c1syXS5zcGxpdCgnOicpWzFdIDogdW5kZWZpbmVkLFxuICB9O1xufTtcblxuU0RQVXRpbHMud3JpdGVDcnlwdG9LZXlQYXJhbXMgPSBmdW5jdGlvbihrZXlQYXJhbXMpIHtcbiAgcmV0dXJuIGtleVBhcmFtcy5rZXlNZXRob2QgKyAnOidcbiAgICArIGtleVBhcmFtcy5rZXlTYWx0ICtcbiAgICAoa2V5UGFyYW1zLmxpZmVUaW1lID8gJ3wnICsga2V5UGFyYW1zLmxpZmVUaW1lIDogJycpICtcbiAgICAoa2V5UGFyYW1zLm1raVZhbHVlICYmIGtleVBhcmFtcy5ta2lMZW5ndGhcbiAgICAgID8gJ3wnICsga2V5UGFyYW1zLm1raVZhbHVlICsgJzonICsga2V5UGFyYW1zLm1raUxlbmd0aFxuICAgICAgOiAnJyk7XG59O1xuXG4vLyBFeHRyYWN0cyBhbGwgU0RFUyBwYXJhbWV0ZXJzLlxuU0RQVXRpbHMuZ2V0Q3J5cHRvUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1jcnlwdG86Jyk7XG4gIHJldHVybiBsaW5lcy5tYXAoU0RQVXRpbHMucGFyc2VDcnlwdG9MaW5lKTtcbn07XG5cbi8vIFBhcnNlcyBJQ0UgaW5mb3JtYXRpb24gZnJvbSBTRFAgbWVkaWEgc2VjdGlvbiBvciBzZXNzaW9ucGFydC5cbi8vIEZJWE1FOiBmb3IgY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMgdGhpcyBzaG91bGQgb25seVxuLy8gICBnZXQgdGhlIGljZS11ZnJhZyBhbmQgaWNlLXB3ZCBsaW5lcyBhcyBpbnB1dC5cblNEUFV0aWxzLmdldEljZVBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIGNvbnN0IHVmcmFnID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uICsgc2Vzc2lvbnBhcnQsXG4gICAgJ2E9aWNlLXVmcmFnOicpWzBdO1xuICBjb25zdCBwd2QgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1pY2UtcHdkOicpWzBdO1xuICBpZiAoISh1ZnJhZyAmJiBwd2QpKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgcmV0dXJuIHtcbiAgICB1c2VybmFtZUZyYWdtZW50OiB1ZnJhZy5zdWJzdHIoMTIpLFxuICAgIHBhc3N3b3JkOiBwd2Quc3Vic3RyKDEwKSxcbiAgfTtcbn07XG5cbi8vIFNlcmlhbGl6ZXMgSUNFIHBhcmFtZXRlcnMgdG8gU0RQLlxuU0RQVXRpbHMud3JpdGVJY2VQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocGFyYW1zKSB7XG4gIGxldCBzZHAgPSAnYT1pY2UtdWZyYWc6JyArIHBhcmFtcy51c2VybmFtZUZyYWdtZW50ICsgJ1xcclxcbicgK1xuICAgICAgJ2E9aWNlLXB3ZDonICsgcGFyYW1zLnBhc3N3b3JkICsgJ1xcclxcbic7XG4gIGlmIChwYXJhbXMuaWNlTGl0ZSkge1xuICAgIHNkcCArPSAnYT1pY2UtbGl0ZVxcclxcbic7XG4gIH1cbiAgcmV0dXJuIHNkcDtcbn07XG5cbi8vIFBhcnNlcyB0aGUgU0RQIG1lZGlhIHNlY3Rpb24gYW5kIHJldHVybnMgUlRDUnRwUGFyYW1ldGVycy5cblNEUFV0aWxzLnBhcnNlUnRwUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBkZXNjcmlwdGlvbiA9IHtcbiAgICBjb2RlY3M6IFtdLFxuICAgIGhlYWRlckV4dGVuc2lvbnM6IFtdLFxuICAgIGZlY01lY2hhbmlzbXM6IFtdLFxuICAgIHJ0Y3A6IFtdLFxuICB9O1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICBmb3IgKGxldCBpID0gMzsgaSA8IG1saW5lLmxlbmd0aDsgaSsrKSB7IC8vIGZpbmQgYWxsIGNvZGVjcyBmcm9tIG1saW5lWzMuLl1cbiAgICBjb25zdCBwdCA9IG1saW5lW2ldO1xuICAgIGNvbnN0IHJ0cG1hcGxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRwbWFwOicgKyBwdCArICcgJylbMF07XG4gICAgaWYgKHJ0cG1hcGxpbmUpIHtcbiAgICAgIGNvbnN0IGNvZGVjID0gU0RQVXRpbHMucGFyc2VSdHBNYXAocnRwbWFwbGluZSk7XG4gICAgICBjb25zdCBmbXRwcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPWZtdHA6JyArIHB0ICsgJyAnKTtcbiAgICAgIC8vIE9ubHkgdGhlIGZpcnN0IGE9Zm10cDo8cHQ+IGlzIGNvbnNpZGVyZWQuXG4gICAgICBjb2RlYy5wYXJhbWV0ZXJzID0gZm10cHMubGVuZ3RoID8gU0RQVXRpbHMucGFyc2VGbXRwKGZtdHBzWzBdKSA6IHt9O1xuICAgICAgY29kZWMucnRjcEZlZWRiYWNrID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoXG4gICAgICAgIG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1mYjonICsgcHQgKyAnICcpXG4gICAgICAgIC5tYXAoU0RQVXRpbHMucGFyc2VSdGNwRmIpO1xuICAgICAgZGVzY3JpcHRpb24uY29kZWNzLnB1c2goY29kZWMpO1xuICAgICAgLy8gcGFyc2UgRkVDIG1lY2hhbmlzbXMgZnJvbSBydHBtYXAgbGluZXMuXG4gICAgICBzd2l0Y2ggKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSkge1xuICAgICAgICBjYXNlICdSRUQnOlxuICAgICAgICBjYXNlICdVTFBGRUMnOlxuICAgICAgICAgIGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMucHVzaChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OiAvLyBvbmx5IFJFRCBhbmQgVUxQRkVDIGFyZSByZWNvZ25pemVkIGFzIEZFQyBtZWNoYW5pc21zLlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuICBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPWV4dG1hcDonKS5mb3JFYWNoKGxpbmUgPT4ge1xuICAgIGRlc2NyaXB0aW9uLmhlYWRlckV4dGVuc2lvbnMucHVzaChTRFBVdGlscy5wYXJzZUV4dG1hcChsaW5lKSk7XG4gIH0pO1xuICAvLyBGSVhNRTogcGFyc2UgcnRjcC5cbiAgcmV0dXJuIGRlc2NyaXB0aW9uO1xufTtcblxuLy8gR2VuZXJhdGVzIHBhcnRzIG9mIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBkZXNjcmliaW5nIHRoZSBjYXBhYmlsaXRpZXMgL1xuLy8gcGFyYW1ldGVycy5cblNEUFV0aWxzLndyaXRlUnRwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihraW5kLCBjYXBzKSB7XG4gIGxldCBzZHAgPSAnJztcblxuICAvLyBCdWlsZCB0aGUgbWxpbmUuXG4gIHNkcCArPSAnbT0nICsga2luZCArICcgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLmxlbmd0aCA+IDAgPyAnOScgOiAnMCc7IC8vIHJlamVjdCBpZiBubyBjb2RlY3MuXG4gIHNkcCArPSAnIFVEUC9UTFMvUlRQL1NBVlBGICc7XG4gIHNkcCArPSBjYXBzLmNvZGVjcy5tYXAoY29kZWMgPT4ge1xuICAgIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gY29kZWMucHJlZmVycmVkUGF5bG9hZFR5cGU7XG4gICAgfVxuICAgIHJldHVybiBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgfSkuam9pbignICcpICsgJ1xcclxcbic7XG5cbiAgc2RwICs9ICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJztcbiAgc2RwICs9ICdhPXJ0Y3A6OSBJTiBJUDQgMC4wLjAuMFxcclxcbic7XG5cbiAgLy8gQWRkIGE9cnRwbWFwIGxpbmVzIGZvciBlYWNoIGNvZGVjLiBBbHNvIGZtdHAgYW5kIHJ0Y3AtZmIuXG4gIGNhcHMuY29kZWNzLmZvckVhY2goY29kZWMgPT4ge1xuICAgIHNkcCArPSBTRFBVdGlscy53cml0ZVJ0cE1hcChjb2RlYyk7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlRm10cChjb2RlYyk7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlUnRjcEZiKGNvZGVjKTtcbiAgfSk7XG4gIGxldCBtYXhwdGltZSA9IDA7XG4gIGNhcHMuY29kZWNzLmZvckVhY2goY29kZWMgPT4ge1xuICAgIGlmIChjb2RlYy5tYXhwdGltZSA+IG1heHB0aW1lKSB7XG4gICAgICBtYXhwdGltZSA9IGNvZGVjLm1heHB0aW1lO1xuICAgIH1cbiAgfSk7XG4gIGlmIChtYXhwdGltZSA+IDApIHtcbiAgICBzZHAgKz0gJ2E9bWF4cHRpbWU6JyArIG1heHB0aW1lICsgJ1xcclxcbic7XG4gIH1cblxuICBpZiAoY2Fwcy5oZWFkZXJFeHRlbnNpb25zKSB7XG4gICAgY2Fwcy5oZWFkZXJFeHRlbnNpb25zLmZvckVhY2goZXh0ZW5zaW9uID0+IHtcbiAgICAgIHNkcCArPSBTRFBVdGlscy53cml0ZUV4dG1hcChleHRlbnNpb24pO1xuICAgIH0pO1xuICB9XG4gIC8vIEZJWE1FOiB3cml0ZSBmZWNNZWNoYW5pc21zLlxuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBhbmQgcmV0dXJucyBhbiBhcnJheSBvZlxuLy8gUlRDUnRwRW5jb2RpbmdQYXJhbWV0ZXJzLlxuU0RQVXRpbHMucGFyc2VSdHBFbmNvZGluZ1BhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgZW5jb2RpbmdQYXJhbWV0ZXJzID0gW107XG4gIGNvbnN0IGRlc2NyaXB0aW9uID0gU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IGhhc1JlZCA9IGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMuaW5kZXhPZignUkVEJykgIT09IC0xO1xuICBjb25zdCBoYXNVbHBmZWMgPSBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLmluZGV4T2YoJ1VMUEZFQycpICE9PSAtMTtcblxuICAvLyBmaWx0ZXIgYT1zc3JjOi4uLiBjbmFtZTosIGlnbm9yZSBQbGFuQi1tc2lkXG4gIGNvbnN0IHNzcmNzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChsaW5lID0+IFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpKVxuICAgIC5maWx0ZXIocGFydHMgPT4gcGFydHMuYXR0cmlidXRlID09PSAnY25hbWUnKTtcbiAgY29uc3QgcHJpbWFyeVNzcmMgPSBzc3Jjcy5sZW5ndGggPiAwICYmIHNzcmNzWzBdLnNzcmM7XG4gIGxldCBzZWNvbmRhcnlTc3JjO1xuXG4gIGNvbnN0IGZsb3dzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjLWdyb3VwOkZJRCcpXG4gICAgLm1hcChsaW5lID0+IHtcbiAgICAgIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHIoMTcpLnNwbGl0KCcgJyk7XG4gICAgICByZXR1cm4gcGFydHMubWFwKHBhcnQgPT4gcGFyc2VJbnQocGFydCwgMTApKTtcbiAgICB9KTtcbiAgaWYgKGZsb3dzLmxlbmd0aCA+IDAgJiYgZmxvd3NbMF0ubGVuZ3RoID4gMSAmJiBmbG93c1swXVswXSA9PT0gcHJpbWFyeVNzcmMpIHtcbiAgICBzZWNvbmRhcnlTc3JjID0gZmxvd3NbMF1bMV07XG4gIH1cblxuICBkZXNjcmlwdGlvbi5jb2RlY3MuZm9yRWFjaChjb2RlYyA9PiB7XG4gICAgaWYgKGNvZGVjLm5hbWUudG9VcHBlckNhc2UoKSA9PT0gJ1JUWCcgJiYgY29kZWMucGFyYW1ldGVycy5hcHQpIHtcbiAgICAgIGxldCBlbmNQYXJhbSA9IHtcbiAgICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgICAgIGNvZGVjUGF5bG9hZFR5cGU6IHBhcnNlSW50KGNvZGVjLnBhcmFtZXRlcnMuYXB0LCAxMCksXG4gICAgICB9O1xuICAgICAgaWYgKHByaW1hcnlTc3JjICYmIHNlY29uZGFyeVNzcmMpIHtcbiAgICAgICAgZW5jUGFyYW0ucnR4ID0ge3NzcmM6IHNlY29uZGFyeVNzcmN9O1xuICAgICAgfVxuICAgICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goZW5jUGFyYW0pO1xuICAgICAgaWYgKGhhc1JlZCkge1xuICAgICAgICBlbmNQYXJhbSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoZW5jUGFyYW0pKTtcbiAgICAgICAgZW5jUGFyYW0uZmVjID0ge1xuICAgICAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgICAgICAgIG1lY2hhbmlzbTogaGFzVWxwZmVjID8gJ3JlZCt1bHBmZWMnIDogJ3JlZCcsXG4gICAgICAgIH07XG4gICAgICAgIGVuY29kaW5nUGFyYW1ldGVycy5wdXNoKGVuY1BhcmFtKTtcbiAgICAgIH1cbiAgICB9XG4gIH0pO1xuICBpZiAoZW5jb2RpbmdQYXJhbWV0ZXJzLmxlbmd0aCA9PT0gMCAmJiBwcmltYXJ5U3NyYykge1xuICAgIGVuY29kaW5nUGFyYW1ldGVycy5wdXNoKHtcbiAgICAgIHNzcmM6IHByaW1hcnlTc3JjLFxuICAgIH0pO1xuICB9XG5cbiAgLy8gd2Ugc3VwcG9ydCBib3RoIGI9QVMgYW5kIGI9VElBUyBidXQgaW50ZXJwcmV0IEFTIGFzIFRJQVMuXG4gIGxldCBiYW5kd2lkdGggPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdiPScpO1xuICBpZiAoYmFuZHdpZHRoLmxlbmd0aCkge1xuICAgIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1USUFTOicpID09PSAwKSB7XG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyKDcpLCAxMCk7XG4gICAgfSBlbHNlIGlmIChiYW5kd2lkdGhbMF0uaW5kZXhPZignYj1BUzonKSA9PT0gMCkge1xuICAgICAgLy8gdXNlIGZvcm11bGEgZnJvbSBKU0VQIHRvIGNvbnZlcnQgYj1BUyB0byBUSUFTIHZhbHVlLlxuICAgICAgYmFuZHdpZHRoID0gcGFyc2VJbnQoYmFuZHdpZHRoWzBdLnN1YnN0cig1KSwgMTApICogMTAwMCAqIDAuOTVcbiAgICAgICAgICAtICg1MCAqIDQwICogOCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGJhbmR3aWR0aCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gICAgZW5jb2RpbmdQYXJhbWV0ZXJzLmZvckVhY2gocGFyYW1zID0+IHtcbiAgICAgIHBhcmFtcy5tYXhCaXRyYXRlID0gYmFuZHdpZHRoO1xuICAgIH0pO1xuICB9XG4gIHJldHVybiBlbmNvZGluZ1BhcmFtZXRlcnM7XG59O1xuXG4vLyBwYXJzZXMgaHR0cDovL2RyYWZ0Lm9ydGMub3JnLyNydGNydGNwcGFyYW1ldGVycypcblNEUFV0aWxzLnBhcnNlUnRjcFBhcmFtZXRlcnMgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgcnRjcFBhcmFtZXRlcnMgPSB7fTtcblxuICAvLyBHZXRzIHRoZSBmaXJzdCBTU1JDLiBOb3RlIHRoYXQgd2l0aCBSVFggdGhlcmUgbWlnaHQgYmUgbXVsdGlwbGVcbiAgLy8gU1NSQ3MuXG4gIGNvbnN0IHJlbW90ZVNzcmMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmM6JylcbiAgICAubWFwKGxpbmUgPT4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSkpXG4gICAgLmZpbHRlcihvYmogPT4gb2JqLmF0dHJpYnV0ZSA9PT0gJ2NuYW1lJylbMF07XG4gIGlmIChyZW1vdGVTc3JjKSB7XG4gICAgcnRjcFBhcmFtZXRlcnMuY25hbWUgPSByZW1vdGVTc3JjLnZhbHVlO1xuICAgIHJ0Y3BQYXJhbWV0ZXJzLnNzcmMgPSByZW1vdGVTc3JjLnNzcmM7XG4gIH1cblxuICAvLyBFZGdlIHVzZXMgdGhlIGNvbXBvdW5kIGF0dHJpYnV0ZSBpbnN0ZWFkIG9mIHJlZHVjZWRTaXplXG4gIC8vIGNvbXBvdW5kIGlzICFyZWR1Y2VkU2l6ZVxuICBjb25zdCByc2l6ZSA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1yc2l6ZScpO1xuICBydGNwUGFyYW1ldGVycy5yZWR1Y2VkU2l6ZSA9IHJzaXplLmxlbmd0aCA+IDA7XG4gIHJ0Y3BQYXJhbWV0ZXJzLmNvbXBvdW5kID0gcnNpemUubGVuZ3RoID09PSAwO1xuXG4gIC8vIHBhcnNlcyB0aGUgcnRjcC1tdXggYXR0ctGWYnV0ZS5cbiAgLy8gTm90ZSB0aGF0IEVkZ2UgZG9lcyBub3Qgc3VwcG9ydCB1bm11eGVkIFJUQ1AuXG4gIGNvbnN0IG11eCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9cnRjcC1tdXgnKTtcbiAgcnRjcFBhcmFtZXRlcnMubXV4ID0gbXV4Lmxlbmd0aCA+IDA7XG5cbiAgcmV0dXJuIHJ0Y3BQYXJhbWV0ZXJzO1xufTtcblxuU0RQVXRpbHMud3JpdGVSdGNwUGFyYW1ldGVycyA9IGZ1bmN0aW9uKHJ0Y3BQYXJhbWV0ZXJzKSB7XG4gIGxldCBzZHAgPSAnJztcbiAgaWYgKHJ0Y3BQYXJhbWV0ZXJzLnJlZHVjZWRTaXplKSB7XG4gICAgc2RwICs9ICdhPXJ0Y3AtcnNpemVcXHJcXG4nO1xuICB9XG4gIGlmIChydGNwUGFyYW1ldGVycy5tdXgpIHtcbiAgICBzZHAgKz0gJ2E9cnRjcC1tdXhcXHJcXG4nO1xuICB9XG4gIGlmIChydGNwUGFyYW1ldGVycy5zc3JjICE9PSB1bmRlZmluZWQgJiYgcnRjcFBhcmFtZXRlcnMuY25hbWUpIHtcbiAgICBzZHAgKz0gJ2E9c3NyYzonICsgcnRjcFBhcmFtZXRlcnMuc3NyYyArXG4gICAgICAnIGNuYW1lOicgKyBydGNwUGFyYW1ldGVycy5jbmFtZSArICdcXHJcXG4nO1xuICB9XG4gIHJldHVybiBzZHA7XG59O1xuXG5cbi8vIHBhcnNlcyBlaXRoZXIgYT1tc2lkOiBvciBhPXNzcmM6Li4uIG1zaWQgbGluZXMgYW5kIHJldHVybnNcbi8vIHRoZSBpZCBvZiB0aGUgTWVkaWFTdHJlYW0gYW5kIE1lZGlhU3RyZWFtVHJhY2suXG5TRFBVdGlscy5wYXJzZU1zaWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgbGV0IHBhcnRzO1xuICBjb25zdCBzcGVjID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1tc2lkOicpO1xuICBpZiAoc3BlYy5sZW5ndGggPT09IDEpIHtcbiAgICBwYXJ0cyA9IHNwZWNbMF0uc3Vic3RyKDcpLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtzdHJlYW06IHBhcnRzWzBdLCB0cmFjazogcGFydHNbMV19O1xuICB9XG4gIGNvbnN0IHBsYW5CID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zc3JjOicpXG4gICAgLm1hcChsaW5lID0+IFNEUFV0aWxzLnBhcnNlU3NyY01lZGlhKGxpbmUpKVxuICAgIC5maWx0ZXIobXNpZFBhcnRzID0+IG1zaWRQYXJ0cy5hdHRyaWJ1dGUgPT09ICdtc2lkJyk7XG4gIGlmIChwbGFuQi5sZW5ndGggPiAwKSB7XG4gICAgcGFydHMgPSBwbGFuQlswXS52YWx1ZS5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7c3RyZWFtOiBwYXJ0c1swXSwgdHJhY2s6IHBhcnRzWzFdfTtcbiAgfVxufTtcblxuLy8gU0NUUFxuLy8gcGFyc2VzIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTI2IGZpcnN0IGFuZCBmYWxscyBiYWNrXG4vLyB0byBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0wNVxuU0RQVXRpbHMucGFyc2VTY3RwRGVzY3JpcHRpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbWxpbmUgPSBTRFBVdGlscy5wYXJzZU1MaW5lKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IG1heFNpemVMaW5lID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1tYXgtbWVzc2FnZS1zaXplOicpO1xuICBsZXQgbWF4TWVzc2FnZVNpemU7XG4gIGlmIChtYXhTaXplTGluZS5sZW5ndGggPiAwKSB7XG4gICAgbWF4TWVzc2FnZVNpemUgPSBwYXJzZUludChtYXhTaXplTGluZVswXS5zdWJzdHIoMTkpLCAxMCk7XG4gIH1cbiAgaWYgKGlzTmFOKG1heE1lc3NhZ2VTaXplKSkge1xuICAgIG1heE1lc3NhZ2VTaXplID0gNjU1MzY7XG4gIH1cbiAgY29uc3Qgc2N0cFBvcnQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNjdHAtcG9ydDonKTtcbiAgaWYgKHNjdHBQb3J0Lmxlbmd0aCA+IDApIHtcbiAgICByZXR1cm4ge1xuICAgICAgcG9ydDogcGFyc2VJbnQoc2N0cFBvcnRbMF0uc3Vic3RyKDEyKSwgMTApLFxuICAgICAgcHJvdG9jb2w6IG1saW5lLmZtdCxcbiAgICAgIG1heE1lc3NhZ2VTaXplLFxuICAgIH07XG4gIH1cbiAgY29uc3Qgc2N0cE1hcExpbmVzID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1zY3RwbWFwOicpO1xuICBpZiAoc2N0cE1hcExpbmVzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBwYXJ0cyA9IHNjdHBNYXBMaW5lc1swXVxuICAgICAgLnN1YnN0cigxMClcbiAgICAgIC5zcGxpdCgnICcpO1xuICAgIHJldHVybiB7XG4gICAgICBwb3J0OiBwYXJzZUludChwYXJ0c1swXSwgMTApLFxuICAgICAgcHJvdG9jb2w6IHBhcnRzWzFdLFxuICAgICAgbWF4TWVzc2FnZVNpemUsXG4gICAgfTtcbiAgfVxufTtcblxuLy8gU0NUUFxuLy8gb3V0cHV0cyB0aGUgZHJhZnQtaWV0Zi1tbXVzaWMtc2N0cC1zZHAtMjYgdmVyc2lvbiB0aGF0IGFsbCBicm93c2Vyc1xuLy8gc3VwcG9ydCBieSBub3cgcmVjZWl2aW5nIGluIHRoaXMgZm9ybWF0LCB1bmxlc3Mgd2Ugb3JpZ2luYWxseSBwYXJzZWRcbi8vIGFzIHRoZSBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0wNSBmb3JtYXQgKGluZGljYXRlZCBieSB0aGUgbS1saW5lXG4vLyBwcm90b2NvbCBvZiBEVExTL1NDVFAgLS0gd2l0aG91dCBVRFAvIG9yIFRDUC8pXG5TRFBVdGlscy53cml0ZVNjdHBEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKG1lZGlhLCBzY3RwKSB7XG4gIGxldCBvdXRwdXQgPSBbXTtcbiAgaWYgKG1lZGlhLnByb3RvY29sICE9PSAnRFRMUy9TQ1RQJykge1xuICAgIG91dHB1dCA9IFtcbiAgICAgICdtPScgKyBtZWRpYS5raW5kICsgJyA5ICcgKyBtZWRpYS5wcm90b2NvbCArICcgJyArIHNjdHAucHJvdG9jb2wgKyAnXFxyXFxuJyxcbiAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyxcbiAgICAgICdhPXNjdHAtcG9ydDonICsgc2N0cC5wb3J0ICsgJ1xcclxcbicsXG4gICAgXTtcbiAgfSBlbHNlIHtcbiAgICBvdXRwdXQgPSBbXG4gICAgICAnbT0nICsgbWVkaWEua2luZCArICcgOSAnICsgbWVkaWEucHJvdG9jb2wgKyAnICcgKyBzY3RwLnBvcnQgKyAnXFxyXFxuJyxcbiAgICAgICdjPUlOIElQNCAwLjAuMC4wXFxyXFxuJyxcbiAgICAgICdhPXNjdHBtYXA6JyArIHNjdHAucG9ydCArICcgJyArIHNjdHAucHJvdG9jb2wgKyAnIDY1NTM1XFxyXFxuJyxcbiAgICBdO1xuICB9XG4gIGlmIChzY3RwLm1heE1lc3NhZ2VTaXplICE9PSB1bmRlZmluZWQpIHtcbiAgICBvdXRwdXQucHVzaCgnYT1tYXgtbWVzc2FnZS1zaXplOicgKyBzY3RwLm1heE1lc3NhZ2VTaXplICsgJ1xcclxcbicpO1xuICB9XG4gIHJldHVybiBvdXRwdXQuam9pbignJyk7XG59O1xuXG4vLyBHZW5lcmF0ZSBhIHNlc3Npb24gSUQgZm9yIFNEUC5cbi8vIGh0dHBzOi8vdG9vbHMuaWV0Zi5vcmcvaHRtbC9kcmFmdC1pZXRmLXJ0Y3dlYi1qc2VwLTIwI3NlY3Rpb24tNS4yLjFcbi8vIHJlY29tbWVuZHMgdXNpbmcgYSBjcnlwdG9ncmFwaGljYWxseSByYW5kb20gK3ZlIDY0LWJpdCB2YWx1ZVxuLy8gYnV0IHJpZ2h0IG5vdyB0aGlzIHNob3VsZCBiZSBhY2NlcHRhYmxlIGFuZCB3aXRoaW4gdGhlIHJpZ2h0IHJhbmdlXG5TRFBVdGlscy5nZW5lcmF0ZVNlc3Npb25JZCA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygpLnN1YnN0cigyLCAyMSk7XG59O1xuXG4vLyBXcml0ZSBib2lsZXIgcGxhdGUgZm9yIHN0YXJ0IG9mIFNEUFxuLy8gc2Vzc0lkIGFyZ3VtZW50IGlzIG9wdGlvbmFsIC0gaWYgbm90IHN1cHBsaWVkIGl0IHdpbGxcbi8vIGJlIGdlbmVyYXRlZCByYW5kb21seVxuLy8gc2Vzc1ZlcnNpb24gaXMgb3B0aW9uYWwgYW5kIGRlZmF1bHRzIHRvIDJcbi8vIHNlc3NVc2VyIGlzIG9wdGlvbmFsIGFuZCBkZWZhdWx0cyB0byAndGhpc2lzYWRhcHRlcm9ydGMnXG5TRFBVdGlscy53cml0ZVNlc3Npb25Cb2lsZXJwbGF0ZSA9IGZ1bmN0aW9uKHNlc3NJZCwgc2Vzc1Zlciwgc2Vzc1VzZXIpIHtcbiAgbGV0IHNlc3Npb25JZDtcbiAgY29uc3QgdmVyc2lvbiA9IHNlc3NWZXIgIT09IHVuZGVmaW5lZCA/IHNlc3NWZXIgOiAyO1xuICBpZiAoc2Vzc0lkKSB7XG4gICAgc2Vzc2lvbklkID0gc2Vzc0lkO1xuICB9IGVsc2Uge1xuICAgIHNlc3Npb25JZCA9IFNEUFV0aWxzLmdlbmVyYXRlU2Vzc2lvbklkKCk7XG4gIH1cbiAgY29uc3QgdXNlciA9IHNlc3NVc2VyIHx8ICd0aGlzaXNhZGFwdGVyb3J0Yyc7XG4gIC8vIEZJWE1FOiBzZXNzLWlkIHNob3VsZCBiZSBhbiBOVFAgdGltZXN0YW1wLlxuICByZXR1cm4gJ3Y9MFxcclxcbicgK1xuICAgICAgJ289JyArIHVzZXIgKyAnICcgKyBzZXNzaW9uSWQgKyAnICcgKyB2ZXJzaW9uICtcbiAgICAgICAgJyBJTiBJUDQgMTI3LjAuMC4xXFxyXFxuJyArXG4gICAgICAncz0tXFxyXFxuJyArXG4gICAgICAndD0wIDBcXHJcXG4nO1xufTtcblxuLy8gR2V0cyB0aGUgZGlyZWN0aW9uIGZyb20gdGhlIG1lZGlhU2VjdGlvbiBvciB0aGUgc2Vzc2lvbnBhcnQuXG5TRFBVdGlscy5nZXREaXJlY3Rpb24gPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24sIHNlc3Npb25wYXJ0KSB7XG4gIC8vIExvb2sgZm9yIHNlbmRyZWN2LCBzZW5kb25seSwgcmVjdm9ubHksIGluYWN0aXZlLCBkZWZhdWx0IHRvIHNlbmRyZWN2LlxuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBsaW5lcy5sZW5ndGg7IGkrKykge1xuICAgIHN3aXRjaCAobGluZXNbaV0pIHtcbiAgICAgIGNhc2UgJ2E9c2VuZHJlY3YnOlxuICAgICAgY2FzZSAnYT1zZW5kb25seSc6XG4gICAgICBjYXNlICdhPXJlY3Zvbmx5JzpcbiAgICAgIGNhc2UgJ2E9aW5hY3RpdmUnOlxuICAgICAgICByZXR1cm4gbGluZXNbaV0uc3Vic3RyKDIpO1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gRklYTUU6IFdoYXQgc2hvdWxkIGhhcHBlbiBoZXJlP1xuICAgIH1cbiAgfVxuICBpZiAoc2Vzc2lvbnBhcnQpIHtcbiAgICByZXR1cm4gU0RQVXRpbHMuZ2V0RGlyZWN0aW9uKHNlc3Npb25wYXJ0KTtcbiAgfVxuICByZXR1cm4gJ3NlbmRyZWN2Jztcbn07XG5cblNEUFV0aWxzLmdldEtpbmQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IG1saW5lID0gbGluZXNbMF0uc3BsaXQoJyAnKTtcbiAgcmV0dXJuIG1saW5lWzBdLnN1YnN0cigyKTtcbn07XG5cblNEUFV0aWxzLmlzUmVqZWN0ZWQgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgcmV0dXJuIG1lZGlhU2VjdGlvbi5zcGxpdCgnICcsIDIpWzFdID09PSAnMCc7XG59O1xuXG5TRFBVdGlscy5wYXJzZU1MaW5lID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBwYXJ0cyA9IGxpbmVzWzBdLnN1YnN0cigyKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGtpbmQ6IHBhcnRzWzBdLFxuICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzFdLCAxMCksXG4gICAgcHJvdG9jb2w6IHBhcnRzWzJdLFxuICAgIGZtdDogcGFydHMuc2xpY2UoMykuam9pbignICcpLFxuICB9O1xufTtcblxuU0RQVXRpbHMucGFyc2VPTGluZSA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBsaW5lID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnbz0nKVswXTtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cigyKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHVzZXJuYW1lOiBwYXJ0c1swXSxcbiAgICBzZXNzaW9uSWQ6IHBhcnRzWzFdLFxuICAgIHNlc3Npb25WZXJzaW9uOiBwYXJzZUludChwYXJ0c1syXSwgMTApLFxuICAgIG5ldFR5cGU6IHBhcnRzWzNdLFxuICAgIGFkZHJlc3NUeXBlOiBwYXJ0c1s0XSxcbiAgICBhZGRyZXNzOiBwYXJ0c1s1XSxcbiAgfTtcbn07XG5cbi8vIGEgdmVyeSBuYWl2ZSBpbnRlcnByZXRhdGlvbiBvZiBhIHZhbGlkIFNEUC5cblNEUFV0aWxzLmlzVmFsaWRTRFAgPSBmdW5jdGlvbihibG9iKSB7XG4gIGlmICh0eXBlb2YgYmxvYiAhPT0gJ3N0cmluZycgfHwgYmxvYi5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKGJsb2IpO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgaWYgKGxpbmVzW2ldLmxlbmd0aCA8IDIgfHwgbGluZXNbaV0uY2hhckF0KDEpICE9PSAnPScpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgLy8gVE9ETzogY2hlY2sgdGhlIG1vZGlmaWVyIGEgYml0IG1vcmUuXG4gIH1cbiAgcmV0dXJuIHRydWU7XG59O1xuXG4vLyBFeHBvc2UgcHVibGljIG1ldGhvZHMuXG5pZiAodHlwZW9mIG1vZHVsZSA9PT0gJ29iamVjdCcpIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSBTRFBVdGlscztcbn1cbiIsInZhciBtaiA9IHJlcXVpcmUoXCJtaW5pamFudXNcIik7XG5tai5KYW51c1Nlc3Npb24ucHJvdG90eXBlLnNlbmRPcmlnaW5hbCA9IG1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZDtcbm1qLkphbnVzU2Vzc2lvbi5wcm90b3R5cGUuc2VuZCA9IGZ1bmN0aW9uKHR5cGUsIHNpZ25hbCkge1xuICByZXR1cm4gdGhpcy5zZW5kT3JpZ2luYWwodHlwZSwgc2lnbmFsKS5jYXRjaCgoZSkgPT4ge1xuICAgIGlmIChlLm1lc3NhZ2UgJiYgZS5tZXNzYWdlLmluZGV4T2YoXCJ0aW1lZCBvdXRcIikgPiAtMSkge1xuICAgICAgY29uc29sZS5lcnJvcihcIndlYiBzb2NrZXQgdGltZWQgb3V0XCIpO1xuICAgICAgTkFGLmNvbm5lY3Rpb24uYWRhcHRlci5yZWNvbm5lY3QoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3coZSk7XG4gICAgfVxuICB9KTtcbn1cblxudmFyIHNkcFV0aWxzID0gcmVxdWlyZShcInNkcFwiKTtcbi8vdmFyIGRlYnVnID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6ZGVidWdcIik7XG4vL3ZhciB3YXJuID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6d2FyblwiKTtcbi8vdmFyIGVycm9yID0gcmVxdWlyZShcImRlYnVnXCIpKFwibmFmLWphbnVzLWFkYXB0ZXI6ZXJyb3JcIik7XG52YXIgZGVidWcgPSBjb25zb2xlLmxvZztcbnZhciB3YXJuID0gY29uc29sZS53YXJuO1xudmFyIGVycm9yID0gY29uc29sZS5lcnJvcjtcbnZhciBpc1NhZmFyaSA9IC9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QobmF2aWdhdG9yLnVzZXJBZ2VudCk7XG5cbmNvbnN0IFNVQlNDUklCRV9USU1FT1VUX01TID0gMTUwMDA7XG5cbmZ1bmN0aW9uIGRlYm91bmNlKGZuKSB7XG4gIHZhciBjdXJyID0gUHJvbWlzZS5yZXNvbHZlKCk7XG4gIHJldHVybiBmdW5jdGlvbigpIHtcbiAgICB2YXIgYXJncyA9IEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGFyZ3VtZW50cyk7XG4gICAgY3VyciA9IGN1cnIudGhlbihfID0+IGZuLmFwcGx5KHRoaXMsIGFyZ3MpKTtcbiAgfTtcbn1cblxuZnVuY3Rpb24gcmFuZG9tVWludCgpIHtcbiAgcmV0dXJuIE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIE51bWJlci5NQVhfU0FGRV9JTlRFR0VSKTtcbn1cblxuZnVuY3Rpb24gdW50aWxEYXRhQ2hhbm5lbE9wZW4oZGF0YUNoYW5uZWwpIHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBpZiAoZGF0YUNoYW5uZWwucmVhZHlTdGF0ZSA9PT0gXCJvcGVuXCIpIHtcbiAgICAgIHJlc29sdmUoKTtcbiAgICB9IGVsc2Uge1xuICAgICAgbGV0IHJlc29sdmVyLCByZWplY3RvcjtcblxuICAgICAgY29uc3QgY2xlYXIgPSAoKSA9PiB7XG4gICAgICAgIGRhdGFDaGFubmVsLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHJlc29sdmVyKTtcbiAgICAgICAgZGF0YUNoYW5uZWwucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsIHJlamVjdG9yKTtcbiAgICAgIH07XG5cbiAgICAgIHJlc29sdmVyID0gKCkgPT4ge1xuICAgICAgICBjbGVhcigpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9O1xuICAgICAgcmVqZWN0b3IgPSAoKSA9PiB7XG4gICAgICAgIGNsZWFyKCk7XG4gICAgICAgIHJlamVjdCgpO1xuICAgICAgfTtcblxuICAgICAgZGF0YUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgcmVzb2x2ZXIpO1xuICAgICAgZGF0YUNoYW5uZWwuYWRkRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsIHJlamVjdG9yKTtcbiAgICB9XG4gIH0pO1xufVxuXG5jb25zdCBpc0gyNjRWaWRlb1N1cHBvcnRlZCA9ICgoKSA9PiB7XG4gIGNvbnN0IHZpZGVvID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInZpZGVvXCIpO1xuICByZXR1cm4gdmlkZW8uY2FuUGxheVR5cGUoJ3ZpZGVvL21wNDsgY29kZWNzPVwiYXZjMS40MkUwMUUsIG1wNGEuNDAuMlwiJykgIT09IFwiXCI7XG59KSgpO1xuXG5jb25zdCBPUFVTX1BBUkFNRVRFUlMgPSB7XG4gIC8vIGluZGljYXRlcyB0aGF0IHdlIHdhbnQgdG8gZW5hYmxlIERUWCB0byBlbGlkZSBzaWxlbmNlIHBhY2tldHNcbiAgdXNlZHR4OiAxLFxuICAvLyBpbmRpY2F0ZXMgdGhhdCB3ZSBwcmVmZXIgdG8gcmVjZWl2ZSBtb25vIGF1ZGlvIChpbXBvcnRhbnQgZm9yIHZvaXAgcHJvZmlsZSlcbiAgc3RlcmVvOiAwLFxuICAvLyBpbmRpY2F0ZXMgdGhhdCB3ZSBwcmVmZXIgdG8gc2VuZCBtb25vIGF1ZGlvIChpbXBvcnRhbnQgZm9yIHZvaXAgcHJvZmlsZSlcbiAgXCJzcHJvcC1zdGVyZW9cIjogMFxufTtcblxuY29uc3QgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHID0ge1xuICBpY2VTZXJ2ZXJzOiBbeyB1cmxzOiBcInN0dW46c3R1bjEubC5nb29nbGUuY29tOjE5MzAyXCIgfSwgeyB1cmxzOiBcInN0dW46c3R1bjIubC5nb29nbGUuY29tOjE5MzAyXCIgfV1cbn07XG5cbmNvbnN0IFdTX05PUk1BTF9DTE9TVVJFID0gMTAwMDtcblxuY2xhc3MgSmFudXNBZGFwdGVyIHtcbiAgY29uc3RydWN0b3IoKSB7XG4gICAgdGhpcy5yb29tID0gbnVsbDtcbiAgICAvLyBXZSBleHBlY3QgdGhlIGNvbnN1bWVyIHRvIHNldCBhIGNsaWVudCBpZCBiZWZvcmUgY29ubmVjdGluZy5cbiAgICB0aGlzLmNsaWVudElkID0gbnVsbDtcbiAgICB0aGlzLmpvaW5Ub2tlbiA9IG51bGw7XG5cbiAgICB0aGlzLnNlcnZlclVybCA9IG51bGw7XG4gICAgdGhpcy53ZWJSdGNPcHRpb25zID0ge307XG4gICAgdGhpcy5wZWVyQ29ubmVjdGlvbkNvbmZpZyA9IG51bGw7XG4gICAgdGhpcy53cyA9IG51bGw7XG4gICAgdGhpcy5zZXNzaW9uID0gbnVsbDtcbiAgICB0aGlzLnJlbGlhYmxlVHJhbnNwb3J0ID0gXCJkYXRhY2hhbm5lbFwiO1xuICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCA9IFwiZGF0YWNoYW5uZWxcIjtcblxuICAgIC8vIEluIHRoZSBldmVudCB0aGUgc2VydmVyIHJlc3RhcnRzIGFuZCBhbGwgY2xpZW50cyBsb3NlIGNvbm5lY3Rpb24sIHJlY29ubmVjdCB3aXRoXG4gICAgLy8gc29tZSByYW5kb20gaml0dGVyIGFkZGVkIHRvIHByZXZlbnQgc2ltdWx0YW5lb3VzIHJlY29ubmVjdGlvbiByZXF1ZXN0cy5cbiAgICB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheSA9IDEwMDAgKiBNYXRoLnJhbmRvbSgpO1xuICAgIHRoaXMucmVjb25uZWN0aW9uRGVsYXkgPSB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheTtcbiAgICB0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQgPSBudWxsO1xuICAgIHRoaXMubWF4UmVjb25uZWN0aW9uQXR0ZW1wdHMgPSAxMDtcbiAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzID0gMDtcblxuICAgIHRoaXMucHVibGlzaGVyID0gbnVsbDtcbiAgICB0aGlzLm9jY3VwYW50cyA9IHt9O1xuICAgIHRoaXMubGVmdE9jY3VwYW50cyA9IG5ldyBTZXQoKTtcbiAgICB0aGlzLm1lZGlhU3RyZWFtcyA9IHt9O1xuICAgIHRoaXMubG9jYWxNZWRpYVN0cmVhbSA9IG51bGw7XG4gICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cyA9IG5ldyBNYXAoKTtcblxuICAgIHRoaXMuYmxvY2tlZENsaWVudHMgPSBuZXcgTWFwKCk7XG4gICAgdGhpcy5mcm96ZW5VcGRhdGVzID0gbmV3IE1hcCgpO1xuXG4gICAgdGhpcy50aW1lT2Zmc2V0cyA9IFtdO1xuICAgIHRoaXMuc2VydmVyVGltZVJlcXVlc3RzID0gMDtcbiAgICB0aGlzLmF2Z1RpbWVPZmZzZXQgPSAwO1xuXG4gICAgdGhpcy5vbldlYnNvY2tldE9wZW4gPSB0aGlzLm9uV2Vic29ja2V0T3Blbi5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25XZWJzb2NrZXRDbG9zZSA9IHRoaXMub25XZWJzb2NrZXRDbG9zZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25XZWJzb2NrZXRNZXNzYWdlID0gdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UuYmluZCh0aGlzKTtcbiAgICB0aGlzLm9uRGF0YUNoYW5uZWxNZXNzYWdlID0gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZS5iaW5kKHRoaXMpO1xuICAgIHRoaXMub25EYXRhID0gdGhpcy5vbkRhdGEuYmluZCh0aGlzKTtcbiAgfVxuXG4gIHNldFNlcnZlclVybCh1cmwpIHtcbiAgICB0aGlzLnNlcnZlclVybCA9IHVybDtcbiAgfVxuXG4gIHNldEFwcChhcHApIHt9XG5cbiAgc2V0Um9vbShyb29tTmFtZSkge1xuICAgIHRoaXMucm9vbSA9IHJvb21OYW1lO1xuICB9XG5cbiAgc2V0Sm9pblRva2VuKGpvaW5Ub2tlbikge1xuICAgIHRoaXMuam9pblRva2VuID0gam9pblRva2VuO1xuICB9XG5cbiAgc2V0Q2xpZW50SWQoY2xpZW50SWQpIHtcbiAgICB0aGlzLmNsaWVudElkID0gY2xpZW50SWQ7XG4gIH1cblxuICBzZXRXZWJSdGNPcHRpb25zKG9wdGlvbnMpIHtcbiAgICB0aGlzLndlYlJ0Y09wdGlvbnMgPSBvcHRpb25zO1xuICB9XG5cbiAgc2V0UGVlckNvbm5lY3Rpb25Db25maWcocGVlckNvbm5lY3Rpb25Db25maWcpIHtcbiAgICB0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnID0gcGVlckNvbm5lY3Rpb25Db25maWc7XG4gIH1cblxuICBzZXRTZXJ2ZXJDb25uZWN0TGlzdGVuZXJzKHN1Y2Nlc3NMaXN0ZW5lciwgZmFpbHVyZUxpc3RlbmVyKSB7XG4gICAgdGhpcy5jb25uZWN0U3VjY2VzcyA9IHN1Y2Nlc3NMaXN0ZW5lcjtcbiAgICB0aGlzLmNvbm5lY3RGYWlsdXJlID0gZmFpbHVyZUxpc3RlbmVyO1xuICB9XG5cbiAgc2V0Um9vbU9jY3VwYW50TGlzdGVuZXIob2NjdXBhbnRMaXN0ZW5lcikge1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkID0gb2NjdXBhbnRMaXN0ZW5lcjtcbiAgfVxuXG4gIHNldERhdGFDaGFubmVsTGlzdGVuZXJzKG9wZW5MaXN0ZW5lciwgY2xvc2VkTGlzdGVuZXIsIG1lc3NhZ2VMaXN0ZW5lcikge1xuICAgIHRoaXMub25PY2N1cGFudENvbm5lY3RlZCA9IG9wZW5MaXN0ZW5lcjtcbiAgICB0aGlzLm9uT2NjdXBhbnREaXNjb25uZWN0ZWQgPSBjbG9zZWRMaXN0ZW5lcjtcbiAgICB0aGlzLm9uT2NjdXBhbnRNZXNzYWdlID0gbWVzc2FnZUxpc3RlbmVyO1xuICB9XG5cbiAgc2V0UmVjb25uZWN0aW9uTGlzdGVuZXJzKHJlY29ubmVjdGluZ0xpc3RlbmVyLCByZWNvbm5lY3RlZExpc3RlbmVyLCByZWNvbm5lY3Rpb25FcnJvckxpc3RlbmVyKSB7XG4gICAgLy8gb25SZWNvbm5lY3RpbmcgaXMgY2FsbGVkIHdpdGggdGhlIG51bWJlciBvZiBtaWxsaXNlY29uZHMgdW50aWwgdGhlIG5leHQgcmVjb25uZWN0aW9uIGF0dGVtcHRcbiAgICB0aGlzLm9uUmVjb25uZWN0aW5nID0gcmVjb25uZWN0aW5nTGlzdGVuZXI7XG4gICAgLy8gb25SZWNvbm5lY3RlZCBpcyBjYWxsZWQgd2hlbiB0aGUgY29ubmVjdGlvbiBoYXMgYmVlbiByZWVzdGFibGlzaGVkXG4gICAgdGhpcy5vblJlY29ubmVjdGVkID0gcmVjb25uZWN0ZWRMaXN0ZW5lcjtcbiAgICAvLyBvblJlY29ubmVjdGlvbkVycm9yIGlzIGNhbGxlZCB3aXRoIGFuIGVycm9yIHdoZW4gbWF4UmVjb25uZWN0aW9uQXR0ZW1wdHMgaGFzIGJlZW4gcmVhY2hlZFxuICAgIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvciA9IHJlY29ubmVjdGlvbkVycm9yTGlzdGVuZXI7XG4gIH1cblxuICBjb25uZWN0KCkge1xuICAgIGRlYnVnKGBjb25uZWN0aW5nIHRvICR7dGhpcy5zZXJ2ZXJVcmx9YCk7XG5cbiAgICBjb25zdCB3ZWJzb2NrZXRDb25uZWN0aW9uID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy53cyA9IG5ldyBXZWJTb2NrZXQodGhpcy5zZXJ2ZXJVcmwsIFwiamFudXMtcHJvdG9jb2xcIik7XG5cbiAgICAgIHRoaXMuc2Vzc2lvbiA9IG5ldyBtai5KYW51c1Nlc3Npb24odGhpcy53cy5zZW5kLmJpbmQodGhpcy53cyksIHsgdGltZW91dE1zOiA0MDAwMCB9KTtcblxuICAgICAgdGhpcy53cy5hZGRFdmVudExpc3RlbmVyKFwiY2xvc2VcIiwgdGhpcy5vbldlYnNvY2tldENsb3NlKTtcbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UpO1xuXG4gICAgICB0aGlzLndzT25PcGVuID0gKCkgPT4ge1xuICAgICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJvcGVuXCIsIHRoaXMud3NPbk9wZW4pO1xuICAgICAgICB0aGlzLm9uV2Vic29ja2V0T3BlbigpXG4gICAgICAgICAgLnRoZW4ocmVzb2x2ZSlcbiAgICAgICAgICAuY2F0Y2gocmVqZWN0KTtcbiAgICAgIH07XG5cbiAgICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgdGhpcy53c09uT3Blbik7XG4gICAgfSk7XG5cbiAgICByZXR1cm4gUHJvbWlzZS5hbGwoW3dlYnNvY2tldENvbm5lY3Rpb24sIHRoaXMudXBkYXRlVGltZU9mZnNldCgpXSk7XG4gIH1cblxuICBkaXNjb25uZWN0KCkge1xuICAgIGRlYnVnKGBkaXNjb25uZWN0aW5nYCk7XG5cbiAgICBjbGVhclRpbWVvdXQodGhpcy5yZWNvbm5lY3Rpb25UaW1lb3V0KTtcblxuICAgIHRoaXMucmVtb3ZlQWxsT2NjdXBhbnRzKCk7XG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzID0gbmV3IFNldCgpO1xuXG4gICAgaWYgKHRoaXMucHVibGlzaGVyKSB7XG4gICAgICAvLyBDbG9zZSB0aGUgcHVibGlzaGVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMucHVibGlzaGVyLmNvbm4uY2xvc2UoKTtcbiAgICAgIHRoaXMucHVibGlzaGVyID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5zZXNzaW9uKSB7XG4gICAgICB0aGlzLnNlc3Npb24uZGlzcG9zZSgpO1xuICAgICAgdGhpcy5zZXNzaW9uID0gbnVsbDtcbiAgICB9XG5cbiAgICBpZiAodGhpcy53cykge1xuICAgICAgdGhpcy53cy5yZW1vdmVFdmVudExpc3RlbmVyKFwib3BlblwiLCB0aGlzLndzT25PcGVuKTtcbiAgICAgIHRoaXMud3MucmVtb3ZlRXZlbnRMaXN0ZW5lcihcImNsb3NlXCIsIHRoaXMub25XZWJzb2NrZXRDbG9zZSk7XG4gICAgICB0aGlzLndzLnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIHRoaXMub25XZWJzb2NrZXRNZXNzYWdlKTtcbiAgICAgIHRoaXMud3MuY2xvc2UoKTtcbiAgICAgIHRoaXMud3MgPSBudWxsO1xuICAgIH1cblxuICAgIC8vIE5vdyB0aGF0IGFsbCBSVENQZWVyQ29ubmVjdGlvbiBjbG9zZWQsIGJlIHN1cmUgdG8gbm90IGNhbGxcbiAgICAvLyByZWNvbm5lY3QoKSBhZ2FpbiB2aWEgcGVyZm9ybURlbGF5ZWRSZWNvbm5lY3QgaWYgcHJldmlvdXNcbiAgICAvLyBSVENQZWVyQ29ubmVjdGlvbiB3YXMgaW4gdGhlIGZhaWxlZCBzdGF0ZS5cbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgICAgdGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCA9IG51bGw7XG4gICAgfVxuICB9XG5cbiAgaXNEaXNjb25uZWN0ZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMud3MgPT09IG51bGw7XG4gIH1cblxuICBhc3luYyBvbldlYnNvY2tldE9wZW4oKSB7XG4gICAgLy8gQ3JlYXRlIHRoZSBKYW51cyBTZXNzaW9uXG4gICAgYXdhaXQgdGhpcy5zZXNzaW9uLmNyZWF0ZSgpO1xuXG4gICAgLy8gQXR0YWNoIHRoZSBTRlUgUGx1Z2luIGFuZCBjcmVhdGUgYSBSVENQZWVyQ29ubmVjdGlvbiBmb3IgdGhlIHB1Ymxpc2hlci5cbiAgICAvLyBUaGUgcHVibGlzaGVyIHNlbmRzIGF1ZGlvIGFuZCBvcGVucyB0d28gYmlkaXJlY3Rpb25hbCBkYXRhIGNoYW5uZWxzLlxuICAgIC8vIE9uZSByZWxpYWJsZSBkYXRhY2hhbm5lbCBhbmQgb25lIHVucmVsaWFibGUuXG4gICAgdGhpcy5wdWJsaXNoZXIgPSBhd2FpdCB0aGlzLmNyZWF0ZVB1Ymxpc2hlcigpO1xuXG4gICAgLy8gQ2FsbCB0aGUgbmFmIGNvbm5lY3RTdWNjZXNzIGNhbGxiYWNrIGJlZm9yZSB3ZSBzdGFydCByZWNlaXZpbmcgV2ViUlRDIG1lc3NhZ2VzLlxuICAgIHRoaXMuY29ubmVjdFN1Y2Nlc3ModGhpcy5jbGllbnRJZCk7XG5cbiAgICBjb25zdCBhZGRPY2N1cGFudFByb21pc2VzID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHRoaXMucHVibGlzaGVyLmluaXRpYWxPY2N1cGFudHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IG9jY3VwYW50SWQgPSB0aGlzLnB1Ymxpc2hlci5pbml0aWFsT2NjdXBhbnRzW2ldO1xuICAgICAgaWYgKG9jY3VwYW50SWQgPT09IHRoaXMuY2xpZW50SWQpIGNvbnRpbnVlOyAvLyBIYXBwZW5zIGR1cmluZyBub24tZ3JhY2VmdWwgcmVjb25uZWN0cyBkdWUgdG8gem9tYmllIHNlc3Npb25zXG4gICAgICBhZGRPY2N1cGFudFByb21pc2VzLnB1c2godGhpcy5hZGRPY2N1cGFudChvY2N1cGFudElkKSk7XG4gICAgfVxuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoYWRkT2NjdXBhbnRQcm9taXNlcyk7XG4gIH1cblxuICBvbldlYnNvY2tldENsb3NlKGV2ZW50KSB7XG4gICAgLy8gVGhlIGNvbm5lY3Rpb24gd2FzIGNsb3NlZCBzdWNjZXNzZnVsbHkuIERvbid0IHRyeSB0byByZWNvbm5lY3QuXG4gICAgaWYgKGV2ZW50LmNvZGUgPT09IFdTX05PUk1BTF9DTE9TVVJFKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc29sZS53YXJuKFwiSmFudXMgd2Vic29ja2V0IGNsb3NlZCB1bmV4cGVjdGVkbHkuXCIpO1xuICAgIGlmICh0aGlzLm9uUmVjb25uZWN0aW5nKSB7XG4gICAgICB0aGlzLm9uUmVjb25uZWN0aW5nKHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgIH1cblxuICAgIHRoaXMucmVjb25uZWN0aW9uVGltZW91dCA9IHNldFRpbWVvdXQoKCkgPT4gdGhpcy5yZWNvbm5lY3QoKSwgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gIH1cblxuICByZWNvbm5lY3QoKSB7XG4gICAgLy8gRGlzcG9zZSBvZiBhbGwgbmV0d29ya2VkIGVudGl0aWVzIGFuZCBvdGhlciByZXNvdXJjZXMgdGllZCB0byB0aGUgc2Vzc2lvbi5cbiAgICB0aGlzLmRpc2Nvbm5lY3QoKTtcblxuICAgIHRoaXMuY29ubmVjdCgpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIHRoaXMucmVjb25uZWN0aW9uRGVsYXkgPSB0aGlzLmluaXRpYWxSZWNvbm5lY3Rpb25EZWxheTtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25BdHRlbXB0cyA9IDA7XG5cbiAgICAgICAgaWYgKHRoaXMub25SZWNvbm5lY3RlZCkge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RlZCgpO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgdGhpcy5yZWNvbm5lY3Rpb25EZWxheSArPSAxMDAwO1xuICAgICAgICB0aGlzLnJlY29ubmVjdGlvbkF0dGVtcHRzKys7XG5cbiAgICAgICAgaWYgKHRoaXMucmVjb25uZWN0aW9uQXR0ZW1wdHMgPiB0aGlzLm1heFJlY29ubmVjdGlvbkF0dGVtcHRzICYmIHRoaXMub25SZWNvbm5lY3Rpb25FcnJvcikge1xuICAgICAgICAgIHJldHVybiB0aGlzLm9uUmVjb25uZWN0aW9uRXJyb3IoXG4gICAgICAgICAgICBuZXcgRXJyb3IoXCJDb25uZWN0aW9uIGNvdWxkIG5vdCBiZSByZWVzdGFibGlzaGVkLCBleGNlZWRlZCBtYXhpbXVtIG51bWJlciBvZiByZWNvbm5lY3Rpb24gYXR0ZW1wdHMuXCIpXG4gICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnNvbGUud2FybihcIkVycm9yIGR1cmluZyByZWNvbm5lY3QsIHJldHJ5aW5nLlwiKTtcbiAgICAgICAgY29uc29sZS53YXJuKGVycm9yKTtcblxuICAgICAgICBpZiAodGhpcy5vblJlY29ubmVjdGluZykge1xuICAgICAgICAgIHRoaXMub25SZWNvbm5lY3RpbmcodGhpcy5yZWNvbm5lY3Rpb25EZWxheSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnJlY29ubmVjdGlvblRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHRoaXMucmVjb25uZWN0KCksIHRoaXMucmVjb25uZWN0aW9uRGVsYXkpO1xuICAgICAgfSk7XG4gIH1cblxuICBwZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpIHtcbiAgICBpZiAodGhpcy5kZWxheWVkUmVjb25uZWN0VGltZW91dCkge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpO1xuICAgIH1cblxuICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQgPSBudWxsO1xuICAgICAgdGhpcy5yZWNvbm5lY3QoKTtcbiAgICB9LCAxMDAwMCk7XG4gIH1cblxuICBvbldlYnNvY2tldE1lc3NhZ2UoZXZlbnQpIHtcbiAgICB0aGlzLnNlc3Npb24ucmVjZWl2ZShKU09OLnBhcnNlKGV2ZW50LmRhdGEpKTtcbiAgfVxuXG4gIGFzeW5jIGFkZE9jY3VwYW50KG9jY3VwYW50SWQpIHtcbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuXG4gICAgdGhpcy5sZWZ0T2NjdXBhbnRzLmRlbGV0ZShvY2N1cGFudElkKTtcblxuICAgIHZhciBzdWJzY3JpYmVyID0gYXdhaXQgdGhpcy5jcmVhdGVTdWJzY3JpYmVyKG9jY3VwYW50SWQpO1xuXG4gICAgaWYgKCFzdWJzY3JpYmVyKSByZXR1cm47XG5cbiAgICB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXSA9IHN1YnNjcmliZXI7XG5cbiAgICB0aGlzLnNldE1lZGlhU3RyZWFtKG9jY3VwYW50SWQsIHN1YnNjcmliZXIubWVkaWFTdHJlYW0pO1xuXG4gICAgLy8gQ2FsbCB0aGUgTmV0d29ya2VkIEFGcmFtZSBjYWxsYmFja3MgZm9yIHRoZSBuZXcgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50Q29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcblxuICAgIHJldHVybiBzdWJzY3JpYmVyO1xuICB9XG5cbiAgcmVtb3ZlQWxsT2NjdXBhbnRzKCkge1xuICAgIGZvciAoY29uc3Qgb2NjdXBhbnRJZCBvZiBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh0aGlzLm9jY3VwYW50cykpIHtcbiAgICAgIHRoaXMucmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCk7XG4gICAgfVxuICB9XG5cbiAgcmVtb3ZlT2NjdXBhbnQob2NjdXBhbnRJZCkge1xuICAgIHRoaXMubGVmdE9jY3VwYW50cy5hZGQob2NjdXBhbnRJZCk7XG5cbiAgICBpZiAodGhpcy5vY2N1cGFudHNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIC8vIENsb3NlIHRoZSBzdWJzY3JpYmVyIHBlZXIgY29ubmVjdGlvbi4gV2hpY2ggYWxzbyBkZXRhY2hlcyB0aGUgcGx1Z2luIGhhbmRsZS5cbiAgICAgIHRoaXMub2NjdXBhbnRzW29jY3VwYW50SWRdLmNvbm4uY2xvc2UoKTtcbiAgICAgIGRlbGV0ZSB0aGlzLm9jY3VwYW50c1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5tZWRpYVN0cmVhbXNbb2NjdXBhbnRJZF0pIHtcbiAgICAgIGRlbGV0ZSB0aGlzLm1lZGlhU3RyZWFtc1tvY2N1cGFudElkXTtcbiAgICB9XG5cbiAgICBpZiAodGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbnN0IG1zZyA9IFwiVGhlIHVzZXIgZGlzY29ubmVjdGVkIGJlZm9yZSB0aGUgbWVkaWEgc3RyZWFtIHdhcyByZXNvbHZlZC5cIjtcbiAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KG9jY3VwYW50SWQpLmF1ZGlvLnJlamVjdChtc2cpO1xuICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQob2NjdXBhbnRJZCkudmlkZW8ucmVqZWN0KG1zZyk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmRlbGV0ZShvY2N1cGFudElkKTtcbiAgICB9XG5cbiAgICAvLyBDYWxsIHRoZSBOZXR3b3JrZWQgQUZyYW1lIGNhbGxiYWNrcyBmb3IgdGhlIHJlbW92ZWQgb2NjdXBhbnQuXG4gICAgdGhpcy5vbk9jY3VwYW50RGlzY29ubmVjdGVkKG9jY3VwYW50SWQpO1xuICAgIHRoaXMub25PY2N1cGFudHNDaGFuZ2VkKHRoaXMub2NjdXBhbnRzKTtcbiAgfVxuXG4gIGFzc29jaWF0ZShjb25uLCBoYW5kbGUpIHtcbiAgICBjb25uLmFkZEV2ZW50TGlzdGVuZXIoXCJpY2VjYW5kaWRhdGVcIiwgZXYgPT4ge1xuICAgICAgaGFuZGxlLnNlbmRUcmlja2xlKGV2LmNhbmRpZGF0ZSB8fCBudWxsKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgdHJpY2tsaW5nIElDRTogJW9cIiwgZSkpO1xuICAgIH0pO1xuICAgIGNvbm4uYWRkRXZlbnRMaXN0ZW5lcihcImljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZVwiLCBldiA9PiB7XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS5sb2coXCJJQ0Ugc3RhdGUgY2hhbmdlZCB0byBjb25uZWN0ZWRcIik7XG4gICAgICB9XG4gICAgICBpZiAoY29ubi5pY2VDb25uZWN0aW9uU3RhdGUgPT09IFwiZGlzY29ubmVjdGVkXCIpIHtcbiAgICAgICAgY29uc29sZS53YXJuKFwiSUNFIHN0YXRlIGNoYW5nZWQgdG8gZGlzY29ubmVjdGVkXCIpO1xuICAgICAgfVxuICAgICAgaWYgKGNvbm4uaWNlQ29ubmVjdGlvblN0YXRlID09PSBcImZhaWxlZFwiKSB7XG4gICAgICAgIGNvbnNvbGUud2FybihcIklDRSBmYWlsdXJlIGRldGVjdGVkLiBSZWNvbm5lY3RpbmcgaW4gMTBzLlwiKTtcbiAgICAgICAgdGhpcy5wZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpO1xuICAgICAgfVxuICAgIH0pXG5cbiAgICAvLyB3ZSBoYXZlIHRvIGRlYm91bmNlIHRoZXNlIGJlY2F1c2UgamFudXMgZ2V0cyBhbmdyeSBpZiB5b3Ugc2VuZCBpdCBhIG5ldyBTRFAgYmVmb3JlXG4gICAgLy8gaXQncyBmaW5pc2hlZCBwcm9jZXNzaW5nIGFuIGV4aXN0aW5nIFNEUC4gaW4gYWN0dWFsaXR5LCBpdCBzZWVtcyBsaWtlIHRoaXMgaXMgbWF5YmVcbiAgICAvLyB0b28gbGliZXJhbCBhbmQgd2UgbmVlZCB0byB3YWl0IHNvbWUgYW1vdW50IG9mIHRpbWUgYWZ0ZXIgYW4gb2ZmZXIgYmVmb3JlIHNlbmRpbmcgYW5vdGhlcixcbiAgICAvLyBidXQgd2UgZG9uJ3QgY3VycmVudGx5IGtub3cgYW55IGdvb2Qgd2F5IG9mIGRldGVjdGluZyBleGFjdGx5IGhvdyBsb25nIDooXG4gICAgY29ubi5hZGRFdmVudExpc3RlbmVyKFxuICAgICAgXCJuZWdvdGlhdGlvbm5lZWRlZFwiLFxuICAgICAgZGVib3VuY2UoZXYgPT4ge1xuICAgICAgICBkZWJ1ZyhcIlNlbmRpbmcgbmV3IG9mZmVyIGZvciBoYW5kbGU6ICVvXCIsIGhhbmRsZSk7XG4gICAgICAgIHZhciBvZmZlciA9IGNvbm4uY3JlYXRlT2ZmZXIoKS50aGVuKHRoaXMuY29uZmlndXJlUHVibGlzaGVyU2RwKS50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpO1xuICAgICAgICB2YXIgbG9jYWwgPSBvZmZlci50aGVuKG8gPT4gY29ubi5zZXRMb2NhbERlc2NyaXB0aW9uKG8pKTtcbiAgICAgICAgdmFyIHJlbW90ZSA9IG9mZmVyO1xuXG4gICAgICAgIHJlbW90ZSA9IHJlbW90ZVxuICAgICAgICAgIC50aGVuKHRoaXMuZml4U2FmYXJpSWNlVUZyYWcpXG4gICAgICAgICAgLnRoZW4oaiA9PiBoYW5kbGUuc2VuZEpzZXAoaikpXG4gICAgICAgICAgLnRoZW4ociA9PiBjb25uLnNldFJlbW90ZURlc2NyaXB0aW9uKHIuanNlcCkpO1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5hbGwoW2xvY2FsLCByZW1vdGVdKS5jYXRjaChlID0+IGVycm9yKFwiRXJyb3IgbmVnb3RpYXRpbmcgb2ZmZXI6ICVvXCIsIGUpKTtcbiAgICAgIH0pXG4gICAgKTtcbiAgICBoYW5kbGUub24oXG4gICAgICBcImV2ZW50XCIsXG4gICAgICBkZWJvdW5jZShldiA9PiB7XG4gICAgICAgIHZhciBqc2VwID0gZXYuanNlcDtcbiAgICAgICAgaWYgKGpzZXAgJiYganNlcC50eXBlID09IFwib2ZmZXJcIikge1xuICAgICAgICAgIGRlYnVnKFwiQWNjZXB0aW5nIG5ldyBvZmZlciBmb3IgaGFuZGxlOiAlb1wiLCBoYW5kbGUpO1xuICAgICAgICAgIHZhciBhbnN3ZXIgPSBjb25uXG4gICAgICAgICAgICAuc2V0UmVtb3RlRGVzY3JpcHRpb24odGhpcy5jb25maWd1cmVTdWJzY3JpYmVyU2RwKGpzZXApKVxuICAgICAgICAgICAgLnRoZW4oXyA9PiBjb25uLmNyZWF0ZUFuc3dlcigpKVxuICAgICAgICAgICAgLnRoZW4odGhpcy5maXhTYWZhcmlJY2VVRnJhZyk7XG4gICAgICAgICAgdmFyIGxvY2FsID0gYW5zd2VyLnRoZW4oYSA9PiBjb25uLnNldExvY2FsRGVzY3JpcHRpb24oYSkpO1xuICAgICAgICAgIHZhciByZW1vdGUgPSBhbnN3ZXIudGhlbihqID0+IGhhbmRsZS5zZW5kSnNlcChqKSk7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UuYWxsKFtsb2NhbCwgcmVtb3RlXSkuY2F0Y2goZSA9PiBlcnJvcihcIkVycm9yIG5lZ290aWF0aW5nIGFuc3dlcjogJW9cIiwgZSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIHNvbWUgb3RoZXIga2luZCBvZiBldmVudCwgbm90aGluZyB0byBkb1xuICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBhc3luYyBjcmVhdGVQdWJsaXNoZXIoKSB7XG4gICAgdmFyIGhhbmRsZSA9IG5ldyBtai5KYW51c1BsdWdpbkhhbmRsZSh0aGlzLnNlc3Npb24pO1xuICAgIHZhciBjb25uID0gbmV3IFJUQ1BlZXJDb25uZWN0aW9uKHRoaXMucGVlckNvbm5lY3Rpb25Db25maWcgfHwgREVGQVVMVF9QRUVSX0NPTk5FQ1RJT05fQ09ORklHKTtcblxuICAgIGRlYnVnKFwicHViIHdhaXRpbmcgZm9yIHNmdVwiKTtcbiAgICBhd2FpdCBoYW5kbGUuYXR0YWNoKFwiamFudXMucGx1Z2luLnNmdVwiKTtcblxuICAgIHRoaXMuYXNzb2NpYXRlKGNvbm4sIGhhbmRsZSk7XG5cbiAgICBkZWJ1ZyhcInB1YiB3YWl0aW5nIGZvciBkYXRhIGNoYW5uZWxzICYgd2VicnRjdXBcIik7XG4gICAgdmFyIHdlYnJ0Y3VwID0gbmV3IFByb21pc2UocmVzb2x2ZSA9PiBoYW5kbGUub24oXCJ3ZWJydGN1cFwiLCByZXNvbHZlKSk7XG5cbiAgICAvLyBVbnJlbGlhYmxlIGRhdGFjaGFubmVsOiBzZW5kaW5nIGFuZCByZWNlaXZpbmcgY29tcG9uZW50IHVwZGF0ZXMuXG4gICAgLy8gUmVsaWFibGUgZGF0YWNoYW5uZWw6IHNlbmRpbmcgYW5kIHJlY2lldmluZyBlbnRpdHkgaW5zdGFudGlhdGlvbnMuXG4gICAgdmFyIHJlbGlhYmxlQ2hhbm5lbCA9IGNvbm4uY3JlYXRlRGF0YUNoYW5uZWwoXCJyZWxpYWJsZVwiLCB7IG9yZGVyZWQ6IHRydWUgfSk7XG4gICAgdmFyIHVucmVsaWFibGVDaGFubmVsID0gY29ubi5jcmVhdGVEYXRhQ2hhbm5lbChcInVucmVsaWFibGVcIiwge1xuICAgICAgb3JkZXJlZDogZmFsc2UsXG4gICAgICBtYXhSZXRyYW5zbWl0czogMFxuICAgIH0pO1xuXG4gICAgcmVsaWFibGVDaGFubmVsLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGUgPT4gdGhpcy5vbkRhdGFDaGFubmVsTWVzc2FnZShlLCBcImphbnVzLXJlbGlhYmxlXCIpKTtcbiAgICB1bnJlbGlhYmxlQ2hhbm5lbC5hZGRFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBlID0+IHRoaXMub25EYXRhQ2hhbm5lbE1lc3NhZ2UoZSwgXCJqYW51cy11bnJlbGlhYmxlXCIpKTtcblxuICAgIGF3YWl0IHdlYnJ0Y3VwO1xuICAgIGF3YWl0IHVudGlsRGF0YUNoYW5uZWxPcGVuKHJlbGlhYmxlQ2hhbm5lbCk7XG4gICAgYXdhaXQgdW50aWxEYXRhQ2hhbm5lbE9wZW4odW5yZWxpYWJsZUNoYW5uZWwpO1xuXG4gICAgLy8gZG9pbmcgdGhpcyBoZXJlIGlzIHNvcnQgb2YgYSBoYWNrIGFyb3VuZCBjaHJvbWUgcmVuZWdvdGlhdGlvbiB3ZWlyZG5lc3MgLS1cbiAgICAvLyBpZiB3ZSBkbyBpdCBwcmlvciB0byB3ZWJydGN1cCwgY2hyb21lIG9uIGdlYXIgVlIgd2lsbCBzb21ldGltZXMgcHV0IGFcbiAgICAvLyByZW5lZ290aWF0aW9uIG9mZmVyIGluIGZsaWdodCB3aGlsZSB0aGUgZmlyc3Qgb2ZmZXIgd2FzIHN0aWxsIGJlaW5nXG4gICAgLy8gcHJvY2Vzc2VkIGJ5IGphbnVzLiB3ZSBzaG91bGQgZmluZCBzb21lIG1vcmUgcHJpbmNpcGxlZCB3YXkgdG8gZmlndXJlIG91dFxuICAgIC8vIHdoZW4gamFudXMgaXMgZG9uZSBpbiB0aGUgZnV0dXJlLlxuICAgIGlmICh0aGlzLmxvY2FsTWVkaWFTdHJlYW0pIHtcbiAgICAgIHRoaXMubG9jYWxNZWRpYVN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgICAgY29ubi5hZGRUcmFjayh0cmFjaywgdGhpcy5sb2NhbE1lZGlhU3RyZWFtKTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIC8vIEhhbmRsZSBhbGwgb2YgdGhlIGpvaW4gYW5kIGxlYXZlIGV2ZW50cy5cbiAgICBoYW5kbGUub24oXCJldmVudFwiLCBldiA9PiB7XG4gICAgICB2YXIgZGF0YSA9IGV2LnBsdWdpbmRhdGEuZGF0YTtcbiAgICAgIGlmIChkYXRhLmV2ZW50ID09IFwiam9pblwiICYmIGRhdGEucm9vbV9pZCA9PSB0aGlzLnJvb20pIHtcbiAgICAgICAgaWYgKHRoaXMuZGVsYXllZFJlY29ubmVjdFRpbWVvdXQpIHtcbiAgICAgICAgICAvLyBEb24ndCBjcmVhdGUgYSBuZXcgUlRDUGVlckNvbm5lY3Rpb24sIGFsbCBSVENQZWVyQ29ubmVjdGlvbiB3aWxsIGJlIGNsb3NlZCBpbiBsZXNzIHRoYW4gMTBzLlxuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmFkZE9jY3VwYW50KGRhdGEudXNlcl9pZCk7XG4gICAgICB9IGVsc2UgaWYgKGRhdGEuZXZlbnQgPT0gXCJsZWF2ZVwiICYmIGRhdGEucm9vbV9pZCA9PSB0aGlzLnJvb20pIHtcbiAgICAgICAgdGhpcy5yZW1vdmVPY2N1cGFudChkYXRhLnVzZXJfaWQpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09IFwiYmxvY2tlZFwiKSB7XG4gICAgICAgIGRvY3VtZW50LmJvZHkuZGlzcGF0Y2hFdmVudChuZXcgQ3VzdG9tRXZlbnQoXCJibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBkYXRhLmJ5IH0gfSkpO1xuICAgICAgfSBlbHNlIGlmIChkYXRhLmV2ZW50ID09IFwidW5ibG9ja2VkXCIpIHtcbiAgICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcInVuYmxvY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogZGF0YS5ieSB9IH0pKTtcbiAgICAgIH0gZWxzZSBpZiAoZGF0YS5ldmVudCA9PT0gXCJkYXRhXCIpIHtcbiAgICAgICAgdGhpcy5vbkRhdGEoSlNPTi5wYXJzZShkYXRhLmJvZHkpLCBcImphbnVzLWV2ZW50XCIpO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgZGVidWcoXCJwdWIgd2FpdGluZyBmb3Igam9pblwiKTtcblxuICAgIC8vIFNlbmQgam9pbiBtZXNzYWdlIHRvIGphbnVzLiBMaXN0ZW4gZm9yIGpvaW4vbGVhdmUgbWVzc2FnZXMuIEF1dG9tYXRpY2FsbHkgc3Vic2NyaWJlIHRvIGFsbCB1c2VycycgV2ViUlRDIGRhdGEuXG4gICAgdmFyIG1lc3NhZ2UgPSBhd2FpdCB0aGlzLnNlbmRKb2luKGhhbmRsZSwge1xuICAgICAgbm90aWZpY2F0aW9uczogdHJ1ZSxcbiAgICAgIGRhdGE6IHRydWVcbiAgICB9KTtcblxuICAgIGlmICghbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEuc3VjY2Vzcykge1xuICAgICAgY29uc3QgZXJyID0gbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEuZXJyb3I7XG4gICAgICBjb25zb2xlLmVycm9yKGVycik7XG4gICAgICAvLyBXZSBtYXkgZ2V0IGhlcmUgYmVjYXVzZSBvZiBhbiBleHBpcmVkIEpXVC5cbiAgICAgIC8vIENsb3NlIHRoZSBjb25uZWN0aW9uIG91cnNlbGYgb3RoZXJ3aXNlIGphbnVzIHdpbGwgY2xvc2UgaXQgYWZ0ZXJcbiAgICAgIC8vIHNlc3Npb25fdGltZW91dCBiZWNhdXNlIHdlIGRpZG4ndCBzZW5kIGFueSBrZWVwYWxpdmUgYW5kIHRoaXMgd2lsbFxuICAgICAgLy8gdHJpZ2dlciBhIGRlbGF5ZWQgcmVjb25uZWN0IGJlY2F1c2Ugb2YgdGhlIGljZWNvbm5lY3Rpb25zdGF0ZWNoYW5nZVxuICAgICAgLy8gbGlzdGVuZXIgZm9yIGZhaWx1cmUgc3RhdGUuXG4gICAgICAvLyBFdmVuIGlmIHRoZSBhcHAgY29kZSBjYWxscyBkaXNjb25uZWN0IGluIGNhc2Ugb2YgZXJyb3IsIGRpc2Nvbm5lY3RcbiAgICAgIC8vIHdvbid0IGNsb3NlIHRoZSBwZWVyIGNvbm5lY3Rpb24gYmVjYXVzZSB0aGlzLnB1Ymxpc2hlciBpcyBub3Qgc2V0LlxuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cblxuICAgIHZhciBpbml0aWFsT2NjdXBhbnRzID0gbWVzc2FnZS5wbHVnaW5kYXRhLmRhdGEucmVzcG9uc2UudXNlcnNbdGhpcy5yb29tXSB8fCBbXTtcblxuICAgIGlmIChpbml0aWFsT2NjdXBhbnRzLmluY2x1ZGVzKHRoaXMuY2xpZW50SWQpKSB7XG4gICAgICBjb25zb2xlLndhcm4oXCJKYW51cyBzdGlsbCBoYXMgcHJldmlvdXMgc2Vzc2lvbiBmb3IgdGhpcyBjbGllbnQuIFJlY29ubmVjdGluZyBpbiAxMHMuXCIpO1xuICAgICAgdGhpcy5wZXJmb3JtRGVsYXllZFJlY29ubmVjdCgpO1xuICAgIH1cblxuICAgIGRlYnVnKFwicHVibGlzaGVyIHJlYWR5XCIpO1xuICAgIHJldHVybiB7XG4gICAgICBoYW5kbGUsXG4gICAgICBpbml0aWFsT2NjdXBhbnRzLFxuICAgICAgcmVsaWFibGVDaGFubmVsLFxuICAgICAgdW5yZWxpYWJsZUNoYW5uZWwsXG4gICAgICBjb25uXG4gICAgfTtcbiAgfVxuXG4gIGNvbmZpZ3VyZVB1Ymxpc2hlclNkcChqc2VwKSB7XG4gICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKC9hPWZtdHA6KDEwOXwxMTEpLipcXHJcXG4vZywgKGxpbmUsIHB0KSA9PiB7XG4gICAgICBjb25zdCBwYXJhbWV0ZXJzID0gT2JqZWN0LmFzc2lnbihzZHBVdGlscy5wYXJzZUZtdHAobGluZSksIE9QVVNfUEFSQU1FVEVSUyk7XG4gICAgICByZXR1cm4gc2RwVXRpbHMud3JpdGVGbXRwKHsgcGF5bG9hZFR5cGU6IHB0LCBwYXJhbWV0ZXJzOiBwYXJhbWV0ZXJzIH0pO1xuICAgIH0pO1xuICAgIHJldHVybiBqc2VwO1xuICB9XG5cbiAgY29uZmlndXJlU3Vic2NyaWJlclNkcChqc2VwKSB7XG4gICAgLy8gdG9kbzogY29uc2lkZXIgY2xlYW5pbmcgdXAgdGhlc2UgaGFja3MgdG8gdXNlIHNkcHV0aWxzXG4gICAgaWYgKCFpc0gyNjRWaWRlb1N1cHBvcnRlZCkge1xuICAgICAgaWYgKG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZihcIkhlYWRsZXNzQ2hyb21lXCIpICE9PSAtMSkge1xuICAgICAgICAvLyBIZWFkbGVzc0Nocm9tZSAoZS5nLiBwdXBwZXRlZXIpIGRvZXNuJ3Qgc3VwcG9ydCB3ZWJydGMgdmlkZW8gc3RyZWFtcywgc28gd2UgcmVtb3ZlIHRob3NlIGxpbmVzIGZyb20gdGhlIFNEUC5cbiAgICAgICAganNlcC5zZHAgPSBqc2VwLnNkcC5yZXBsYWNlKC9tPXZpZGVvW15dKm09LywgXCJtPVwiKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBUT0RPOiBIYWNrIHRvIGdldCB2aWRlbyB3b3JraW5nIG9uIENocm9tZSBmb3IgQW5kcm9pZC4gaHR0cHM6Ly9ncm91cHMuZ29vZ2xlLmNvbS9mb3J1bS8jIXRvcGljL21vemlsbGEuZGV2Lm1lZGlhL1llMjl2dU1UcG84XG4gICAgaWYgKG5hdmlnYXRvci51c2VyQWdlbnQuaW5kZXhPZihcIkFuZHJvaWRcIikgPT09IC0xKSB7XG4gICAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoXG4gICAgICAgIFwiYT1ydGNwLWZiOjEwNyBnb29nLXJlbWJcXHJcXG5cIixcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcbmE9cnRjcC1mYjoxMDcgdHJhbnNwb3J0LWNjXFxyXFxuYT1mbXRwOjEwNyBsZXZlbC1hc3ltbWV0cnktYWxsb3dlZD0xO3BhY2tldGl6YXRpb24tbW9kZT0xO3Byb2ZpbGUtbGV2ZWwtaWQ9NDJlMDFmXFxyXFxuXCJcbiAgICAgICk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGpzZXAuc2RwID0ganNlcC5zZHAucmVwbGFjZShcbiAgICAgICAgXCJhPXJ0Y3AtZmI6MTA3IGdvb2ctcmVtYlxcclxcblwiLFxuICAgICAgICBcImE9cnRjcC1mYjoxMDcgZ29vZy1yZW1iXFxyXFxuYT1ydGNwLWZiOjEwNyB0cmFuc3BvcnQtY2NcXHJcXG5hPWZtdHA6MTA3IGxldmVsLWFzeW1tZXRyeS1hbGxvd2VkPTE7cGFja2V0aXphdGlvbi1tb2RlPTE7cHJvZmlsZS1sZXZlbC1pZD00MjAwMWZcXHJcXG5cIlxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIGpzZXA7XG4gIH1cblxuICBhc3luYyBmaXhTYWZhcmlJY2VVRnJhZyhqc2VwKSB7XG4gICAgLy8gU2FmYXJpIHByb2R1Y2VzIGEgXFxuIGluc3RlYWQgb2YgYW4gXFxyXFxuIGZvciB0aGUgaWNlLXVmcmFnLiBTZWUgaHR0cHM6Ly9naXRodWIuY29tL21lZXRlY2hvL2phbnVzLWdhdGV3YXkvaXNzdWVzLzE4MThcbiAgICBqc2VwLnNkcCA9IGpzZXAuc2RwLnJlcGxhY2UoL1teXFxyXVxcbmE9aWNlLXVmcmFnL2csIFwiXFxyXFxuYT1pY2UtdWZyYWdcIik7XG4gICAgcmV0dXJuIGpzZXBcbiAgfVxuXG4gIGFzeW5jIGNyZWF0ZVN1YnNjcmliZXIob2NjdXBhbnRJZCwgbWF4UmV0cmllcyA9IDUpIHtcbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsbGVkIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgYmVmb3JlIHN1YnNjcmlwdGlvbiBuZWdvdGF0aW9uLlwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHZhciBoYW5kbGUgPSBuZXcgbWouSmFudXNQbHVnaW5IYW5kbGUodGhpcy5zZXNzaW9uKTtcbiAgICB2YXIgY29ubiA9IG5ldyBSVENQZWVyQ29ubmVjdGlvbih0aGlzLnBlZXJDb25uZWN0aW9uQ29uZmlnIHx8IERFRkFVTFRfUEVFUl9DT05ORUNUSU9OX0NPTkZJRyk7XG5cbiAgICBkZWJ1ZyhvY2N1cGFudElkICsgXCI6IHN1YiB3YWl0aW5nIGZvciBzZnVcIik7XG4gICAgYXdhaXQgaGFuZGxlLmF0dGFjaChcImphbnVzLnBsdWdpbi5zZnVcIik7XG5cbiAgICB0aGlzLmFzc29jaWF0ZShjb25uLCBoYW5kbGUpO1xuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWIgd2FpdGluZyBmb3Igam9pblwiKTtcblxuICAgIGlmICh0aGlzLmxlZnRPY2N1cGFudHMuaGFzKG9jY3VwYW50SWQpKSB7XG4gICAgICBjb25uLmNsb3NlKCk7XG4gICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiBjYW5jZWxsZWQgb2NjdXBhbnQgY29ubmVjdGlvbiwgb2NjdXBhbnQgbGVmdCBhZnRlciBhdHRhY2hcIik7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBsZXQgd2VicnRjRmFpbGVkID0gZmFsc2U7XG5cbiAgICBjb25zdCB3ZWJydGN1cCA9IG5ldyBQcm9taXNlKHJlc29sdmUgPT4ge1xuICAgICAgY29uc3QgbGVmdEludGVydmFsID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgICAgIGNsZWFySW50ZXJ2YWwobGVmdEludGVydmFsKTtcbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH1cbiAgICAgIH0sIDEwMDApO1xuXG4gICAgICBjb25zdCB0aW1lb3V0ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwobGVmdEludGVydmFsKTtcbiAgICAgICAgd2VicnRjRmFpbGVkID0gdHJ1ZTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSwgU1VCU0NSSUJFX1RJTUVPVVRfTVMpO1xuXG4gICAgICBoYW5kbGUub24oXCJ3ZWJydGN1cFwiLCAoKSA9PiB7XG4gICAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcbiAgICAgICAgY2xlYXJJbnRlcnZhbChsZWZ0SW50ZXJ2YWwpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8vIFNlbmQgam9pbiBtZXNzYWdlIHRvIGphbnVzLiBEb24ndCBsaXN0ZW4gZm9yIGpvaW4vbGVhdmUgbWVzc2FnZXMuIFN1YnNjcmliZSB0byB0aGUgb2NjdXBhbnQncyBtZWRpYS5cbiAgICAvLyBKYW51cyBzaG91bGQgc2VuZCB1cyBhbiBvZmZlciBmb3IgdGhpcyBvY2N1cGFudCdzIG1lZGlhIGluIHJlc3BvbnNlIHRvIHRoaXMuXG4gICAgYXdhaXQgdGhpcy5zZW5kSm9pbihoYW5kbGUsIHsgbWVkaWE6IG9jY3VwYW50SWQgfSk7XG5cbiAgICBpZiAodGhpcy5sZWZ0T2NjdXBhbnRzLmhhcyhvY2N1cGFudElkKSkge1xuICAgICAgY29ubi5jbG9zZSgpO1xuICAgICAgY29uc29sZS53YXJuKG9jY3VwYW50SWQgKyBcIjogY2FuY2VsbGVkIG9jY3VwYW50IGNvbm5lY3Rpb24sIG9jY3VwYW50IGxlZnQgYWZ0ZXIgam9pblwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGRlYnVnKG9jY3VwYW50SWQgKyBcIjogc3ViIHdhaXRpbmcgZm9yIHdlYnJ0Y3VwXCIpO1xuICAgIGF3YWl0IHdlYnJ0Y3VwO1xuXG4gICAgaWYgKHRoaXMubGVmdE9jY3VwYW50cy5oYXMob2NjdXBhbnRJZCkpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGNvbnNvbGUud2FybihvY2N1cGFudElkICsgXCI6IGNhbmNlbCBvY2N1cGFudCBjb25uZWN0aW9uLCBvY2N1cGFudCBsZWZ0IGR1cmluZyBvciBhZnRlciB3ZWJydGN1cFwiKTtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIGlmICh3ZWJydGNGYWlsZWQpIHtcbiAgICAgIGNvbm4uY2xvc2UoKTtcbiAgICAgIGlmIChtYXhSZXRyaWVzID4gMCkge1xuICAgICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiB3ZWJydGMgdXAgdGltZWQgb3V0LCByZXRyeWluZ1wiKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlU3Vic2NyaWJlcihvY2N1cGFudElkLCBtYXhSZXRyaWVzIC0gMSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLndhcm4ob2NjdXBhbnRJZCArIFwiOiB3ZWJydGMgdXAgdGltZWQgb3V0XCIpO1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaXNTYWZhcmkgJiYgIXRoaXMuX2lPU0hhY2tEZWxheWVkSW5pdGlhbFBlZXIpIHtcbiAgICAgIC8vIEhBQ0s6IHRoZSBmaXJzdCBwZWVyIG9uIFNhZmFyaSBkdXJpbmcgcGFnZSBsb2FkIGNhbiBmYWlsIHRvIHdvcmsgaWYgd2UgZG9uJ3RcbiAgICAgIC8vIHdhaXQgc29tZSB0aW1lIGJlZm9yZSBjb250aW51aW5nIGhlcmUuIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL21vemlsbGEvaHVicy9wdWxsLzE2OTJcbiAgICAgIGF3YWl0IChuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCAzMDAwKSkpO1xuICAgICAgdGhpcy5faU9TSGFja0RlbGF5ZWRJbml0aWFsUGVlciA9IHRydWU7XG4gICAgfVxuXG4gICAgdmFyIG1lZGlhU3RyZWFtID0gbmV3IE1lZGlhU3RyZWFtKCk7XG4gICAgdmFyIHJlY2VpdmVycyA9IGNvbm4uZ2V0UmVjZWl2ZXJzKCk7XG4gICAgcmVjZWl2ZXJzLmZvckVhY2gocmVjZWl2ZXIgPT4ge1xuICAgICAgaWYgKHJlY2VpdmVyLnRyYWNrKSB7XG4gICAgICAgIG1lZGlhU3RyZWFtLmFkZFRyYWNrKHJlY2VpdmVyLnRyYWNrKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAobWVkaWFTdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoID09PSAwKSB7XG4gICAgICBtZWRpYVN0cmVhbSA9IG51bGw7XG4gICAgfVxuXG4gICAgZGVidWcob2NjdXBhbnRJZCArIFwiOiBzdWJzY3JpYmVyIHJlYWR5XCIpO1xuICAgIHJldHVybiB7XG4gICAgICBoYW5kbGUsXG4gICAgICBtZWRpYVN0cmVhbSxcbiAgICAgIGNvbm5cbiAgICB9O1xuICB9XG5cbiAgc2VuZEpvaW4oaGFuZGxlLCBzdWJzY3JpYmUpIHtcbiAgICByZXR1cm4gaGFuZGxlLnNlbmRNZXNzYWdlKHtcbiAgICAgIGtpbmQ6IFwiam9pblwiLFxuICAgICAgcm9vbV9pZDogdGhpcy5yb29tLFxuICAgICAgdXNlcl9pZDogdGhpcy5jbGllbnRJZCxcbiAgICAgIHN1YnNjcmliZSxcbiAgICAgIHRva2VuOiB0aGlzLmpvaW5Ub2tlblxuICAgIH0pO1xuICB9XG5cbiAgdG9nZ2xlRnJlZXplKCkge1xuICAgIGlmICh0aGlzLmZyb3plbikge1xuICAgICAgdGhpcy51bmZyZWV6ZSgpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmZyZWV6ZSgpO1xuICAgIH1cbiAgfVxuXG4gIGZyZWV6ZSgpIHtcbiAgICB0aGlzLmZyb3plbiA9IHRydWU7XG4gIH1cblxuICB1bmZyZWV6ZSgpIHtcbiAgICB0aGlzLmZyb3plbiA9IGZhbHNlO1xuICAgIHRoaXMuZmx1c2hQZW5kaW5nVXBkYXRlcygpO1xuICB9XG5cbiAgZGF0YUZvclVwZGF0ZU11bHRpTWVzc2FnZShuZXR3b3JrSWQsIG1lc3NhZ2UpIHtcbiAgICAvLyBcImRcIiBpcyBhbiBhcnJheSBvZiBlbnRpdHkgZGF0YXMsIHdoZXJlIGVhY2ggaXRlbSBpbiB0aGUgYXJyYXkgcmVwcmVzZW50cyBhIHVuaXF1ZSBlbnRpdHkgYW5kIGNvbnRhaW5zXG4gICAgLy8gbWV0YWRhdGEgZm9yIHRoZSBlbnRpdHksIGFuZCBhbiBhcnJheSBvZiBjb21wb25lbnRzIHRoYXQgaGF2ZSBiZWVuIHVwZGF0ZWQgb24gdGhlIGVudGl0eS5cbiAgICAvLyBUaGlzIG1ldGhvZCBmaW5kcyB0aGUgZGF0YSBjb3JyZXNwb25kaW5nIHRvIHRoZSBnaXZlbiBuZXR3b3JrSWQuXG4gICAgZm9yIChsZXQgaSA9IDAsIGwgPSBtZXNzYWdlLmRhdGEuZC5sZW5ndGg7IGkgPCBsOyBpKyspIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBtZXNzYWdlLmRhdGEuZFtpXTtcblxuICAgICAgaWYgKGRhdGEubmV0d29ya0lkID09PSBuZXR3b3JrSWQpIHtcbiAgICAgICAgcmV0dXJuIGRhdGE7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBnZXRQZW5kaW5nRGF0YShuZXR3b3JrSWQsIG1lc3NhZ2UpIHtcbiAgICBpZiAoIW1lc3NhZ2UpIHJldHVybiBudWxsO1xuXG4gICAgbGV0IGRhdGEgPSBtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyB0aGlzLmRhdGFGb3JVcGRhdGVNdWx0aU1lc3NhZ2UobmV0d29ya0lkLCBtZXNzYWdlKSA6IG1lc3NhZ2UuZGF0YTtcblxuICAgIC8vIElnbm9yZSBtZXNzYWdlcyByZWxhdGluZyB0byB1c2VycyB3aG8gaGF2ZSBkaXNjb25uZWN0ZWQgc2luY2UgZnJlZXppbmcsIHRoZWlyIGVudGl0aWVzXG4gICAgLy8gd2lsbCBoYXZlIGFsZWFkeSBiZWVuIHJlbW92ZWQgYnkgTkFGLlxuICAgIC8vIE5vdGUgdGhhdCBkZWxldGUgbWVzc2FnZXMgaGF2ZSBubyBcIm93bmVyXCIgc28gd2UgaGF2ZSB0byBjaGVjayBmb3IgdGhhdCBhcyB3ZWxsLlxuICAgIGlmIChkYXRhLm93bmVyICYmICF0aGlzLm9jY3VwYW50c1tkYXRhLm93bmVyXSkgcmV0dXJuIG51bGw7XG5cbiAgICAvLyBJZ25vcmUgbWVzc2FnZXMgZnJvbSB1c2VycyB0aGF0IHdlIG1heSBoYXZlIGJsb2NrZWQgd2hpbGUgZnJvemVuLlxuICAgIGlmIChkYXRhLm93bmVyICYmIHRoaXMuYmxvY2tlZENsaWVudHMuaGFzKGRhdGEub3duZXIpKSByZXR1cm4gbnVsbDtcblxuICAgIHJldHVybiBkYXRhXG4gIH1cblxuICAvLyBVc2VkIGV4dGVybmFsbHlcbiAgZ2V0UGVuZGluZ0RhdGFGb3JOZXR3b3JrSWQobmV0d29ya0lkKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0UGVuZGluZ0RhdGEobmV0d29ya0lkLCB0aGlzLmZyb3plblVwZGF0ZXMuZ2V0KG5ldHdvcmtJZCkpO1xuICB9XG5cbiAgZmx1c2hQZW5kaW5nVXBkYXRlcygpIHtcbiAgICBmb3IgKGNvbnN0IFtuZXR3b3JrSWQsIG1lc3NhZ2VdIG9mIHRoaXMuZnJvemVuVXBkYXRlcykge1xuICAgICAgbGV0IGRhdGEgPSB0aGlzLmdldFBlbmRpbmdEYXRhKG5ldHdvcmtJZCwgbWVzc2FnZSk7XG4gICAgICBpZiAoIWRhdGEpIGNvbnRpbnVlO1xuXG4gICAgICAvLyBPdmVycmlkZSB0aGUgZGF0YSB0eXBlIG9uIFwidW1cIiBtZXNzYWdlcyB0eXBlcywgc2luY2Ugd2UgZXh0cmFjdCBlbnRpdHkgdXBkYXRlcyBmcm9tIFwidW1cIiBtZXNzYWdlcyBpbnRvXG4gICAgICAvLyBpbmRpdmlkdWFsIGZyb3plblVwZGF0ZXMgaW4gc3RvcmVTaW5nbGVNZXNzYWdlLlxuICAgICAgY29uc3QgZGF0YVR5cGUgPSBtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIgPyBcInVcIiA6IG1lc3NhZ2UuZGF0YVR5cGU7XG5cbiAgICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UobnVsbCwgZGF0YVR5cGUsIGRhdGEsIG1lc3NhZ2Uuc291cmNlKTtcbiAgICB9XG4gICAgdGhpcy5mcm96ZW5VcGRhdGVzLmNsZWFyKCk7XG4gIH1cblxuICBzdG9yZU1lc3NhZ2UobWVzc2FnZSkge1xuICAgIGlmIChtZXNzYWdlLmRhdGFUeXBlID09PSBcInVtXCIpIHsgLy8gVXBkYXRlTXVsdGlcbiAgICAgIGZvciAobGV0IGkgPSAwLCBsID0gbWVzc2FnZS5kYXRhLmQubGVuZ3RoOyBpIDwgbDsgaSsrKSB7XG4gICAgICAgIHRoaXMuc3RvcmVTaW5nbGVNZXNzYWdlKG1lc3NhZ2UsIGkpO1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnN0b3JlU2luZ2xlTWVzc2FnZShtZXNzYWdlKTtcbiAgICB9XG4gIH1cblxuICBzdG9yZVNpbmdsZU1lc3NhZ2UobWVzc2FnZSwgaW5kZXgpIHtcbiAgICBjb25zdCBkYXRhID0gaW5kZXggIT09IHVuZGVmaW5lZCA/IG1lc3NhZ2UuZGF0YS5kW2luZGV4XSA6IG1lc3NhZ2UuZGF0YTtcbiAgICBjb25zdCBkYXRhVHlwZSA9IG1lc3NhZ2UuZGF0YVR5cGU7XG4gICAgY29uc3Qgc291cmNlID0gbWVzc2FnZS5zb3VyY2U7XG5cbiAgICBjb25zdCBuZXR3b3JrSWQgPSBkYXRhLm5ldHdvcmtJZDtcblxuICAgIGlmICghdGhpcy5mcm96ZW5VcGRhdGVzLmhhcyhuZXR3b3JrSWQpKSB7XG4gICAgICB0aGlzLmZyb3plblVwZGF0ZXMuc2V0KG5ldHdvcmtJZCwgbWVzc2FnZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHN0b3JlZE1lc3NhZ2UgPSB0aGlzLmZyb3plblVwZGF0ZXMuZ2V0KG5ldHdvcmtJZCk7XG4gICAgICBjb25zdCBzdG9yZWREYXRhID0gc3RvcmVkTWVzc2FnZS5kYXRhVHlwZSA9PT0gXCJ1bVwiID8gdGhpcy5kYXRhRm9yVXBkYXRlTXVsdGlNZXNzYWdlKG5ldHdvcmtJZCwgc3RvcmVkTWVzc2FnZSkgOiBzdG9yZWRNZXNzYWdlLmRhdGE7XG5cbiAgICAgIC8vIEF2b2lkIHVwZGF0aW5nIGNvbXBvbmVudHMgaWYgdGhlIGVudGl0eSBkYXRhIHJlY2VpdmVkIGRpZCBub3QgY29tZSBmcm9tIHRoZSBjdXJyZW50IG93bmVyLlxuICAgICAgY29uc3QgaXNPdXRkYXRlZE1lc3NhZ2UgPSBkYXRhLmxhc3RPd25lclRpbWUgPCBzdG9yZWREYXRhLmxhc3RPd25lclRpbWU7XG4gICAgICBjb25zdCBpc0NvbnRlbXBvcmFuZW91c01lc3NhZ2UgPSBkYXRhLmxhc3RPd25lclRpbWUgPT09IHN0b3JlZERhdGEubGFzdE93bmVyVGltZTtcbiAgICAgIGlmIChpc091dGRhdGVkTWVzc2FnZSB8fCAoaXNDb250ZW1wb3JhbmVvdXNNZXNzYWdlICYmIHN0b3JlZERhdGEub3duZXIgPiBkYXRhLm93bmVyKSkge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGlmIChkYXRhVHlwZSA9PT0gXCJyXCIpIHtcbiAgICAgICAgY29uc3QgY3JlYXRlZFdoaWxlRnJvemVuID0gc3RvcmVkRGF0YSAmJiBzdG9yZWREYXRhLmlzRmlyc3RTeW5jO1xuICAgICAgICBpZiAoY3JlYXRlZFdoaWxlRnJvemVuKSB7XG4gICAgICAgICAgLy8gSWYgdGhlIGVudGl0eSB3YXMgY3JlYXRlZCBhbmQgZGVsZXRlZCB3aGlsZSBmcm96ZW4sIGRvbid0IGJvdGhlciBjb252ZXlpbmcgYW55dGhpbmcgdG8gdGhlIGNvbnN1bWVyLlxuICAgICAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5kZWxldGUobmV0d29ya0lkKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBEZWxldGUgbWVzc2FnZXMgb3ZlcnJpZGUgYW55IG90aGVyIG1lc3NhZ2VzIGZvciB0aGlzIGVudGl0eVxuICAgICAgICAgIHRoaXMuZnJvemVuVXBkYXRlcy5zZXQobmV0d29ya0lkLCBtZXNzYWdlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gbWVyZ2UgaW4gY29tcG9uZW50IHVwZGF0ZXNcbiAgICAgICAgaWYgKHN0b3JlZERhdGEuY29tcG9uZW50cyAmJiBkYXRhLmNvbXBvbmVudHMpIHtcbiAgICAgICAgICBPYmplY3QuYXNzaWduKHN0b3JlZERhdGEuY29tcG9uZW50cywgZGF0YS5jb21wb25lbnRzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIG9uRGF0YUNoYW5uZWxNZXNzYWdlKGUsIHNvdXJjZSkge1xuICAgIHRoaXMub25EYXRhKEpTT04ucGFyc2UoZS5kYXRhKSwgc291cmNlKTtcbiAgfVxuXG4gIG9uRGF0YShtZXNzYWdlLCBzb3VyY2UpIHtcbiAgICBpZiAoZGVidWcuZW5hYmxlZCkge1xuICAgICAgZGVidWcoYERDIGluOiAke21lc3NhZ2V9YCk7XG4gICAgfVxuXG4gICAgaWYgKCFtZXNzYWdlLmRhdGFUeXBlKSByZXR1cm47XG5cbiAgICBtZXNzYWdlLnNvdXJjZSA9IHNvdXJjZTtcblxuICAgIGlmICh0aGlzLmZyb3plbikge1xuICAgICAgdGhpcy5zdG9yZU1lc3NhZ2UobWVzc2FnZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMub25PY2N1cGFudE1lc3NhZ2UobnVsbCwgbWVzc2FnZS5kYXRhVHlwZSwgbWVzc2FnZS5kYXRhLCBtZXNzYWdlLnNvdXJjZSk7XG4gICAgfVxuICB9XG5cbiAgc2hvdWxkU3RhcnRDb25uZWN0aW9uVG8oY2xpZW50KSB7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBzdGFydFN0cmVhbUNvbm5lY3Rpb24oY2xpZW50KSB7fVxuXG4gIGNsb3NlU3RyZWFtQ29ubmVjdGlvbihjbGllbnQpIHt9XG5cbiAgZ2V0Q29ubmVjdFN0YXR1cyhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLm9jY3VwYW50c1tjbGllbnRJZF0gPyBOQUYuYWRhcHRlcnMuSVNfQ09OTkVDVEVEIDogTkFGLmFkYXB0ZXJzLk5PVF9DT05ORUNURUQ7XG4gIH1cblxuICBhc3luYyB1cGRhdGVUaW1lT2Zmc2V0KCkge1xuICAgIGlmICh0aGlzLmlzRGlzY29ubmVjdGVkKCkpIHJldHVybjtcblxuICAgIGNvbnN0IGNsaWVudFNlbnRUaW1lID0gRGF0ZS5ub3coKTtcblxuICAgIGNvbnN0IHJlcyA9IGF3YWl0IGZldGNoKGRvY3VtZW50LmxvY2F0aW9uLmhyZWYsIHtcbiAgICAgIG1ldGhvZDogXCJIRUFEXCIsXG4gICAgICBjYWNoZTogXCJuby1jYWNoZVwiXG4gICAgfSk7XG5cbiAgICBjb25zdCBwcmVjaXNpb24gPSAxMDAwO1xuICAgIGNvbnN0IHNlcnZlclJlY2VpdmVkVGltZSA9IG5ldyBEYXRlKHJlcy5oZWFkZXJzLmdldChcIkRhdGVcIikpLmdldFRpbWUoKSArIHByZWNpc2lvbiAvIDI7XG4gICAgY29uc3QgY2xpZW50UmVjZWl2ZWRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBjb25zdCBzZXJ2ZXJUaW1lID0gc2VydmVyUmVjZWl2ZWRUaW1lICsgKGNsaWVudFJlY2VpdmVkVGltZSAtIGNsaWVudFNlbnRUaW1lKSAvIDI7XG4gICAgY29uc3QgdGltZU9mZnNldCA9IHNlcnZlclRpbWUgLSBjbGllbnRSZWNlaXZlZFRpbWU7XG5cbiAgICB0aGlzLnNlcnZlclRpbWVSZXF1ZXN0cysrO1xuXG4gICAgaWYgKHRoaXMuc2VydmVyVGltZVJlcXVlc3RzIDw9IDEwKSB7XG4gICAgICB0aGlzLnRpbWVPZmZzZXRzLnB1c2godGltZU9mZnNldCk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMudGltZU9mZnNldHNbdGhpcy5zZXJ2ZXJUaW1lUmVxdWVzdHMgJSAxMF0gPSB0aW1lT2Zmc2V0O1xuICAgIH1cblxuICAgIHRoaXMuYXZnVGltZU9mZnNldCA9IHRoaXMudGltZU9mZnNldHMucmVkdWNlKChhY2MsIG9mZnNldCkgPT4gKGFjYyArPSBvZmZzZXQpLCAwKSAvIHRoaXMudGltZU9mZnNldHMubGVuZ3RoO1xuXG4gICAgaWYgKHRoaXMuc2VydmVyVGltZVJlcXVlc3RzID4gMTApIHtcbiAgICAgIGRlYnVnKGBuZXcgc2VydmVyIHRpbWUgb2Zmc2V0OiAke3RoaXMuYXZnVGltZU9mZnNldH1tc2ApO1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnVwZGF0ZVRpbWVPZmZzZXQoKSwgNSAqIDYwICogMTAwMCk7IC8vIFN5bmMgY2xvY2sgZXZlcnkgNSBtaW51dGVzLlxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnVwZGF0ZVRpbWVPZmZzZXQoKTtcbiAgICB9XG4gIH1cblxuICBnZXRTZXJ2ZXJUaW1lKCkge1xuICAgIHJldHVybiBEYXRlLm5vdygpICsgdGhpcy5hdmdUaW1lT2Zmc2V0O1xuICB9XG5cbiAgZ2V0TWVkaWFTdHJlYW0oY2xpZW50SWQsIHR5cGUgPSBcImF1ZGlvXCIpIHtcbiAgICBpZiAodGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdKSB7XG4gICAgICBkZWJ1ZyhgQWxyZWFkeSBoYWQgJHt0eXBlfSBmb3IgJHtjbGllbnRJZH1gKTtcbiAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodGhpcy5tZWRpYVN0cmVhbXNbY2xpZW50SWRdW3R5cGVdKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZGVidWcoYFdhaXRpbmcgb24gJHt0eXBlfSBmb3IgJHtjbGllbnRJZH1gKTtcbiAgICAgIGlmICghdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMoY2xpZW50SWQpKSB7XG4gICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuc2V0KGNsaWVudElkLCB7fSk7XG5cbiAgICAgICAgY29uc3QgYXVkaW9Qcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS5hdWRpbyA9IHsgcmVzb2x2ZSwgcmVqZWN0IH07XG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB2aWRlb1Byb21pc2UgPSBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLnZpZGVvID0geyByZXNvbHZlLCByZWplY3QgfTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpLmF1ZGlvLnByb21pc2UgPSBhdWRpb1Byb21pc2U7XG4gICAgICAgIHRoaXMucGVuZGluZ01lZGlhUmVxdWVzdHMuZ2V0KGNsaWVudElkKS52aWRlby5wcm9taXNlID0gdmlkZW9Qcm9taXNlO1xuXG4gICAgICAgIGF1ZGlvUHJvbWlzZS5jYXRjaChlID0+IGNvbnNvbGUud2FybihgJHtjbGllbnRJZH0gZ2V0TWVkaWFTdHJlYW0gQXVkaW8gRXJyb3JgLCBlKSk7XG4gICAgICAgIHZpZGVvUHJvbWlzZS5jYXRjaChlID0+IGNvbnNvbGUud2FybihgJHtjbGllbnRJZH0gZ2V0TWVkaWFTdHJlYW0gVmlkZW8gRXJyb3JgLCBlKSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gdGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5nZXQoY2xpZW50SWQpW3R5cGVdLnByb21pc2U7XG4gICAgfVxuICB9XG5cbiAgc2V0TWVkaWFTdHJlYW0oY2xpZW50SWQsIHN0cmVhbSkge1xuICAgIC8vIFNhZmFyaSBkb2Vzbid0IGxpa2UgaXQgd2hlbiB5b3UgdXNlIHNpbmdsZSBhIG1peGVkIG1lZGlhIHN0cmVhbSB3aGVyZSBvbmUgb2YgdGhlIHRyYWNrcyBpcyBpbmFjdGl2ZSwgc28gd2VcbiAgICAvLyBzcGxpdCB0aGUgdHJhY2tzIGludG8gdHdvIHN0cmVhbXMuXG4gICAgY29uc3QgYXVkaW9TdHJlYW0gPSBuZXcgTWVkaWFTdHJlYW0oKTtcbiAgICB0cnkge1xuICAgIHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmZvckVhY2godHJhY2sgPT4gYXVkaW9TdHJlYW0uYWRkVHJhY2sodHJhY2spKTtcblxuICAgIH0gY2F0Y2goZSkge1xuICAgICAgY29uc29sZS53YXJuKGAke2NsaWVudElkfSBzZXRNZWRpYVN0cmVhbSBBdWRpbyBFcnJvcmAsIGUpO1xuICAgIH1cbiAgICBjb25zdCB2aWRlb1N0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuICAgIHRyeSB7XG4gICAgc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB2aWRlb1N0cmVhbS5hZGRUcmFjayh0cmFjaykpO1xuXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY29uc29sZS53YXJuKGAke2NsaWVudElkfSBzZXRNZWRpYVN0cmVhbSBWaWRlbyBFcnJvcmAsIGUpO1xuICAgIH1cblxuICAgIHRoaXMubWVkaWFTdHJlYW1zW2NsaWVudElkXSA9IHsgYXVkaW86IGF1ZGlvU3RyZWFtLCB2aWRlbzogdmlkZW9TdHJlYW0gfTtcblxuICAgIC8vIFJlc29sdmUgdGhlIHByb21pc2UgZm9yIHRoZSB1c2VyJ3MgbWVkaWEgc3RyZWFtIGlmIGl0IGV4aXN0cy5cbiAgICBpZiAodGhpcy5wZW5kaW5nTWVkaWFSZXF1ZXN0cy5oYXMoY2xpZW50SWQpKSB7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkuYXVkaW8ucmVzb2x2ZShhdWRpb1N0cmVhbSk7XG4gICAgICB0aGlzLnBlbmRpbmdNZWRpYVJlcXVlc3RzLmdldChjbGllbnRJZCkudmlkZW8ucmVzb2x2ZSh2aWRlb1N0cmVhbSk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgc2V0TG9jYWxNZWRpYVN0cmVhbShzdHJlYW0pIHtcbiAgICAvLyBvdXIgam9iIGhlcmUgaXMgdG8gbWFrZSBzdXJlIHRoZSBjb25uZWN0aW9uIHdpbmRzIHVwIHdpdGggUlRQIHNlbmRlcnMgc2VuZGluZyB0aGUgc3R1ZmYgaW4gdGhpcyBzdHJlYW0sXG4gICAgLy8gYW5kIG5vdCB0aGUgc3R1ZmYgdGhhdCBpc24ndCBpbiB0aGlzIHN0cmVhbS4gc3RyYXRlZ3kgaXMgdG8gcmVwbGFjZSBleGlzdGluZyB0cmFja3MgaWYgd2UgY2FuLCBhZGQgdHJhY2tzXG4gICAgLy8gdGhhdCB3ZSBjYW4ndCByZXBsYWNlLCBhbmQgZGlzYWJsZSB0cmFja3MgdGhhdCBkb24ndCBleGlzdCBhbnltb3JlLlxuXG4gICAgLy8gbm90ZSB0aGF0IHdlIGRvbid0IGV2ZXIgcmVtb3ZlIGEgdHJhY2sgZnJvbSB0aGUgc3RyZWFtIC0tIHNpbmNlIEphbnVzIGRvZXNuJ3Qgc3VwcG9ydCBVbmlmaWVkIFBsYW4sIHdlIGFic29sdXRlbHlcbiAgICAvLyBjYW4ndCB3aW5kIHVwIHdpdGggYSBTRFAgdGhhdCBoYXMgPjEgYXVkaW8gb3IgPjEgdmlkZW8gdHJhY2tzLCBldmVuIGlmIG9uZSBvZiB0aGVtIGlzIGluYWN0aXZlICh3aGF0IHlvdSBnZXQgaWZcbiAgICAvLyB5b3UgcmVtb3ZlIGEgdHJhY2sgZnJvbSBhbiBleGlzdGluZyBzdHJlYW0uKVxuICAgIGlmICh0aGlzLnB1Ymxpc2hlciAmJiB0aGlzLnB1Ymxpc2hlci5jb25uKSB7XG4gICAgICBjb25zdCBleGlzdGluZ1NlbmRlcnMgPSB0aGlzLnB1Ymxpc2hlci5jb25uLmdldFNlbmRlcnMoKTtcbiAgICAgIGNvbnN0IG5ld1NlbmRlcnMgPSBbXTtcbiAgICAgIGNvbnN0IHRyYWNrcyA9IHN0cmVhbS5nZXRUcmFja3MoKTtcblxuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0cmFja3MubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgY29uc3QgdCA9IHRyYWNrc1tpXTtcbiAgICAgICAgY29uc3Qgc2VuZGVyID0gZXhpc3RpbmdTZW5kZXJzLmZpbmQocyA9PiBzLnRyYWNrICE9IG51bGwgJiYgcy50cmFjay5raW5kID09IHQua2luZCk7XG5cbiAgICAgICAgaWYgKHNlbmRlciAhPSBudWxsKSB7XG4gICAgICAgICAgaWYgKHNlbmRlci5yZXBsYWNlVHJhY2spIHtcbiAgICAgICAgICAgIGF3YWl0IHNlbmRlci5yZXBsYWNlVHJhY2sodCk7XG5cbiAgICAgICAgICAgIC8vIFdvcmthcm91bmQgaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTU3Njc3MVxuICAgICAgICAgICAgaWYgKHQua2luZCA9PT0gXCJ2aWRlb1wiICYmIHQuZW5hYmxlZCAmJiBuYXZpZ2F0b3IudXNlckFnZW50LnRvTG93ZXJDYXNlKCkuaW5kZXhPZignZmlyZWZveCcpID4gLTEpIHtcbiAgICAgICAgICAgICAgdC5lbmFibGVkID0gZmFsc2U7XG4gICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdC5lbmFibGVkID0gdHJ1ZSwgMTAwMCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIEZhbGxiYWNrIGZvciBicm93c2VycyB0aGF0IGRvbid0IHN1cHBvcnQgcmVwbGFjZVRyYWNrLiBBdCB0aGlzIHRpbWUgb2YgdGhpcyB3cml0aW5nXG4gICAgICAgICAgICAvLyBtb3N0IGJyb3dzZXJzIHN1cHBvcnQgaXQsIGFuZCB0ZXN0aW5nIHRoaXMgY29kZSBwYXRoIHNlZW1zIHRvIG5vdCB3b3JrIHByb3Blcmx5XG4gICAgICAgICAgICAvLyBpbiBDaHJvbWUgYW55bW9yZS5cbiAgICAgICAgICAgIHN0cmVhbS5yZW1vdmVUcmFjayhzZW5kZXIudHJhY2spO1xuICAgICAgICAgICAgc3RyZWFtLmFkZFRyYWNrKHQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBuZXdTZW5kZXJzLnB1c2goc2VuZGVyKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdTZW5kZXJzLnB1c2godGhpcy5wdWJsaXNoZXIuY29ubi5hZGRUcmFjayh0LCBzdHJlYW0pKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgZXhpc3RpbmdTZW5kZXJzLmZvckVhY2gocyA9PiB7XG4gICAgICAgIGlmICghbmV3U2VuZGVycy5pbmNsdWRlcyhzKSkge1xuICAgICAgICAgIHMudHJhY2suZW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gICAgdGhpcy5sb2NhbE1lZGlhU3RyZWFtID0gc3RyZWFtO1xuICAgIHRoaXMuc2V0TWVkaWFTdHJlYW0odGhpcy5jbGllbnRJZCwgc3RyZWFtKTtcbiAgfVxuXG4gIGVuYWJsZU1pY3JvcGhvbmUoZW5hYmxlZCkge1xuICAgIGlmICh0aGlzLnB1Ymxpc2hlciAmJiB0aGlzLnB1Ymxpc2hlci5jb25uKSB7XG4gICAgICB0aGlzLnB1Ymxpc2hlci5jb25uLmdldFNlbmRlcnMoKS5mb3JFYWNoKHMgPT4ge1xuICAgICAgICBpZiAocy50cmFjay5raW5kID09IFwiYXVkaW9cIikge1xuICAgICAgICAgIHMudHJhY2suZW5hYmxlZCA9IGVuYWJsZWQ7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIHNlbmREYXRhKGNsaWVudElkLCBkYXRhVHlwZSwgZGF0YSkge1xuICAgIGlmICghdGhpcy5wdWJsaXNoZXIpIHtcbiAgICAgIGNvbnNvbGUud2FybihcInNlbmREYXRhIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCkge1xuICAgICAgICBjYXNlIFwid2Vic29ja2V0XCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJkYXRhXCIsIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSksIHdob206IGNsaWVudElkIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci51bnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhIH0pKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aGlzLnVucmVsaWFibGVUcmFuc3BvcnQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBzZW5kRGF0YUd1YXJhbnRlZWQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwic2VuZERhdGFHdWFyYW50ZWVkIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pLCB3aG9tOiBjbGllbnRJZCB9KTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImRhdGFjaGFubmVsXCI6XG4gICAgICAgICAgdGhpcy5wdWJsaXNoZXIucmVsaWFibGVDaGFubmVsLnNlbmQoSlNPTi5zdHJpbmdpZnkoeyBjbGllbnRJZCwgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMucmVsaWFibGVUcmFuc3BvcnQoY2xpZW50SWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBicm9hZGNhc3REYXRhKGRhdGFUeXBlLCBkYXRhKSB7XG4gICAgaWYgKCF0aGlzLnB1Ymxpc2hlcikge1xuICAgICAgY29uc29sZS53YXJuKFwiYnJvYWRjYXN0RGF0YSBjYWxsZWQgd2l0aG91dCBhIHB1Ymxpc2hlclwiKTtcbiAgICB9IGVsc2Uge1xuICAgICAgc3dpdGNoICh0aGlzLnVucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci51bnJlbGlhYmxlQ2hhbm5lbC5zZW5kKEpTT04uc3RyaW5naWZ5KHsgZGF0YVR5cGUsIGRhdGEgfSkpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRoaXMudW5yZWxpYWJsZVRyYW5zcG9ydCh1bmRlZmluZWQsIGRhdGFUeXBlLCBkYXRhKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBicm9hZGNhc3REYXRhR3VhcmFudGVlZChkYXRhVHlwZSwgZGF0YSkge1xuICAgIGlmICghdGhpcy5wdWJsaXNoZXIpIHtcbiAgICAgIGNvbnNvbGUud2FybihcImJyb2FkY2FzdERhdGFHdWFyYW50ZWVkIGNhbGxlZCB3aXRob3V0IGEgcHVibGlzaGVyXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBzd2l0Y2ggKHRoaXMucmVsaWFibGVUcmFuc3BvcnQpIHtcbiAgICAgICAgY2FzZSBcIndlYnNvY2tldFwiOlxuICAgICAgICAgIHRoaXMucHVibGlzaGVyLmhhbmRsZS5zZW5kTWVzc2FnZSh7IGtpbmQ6IFwiZGF0YVwiLCBib2R5OiBKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pIH0pO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIFwiZGF0YWNoYW5uZWxcIjpcbiAgICAgICAgICB0aGlzLnB1Ymxpc2hlci5yZWxpYWJsZUNoYW5uZWwuc2VuZChKU09OLnN0cmluZ2lmeSh7IGRhdGFUeXBlLCBkYXRhIH0pKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICB0aGlzLnJlbGlhYmxlVHJhbnNwb3J0KHVuZGVmaW5lZCwgZGF0YVR5cGUsIGRhdGEpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGtpY2soY2xpZW50SWQsIHBlcm1zVG9rZW4pIHtcbiAgICByZXR1cm4gdGhpcy5wdWJsaXNoZXIuaGFuZGxlLnNlbmRNZXNzYWdlKHsga2luZDogXCJraWNrXCIsIHJvb21faWQ6IHRoaXMucm9vbSwgdXNlcl9pZDogY2xpZW50SWQsIHRva2VuOiBwZXJtc1Rva2VuIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImtpY2tlZFwiLCB7IGRldGFpbDogeyBjbGllbnRJZDogY2xpZW50SWQgfSB9KSk7XG4gICAgfSk7XG4gIH1cblxuICBibG9jayhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcImJsb2NrXCIsIHdob206IGNsaWVudElkIH0pLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5ibG9ja2VkQ2xpZW50cy5zZXQoY2xpZW50SWQsIHRydWUpO1xuICAgICAgZG9jdW1lbnQuYm9keS5kaXNwYXRjaEV2ZW50KG5ldyBDdXN0b21FdmVudChcImJsb2NrZWRcIiwgeyBkZXRhaWw6IHsgY2xpZW50SWQ6IGNsaWVudElkIH0gfSkpO1xuICAgIH0pO1xuICB9XG5cbiAgdW5ibG9jayhjbGllbnRJZCkge1xuICAgIHJldHVybiB0aGlzLnB1Ymxpc2hlci5oYW5kbGUuc2VuZE1lc3NhZ2UoeyBraW5kOiBcInVuYmxvY2tcIiwgd2hvbTogY2xpZW50SWQgfSkudGhlbigoKSA9PiB7XG4gICAgICB0aGlzLmJsb2NrZWRDbGllbnRzLmRlbGV0ZShjbGllbnRJZCk7XG4gICAgICBkb2N1bWVudC5ib2R5LmRpc3BhdGNoRXZlbnQobmV3IEN1c3RvbUV2ZW50KFwidW5ibG9ja2VkXCIsIHsgZGV0YWlsOiB7IGNsaWVudElkOiBjbGllbnRJZCB9IH0pKTtcbiAgICB9KTtcbiAgfVxufVxuXG5OQUYuYWRhcHRlcnMucmVnaXN0ZXIoXCJqYW51c1wiLCBKYW51c0FkYXB0ZXIpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEphbnVzQWRhcHRlcjtcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file diff --git a/dist/naf-janus-adapter.min.js b/dist/naf-janus-adapter.min.js index 47ede47..d97a9ab 100644 --- a/dist/naf-janus-adapter.min.js +++ b/dist/naf-janus-adapter.min.js @@ -1,2 +1,2 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var s=t[r]={i:r,l:!1,exports:{}};return e[r].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(r,s,function(t){return e[t]}.bind(null,s));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){function r(e){return function(){var t=e.apply(this,arguments);return new Promise((function(e,n){return function r(s,i){try{var a=t[s](i),o=a.value}catch(e){return void n(e)}if(!a.done)return Promise.resolve(o).then((function(e){r("next",e)}),(function(e){r("throw",e)}));e(o)}("next")}))}}var s=n(1);s.JanusSession.prototype.sendOriginal=s.JanusSession.prototype.send,s.JanusSession.prototype.send=function(e,t){return this.sendOriginal(e,t).catch(e=>{if(!(e.message&&e.message.indexOf("timed out")>-1))throw e;console.error("web socket timed out"),NAF.connection.adapter.reconnect()})};var i=n(2),a=console.log,o=(console.warn,console.error),c=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function u(e){var t=Promise.resolve();return function(){var n=Array.prototype.slice.call(arguments);t=t.then(t=>e.apply(this,n))}}function d(e){return new Promise((t,n)=>{if("open"===e.readyState)t();else{let r,s;const i=()=>{e.removeEventListener("open",r),e.removeEventListener("error",s)};r=()=>{i(),t()},s=()=>{i(),n()},e.addEventListener("open",r),e.addEventListener("error",s)}})}const l=""!==document.createElement("video").canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'),p={usedtx:1,stereo:0,"sprop-stereo":0},h={iceServers:[{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}]};class f{constructor(){this.room=null,this.clientId=null,this.joinToken=null,this.serverUrl=null,this.webRtcOptions={},this.peerConnectionConfig=null,this.ws=null,this.session=null,this.reliableTransport="datachannel",this.unreliableTransport="datachannel",this.initialReconnectionDelay=1e3*Math.random(),this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionTimeout=null,this.maxReconnectionAttempts=10,this.reconnectionAttempts=0,this.publisher=null,this.occupants={},this.leftOccupants=new Set,this.mediaStreams={},this.localMediaStream=null,this.pendingMediaRequests=new Map,this.blockedClients=new Map,this.frozenUpdates=new Map,this.timeOffsets=[],this.serverTimeRequests=0,this.avgTimeOffset=0,this.onWebsocketOpen=this.onWebsocketOpen.bind(this),this.onWebsocketClose=this.onWebsocketClose.bind(this),this.onWebsocketMessage=this.onWebsocketMessage.bind(this),this.onDataChannelMessage=this.onDataChannelMessage.bind(this),this.onData=this.onData.bind(this)}setServerUrl(e){this.serverUrl=e}setApp(e){}setRoom(e){this.room=e}setJoinToken(e){this.joinToken=e}setClientId(e){this.clientId=e}setWebRtcOptions(e){this.webRtcOptions=e}setPeerConnectionConfig(e){this.peerConnectionConfig=e}setServerConnectListeners(e,t){this.connectSuccess=e,this.connectFailure=t}setRoomOccupantListener(e){this.onOccupantsChanged=e}setDataChannelListeners(e,t,n){this.onOccupantConnected=e,this.onOccupantDisconnected=t,this.onOccupantMessage=n}setReconnectionListeners(e,t,n){this.onReconnecting=e,this.onReconnected=t,this.onReconnectionError=n}connect(){a("connecting to "+this.serverUrl);const e=new Promise((e,t)=>{this.ws=new WebSocket(this.serverUrl,"janus-protocol"),this.session=new s.JanusSession(this.ws.send.bind(this.ws),{timeoutMs:4e4}),this.ws.addEventListener("close",this.onWebsocketClose),this.ws.addEventListener("message",this.onWebsocketMessage),this.wsOnOpen=()=>{this.ws.removeEventListener("open",this.wsOnOpen),this.onWebsocketOpen().then(e).catch(t)},this.ws.addEventListener("open",this.wsOnOpen)});return Promise.all([e,this.updateTimeOffset()])}disconnect(){a("disconnecting"),clearTimeout(this.reconnectionTimeout),this.removeAllOccupants(),this.leftOccupants=new Set,this.publisher&&(this.publisher.conn.close(),this.publisher=null),this.session&&(this.session.dispose(),this.session=null),this.ws&&(this.ws.removeEventListener("open",this.wsOnOpen),this.ws.removeEventListener("close",this.onWebsocketClose),this.ws.removeEventListener("message",this.onWebsocketMessage),this.ws.close(),this.ws=null),this.delayedReconnectTimeout&&(clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=null)}isDisconnected(){return null===this.ws}onWebsocketOpen(){var e=this;return r((function*(){yield e.session.create(),e.publisher=yield e.createPublisher(),e.connectSuccess(e.clientId);const t=[];for(let n=0;nthis.reconnect(),this.reconnectionDelay))}reconnect(){this.disconnect(),this.connect().then(()=>{this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionAttempts=0,this.onReconnected&&this.onReconnected()}).catch(e=>{if(this.reconnectionDelay+=1e3,this.reconnectionAttempts++,this.reconnectionAttempts>this.maxReconnectionAttempts&&this.onReconnectionError)return this.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts."));console.warn("Error during reconnect, retrying."),console.warn(e),this.onReconnecting&&this.onReconnecting(this.reconnectionDelay),this.reconnectionTimeout=setTimeout(()=>this.reconnect(),this.reconnectionDelay)})}performDelayedReconnect(){this.delayedReconnectTimeout&&clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=setTimeout(()=>{this.delayedReconnectTimeout=null,this.reconnect()},1e4)}onWebsocketMessage(e){this.session.receive(JSON.parse(e.data))}addOccupant(e){var t=this;return r((function*(){t.occupants[e]&&t.removeOccupant(e),t.leftOccupants.delete(e);var n=yield t.createSubscriber(e);if(n)return t.occupants[e]=n,t.setMediaStream(e,n.mediaStream),t.onOccupantConnected(e),t.onOccupantsChanged(t.occupants),n}))()}removeAllOccupants(){for(const e of Object.getOwnPropertyNames(this.occupants))this.removeOccupant(e)}removeOccupant(e){if(this.leftOccupants.add(e),this.occupants[e]&&(this.occupants[e].conn.close(),delete this.occupants[e]),this.mediaStreams[e]&&delete this.mediaStreams[e],this.pendingMediaRequests.has(e)){const t="The user disconnected before the media stream was resolved.";this.pendingMediaRequests.get(e).audio.reject(t),this.pendingMediaRequests.get(e).video.reject(t),this.pendingMediaRequests.delete(e)}this.onOccupantDisconnected(e),this.onOccupantsChanged(this.occupants)}associate(e,t){e.addEventListener("icecandidate",e=>{t.sendTrickle(e.candidate||null).catch(e=>o("Error trickling ICE: %o",e))}),e.addEventListener("iceconnectionstatechange",t=>{"connected"===e.iceConnectionState&&console.log("ICE state changed to connected"),"disconnected"===e.iceConnectionState&&console.warn("ICE state changed to disconnected"),"failed"===e.iceConnectionState&&(console.warn("ICE failure detected. Reconnecting in 10s."),this.performDelayedReconnect())}),e.addEventListener("negotiationneeded",u(n=>{a("Sending new offer for handle: %o",t);var r=e.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag),s=r.then(t=>e.setLocalDescription(t)),i=r;return i=i.then(this.fixSafariIceUFrag).then(e=>t.sendJsep(e)).then(t=>e.setRemoteDescription(t.jsep)),Promise.all([s,i]).catch(e=>o("Error negotiating offer: %o",e))})),t.on("event",u(n=>{var r=n.jsep;if(r&&"offer"==r.type){a("Accepting new offer for handle: %o",t);var s=e.setRemoteDescription(this.configureSubscriberSdp(r)).then(t=>e.createAnswer()).then(this.fixSafariIceUFrag),i=s.then(t=>e.setLocalDescription(t)),c=s.then(e=>t.sendJsep(e));return Promise.all([i,c]).catch(e=>o("Error negotiating answer: %o",e))}return null}))}createPublisher(){var e=this;return r((function*(){var t=new s.JanusPluginHandle(e.session),n=new RTCPeerConnection(e.peerConnectionConfig||h);a("pub waiting for sfu"),yield t.attach("janus.plugin.sfu"),e.associate(n,t),a("pub waiting for data channels & webrtcup");var r=new Promise((function(e){return t.on("webrtcup",e)})),i=n.createDataChannel("reliable",{ordered:!0}),o=n.createDataChannel("unreliable",{ordered:!1,maxRetransmits:0});i.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-reliable")})),o.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-unreliable")})),yield r,yield d(i),yield d(o),e.localMediaStream&&e.localMediaStream.getTracks().forEach((function(t){n.addTrack(t,e.localMediaStream)})),t.on("event",(function(t){var n=t.plugindata.data;if("join"==n.event&&n.room_id==e.room){if(e.delayedReconnectTimeout)return;e.addOccupant(n.user_id)}else"leave"==n.event&&n.room_id==e.room?e.removeOccupant(n.user_id):"blocked"==n.event?document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:n.by}})):"unblocked"==n.event?document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:n.by}})):"data"===n.event&&e.onData(JSON.parse(n.body),"janus-event")})),a("pub waiting for join");var c=yield e.sendJoin(t,{notifications:!0,data:!0});if(!c.plugindata.data.success){const e=c.plugindata.data.error;throw console.error(e),n.close(),e}var u=c.plugindata.data.response.users[e.room]||[];return u.includes(e.clientId)&&(console.warn("Janus still has previous session for this client. Reconnecting in 10s."),e.performDelayedReconnect()),a("publisher ready"),{handle:t,initialOccupants:u,reliableChannel:i,unreliableChannel:o,conn:n}}))()}configurePublisherSdp(e){return e.sdp=e.sdp.replace(/a=fmtp:(109|111).*\r\n/g,(e,t)=>{const n=Object.assign(i.parseFmtp(e),p);return i.writeFmtp({payloadType:t,parameters:n})}),e}configureSubscriberSdp(e){return l||-1!==navigator.userAgent.indexOf("HeadlessChrome")&&(e.sdp=e.sdp.replace(/m=video[^]*m=/,"m=")),-1===navigator.userAgent.indexOf("Android")?e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"):e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"),e}fixSafariIceUFrag(e){return r((function*(){return e.sdp=e.sdp.replace(/[^\r]\na=ice-ufrag/g,"\r\na=ice-ufrag"),e}))()}createSubscriber(e,t=5){var n=this;return r((function*(){if(n.leftOccupants.has(e))return console.warn(e+": cancelled occupant connection, occupant left before subscription negotation."),null;var r=new s.JanusPluginHandle(n.session),i=new RTCPeerConnection(n.peerConnectionConfig||h);if(a(e+": sub waiting for sfu"),yield r.attach("janus.plugin.sfu"),n.associate(i,r),a(e+": sub waiting for join"),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after attach"),null;let o=!1;const u=new Promise((function(t){const s=setInterval((function(){n.leftOccupants.has(e)&&(clearInterval(s),t())}),1e3),i=setTimeout((function(){clearInterval(s),o=!0,t()}),15e3);r.on("webrtcup",(function(){clearTimeout(i),clearInterval(s),t()}))}));if(yield n.sendJoin(r,{media:e}),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after join"),null;if(a(e+": sub waiting for webrtcup"),yield u,n.leftOccupants.has(e))return i.close(),console.warn(e+": cancel occupant connection, occupant left during or after webrtcup"),null;if(o)return i.close(),t>0?(console.warn(e+": webrtc up timed out, retrying"),n.createSubscriber(e,t-1)):(console.warn(e+": webrtc up timed out"),null);c&&!n._iOSHackDelayedInitialPeer&&(yield new Promise((function(e){return setTimeout(e,3e3)})),n._iOSHackDelayedInitialPeer=!0);var d=new MediaStream;return i.getReceivers().forEach((function(e){e.track&&d.addTrack(e.track)})),0===d.getTracks().length&&(d=null),a(e+": subscriber ready"),{handle:r,mediaStream:d,conn:i}}))()}sendJoin(e,t){return e.sendMessage({kind:"join",room_id:this.room,user_id:this.clientId,subscribe:t,token:this.joinToken})}toggleFreeze(){this.frozen?this.unfreeze():this.freeze()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1,this.flushPendingUpdates()}dataForUpdateMultiMessage(e,t){for(let n=0,r=t.data.d.length;nn.owner)return;if("r"===r){i&&i.isFirstSync?this.frozenUpdates.delete(s):this.frozenUpdates.set(s,e)}else i.components&&n.components&&Object.assign(i.components,n.components)}else this.frozenUpdates.set(s,e)}onDataChannelMessage(e,t){this.onData(JSON.parse(e.data),t)}onData(e,t){a.enabled&&a("DC in: "+e),e.dataType&&(e.source=t,this.frozen?this.storeMessage(e):this.onOccupantMessage(null,e.dataType,e.data,e.source))}shouldStartConnectionTo(e){return!0}startStreamConnection(e){}closeStreamConnection(e){}getConnectStatus(e){return this.occupants[e]?NAF.adapters.IS_CONNECTED:NAF.adapters.NOT_CONNECTED}updateTimeOffset(){var e=this;return r((function*(){if(e.isDisconnected())return;const t=Date.now(),n=yield fetch(document.location.href,{method:"HEAD",cache:"no-cache"}),r=new Date(n.headers.get("Date")).getTime()+500,s=Date.now(),i=r+(s-t)/2-s;e.serverTimeRequests++,e.serverTimeRequests<=10?e.timeOffsets.push(i):e.timeOffsets[e.serverTimeRequests%10]=i,e.avgTimeOffset=e.timeOffsets.reduce((function(e,t){return e+t}),0)/e.timeOffsets.length,e.serverTimeRequests>10?(a(`new server time offset: ${e.avgTimeOffset}ms`),setTimeout((function(){return e.updateTimeOffset()}),3e5)):e.updateTimeOffset()}))()}getServerTime(){return Date.now()+this.avgTimeOffset}getMediaStream(e,t="audio"){if(this.mediaStreams[e])return a(`Already had ${t} for ${e}`),Promise.resolve(this.mediaStreams[e][t]);if(a(`Waiting on ${t} for ${e}`),!this.pendingMediaRequests.has(e)){this.pendingMediaRequests.set(e,{});const t=new Promise((t,n)=>{this.pendingMediaRequests.get(e).audio={resolve:t,reject:n}}),n=new Promise((t,n)=>{this.pendingMediaRequests.get(e).video={resolve:t,reject:n}});this.pendingMediaRequests.get(e).audio.promise=t,this.pendingMediaRequests.get(e).video.promise=n,t.catch(t=>console.warn(e+" getMediaStream Audio Error",t)),n.catch(t=>console.warn(e+" getMediaStream Video Error",t))}return this.pendingMediaRequests.get(e)[t].promise}setMediaStream(e,t){const n=new MediaStream;try{t.getAudioTracks().forEach(e=>n.addTrack(e))}catch(t){console.warn(e+" setMediaStream Audio Error",t)}const r=new MediaStream;try{t.getVideoTracks().forEach(e=>r.addTrack(e))}catch(t){console.warn(e+" setMediaStream Video Error",t)}this.mediaStreams[e]={audio:n,video:r},this.pendingMediaRequests.has(e)&&(this.pendingMediaRequests.get(e).audio.resolve(n),this.pendingMediaRequests.get(e).video.resolve(r))}setLocalMediaStream(e){var t=this;return r((function*(){if(t.publisher&&t.publisher.conn){const n=t.publisher.conn.getSenders(),r=[],s=e.getTracks();for(let i=0;i-1&&(a.enabled=!1,setTimeout((function(){return a.enabled=!0}),1e3))):(e.removeTrack(o.track),e.addTrack(a)),r.push(o)):r.push(t.publisher.conn.addTrack(a,e))}n.forEach((function(e){r.includes(e)||(e.track.enabled=!1)}))}t.localMediaStream=e,t.setMediaStream(t.clientId,e)}))()}enableMicrophone(e){this.publisher&&this.publisher.conn&&this.publisher.conn.getSenders().forEach(t=>{"audio"==t.track.kind&&(t.track.enabled=e)})}sendData(e,t,n){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.unreliableTransport(e,t,n)}else console.warn("sendData called without a publisher")}sendDataGuaranteed(e,t,n){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.reliableTransport(e,t,n)}else console.warn("sendDataGuaranteed called without a publisher")}broadcastData(e,t){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.unreliableTransport(void 0,e,t)}else console.warn("broadcastData called without a publisher")}broadcastDataGuaranteed(e,t){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.reliableTransport(void 0,e,t)}else console.warn("broadcastDataGuaranteed called without a publisher")}kick(e,t){return this.publisher.handle.sendMessage({kind:"kick",room_id:this.room,user_id:e,token:t}).then(()=>{document.body.dispatchEvent(new CustomEvent("kicked",{detail:{clientId:e}}))})}block(e){return this.publisher.handle.sendMessage({kind:"block",whom:e}).then(()=>{this.blockedClients.set(e,!0),document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:e}}))})}unblock(e){return this.publisher.handle.sendMessage({kind:"unblock",whom:e}).then(()=>{this.blockedClients.delete(e),document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:e}}))})}}NAF.adapters.register("janus",f),e.exports=f},function(e,t){function n(e){this.session=e,this.id=void 0}function r(e,t){this.output=e,this.id=void 0,this.nextTxId=0,this.txns={},this.eventHandlers={},this.options=Object.assign({verbose:!1,timeoutMs:1e4,keepaliveMs:3e4},t)}n.prototype.attach=function(e){var t={plugin:e,"force-bundle":!0,"force-rtcp-mux":!0};return this.session.send("attach",t).then(e=>(this.id=e.data.id,e))},n.prototype.detach=function(){return this.send("detach")},n.prototype.on=function(e,t){return this.session.on(e,e=>{e.sender==this.id&&t(e)})},n.prototype.send=function(e,t){return this.session.send(e,Object.assign({handle_id:this.id},t))},n.prototype.sendMessage=function(e){return this.send("message",{body:e})},n.prototype.sendJsep=function(e){return this.send("message",{body:{},jsep:e})},n.prototype.sendTrickle=function(e){return this.send("trickle",{candidate:e})},r.prototype.create=function(){return this.send("create").then(e=>(this.id=e.data.id,e))},r.prototype.destroy=function(){return this.send("destroy").then(e=>(this.dispose(),e))},r.prototype.dispose=function(){for(var e in this._killKeepalive(),this.eventHandlers={},this.txns)if(this.txns.hasOwnProperty(e)){var t=this.txns[e];clearTimeout(t.timeout),t.reject(new Error("Janus session was disposed.")),delete this.txns[e]}},r.prototype.isError=function(e){return"error"===e.janus},r.prototype.on=function(e,t){var n=this.eventHandlers[e];null==n&&(n=this.eventHandlers[e]=[]),n.push(t)},r.prototype.receive=function(e){this.options.verbose&&this._logIncoming(e),e.session_id!=this.id&&console.warn("Incorrect session ID received in Janus signalling message: was "+e.session_id+", expected "+this.id+".");var t=e.janus,n=this.eventHandlers[t];if(null!=n)for(var r=0;r{var s=null;this.options.timeoutMs&&(s=setTimeout(()=>{delete this.txns[t.transaction],r(new Error("Signalling transaction with txid "+t.transaction+" timed out."))},this.options.timeoutMs)),this.txns[t.transaction]={resolve:n,reject:r,timeout:s,type:e},this._transmit(e,t)})},r.prototype._transmit=function(e,t){t=Object.assign({janus:e},t),null!=this.id&&(t=Object.assign({session_id:this.id},t)),this.options.verbose&&this._logOutgoing(t),this.output(JSON.stringify(t)),this._resetKeepalive()},r.prototype._logOutgoing=function(e){var t=e.janus;"message"===t&&e.jsep&&(t=e.jsep.type);var n="> Outgoing Janus "+(t||"signal")+" (#"+e.transaction+"): ";console.debug("%c"+n,"color: #040",e)},r.prototype._logIncoming=function(e){var t=e.janus,n=e.transaction?"< Incoming Janus "+(t||"signal")+" (#"+e.transaction+"): ":"< Incoming Janus "+(t||"signal")+": ";console.debug("%c"+n,"color: #004",e)},r.prototype._sendKeepalive=function(){return this.send("keepalive")},r.prototype._killKeepalive=function(){clearTimeout(this.keepaliveTimeout)},r.prototype._resetKeepalive=function(){this._killKeepalive(),this.options.keepaliveMs&&(this.keepaliveTimeout=setTimeout(()=>{this._sendKeepalive().catch(e=>console.error("Error received from keepalive: ",e))},this.options.keepaliveMs))},e.exports={JanusPluginHandle:n,JanusSession:r}},function(e,t,n){"use strict";var r={generateIdentifier:function(){return Math.random().toString(36).substr(2,10)}};r.localCName=r.generateIdentifier(),r.splitLines=function(e){return e.trim().split("\n").map((function(e){return e.trim()}))},r.splitSections=function(e){return e.split("\nm=").map((function(e,t){return(t>0?"m="+e:e).trim()+"\r\n"}))},r.getDescription=function(e){var t=r.splitSections(e);return t&&t[0]},r.getMediaSections=function(e){var t=r.splitSections(e);return t.shift(),t},r.matchPrefix=function(e,t){return r.splitLines(e).filter((function(e){return 0===e.indexOf(t)}))},r.parseCandidate=function(e){for(var t,n={foundation:(t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" "))[0],component:parseInt(t[1],10),protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]},r=8;r0?t[0].split("/")[1]:"sendrecv",uri:t[1]}},r.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+"\r\n"},r.parseFmtp=function(e){for(var t,n={},r=e.substr(e.indexOf(" ")+1).split(";"),s=0;s-1?(n.attribute=e.substr(t+1,r-t-1),n.value=e.substr(r+1)):n.attribute=e.substr(t+1),n},r.parseSsrcGroup=function(e){var t=e.substr(13).split(" ");return{semantics:t.shift(),ssrcs:t.map((function(e){return parseInt(e,10)}))}},r.getMid=function(e){var t=r.matchPrefix(e,"a=mid:")[0];if(t)return t.substr(6)},r.parseFingerprint=function(e){var t=e.substr(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1]}},r.getDtlsParameters=function(e,t){return{role:"auto",fingerprints:r.matchPrefix(e+t,"a=fingerprint:").map(r.parseFingerprint)}},r.writeDtlsParameters=function(e,t){var n="a=setup:"+t+"\r\n";return e.fingerprints.forEach((function(e){n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"})),n},r.parseCryptoLine=function(e){var t=e.substr(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},r.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?r.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},r.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;var t=e.substr(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},r.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},r.getCryptoParameters=function(e,t){return r.matchPrefix(e+t,"a=crypto:").map(r.parseCryptoLine)},r.getIceParameters=function(e,t){var n=r.matchPrefix(e+t,"a=ice-ufrag:")[0],s=r.matchPrefix(e+t,"a=ice-pwd:")[0];return n&&s?{usernameFragment:n.substr(12),password:s.substr(10)}:null},r.writeIceParameters=function(e){return"a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n"},r.parseRtpParameters=function(e){for(var t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},n=r.splitLines(e)[0].split(" "),s=3;s0?"9":"0",n+=" UDP/TLS/RTP/SAVPF ",n+=t.codecs.map((function(e){return void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType})).join(" ")+"\r\n",n+="c=IN IP4 0.0.0.0\r\n",n+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach((function(e){n+=r.writeRtpMap(e),n+=r.writeFmtp(e),n+=r.writeRtcpFb(e)}));var s=0;return t.codecs.forEach((function(e){e.maxptime>s&&(s=e.maxptime)})),s>0&&(n+="a=maxptime:"+s+"\r\n"),n+="a=rtcp-mux\r\n",t.headerExtensions&&t.headerExtensions.forEach((function(e){n+=r.writeExtmap(e)})),n},r.parseRtpEncodingParameters=function(e){var t,n=[],s=r.parseRtpParameters(e),i=-1!==s.fecMechanisms.indexOf("RED"),a=-1!==s.fecMechanisms.indexOf("ULPFEC"),o=r.matchPrefix(e,"a=ssrc:").map((function(e){return r.parseSsrcMedia(e)})).filter((function(e){return"cname"===e.attribute})),c=o.length>0&&o[0].ssrc,u=r.matchPrefix(e,"a=ssrc-group:FID").map((function(e){return e.substr(17).split(" ").map((function(e){return parseInt(e,10)}))}));u.length>0&&u[0].length>1&&u[0][0]===c&&(t=u[0][1]),s.codecs.forEach((function(e){if("RTX"===e.name.toUpperCase()&&e.parameters.apt){var r={ssrc:c,codecPayloadType:parseInt(e.parameters.apt,10)};c&&t&&(r.rtx={ssrc:t}),n.push(r),i&&((r=JSON.parse(JSON.stringify(r))).fec={ssrc:c,mechanism:a?"red+ulpfec":"red"},n.push(r))}})),0===n.length&&c&&n.push({ssrc:c});var d=r.matchPrefix(e,"b=");return d.length&&(d=0===d[0].indexOf("b=TIAS:")?parseInt(d[0].substr(7),10):0===d[0].indexOf("b=AS:")?1e3*parseInt(d[0].substr(5),10)*.95-16e3:void 0,n.forEach((function(e){e.maxBitrate=d}))),n},r.parseRtcpParameters=function(e){var t={},n=r.matchPrefix(e,"a=ssrc:").map((function(e){return r.parseSsrcMedia(e)})).filter((function(e){return"cname"===e.attribute}))[0];n&&(t.cname=n.value,t.ssrc=n.ssrc);var s=r.matchPrefix(e,"a=rtcp-rsize");t.reducedSize=s.length>0,t.compound=0===s.length;var i=r.matchPrefix(e,"a=rtcp-mux");return t.mux=i.length>0,t},r.parseMsid=function(e){var t,n=r.matchPrefix(e,"a=msid:");if(1===n.length)return{stream:(t=n[0].substr(7).split(" "))[0],track:t[1]};var s=r.matchPrefix(e,"a=ssrc:").map((function(e){return r.parseSsrcMedia(e)})).filter((function(e){return"msid"===e.attribute}));return s.length>0?{stream:(t=s[0].value.split(" "))[0],track:t[1]}:void 0},r.parseSctpDescription=function(e){var t,n=r.parseMLine(e),s=r.matchPrefix(e,"a=max-message-size:");s.length>0&&(t=parseInt(s[0].substr(19),10)),isNaN(t)&&(t=65536);var i=r.matchPrefix(e,"a=sctp-port:");if(i.length>0)return{port:parseInt(i[0].substr(12),10),protocol:n.fmt,maxMessageSize:t};if(r.matchPrefix(e,"a=sctpmap:").length>0){var a=r.matchPrefix(e,"a=sctpmap:")[0].substr(10).split(" ");return{port:parseInt(a[0],10),protocol:a[1],maxMessageSize:t}}},r.writeSctpDescription=function(e,t){var n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},r.generateSessionId=function(){return Math.random().toString().substr(2,21)},r.writeSessionBoilerplate=function(e,t,n){var s=void 0!==t?t:2;return"v=0\r\no="+(n||"thisisadapterortc")+" "+(e||r.generateSessionId())+" "+s+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},r.writeMediaSection=function(e,t,n,s){var i=r.writeRtpDescription(e.kind,t);if(i+=r.writeIceParameters(e.iceGatherer.getLocalParameters()),i+=r.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===n?"actpass":"active"),i+="a=mid:"+e.mid+"\r\n",e.direction?i+="a="+e.direction+"\r\n":e.rtpSender&&e.rtpReceiver?i+="a=sendrecv\r\n":e.rtpSender?i+="a=sendonly\r\n":e.rtpReceiver?i+="a=recvonly\r\n":i+="a=inactive\r\n",e.rtpSender){var a="msid:"+s.id+" "+e.rtpSender.track.id+"\r\n";i+="a="+a,i+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+a,e.sendEncodingParameters[0].rtx&&(i+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" "+a,i+="a=ssrc-group:FID "+e.sendEncodingParameters[0].ssrc+" "+e.sendEncodingParameters[0].rtx.ssrc+"\r\n")}return i+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+r.localCName+"\r\n",e.rtpSender&&e.sendEncodingParameters[0].rtx&&(i+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" cname:"+r.localCName+"\r\n"),i},r.getDirection=function(e,t){for(var n=r.splitLines(e),s=0;s{if(!(e.message&&e.message.indexOf("timed out")>-1))throw e;console.error("web socket timed out"),NAF.connection.adapter.reconnect()})};var i=n(2),a=console.log,o=(console.warn,console.error),c=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function l(e){var t=Promise.resolve();return function(){var n=Array.prototype.slice.call(arguments);t=t.then(t=>e.apply(this,n))}}function u(e){return new Promise((t,n)=>{if("open"===e.readyState)t();else{let s,r;const i=()=>{e.removeEventListener("open",s),e.removeEventListener("error",r)};s=()=>{i(),t()},r=()=>{i(),n()},e.addEventListener("open",s),e.addEventListener("error",r)}})}const d=""!==document.createElement("video").canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'),p={usedtx:1,stereo:0,"sprop-stereo":0},h={iceServers:[{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}]};class f{constructor(){this.room=null,this.clientId=null,this.joinToken=null,this.serverUrl=null,this.webRtcOptions={},this.peerConnectionConfig=null,this.ws=null,this.session=null,this.reliableTransport="datachannel",this.unreliableTransport="datachannel",this.initialReconnectionDelay=1e3*Math.random(),this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionTimeout=null,this.maxReconnectionAttempts=10,this.reconnectionAttempts=0,this.publisher=null,this.occupants={},this.leftOccupants=new Set,this.mediaStreams={},this.localMediaStream=null,this.pendingMediaRequests=new Map,this.blockedClients=new Map,this.frozenUpdates=new Map,this.timeOffsets=[],this.serverTimeRequests=0,this.avgTimeOffset=0,this.onWebsocketOpen=this.onWebsocketOpen.bind(this),this.onWebsocketClose=this.onWebsocketClose.bind(this),this.onWebsocketMessage=this.onWebsocketMessage.bind(this),this.onDataChannelMessage=this.onDataChannelMessage.bind(this),this.onData=this.onData.bind(this)}setServerUrl(e){this.serverUrl=e}setApp(e){}setRoom(e){this.room=e}setJoinToken(e){this.joinToken=e}setClientId(e){this.clientId=e}setWebRtcOptions(e){this.webRtcOptions=e}setPeerConnectionConfig(e){this.peerConnectionConfig=e}setServerConnectListeners(e,t){this.connectSuccess=e,this.connectFailure=t}setRoomOccupantListener(e){this.onOccupantsChanged=e}setDataChannelListeners(e,t,n){this.onOccupantConnected=e,this.onOccupantDisconnected=t,this.onOccupantMessage=n}setReconnectionListeners(e,t,n){this.onReconnecting=e,this.onReconnected=t,this.onReconnectionError=n}connect(){a("connecting to "+this.serverUrl);const e=new Promise((e,t)=>{this.ws=new WebSocket(this.serverUrl,"janus-protocol"),this.session=new r.JanusSession(this.ws.send.bind(this.ws),{timeoutMs:4e4}),this.ws.addEventListener("close",this.onWebsocketClose),this.ws.addEventListener("message",this.onWebsocketMessage),this.wsOnOpen=()=>{this.ws.removeEventListener("open",this.wsOnOpen),this.onWebsocketOpen().then(e).catch(t)},this.ws.addEventListener("open",this.wsOnOpen)});return Promise.all([e,this.updateTimeOffset()])}disconnect(){a("disconnecting"),clearTimeout(this.reconnectionTimeout),this.removeAllOccupants(),this.leftOccupants=new Set,this.publisher&&(this.publisher.conn.close(),this.publisher=null),this.session&&(this.session.dispose(),this.session=null),this.ws&&(this.ws.removeEventListener("open",this.wsOnOpen),this.ws.removeEventListener("close",this.onWebsocketClose),this.ws.removeEventListener("message",this.onWebsocketMessage),this.ws.close(),this.ws=null),this.delayedReconnectTimeout&&(clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=null)}isDisconnected(){return null===this.ws}onWebsocketOpen(){var e=this;return s((function*(){yield e.session.create(),e.publisher=yield e.createPublisher(),e.connectSuccess(e.clientId);const t=[];for(let n=0;nthis.reconnect(),this.reconnectionDelay))}reconnect(){this.disconnect(),this.connect().then(()=>{this.reconnectionDelay=this.initialReconnectionDelay,this.reconnectionAttempts=0,this.onReconnected&&this.onReconnected()}).catch(e=>{if(this.reconnectionDelay+=1e3,this.reconnectionAttempts++,this.reconnectionAttempts>this.maxReconnectionAttempts&&this.onReconnectionError)return this.onReconnectionError(new Error("Connection could not be reestablished, exceeded maximum number of reconnection attempts."));console.warn("Error during reconnect, retrying."),console.warn(e),this.onReconnecting&&this.onReconnecting(this.reconnectionDelay),this.reconnectionTimeout=setTimeout(()=>this.reconnect(),this.reconnectionDelay)})}performDelayedReconnect(){this.delayedReconnectTimeout&&clearTimeout(this.delayedReconnectTimeout),this.delayedReconnectTimeout=setTimeout(()=>{this.delayedReconnectTimeout=null,this.reconnect()},1e4)}onWebsocketMessage(e){this.session.receive(JSON.parse(e.data))}addOccupant(e){var t=this;return s((function*(){t.occupants[e]&&t.removeOccupant(e),t.leftOccupants.delete(e);var n=yield t.createSubscriber(e);if(n)return t.occupants[e]=n,t.setMediaStream(e,n.mediaStream),t.onOccupantConnected(e),t.onOccupantsChanged(t.occupants),n}))()}removeAllOccupants(){for(const e of Object.getOwnPropertyNames(this.occupants))this.removeOccupant(e)}removeOccupant(e){if(this.leftOccupants.add(e),this.occupants[e]&&(this.occupants[e].conn.close(),delete this.occupants[e]),this.mediaStreams[e]&&delete this.mediaStreams[e],this.pendingMediaRequests.has(e)){const t="The user disconnected before the media stream was resolved.";this.pendingMediaRequests.get(e).audio.reject(t),this.pendingMediaRequests.get(e).video.reject(t),this.pendingMediaRequests.delete(e)}this.onOccupantDisconnected(e),this.onOccupantsChanged(this.occupants)}associate(e,t){e.addEventListener("icecandidate",e=>{t.sendTrickle(e.candidate||null).catch(e=>o("Error trickling ICE: %o",e))}),e.addEventListener("iceconnectionstatechange",t=>{"connected"===e.iceConnectionState&&console.log("ICE state changed to connected"),"disconnected"===e.iceConnectionState&&console.warn("ICE state changed to disconnected"),"failed"===e.iceConnectionState&&(console.warn("ICE failure detected. Reconnecting in 10s."),this.performDelayedReconnect())}),e.addEventListener("negotiationneeded",l(n=>{a("Sending new offer for handle: %o",t);var s=e.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag),r=s.then(t=>e.setLocalDescription(t)),i=s;return i=i.then(this.fixSafariIceUFrag).then(e=>t.sendJsep(e)).then(t=>e.setRemoteDescription(t.jsep)),Promise.all([r,i]).catch(e=>o("Error negotiating offer: %o",e))})),t.on("event",l(n=>{var s=n.jsep;if(s&&"offer"==s.type){a("Accepting new offer for handle: %o",t);var r=e.setRemoteDescription(this.configureSubscriberSdp(s)).then(t=>e.createAnswer()).then(this.fixSafariIceUFrag),i=r.then(t=>e.setLocalDescription(t)),c=r.then(e=>t.sendJsep(e));return Promise.all([i,c]).catch(e=>o("Error negotiating answer: %o",e))}return null}))}createPublisher(){var e=this;return s((function*(){var t=new r.JanusPluginHandle(e.session),n=new RTCPeerConnection(e.peerConnectionConfig||h);a("pub waiting for sfu"),yield t.attach("janus.plugin.sfu"),e.associate(n,t),a("pub waiting for data channels & webrtcup");var s=new Promise((function(e){return t.on("webrtcup",e)})),i=n.createDataChannel("reliable",{ordered:!0}),o=n.createDataChannel("unreliable",{ordered:!1,maxRetransmits:0});i.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-reliable")})),o.addEventListener("message",(function(t){return e.onDataChannelMessage(t,"janus-unreliable")})),yield s,yield u(i),yield u(o),e.localMediaStream&&e.localMediaStream.getTracks().forEach((function(t){n.addTrack(t,e.localMediaStream)})),t.on("event",(function(t){var n=t.plugindata.data;if("join"==n.event&&n.room_id==e.room){if(e.delayedReconnectTimeout)return;e.addOccupant(n.user_id)}else"leave"==n.event&&n.room_id==e.room?e.removeOccupant(n.user_id):"blocked"==n.event?document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:n.by}})):"unblocked"==n.event?document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:n.by}})):"data"===n.event&&e.onData(JSON.parse(n.body),"janus-event")})),a("pub waiting for join");var c=yield e.sendJoin(t,{notifications:!0,data:!0});if(!c.plugindata.data.success){const e=c.plugindata.data.error;throw console.error(e),n.close(),e}var l=c.plugindata.data.response.users[e.room]||[];return l.includes(e.clientId)&&(console.warn("Janus still has previous session for this client. Reconnecting in 10s."),e.performDelayedReconnect()),a("publisher ready"),{handle:t,initialOccupants:l,reliableChannel:i,unreliableChannel:o,conn:n}}))()}configurePublisherSdp(e){return e.sdp=e.sdp.replace(/a=fmtp:(109|111).*\r\n/g,(e,t)=>{const n=Object.assign(i.parseFmtp(e),p);return i.writeFmtp({payloadType:t,parameters:n})}),e}configureSubscriberSdp(e){return d||-1!==navigator.userAgent.indexOf("HeadlessChrome")&&(e.sdp=e.sdp.replace(/m=video[^]*m=/,"m=")),-1===navigator.userAgent.indexOf("Android")?e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\n"):e.sdp=e.sdp.replace("a=rtcp-fb:107 goog-remb\r\n","a=rtcp-fb:107 goog-remb\r\na=rtcp-fb:107 transport-cc\r\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\r\n"),e}fixSafariIceUFrag(e){return s((function*(){return e.sdp=e.sdp.replace(/[^\r]\na=ice-ufrag/g,"\r\na=ice-ufrag"),e}))()}createSubscriber(e,t=5){var n=this;return s((function*(){if(n.leftOccupants.has(e))return console.warn(e+": cancelled occupant connection, occupant left before subscription negotation."),null;var s=new r.JanusPluginHandle(n.session),i=new RTCPeerConnection(n.peerConnectionConfig||h);if(a(e+": sub waiting for sfu"),yield s.attach("janus.plugin.sfu"),n.associate(i,s),a(e+": sub waiting for join"),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after attach"),null;let o=!1;const l=new Promise((function(t){const r=setInterval((function(){n.leftOccupants.has(e)&&(clearInterval(r),t())}),1e3),i=setTimeout((function(){clearInterval(r),o=!0,t()}),15e3);s.on("webrtcup",(function(){clearTimeout(i),clearInterval(r),t()}))}));if(yield n.sendJoin(s,{media:e}),n.leftOccupants.has(e))return i.close(),console.warn(e+": cancelled occupant connection, occupant left after join"),null;if(a(e+": sub waiting for webrtcup"),yield l,n.leftOccupants.has(e))return i.close(),console.warn(e+": cancel occupant connection, occupant left during or after webrtcup"),null;if(o)return i.close(),t>0?(console.warn(e+": webrtc up timed out, retrying"),n.createSubscriber(e,t-1)):(console.warn(e+": webrtc up timed out"),null);c&&!n._iOSHackDelayedInitialPeer&&(yield new Promise((function(e){return setTimeout(e,3e3)})),n._iOSHackDelayedInitialPeer=!0);var u=new MediaStream;return i.getReceivers().forEach((function(e){e.track&&u.addTrack(e.track)})),0===u.getTracks().length&&(u=null),a(e+": subscriber ready"),{handle:s,mediaStream:u,conn:i}}))()}sendJoin(e,t){return e.sendMessage({kind:"join",room_id:this.room,user_id:this.clientId,subscribe:t,token:this.joinToken})}toggleFreeze(){this.frozen?this.unfreeze():this.freeze()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1,this.flushPendingUpdates()}dataForUpdateMultiMessage(e,t){for(let n=0,s=t.data.d.length;nn.owner)return;if("r"===s){i&&i.isFirstSync?this.frozenUpdates.delete(r):this.frozenUpdates.set(r,e)}else i.components&&n.components&&Object.assign(i.components,n.components)}else this.frozenUpdates.set(r,e)}onDataChannelMessage(e,t){this.onData(JSON.parse(e.data),t)}onData(e,t){a.enabled&&a("DC in: "+e),e.dataType&&(e.source=t,this.frozen?this.storeMessage(e):this.onOccupantMessage(null,e.dataType,e.data,e.source))}shouldStartConnectionTo(e){return!0}startStreamConnection(e){}closeStreamConnection(e){}getConnectStatus(e){return this.occupants[e]?NAF.adapters.IS_CONNECTED:NAF.adapters.NOT_CONNECTED}updateTimeOffset(){var e=this;return s((function*(){if(e.isDisconnected())return;const t=Date.now(),n=yield fetch(document.location.href,{method:"HEAD",cache:"no-cache"}),s=new Date(n.headers.get("Date")).getTime()+500,r=Date.now(),i=s+(r-t)/2-r;e.serverTimeRequests++,e.serverTimeRequests<=10?e.timeOffsets.push(i):e.timeOffsets[e.serverTimeRequests%10]=i,e.avgTimeOffset=e.timeOffsets.reduce((function(e,t){return e+t}),0)/e.timeOffsets.length,e.serverTimeRequests>10?(a(`new server time offset: ${e.avgTimeOffset}ms`),setTimeout((function(){return e.updateTimeOffset()}),3e5)):e.updateTimeOffset()}))()}getServerTime(){return Date.now()+this.avgTimeOffset}getMediaStream(e,t="audio"){if(this.mediaStreams[e])return a(`Already had ${t} for ${e}`),Promise.resolve(this.mediaStreams[e][t]);if(a(`Waiting on ${t} for ${e}`),!this.pendingMediaRequests.has(e)){this.pendingMediaRequests.set(e,{});const t=new Promise((t,n)=>{this.pendingMediaRequests.get(e).audio={resolve:t,reject:n}}),n=new Promise((t,n)=>{this.pendingMediaRequests.get(e).video={resolve:t,reject:n}});this.pendingMediaRequests.get(e).audio.promise=t,this.pendingMediaRequests.get(e).video.promise=n,t.catch(t=>console.warn(e+" getMediaStream Audio Error",t)),n.catch(t=>console.warn(e+" getMediaStream Video Error",t))}return this.pendingMediaRequests.get(e)[t].promise}setMediaStream(e,t){const n=new MediaStream;try{t.getAudioTracks().forEach(e=>n.addTrack(e))}catch(t){console.warn(e+" setMediaStream Audio Error",t)}const s=new MediaStream;try{t.getVideoTracks().forEach(e=>s.addTrack(e))}catch(t){console.warn(e+" setMediaStream Video Error",t)}this.mediaStreams[e]={audio:n,video:s},this.pendingMediaRequests.has(e)&&(this.pendingMediaRequests.get(e).audio.resolve(n),this.pendingMediaRequests.get(e).video.resolve(s))}setLocalMediaStream(e){var t=this;return s((function*(){if(t.publisher&&t.publisher.conn){const n=t.publisher.conn.getSenders(),s=[],r=e.getTracks();for(let i=0;i-1&&(a.enabled=!1,setTimeout((function(){return a.enabled=!0}),1e3))):(e.removeTrack(o.track),e.addTrack(a)),s.push(o)):s.push(t.publisher.conn.addTrack(a,e))}n.forEach((function(e){s.includes(e)||(e.track.enabled=!1)}))}t.localMediaStream=e,t.setMediaStream(t.clientId,e)}))()}enableMicrophone(e){this.publisher&&this.publisher.conn&&this.publisher.conn.getSenders().forEach(t=>{"audio"==t.track.kind&&(t.track.enabled=e)})}sendData(e,t,n){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.unreliableTransport(e,t,n)}else console.warn("sendData called without a publisher")}sendDataGuaranteed(e,t,n){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:t,data:n}),whom:e});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({clientId:e,dataType:t,data:n}));break;default:this.reliableTransport(e,t,n)}else console.warn("sendDataGuaranteed called without a publisher")}broadcastData(e,t){if(this.publisher)switch(this.unreliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.unreliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.unreliableTransport(void 0,e,t)}else console.warn("broadcastData called without a publisher")}broadcastDataGuaranteed(e,t){if(this.publisher)switch(this.reliableTransport){case"websocket":this.publisher.handle.sendMessage({kind:"data",body:JSON.stringify({dataType:e,data:t})});break;case"datachannel":this.publisher.reliableChannel.send(JSON.stringify({dataType:e,data:t}));break;default:this.reliableTransport(void 0,e,t)}else console.warn("broadcastDataGuaranteed called without a publisher")}kick(e,t){return this.publisher.handle.sendMessage({kind:"kick",room_id:this.room,user_id:e,token:t}).then(()=>{document.body.dispatchEvent(new CustomEvent("kicked",{detail:{clientId:e}}))})}block(e){return this.publisher.handle.sendMessage({kind:"block",whom:e}).then(()=>{this.blockedClients.set(e,!0),document.body.dispatchEvent(new CustomEvent("blocked",{detail:{clientId:e}}))})}unblock(e){return this.publisher.handle.sendMessage({kind:"unblock",whom:e}).then(()=>{this.blockedClients.delete(e),document.body.dispatchEvent(new CustomEvent("unblocked",{detail:{clientId:e}}))})}}NAF.adapters.register("janus",f),e.exports=f},function(e,t){function n(e){this.session=e,this.id=void 0}function s(e,t){this.output=e,this.id=void 0,this.nextTxId=0,this.txns={},this.eventHandlers={},this.options=Object.assign({verbose:!1,timeoutMs:1e4,keepaliveMs:3e4},t)}n.prototype.attach=function(e){var t={plugin:e,"force-bundle":!0,"force-rtcp-mux":!0};return this.session.send("attach",t).then(e=>(this.id=e.data.id,e))},n.prototype.detach=function(){return this.send("detach")},n.prototype.on=function(e,t){return this.session.on(e,e=>{e.sender==this.id&&t(e)})},n.prototype.send=function(e,t){return this.session.send(e,Object.assign({handle_id:this.id},t))},n.prototype.sendMessage=function(e){return this.send("message",{body:e})},n.prototype.sendJsep=function(e){return this.send("message",{body:{},jsep:e})},n.prototype.sendTrickle=function(e){return this.send("trickle",{candidate:e})},s.prototype.create=function(){return this.send("create").then(e=>(this.id=e.data.id,e))},s.prototype.destroy=function(){return this.send("destroy").then(e=>(this.dispose(),e))},s.prototype.dispose=function(){for(var e in this._killKeepalive(),this.eventHandlers={},this.txns)if(this.txns.hasOwnProperty(e)){var t=this.txns[e];clearTimeout(t.timeout),t.reject(new Error("Janus session was disposed.")),delete this.txns[e]}},s.prototype.isError=function(e){return"error"===e.janus},s.prototype.on=function(e,t){var n=this.eventHandlers[e];null==n&&(n=this.eventHandlers[e]=[]),n.push(t)},s.prototype.receive=function(e){this.options.verbose&&this._logIncoming(e),e.session_id!=this.id&&console.warn("Incorrect session ID received in Janus signalling message: was "+e.session_id+", expected "+this.id+".");var t=e.janus,n=this.eventHandlers[t];if(null!=n)for(var s=0;s{var r=null;this.options.timeoutMs&&(r=setTimeout(()=>{delete this.txns[t.transaction],s(new Error("Signalling transaction with txid "+t.transaction+" timed out."))},this.options.timeoutMs)),this.txns[t.transaction]={resolve:n,reject:s,timeout:r,type:e},this._transmit(e,t)})},s.prototype._transmit=function(e,t){t=Object.assign({janus:e},t),null!=this.id&&(t=Object.assign({session_id:this.id},t)),this.options.verbose&&this._logOutgoing(t),this.output(JSON.stringify(t)),this._resetKeepalive()},s.prototype._logOutgoing=function(e){var t=e.janus;"message"===t&&e.jsep&&(t=e.jsep.type);var n="> Outgoing Janus "+(t||"signal")+" (#"+e.transaction+"): ";console.debug("%c"+n,"color: #040",e)},s.prototype._logIncoming=function(e){var t=e.janus,n=e.transaction?"< Incoming Janus "+(t||"signal")+" (#"+e.transaction+"): ":"< Incoming Janus "+(t||"signal")+": ";console.debug("%c"+n,"color: #004",e)},s.prototype._sendKeepalive=function(){return this.send("keepalive")},s.prototype._killKeepalive=function(){clearTimeout(this.keepaliveTimeout)},s.prototype._resetKeepalive=function(){this._killKeepalive(),this.options.keepaliveMs&&(this.keepaliveTimeout=setTimeout(()=>{this._sendKeepalive().catch(e=>console.error("Error received from keepalive: ",e))},this.options.keepaliveMs))},e.exports={JanusPluginHandle:n,JanusSession:s}},function(e,t,n){"use strict";const s={generateIdentifier:function(){return Math.random().toString(36).substr(2,10)}};s.localCName=s.generateIdentifier(),s.splitLines=function(e){return e.trim().split("\n").map(e=>e.trim())},s.splitSections=function(e){return e.split("\nm=").map((e,t)=>(t>0?"m="+e:e).trim()+"\r\n")},s.getDescription=function(e){const t=s.splitSections(e);return t&&t[0]},s.getMediaSections=function(e){const t=s.splitSections(e);return t.shift(),t},s.matchPrefix=function(e,t){return s.splitLines(e).filter(e=>0===e.indexOf(t))},s.parseCandidate=function(e){let t;t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" ");const n={foundation:t[0],component:{1:"rtp",2:"rtcp"}[t[1]]||t[1],protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],address:t[4],port:parseInt(t[5],10),type:t[7]};for(let e=8;e0?t[0].split("/")[1]:"sendrecv",uri:t[1]}},s.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+"\r\n"},s.parseFmtp=function(e){const t={};let n;const s=e.substr(e.indexOf(" ")+1).split(";");for(let e=0;e{void 0!==e.parameters[t]?s.push(t+"="+e.parameters[t]):s.push(t)}),t+="a=fmtp:"+n+" "+s.join(";")+"\r\n"}return t},s.parseRtcpFb=function(e){const t=e.substr(e.indexOf(" ")+1).split(" ");return{type:t.shift(),parameter:t.join(" ")}},s.writeRtcpFb=function(e){let t="",n=e.payloadType;return void 0!==e.preferredPayloadType&&(n=e.preferredPayloadType),e.rtcpFeedback&&e.rtcpFeedback.length&&e.rtcpFeedback.forEach(e=>{t+="a=rtcp-fb:"+n+" "+e.type+(e.parameter&&e.parameter.length?" "+e.parameter:"")+"\r\n"}),t},s.parseSsrcMedia=function(e){const t=e.indexOf(" "),n={ssrc:parseInt(e.substr(7,t-7),10)},s=e.indexOf(":",t);return s>-1?(n.attribute=e.substr(t+1,s-t-1),n.value=e.substr(s+1)):n.attribute=e.substr(t+1),n},s.parseSsrcGroup=function(e){const t=e.substr(13).split(" ");return{semantics:t.shift(),ssrcs:t.map(e=>parseInt(e,10))}},s.getMid=function(e){const t=s.matchPrefix(e,"a=mid:")[0];if(t)return t.substr(6)},s.parseFingerprint=function(e){const t=e.substr(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1].toUpperCase()}},s.getDtlsParameters=function(e,t){return{role:"auto",fingerprints:s.matchPrefix(e+t,"a=fingerprint:").map(s.parseFingerprint)}},s.writeDtlsParameters=function(e,t){let n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(e=>{n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},s.parseCryptoLine=function(e){const t=e.substr(9).split(" ");return{tag:parseInt(t[0],10),cryptoSuite:t[1],keyParams:t[2],sessionParams:t.slice(3)}},s.writeCryptoLine=function(e){return"a=crypto:"+e.tag+" "+e.cryptoSuite+" "+("object"==typeof e.keyParams?s.writeCryptoKeyParams(e.keyParams):e.keyParams)+(e.sessionParams?" "+e.sessionParams.join(" "):"")+"\r\n"},s.parseCryptoKeyParams=function(e){if(0!==e.indexOf("inline:"))return null;const t=e.substr(7).split("|");return{keyMethod:"inline",keySalt:t[0],lifeTime:t[1],mkiValue:t[2]?t[2].split(":")[0]:void 0,mkiLength:t[2]?t[2].split(":")[1]:void 0}},s.writeCryptoKeyParams=function(e){return e.keyMethod+":"+e.keySalt+(e.lifeTime?"|"+e.lifeTime:"")+(e.mkiValue&&e.mkiLength?"|"+e.mkiValue+":"+e.mkiLength:"")},s.getCryptoParameters=function(e,t){return s.matchPrefix(e+t,"a=crypto:").map(s.parseCryptoLine)},s.getIceParameters=function(e,t){const n=s.matchPrefix(e+t,"a=ice-ufrag:")[0],r=s.matchPrefix(e+t,"a=ice-pwd:")[0];return n&&r?{usernameFragment:n.substr(12),password:r.substr(10)}:null},s.writeIceParameters=function(e){let t="a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n";return e.iceLite&&(t+="a=ice-lite\r\n"),t},s.parseRtpParameters=function(e){const t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},n=s.splitLines(e)[0].split(" ");for(let r=3;r{t.headerExtensions.push(s.parseExtmap(e))}),t},s.writeRtpDescription=function(e,t){let n="";n+="m="+e+" ",n+=t.codecs.length>0?"9":"0",n+=" UDP/TLS/RTP/SAVPF ",n+=t.codecs.map(e=>void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType).join(" ")+"\r\n",n+="c=IN IP4 0.0.0.0\r\n",n+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach(e=>{n+=s.writeRtpMap(e),n+=s.writeFmtp(e),n+=s.writeRtcpFb(e)});let r=0;return t.codecs.forEach(e=>{e.maxptime>r&&(r=e.maxptime)}),r>0&&(n+="a=maxptime:"+r+"\r\n"),t.headerExtensions&&t.headerExtensions.forEach(e=>{n+=s.writeExtmap(e)}),n},s.parseRtpEncodingParameters=function(e){const t=[],n=s.parseRtpParameters(e),r=-1!==n.fecMechanisms.indexOf("RED"),i=-1!==n.fecMechanisms.indexOf("ULPFEC"),a=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute),o=a.length>0&&a[0].ssrc;let c;const l=s.matchPrefix(e,"a=ssrc-group:FID").map(e=>e.substr(17).split(" ").map(e=>parseInt(e,10)));l.length>0&&l[0].length>1&&l[0][0]===o&&(c=l[0][1]),n.codecs.forEach(e=>{if("RTX"===e.name.toUpperCase()&&e.parameters.apt){let n={ssrc:o,codecPayloadType:parseInt(e.parameters.apt,10)};o&&c&&(n.rtx={ssrc:c}),t.push(n),r&&(n=JSON.parse(JSON.stringify(n)),n.fec={ssrc:o,mechanism:i?"red+ulpfec":"red"},t.push(n))}}),0===t.length&&o&&t.push({ssrc:o});let u=s.matchPrefix(e,"b=");return u.length&&(u=0===u[0].indexOf("b=TIAS:")?parseInt(u[0].substr(7),10):0===u[0].indexOf("b=AS:")?1e3*parseInt(u[0].substr(5),10)*.95-16e3:void 0,t.forEach(e=>{e.maxBitrate=u})),t},s.parseRtcpParameters=function(e){const t={},n=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"cname"===e.attribute)[0];n&&(t.cname=n.value,t.ssrc=n.ssrc);const r=s.matchPrefix(e,"a=rtcp-rsize");t.reducedSize=r.length>0,t.compound=0===r.length;const i=s.matchPrefix(e,"a=rtcp-mux");return t.mux=i.length>0,t},s.writeRtcpParameters=function(e){let t="";return e.reducedSize&&(t+="a=rtcp-rsize\r\n"),e.mux&&(t+="a=rtcp-mux\r\n"),void 0!==e.ssrc&&e.cname&&(t+="a=ssrc:"+e.ssrc+" cname:"+e.cname+"\r\n"),t},s.parseMsid=function(e){let t;const n=s.matchPrefix(e,"a=msid:");if(1===n.length)return t=n[0].substr(7).split(" "),{stream:t[0],track:t[1]};const r=s.matchPrefix(e,"a=ssrc:").map(e=>s.parseSsrcMedia(e)).filter(e=>"msid"===e.attribute);return r.length>0?(t=r[0].value.split(" "),{stream:t[0],track:t[1]}):void 0},s.parseSctpDescription=function(e){const t=s.parseMLine(e),n=s.matchPrefix(e,"a=max-message-size:");let r;n.length>0&&(r=parseInt(n[0].substr(19),10)),isNaN(r)&&(r=65536);const i=s.matchPrefix(e,"a=sctp-port:");if(i.length>0)return{port:parseInt(i[0].substr(12),10),protocol:t.fmt,maxMessageSize:r};const a=s.matchPrefix(e,"a=sctpmap:");if(a.length>0){const e=a[0].substr(10).split(" ");return{port:parseInt(e[0],10),protocol:e[1],maxMessageSize:r}}},s.writeSctpDescription=function(e,t){let n=[];return n="DTLS/SCTP"!==e.protocol?["m="+e.kind+" 9 "+e.protocol+" "+t.protocol+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctp-port:"+t.port+"\r\n"]:["m="+e.kind+" 9 "+e.protocol+" "+t.port+"\r\n","c=IN IP4 0.0.0.0\r\n","a=sctpmap:"+t.port+" "+t.protocol+" 65535\r\n"],void 0!==t.maxMessageSize&&n.push("a=max-message-size:"+t.maxMessageSize+"\r\n"),n.join("")},s.generateSessionId=function(){return Math.random().toString().substr(2,21)},s.writeSessionBoilerplate=function(e,t,n){let r;const i=void 0!==t?t:2;r=e||s.generateSessionId();return"v=0\r\no="+(n||"thisisadapterortc")+" "+r+" "+i+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},s.getDirection=function(e,t){const n=s.splitLines(e);for(let e=0;e {\n if (e.message && e.message.indexOf(\"timed out\") > -1) {\n console.error(\"web socket timed out\");\n NAF.connection.adapter.reconnect();\n } else {\n throw(e);\n }\n });\n}\n\nvar sdpUtils = require(\"sdp\");\n//var debug = require(\"debug\")(\"naf-janus-adapter:debug\");\n//var warn = require(\"debug\")(\"naf-janus-adapter:warn\");\n//var error = require(\"debug\")(\"naf-janus-adapter:error\");\nvar debug = console.log;\nvar warn = console.warn;\nvar error = console.error;\nvar isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\nconst SUBSCRIBE_TIMEOUT_MS = 15000;\n\nfunction debounce(fn) {\n var curr = Promise.resolve();\n return function() {\n var args = Array.prototype.slice.call(arguments);\n curr = curr.then(_ => fn.apply(this, args));\n };\n}\n\nfunction randomUint() {\n return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction untilDataChannelOpen(dataChannel) {\n return new Promise((resolve, reject) => {\n if (dataChannel.readyState === \"open\") {\n resolve();\n } else {\n let resolver, rejector;\n\n const clear = () => {\n dataChannel.removeEventListener(\"open\", resolver);\n dataChannel.removeEventListener(\"error\", rejector);\n };\n\n resolver = () => {\n clear();\n resolve();\n };\n rejector = () => {\n clear();\n reject();\n };\n\n dataChannel.addEventListener(\"open\", resolver);\n dataChannel.addEventListener(\"error\", rejector);\n }\n });\n}\n\nconst isH264VideoSupported = (() => {\n const video = document.createElement(\"video\");\n return video.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"') !== \"\";\n})();\n\nconst OPUS_PARAMETERS = {\n // indicates that we want to enable DTX to elide silence packets\n usedtx: 1,\n // indicates that we prefer to receive mono audio (important for voip profile)\n stereo: 0,\n // indicates that we prefer to send mono audio (important for voip profile)\n \"sprop-stereo\": 0\n};\n\nconst DEFAULT_PEER_CONNECTION_CONFIG = {\n iceServers: [{ urls: \"stun:stun1.l.google.com:19302\" }, { urls: \"stun:stun2.l.google.com:19302\" }]\n};\n\nconst WS_NORMAL_CLOSURE = 1000;\n\nclass JanusAdapter {\n constructor() {\n this.room = null;\n // We expect the consumer to set a client id before connecting.\n this.clientId = null;\n this.joinToken = null;\n\n this.serverUrl = null;\n this.webRtcOptions = {};\n this.peerConnectionConfig = null;\n this.ws = null;\n this.session = null;\n this.reliableTransport = \"datachannel\";\n this.unreliableTransport = \"datachannel\";\n\n // In the event the server restarts and all clients lose connection, reconnect with\n // some random jitter added to prevent simultaneous reconnection requests.\n this.initialReconnectionDelay = 1000 * Math.random();\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionTimeout = null;\n this.maxReconnectionAttempts = 10;\n this.reconnectionAttempts = 0;\n\n this.publisher = null;\n this.occupants = {};\n this.leftOccupants = new Set();\n this.mediaStreams = {};\n this.localMediaStream = null;\n this.pendingMediaRequests = new Map();\n\n this.blockedClients = new Map();\n this.frozenUpdates = new Map();\n\n this.timeOffsets = [];\n this.serverTimeRequests = 0;\n this.avgTimeOffset = 0;\n\n this.onWebsocketOpen = this.onWebsocketOpen.bind(this);\n this.onWebsocketClose = this.onWebsocketClose.bind(this);\n this.onWebsocketMessage = this.onWebsocketMessage.bind(this);\n this.onDataChannelMessage = this.onDataChannelMessage.bind(this);\n this.onData = this.onData.bind(this);\n }\n\n setServerUrl(url) {\n this.serverUrl = url;\n }\n\n setApp(app) {}\n\n setRoom(roomName) {\n this.room = roomName;\n }\n\n setJoinToken(joinToken) {\n this.joinToken = joinToken;\n }\n\n setClientId(clientId) {\n this.clientId = clientId;\n }\n\n setWebRtcOptions(options) {\n this.webRtcOptions = options;\n }\n\n setPeerConnectionConfig(peerConnectionConfig) {\n this.peerConnectionConfig = peerConnectionConfig;\n }\n\n setServerConnectListeners(successListener, failureListener) {\n this.connectSuccess = successListener;\n this.connectFailure = failureListener;\n }\n\n setRoomOccupantListener(occupantListener) {\n this.onOccupantsChanged = occupantListener;\n }\n\n setDataChannelListeners(openListener, closedListener, messageListener) {\n this.onOccupantConnected = openListener;\n this.onOccupantDisconnected = closedListener;\n this.onOccupantMessage = messageListener;\n }\n\n setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) {\n // onReconnecting is called with the number of milliseconds until the next reconnection attempt\n this.onReconnecting = reconnectingListener;\n // onReconnected is called when the connection has been reestablished\n this.onReconnected = reconnectedListener;\n // onReconnectionError is called with an error when maxReconnectionAttempts has been reached\n this.onReconnectionError = reconnectionErrorListener;\n }\n\n connect() {\n debug(`connecting to ${this.serverUrl}`);\n\n const websocketConnection = new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.serverUrl, \"janus-protocol\");\n\n this.session = new mj.JanusSession(this.ws.send.bind(this.ws), { timeoutMs: 40000 });\n\n this.ws.addEventListener(\"close\", this.onWebsocketClose);\n this.ws.addEventListener(\"message\", this.onWebsocketMessage);\n\n this.wsOnOpen = () => {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.onWebsocketOpen()\n .then(resolve)\n .catch(reject);\n };\n\n this.ws.addEventListener(\"open\", this.wsOnOpen);\n });\n\n return Promise.all([websocketConnection, this.updateTimeOffset()]);\n }\n\n disconnect() {\n debug(`disconnecting`);\n\n clearTimeout(this.reconnectionTimeout);\n\n this.removeAllOccupants();\n this.leftOccupants = new Set();\n\n if (this.publisher) {\n // Close the publisher peer connection. Which also detaches the plugin handle.\n this.publisher.conn.close();\n this.publisher = null;\n }\n\n if (this.session) {\n this.session.dispose();\n this.session = null;\n }\n\n if (this.ws) {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.ws.removeEventListener(\"close\", this.onWebsocketClose);\n this.ws.removeEventListener(\"message\", this.onWebsocketMessage);\n this.ws.close();\n this.ws = null;\n }\n\n // Now that all RTCPeerConnection closed, be sure to not call\n // reconnect() again via performDelayedReconnect if previous\n // RTCPeerConnection was in the failed state.\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n this.delayedReconnectTimeout = null;\n }\n }\n\n isDisconnected() {\n return this.ws === null;\n }\n\n async onWebsocketOpen() {\n // Create the Janus Session\n await this.session.create();\n\n // Attach the SFU Plugin and create a RTCPeerConnection for the publisher.\n // The publisher sends audio and opens two bidirectional data channels.\n // One reliable datachannel and one unreliable.\n this.publisher = await this.createPublisher();\n\n // Call the naf connectSuccess callback before we start receiving WebRTC messages.\n this.connectSuccess(this.clientId);\n\n const addOccupantPromises = [];\n\n for (let i = 0; i < this.publisher.initialOccupants.length; i++) {\n const occupantId = this.publisher.initialOccupants[i];\n if (occupantId === this.clientId) continue; // Happens during non-graceful reconnects due to zombie sessions\n addOccupantPromises.push(this.addOccupant(occupantId));\n }\n\n await Promise.all(addOccupantPromises);\n }\n\n onWebsocketClose(event) {\n // The connection was closed successfully. Don't try to reconnect.\n if (event.code === WS_NORMAL_CLOSURE) {\n return;\n }\n\n console.warn(\"Janus websocket closed unexpectedly.\");\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n }\n\n reconnect() {\n // Dispose of all networked entities and other resources tied to the session.\n this.disconnect();\n\n this.connect()\n .then(() => {\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionAttempts = 0;\n\n if (this.onReconnected) {\n this.onReconnected();\n }\n })\n .catch(error => {\n this.reconnectionDelay += 1000;\n this.reconnectionAttempts++;\n\n if (this.reconnectionAttempts > this.maxReconnectionAttempts && this.onReconnectionError) {\n return this.onReconnectionError(\n new Error(\"Connection could not be reestablished, exceeded maximum number of reconnection attempts.\")\n );\n }\n\n console.warn(\"Error during reconnect, retrying.\");\n console.warn(error);\n\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n });\n }\n\n performDelayedReconnect() {\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n }\n\n this.delayedReconnectTimeout = setTimeout(() => {\n this.delayedReconnectTimeout = null;\n this.reconnect();\n }, 10000);\n }\n\n onWebsocketMessage(event) {\n this.session.receive(JSON.parse(event.data));\n }\n\n async addOccupant(occupantId) {\n if (this.occupants[occupantId]) {\n this.removeOccupant(occupantId);\n }\n\n this.leftOccupants.delete(occupantId);\n\n var subscriber = await this.createSubscriber(occupantId);\n\n if (!subscriber) return;\n\n this.occupants[occupantId] = subscriber;\n\n this.setMediaStream(occupantId, subscriber.mediaStream);\n\n // Call the Networked AFrame callbacks for the new occupant.\n this.onOccupantConnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n\n return subscriber;\n }\n\n removeAllOccupants() {\n for (const occupantId of Object.getOwnPropertyNames(this.occupants)) {\n this.removeOccupant(occupantId);\n }\n }\n\n removeOccupant(occupantId) {\n this.leftOccupants.add(occupantId);\n\n if (this.occupants[occupantId]) {\n // Close the subscriber peer connection. Which also detaches the plugin handle.\n this.occupants[occupantId].conn.close();\n delete this.occupants[occupantId];\n }\n\n if (this.mediaStreams[occupantId]) {\n delete this.mediaStreams[occupantId];\n }\n\n if (this.pendingMediaRequests.has(occupantId)) {\n const msg = \"The user disconnected before the media stream was resolved.\";\n this.pendingMediaRequests.get(occupantId).audio.reject(msg);\n this.pendingMediaRequests.get(occupantId).video.reject(msg);\n this.pendingMediaRequests.delete(occupantId);\n }\n\n // Call the Networked AFrame callbacks for the removed occupant.\n this.onOccupantDisconnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n }\n\n associate(conn, handle) {\n conn.addEventListener(\"icecandidate\", ev => {\n handle.sendTrickle(ev.candidate || null).catch(e => error(\"Error trickling ICE: %o\", e));\n });\n conn.addEventListener(\"iceconnectionstatechange\", ev => {\n if (conn.iceConnectionState === \"connected\") {\n console.log(\"ICE state changed to connected\");\n }\n if (conn.iceConnectionState === \"disconnected\") {\n console.warn(\"ICE state changed to disconnected\");\n }\n if (conn.iceConnectionState === \"failed\") {\n console.warn(\"ICE failure detected. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n })\n\n // we have to debounce these because janus gets angry if you send it a new SDP before\n // it's finished processing an existing SDP. in actuality, it seems like this is maybe\n // too liberal and we need to wait some amount of time after an offer before sending another,\n // but we don't currently know any good way of detecting exactly how long :(\n conn.addEventListener(\n \"negotiationneeded\",\n debounce(ev => {\n debug(\"Sending new offer for handle: %o\", handle);\n var offer = conn.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag);\n var local = offer.then(o => conn.setLocalDescription(o));\n var remote = offer;\n\n remote = remote\n .then(this.fixSafariIceUFrag)\n .then(j => handle.sendJsep(j))\n .then(r => conn.setRemoteDescription(r.jsep));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating offer: %o\", e));\n })\n );\n handle.on(\n \"event\",\n debounce(ev => {\n var jsep = ev.jsep;\n if (jsep && jsep.type == \"offer\") {\n debug(\"Accepting new offer for handle: %o\", handle);\n var answer = conn\n .setRemoteDescription(this.configureSubscriberSdp(jsep))\n .then(_ => conn.createAnswer())\n .then(this.fixSafariIceUFrag);\n var local = answer.then(a => conn.setLocalDescription(a));\n var remote = answer.then(j => handle.sendJsep(j));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating answer: %o\", e));\n } else {\n // some other kind of event, nothing to do\n return null;\n }\n })\n );\n }\n\n async createPublisher() {\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(\"pub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(\"pub waiting for data channels & webrtcup\");\n var webrtcup = new Promise(resolve => handle.on(\"webrtcup\", resolve));\n\n // Unreliable datachannel: sending and receiving component updates.\n // Reliable datachannel: sending and recieving entity instantiations.\n var reliableChannel = conn.createDataChannel(\"reliable\", { ordered: true });\n var unreliableChannel = conn.createDataChannel(\"unreliable\", {\n ordered: false,\n maxRetransmits: 0\n });\n\n reliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-reliable\"));\n unreliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-unreliable\"));\n\n await webrtcup;\n await untilDataChannelOpen(reliableChannel);\n await untilDataChannelOpen(unreliableChannel);\n\n // doing this here is sort of a hack around chrome renegotiation weirdness --\n // if we do it prior to webrtcup, chrome on gear VR will sometimes put a\n // renegotiation offer in flight while the first offer was still being\n // processed by janus. we should find some more principled way to figure out\n // when janus is done in the future.\n if (this.localMediaStream) {\n this.localMediaStream.getTracks().forEach(track => {\n conn.addTrack(track, this.localMediaStream);\n });\n }\n\n // Handle all of the join and leave events.\n handle.on(\"event\", ev => {\n var data = ev.plugindata.data;\n if (data.event == \"join\" && data.room_id == this.room) {\n if (this.delayedReconnectTimeout) {\n // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s.\n return;\n }\n this.addOccupant(data.user_id);\n } else if (data.event == \"leave\" && data.room_id == this.room) {\n this.removeOccupant(data.user_id);\n } else if (data.event == \"blocked\") {\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: data.by } }));\n } else if (data.event == \"unblocked\") {\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: data.by } }));\n } else if (data.event === \"data\") {\n this.onData(JSON.parse(data.body), \"janus-event\");\n }\n });\n\n debug(\"pub waiting for join\");\n\n // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data.\n var message = await this.sendJoin(handle, {\n notifications: true,\n data: true\n });\n\n if (!message.plugindata.data.success) {\n const err = message.plugindata.data.error;\n console.error(err);\n // We may get here because of an expired JWT.\n // Close the connection ourself otherwise janus will close it after\n // session_timeout because we didn't send any keepalive and this will\n // trigger a delayed reconnect because of the iceconnectionstatechange\n // listener for failure state.\n // Even if the app code calls disconnect in case of error, disconnect\n // won't close the peer connection because this.publisher is not set.\n conn.close();\n throw err;\n }\n\n var initialOccupants = message.plugindata.data.response.users[this.room] || [];\n\n if (initialOccupants.includes(this.clientId)) {\n console.warn(\"Janus still has previous session for this client. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n\n debug(\"publisher ready\");\n return {\n handle,\n initialOccupants,\n reliableChannel,\n unreliableChannel,\n conn\n };\n }\n\n configurePublisherSdp(jsep) {\n jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\\r\\n/g, (line, pt) => {\n const parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS);\n return sdpUtils.writeFmtp({ payloadType: pt, parameters: parameters });\n });\n return jsep;\n }\n\n configureSubscriberSdp(jsep) {\n // todo: consider cleaning up these hacks to use sdputils\n if (!isH264VideoSupported) {\n if (navigator.userAgent.indexOf(\"HeadlessChrome\") !== -1) {\n // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP.\n jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, \"m=\");\n }\n }\n\n // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8\n if (navigator.userAgent.indexOf(\"Android\") === -1) {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n\"\n );\n } else {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n\"\n );\n }\n return jsep;\n }\n\n async fixSafariIceUFrag(jsep) {\n // Safari produces a \\n instead of an \\r\\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818\n jsep.sdp = jsep.sdp.replace(/[^\\r]\\na=ice-ufrag/g, \"\\r\\na=ice-ufrag\");\n return jsep\n }\n\n async createSubscriber(occupantId, maxRetries = 5) {\n if (this.leftOccupants.has(occupantId)) {\n console.warn(occupantId + \": cancelled occupant connection, occupant left before subscription negotation.\");\n return null;\n }\n\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(occupantId + \": sub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(occupantId + \": sub waiting for join\");\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after attach\");\n return null;\n }\n\n let webrtcFailed = false;\n\n const webrtcup = new Promise(resolve => {\n const leftInterval = setInterval(() => {\n if (this.leftOccupants.has(occupantId)) {\n clearInterval(leftInterval);\n resolve();\n }\n }, 1000);\n\n const timeout = setTimeout(() => {\n clearInterval(leftInterval);\n webrtcFailed = true;\n resolve();\n }, SUBSCRIBE_TIMEOUT_MS);\n\n handle.on(\"webrtcup\", () => {\n clearTimeout(timeout);\n clearInterval(leftInterval);\n resolve();\n });\n });\n\n // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media.\n // Janus should send us an offer for this occupant's media in response to this.\n await this.sendJoin(handle, { media: occupantId });\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after join\");\n return null;\n }\n\n debug(occupantId + \": sub waiting for webrtcup\");\n await webrtcup;\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancel occupant connection, occupant left during or after webrtcup\");\n return null;\n }\n\n if (webrtcFailed) {\n conn.close();\n if (maxRetries > 0) {\n console.warn(occupantId + \": webrtc up timed out, retrying\");\n return this.createSubscriber(occupantId, maxRetries - 1);\n } else {\n console.warn(occupantId + \": webrtc up timed out\");\n return null;\n }\n }\n\n if (isSafari && !this._iOSHackDelayedInitialPeer) {\n // HACK: the first peer on Safari during page load can fail to work if we don't\n // wait some time before continuing here. See: https://github.com/mozilla/hubs/pull/1692\n await (new Promise((resolve) => setTimeout(resolve, 3000)));\n this._iOSHackDelayedInitialPeer = true;\n }\n\n var mediaStream = new MediaStream();\n var receivers = conn.getReceivers();\n receivers.forEach(receiver => {\n if (receiver.track) {\n mediaStream.addTrack(receiver.track);\n }\n });\n if (mediaStream.getTracks().length === 0) {\n mediaStream = null;\n }\n\n debug(occupantId + \": subscriber ready\");\n return {\n handle,\n mediaStream,\n conn\n };\n }\n\n sendJoin(handle, subscribe) {\n return handle.sendMessage({\n kind: \"join\",\n room_id: this.room,\n user_id: this.clientId,\n subscribe,\n token: this.joinToken\n });\n }\n\n toggleFreeze() {\n if (this.frozen) {\n this.unfreeze();\n } else {\n this.freeze();\n }\n }\n\n freeze() {\n this.frozen = true;\n }\n\n unfreeze() {\n this.frozen = false;\n this.flushPendingUpdates();\n }\n\n dataForUpdateMultiMessage(networkId, message) {\n // \"d\" is an array of entity datas, where each item in the array represents a unique entity and contains\n // metadata for the entity, and an array of components that have been updated on the entity.\n // This method finds the data corresponding to the given networkId.\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n const data = message.data.d[i];\n\n if (data.networkId === networkId) {\n return data;\n }\n }\n\n return null;\n }\n\n getPendingData(networkId, message) {\n if (!message) return null;\n\n let data = message.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, message) : message.data;\n\n // Ignore messages relating to users who have disconnected since freezing, their entities\n // will have aleady been removed by NAF.\n // Note that delete messages have no \"owner\" so we have to check for that as well.\n if (data.owner && !this.occupants[data.owner]) return null;\n\n // Ignore messages from users that we may have blocked while frozen.\n if (data.owner && this.blockedClients.has(data.owner)) return null;\n\n return data\n }\n\n // Used externally\n getPendingDataForNetworkId(networkId) {\n return this.getPendingData(networkId, this.frozenUpdates.get(networkId));\n }\n\n flushPendingUpdates() {\n for (const [networkId, message] of this.frozenUpdates) {\n let data = this.getPendingData(networkId, message);\n if (!data) continue;\n\n // Override the data type on \"um\" messages types, since we extract entity updates from \"um\" messages into\n // individual frozenUpdates in storeSingleMessage.\n const dataType = message.dataType === \"um\" ? \"u\" : message.dataType;\n\n this.onOccupantMessage(null, dataType, data, message.source);\n }\n this.frozenUpdates.clear();\n }\n\n storeMessage(message) {\n if (message.dataType === \"um\") { // UpdateMulti\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n this.storeSingleMessage(message, i);\n }\n } else {\n this.storeSingleMessage(message);\n }\n }\n\n storeSingleMessage(message, index) {\n const data = index !== undefined ? message.data.d[index] : message.data;\n const dataType = message.dataType;\n const source = message.source;\n\n const networkId = data.networkId;\n\n if (!this.frozenUpdates.has(networkId)) {\n this.frozenUpdates.set(networkId, message);\n } else {\n const storedMessage = this.frozenUpdates.get(networkId);\n const storedData = storedMessage.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data;\n\n // Avoid updating components if the entity data received did not come from the current owner.\n const isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime;\n const isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime;\n if (isOutdatedMessage || (isContemporaneousMessage && storedData.owner > data.owner)) {\n return;\n }\n\n if (dataType === \"r\") {\n const createdWhileFrozen = storedData && storedData.isFirstSync;\n if (createdWhileFrozen) {\n // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer.\n this.frozenUpdates.delete(networkId);\n } else {\n // Delete messages override any other messages for this entity\n this.frozenUpdates.set(networkId, message);\n }\n } else {\n // merge in component updates\n if (storedData.components && data.components) {\n Object.assign(storedData.components, data.components);\n }\n }\n }\n }\n\n onDataChannelMessage(e, source) {\n this.onData(JSON.parse(e.data), source);\n }\n\n onData(message, source) {\n if (debug.enabled) {\n debug(`DC in: ${message}`);\n }\n\n if (!message.dataType) return;\n\n message.source = source;\n\n if (this.frozen) {\n this.storeMessage(message);\n } else {\n this.onOccupantMessage(null, message.dataType, message.data, message.source);\n }\n }\n\n shouldStartConnectionTo(client) {\n return true;\n }\n\n startStreamConnection(client) {}\n\n closeStreamConnection(client) {}\n\n getConnectStatus(clientId) {\n return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED;\n }\n\n async updateTimeOffset() {\n if (this.isDisconnected()) return;\n\n const clientSentTime = Date.now();\n\n const res = await fetch(document.location.href, {\n method: \"HEAD\",\n cache: \"no-cache\"\n });\n\n const precision = 1000;\n const serverReceivedTime = new Date(res.headers.get(\"Date\")).getTime() + precision / 2;\n const clientReceivedTime = Date.now();\n const serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2;\n const timeOffset = serverTime - clientReceivedTime;\n\n this.serverTimeRequests++;\n\n if (this.serverTimeRequests <= 10) {\n this.timeOffsets.push(timeOffset);\n } else {\n this.timeOffsets[this.serverTimeRequests % 10] = timeOffset;\n }\n\n this.avgTimeOffset = this.timeOffsets.reduce((acc, offset) => (acc += offset), 0) / this.timeOffsets.length;\n\n if (this.serverTimeRequests > 10) {\n debug(`new server time offset: ${this.avgTimeOffset}ms`);\n setTimeout(() => this.updateTimeOffset(), 5 * 60 * 1000); // Sync clock every 5 minutes.\n } else {\n this.updateTimeOffset();\n }\n }\n\n getServerTime() {\n return Date.now() + this.avgTimeOffset;\n }\n\n getMediaStream(clientId, type = \"audio\") {\n if (this.mediaStreams[clientId]) {\n debug(`Already had ${type} for ${clientId}`);\n return Promise.resolve(this.mediaStreams[clientId][type]);\n } else {\n debug(`Waiting on ${type} for ${clientId}`);\n if (!this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.set(clientId, {});\n\n const audioPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).audio = { resolve, reject };\n });\n const videoPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).video = { resolve, reject };\n });\n\n this.pendingMediaRequests.get(clientId).audio.promise = audioPromise;\n this.pendingMediaRequests.get(clientId).video.promise = videoPromise;\n\n audioPromise.catch(e => console.warn(`${clientId} getMediaStream Audio Error`, e));\n videoPromise.catch(e => console.warn(`${clientId} getMediaStream Video Error`, e));\n }\n return this.pendingMediaRequests.get(clientId)[type].promise;\n }\n }\n\n setMediaStream(clientId, stream) {\n // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we\n // split the tracks into two streams.\n const audioStream = new MediaStream();\n try {\n stream.getAudioTracks().forEach(track => audioStream.addTrack(track));\n\n } catch(e) {\n console.warn(`${clientId} setMediaStream Audio Error`, e);\n }\n const videoStream = new MediaStream();\n try {\n stream.getVideoTracks().forEach(track => videoStream.addTrack(track));\n\n } catch (e) {\n console.warn(`${clientId} setMediaStream Video Error`, e);\n }\n\n this.mediaStreams[clientId] = { audio: audioStream, video: videoStream };\n\n // Resolve the promise for the user's media stream if it exists.\n if (this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.get(clientId).audio.resolve(audioStream);\n this.pendingMediaRequests.get(clientId).video.resolve(videoStream);\n }\n }\n\n async setLocalMediaStream(stream) {\n // our job here is to make sure the connection winds up with RTP senders sending the stuff in this stream,\n // and not the stuff that isn't in this stream. strategy is to replace existing tracks if we can, add tracks\n // that we can't replace, and disable tracks that don't exist anymore.\n\n // note that we don't ever remove a track from the stream -- since Janus doesn't support Unified Plan, we absolutely\n // can't wind up with a SDP that has >1 audio or >1 video tracks, even if one of them is inactive (what you get if\n // you remove a track from an existing stream.)\n if (this.publisher && this.publisher.conn) {\n const existingSenders = this.publisher.conn.getSenders();\n const newSenders = [];\n const tracks = stream.getTracks();\n\n for (let i = 0; i < tracks.length; i++) {\n const t = tracks[i];\n const sender = existingSenders.find(s => s.track != null && s.track.kind == t.kind);\n\n if (sender != null) {\n if (sender.replaceTrack) {\n await sender.replaceTrack(t);\n\n // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771\n if (t.kind === \"video\" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {\n t.enabled = false;\n setTimeout(() => t.enabled = true, 1000);\n }\n } else {\n // Fallback for browsers that don't support replaceTrack. At this time of this writing\n // most browsers support it, and testing this code path seems to not work properly\n // in Chrome anymore.\n stream.removeTrack(sender.track);\n stream.addTrack(t);\n }\n newSenders.push(sender);\n } else {\n newSenders.push(this.publisher.conn.addTrack(t, stream));\n }\n }\n existingSenders.forEach(s => {\n if (!newSenders.includes(s)) {\n s.track.enabled = false;\n }\n });\n }\n this.localMediaStream = stream;\n this.setMediaStream(this.clientId, stream);\n }\n\n enableMicrophone(enabled) {\n if (this.publisher && this.publisher.conn) {\n this.publisher.conn.getSenders().forEach(s => {\n if (s.track.kind == \"audio\") {\n s.track.enabled = enabled;\n }\n });\n }\n }\n\n sendData(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.unreliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n sendDataGuaranteed(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.reliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n broadcastData(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.unreliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n broadcastDataGuaranteed(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.reliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n kick(clientId, permsToken) {\n return this.publisher.handle.sendMessage({ kind: \"kick\", room_id: this.room, user_id: clientId, token: permsToken }).then(() => {\n document.body.dispatchEvent(new CustomEvent(\"kicked\", { detail: { clientId: clientId } }));\n });\n }\n\n block(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"block\", whom: clientId }).then(() => {\n this.blockedClients.set(clientId, true);\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: clientId } }));\n });\n }\n\n unblock(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"unblock\", whom: clientId }).then(() => {\n this.blockedClients.delete(clientId);\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: clientId } }));\n });\n }\n}\n\nNAF.adapters.register(\"janus\", JanusAdapter);\n\nmodule.exports = JanusAdapter;\n","/**\n * Represents a handle to a single Janus plugin on a Janus session. Each WebRTC connection to the Janus server will be\n * associated with a single handle. Once attached to the server, this handle will be given a unique ID which should be\n * used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#handles.\n **/\nfunction JanusPluginHandle(session) {\n this.session = session;\n this.id = undefined;\n}\n\n/** Attaches this handle to the Janus server and sets its ID. **/\nJanusPluginHandle.prototype.attach = function(plugin) {\n var payload = { plugin: plugin, \"force-bundle\": true, \"force-rtcp-mux\": true };\n return this.session.send(\"attach\", payload).then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/** Detaches this handle. **/\nJanusPluginHandle.prototype.detach = function() {\n return this.send(\"detach\");\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this plugin handle with the\n * `janus` attribute equal to `ev`.\n **/\nJanusPluginHandle.prototype.on = function(ev, callback) {\n return this.session.on(ev, signal => {\n if (signal.sender == this.id) {\n callback(signal);\n }\n });\n};\n\n/**\n * Sends a signal associated with this handle. Signals should be JSON-serializable objects. Returns a promise that will\n * be resolved or rejected when a response to this signal is received, or when no response is received within the\n * session timeout.\n **/\nJanusPluginHandle.prototype.send = function(type, signal) {\n return this.session.send(type, Object.assign({ handle_id: this.id }, signal));\n};\n\n/** Sends a plugin-specific message associated with this handle. **/\nJanusPluginHandle.prototype.sendMessage = function(body) {\n return this.send(\"message\", { body: body });\n};\n\n/** Sends a JSEP offer or answer associated with this handle. **/\nJanusPluginHandle.prototype.sendJsep = function(jsep) {\n return this.send(\"message\", { body: {}, jsep: jsep });\n};\n\n/** Sends an ICE trickle candidate associated with this handle. **/\nJanusPluginHandle.prototype.sendTrickle = function(candidate) {\n return this.send(\"trickle\", { candidate: candidate });\n};\n\n/**\n * Represents a Janus session -- a Janus context from within which you can open multiple handles and connections. Once\n * created, this session will be given a unique ID which should be used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#sessions.\n **/\nfunction JanusSession(output, options) {\n this.output = output;\n this.id = undefined;\n this.nextTxId = 0;\n this.txns = {};\n this.eventHandlers = {};\n this.options = Object.assign({\n verbose: false,\n timeoutMs: 10000,\n keepaliveMs: 30000\n }, options);\n}\n\n/** Creates this session on the Janus server and sets its ID. **/\nJanusSession.prototype.create = function() {\n return this.send(\"create\").then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/**\n * Destroys this session. Note that upon destruction, Janus will also close the signalling transport (if applicable) and\n * any open WebRTC connections.\n **/\nJanusSession.prototype.destroy = function() {\n return this.send(\"destroy\").then((resp) => {\n this.dispose();\n return resp;\n });\n};\n\n/**\n * Disposes of this session in a way such that no further incoming signalling messages will be processed.\n * Outstanding transactions will be rejected.\n **/\nJanusSession.prototype.dispose = function() {\n this._killKeepalive();\n this.eventHandlers = {};\n for (var txId in this.txns) {\n if (this.txns.hasOwnProperty(txId)) {\n var txn = this.txns[txId];\n clearTimeout(txn.timeout);\n txn.reject(new Error(\"Janus session was disposed.\"));\n delete this.txns[txId];\n }\n }\n};\n\n/**\n * Whether this signal represents an error, and the associated promise (if any) should be rejected.\n * Users should override this to handle any custom plugin-specific error conventions.\n **/\nJanusSession.prototype.isError = function(signal) {\n return signal.janus === \"error\";\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this session with the\n * `janus` attribute equal to `ev`.\n **/\nJanusSession.prototype.on = function(ev, callback) {\n var handlers = this.eventHandlers[ev];\n if (handlers == null) {\n handlers = this.eventHandlers[ev] = [];\n }\n handlers.push(callback);\n};\n\n/**\n * Callback for receiving JSON signalling messages pertinent to this session. If the signals are responses to previously\n * sent signals, the promises for the outgoing signals will be resolved or rejected appropriately with this signal as an\n * argument.\n *\n * External callers should call this function every time a new signal arrives on the transport; for example, in a\n * WebSocket's `message` event, or when a new datum shows up in an HTTP long-polling response.\n **/\nJanusSession.prototype.receive = function(signal) {\n if (this.options.verbose) {\n this._logIncoming(signal);\n }\n if (signal.session_id != this.id) {\n console.warn(\"Incorrect session ID received in Janus signalling message: was \" + signal.session_id + \", expected \" + this.id + \".\");\n }\n\n var responseType = signal.janus;\n var handlers = this.eventHandlers[responseType];\n if (handlers != null) {\n for (var i = 0; i < handlers.length; i++) {\n handlers[i](signal);\n }\n }\n\n if (signal.transaction != null) {\n var txn = this.txns[signal.transaction];\n if (txn == null) {\n // this is a response to a transaction that wasn't caused via JanusSession.send, or a plugin replied twice to a\n // single request, or the session was disposed, or something else that isn't under our purview; that's fine\n return;\n }\n\n if (responseType === \"ack\" && txn.type == \"message\") {\n // this is an ack of an asynchronously-processed plugin request, we should wait to resolve the promise until the\n // actual response comes in\n return;\n }\n\n clearTimeout(txn.timeout);\n\n delete this.txns[signal.transaction];\n (this.isError(signal) ? txn.reject : txn.resolve)(signal);\n }\n};\n\n/**\n * Sends a signal associated with this session, beginning a new transaction. Returns a promise that will be resolved or\n * rejected when a response is received in the same transaction, or when no response is received within the session\n * timeout.\n **/\nJanusSession.prototype.send = function(type, signal) {\n signal = Object.assign({ transaction: (this.nextTxId++).toString() }, signal);\n return new Promise((resolve, reject) => {\n var timeout = null;\n if (this.options.timeoutMs) {\n timeout = setTimeout(() => {\n delete this.txns[signal.transaction];\n reject(new Error(\"Signalling transaction with txid \" + signal.transaction + \" timed out.\"));\n }, this.options.timeoutMs);\n }\n this.txns[signal.transaction] = { resolve: resolve, reject: reject, timeout: timeout, type: type };\n this._transmit(type, signal);\n });\n};\n\nJanusSession.prototype._transmit = function(type, signal) {\n signal = Object.assign({ janus: type }, signal);\n\n if (this.id != null) { // this.id is undefined in the special case when we're sending the session create message\n signal = Object.assign({ session_id: this.id }, signal);\n }\n\n if (this.options.verbose) {\n this._logOutgoing(signal);\n }\n\n this.output(JSON.stringify(signal));\n this._resetKeepalive();\n};\n\nJanusSession.prototype._logOutgoing = function(signal) {\n var kind = signal.janus;\n if (kind === \"message\" && signal.jsep) {\n kind = signal.jsep.type;\n }\n var message = \"> Outgoing Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \";\n console.debug(\"%c\" + message, \"color: #040\", signal);\n};\n\nJanusSession.prototype._logIncoming = function(signal) {\n var kind = signal.janus;\n var message = signal.transaction ?\n \"< Incoming Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \" :\n \"< Incoming Janus \" + (kind || \"signal\") + \": \";\n console.debug(\"%c\" + message, \"color: #004\", signal);\n};\n\nJanusSession.prototype._sendKeepalive = function() {\n return this.send(\"keepalive\");\n};\n\nJanusSession.prototype._killKeepalive = function() {\n clearTimeout(this.keepaliveTimeout);\n};\n\nJanusSession.prototype._resetKeepalive = function() {\n this._killKeepalive();\n if (this.options.keepaliveMs) {\n this.keepaliveTimeout = setTimeout(() => {\n this._sendKeepalive().catch(e => console.error(\"Error received from keepalive: \", e));\n }, this.options.keepaliveMs);\n }\n};\n\nmodule.exports = {\n JanusPluginHandle,\n JanusSession\n};\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: 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"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.js","webpack:///./node_modules/minijanus/minijanus.js","webpack:///./node_modules/sdp/sdp.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","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","mj","require","JanusSession","sendOriginal","send","type","signal","this","catch","e","message","indexOf","console","error","NAF","connection","adapter","reconnect","sdpUtils","debug","log","warn","isSafari","test","navigator","userAgent","debounce","fn","curr","Promise","resolve","args","Array","slice","arguments","then","_","apply","untilDataChannelOpen","dataChannel","reject","readyState","resolver","rejector","clear","removeEventListener","addEventListener","isH264VideoSupported","document","createElement","canPlayType","OPUS_PARAMETERS","usedtx","stereo","DEFAULT_PEER_CONNECTION_CONFIG","iceServers","urls","JanusAdapter","constructor","room","clientId","joinToken","serverUrl","webRtcOptions","peerConnectionConfig","ws","session","reliableTransport","unreliableTransport","initialReconnectionDelay","Math","random","reconnectionDelay","reconnectionTimeout","maxReconnectionAttempts","reconnectionAttempts","publisher","occupants","leftOccupants","Set","mediaStreams","localMediaStream","pendingMediaRequests","Map","blockedClients","frozenUpdates","timeOffsets","serverTimeRequests","avgTimeOffset","onWebsocketOpen","onWebsocketClose","onWebsocketMessage","onDataChannelMessage","onData","setServerUrl","url","setApp","app","setRoom","roomName","setJoinToken","setClientId","setWebRtcOptions","options","setPeerConnectionConfig","setServerConnectListeners","successListener","failureListener","connectSuccess","connectFailure","setRoomOccupantListener","occupantListener","onOccupantsChanged","setDataChannelListeners","openListener","closedListener","messageListener","onOccupantConnected","onOccupantDisconnected","onOccupantMessage","setReconnectionListeners","reconnectingListener","reconnectedListener","reconnectionErrorListener","onReconnecting","onReconnected","onReconnectionError","connect","websocketConnection","WebSocket","timeoutMs","wsOnOpen","all","updateTimeOffset","disconnect","clearTimeout","removeAllOccupants","conn","close","dispose","delayedReconnectTimeout","isDisconnected","createPublisher","addOccupantPromises","initialOccupants","length","occupantId","push","addOccupant","event","code","setTimeout","Error","performDelayedReconnect","receive","JSON","parse","data","removeOccupant","delete","subscriber","createSubscriber","setMediaStream","mediaStream","getOwnPropertyNames","add","has","msg","audio","video","associate","handle","ev","sendTrickle","candidate","iceConnectionState","offer","createOffer","configurePublisherSdp","fixSafariIceUFrag","local","setLocalDescription","remote","j","sendJsep","setRemoteDescription","jsep","on","answer","configureSubscriberSdp","createAnswer","a","JanusPluginHandle","RTCPeerConnection","attach","webrtcup","reliableChannel","createDataChannel","ordered","unreliableChannel","maxRetransmits","getTracks","forEach","track","addTrack","plugindata","room_id","user_id","body","dispatchEvent","CustomEvent","detail","by","sendJoin","notifications","success","err","response","users","includes","sdp","replace","line","pt","parameters","assign","parseFmtp","writeFmtp","payloadType","maxRetries","webrtcFailed","leftInterval","setInterval","clearInterval","timeout","media","_iOSHackDelayedInitialPeer","MediaStream","getReceivers","receiver","subscribe","sendMessage","kind","token","toggleFreeze","frozen","unfreeze","freeze","flushPendingUpdates","dataForUpdateMultiMessage","networkId","getPendingData","dataType","owner","getPendingDataForNetworkId","source","storeMessage","storeSingleMessage","index","undefined","storedMessage","storedData","isOutdatedMessage","lastOwnerTime","isContemporaneousMessage","isFirstSync","set","components","enabled","shouldStartConnectionTo","client","startStreamConnection","closeStreamConnection","getConnectStatus","adapters","IS_CONNECTED","NOT_CONNECTED","clientSentTime","Date","now","res","fetch","location","href","method","cache","serverReceivedTime","headers","getTime","precision","clientReceivedTime","timeOffset","reduce","acc","offset","getServerTime","getMediaStream","audioPromise","videoPromise","promise","stream","audioStream","getAudioTracks","videoStream","getVideoTracks","setLocalMediaStream","existingSenders","getSenders","newSenders","tracks","sender","find","replaceTrack","toLowerCase","removeTrack","enableMicrophone","sendData","stringify","whom","sendDataGuaranteed","broadcastData","broadcastDataGuaranteed","kick","permsToken","block","unblock","register","id","output","nextTxId","txns","eventHandlers","verbose","keepaliveMs","plugin","payload","resp","detach","callback","handle_id","destroy","txId","_killKeepalive","txn","isError","janus","handlers","_logIncoming","session_id","responseType","transaction","toString","_transmit","_logOutgoing","_resetKeepalive","_sendKeepalive","keepaliveTimeout","SDPUtils","substr","localCName","generateIdentifier","splitLines","blob","trim","split","map","splitSections","part","getDescription","sections","getMediaSections","shift","matchPrefix","prefix","filter","parseCandidate","parts","substring","foundation","component","1","2","protocol","priority","parseInt","ip","address","port","relatedAddress","relatedPort","tcpType","ufrag","usernameFragment","writeCandidate","toUpperCase","join","parseIceOptions","parseRtpMap","parsed","clockRate","channels","numChannels","writeRtpMap","codec","preferredPayloadType","parseExtmap","direction","uri","writeExtmap","headerExtension","preferredId","kv","keys","params","param","parseRtcpFb","parameter","writeRtcpFb","lines","rtcpFeedback","fb","parseSsrcMedia","sp","ssrc","colon","attribute","parseSsrcGroup","semantics","ssrcs","getMid","mediaSection","mid","parseFingerprint","algorithm","getDtlsParameters","sessionpart","role","fingerprints","writeDtlsParameters","setupType","fp","parseCryptoLine","tag","cryptoSuite","keyParams","sessionParams","writeCryptoLine","writeCryptoKeyParams","parseCryptoKeyParams","keyMethod","keySalt","lifeTime","mkiValue","mkiLength","getCryptoParameters","getIceParameters","pwd","password","writeIceParameters","iceLite","parseRtpParameters","description","codecs","headerExtensions","fecMechanisms","rtcp","mline","rtpmapline","fmtps","writeRtpDescription","caps","maxptime","extension","parseRtpEncodingParameters","encodingParameters","hasRed","hasUlpfec","primarySsrc","secondarySsrc","flows","apt","encParam","codecPayloadType","rtx","fec","mechanism","bandwidth","maxBitrate","parseRtcpParameters","rtcpParameters","remoteSsrc","obj","cname","rsize","reducedSize","compound","mux","writeRtcpParameters","parseMsid","spec","planB","msidParts","parseSctpDescription","parseMLine","maxSizeLine","maxMessageSize","isNaN","sctpPort","fmt","sctpMapLines","writeSctpDescription","sctp","generateSessionId","writeSessionBoilerplate","sessId","sessVer","sessUser","sessionId","version","getDirection","getKind","isRejected","parseOLine","username","sessionVersion","netType","addressType","isValidSDP","charAt"],"mappings":"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","file":"naf-janus-adapter.min.js","sourcesContent":[" \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 = 0);\n","var mj = require(\"minijanus\");\nmj.JanusSession.prototype.sendOriginal = mj.JanusSession.prototype.send;\nmj.JanusSession.prototype.send = function(type, signal) {\n return this.sendOriginal(type, signal).catch((e) => {\n if (e.message && e.message.indexOf(\"timed out\") > -1) {\n console.error(\"web socket timed out\");\n NAF.connection.adapter.reconnect();\n } else {\n throw(e);\n }\n });\n}\n\nvar sdpUtils = require(\"sdp\");\n//var debug = require(\"debug\")(\"naf-janus-adapter:debug\");\n//var warn = require(\"debug\")(\"naf-janus-adapter:warn\");\n//var error = require(\"debug\")(\"naf-janus-adapter:error\");\nvar debug = console.log;\nvar warn = console.warn;\nvar error = console.error;\nvar isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\n\nconst SUBSCRIBE_TIMEOUT_MS = 15000;\n\nfunction debounce(fn) {\n var curr = Promise.resolve();\n return function() {\n var args = Array.prototype.slice.call(arguments);\n curr = curr.then(_ => fn.apply(this, args));\n };\n}\n\nfunction randomUint() {\n return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);\n}\n\nfunction untilDataChannelOpen(dataChannel) {\n return new Promise((resolve, reject) => {\n if (dataChannel.readyState === \"open\") {\n resolve();\n } else {\n let resolver, rejector;\n\n const clear = () => {\n dataChannel.removeEventListener(\"open\", resolver);\n dataChannel.removeEventListener(\"error\", rejector);\n };\n\n resolver = () => {\n clear();\n resolve();\n };\n rejector = () => {\n clear();\n reject();\n };\n\n dataChannel.addEventListener(\"open\", resolver);\n dataChannel.addEventListener(\"error\", rejector);\n }\n });\n}\n\nconst isH264VideoSupported = (() => {\n const video = document.createElement(\"video\");\n return video.canPlayType('video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"') !== \"\";\n})();\n\nconst OPUS_PARAMETERS = {\n // indicates that we want to enable DTX to elide silence packets\n usedtx: 1,\n // indicates that we prefer to receive mono audio (important for voip profile)\n stereo: 0,\n // indicates that we prefer to send mono audio (important for voip profile)\n \"sprop-stereo\": 0\n};\n\nconst DEFAULT_PEER_CONNECTION_CONFIG = {\n iceServers: [{ urls: \"stun:stun1.l.google.com:19302\" }, { urls: \"stun:stun2.l.google.com:19302\" }]\n};\n\nconst WS_NORMAL_CLOSURE = 1000;\n\nclass JanusAdapter {\n constructor() {\n this.room = null;\n // We expect the consumer to set a client id before connecting.\n this.clientId = null;\n this.joinToken = null;\n\n this.serverUrl = null;\n this.webRtcOptions = {};\n this.peerConnectionConfig = null;\n this.ws = null;\n this.session = null;\n this.reliableTransport = \"datachannel\";\n this.unreliableTransport = \"datachannel\";\n\n // In the event the server restarts and all clients lose connection, reconnect with\n // some random jitter added to prevent simultaneous reconnection requests.\n this.initialReconnectionDelay = 1000 * Math.random();\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionTimeout = null;\n this.maxReconnectionAttempts = 10;\n this.reconnectionAttempts = 0;\n\n this.publisher = null;\n this.occupants = {};\n this.leftOccupants = new Set();\n this.mediaStreams = {};\n this.localMediaStream = null;\n this.pendingMediaRequests = new Map();\n\n this.blockedClients = new Map();\n this.frozenUpdates = new Map();\n\n this.timeOffsets = [];\n this.serverTimeRequests = 0;\n this.avgTimeOffset = 0;\n\n this.onWebsocketOpen = this.onWebsocketOpen.bind(this);\n this.onWebsocketClose = this.onWebsocketClose.bind(this);\n this.onWebsocketMessage = this.onWebsocketMessage.bind(this);\n this.onDataChannelMessage = this.onDataChannelMessage.bind(this);\n this.onData = this.onData.bind(this);\n }\n\n setServerUrl(url) {\n this.serverUrl = url;\n }\n\n setApp(app) {}\n\n setRoom(roomName) {\n this.room = roomName;\n }\n\n setJoinToken(joinToken) {\n this.joinToken = joinToken;\n }\n\n setClientId(clientId) {\n this.clientId = clientId;\n }\n\n setWebRtcOptions(options) {\n this.webRtcOptions = options;\n }\n\n setPeerConnectionConfig(peerConnectionConfig) {\n this.peerConnectionConfig = peerConnectionConfig;\n }\n\n setServerConnectListeners(successListener, failureListener) {\n this.connectSuccess = successListener;\n this.connectFailure = failureListener;\n }\n\n setRoomOccupantListener(occupantListener) {\n this.onOccupantsChanged = occupantListener;\n }\n\n setDataChannelListeners(openListener, closedListener, messageListener) {\n this.onOccupantConnected = openListener;\n this.onOccupantDisconnected = closedListener;\n this.onOccupantMessage = messageListener;\n }\n\n setReconnectionListeners(reconnectingListener, reconnectedListener, reconnectionErrorListener) {\n // onReconnecting is called with the number of milliseconds until the next reconnection attempt\n this.onReconnecting = reconnectingListener;\n // onReconnected is called when the connection has been reestablished\n this.onReconnected = reconnectedListener;\n // onReconnectionError is called with an error when maxReconnectionAttempts has been reached\n this.onReconnectionError = reconnectionErrorListener;\n }\n\n connect() {\n debug(`connecting to ${this.serverUrl}`);\n\n const websocketConnection = new Promise((resolve, reject) => {\n this.ws = new WebSocket(this.serverUrl, \"janus-protocol\");\n\n this.session = new mj.JanusSession(this.ws.send.bind(this.ws), { timeoutMs: 40000 });\n\n this.ws.addEventListener(\"close\", this.onWebsocketClose);\n this.ws.addEventListener(\"message\", this.onWebsocketMessage);\n\n this.wsOnOpen = () => {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.onWebsocketOpen()\n .then(resolve)\n .catch(reject);\n };\n\n this.ws.addEventListener(\"open\", this.wsOnOpen);\n });\n\n return Promise.all([websocketConnection, this.updateTimeOffset()]);\n }\n\n disconnect() {\n debug(`disconnecting`);\n\n clearTimeout(this.reconnectionTimeout);\n\n this.removeAllOccupants();\n this.leftOccupants = new Set();\n\n if (this.publisher) {\n // Close the publisher peer connection. Which also detaches the plugin handle.\n this.publisher.conn.close();\n this.publisher = null;\n }\n\n if (this.session) {\n this.session.dispose();\n this.session = null;\n }\n\n if (this.ws) {\n this.ws.removeEventListener(\"open\", this.wsOnOpen);\n this.ws.removeEventListener(\"close\", this.onWebsocketClose);\n this.ws.removeEventListener(\"message\", this.onWebsocketMessage);\n this.ws.close();\n this.ws = null;\n }\n\n // Now that all RTCPeerConnection closed, be sure to not call\n // reconnect() again via performDelayedReconnect if previous\n // RTCPeerConnection was in the failed state.\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n this.delayedReconnectTimeout = null;\n }\n }\n\n isDisconnected() {\n return this.ws === null;\n }\n\n async onWebsocketOpen() {\n // Create the Janus Session\n await this.session.create();\n\n // Attach the SFU Plugin and create a RTCPeerConnection for the publisher.\n // The publisher sends audio and opens two bidirectional data channels.\n // One reliable datachannel and one unreliable.\n this.publisher = await this.createPublisher();\n\n // Call the naf connectSuccess callback before we start receiving WebRTC messages.\n this.connectSuccess(this.clientId);\n\n const addOccupantPromises = [];\n\n for (let i = 0; i < this.publisher.initialOccupants.length; i++) {\n const occupantId = this.publisher.initialOccupants[i];\n if (occupantId === this.clientId) continue; // Happens during non-graceful reconnects due to zombie sessions\n addOccupantPromises.push(this.addOccupant(occupantId));\n }\n\n await Promise.all(addOccupantPromises);\n }\n\n onWebsocketClose(event) {\n // The connection was closed successfully. Don't try to reconnect.\n if (event.code === WS_NORMAL_CLOSURE) {\n return;\n }\n\n console.warn(\"Janus websocket closed unexpectedly.\");\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n }\n\n reconnect() {\n // Dispose of all networked entities and other resources tied to the session.\n this.disconnect();\n\n this.connect()\n .then(() => {\n this.reconnectionDelay = this.initialReconnectionDelay;\n this.reconnectionAttempts = 0;\n\n if (this.onReconnected) {\n this.onReconnected();\n }\n })\n .catch(error => {\n this.reconnectionDelay += 1000;\n this.reconnectionAttempts++;\n\n if (this.reconnectionAttempts > this.maxReconnectionAttempts && this.onReconnectionError) {\n return this.onReconnectionError(\n new Error(\"Connection could not be reestablished, exceeded maximum number of reconnection attempts.\")\n );\n }\n\n console.warn(\"Error during reconnect, retrying.\");\n console.warn(error);\n\n if (this.onReconnecting) {\n this.onReconnecting(this.reconnectionDelay);\n }\n\n this.reconnectionTimeout = setTimeout(() => this.reconnect(), this.reconnectionDelay);\n });\n }\n\n performDelayedReconnect() {\n if (this.delayedReconnectTimeout) {\n clearTimeout(this.delayedReconnectTimeout);\n }\n\n this.delayedReconnectTimeout = setTimeout(() => {\n this.delayedReconnectTimeout = null;\n this.reconnect();\n }, 10000);\n }\n\n onWebsocketMessage(event) {\n this.session.receive(JSON.parse(event.data));\n }\n\n async addOccupant(occupantId) {\n if (this.occupants[occupantId]) {\n this.removeOccupant(occupantId);\n }\n\n this.leftOccupants.delete(occupantId);\n\n var subscriber = await this.createSubscriber(occupantId);\n\n if (!subscriber) return;\n\n this.occupants[occupantId] = subscriber;\n\n this.setMediaStream(occupantId, subscriber.mediaStream);\n\n // Call the Networked AFrame callbacks for the new occupant.\n this.onOccupantConnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n\n return subscriber;\n }\n\n removeAllOccupants() {\n for (const occupantId of Object.getOwnPropertyNames(this.occupants)) {\n this.removeOccupant(occupantId);\n }\n }\n\n removeOccupant(occupantId) {\n this.leftOccupants.add(occupantId);\n\n if (this.occupants[occupantId]) {\n // Close the subscriber peer connection. Which also detaches the plugin handle.\n this.occupants[occupantId].conn.close();\n delete this.occupants[occupantId];\n }\n\n if (this.mediaStreams[occupantId]) {\n delete this.mediaStreams[occupantId];\n }\n\n if (this.pendingMediaRequests.has(occupantId)) {\n const msg = \"The user disconnected before the media stream was resolved.\";\n this.pendingMediaRequests.get(occupantId).audio.reject(msg);\n this.pendingMediaRequests.get(occupantId).video.reject(msg);\n this.pendingMediaRequests.delete(occupantId);\n }\n\n // Call the Networked AFrame callbacks for the removed occupant.\n this.onOccupantDisconnected(occupantId);\n this.onOccupantsChanged(this.occupants);\n }\n\n associate(conn, handle) {\n conn.addEventListener(\"icecandidate\", ev => {\n handle.sendTrickle(ev.candidate || null).catch(e => error(\"Error trickling ICE: %o\", e));\n });\n conn.addEventListener(\"iceconnectionstatechange\", ev => {\n if (conn.iceConnectionState === \"connected\") {\n console.log(\"ICE state changed to connected\");\n }\n if (conn.iceConnectionState === \"disconnected\") {\n console.warn(\"ICE state changed to disconnected\");\n }\n if (conn.iceConnectionState === \"failed\") {\n console.warn(\"ICE failure detected. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n })\n\n // we have to debounce these because janus gets angry if you send it a new SDP before\n // it's finished processing an existing SDP. in actuality, it seems like this is maybe\n // too liberal and we need to wait some amount of time after an offer before sending another,\n // but we don't currently know any good way of detecting exactly how long :(\n conn.addEventListener(\n \"negotiationneeded\",\n debounce(ev => {\n debug(\"Sending new offer for handle: %o\", handle);\n var offer = conn.createOffer().then(this.configurePublisherSdp).then(this.fixSafariIceUFrag);\n var local = offer.then(o => conn.setLocalDescription(o));\n var remote = offer;\n\n remote = remote\n .then(this.fixSafariIceUFrag)\n .then(j => handle.sendJsep(j))\n .then(r => conn.setRemoteDescription(r.jsep));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating offer: %o\", e));\n })\n );\n handle.on(\n \"event\",\n debounce(ev => {\n var jsep = ev.jsep;\n if (jsep && jsep.type == \"offer\") {\n debug(\"Accepting new offer for handle: %o\", handle);\n var answer = conn\n .setRemoteDescription(this.configureSubscriberSdp(jsep))\n .then(_ => conn.createAnswer())\n .then(this.fixSafariIceUFrag);\n var local = answer.then(a => conn.setLocalDescription(a));\n var remote = answer.then(j => handle.sendJsep(j));\n return Promise.all([local, remote]).catch(e => error(\"Error negotiating answer: %o\", e));\n } else {\n // some other kind of event, nothing to do\n return null;\n }\n })\n );\n }\n\n async createPublisher() {\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(\"pub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(\"pub waiting for data channels & webrtcup\");\n var webrtcup = new Promise(resolve => handle.on(\"webrtcup\", resolve));\n\n // Unreliable datachannel: sending and receiving component updates.\n // Reliable datachannel: sending and recieving entity instantiations.\n var reliableChannel = conn.createDataChannel(\"reliable\", { ordered: true });\n var unreliableChannel = conn.createDataChannel(\"unreliable\", {\n ordered: false,\n maxRetransmits: 0\n });\n\n reliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-reliable\"));\n unreliableChannel.addEventListener(\"message\", e => this.onDataChannelMessage(e, \"janus-unreliable\"));\n\n await webrtcup;\n await untilDataChannelOpen(reliableChannel);\n await untilDataChannelOpen(unreliableChannel);\n\n // doing this here is sort of a hack around chrome renegotiation weirdness --\n // if we do it prior to webrtcup, chrome on gear VR will sometimes put a\n // renegotiation offer in flight while the first offer was still being\n // processed by janus. we should find some more principled way to figure out\n // when janus is done in the future.\n if (this.localMediaStream) {\n this.localMediaStream.getTracks().forEach(track => {\n conn.addTrack(track, this.localMediaStream);\n });\n }\n\n // Handle all of the join and leave events.\n handle.on(\"event\", ev => {\n var data = ev.plugindata.data;\n if (data.event == \"join\" && data.room_id == this.room) {\n if (this.delayedReconnectTimeout) {\n // Don't create a new RTCPeerConnection, all RTCPeerConnection will be closed in less than 10s.\n return;\n }\n this.addOccupant(data.user_id);\n } else if (data.event == \"leave\" && data.room_id == this.room) {\n this.removeOccupant(data.user_id);\n } else if (data.event == \"blocked\") {\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: data.by } }));\n } else if (data.event == \"unblocked\") {\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: data.by } }));\n } else if (data.event === \"data\") {\n this.onData(JSON.parse(data.body), \"janus-event\");\n }\n });\n\n debug(\"pub waiting for join\");\n\n // Send join message to janus. Listen for join/leave messages. Automatically subscribe to all users' WebRTC data.\n var message = await this.sendJoin(handle, {\n notifications: true,\n data: true\n });\n\n if (!message.plugindata.data.success) {\n const err = message.plugindata.data.error;\n console.error(err);\n // We may get here because of an expired JWT.\n // Close the connection ourself otherwise janus will close it after\n // session_timeout because we didn't send any keepalive and this will\n // trigger a delayed reconnect because of the iceconnectionstatechange\n // listener for failure state.\n // Even if the app code calls disconnect in case of error, disconnect\n // won't close the peer connection because this.publisher is not set.\n conn.close();\n throw err;\n }\n\n var initialOccupants = message.plugindata.data.response.users[this.room] || [];\n\n if (initialOccupants.includes(this.clientId)) {\n console.warn(\"Janus still has previous session for this client. Reconnecting in 10s.\");\n this.performDelayedReconnect();\n }\n\n debug(\"publisher ready\");\n return {\n handle,\n initialOccupants,\n reliableChannel,\n unreliableChannel,\n conn\n };\n }\n\n configurePublisherSdp(jsep) {\n jsep.sdp = jsep.sdp.replace(/a=fmtp:(109|111).*\\r\\n/g, (line, pt) => {\n const parameters = Object.assign(sdpUtils.parseFmtp(line), OPUS_PARAMETERS);\n return sdpUtils.writeFmtp({ payloadType: pt, parameters: parameters });\n });\n return jsep;\n }\n\n configureSubscriberSdp(jsep) {\n // todo: consider cleaning up these hacks to use sdputils\n if (!isH264VideoSupported) {\n if (navigator.userAgent.indexOf(\"HeadlessChrome\") !== -1) {\n // HeadlessChrome (e.g. puppeteer) doesn't support webrtc video streams, so we remove those lines from the SDP.\n jsep.sdp = jsep.sdp.replace(/m=video[^]*m=/, \"m=\");\n }\n }\n\n // TODO: Hack to get video working on Chrome for Android. https://groups.google.com/forum/#!topic/mozilla.dev.media/Ye29vuMTpo8\n if (navigator.userAgent.indexOf(\"Android\") === -1) {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n\"\n );\n } else {\n jsep.sdp = jsep.sdp.replace(\n \"a=rtcp-fb:107 goog-remb\\r\\n\",\n \"a=rtcp-fb:107 goog-remb\\r\\na=rtcp-fb:107 transport-cc\\r\\na=fmtp:107 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f\\r\\n\"\n );\n }\n return jsep;\n }\n\n async fixSafariIceUFrag(jsep) {\n // Safari produces a \\n instead of an \\r\\n for the ice-ufrag. See https://github.com/meetecho/janus-gateway/issues/1818\n jsep.sdp = jsep.sdp.replace(/[^\\r]\\na=ice-ufrag/g, \"\\r\\na=ice-ufrag\");\n return jsep\n }\n\n async createSubscriber(occupantId, maxRetries = 5) {\n if (this.leftOccupants.has(occupantId)) {\n console.warn(occupantId + \": cancelled occupant connection, occupant left before subscription negotation.\");\n return null;\n }\n\n var handle = new mj.JanusPluginHandle(this.session);\n var conn = new RTCPeerConnection(this.peerConnectionConfig || DEFAULT_PEER_CONNECTION_CONFIG);\n\n debug(occupantId + \": sub waiting for sfu\");\n await handle.attach(\"janus.plugin.sfu\");\n\n this.associate(conn, handle);\n\n debug(occupantId + \": sub waiting for join\");\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after attach\");\n return null;\n }\n\n let webrtcFailed = false;\n\n const webrtcup = new Promise(resolve => {\n const leftInterval = setInterval(() => {\n if (this.leftOccupants.has(occupantId)) {\n clearInterval(leftInterval);\n resolve();\n }\n }, 1000);\n\n const timeout = setTimeout(() => {\n clearInterval(leftInterval);\n webrtcFailed = true;\n resolve();\n }, SUBSCRIBE_TIMEOUT_MS);\n\n handle.on(\"webrtcup\", () => {\n clearTimeout(timeout);\n clearInterval(leftInterval);\n resolve();\n });\n });\n\n // Send join message to janus. Don't listen for join/leave messages. Subscribe to the occupant's media.\n // Janus should send us an offer for this occupant's media in response to this.\n await this.sendJoin(handle, { media: occupantId });\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancelled occupant connection, occupant left after join\");\n return null;\n }\n\n debug(occupantId + \": sub waiting for webrtcup\");\n await webrtcup;\n\n if (this.leftOccupants.has(occupantId)) {\n conn.close();\n console.warn(occupantId + \": cancel occupant connection, occupant left during or after webrtcup\");\n return null;\n }\n\n if (webrtcFailed) {\n conn.close();\n if (maxRetries > 0) {\n console.warn(occupantId + \": webrtc up timed out, retrying\");\n return this.createSubscriber(occupantId, maxRetries - 1);\n } else {\n console.warn(occupantId + \": webrtc up timed out\");\n return null;\n }\n }\n\n if (isSafari && !this._iOSHackDelayedInitialPeer) {\n // HACK: the first peer on Safari during page load can fail to work if we don't\n // wait some time before continuing here. See: https://github.com/mozilla/hubs/pull/1692\n await (new Promise((resolve) => setTimeout(resolve, 3000)));\n this._iOSHackDelayedInitialPeer = true;\n }\n\n var mediaStream = new MediaStream();\n var receivers = conn.getReceivers();\n receivers.forEach(receiver => {\n if (receiver.track) {\n mediaStream.addTrack(receiver.track);\n }\n });\n if (mediaStream.getTracks().length === 0) {\n mediaStream = null;\n }\n\n debug(occupantId + \": subscriber ready\");\n return {\n handle,\n mediaStream,\n conn\n };\n }\n\n sendJoin(handle, subscribe) {\n return handle.sendMessage({\n kind: \"join\",\n room_id: this.room,\n user_id: this.clientId,\n subscribe,\n token: this.joinToken\n });\n }\n\n toggleFreeze() {\n if (this.frozen) {\n this.unfreeze();\n } else {\n this.freeze();\n }\n }\n\n freeze() {\n this.frozen = true;\n }\n\n unfreeze() {\n this.frozen = false;\n this.flushPendingUpdates();\n }\n\n dataForUpdateMultiMessage(networkId, message) {\n // \"d\" is an array of entity datas, where each item in the array represents a unique entity and contains\n // metadata for the entity, and an array of components that have been updated on the entity.\n // This method finds the data corresponding to the given networkId.\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n const data = message.data.d[i];\n\n if (data.networkId === networkId) {\n return data;\n }\n }\n\n return null;\n }\n\n getPendingData(networkId, message) {\n if (!message) return null;\n\n let data = message.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, message) : message.data;\n\n // Ignore messages relating to users who have disconnected since freezing, their entities\n // will have aleady been removed by NAF.\n // Note that delete messages have no \"owner\" so we have to check for that as well.\n if (data.owner && !this.occupants[data.owner]) return null;\n\n // Ignore messages from users that we may have blocked while frozen.\n if (data.owner && this.blockedClients.has(data.owner)) return null;\n\n return data\n }\n\n // Used externally\n getPendingDataForNetworkId(networkId) {\n return this.getPendingData(networkId, this.frozenUpdates.get(networkId));\n }\n\n flushPendingUpdates() {\n for (const [networkId, message] of this.frozenUpdates) {\n let data = this.getPendingData(networkId, message);\n if (!data) continue;\n\n // Override the data type on \"um\" messages types, since we extract entity updates from \"um\" messages into\n // individual frozenUpdates in storeSingleMessage.\n const dataType = message.dataType === \"um\" ? \"u\" : message.dataType;\n\n this.onOccupantMessage(null, dataType, data, message.source);\n }\n this.frozenUpdates.clear();\n }\n\n storeMessage(message) {\n if (message.dataType === \"um\") { // UpdateMulti\n for (let i = 0, l = message.data.d.length; i < l; i++) {\n this.storeSingleMessage(message, i);\n }\n } else {\n this.storeSingleMessage(message);\n }\n }\n\n storeSingleMessage(message, index) {\n const data = index !== undefined ? message.data.d[index] : message.data;\n const dataType = message.dataType;\n const source = message.source;\n\n const networkId = data.networkId;\n\n if (!this.frozenUpdates.has(networkId)) {\n this.frozenUpdates.set(networkId, message);\n } else {\n const storedMessage = this.frozenUpdates.get(networkId);\n const storedData = storedMessage.dataType === \"um\" ? this.dataForUpdateMultiMessage(networkId, storedMessage) : storedMessage.data;\n\n // Avoid updating components if the entity data received did not come from the current owner.\n const isOutdatedMessage = data.lastOwnerTime < storedData.lastOwnerTime;\n const isContemporaneousMessage = data.lastOwnerTime === storedData.lastOwnerTime;\n if (isOutdatedMessage || (isContemporaneousMessage && storedData.owner > data.owner)) {\n return;\n }\n\n if (dataType === \"r\") {\n const createdWhileFrozen = storedData && storedData.isFirstSync;\n if (createdWhileFrozen) {\n // If the entity was created and deleted while frozen, don't bother conveying anything to the consumer.\n this.frozenUpdates.delete(networkId);\n } else {\n // Delete messages override any other messages for this entity\n this.frozenUpdates.set(networkId, message);\n }\n } else {\n // merge in component updates\n if (storedData.components && data.components) {\n Object.assign(storedData.components, data.components);\n }\n }\n }\n }\n\n onDataChannelMessage(e, source) {\n this.onData(JSON.parse(e.data), source);\n }\n\n onData(message, source) {\n if (debug.enabled) {\n debug(`DC in: ${message}`);\n }\n\n if (!message.dataType) return;\n\n message.source = source;\n\n if (this.frozen) {\n this.storeMessage(message);\n } else {\n this.onOccupantMessage(null, message.dataType, message.data, message.source);\n }\n }\n\n shouldStartConnectionTo(client) {\n return true;\n }\n\n startStreamConnection(client) {}\n\n closeStreamConnection(client) {}\n\n getConnectStatus(clientId) {\n return this.occupants[clientId] ? NAF.adapters.IS_CONNECTED : NAF.adapters.NOT_CONNECTED;\n }\n\n async updateTimeOffset() {\n if (this.isDisconnected()) return;\n\n const clientSentTime = Date.now();\n\n const res = await fetch(document.location.href, {\n method: \"HEAD\",\n cache: \"no-cache\"\n });\n\n const precision = 1000;\n const serverReceivedTime = new Date(res.headers.get(\"Date\")).getTime() + precision / 2;\n const clientReceivedTime = Date.now();\n const serverTime = serverReceivedTime + (clientReceivedTime - clientSentTime) / 2;\n const timeOffset = serverTime - clientReceivedTime;\n\n this.serverTimeRequests++;\n\n if (this.serverTimeRequests <= 10) {\n this.timeOffsets.push(timeOffset);\n } else {\n this.timeOffsets[this.serverTimeRequests % 10] = timeOffset;\n }\n\n this.avgTimeOffset = this.timeOffsets.reduce((acc, offset) => (acc += offset), 0) / this.timeOffsets.length;\n\n if (this.serverTimeRequests > 10) {\n debug(`new server time offset: ${this.avgTimeOffset}ms`);\n setTimeout(() => this.updateTimeOffset(), 5 * 60 * 1000); // Sync clock every 5 minutes.\n } else {\n this.updateTimeOffset();\n }\n }\n\n getServerTime() {\n return Date.now() + this.avgTimeOffset;\n }\n\n getMediaStream(clientId, type = \"audio\") {\n if (this.mediaStreams[clientId]) {\n debug(`Already had ${type} for ${clientId}`);\n return Promise.resolve(this.mediaStreams[clientId][type]);\n } else {\n debug(`Waiting on ${type} for ${clientId}`);\n if (!this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.set(clientId, {});\n\n const audioPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).audio = { resolve, reject };\n });\n const videoPromise = new Promise((resolve, reject) => {\n this.pendingMediaRequests.get(clientId).video = { resolve, reject };\n });\n\n this.pendingMediaRequests.get(clientId).audio.promise = audioPromise;\n this.pendingMediaRequests.get(clientId).video.promise = videoPromise;\n\n audioPromise.catch(e => console.warn(`${clientId} getMediaStream Audio Error`, e));\n videoPromise.catch(e => console.warn(`${clientId} getMediaStream Video Error`, e));\n }\n return this.pendingMediaRequests.get(clientId)[type].promise;\n }\n }\n\n setMediaStream(clientId, stream) {\n // Safari doesn't like it when you use single a mixed media stream where one of the tracks is inactive, so we\n // split the tracks into two streams.\n const audioStream = new MediaStream();\n try {\n stream.getAudioTracks().forEach(track => audioStream.addTrack(track));\n\n } catch(e) {\n console.warn(`${clientId} setMediaStream Audio Error`, e);\n }\n const videoStream = new MediaStream();\n try {\n stream.getVideoTracks().forEach(track => videoStream.addTrack(track));\n\n } catch (e) {\n console.warn(`${clientId} setMediaStream Video Error`, e);\n }\n\n this.mediaStreams[clientId] = { audio: audioStream, video: videoStream };\n\n // Resolve the promise for the user's media stream if it exists.\n if (this.pendingMediaRequests.has(clientId)) {\n this.pendingMediaRequests.get(clientId).audio.resolve(audioStream);\n this.pendingMediaRequests.get(clientId).video.resolve(videoStream);\n }\n }\n\n async setLocalMediaStream(stream) {\n // our job here is to make sure the connection winds up with RTP senders sending the stuff in this stream,\n // and not the stuff that isn't in this stream. strategy is to replace existing tracks if we can, add tracks\n // that we can't replace, and disable tracks that don't exist anymore.\n\n // note that we don't ever remove a track from the stream -- since Janus doesn't support Unified Plan, we absolutely\n // can't wind up with a SDP that has >1 audio or >1 video tracks, even if one of them is inactive (what you get if\n // you remove a track from an existing stream.)\n if (this.publisher && this.publisher.conn) {\n const existingSenders = this.publisher.conn.getSenders();\n const newSenders = [];\n const tracks = stream.getTracks();\n\n for (let i = 0; i < tracks.length; i++) {\n const t = tracks[i];\n const sender = existingSenders.find(s => s.track != null && s.track.kind == t.kind);\n\n if (sender != null) {\n if (sender.replaceTrack) {\n await sender.replaceTrack(t);\n\n // Workaround https://bugzilla.mozilla.org/show_bug.cgi?id=1576771\n if (t.kind === \"video\" && t.enabled && navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {\n t.enabled = false;\n setTimeout(() => t.enabled = true, 1000);\n }\n } else {\n // Fallback for browsers that don't support replaceTrack. At this time of this writing\n // most browsers support it, and testing this code path seems to not work properly\n // in Chrome anymore.\n stream.removeTrack(sender.track);\n stream.addTrack(t);\n }\n newSenders.push(sender);\n } else {\n newSenders.push(this.publisher.conn.addTrack(t, stream));\n }\n }\n existingSenders.forEach(s => {\n if (!newSenders.includes(s)) {\n s.track.enabled = false;\n }\n });\n }\n this.localMediaStream = stream;\n this.setMediaStream(this.clientId, stream);\n }\n\n enableMicrophone(enabled) {\n if (this.publisher && this.publisher.conn) {\n this.publisher.conn.getSenders().forEach(s => {\n if (s.track.kind == \"audio\") {\n s.track.enabled = enabled;\n }\n });\n }\n }\n\n sendData(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.unreliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n sendDataGuaranteed(clientId, dataType, data) {\n if (!this.publisher) {\n console.warn(\"sendDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }), whom: clientId });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ clientId, dataType, data }));\n break;\n default:\n this.reliableTransport(clientId, dataType, data);\n break;\n }\n }\n }\n\n broadcastData(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastData called without a publisher\");\n } else {\n switch (this.unreliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.unreliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.unreliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n broadcastDataGuaranteed(dataType, data) {\n if (!this.publisher) {\n console.warn(\"broadcastDataGuaranteed called without a publisher\");\n } else {\n switch (this.reliableTransport) {\n case \"websocket\":\n this.publisher.handle.sendMessage({ kind: \"data\", body: JSON.stringify({ dataType, data }) });\n break;\n case \"datachannel\":\n this.publisher.reliableChannel.send(JSON.stringify({ dataType, data }));\n break;\n default:\n this.reliableTransport(undefined, dataType, data);\n break;\n }\n }\n }\n\n kick(clientId, permsToken) {\n return this.publisher.handle.sendMessage({ kind: \"kick\", room_id: this.room, user_id: clientId, token: permsToken }).then(() => {\n document.body.dispatchEvent(new CustomEvent(\"kicked\", { detail: { clientId: clientId } }));\n });\n }\n\n block(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"block\", whom: clientId }).then(() => {\n this.blockedClients.set(clientId, true);\n document.body.dispatchEvent(new CustomEvent(\"blocked\", { detail: { clientId: clientId } }));\n });\n }\n\n unblock(clientId) {\n return this.publisher.handle.sendMessage({ kind: \"unblock\", whom: clientId }).then(() => {\n this.blockedClients.delete(clientId);\n document.body.dispatchEvent(new CustomEvent(\"unblocked\", { detail: { clientId: clientId } }));\n });\n }\n}\n\nNAF.adapters.register(\"janus\", JanusAdapter);\n\nmodule.exports = JanusAdapter;\n","/**\n * Represents a handle to a single Janus plugin on a Janus session. Each WebRTC connection to the Janus server will be\n * associated with a single handle. Once attached to the server, this handle will be given a unique ID which should be\n * used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#handles.\n **/\nfunction JanusPluginHandle(session) {\n this.session = session;\n this.id = undefined;\n}\n\n/** Attaches this handle to the Janus server and sets its ID. **/\nJanusPluginHandle.prototype.attach = function(plugin) {\n var payload = { plugin: plugin, \"force-bundle\": true, \"force-rtcp-mux\": true };\n return this.session.send(\"attach\", payload).then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/** Detaches this handle. **/\nJanusPluginHandle.prototype.detach = function() {\n return this.send(\"detach\");\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this plugin handle with the\n * `janus` attribute equal to `ev`.\n **/\nJanusPluginHandle.prototype.on = function(ev, callback) {\n return this.session.on(ev, signal => {\n if (signal.sender == this.id) {\n callback(signal);\n }\n });\n};\n\n/**\n * Sends a signal associated with this handle. Signals should be JSON-serializable objects. Returns a promise that will\n * be resolved or rejected when a response to this signal is received, or when no response is received within the\n * session timeout.\n **/\nJanusPluginHandle.prototype.send = function(type, signal) {\n return this.session.send(type, Object.assign({ handle_id: this.id }, signal));\n};\n\n/** Sends a plugin-specific message associated with this handle. **/\nJanusPluginHandle.prototype.sendMessage = function(body) {\n return this.send(\"message\", { body: body });\n};\n\n/** Sends a JSEP offer or answer associated with this handle. **/\nJanusPluginHandle.prototype.sendJsep = function(jsep) {\n return this.send(\"message\", { body: {}, jsep: jsep });\n};\n\n/** Sends an ICE trickle candidate associated with this handle. **/\nJanusPluginHandle.prototype.sendTrickle = function(candidate) {\n return this.send(\"trickle\", { candidate: candidate });\n};\n\n/**\n * Represents a Janus session -- a Janus context from within which you can open multiple handles and connections. Once\n * created, this session will be given a unique ID which should be used to associate it with future signalling messages.\n *\n * See https://janus.conf.meetecho.com/docs/rest.html#sessions.\n **/\nfunction JanusSession(output, options) {\n this.output = output;\n this.id = undefined;\n this.nextTxId = 0;\n this.txns = {};\n this.eventHandlers = {};\n this.options = Object.assign({\n verbose: false,\n timeoutMs: 10000,\n keepaliveMs: 30000\n }, options);\n}\n\n/** Creates this session on the Janus server and sets its ID. **/\nJanusSession.prototype.create = function() {\n return this.send(\"create\").then(resp => {\n this.id = resp.data.id;\n return resp;\n });\n};\n\n/**\n * Destroys this session. Note that upon destruction, Janus will also close the signalling transport (if applicable) and\n * any open WebRTC connections.\n **/\nJanusSession.prototype.destroy = function() {\n return this.send(\"destroy\").then((resp) => {\n this.dispose();\n return resp;\n });\n};\n\n/**\n * Disposes of this session in a way such that no further incoming signalling messages will be processed.\n * Outstanding transactions will be rejected.\n **/\nJanusSession.prototype.dispose = function() {\n this._killKeepalive();\n this.eventHandlers = {};\n for (var txId in this.txns) {\n if (this.txns.hasOwnProperty(txId)) {\n var txn = this.txns[txId];\n clearTimeout(txn.timeout);\n txn.reject(new Error(\"Janus session was disposed.\"));\n delete this.txns[txId];\n }\n }\n};\n\n/**\n * Whether this signal represents an error, and the associated promise (if any) should be rejected.\n * Users should override this to handle any custom plugin-specific error conventions.\n **/\nJanusSession.prototype.isError = function(signal) {\n return signal.janus === \"error\";\n};\n\n/** Registers a callback to be fired upon the reception of any incoming Janus signals for this session with the\n * `janus` attribute equal to `ev`.\n **/\nJanusSession.prototype.on = function(ev, callback) {\n var handlers = this.eventHandlers[ev];\n if (handlers == null) {\n handlers = this.eventHandlers[ev] = [];\n }\n handlers.push(callback);\n};\n\n/**\n * Callback for receiving JSON signalling messages pertinent to this session. If the signals are responses to previously\n * sent signals, the promises for the outgoing signals will be resolved or rejected appropriately with this signal as an\n * argument.\n *\n * External callers should call this function every time a new signal arrives on the transport; for example, in a\n * WebSocket's `message` event, or when a new datum shows up in an HTTP long-polling response.\n **/\nJanusSession.prototype.receive = function(signal) {\n if (this.options.verbose) {\n this._logIncoming(signal);\n }\n if (signal.session_id != this.id) {\n console.warn(\"Incorrect session ID received in Janus signalling message: was \" + signal.session_id + \", expected \" + this.id + \".\");\n }\n\n var responseType = signal.janus;\n var handlers = this.eventHandlers[responseType];\n if (handlers != null) {\n for (var i = 0; i < handlers.length; i++) {\n handlers[i](signal);\n }\n }\n\n if (signal.transaction != null) {\n var txn = this.txns[signal.transaction];\n if (txn == null) {\n // this is a response to a transaction that wasn't caused via JanusSession.send, or a plugin replied twice to a\n // single request, or the session was disposed, or something else that isn't under our purview; that's fine\n return;\n }\n\n if (responseType === \"ack\" && txn.type == \"message\") {\n // this is an ack of an asynchronously-processed plugin request, we should wait to resolve the promise until the\n // actual response comes in\n return;\n }\n\n clearTimeout(txn.timeout);\n\n delete this.txns[signal.transaction];\n (this.isError(signal) ? txn.reject : txn.resolve)(signal);\n }\n};\n\n/**\n * Sends a signal associated with this session, beginning a new transaction. Returns a promise that will be resolved or\n * rejected when a response is received in the same transaction, or when no response is received within the session\n * timeout.\n **/\nJanusSession.prototype.send = function(type, signal) {\n signal = Object.assign({ transaction: (this.nextTxId++).toString() }, signal);\n return new Promise((resolve, reject) => {\n var timeout = null;\n if (this.options.timeoutMs) {\n timeout = setTimeout(() => {\n delete this.txns[signal.transaction];\n reject(new Error(\"Signalling transaction with txid \" + signal.transaction + \" timed out.\"));\n }, this.options.timeoutMs);\n }\n this.txns[signal.transaction] = { resolve: resolve, reject: reject, timeout: timeout, type: type };\n this._transmit(type, signal);\n });\n};\n\nJanusSession.prototype._transmit = function(type, signal) {\n signal = Object.assign({ janus: type }, signal);\n\n if (this.id != null) { // this.id is undefined in the special case when we're sending the session create message\n signal = Object.assign({ session_id: this.id }, signal);\n }\n\n if (this.options.verbose) {\n this._logOutgoing(signal);\n }\n\n this.output(JSON.stringify(signal));\n this._resetKeepalive();\n};\n\nJanusSession.prototype._logOutgoing = function(signal) {\n var kind = signal.janus;\n if (kind === \"message\" && signal.jsep) {\n kind = signal.jsep.type;\n }\n var message = \"> Outgoing Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \";\n console.debug(\"%c\" + message, \"color: #040\", signal);\n};\n\nJanusSession.prototype._logIncoming = function(signal) {\n var kind = signal.janus;\n var message = signal.transaction ?\n \"< Incoming Janus \" + (kind || \"signal\") + \" (#\" + signal.transaction + \"): \" :\n \"< Incoming Janus \" + (kind || \"signal\") + \": \";\n console.debug(\"%c\" + message, \"color: #004\", signal);\n};\n\nJanusSession.prototype._sendKeepalive = function() {\n return this.send(\"keepalive\");\n};\n\nJanusSession.prototype._killKeepalive = function() {\n clearTimeout(this.keepaliveTimeout);\n};\n\nJanusSession.prototype._resetKeepalive = function() {\n this._killKeepalive();\n if (this.options.keepaliveMs) {\n this.keepaliveTimeout = setTimeout(() => {\n this._sendKeepalive().catch(e => console.error(\"Error received from keepalive: \", e));\n }, this.options.keepaliveMs);\n }\n};\n\nmodule.exports = {\n JanusPluginHandle,\n JanusSession\n};\n","/* eslint-env node */\n'use strict';\n\n// SDP helpers.\nconst SDPUtils = {};\n\n// Generate an alphanumeric identifier for cname or mids.\n// TODO: use UUIDs instead? https://gist.github.com/jed/982883\nSDPUtils.generateIdentifier = function() {\n return Math.random().toString(36).substr(2, 10);\n};\n\n// The RTCP CNAME used by all peerconnections from the same JS.\nSDPUtils.localCName = SDPUtils.generateIdentifier();\n\n// Splits SDP into lines, dealing with both CRLF and LF.\nSDPUtils.splitLines = function(blob) {\n return blob.trim().split('\\n').map(line => line.trim());\n};\n// Splits SDP into sessionpart and mediasections. Ensures CRLF.\nSDPUtils.splitSections = function(blob) {\n const parts = blob.split('\\nm=');\n return parts.map((part, index) => (index > 0 ?\n 'm=' + part : part).trim() + '\\r\\n');\n};\n\n// Returns the session description.\nSDPUtils.getDescription = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n return sections && sections[0];\n};\n\n// Returns the individual media sections.\nSDPUtils.getMediaSections = function(blob) {\n const sections = SDPUtils.splitSections(blob);\n sections.shift();\n return sections;\n};\n\n// Returns lines that start with a certain prefix.\nSDPUtils.matchPrefix = function(blob, prefix) {\n return SDPUtils.splitLines(blob).filter(line => line.indexOf(prefix) === 0);\n};\n\n// Parses an ICE candidate line. Sample input:\n// candidate:702786350 2 udp 41819902 8.8.8.8 60769 typ relay raddr 8.8.8.8\n// rport 55996\"\n// Input can be prefixed with a=.\nSDPUtils.parseCandidate = function(line) {\n let parts;\n // Parse both variants.\n if (line.indexOf('a=candidate:') === 0) {\n parts = line.substring(12).split(' ');\n } else {\n parts = line.substring(10).split(' ');\n }\n\n const candidate = {\n foundation: parts[0],\n component: {1: 'rtp', 2: 'rtcp'}[parts[1]] || parts[1],\n protocol: parts[2].toLowerCase(),\n priority: parseInt(parts[3], 10),\n ip: parts[4],\n address: parts[4], // address is an alias for ip.\n port: parseInt(parts[5], 10),\n // skip parts[6] == 'typ'\n type: parts[7],\n };\n\n for (let i = 8; i < parts.length; i += 2) {\n switch (parts[i]) {\n case 'raddr':\n candidate.relatedAddress = parts[i + 1];\n break;\n case 'rport':\n candidate.relatedPort = parseInt(parts[i + 1], 10);\n break;\n case 'tcptype':\n candidate.tcpType = parts[i + 1];\n break;\n case 'ufrag':\n candidate.ufrag = parts[i + 1]; // for backward compatibility.\n candidate.usernameFragment = parts[i + 1];\n break;\n default: // extension handling, in particular ufrag. Don't overwrite.\n if (candidate[parts[i]] === undefined) {\n candidate[parts[i]] = parts[i + 1];\n }\n break;\n }\n }\n return candidate;\n};\n\n// Translates a candidate object into SDP candidate attribute.\n// This does not include the a= prefix!\nSDPUtils.writeCandidate = function(candidate) {\n const sdp = [];\n sdp.push(candidate.foundation);\n\n const component = candidate.component;\n if (component === 'rtp') {\n sdp.push(1);\n } else if (component === 'rtcp') {\n sdp.push(2);\n } else {\n sdp.push(component);\n }\n sdp.push(candidate.protocol.toUpperCase());\n sdp.push(candidate.priority);\n sdp.push(candidate.address || candidate.ip);\n sdp.push(candidate.port);\n\n const type = candidate.type;\n sdp.push('typ');\n sdp.push(type);\n if (type !== 'host' && candidate.relatedAddress &&\n candidate.relatedPort) {\n sdp.push('raddr');\n sdp.push(candidate.relatedAddress);\n sdp.push('rport');\n sdp.push(candidate.relatedPort);\n }\n if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {\n sdp.push('tcptype');\n sdp.push(candidate.tcpType);\n }\n if (candidate.usernameFragment || candidate.ufrag) {\n sdp.push('ufrag');\n sdp.push(candidate.usernameFragment || candidate.ufrag);\n }\n return 'candidate:' + sdp.join(' ');\n};\n\n// Parses an ice-options line, returns an array of option tags.\n// Sample input:\n// a=ice-options:foo bar\nSDPUtils.parseIceOptions = function(line) {\n return line.substr(14).split(' ');\n};\n\n// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:\n// a=rtpmap:111 opus/48000/2\nSDPUtils.parseRtpMap = function(line) {\n let parts = line.substr(9).split(' ');\n const parsed = {\n payloadType: parseInt(parts.shift(), 10), // was: id\n };\n\n parts = parts[0].split('/');\n\n parsed.name = parts[0];\n parsed.clockRate = parseInt(parts[1], 10); // was: clockrate\n parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;\n // legacy alias, got renamed back to channels in ORTC.\n parsed.numChannels = parsed.channels;\n return parsed;\n};\n\n// Generates a rtpmap line from RTCRtpCodecCapability or\n// RTCRtpCodecParameters.\nSDPUtils.writeRtpMap = function(codec) {\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n const channels = codec.channels || codec.numChannels || 1;\n return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +\n (channels !== 1 ? '/' + channels : '') + '\\r\\n';\n};\n\n// Parses a extmap line (headerextension from RFC 5285). Sample input:\n// a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n// a=extmap:2/sendonly urn:ietf:params:rtp-hdrext:toffset\nSDPUtils.parseExtmap = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n id: parseInt(parts[0], 10),\n direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',\n uri: parts[1],\n };\n};\n\n// Generates an extmap line from RTCRtpHeaderExtensionParameters or\n// RTCRtpHeaderExtension.\nSDPUtils.writeExtmap = function(headerExtension) {\n return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +\n (headerExtension.direction && headerExtension.direction !== 'sendrecv'\n ? '/' + headerExtension.direction\n : '') +\n ' ' + headerExtension.uri + '\\r\\n';\n};\n\n// Parses a fmtp line, returns dictionary. Sample input:\n// a=fmtp:96 vbr=on;cng=on\n// Also deals with vbr=on; cng=on\nSDPUtils.parseFmtp = function(line) {\n const parsed = {};\n let kv;\n const parts = line.substr(line.indexOf(' ') + 1).split(';');\n for (let j = 0; j < parts.length; j++) {\n kv = parts[j].trim().split('=');\n parsed[kv[0].trim()] = kv[1];\n }\n return parsed;\n};\n\n// Generates a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeFmtp = function(codec) {\n let line = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.parameters && Object.keys(codec.parameters).length) {\n const params = [];\n Object.keys(codec.parameters).forEach(param => {\n if (codec.parameters[param] !== undefined) {\n params.push(param + '=' + codec.parameters[param]);\n } else {\n params.push(param);\n }\n });\n line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\\r\\n';\n }\n return line;\n};\n\n// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:\n// a=rtcp-fb:98 nack rpsi\nSDPUtils.parseRtcpFb = function(line) {\n const parts = line.substr(line.indexOf(' ') + 1).split(' ');\n return {\n type: parts.shift(),\n parameter: parts.join(' '),\n };\n};\n\n// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.\nSDPUtils.writeRtcpFb = function(codec) {\n let lines = '';\n let pt = codec.payloadType;\n if (codec.preferredPayloadType !== undefined) {\n pt = codec.preferredPayloadType;\n }\n if (codec.rtcpFeedback && codec.rtcpFeedback.length) {\n // FIXME: special handling for trr-int?\n codec.rtcpFeedback.forEach(fb => {\n lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +\n (fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +\n '\\r\\n';\n });\n }\n return lines;\n};\n\n// Parses a RFC 5576 ssrc media attribute. Sample input:\n// a=ssrc:3735928559 cname:something\nSDPUtils.parseSsrcMedia = function(line) {\n const sp = line.indexOf(' ');\n const parts = {\n ssrc: parseInt(line.substr(7, sp - 7), 10),\n };\n const colon = line.indexOf(':', sp);\n if (colon > -1) {\n parts.attribute = line.substr(sp + 1, colon - sp - 1);\n parts.value = line.substr(colon + 1);\n } else {\n parts.attribute = line.substr(sp + 1);\n }\n return parts;\n};\n\n// Parse a ssrc-group line (see RFC 5576). Sample input:\n// a=ssrc-group:semantics 12 34\nSDPUtils.parseSsrcGroup = function(line) {\n const parts = line.substr(13).split(' ');\n return {\n semantics: parts.shift(),\n ssrcs: parts.map(ssrc => parseInt(ssrc, 10)),\n };\n};\n\n// Extracts the MID (RFC 5888) from a media section.\n// Returns the MID or undefined if no mid line was found.\nSDPUtils.getMid = function(mediaSection) {\n const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];\n if (mid) {\n return mid.substr(6);\n }\n};\n\n// Parses a fingerprint line for DTLS-SRTP.\nSDPUtils.parseFingerprint = function(line) {\n const parts = line.substr(14).split(' ');\n return {\n algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.\n value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.\n };\n};\n\n// Extracts DTLS parameters from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the fingerprint line as input. See also getIceParameters.\nSDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=fingerprint:');\n // Note: a=setup line is ignored since we use the 'auto' role in Edge.\n return {\n role: 'auto',\n fingerprints: lines.map(SDPUtils.parseFingerprint),\n };\n};\n\n// Serializes DTLS parameters to SDP.\nSDPUtils.writeDtlsParameters = function(params, setupType) {\n let sdp = 'a=setup:' + setupType + '\\r\\n';\n params.fingerprints.forEach(fp => {\n sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\\r\\n';\n });\n return sdp;\n};\n\n// Parses a=crypto lines into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members\nSDPUtils.parseCryptoLine = function(line) {\n const parts = line.substr(9).split(' ');\n return {\n tag: parseInt(parts[0], 10),\n cryptoSuite: parts[1],\n keyParams: parts[2],\n sessionParams: parts.slice(3),\n };\n};\n\nSDPUtils.writeCryptoLine = function(parameters) {\n return 'a=crypto:' + parameters.tag + ' ' +\n parameters.cryptoSuite + ' ' +\n (typeof parameters.keyParams === 'object'\n ? SDPUtils.writeCryptoKeyParams(parameters.keyParams)\n : parameters.keyParams) +\n (parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +\n '\\r\\n';\n};\n\n// Parses the crypto key parameters into\n// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*\nSDPUtils.parseCryptoKeyParams = function(keyParams) {\n if (keyParams.indexOf('inline:') !== 0) {\n return null;\n }\n const parts = keyParams.substr(7).split('|');\n return {\n keyMethod: 'inline',\n keySalt: parts[0],\n lifeTime: parts[1],\n mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,\n mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,\n };\n};\n\nSDPUtils.writeCryptoKeyParams = function(keyParams) {\n return keyParams.keyMethod + ':'\n + keyParams.keySalt +\n (keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +\n (keyParams.mkiValue && keyParams.mkiLength\n ? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength\n : '');\n};\n\n// Extracts all SDES parameters.\nSDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {\n const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=crypto:');\n return lines.map(SDPUtils.parseCryptoLine);\n};\n\n// Parses ICE information from SDP media section or sessionpart.\n// FIXME: for consistency with other functions this should only\n// get the ice-ufrag and ice-pwd lines as input.\nSDPUtils.getIceParameters = function(mediaSection, sessionpart) {\n const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-ufrag:')[0];\n const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,\n 'a=ice-pwd:')[0];\n if (!(ufrag && pwd)) {\n return null;\n }\n return {\n usernameFragment: ufrag.substr(12),\n password: pwd.substr(10),\n };\n};\n\n// Serializes ICE parameters to SDP.\nSDPUtils.writeIceParameters = function(params) {\n let sdp = 'a=ice-ufrag:' + params.usernameFragment + '\\r\\n' +\n 'a=ice-pwd:' + params.password + '\\r\\n';\n if (params.iceLite) {\n sdp += 'a=ice-lite\\r\\n';\n }\n return sdp;\n};\n\n// Parses the SDP media section and returns RTCRtpParameters.\nSDPUtils.parseRtpParameters = function(mediaSection) {\n const description = {\n codecs: [],\n headerExtensions: [],\n fecMechanisms: [],\n rtcp: [],\n };\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n for (let i = 3; i < mline.length; i++) { // find all codecs from mline[3..]\n const pt = mline[i];\n const rtpmapline = SDPUtils.matchPrefix(\n mediaSection, 'a=rtpmap:' + pt + ' ')[0];\n if (rtpmapline) {\n const codec = SDPUtils.parseRtpMap(rtpmapline);\n const fmtps = SDPUtils.matchPrefix(\n mediaSection, 'a=fmtp:' + pt + ' ');\n // Only the first a=fmtp: is considered.\n codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};\n codec.rtcpFeedback = SDPUtils.matchPrefix(\n mediaSection, 'a=rtcp-fb:' + pt + ' ')\n .map(SDPUtils.parseRtcpFb);\n description.codecs.push(codec);\n // parse FEC mechanisms from rtpmap lines.\n switch (codec.name.toUpperCase()) {\n case 'RED':\n case 'ULPFEC':\n description.fecMechanisms.push(codec.name.toUpperCase());\n break;\n default: // only RED and ULPFEC are recognized as FEC mechanisms.\n break;\n }\n }\n }\n SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {\n description.headerExtensions.push(SDPUtils.parseExtmap(line));\n });\n // FIXME: parse rtcp.\n return description;\n};\n\n// Generates parts of the SDP media section describing the capabilities /\n// parameters.\nSDPUtils.writeRtpDescription = function(kind, caps) {\n let sdp = '';\n\n // Build the mline.\n sdp += 'm=' + kind + ' ';\n sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.\n sdp += ' UDP/TLS/RTP/SAVPF ';\n sdp += caps.codecs.map(codec => {\n if (codec.preferredPayloadType !== undefined) {\n return codec.preferredPayloadType;\n }\n return codec.payloadType;\n }).join(' ') + '\\r\\n';\n\n sdp += 'c=IN IP4 0.0.0.0\\r\\n';\n sdp += 'a=rtcp:9 IN IP4 0.0.0.0\\r\\n';\n\n // Add a=rtpmap lines for each codec. Also fmtp and rtcp-fb.\n caps.codecs.forEach(codec => {\n sdp += SDPUtils.writeRtpMap(codec);\n sdp += SDPUtils.writeFmtp(codec);\n sdp += SDPUtils.writeRtcpFb(codec);\n });\n let maxptime = 0;\n caps.codecs.forEach(codec => {\n if (codec.maxptime > maxptime) {\n maxptime = codec.maxptime;\n }\n });\n if (maxptime > 0) {\n sdp += 'a=maxptime:' + maxptime + '\\r\\n';\n }\n\n if (caps.headerExtensions) {\n caps.headerExtensions.forEach(extension => {\n sdp += SDPUtils.writeExtmap(extension);\n });\n }\n // FIXME: write fecMechanisms.\n return sdp;\n};\n\n// Parses the SDP media section and returns an array of\n// RTCRtpEncodingParameters.\nSDPUtils.parseRtpEncodingParameters = function(mediaSection) {\n const encodingParameters = [];\n const description = SDPUtils.parseRtpParameters(mediaSection);\n const hasRed = description.fecMechanisms.indexOf('RED') !== -1;\n const hasUlpfec = description.fecMechanisms.indexOf('ULPFEC') !== -1;\n\n // filter a=ssrc:... cname:, ignore PlanB-msid\n const ssrcs = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(parts => parts.attribute === 'cname');\n const primarySsrc = ssrcs.length > 0 && ssrcs[0].ssrc;\n let secondarySsrc;\n\n const flows = SDPUtils.matchPrefix(mediaSection, 'a=ssrc-group:FID')\n .map(line => {\n const parts = line.substr(17).split(' ');\n return parts.map(part => parseInt(part, 10));\n });\n if (flows.length > 0 && flows[0].length > 1 && flows[0][0] === primarySsrc) {\n secondarySsrc = flows[0][1];\n }\n\n description.codecs.forEach(codec => {\n if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {\n let encParam = {\n ssrc: primarySsrc,\n codecPayloadType: parseInt(codec.parameters.apt, 10),\n };\n if (primarySsrc && secondarySsrc) {\n encParam.rtx = {ssrc: secondarySsrc};\n }\n encodingParameters.push(encParam);\n if (hasRed) {\n encParam = JSON.parse(JSON.stringify(encParam));\n encParam.fec = {\n ssrc: primarySsrc,\n mechanism: hasUlpfec ? 'red+ulpfec' : 'red',\n };\n encodingParameters.push(encParam);\n }\n }\n });\n if (encodingParameters.length === 0 && primarySsrc) {\n encodingParameters.push({\n ssrc: primarySsrc,\n });\n }\n\n // we support both b=AS and b=TIAS but interpret AS as TIAS.\n let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');\n if (bandwidth.length) {\n if (bandwidth[0].indexOf('b=TIAS:') === 0) {\n bandwidth = parseInt(bandwidth[0].substr(7), 10);\n } else if (bandwidth[0].indexOf('b=AS:') === 0) {\n // use formula from JSEP to convert b=AS to TIAS value.\n bandwidth = parseInt(bandwidth[0].substr(5), 10) * 1000 * 0.95\n - (50 * 40 * 8);\n } else {\n bandwidth = undefined;\n }\n encodingParameters.forEach(params => {\n params.maxBitrate = bandwidth;\n });\n }\n return encodingParameters;\n};\n\n// parses http://draft.ortc.org/#rtcrtcpparameters*\nSDPUtils.parseRtcpParameters = function(mediaSection) {\n const rtcpParameters = {};\n\n // Gets the first SSRC. Note that with RTX there might be multiple\n // SSRCs.\n const remoteSsrc = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(obj => obj.attribute === 'cname')[0];\n if (remoteSsrc) {\n rtcpParameters.cname = remoteSsrc.value;\n rtcpParameters.ssrc = remoteSsrc.ssrc;\n }\n\n // Edge uses the compound attribute instead of reducedSize\n // compound is !reducedSize\n const rsize = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-rsize');\n rtcpParameters.reducedSize = rsize.length > 0;\n rtcpParameters.compound = rsize.length === 0;\n\n // parses the rtcp-mux attrіbute.\n // Note that Edge does not support unmuxed RTCP.\n const mux = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-mux');\n rtcpParameters.mux = mux.length > 0;\n\n return rtcpParameters;\n};\n\nSDPUtils.writeRtcpParameters = function(rtcpParameters) {\n let sdp = '';\n if (rtcpParameters.reducedSize) {\n sdp += 'a=rtcp-rsize\\r\\n';\n }\n if (rtcpParameters.mux) {\n sdp += 'a=rtcp-mux\\r\\n';\n }\n if (rtcpParameters.ssrc !== undefined && rtcpParameters.cname) {\n sdp += 'a=ssrc:' + rtcpParameters.ssrc +\n ' cname:' + rtcpParameters.cname + '\\r\\n';\n }\n return sdp;\n};\n\n\n// parses either a=msid: or a=ssrc:... msid lines and returns\n// the id of the MediaStream and MediaStreamTrack.\nSDPUtils.parseMsid = function(mediaSection) {\n let parts;\n const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');\n if (spec.length === 1) {\n parts = spec[0].substr(7).split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n const planB = SDPUtils.matchPrefix(mediaSection, 'a=ssrc:')\n .map(line => SDPUtils.parseSsrcMedia(line))\n .filter(msidParts => msidParts.attribute === 'msid');\n if (planB.length > 0) {\n parts = planB[0].value.split(' ');\n return {stream: parts[0], track: parts[1]};\n }\n};\n\n// SCTP\n// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back\n// to draft-ietf-mmusic-sctp-sdp-05\nSDPUtils.parseSctpDescription = function(mediaSection) {\n const mline = SDPUtils.parseMLine(mediaSection);\n const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');\n let maxMessageSize;\n if (maxSizeLine.length > 0) {\n maxMessageSize = parseInt(maxSizeLine[0].substr(19), 10);\n }\n if (isNaN(maxMessageSize)) {\n maxMessageSize = 65536;\n }\n const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');\n if (sctpPort.length > 0) {\n return {\n port: parseInt(sctpPort[0].substr(12), 10),\n protocol: mline.fmt,\n maxMessageSize,\n };\n }\n const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');\n if (sctpMapLines.length > 0) {\n const parts = sctpMapLines[0]\n .substr(10)\n .split(' ');\n return {\n port: parseInt(parts[0], 10),\n protocol: parts[1],\n maxMessageSize,\n };\n }\n};\n\n// SCTP\n// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers\n// support by now receiving in this format, unless we originally parsed\n// as the draft-ietf-mmusic-sctp-sdp-05 format (indicated by the m-line\n// protocol of DTLS/SCTP -- without UDP/ or TCP/)\nSDPUtils.writeSctpDescription = function(media, sctp) {\n let output = [];\n if (media.protocol !== 'DTLS/SCTP') {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.protocol + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctp-port:' + sctp.port + '\\r\\n',\n ];\n } else {\n output = [\n 'm=' + media.kind + ' 9 ' + media.protocol + ' ' + sctp.port + '\\r\\n',\n 'c=IN IP4 0.0.0.0\\r\\n',\n 'a=sctpmap:' + sctp.port + ' ' + sctp.protocol + ' 65535\\r\\n',\n ];\n }\n if (sctp.maxMessageSize !== undefined) {\n output.push('a=max-message-size:' + sctp.maxMessageSize + '\\r\\n');\n }\n return output.join('');\n};\n\n// Generate a session ID for SDP.\n// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1\n// recommends using a cryptographically random +ve 64-bit value\n// but right now this should be acceptable and within the right range\nSDPUtils.generateSessionId = function() {\n return Math.random().toString().substr(2, 21);\n};\n\n// Write boiler plate for start of SDP\n// sessId argument is optional - if not supplied it will\n// be generated randomly\n// sessVersion is optional and defaults to 2\n// sessUser is optional and defaults to 'thisisadapterortc'\nSDPUtils.writeSessionBoilerplate = function(sessId, sessVer, sessUser) {\n let sessionId;\n const version = sessVer !== undefined ? sessVer : 2;\n if (sessId) {\n sessionId = sessId;\n } else {\n sessionId = SDPUtils.generateSessionId();\n }\n const user = sessUser || 'thisisadapterortc';\n // FIXME: sess-id should be an NTP timestamp.\n return 'v=0\\r\\n' +\n 'o=' + user + ' ' + sessionId + ' ' + version +\n ' IN IP4 127.0.0.1\\r\\n' +\n 's=-\\r\\n' +\n 't=0 0\\r\\n';\n};\n\n// Gets the direction from the mediaSection or the sessionpart.\nSDPUtils.getDirection = function(mediaSection, sessionpart) {\n // Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.\n const lines = SDPUtils.splitLines(mediaSection);\n for (let i = 0; i < lines.length; i++) {\n switch (lines[i]) {\n case 'a=sendrecv':\n case 'a=sendonly':\n case 'a=recvonly':\n case 'a=inactive':\n return lines[i].substr(2);\n default:\n // FIXME: What should happen here?\n }\n }\n if (sessionpart) {\n return SDPUtils.getDirection(sessionpart);\n }\n return 'sendrecv';\n};\n\nSDPUtils.getKind = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const mline = lines[0].split(' ');\n return mline[0].substr(2);\n};\n\nSDPUtils.isRejected = function(mediaSection) {\n return mediaSection.split(' ', 2)[1] === '0';\n};\n\nSDPUtils.parseMLine = function(mediaSection) {\n const lines = SDPUtils.splitLines(mediaSection);\n const parts = lines[0].substr(2).split(' ');\n return {\n kind: parts[0],\n port: parseInt(parts[1], 10),\n protocol: parts[2],\n fmt: parts.slice(3).join(' '),\n };\n};\n\nSDPUtils.parseOLine = function(mediaSection) {\n const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];\n const parts = line.substr(2).split(' ');\n return {\n username: parts[0],\n sessionId: parts[1],\n sessionVersion: parseInt(parts[2], 10),\n netType: parts[3],\n addressType: parts[4],\n address: parts[5],\n };\n};\n\n// a very naive interpretation of a valid SDP.\nSDPUtils.isValidSDP = function(blob) {\n if (typeof blob !== 'string' || blob.length === 0) {\n return false;\n }\n const lines = SDPUtils.splitLines(blob);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {\n return false;\n }\n // TODO: check the modifier a bit more.\n }\n return true;\n};\n\n// Expose public methods.\nif (typeof module === 'object') {\n module.exports = SDPUtils;\n}\n"],"sourceRoot":""} \ No newline at end of file