|
|
/******/ (() => { // webpackBootstrap
|
|
|
/******/ var __webpack_modules__ = ({
|
|
|
|
|
|
/***/ "./node_modules/freeice/index.js":
|
|
|
/*!***************************************!*\
|
|
|
!*** ./node_modules/freeice/index.js ***!
|
|
|
\***************************************/
|
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
/* jshint node: true */
|
|
|
|
|
|
|
|
|
var normalice = __webpack_require__(/*! normalice */ "./node_modules/normalice/index.js");
|
|
|
|
|
|
/**
|
|
|
# freeice
|
|
|
|
|
|
The `freeice` module is a simple way of getting random STUN or TURN server
|
|
|
for your WebRTC application. The list of servers (just STUN at this stage)
|
|
|
were sourced from this [gist](https://gist.github.com/zziuni/3741933).
|
|
|
|
|
|
## Example Use
|
|
|
|
|
|
The following demonstrates how you can use `freeice` with
|
|
|
[rtc-quickconnect](https://github.com/rtc-io/rtc-quickconnect):
|
|
|
|
|
|
<<< examples/quickconnect.js
|
|
|
|
|
|
As the `freeice` module generates ice servers in a list compliant with the
|
|
|
WebRTC spec you will be able to use it with raw `RTCPeerConnection`
|
|
|
constructors and other WebRTC libraries.
|
|
|
|
|
|
## Hey, don't use my STUN/TURN server!
|
|
|
|
|
|
If for some reason your free STUN or TURN server ends up in the
|
|
|
list of servers ([stun](https://github.com/DamonOehlman/freeice/blob/master/stun.json) or
|
|
|
[turn](https://github.com/DamonOehlman/freeice/blob/master/turn.json))
|
|
|
that is used in this module, you can feel
|
|
|
free to open an issue on this repository and those servers will be removed
|
|
|
within 24 hours (or sooner). This is the quickest and probably the most
|
|
|
polite way to have something removed (and provides us some visibility
|
|
|
if someone opens a pull request requesting that a server is added).
|
|
|
|
|
|
## Please add my server!
|
|
|
|
|
|
If you have a server that you wish to add to the list, that's awesome! I'm
|
|
|
sure I speak on behalf of a whole pile of WebRTC developers who say thanks.
|
|
|
To get it into the list, feel free to either open a pull request or if you
|
|
|
find that process a bit daunting then just create an issue requesting
|
|
|
the addition of the server (make sure you provide all the details, and if
|
|
|
you have a Terms of Service then including that in the PR/issue would be
|
|
|
awesome).
|
|
|
|
|
|
## I know of a free server, can I add it?
|
|
|
|
|
|
Sure, if you do your homework and make sure it is ok to use (I'm currently
|
|
|
in the process of reviewing the terms of those STUN servers included from
|
|
|
the original list). If it's ok to go, then please see the previous entry
|
|
|
for how to add it.
|
|
|
|
|
|
## Current List of Servers
|
|
|
|
|
|
* current as at the time of last `README.md` file generation
|
|
|
|
|
|
### STUN
|
|
|
|
|
|
<<< stun.json
|
|
|
|
|
|
### TURN
|
|
|
|
|
|
<<< turn.json
|
|
|
|
|
|
**/
|
|
|
|
|
|
var freeice = function(opts) {
|
|
|
// if a list of servers has been provided, then use it instead of defaults
|
|
|
var servers = {
|
|
|
stun: (opts || {}).stun || __webpack_require__(/*! ./stun.json */ "./node_modules/freeice/stun.json"),
|
|
|
turn: (opts || {}).turn || __webpack_require__(/*! ./turn.json */ "./node_modules/freeice/turn.json")
|
|
|
};
|
|
|
|
|
|
var stunCount = (opts || {}).stunCount || 2;
|
|
|
var turnCount = (opts || {}).turnCount || 0;
|
|
|
var selected;
|
|
|
|
|
|
function getServers(type, count) {
|
|
|
var out = [];
|
|
|
var input = [].concat(servers[type]);
|
|
|
var idx;
|
|
|
|
|
|
while (input.length && out.length < count) {
|
|
|
idx = (Math.random() * input.length) | 0;
|
|
|
out = out.concat(input.splice(idx, 1));
|
|
|
}
|
|
|
|
|
|
return out.map(function(url) {
|
|
|
//If it's a not a string, don't try to "normalice" it otherwise using type:url will screw it up
|
|
|
if ((typeof url !== 'string') && (! (url instanceof String))) {
|
|
|
return url;
|
|
|
} else {
|
|
|
return normalice(type + ':' + url);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// add stun servers
|
|
|
selected = [].concat(getServers('stun', stunCount));
|
|
|
|
|
|
if (turnCount) {
|
|
|
selected = selected.concat(getServers('turn', turnCount));
|
|
|
}
|
|
|
|
|
|
return selected;
|
|
|
};
|
|
|
|
|
|
module.exports = freeice;
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/normalice/index.js":
|
|
|
/*!*****************************************!*\
|
|
|
!*** ./node_modules/normalice/index.js ***!
|
|
|
\*****************************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
/**
|
|
|
# normalice
|
|
|
|
|
|
Normalize an ice server configuration object (or plain old string) into a format
|
|
|
that is usable in all browsers supporting WebRTC. Primarily this module is designed
|
|
|
to help with the transition of the `url` attribute of the configuration object to
|
|
|
the `urls` attribute.
|
|
|
|
|
|
## Example Usage
|
|
|
|
|
|
<<< examples/simple.js
|
|
|
|
|
|
**/
|
|
|
|
|
|
var protocols = [
|
|
|
'stun:',
|
|
|
'turn:'
|
|
|
];
|
|
|
|
|
|
module.exports = function(input) {
|
|
|
var url = (input || {}).url || input;
|
|
|
var protocol;
|
|
|
var parts;
|
|
|
var output = {};
|
|
|
|
|
|
// if we don't have a string url, then allow the input to passthrough
|
|
|
if (typeof url != 'string' && (! (url instanceof String))) {
|
|
|
return input;
|
|
|
}
|
|
|
|
|
|
// trim the url string, and convert to an array
|
|
|
url = url.trim();
|
|
|
|
|
|
// if the protocol is not known, then passthrough
|
|
|
protocol = protocols[protocols.indexOf(url.slice(0, 5))];
|
|
|
if (! protocol) {
|
|
|
return input;
|
|
|
}
|
|
|
|
|
|
// now let's attack the remaining url parts
|
|
|
url = url.slice(5);
|
|
|
parts = url.split('@');
|
|
|
|
|
|
output.username = input.username;
|
|
|
output.credential = input.credential;
|
|
|
// if we have an authentication part, then set the credentials
|
|
|
if (parts.length > 1) {
|
|
|
url = parts[1];
|
|
|
parts = parts[0].split(':');
|
|
|
|
|
|
// add the output credential and username
|
|
|
output.username = parts[0];
|
|
|
output.credential = (input || {}).credential || parts[1] || '';
|
|
|
}
|
|
|
|
|
|
output.url = protocol + url;
|
|
|
output.urls = [ output.url ];
|
|
|
|
|
|
return output;
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/sdp/sdp.js":
|
|
|
/*!*********************************!*\
|
|
|
!*** ./node_modules/sdp/sdp.js ***!
|
|
|
\*********************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
"use strict";
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
// SDP helpers.
|
|
|
const SDPUtils = {};
|
|
|
|
|
|
// Generate an alphanumeric identifier for cname or mids.
|
|
|
// TODO: use UUIDs instead? https://gist.github.com/jed/982883
|
|
|
SDPUtils.generateIdentifier = function() {
|
|
|
return Math.random().toString(36).substring(2, 12);
|
|
|
};
|
|
|
|
|
|
// The RTCP CNAME used by all peerconnections from the same JS.
|
|
|
SDPUtils.localCName = SDPUtils.generateIdentifier();
|
|
|
|
|
|
// Splits SDP into lines, dealing with both CRLF and LF.
|
|
|
SDPUtils.splitLines = function(blob) {
|
|
|
return blob.trim().split('\n').map(line => line.trim());
|
|
|
};
|
|
|
// Splits SDP into sessionpart and mediasections. Ensures CRLF.
|
|
|
SDPUtils.splitSections = function(blob) {
|
|
|
const parts = blob.split('\nm=');
|
|
|
return parts.map((part, index) => (index > 0 ?
|
|
|
'm=' + part : part).trim() + '\r\n');
|
|
|
};
|
|
|
|
|
|
// Returns the session description.
|
|
|
SDPUtils.getDescription = function(blob) {
|
|
|
const sections = SDPUtils.splitSections(blob);
|
|
|
return sections && sections[0];
|
|
|
};
|
|
|
|
|
|
// Returns the individual media sections.
|
|
|
SDPUtils.getMediaSections = function(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(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) {
|
|
|
let parts;
|
|
|
// Parse both variants.
|
|
|
if (line.indexOf('a=candidate:') === 0) {
|
|
|
parts = line.substring(12).split(' ');
|
|
|
} else {
|
|
|
parts = line.substring(10).split(' ');
|
|
|
}
|
|
|
|
|
|
const candidate = {
|
|
|
foundation: parts[0],
|
|
|
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],
|
|
|
};
|
|
|
|
|
|
for (let i = 8; i < parts.length; i += 2) {
|
|
|
switch (parts[i]) {
|
|
|
case 'raddr':
|
|
|
candidate.relatedAddress = parts[i + 1];
|
|
|
break;
|
|
|
case 'rport':
|
|
|
candidate.relatedPort = parseInt(parts[i + 1], 10);
|
|
|
break;
|
|
|
case 'tcptype':
|
|
|
candidate.tcpType = parts[i + 1];
|
|
|
break;
|
|
|
case 'ufrag':
|
|
|
candidate.ufrag = parts[i + 1]; // for backward compatibility.
|
|
|
candidate.usernameFragment = parts[i + 1];
|
|
|
break;
|
|
|
default: // extension handling, in particular ufrag. Don't overwrite.
|
|
|
if (candidate[parts[i]] === undefined) {
|
|
|
candidate[parts[i]] = parts[i + 1];
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
return candidate;
|
|
|
};
|
|
|
|
|
|
// Translates a candidate object into SDP candidate attribute.
|
|
|
// This does not include the a= prefix!
|
|
|
SDPUtils.writeCandidate = function(candidate) {
|
|
|
const sdp = [];
|
|
|
sdp.push(candidate.foundation);
|
|
|
|
|
|
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);
|
|
|
|
|
|
const type = candidate.type;
|
|
|
sdp.push('typ');
|
|
|
sdp.push(type);
|
|
|
if (type !== 'host' && candidate.relatedAddress &&
|
|
|
candidate.relatedPort) {
|
|
|
sdp.push('raddr');
|
|
|
sdp.push(candidate.relatedAddress);
|
|
|
sdp.push('rport');
|
|
|
sdp.push(candidate.relatedPort);
|
|
|
}
|
|
|
if (candidate.tcpType && candidate.protocol.toLowerCase() === 'tcp') {
|
|
|
sdp.push('tcptype');
|
|
|
sdp.push(candidate.tcpType);
|
|
|
}
|
|
|
if (candidate.usernameFragment || candidate.ufrag) {
|
|
|
sdp.push('ufrag');
|
|
|
sdp.push(candidate.usernameFragment || candidate.ufrag);
|
|
|
}
|
|
|
return 'candidate:' + sdp.join(' ');
|
|
|
};
|
|
|
|
|
|
// Parses an ice-options line, returns an array of option tags.
|
|
|
// Sample input:
|
|
|
// a=ice-options:foo bar
|
|
|
SDPUtils.parseIceOptions = function(line) {
|
|
|
return line.substring(14).split(' ');
|
|
|
};
|
|
|
|
|
|
// Parses a rtpmap line, returns RTCRtpCoddecParameters. Sample input:
|
|
|
// a=rtpmap:111 opus/48000/2
|
|
|
SDPUtils.parseRtpMap = function(line) {
|
|
|
let parts = line.substring(9).split(' ');
|
|
|
const parsed = {
|
|
|
payloadType: parseInt(parts.shift(), 10), // was: id
|
|
|
};
|
|
|
|
|
|
parts = parts[0].split('/');
|
|
|
|
|
|
parsed.name = parts[0];
|
|
|
parsed.clockRate = parseInt(parts[1], 10); // was: clockrate
|
|
|
parsed.channels = parts.length === 3 ? parseInt(parts[2], 10) : 1;
|
|
|
// legacy alias, got renamed back to channels in ORTC.
|
|
|
parsed.numChannels = parsed.channels;
|
|
|
return parsed;
|
|
|
};
|
|
|
|
|
|
// Generates a rtpmap line from RTCRtpCodecCapability or
|
|
|
// RTCRtpCodecParameters.
|
|
|
SDPUtils.writeRtpMap = function(codec) {
|
|
|
let pt = codec.payloadType;
|
|
|
if (codec.preferredPayloadType !== undefined) {
|
|
|
pt = codec.preferredPayloadType;
|
|
|
}
|
|
|
const channels = codec.channels || codec.numChannels || 1;
|
|
|
return 'a=rtpmap:' + pt + ' ' + codec.name + '/' + codec.clockRate +
|
|
|
(channels !== 1 ? '/' + channels : '') + '\r\n';
|
|
|
};
|
|
|
|
|
|
// 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) {
|
|
|
const parts = line.substring(9).split(' ');
|
|
|
return {
|
|
|
id: parseInt(parts[0], 10),
|
|
|
direction: parts[0].indexOf('/') > 0 ? parts[0].split('/')[1] : 'sendrecv',
|
|
|
uri: parts[1],
|
|
|
attributes: parts.slice(2).join(' '),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// Generates an extmap line from RTCRtpHeaderExtensionParameters or
|
|
|
// RTCRtpHeaderExtension.
|
|
|
SDPUtils.writeExtmap = function(headerExtension) {
|
|
|
return 'a=extmap:' + (headerExtension.id || headerExtension.preferredId) +
|
|
|
(headerExtension.direction && headerExtension.direction !== 'sendrecv'
|
|
|
? '/' + headerExtension.direction
|
|
|
: '') +
|
|
|
' ' + headerExtension.uri +
|
|
|
(headerExtension.attributes ? ' ' + headerExtension.attributes : '') +
|
|
|
'\r\n';
|
|
|
};
|
|
|
|
|
|
// 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) {
|
|
|
const parsed = {};
|
|
|
let kv;
|
|
|
const parts = line.substring(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 a fmtp line from RTCRtpCodecCapability or RTCRtpCodecParameters.
|
|
|
SDPUtils.writeFmtp = function(codec) {
|
|
|
let line = '';
|
|
|
let pt = codec.payloadType;
|
|
|
if (codec.preferredPayloadType !== undefined) {
|
|
|
pt = codec.preferredPayloadType;
|
|
|
}
|
|
|
if (codec.parameters && Object.keys(codec.parameters).length) {
|
|
|
const params = [];
|
|
|
Object.keys(codec.parameters).forEach(param => {
|
|
|
if (codec.parameters[param] !== undefined) {
|
|
|
params.push(param + '=' + codec.parameters[param]);
|
|
|
} else {
|
|
|
params.push(param);
|
|
|
}
|
|
|
});
|
|
|
line += 'a=fmtp:' + pt + ' ' + params.join(';') + '\r\n';
|
|
|
}
|
|
|
return line;
|
|
|
};
|
|
|
|
|
|
// Parses a rtcp-fb line, returns RTCPRtcpFeedback object. Sample input:
|
|
|
// a=rtcp-fb:98 nack rpsi
|
|
|
SDPUtils.parseRtcpFb = function(line) {
|
|
|
const parts = line.substring(line.indexOf(' ') + 1).split(' ');
|
|
|
return {
|
|
|
type: parts.shift(),
|
|
|
parameter: parts.join(' '),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// Generate a=rtcp-fb lines from RTCRtpCodecCapability or RTCRtpCodecParameters.
|
|
|
SDPUtils.writeRtcpFb = function(codec) {
|
|
|
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(fb => {
|
|
|
lines += 'a=rtcp-fb:' + pt + ' ' + fb.type +
|
|
|
(fb.parameter && fb.parameter.length ? ' ' + fb.parameter : '') +
|
|
|
'\r\n';
|
|
|
});
|
|
|
}
|
|
|
return lines;
|
|
|
};
|
|
|
|
|
|
// Parses a RFC 5576 ssrc media attribute. Sample input:
|
|
|
// a=ssrc:3735928559 cname:something
|
|
|
SDPUtils.parseSsrcMedia = function(line) {
|
|
|
const sp = line.indexOf(' ');
|
|
|
const parts = {
|
|
|
ssrc: parseInt(line.substring(7, sp), 10),
|
|
|
};
|
|
|
const colon = line.indexOf(':', sp);
|
|
|
if (colon > -1) {
|
|
|
parts.attribute = line.substring(sp + 1, colon);
|
|
|
parts.value = line.substring(colon + 1);
|
|
|
} else {
|
|
|
parts.attribute = line.substring(sp + 1);
|
|
|
}
|
|
|
return parts;
|
|
|
};
|
|
|
|
|
|
// Parse a ssrc-group line (see RFC 5576). Sample input:
|
|
|
// a=ssrc-group:semantics 12 34
|
|
|
SDPUtils.parseSsrcGroup = function(line) {
|
|
|
const parts = line.substring(13).split(' ');
|
|
|
return {
|
|
|
semantics: parts.shift(),
|
|
|
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.
|
|
|
SDPUtils.getMid = function(mediaSection) {
|
|
|
const mid = SDPUtils.matchPrefix(mediaSection, 'a=mid:')[0];
|
|
|
if (mid) {
|
|
|
return mid.substring(6);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// Parses a fingerprint line for DTLS-SRTP.
|
|
|
SDPUtils.parseFingerprint = function(line) {
|
|
|
const parts = line.substring(14).split(' ');
|
|
|
return {
|
|
|
algorithm: parts[0].toLowerCase(), // algorithm is case-sensitive in Edge.
|
|
|
value: parts[1].toUpperCase(), // the definition is upper-case in RFC 4572.
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// Extracts DTLS parameters from SDP media section or sessionpart.
|
|
|
// FIXME: for consistency with other functions this should only
|
|
|
// get the fingerprint line as input. See also getIceParameters.
|
|
|
SDPUtils.getDtlsParameters = function(mediaSection, sessionpart) {
|
|
|
const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
|
|
|
'a=fingerprint:');
|
|
|
// Note: a=setup line is ignored since we use the 'auto' role in Edge.
|
|
|
return {
|
|
|
role: 'auto',
|
|
|
fingerprints: lines.map(SDPUtils.parseFingerprint),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// Serializes DTLS parameters to SDP.
|
|
|
SDPUtils.writeDtlsParameters = function(params, setupType) {
|
|
|
let sdp = 'a=setup:' + setupType + '\r\n';
|
|
|
params.fingerprints.forEach(fp => {
|
|
|
sdp += 'a=fingerprint:' + fp.algorithm + ' ' + fp.value + '\r\n';
|
|
|
});
|
|
|
return sdp;
|
|
|
};
|
|
|
|
|
|
// Parses a=crypto lines into
|
|
|
// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#dictionary-rtcsrtpsdesparameters-members
|
|
|
SDPUtils.parseCryptoLine = function(line) {
|
|
|
const parts = line.substring(9).split(' ');
|
|
|
return {
|
|
|
tag: parseInt(parts[0], 10),
|
|
|
cryptoSuite: parts[1],
|
|
|
keyParams: parts[2],
|
|
|
sessionParams: parts.slice(3),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
SDPUtils.writeCryptoLine = function(parameters) {
|
|
|
return 'a=crypto:' + parameters.tag + ' ' +
|
|
|
parameters.cryptoSuite + ' ' +
|
|
|
(typeof parameters.keyParams === 'object'
|
|
|
? SDPUtils.writeCryptoKeyParams(parameters.keyParams)
|
|
|
: parameters.keyParams) +
|
|
|
(parameters.sessionParams ? ' ' + parameters.sessionParams.join(' ') : '') +
|
|
|
'\r\n';
|
|
|
};
|
|
|
|
|
|
// Parses the crypto key parameters into
|
|
|
// https://rawgit.com/aboba/edgertc/master/msortc-rs4.html#rtcsrtpkeyparam*
|
|
|
SDPUtils.parseCryptoKeyParams = function(keyParams) {
|
|
|
if (keyParams.indexOf('inline:') !== 0) {
|
|
|
return null;
|
|
|
}
|
|
|
const parts = keyParams.substring(7).split('|');
|
|
|
return {
|
|
|
keyMethod: 'inline',
|
|
|
keySalt: parts[0],
|
|
|
lifeTime: parts[1],
|
|
|
mkiValue: parts[2] ? parts[2].split(':')[0] : undefined,
|
|
|
mkiLength: parts[2] ? parts[2].split(':')[1] : undefined,
|
|
|
};
|
|
|
};
|
|
|
|
|
|
SDPUtils.writeCryptoKeyParams = function(keyParams) {
|
|
|
return keyParams.keyMethod + ':'
|
|
|
+ keyParams.keySalt +
|
|
|
(keyParams.lifeTime ? '|' + keyParams.lifeTime : '') +
|
|
|
(keyParams.mkiValue && keyParams.mkiLength
|
|
|
? '|' + keyParams.mkiValue + ':' + keyParams.mkiLength
|
|
|
: '');
|
|
|
};
|
|
|
|
|
|
// Extracts all SDES parameters.
|
|
|
SDPUtils.getCryptoParameters = function(mediaSection, sessionpart) {
|
|
|
const lines = SDPUtils.matchPrefix(mediaSection + sessionpart,
|
|
|
'a=crypto:');
|
|
|
return lines.map(SDPUtils.parseCryptoLine);
|
|
|
};
|
|
|
|
|
|
// Parses ICE information from SDP media section or 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) {
|
|
|
const ufrag = SDPUtils.matchPrefix(mediaSection + sessionpart,
|
|
|
'a=ice-ufrag:')[0];
|
|
|
const pwd = SDPUtils.matchPrefix(mediaSection + sessionpart,
|
|
|
'a=ice-pwd:')[0];
|
|
|
if (!(ufrag && pwd)) {
|
|
|
return null;
|
|
|
}
|
|
|
return {
|
|
|
usernameFragment: ufrag.substring(12),
|
|
|
password: pwd.substring(10),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// Serializes ICE parameters to SDP.
|
|
|
SDPUtils.writeIceParameters = function(params) {
|
|
|
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) {
|
|
|
const description = {
|
|
|
codecs: [],
|
|
|
headerExtensions: [],
|
|
|
fecMechanisms: [],
|
|
|
rtcp: [],
|
|
|
};
|
|
|
const lines = SDPUtils.splitLines(mediaSection);
|
|
|
const mline = lines[0].split(' ');
|
|
|
description.profile = mline[2];
|
|
|
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) {
|
|
|
const codec = SDPUtils.parseRtpMap(rtpmapline);
|
|
|
const fmtps = SDPUtils.matchPrefix(
|
|
|
mediaSection, 'a=fmtp:' + pt + ' ');
|
|
|
// Only the first a=fmtp:<pt> is considered.
|
|
|
codec.parameters = fmtps.length ? SDPUtils.parseFmtp(fmtps[0]) : {};
|
|
|
codec.rtcpFeedback = SDPUtils.matchPrefix(
|
|
|
mediaSection, 'a=rtcp-fb:' + pt + ' ')
|
|
|
.map(SDPUtils.parseRtcpFb);
|
|
|
description.codecs.push(codec);
|
|
|
// parse FEC mechanisms from rtpmap lines.
|
|
|
switch (codec.name.toUpperCase()) {
|
|
|
case 'RED':
|
|
|
case 'ULPFEC':
|
|
|
description.fecMechanisms.push(codec.name.toUpperCase());
|
|
|
break;
|
|
|
default: // only RED and ULPFEC are recognized as FEC mechanisms.
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
SDPUtils.matchPrefix(mediaSection, 'a=extmap:').forEach(line => {
|
|
|
description.headerExtensions.push(SDPUtils.parseExtmap(line));
|
|
|
});
|
|
|
const wildcardRtcpFb = SDPUtils.matchPrefix(mediaSection, 'a=rtcp-fb:* ')
|
|
|
.map(SDPUtils.parseRtcpFb);
|
|
|
description.codecs.forEach(codec => {
|
|
|
wildcardRtcpFb.forEach(fb=> {
|
|
|
const duplicate = codec.rtcpFeedback.find(existingFeedback => {
|
|
|
return existingFeedback.type === fb.type &&
|
|
|
existingFeedback.parameter === fb.parameter;
|
|
|
});
|
|
|
if (!duplicate) {
|
|
|
codec.rtcpFeedback.push(fb);
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
// FIXME: parse rtcp.
|
|
|
return description;
|
|
|
};
|
|
|
|
|
|
// Generates parts of the SDP media section describing the capabilities /
|
|
|
// parameters.
|
|
|
SDPUtils.writeRtpDescription = function(kind, caps) {
|
|
|
let sdp = '';
|
|
|
|
|
|
// Build the mline.
|
|
|
sdp += 'm=' + kind + ' ';
|
|
|
sdp += caps.codecs.length > 0 ? '9' : '0'; // reject if no codecs.
|
|
|
sdp += ' ' + (caps.profile || 'UDP/TLS/RTP/SAVPF') + ' ';
|
|
|
sdp += caps.codecs.map(codec => {
|
|
|
if (codec.preferredPayloadType !== undefined) {
|
|
|
return codec.preferredPayloadType;
|
|
|
}
|
|
|
return codec.payloadType;
|
|
|
}).join(' ') + '\r\n';
|
|
|
|
|
|
sdp += 'c=IN IP4 0.0.0.0\r\n';
|
|
|
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(codec => {
|
|
|
sdp += SDPUtils.writeRtpMap(codec);
|
|
|
sdp += SDPUtils.writeFmtp(codec);
|
|
|
sdp += SDPUtils.writeRtcpFb(codec);
|
|
|
});
|
|
|
let maxptime = 0;
|
|
|
caps.codecs.forEach(codec => {
|
|
|
if (codec.maxptime > maxptime) {
|
|
|
maxptime = codec.maxptime;
|
|
|
}
|
|
|
});
|
|
|
if (maxptime > 0) {
|
|
|
sdp += 'a=maxptime:' + maxptime + '\r\n';
|
|
|
}
|
|
|
|
|
|
if (caps.headerExtensions) {
|
|
|
caps.headerExtensions.forEach(extension => {
|
|
|
sdp += SDPUtils.writeExtmap(extension);
|
|
|
});
|
|
|
}
|
|
|
// FIXME: write fecMechanisms.
|
|
|
return sdp;
|
|
|
};
|
|
|
|
|
|
// Parses the SDP media section and returns an array of
|
|
|
// RTCRtpEncodingParameters.
|
|
|
SDPUtils.parseRtpEncodingParameters = function(mediaSection) {
|
|
|
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
|
|
|
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.substring(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(codec => {
|
|
|
if (codec.name.toUpperCase() === 'RTX' && codec.parameters.apt) {
|
|
|
let encParam = {
|
|
|
ssrc: primarySsrc,
|
|
|
codecPayloadType: parseInt(codec.parameters.apt, 10),
|
|
|
};
|
|
|
if (primarySsrc && secondarySsrc) {
|
|
|
encParam.rtx = {ssrc: secondarySsrc};
|
|
|
}
|
|
|
encodingParameters.push(encParam);
|
|
|
if (hasRed) {
|
|
|
encParam = JSON.parse(JSON.stringify(encParam));
|
|
|
encParam.fec = {
|
|
|
ssrc: primarySsrc,
|
|
|
mechanism: hasUlpfec ? 'red+ulpfec' : 'red',
|
|
|
};
|
|
|
encodingParameters.push(encParam);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
if (encodingParameters.length === 0 && primarySsrc) {
|
|
|
encodingParameters.push({
|
|
|
ssrc: primarySsrc,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// we support both b=AS and b=TIAS but interpret AS as TIAS.
|
|
|
let bandwidth = SDPUtils.matchPrefix(mediaSection, 'b=');
|
|
|
if (bandwidth.length) {
|
|
|
if (bandwidth[0].indexOf('b=TIAS:') === 0) {
|
|
|
bandwidth = parseInt(bandwidth[0].substring(7), 10);
|
|
|
} else if (bandwidth[0].indexOf('b=AS:') === 0) {
|
|
|
// use formula from JSEP to convert b=AS to TIAS value.
|
|
|
bandwidth = parseInt(bandwidth[0].substring(5), 10) * 1000 * 0.95
|
|
|
- (50 * 40 * 8);
|
|
|
} else {
|
|
|
bandwidth = undefined;
|
|
|
}
|
|
|
encodingParameters.forEach(params => {
|
|
|
params.maxBitrate = bandwidth;
|
|
|
});
|
|
|
}
|
|
|
return encodingParameters;
|
|
|
};
|
|
|
|
|
|
// parses http://draft.ortc.org/#rtcrtcpparameters*
|
|
|
SDPUtils.parseRtcpParameters = function(mediaSection) {
|
|
|
const rtcpParameters = {};
|
|
|
|
|
|
// Gets the first SSRC. Note that with RTX there might be multiple
|
|
|
// SSRCs.
|
|
|
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;
|
|
|
}
|
|
|
|
|
|
// Edge uses the compound attribute instead of reducedSize
|
|
|
// compound is !reducedSize
|
|
|
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.
|
|
|
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) {
|
|
|
let parts;
|
|
|
const spec = SDPUtils.matchPrefix(mediaSection, 'a=msid:');
|
|
|
if (spec.length === 1) {
|
|
|
parts = spec[0].substring(7).split(' ');
|
|
|
return {stream: parts[0], track: parts[1]};
|
|
|
}
|
|
|
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]};
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// SCTP
|
|
|
// parses draft-ietf-mmusic-sctp-sdp-26 first and falls back
|
|
|
// to draft-ietf-mmusic-sctp-sdp-05
|
|
|
SDPUtils.parseSctpDescription = function(mediaSection) {
|
|
|
const mline = SDPUtils.parseMLine(mediaSection);
|
|
|
const maxSizeLine = SDPUtils.matchPrefix(mediaSection, 'a=max-message-size:');
|
|
|
let maxMessageSize;
|
|
|
if (maxSizeLine.length > 0) {
|
|
|
maxMessageSize = parseInt(maxSizeLine[0].substring(19), 10);
|
|
|
}
|
|
|
if (isNaN(maxMessageSize)) {
|
|
|
maxMessageSize = 65536;
|
|
|
}
|
|
|
const sctpPort = SDPUtils.matchPrefix(mediaSection, 'a=sctp-port:');
|
|
|
if (sctpPort.length > 0) {
|
|
|
return {
|
|
|
port: parseInt(sctpPort[0].substring(12), 10),
|
|
|
protocol: mline.fmt,
|
|
|
maxMessageSize,
|
|
|
};
|
|
|
}
|
|
|
const sctpMapLines = SDPUtils.matchPrefix(mediaSection, 'a=sctpmap:');
|
|
|
if (sctpMapLines.length > 0) {
|
|
|
const parts = sctpMapLines[0]
|
|
|
.substring(10)
|
|
|
.split(' ');
|
|
|
return {
|
|
|
port: parseInt(parts[0], 10),
|
|
|
protocol: parts[1],
|
|
|
maxMessageSize,
|
|
|
};
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// SCTP
|
|
|
// outputs the draft-ietf-mmusic-sctp-sdp-26 version that all browsers
|
|
|
// support by now receiving in this format, unless we originally parsed
|
|
|
// 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) {
|
|
|
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',
|
|
|
];
|
|
|
} 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',
|
|
|
];
|
|
|
}
|
|
|
if (sctp.maxMessageSize !== undefined) {
|
|
|
output.push('a=max-message-size:' + sctp.maxMessageSize + '\r\n');
|
|
|
}
|
|
|
return output.join('');
|
|
|
};
|
|
|
|
|
|
// Generate a session ID for SDP.
|
|
|
// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-20#section-5.2.1
|
|
|
// recommends using a cryptographically random +ve 64-bit value
|
|
|
// but right now this should be acceptable and within the right range
|
|
|
SDPUtils.generateSessionId = function() {
|
|
|
return Math.random().toString().substr(2, 22);
|
|
|
};
|
|
|
|
|
|
// 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) {
|
|
|
let sessionId;
|
|
|
const version = sessVer !== undefined ? sessVer : 2;
|
|
|
if (sessId) {
|
|
|
sessionId = sessId;
|
|
|
} else {
|
|
|
sessionId = SDPUtils.generateSessionId();
|
|
|
}
|
|
|
const user = sessUser || 'thisisadapterortc';
|
|
|
// FIXME: sess-id should be an NTP timestamp.
|
|
|
return 'v=0\r\n' +
|
|
|
'o=' + user + ' ' + sessionId + ' ' + version +
|
|
|
' IN IP4 127.0.0.1\r\n' +
|
|
|
's=-\r\n' +
|
|
|
't=0 0\r\n';
|
|
|
};
|
|
|
|
|
|
// Gets the direction from the mediaSection or the sessionpart.
|
|
|
SDPUtils.getDirection = function(mediaSection, sessionpart) {
|
|
|
// Look for sendrecv, sendonly, recvonly, inactive, default to sendrecv.
|
|
|
const lines = SDPUtils.splitLines(mediaSection);
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
switch (lines[i]) {
|
|
|
case 'a=sendrecv':
|
|
|
case 'a=sendonly':
|
|
|
case 'a=recvonly':
|
|
|
case 'a=inactive':
|
|
|
return lines[i].substring(2);
|
|
|
default:
|
|
|
// FIXME: What should happen here?
|
|
|
}
|
|
|
}
|
|
|
if (sessionpart) {
|
|
|
return SDPUtils.getDirection(sessionpart);
|
|
|
}
|
|
|
return 'sendrecv';
|
|
|
};
|
|
|
|
|
|
SDPUtils.getKind = function(mediaSection) {
|
|
|
const lines = SDPUtils.splitLines(mediaSection);
|
|
|
const mline = lines[0].split(' ');
|
|
|
return mline[0].substring(2);
|
|
|
};
|
|
|
|
|
|
SDPUtils.isRejected = function(mediaSection) {
|
|
|
return mediaSection.split(' ', 2)[1] === '0';
|
|
|
};
|
|
|
|
|
|
SDPUtils.parseMLine = function(mediaSection) {
|
|
|
const lines = SDPUtils.splitLines(mediaSection);
|
|
|
const parts = lines[0].substring(2).split(' ');
|
|
|
return {
|
|
|
kind: parts[0],
|
|
|
port: parseInt(parts[1], 10),
|
|
|
protocol: parts[2],
|
|
|
fmt: parts.slice(3).join(' '),
|
|
|
};
|
|
|
};
|
|
|
|
|
|
SDPUtils.parseOLine = function(mediaSection) {
|
|
|
const line = SDPUtils.matchPrefix(mediaSection, 'o=')[0];
|
|
|
const parts = line.substring(2).split(' ');
|
|
|
return {
|
|
|
username: parts[0],
|
|
|
sessionId: parts[1],
|
|
|
sessionVersion: parseInt(parts[2], 10),
|
|
|
netType: parts[3],
|
|
|
addressType: parts[4],
|
|
|
address: parts[5],
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// a very naive interpretation of a valid SDP.
|
|
|
SDPUtils.isValidSDP = function(blob) {
|
|
|
if (typeof blob !== 'string' || blob.length === 0) {
|
|
|
return false;
|
|
|
}
|
|
|
const lines = SDPUtils.splitLines(blob);
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
|
if (lines[i].length < 2 || lines[i].charAt(1) !== '=') {
|
|
|
return false;
|
|
|
}
|
|
|
// TODO: check the modifier a bit more.
|
|
|
}
|
|
|
return true;
|
|
|
};
|
|
|
|
|
|
// Expose public methods.
|
|
|
if (true) {
|
|
|
module.exports = SDPUtils;
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/ua-parser-js/src/ua-parser.js":
|
|
|
/*!****************************************************!*\
|
|
|
!*** ./node_modules/ua-parser-js/src/ua-parser.js ***!
|
|
|
\****************************************************/
|
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
|
|
var __WEBPACK_AMD_DEFINE_RESULT__;/////////////////////////////////////////////////////////////////////////////////
|
|
|
/* UAParser.js v1.0.37
|
|
|
Copyright © 2012-2021 Faisal Salman <f@faisalman.com>
|
|
|
MIT License *//*
|
|
|
Detect Browser, Engine, OS, CPU, and Device type/model from User-Agent data.
|
|
|
Supports browser & node.js environment.
|
|
|
Demo : https://faisalman.github.io/ua-parser-js
|
|
|
Source : https://github.com/faisalman/ua-parser-js */
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
(function (window, undefined) {
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
//////////////
|
|
|
// Constants
|
|
|
/////////////
|
|
|
|
|
|
|
|
|
var LIBVERSION = '1.0.37',
|
|
|
EMPTY = '',
|
|
|
UNKNOWN = '?',
|
|
|
FUNC_TYPE = 'function',
|
|
|
UNDEF_TYPE = 'undefined',
|
|
|
OBJ_TYPE = 'object',
|
|
|
STR_TYPE = 'string',
|
|
|
MAJOR = 'major',
|
|
|
MODEL = 'model',
|
|
|
NAME = 'name',
|
|
|
TYPE = 'type',
|
|
|
VENDOR = 'vendor',
|
|
|
VERSION = 'version',
|
|
|
ARCHITECTURE= 'architecture',
|
|
|
CONSOLE = 'console',
|
|
|
MOBILE = 'mobile',
|
|
|
TABLET = 'tablet',
|
|
|
SMARTTV = 'smarttv',
|
|
|
WEARABLE = 'wearable',
|
|
|
EMBEDDED = 'embedded',
|
|
|
UA_MAX_LENGTH = 500;
|
|
|
|
|
|
var AMAZON = 'Amazon',
|
|
|
APPLE = 'Apple',
|
|
|
ASUS = 'ASUS',
|
|
|
BLACKBERRY = 'BlackBerry',
|
|
|
BROWSER = 'Browser',
|
|
|
CHROME = 'Chrome',
|
|
|
EDGE = 'Edge',
|
|
|
FIREFOX = 'Firefox',
|
|
|
GOOGLE = 'Google',
|
|
|
HUAWEI = 'Huawei',
|
|
|
LG = 'LG',
|
|
|
MICROSOFT = 'Microsoft',
|
|
|
MOTOROLA = 'Motorola',
|
|
|
OPERA = 'Opera',
|
|
|
SAMSUNG = 'Samsung',
|
|
|
SHARP = 'Sharp',
|
|
|
SONY = 'Sony',
|
|
|
XIAOMI = 'Xiaomi',
|
|
|
ZEBRA = 'Zebra',
|
|
|
FACEBOOK = 'Facebook',
|
|
|
CHROMIUM_OS = 'Chromium OS',
|
|
|
MAC_OS = 'Mac OS';
|
|
|
|
|
|
///////////
|
|
|
// Helper
|
|
|
//////////
|
|
|
|
|
|
var extend = function (regexes, extensions) {
|
|
|
var mergedRegexes = {};
|
|
|
for (var i in regexes) {
|
|
|
if (extensions[i] && extensions[i].length % 2 === 0) {
|
|
|
mergedRegexes[i] = extensions[i].concat(regexes[i]);
|
|
|
} else {
|
|
|
mergedRegexes[i] = regexes[i];
|
|
|
}
|
|
|
}
|
|
|
return mergedRegexes;
|
|
|
},
|
|
|
enumerize = function (arr) {
|
|
|
var enums = {};
|
|
|
for (var i=0; i<arr.length; i++) {
|
|
|
enums[arr[i].toUpperCase()] = arr[i];
|
|
|
}
|
|
|
return enums;
|
|
|
},
|
|
|
has = function (str1, str2) {
|
|
|
return typeof str1 === STR_TYPE ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
|
|
|
},
|
|
|
lowerize = function (str) {
|
|
|
return str.toLowerCase();
|
|
|
},
|
|
|
majorize = function (version) {
|
|
|
return typeof(version) === STR_TYPE ? version.replace(/[^\d\.]/g, EMPTY).split('.')[0] : undefined;
|
|
|
},
|
|
|
trim = function (str, len) {
|
|
|
if (typeof(str) === STR_TYPE) {
|
|
|
str = str.replace(/^\s\s*/, EMPTY);
|
|
|
return typeof(len) === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
///////////////
|
|
|
// Map helper
|
|
|
//////////////
|
|
|
|
|
|
var rgxMapper = function (ua, arrays) {
|
|
|
|
|
|
var i = 0, j, k, p, q, matches, match;
|
|
|
|
|
|
// loop through all regexes maps
|
|
|
while (i < arrays.length && !matches) {
|
|
|
|
|
|
var regex = arrays[i], // even sequence (0,2,4,..)
|
|
|
props = arrays[i + 1]; // odd sequence (1,3,5,..)
|
|
|
j = k = 0;
|
|
|
|
|
|
// try matching uastring with regexes
|
|
|
while (j < regex.length && !matches) {
|
|
|
|
|
|
if (!regex[j]) { break; }
|
|
|
matches = regex[j++].exec(ua);
|
|
|
|
|
|
if (!!matches) {
|
|
|
for (p = 0; p < props.length; p++) {
|
|
|
match = matches[++k];
|
|
|
q = props[p];
|
|
|
// check if given property is actually array
|
|
|
if (typeof q === OBJ_TYPE && q.length > 0) {
|
|
|
if (q.length === 2) {
|
|
|
if (typeof q[1] == FUNC_TYPE) {
|
|
|
// assign modified match
|
|
|
this[q[0]] = q[1].call(this, match);
|
|
|
} else {
|
|
|
// assign given value, ignore regex match
|
|
|
this[q[0]] = q[1];
|
|
|
}
|
|
|
} else if (q.length === 3) {
|
|
|
// check whether function or regex
|
|
|
if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
|
|
|
// call function (usually string mapper)
|
|
|
this[q[0]] = match ? q[1].call(this, match, q[2]) : undefined;
|
|
|
} else {
|
|
|
// sanitize match using given regex
|
|
|
this[q[0]] = match ? match.replace(q[1], q[2]) : undefined;
|
|
|
}
|
|
|
} else if (q.length === 4) {
|
|
|
this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : undefined;
|
|
|
}
|
|
|
} else {
|
|
|
this[q] = match ? match : undefined;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
i += 2;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
strMapper = function (str, map) {
|
|
|
|
|
|
for (var i in map) {
|
|
|
// check if current value is array
|
|
|
if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
|
|
|
for (var j = 0; j < map[i].length; j++) {
|
|
|
if (has(map[i][j], str)) {
|
|
|
return (i === UNKNOWN) ? undefined : i;
|
|
|
}
|
|
|
}
|
|
|
} else if (has(map[i], str)) {
|
|
|
return (i === UNKNOWN) ? undefined : i;
|
|
|
}
|
|
|
}
|
|
|
return str;
|
|
|
};
|
|
|
|
|
|
///////////////
|
|
|
// String map
|
|
|
//////////////
|
|
|
|
|
|
// Safari < 3.0
|
|
|
var oldSafariMap = {
|
|
|
'1.0' : '/8',
|
|
|
'1.2' : '/1',
|
|
|
'1.3' : '/3',
|
|
|
'2.0' : '/412',
|
|
|
'2.0.2' : '/416',
|
|
|
'2.0.3' : '/417',
|
|
|
'2.0.4' : '/419',
|
|
|
'?' : '/'
|
|
|
},
|
|
|
windowsVersionMap = {
|
|
|
'ME' : '4.90',
|
|
|
'NT 3.11' : 'NT3.51',
|
|
|
'NT 4.0' : 'NT4.0',
|
|
|
'2000' : 'NT 5.0',
|
|
|
'XP' : ['NT 5.1', 'NT 5.2'],
|
|
|
'Vista' : 'NT 6.0',
|
|
|
'7' : 'NT 6.1',
|
|
|
'8' : 'NT 6.2',
|
|
|
'8.1' : 'NT 6.3',
|
|
|
'10' : ['NT 6.4', 'NT 10.0'],
|
|
|
'RT' : 'ARM'
|
|
|
};
|
|
|
|
|
|
//////////////
|
|
|
// Regex map
|
|
|
/////////////
|
|
|
|
|
|
var regexes = {
|
|
|
|
|
|
browser : [[
|
|
|
|
|
|
/\b(?:crmo|crios)\/([\w\.]+)/i // Chrome for Android/iOS
|
|
|
], [VERSION, [NAME, 'Chrome']], [
|
|
|
/edg(?:e|ios|a)?\/([\w\.]+)/i // Microsoft Edge
|
|
|
], [VERSION, [NAME, 'Edge']], [
|
|
|
|
|
|
// Presto based
|
|
|
/(opera mini)\/([-\w\.]+)/i, // Opera Mini
|
|
|
/(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i, // Opera Mobi/Tablet
|
|
|
/(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i // Opera
|
|
|
], [NAME, VERSION], [
|
|
|
/opios[\/ ]+([\w\.]+)/i // Opera mini on iphone >= 8.0
|
|
|
], [VERSION, [NAME, OPERA+' Mini']], [
|
|
|
/\bopr\/([\w\.]+)/i // Opera Webkit
|
|
|
], [VERSION, [NAME, OPERA]], [
|
|
|
|
|
|
// Mixed
|
|
|
/\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i // Baidu
|
|
|
], [VERSION, [NAME, 'Baidu']], [
|
|
|
/(kindle)\/([\w\.]+)/i, // Kindle
|
|
|
/(lunascape|maxthon|netfront|jasmine|blazer)[\/ ]?([\w\.]*)/i, // Lunascape/Maxthon/Netfront/Jasmine/Blazer
|
|
|
// Trident based
|
|
|
/(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i, // Avant/IEMobile/SlimBrowser
|
|
|
/(?:ms|\()(ie) ([\w\.]+)/i, // Internet Explorer
|
|
|
|
|
|
// Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
|
|
|
/(flock|rockmelt|midori|epiphany|silk|skyfire|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|qq|duckduckgo)\/([-\w\.]+)/i,
|
|
|
// Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ, aka ShouQ
|
|
|
/(heytap|ovi)browser\/([\d\.]+)/i, // Heytap/Ovi
|
|
|
/(weibo)__([\d\.]+)/i // Weibo
|
|
|
], [NAME, VERSION], [
|
|
|
/(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i // UCBrowser
|
|
|
], [VERSION, [NAME, 'UC'+BROWSER]], [
|
|
|
/microm.+\bqbcore\/([\w\.]+)/i, // WeChat Desktop for Windows Built-in Browser
|
|
|
/\bqbcore\/([\w\.]+).+microm/i,
|
|
|
/micromessenger\/([\w\.]+)/i // WeChat
|
|
|
], [VERSION, [NAME, 'WeChat']], [
|
|
|
/konqueror\/([\w\.]+)/i // Konqueror
|
|
|
], [VERSION, [NAME, 'Konqueror']], [
|
|
|
/trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i // IE11
|
|
|
], [VERSION, [NAME, 'IE']], [
|
|
|
/ya(?:search)?browser\/([\w\.]+)/i // Yandex
|
|
|
], [VERSION, [NAME, 'Yandex']], [
|
|
|
/slbrowser\/([\w\.]+)/i // Smart Lenovo Browser
|
|
|
], [VERSION, [NAME, 'Smart Lenovo '+BROWSER]], [
|
|
|
/(avast|avg)\/([\w\.]+)/i // Avast/AVG Secure Browser
|
|
|
], [[NAME, /(.+)/, '$1 Secure '+BROWSER], VERSION], [
|
|
|
/\bfocus\/([\w\.]+)/i // Firefox Focus
|
|
|
], [VERSION, [NAME, FIREFOX+' Focus']], [
|
|
|
/\bopt\/([\w\.]+)/i // Opera Touch
|
|
|
], [VERSION, [NAME, OPERA+' Touch']], [
|
|
|
/coc_coc\w+\/([\w\.]+)/i // Coc Coc Browser
|
|
|
], [VERSION, [NAME, 'Coc Coc']], [
|
|
|
/dolfin\/([\w\.]+)/i // Dolphin
|
|
|
], [VERSION, [NAME, 'Dolphin']], [
|
|
|
/coast\/([\w\.]+)/i // Opera Coast
|
|
|
], [VERSION, [NAME, OPERA+' Coast']], [
|
|
|
/miuibrowser\/([\w\.]+)/i // MIUI Browser
|
|
|
], [VERSION, [NAME, 'MIUI '+BROWSER]], [
|
|
|
/fxios\/([-\w\.]+)/i // Firefox for iOS
|
|
|
], [VERSION, [NAME, FIREFOX]], [
|
|
|
/\bqihu|(qi?ho?o?|360)browser/i // 360
|
|
|
], [[NAME, '360 ' + BROWSER]], [
|
|
|
/(oculus|sailfish|huawei|vivo)browser\/([\w\.]+)/i
|
|
|
], [[NAME, /(.+)/, '$1 ' + BROWSER], VERSION], [ // Oculus/Sailfish/HuaweiBrowser/VivoBrowser
|
|
|
/samsungbrowser\/([\w\.]+)/i // Samsung Internet
|
|
|
], [VERSION, [NAME, SAMSUNG + ' Internet']], [
|
|
|
/(comodo_dragon)\/([\w\.]+)/i // Comodo Dragon
|
|
|
], [[NAME, /_/g, ' '], VERSION], [
|
|
|
/metasr[\/ ]?([\d\.]+)/i // Sogou Explorer
|
|
|
], [VERSION, [NAME, 'Sogou Explorer']], [
|
|
|
/(sogou)mo\w+\/([\d\.]+)/i // Sogou Mobile
|
|
|
], [[NAME, 'Sogou Mobile'], VERSION], [
|
|
|
/(electron)\/([\w\.]+) safari/i, // Electron-based App
|
|
|
/(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i, // Tesla
|
|
|
/m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i // QQBrowser/2345 Browser
|
|
|
], [NAME, VERSION], [
|
|
|
/(lbbrowser)/i, // LieBao Browser
|
|
|
/\[(linkedin)app\]/i // LinkedIn App for iOS & Android
|
|
|
], [NAME], [
|
|
|
|
|
|
// WebView
|
|
|
/((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i // Facebook App for iOS & Android
|
|
|
], [[NAME, FACEBOOK], VERSION], [
|
|
|
/(Klarna)\/([\w\.]+)/i, // Klarna Shopping Browser for iOS & Android
|
|
|
/(kakao(?:talk|story))[\/ ]([\w\.]+)/i, // Kakao App
|
|
|
/(naver)\(.*?(\d+\.[\w\.]+).*\)/i, // Naver InApp
|
|
|
/safari (line)\/([\w\.]+)/i, // Line App for iOS
|
|
|
/\b(line)\/([\w\.]+)\/iab/i, // Line App for Android
|
|
|
/(alipay)client\/([\w\.]+)/i, // Alipay
|
|
|
/(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i // Chromium/Instagram/Snapchat
|
|
|
], [NAME, VERSION], [
|
|
|
/\bgsa\/([\w\.]+) .*safari\//i // Google Search Appliance on iOS
|
|
|
], [VERSION, [NAME, 'GSA']], [
|
|
|
/musical_ly(?:.+app_?version\/|_)([\w\.]+)/i // TikTok
|
|
|
], [VERSION, [NAME, 'TikTok']], [
|
|
|
|
|
|
/headlesschrome(?:\/([\w\.]+)| )/i // Chrome Headless
|
|
|
], [VERSION, [NAME, CHROME+' Headless']], [
|
|
|
|
|
|
/ wv\).+(chrome)\/([\w\.]+)/i // Chrome WebView
|
|
|
], [[NAME, CHROME+' WebView'], VERSION], [
|
|
|
|
|
|
/droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i // Android Browser
|
|
|
], [VERSION, [NAME, 'Android '+BROWSER]], [
|
|
|
|
|
|
/(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i // Chrome/OmniWeb/Arora/Tizen/Nokia
|
|
|
], [NAME, VERSION], [
|
|
|
|
|
|
/version\/([\w\.\,]+) .*mobile\/\w+ (safari)/i // Mobile Safari
|
|
|
], [VERSION, [NAME, 'Mobile Safari']], [
|
|
|
/version\/([\w(\.|\,)]+) .*(mobile ?safari|safari)/i // Safari & Safari Mobile
|
|
|
], [VERSION, NAME], [
|
|
|
/webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i // Safari < 3.0
|
|
|
], [NAME, [VERSION, strMapper, oldSafariMap]], [
|
|
|
|
|
|
/(webkit|khtml)\/([\w\.]+)/i
|
|
|
], [NAME, VERSION], [
|
|
|
|
|
|
// Gecko based
|
|
|
/(navigator|netscape\d?)\/([-\w\.]+)/i // Netscape
|
|
|
], [[NAME, 'Netscape'], VERSION], [
|
|
|
/mobile vr; rv:([\w\.]+)\).+firefox/i // Firefox Reality
|
|
|
], [VERSION, [NAME, FIREFOX+' Reality']], [
|
|
|
/ekiohf.+(flow)\/([\w\.]+)/i, // Flow
|
|
|
/(swiftfox)/i, // Swiftfox
|
|
|
/(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror|klar)[\/ ]?([\w\.\+]+)/i,
|
|
|
// IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror/Klar
|
|
|
/(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
|
|
|
// Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
|
|
|
/(firefox)\/([\w\.]+)/i, // Other Firefox-based
|
|
|
/(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i, // Mozilla
|
|
|
|
|
|
// Other
|
|
|
/(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|sleipnir|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
|
|
|
// Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Sleipnir/Obigo/Mosaic/Go/ICE/UP.Browser
|
|
|
/(links) \(([\w\.]+)/i, // Links
|
|
|
/panasonic;(viera)/i // Panasonic Viera
|
|
|
], [NAME, VERSION], [
|
|
|
|
|
|
/(cobalt)\/([\w\.]+)/i // Cobalt
|
|
|
], [NAME, [VERSION, /master.|lts./, ""]]
|
|
|
],
|
|
|
|
|
|
cpu : [[
|
|
|
|
|
|
/(?:(amd|x(?:(?:86|64)[-_])?|wow|win)64)[;\)]/i // AMD64 (x64)
|
|
|
], [[ARCHITECTURE, 'amd64']], [
|
|
|
|
|
|
/(ia32(?=;))/i // IA32 (quicktime)
|
|
|
], [[ARCHITECTURE, lowerize]], [
|
|
|
|
|
|
/((?:i[346]|x)86)[;\)]/i // IA32 (x86)
|
|
|
], [[ARCHITECTURE, 'ia32']], [
|
|
|
|
|
|
/\b(aarch64|arm(v?8e?l?|_?64))\b/i // ARM64
|
|
|
], [[ARCHITECTURE, 'arm64']], [
|
|
|
|
|
|
/\b(arm(?:v[67])?ht?n?[fl]p?)\b/i // ARMHF
|
|
|
], [[ARCHITECTURE, 'armhf']], [
|
|
|
|
|
|
// PocketPC mistakenly identified as PowerPC
|
|
|
/windows (ce|mobile); ppc;/i
|
|
|
], [[ARCHITECTURE, 'arm']], [
|
|
|
|
|
|
/((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i // PowerPC
|
|
|
], [[ARCHITECTURE, /ower/, EMPTY, lowerize]], [
|
|
|
|
|
|
/(sun4\w)[;\)]/i // SPARC
|
|
|
], [[ARCHITECTURE, 'sparc']], [
|
|
|
|
|
|
/((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
|
|
|
// IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
|
|
|
], [[ARCHITECTURE, lowerize]]
|
|
|
],
|
|
|
|
|
|
device : [[
|
|
|
|
|
|
//////////////////////////
|
|
|
// MOBILES & TABLETS
|
|
|
/////////////////////////
|
|
|
|
|
|
// Samsung
|
|
|
/\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
|
|
|
], [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]], [
|
|
|
/\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
|
|
|
/samsung[- ]([-\w]+)/i,
|
|
|
/sec-(sgh\w+)/i
|
|
|
], [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Apple
|
|
|
/(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i // iPod/iPhone
|
|
|
], [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]], [
|
|
|
/\((ipad);[-\w\),; ]+apple/i, // iPad
|
|
|
/applecoremedia\/[\w\.]+ \((ipad)/i,
|
|
|
/\b(ipad)\d\d?,\d\d?[;\]].+ios/i
|
|
|
], [MODEL, [VENDOR, APPLE], [TYPE, TABLET]], [
|
|
|
/(macintosh);/i
|
|
|
], [MODEL, [VENDOR, APPLE]], [
|
|
|
|
|
|
// Sharp
|
|
|
/\b(sh-?[altvz]?\d\d[a-ekm]?)/i
|
|
|
], [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Huawei
|
|
|
/\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
|
|
|
], [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]], [
|
|
|
/(?:huawei|honor)([-\w ]+)[;\)]/i,
|
|
|
/\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
|
|
|
], [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Xiaomi
|
|
|
/\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i, // Xiaomi POCO
|
|
|
/\b; (\w+) build\/hm\1/i, // Xiaomi Hongmi 'numeric' models
|
|
|
/\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i, // Xiaomi Hongmi
|
|
|
/\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i, // Xiaomi Redmi
|
|
|
/oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i, // Xiaomi Redmi 'numeric' models
|
|
|
/\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i // Xiaomi Mi
|
|
|
], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [
|
|
|
/oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i, // Redmi Pad
|
|
|
/\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i // Mi Pad tablets
|
|
|
],[[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, TABLET]], [
|
|
|
|
|
|
// OPPO
|
|
|
/; (\w+) bui.+ oppo/i,
|
|
|
/\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
|
|
|
], [MODEL, [VENDOR, 'OPPO'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Vivo
|
|
|
/vivo (\w+)(?: bui|\))/i,
|
|
|
/\b(v[12]\d{3}\w?[at])(?: bui|;)/i
|
|
|
], [MODEL, [VENDOR, 'Vivo'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Realme
|
|
|
/\b(rmx[1-3]\d{3})(?: bui|;|\))/i
|
|
|
], [MODEL, [VENDOR, 'Realme'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Motorola
|
|
|
/\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
|
|
|
/\bmot(?:orola)?[- ](\w*)/i,
|
|
|
/((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
|
|
|
], [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]], [
|
|
|
/\b(mz60\d|xoom[2 ]{0,2}) build\//i
|
|
|
], [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]], [
|
|
|
|
|
|
// LG
|
|
|
/((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
|
|
|
], [MODEL, [VENDOR, LG], [TYPE, TABLET]], [
|
|
|
/(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
|
|
|
/\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i,
|
|
|
/\blg-?([\d\w]+) bui/i
|
|
|
], [MODEL, [VENDOR, LG], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Lenovo
|
|
|
/(ideatab[-\w ]+)/i,
|
|
|
/lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
|
|
|
], [MODEL, [VENDOR, 'Lenovo'], [TYPE, TABLET]], [
|
|
|
|
|
|
// Nokia
|
|
|
/(?:maemo|nokia).*(n900|lumia \d+)/i,
|
|
|
/nokia[-_ ]?([-\w\.]*)/i
|
|
|
], [[MODEL, /_/g, ' '], [VENDOR, 'Nokia'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Google
|
|
|
/(pixel c)\b/i // Google Pixel C
|
|
|
], [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]], [
|
|
|
/droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i // Google Pixel
|
|
|
], [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Sony
|
|
|
/droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
|
|
|
], [MODEL, [VENDOR, SONY], [TYPE, MOBILE]], [
|
|
|
/sony tablet [ps]/i,
|
|
|
/\b(?:sony)?sgp\w+(?: bui|\))/i
|
|
|
], [[MODEL, 'Xperia Tablet'], [VENDOR, SONY], [TYPE, TABLET]], [
|
|
|
|
|
|
// OnePlus
|
|
|
/ (kb2005|in20[12]5|be20[12][59])\b/i,
|
|
|
/(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
|
|
|
], [MODEL, [VENDOR, 'OnePlus'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Amazon
|
|
|
/(alexa)webm/i,
|
|
|
/(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i, // Kindle Fire without Silk / Echo Show
|
|
|
/(kf[a-z]+)( bui|\)).+silk\//i // Kindle Fire HD
|
|
|
], [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]], [
|
|
|
/((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i // Fire Phone
|
|
|
], [[MODEL, /(.+)/g, 'Fire Phone $1'], [VENDOR, AMAZON], [TYPE, MOBILE]], [
|
|
|
|
|
|
// BlackBerry
|
|
|
/(playbook);[-\w\),; ]+(rim)/i // BlackBerry PlayBook
|
|
|
], [MODEL, VENDOR, [TYPE, TABLET]], [
|
|
|
/\b((?:bb[a-f]|st[hv])100-\d)/i,
|
|
|
/\(bb10; (\w+)/i // BlackBerry 10
|
|
|
], [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Asus
|
|
|
/(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i
|
|
|
], [MODEL, [VENDOR, ASUS], [TYPE, TABLET]], [
|
|
|
/ (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i
|
|
|
], [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]], [
|
|
|
|
|
|
// HTC
|
|
|
/(nexus 9)/i // HTC Nexus 9
|
|
|
], [MODEL, [VENDOR, 'HTC'], [TYPE, TABLET]], [
|
|
|
/(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i, // HTC
|
|
|
|
|
|
// ZTE
|
|
|
/(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
|
|
|
/(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
|
|
|
], [VENDOR, [MODEL, /_/g, ' '], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Acer
|
|
|
/droid.+; ([ab][1-7]-?[0178a]\d\d?)/i
|
|
|
], [MODEL, [VENDOR, 'Acer'], [TYPE, TABLET]], [
|
|
|
|
|
|
// Meizu
|
|
|
/droid.+; (m[1-5] note) bui/i,
|
|
|
/\bmz-([-\w]{2,})/i
|
|
|
], [MODEL, [VENDOR, 'Meizu'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// Ulefone
|
|
|
/; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i
|
|
|
], [MODEL, [VENDOR, 'Ulefone'], [TYPE, MOBILE]], [
|
|
|
|
|
|
// MIXED
|
|
|
/(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
|
|
|
// BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
|
|
|
/(hp) ([\w ]+\w)/i, // HP iPAQ
|
|
|
/(asus)-?(\w+)/i, // Asus
|
|
|
/(microsoft); (lumia[\w ]+)/i, // Microsoft Lumia
|
|
|
/(lenovo)[-_ ]?([-\w]+)/i, // Lenovo
|
|
|
/(jolla)/i, // Jolla
|
|
|
/(oppo) ?([\w ]+) bui/i // OPPO
|
|
|
], [VENDOR, MODEL, [TYPE, MOBILE]], [
|
|
|
|
|
|
/(kobo)\s(ereader|touch)/i, // Kobo
|
|
|
/(archos) (gamepad2?)/i, // Archos
|
|
|
/(hp).+(touchpad(?!.+tablet)|tablet)/i, // HP TouchPad
|
|
|
/(kindle)\/([\w\.]+)/i, // Kindle
|
|
|
/(nook)[\w ]+build\/(\w+)/i, // Nook
|
|
|
/(dell) (strea[kpr\d ]*[\dko])/i, // Dell Streak
|
|
|
/(le[- ]+pan)[- ]+(\w{1,9}) bui/i, // Le Pan Tablets
|
|
|
/(trinity)[- ]*(t\d{3}) bui/i, // Trinity Tablets
|
|
|
/(gigaset)[- ]+(q\w{1,9}) bui/i, // Gigaset Tablets
|
|
|
/(vodafone) ([\w ]+)(?:\)| bui)/i // Vodafone
|
|
|
], [VENDOR, MODEL, [TYPE, TABLET]], [
|
|
|
|
|
|
/(surface duo)/i // Surface Duo
|
|
|
], [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]], [
|
|
|
/droid [\d\.]+; (fp\du?)(?: b|\))/i // Fairphone
|
|
|
], [MODEL, [VENDOR, 'Fairphone'], [TYPE, MOBILE]], [
|
|
|
/(u304aa)/i // AT&T
|
|
|
], [MODEL, [VENDOR, 'AT&T'], [TYPE, MOBILE]], [
|
|
|
/\bsie-(\w*)/i // Siemens
|
|
|
], [MODEL, [VENDOR, 'Siemens'], [TYPE, MOBILE]], [
|
|
|
/\b(rct\w+) b/i // RCA Tablets
|
|
|
], [MODEL, [VENDOR, 'RCA'], [TYPE, TABLET]], [
|
|
|
/\b(venue[\d ]{2,7}) b/i // Dell Venue Tablets
|
|
|
], [MODEL, [VENDOR, 'Dell'], [TYPE, TABLET]], [
|
|
|
/\b(q(?:mv|ta)\w+) b/i // Verizon Tablet
|
|
|
], [MODEL, [VENDOR, 'Verizon'], [TYPE, TABLET]], [
|
|
|
/\b(?:barnes[& ]+noble |bn[rt])([\w\+ ]*) b/i // Barnes & Noble Tablet
|
|
|
], [MODEL, [VENDOR, 'Barnes & Noble'], [TYPE, TABLET]], [
|
|
|
/\b(tm\d{3}\w+) b/i
|
|
|
], [MODEL, [VENDOR, 'NuVision'], [TYPE, TABLET]], [
|
|
|
/\b(k88) b/i // ZTE K Series Tablet
|
|
|
], [MODEL, [VENDOR, 'ZTE'], [TYPE, TABLET]], [
|
|
|
/\b(nx\d{3}j) b/i // ZTE Nubia
|
|
|
], [MODEL, [VENDOR, 'ZTE'], [TYPE, MOBILE]], [
|
|
|
/\b(gen\d{3}) b.+49h/i // Swiss GEN Mobile
|
|
|
], [MODEL, [VENDOR, 'Swiss'], [TYPE, MOBILE]], [
|
|
|
/\b(zur\d{3}) b/i // Swiss ZUR Tablet
|
|
|
], [MODEL, [VENDOR, 'Swiss'], [TYPE, TABLET]], [
|
|
|
/\b((zeki)?tb.*\b) b/i // Zeki Tablets
|
|
|
], [MODEL, [VENDOR, 'Zeki'], [TYPE, TABLET]], [
|
|
|
/\b([yr]\d{2}) b/i,
|
|
|
/\b(dragon[- ]+touch |dt)(\w{5}) b/i // Dragon Touch Tablet
|
|
|
], [[VENDOR, 'Dragon Touch'], MODEL, [TYPE, TABLET]], [
|
|
|
/\b(ns-?\w{0,9}) b/i // Insignia Tablets
|
|
|
], [MODEL, [VENDOR, 'Insignia'], [TYPE, TABLET]], [
|
|
|
/\b((nxa|next)-?\w{0,9}) b/i // NextBook Tablets
|
|
|
], [MODEL, [VENDOR, 'NextBook'], [TYPE, TABLET]], [
|
|
|
/\b(xtreme\_)?(v(1[045]|2[015]|[3469]0|7[05])) b/i // Voice Xtreme Phones
|
|
|
], [[VENDOR, 'Voice'], MODEL, [TYPE, MOBILE]], [
|
|
|
/\b(lvtel\-)?(v1[12]) b/i // LvTel Phones
|
|
|
], [[VENDOR, 'LvTel'], MODEL, [TYPE, MOBILE]], [
|
|
|
/\b(ph-1) /i // Essential PH-1
|
|
|
], [MODEL, [VENDOR, 'Essential'], [TYPE, MOBILE]], [
|
|
|
/\b(v(100md|700na|7011|917g).*\b) b/i // Envizen Tablets
|
|
|
], [MODEL, [VENDOR, 'Envizen'], [TYPE, TABLET]], [
|
|
|
/\b(trio[-\w\. ]+) b/i // MachSpeed Tablets
|
|
|
], [MODEL, [VENDOR, 'MachSpeed'], [TYPE, TABLET]], [
|
|
|
/\btu_(1491) b/i // Rotor Tablets
|
|
|
], [MODEL, [VENDOR, 'Rotor'], [TYPE, TABLET]], [
|
|
|
/(shield[\w ]+) b/i // Nvidia Shield Tablets
|
|
|
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, TABLET]], [
|
|
|
/(sprint) (\w+)/i // Sprint Phones
|
|
|
], [VENDOR, MODEL, [TYPE, MOBILE]], [
|
|
|
/(kin\.[onetw]{3})/i // Microsoft Kin
|
|
|
], [[MODEL, /\./g, ' '], [VENDOR, MICROSOFT], [TYPE, MOBILE]], [
|
|
|
/droid.+; (cc6666?|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i // Zebra
|
|
|
], [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]], [
|
|
|
/droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
|
|
|
], [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]], [
|
|
|
|
|
|
///////////////////
|
|
|
// SMARTTVS
|
|
|
///////////////////
|
|
|
|
|
|
/smart-tv.+(samsung)/i // Samsung
|
|
|
], [VENDOR, [TYPE, SMARTTV]], [
|
|
|
/hbbtv.+maple;(\d+)/i
|
|
|
], [[MODEL, /^/, 'SmartTV'], [VENDOR, SAMSUNG], [TYPE, SMARTTV]], [
|
|
|
/(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i // LG SmartTV
|
|
|
], [[VENDOR, LG], [TYPE, SMARTTV]], [
|
|
|
/(apple) ?tv/i // Apple TV
|
|
|
], [VENDOR, [MODEL, APPLE+' TV'], [TYPE, SMARTTV]], [
|
|
|
/crkey/i // Google Chromecast
|
|
|
], [[MODEL, CHROME+'cast'], [VENDOR, GOOGLE], [TYPE, SMARTTV]], [
|
|
|
/droid.+aft(\w+)( bui|\))/i // Fire TV
|
|
|
], [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]], [
|
|
|
/\(dtv[\);].+(aquos)/i,
|
|
|
/(aquos-tv[\w ]+)\)/i // Sharp
|
|
|
], [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],[
|
|
|
/(bravia[\w ]+)( bui|\))/i // Sony
|
|
|
], [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]], [
|
|
|
/(mitv-\w{5}) bui/i // Xiaomi
|
|
|
], [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]], [
|
|
|
/Hbbtv.*(technisat) (.*);/i // TechniSAT
|
|
|
], [VENDOR, MODEL, [TYPE, SMARTTV]], [
|
|
|
/\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i, // Roku
|
|
|
/hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i // HbbTV devices
|
|
|
], [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]], [
|
|
|
/\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i // SmartTV from Unidentified Vendors
|
|
|
], [[TYPE, SMARTTV]], [
|
|
|
|
|
|
///////////////////
|
|
|
// CONSOLES
|
|
|
///////////////////
|
|
|
|
|
|
/(ouya)/i, // Ouya
|
|
|
/(nintendo) ([wids3utch]+)/i // Nintendo
|
|
|
], [VENDOR, MODEL, [TYPE, CONSOLE]], [
|
|
|
/droid.+; (shield) bui/i // Nvidia
|
|
|
], [MODEL, [VENDOR, 'Nvidia'], [TYPE, CONSOLE]], [
|
|
|
/(playstation [345portablevi]+)/i // Playstation
|
|
|
], [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]], [
|
|
|
/\b(xbox(?: one)?(?!; xbox))[\); ]/i // Microsoft Xbox
|
|
|
], [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]], [
|
|
|
|
|
|
///////////////////
|
|
|
// WEARABLES
|
|
|
///////////////////
|
|
|
|
|
|
/((pebble))app/i // Pebble
|
|
|
], [VENDOR, MODEL, [TYPE, WEARABLE]], [
|
|
|
/(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i // Apple Watch
|
|
|
], [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]], [
|
|
|
/droid.+; (glass) \d/i // Google Glass
|
|
|
], [MODEL, [VENDOR, GOOGLE], [TYPE, WEARABLE]], [
|
|
|
/droid.+; (wt63?0{2,3})\)/i
|
|
|
], [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]], [
|
|
|
/(quest( 2| pro)?)/i // Oculus Quest
|
|
|
], [MODEL, [VENDOR, FACEBOOK], [TYPE, WEARABLE]], [
|
|
|
|
|
|
///////////////////
|
|
|
// EMBEDDED
|
|
|
///////////////////
|
|
|
|
|
|
/(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i // Tesla
|
|
|
], [VENDOR, [TYPE, EMBEDDED]], [
|
|
|
/(aeobc)\b/i // Echo Dot
|
|
|
], [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]], [
|
|
|
|
|
|
////////////////////
|
|
|
// MIXED (GENERIC)
|
|
|
///////////////////
|
|
|
|
|
|
/droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i // Android Phones from Unidentified Vendors
|
|
|
], [MODEL, [TYPE, MOBILE]], [
|
|
|
/droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i // Android Tablets from Unidentified Vendors
|
|
|
], [MODEL, [TYPE, TABLET]], [
|
|
|
/\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i // Unidentifiable Tablet
|
|
|
], [[TYPE, TABLET]], [
|
|
|
/(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i // Unidentifiable Mobile
|
|
|
], [[TYPE, MOBILE]], [
|
|
|
/(android[-\w\. ]{0,9});.+buil/i // Generic Android Device
|
|
|
], [MODEL, [VENDOR, 'Generic']]
|
|
|
],
|
|
|
|
|
|
engine : [[
|
|
|
|
|
|
/windows.+ edge\/([\w\.]+)/i // EdgeHTML
|
|
|
], [VERSION, [NAME, EDGE+'HTML']], [
|
|
|
|
|
|
/webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i // Blink
|
|
|
], [VERSION, [NAME, 'Blink']], [
|
|
|
|
|
|
/(presto)\/([\w\.]+)/i, // Presto
|
|
|
/(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i, // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
|
|
|
/ekioh(flow)\/([\w\.]+)/i, // Flow
|
|
|
/(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i, // KHTML/Tasman/Links
|
|
|
/(icab)[\/ ]([23]\.[\d\.]+)/i, // iCab
|
|
|
/\b(libweb)/i
|
|
|
], [NAME, VERSION], [
|
|
|
|
|
|
/rv\:([\w\.]{1,9})\b.+(gecko)/i // Gecko
|
|
|
], [VERSION, NAME]
|
|
|
],
|
|
|
|
|
|
os : [[
|
|
|
|
|
|
// Windows
|
|
|
/microsoft (windows) (vista|xp)/i // Windows (iTunes)
|
|
|
], [NAME, VERSION], [
|
|
|
/(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i // Windows Phone
|
|
|
], [NAME, [VERSION, strMapper, windowsVersionMap]], [
|
|
|
/windows nt 6\.2; (arm)/i, // Windows RT
|
|
|
/windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i,
|
|
|
/(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i
|
|
|
], [[VERSION, strMapper, windowsVersionMap], [NAME, 'Windows']], [
|
|
|
|
|
|
// iOS/macOS
|
|
|
/ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i, // iOS
|
|
|
/(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
|
|
|
/cfnetwork\/.+darwin/i
|
|
|
], [[VERSION, /_/g, '.'], [NAME, 'iOS']], [
|
|
|
/(mac os x) ?([\w\. ]*)/i,
|
|
|
/(macintosh|mac_powerpc\b)(?!.+haiku)/i // Mac OS
|
|
|
], [[NAME, MAC_OS], [VERSION, /_/g, '.']], [
|
|
|
|
|
|
// Mobile OSes
|
|
|
/droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i // Android-x86/HarmonyOS
|
|
|
], [VERSION, NAME], [ // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS
|
|
|
/(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i,
|
|
|
/(blackberry)\w*\/([\w\.]*)/i, // Blackberry
|
|
|
/(tizen|kaios)[\/ ]([\w\.]+)/i, // Tizen/KaiOS
|
|
|
/\((series40);/i // Series 40
|
|
|
], [NAME, VERSION], [
|
|
|
/\(bb(10);/i // BlackBerry 10
|
|
|
], [VERSION, [NAME, BLACKBERRY]], [
|
|
|
/(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i // Symbian
|
|
|
], [VERSION, [NAME, 'Symbian']], [
|
|
|
/mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i // Firefox OS
|
|
|
], [VERSION, [NAME, FIREFOX+' OS']], [
|
|
|
/web0s;.+rt(tv)/i,
|
|
|
/\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i // WebOS
|
|
|
], [VERSION, [NAME, 'webOS']], [
|
|
|
/watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i // watchOS
|
|
|
], [VERSION, [NAME, 'watchOS']], [
|
|
|
|
|
|
// Google Chromecast
|
|
|
/crkey\/([\d\.]+)/i // Google Chromecast
|
|
|
], [VERSION, [NAME, CHROME+'cast']], [
|
|
|
/(cros) [\w]+(?:\)| ([\w\.]+)\b)/i // Chromium OS
|
|
|
], [[NAME, CHROMIUM_OS], VERSION],[
|
|
|
|
|
|
// Smart TVs
|
|
|
/panasonic;(viera)/i, // Panasonic Viera
|
|
|
/(netrange)mmh/i, // Netrange
|
|
|
/(nettv)\/(\d+\.[\w\.]+)/i, // NetTV
|
|
|
|
|
|
// Console
|
|
|
/(nintendo|playstation) ([wids345portablevuch]+)/i, // Nintendo/Playstation
|
|
|
/(xbox); +xbox ([^\);]+)/i, // Microsoft Xbox (360, One, X, S, Series X, Series S)
|
|
|
|
|
|
// Other
|
|
|
/\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i, // Joli/Palm
|
|
|
/(mint)[\/\(\) ]?(\w*)/i, // Mint
|
|
|
/(mageia|vectorlinux)[; ]/i, // Mageia/VectorLinux
|
|
|
/([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
|
|
|
// Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
|
|
|
/(hurd|linux) ?([\w\.]*)/i, // Hurd/Linux
|
|
|
/(gnu) ?([\w\.]*)/i, // GNU
|
|
|
/\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i, // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
|
|
|
/(haiku) (\w+)/i // Haiku
|
|
|
], [NAME, VERSION], [
|
|
|
/(sunos) ?([\w\.\d]*)/i // Solaris
|
|
|
], [[NAME, 'Solaris'], VERSION], [
|
|
|
/((?:open)?solaris)[-\/ ]?([\w\.]*)/i, // Solaris
|
|
|
/(aix) ((\d)(?=\.|\)| )[\w\.])*/i, // AIX
|
|
|
/\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i, // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
|
|
|
/(unix) ?([\w\.]*)/i // UNIX
|
|
|
], [NAME, VERSION]
|
|
|
]
|
|
|
};
|
|
|
|
|
|
/////////////////
|
|
|
// Constructor
|
|
|
////////////////
|
|
|
|
|
|
var UAParser = function (ua, extensions) {
|
|
|
|
|
|
if (typeof ua === OBJ_TYPE) {
|
|
|
extensions = ua;
|
|
|
ua = undefined;
|
|
|
}
|
|
|
|
|
|
if (!(this instanceof UAParser)) {
|
|
|
return new UAParser(ua, extensions).getResult();
|
|
|
}
|
|
|
|
|
|
var _navigator = (typeof window !== UNDEF_TYPE && window.navigator) ? window.navigator : undefined;
|
|
|
var _ua = ua || ((_navigator && _navigator.userAgent) ? _navigator.userAgent : EMPTY);
|
|
|
var _uach = (_navigator && _navigator.userAgentData) ? _navigator.userAgentData : undefined;
|
|
|
var _rgxmap = extensions ? extend(regexes, extensions) : regexes;
|
|
|
var _isSelfNav = _navigator && _navigator.userAgent == _ua;
|
|
|
|
|
|
this.getBrowser = function () {
|
|
|
var _browser = {};
|
|
|
_browser[NAME] = undefined;
|
|
|
_browser[VERSION] = undefined;
|
|
|
rgxMapper.call(_browser, _ua, _rgxmap.browser);
|
|
|
_browser[MAJOR] = majorize(_browser[VERSION]);
|
|
|
// Brave-specific detection
|
|
|
if (_isSelfNav && _navigator && _navigator.brave && typeof _navigator.brave.isBrave == FUNC_TYPE) {
|
|
|
_browser[NAME] = 'Brave';
|
|
|
}
|
|
|
return _browser;
|
|
|
};
|
|
|
this.getCPU = function () {
|
|
|
var _cpu = {};
|
|
|
_cpu[ARCHITECTURE] = undefined;
|
|
|
rgxMapper.call(_cpu, _ua, _rgxmap.cpu);
|
|
|
return _cpu;
|
|
|
};
|
|
|
this.getDevice = function () {
|
|
|
var _device = {};
|
|
|
_device[VENDOR] = undefined;
|
|
|
_device[MODEL] = undefined;
|
|
|
_device[TYPE] = undefined;
|
|
|
rgxMapper.call(_device, _ua, _rgxmap.device);
|
|
|
if (_isSelfNav && !_device[TYPE] && _uach && _uach.mobile) {
|
|
|
_device[TYPE] = MOBILE;
|
|
|
}
|
|
|
// iPadOS-specific detection: identified as Mac, but has some iOS-only properties
|
|
|
if (_isSelfNav && _device[MODEL] == 'Macintosh' && _navigator && typeof _navigator.standalone !== UNDEF_TYPE && _navigator.maxTouchPoints && _navigator.maxTouchPoints > 2) {
|
|
|
_device[MODEL] = 'iPad';
|
|
|
_device[TYPE] = TABLET;
|
|
|
}
|
|
|
return _device;
|
|
|
};
|
|
|
this.getEngine = function () {
|
|
|
var _engine = {};
|
|
|
_engine[NAME] = undefined;
|
|
|
_engine[VERSION] = undefined;
|
|
|
rgxMapper.call(_engine, _ua, _rgxmap.engine);
|
|
|
return _engine;
|
|
|
};
|
|
|
this.getOS = function () {
|
|
|
var _os = {};
|
|
|
_os[NAME] = undefined;
|
|
|
_os[VERSION] = undefined;
|
|
|
rgxMapper.call(_os, _ua, _rgxmap.os);
|
|
|
if (_isSelfNav && !_os[NAME] && _uach && _uach.platform != 'Unknown') {
|
|
|
_os[NAME] = _uach.platform
|
|
|
.replace(/chrome os/i, CHROMIUM_OS)
|
|
|
.replace(/macos/i, MAC_OS); // backward compatibility
|
|
|
}
|
|
|
return _os;
|
|
|
};
|
|
|
this.getResult = function () {
|
|
|
return {
|
|
|
ua : this.getUA(),
|
|
|
browser : this.getBrowser(),
|
|
|
engine : this.getEngine(),
|
|
|
os : this.getOS(),
|
|
|
device : this.getDevice(),
|
|
|
cpu : this.getCPU()
|
|
|
};
|
|
|
};
|
|
|
this.getUA = function () {
|
|
|
return _ua;
|
|
|
};
|
|
|
this.setUA = function (ua) {
|
|
|
_ua = (typeof ua === STR_TYPE && ua.length > UA_MAX_LENGTH) ? trim(ua, UA_MAX_LENGTH) : ua;
|
|
|
return this;
|
|
|
};
|
|
|
this.setUA(_ua);
|
|
|
return this;
|
|
|
};
|
|
|
|
|
|
UAParser.VERSION = LIBVERSION;
|
|
|
UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR]);
|
|
|
UAParser.CPU = enumerize([ARCHITECTURE]);
|
|
|
UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
|
|
|
UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);
|
|
|
|
|
|
///////////
|
|
|
// Export
|
|
|
//////////
|
|
|
|
|
|
// check js environment
|
|
|
if (typeof(exports) !== UNDEF_TYPE) {
|
|
|
// nodejs env
|
|
|
if ("object" !== UNDEF_TYPE && module.exports) {
|
|
|
exports = module.exports = UAParser;
|
|
|
}
|
|
|
exports.UAParser = UAParser;
|
|
|
} else {
|
|
|
// requirejs env (optional)
|
|
|
if ("function" === FUNC_TYPE && __webpack_require__.amdO) {
|
|
|
!(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
|
|
|
return UAParser;
|
|
|
}).call(exports, __webpack_require__, exports, module),
|
|
|
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
|
} else if (typeof window !== UNDEF_TYPE) {
|
|
|
// browser env
|
|
|
window.UAParser = UAParser;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// jQuery/Zepto specific (optional)
|
|
|
// Note:
|
|
|
// In AMD env the global scope should be kept clean, but jQuery is an exception.
|
|
|
// jQuery always exports to global scope, unless jQuery.noConflict(true) is used,
|
|
|
// and we should catch that.
|
|
|
var $ = typeof window !== UNDEF_TYPE && (window.jQuery || window.Zepto);
|
|
|
if ($ && !$.ua) {
|
|
|
var parser = new UAParser();
|
|
|
$.ua = parser.getResult();
|
|
|
$.ua.get = function () {
|
|
|
return parser.getUA();
|
|
|
};
|
|
|
$.ua.set = function (ua) {
|
|
|
parser.setUA(ua);
|
|
|
var result = parser.getResult();
|
|
|
for (var prop in result) {
|
|
|
$.ua[prop] = result[prop];
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|
|
|
})(typeof window === 'object' ? window : this);
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/adapter_core.js":
|
|
|
/*!************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/adapter_core.js ***!
|
|
|
\************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _adapter_factory_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adapter_factory.js */ "./node_modules/webrtc-adapter/src/js/adapter_factory.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const adapter =
|
|
|
(0,_adapter_factory_js__WEBPACK_IMPORTED_MODULE_0__.adapterFactory)({window: typeof window === 'undefined' ? undefined : window});
|
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (adapter);
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/adapter_factory.js":
|
|
|
/*!***************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/adapter_factory.js ***!
|
|
|
\***************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ adapterFactory: () => (/* binding */ adapterFactory)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/* harmony import */ var _chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./chrome/chrome_shim */ "./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js");
|
|
|
/* harmony import */ var _firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./firefox/firefox_shim */ "./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js");
|
|
|
/* harmony import */ var _safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./safari/safari_shim */ "./node_modules/webrtc-adapter/src/js/safari/safari_shim.js");
|
|
|
/* harmony import */ var _common_shim__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./common_shim */ "./node_modules/webrtc-adapter/src/js/common_shim.js");
|
|
|
/* harmony import */ var sdp__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! sdp */ "./node_modules/sdp/sdp.js");
|
|
|
/* harmony import */ var sdp__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(sdp__WEBPACK_IMPORTED_MODULE_5__);
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
|
|
|
|
|
|
// Browser shims.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Shimming starts here.
|
|
|
function adapterFactory({window} = {}, options = {
|
|
|
shimChrome: true,
|
|
|
shimFirefox: true,
|
|
|
shimSafari: true,
|
|
|
}) {
|
|
|
// Utils.
|
|
|
const logging = _utils__WEBPACK_IMPORTED_MODULE_0__.log;
|
|
|
const browserDetails = _utils__WEBPACK_IMPORTED_MODULE_0__.detectBrowser(window);
|
|
|
|
|
|
const adapter = {
|
|
|
browserDetails,
|
|
|
commonShim: _common_shim__WEBPACK_IMPORTED_MODULE_4__,
|
|
|
extractVersion: _utils__WEBPACK_IMPORTED_MODULE_0__.extractVersion,
|
|
|
disableLog: _utils__WEBPACK_IMPORTED_MODULE_0__.disableLog,
|
|
|
disableWarnings: _utils__WEBPACK_IMPORTED_MODULE_0__.disableWarnings,
|
|
|
// Expose sdp as a convenience. For production apps include directly.
|
|
|
sdp: sdp__WEBPACK_IMPORTED_MODULE_5__,
|
|
|
};
|
|
|
|
|
|
// Shim browser if found.
|
|
|
switch (browserDetails.browser) {
|
|
|
case 'chrome':
|
|
|
if (!_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__ || !_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimPeerConnection ||
|
|
|
!options.shimChrome) {
|
|
|
logging('Chrome shim is not included in this adapter release.');
|
|
|
return adapter;
|
|
|
}
|
|
|
if (browserDetails.version === null) {
|
|
|
logging('Chrome shim can not determine version, not shimming.');
|
|
|
return adapter;
|
|
|
}
|
|
|
logging('adapter.js shimming chrome.');
|
|
|
// Export to the adapter global object visible in the browser.
|
|
|
adapter.browserShim = _chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__;
|
|
|
|
|
|
// Must be called before shimPeerConnection.
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimAddIceCandidateNullOrEmpty(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimParameterlessSetLocalDescription(window, browserDetails);
|
|
|
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimGetUserMedia(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimMediaStream(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimPeerConnection(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimOnTrack(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimAddTrackRemoveTrack(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimGetSendersWithDtmf(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimGetStats(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.shimSenderReceiverGetStats(window, browserDetails);
|
|
|
_chrome_chrome_shim__WEBPACK_IMPORTED_MODULE_1__.fixNegotiationNeeded(window, browserDetails);
|
|
|
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimRTCIceCandidate(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimRTCIceCandidateRelayProtocol(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimConnectionState(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimMaxMessageSize(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimSendThrowTypeError(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.removeExtmapAllowMixed(window, browserDetails);
|
|
|
break;
|
|
|
case 'firefox':
|
|
|
if (!_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__ || !_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimPeerConnection ||
|
|
|
!options.shimFirefox) {
|
|
|
logging('Firefox shim is not included in this adapter release.');
|
|
|
return adapter;
|
|
|
}
|
|
|
logging('adapter.js shimming firefox.');
|
|
|
// Export to the adapter global object visible in the browser.
|
|
|
adapter.browserShim = _firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__;
|
|
|
|
|
|
// Must be called before shimPeerConnection.
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimAddIceCandidateNullOrEmpty(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimParameterlessSetLocalDescription(window, browserDetails);
|
|
|
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimGetUserMedia(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimPeerConnection(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimOnTrack(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimRemoveStream(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimSenderGetStats(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimReceiverGetStats(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimRTCDataChannel(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimAddTransceiver(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimGetParameters(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimCreateOffer(window, browserDetails);
|
|
|
_firefox_firefox_shim__WEBPACK_IMPORTED_MODULE_2__.shimCreateAnswer(window, browserDetails);
|
|
|
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimRTCIceCandidate(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimConnectionState(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimMaxMessageSize(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimSendThrowTypeError(window, browserDetails);
|
|
|
break;
|
|
|
case 'safari':
|
|
|
if (!_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__ || !options.shimSafari) {
|
|
|
logging('Safari shim is not included in this adapter release.');
|
|
|
return adapter;
|
|
|
}
|
|
|
logging('adapter.js shimming safari.');
|
|
|
// Export to the adapter global object visible in the browser.
|
|
|
adapter.browserShim = _safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__;
|
|
|
|
|
|
// Must be called before shimCallbackAPI.
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimAddIceCandidateNullOrEmpty(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimParameterlessSetLocalDescription(window, browserDetails);
|
|
|
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimRTCIceServerUrls(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimCreateOfferLegacy(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimCallbacksAPI(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimLocalStreamsAPI(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimRemoteStreamsAPI(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimTrackEventTransceiver(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimGetUserMedia(window, browserDetails);
|
|
|
_safari_safari_shim__WEBPACK_IMPORTED_MODULE_3__.shimAudioContext(window, browserDetails);
|
|
|
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimRTCIceCandidate(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimRTCIceCandidateRelayProtocol(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimMaxMessageSize(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.shimSendThrowTypeError(window, browserDetails);
|
|
|
_common_shim__WEBPACK_IMPORTED_MODULE_4__.removeExtmapAllowMixed(window, browserDetails);
|
|
|
break;
|
|
|
default:
|
|
|
logging('Unsupported browser!');
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
return adapter;
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js":
|
|
|
/*!******************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/chrome/chrome_shim.js ***!
|
|
|
\******************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ fixNegotiationNeeded: () => (/* binding */ fixNegotiationNeeded),
|
|
|
/* harmony export */ shimAddTrackRemoveTrack: () => (/* binding */ shimAddTrackRemoveTrack),
|
|
|
/* harmony export */ shimAddTrackRemoveTrackWithNative: () => (/* binding */ shimAddTrackRemoveTrackWithNative),
|
|
|
/* harmony export */ shimGetDisplayMedia: () => (/* reexport safe */ _getdisplaymedia__WEBPACK_IMPORTED_MODULE_2__.shimGetDisplayMedia),
|
|
|
/* harmony export */ shimGetSendersWithDtmf: () => (/* binding */ shimGetSendersWithDtmf),
|
|
|
/* harmony export */ shimGetStats: () => (/* binding */ shimGetStats),
|
|
|
/* harmony export */ shimGetUserMedia: () => (/* reexport safe */ _getusermedia__WEBPACK_IMPORTED_MODULE_1__.shimGetUserMedia),
|
|
|
/* harmony export */ shimMediaStream: () => (/* binding */ shimMediaStream),
|
|
|
/* harmony export */ shimOnTrack: () => (/* binding */ shimOnTrack),
|
|
|
/* harmony export */ shimPeerConnection: () => (/* binding */ shimPeerConnection),
|
|
|
/* harmony export */ shimSenderReceiverGetStats: () => (/* binding */ shimSenderReceiverGetStats)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils.js */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/* harmony import */ var _getusermedia__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./getusermedia */ "./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js");
|
|
|
/* harmony import */ var _getdisplaymedia__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./getdisplaymedia */ "./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function shimMediaStream(window) {
|
|
|
window.MediaStream = window.MediaStream || window.webkitMediaStream;
|
|
|
}
|
|
|
|
|
|
function shimOnTrack(window) {
|
|
|
if (typeof window === 'object' && window.RTCPeerConnection && !('ontrack' in
|
|
|
window.RTCPeerConnection.prototype)) {
|
|
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'ontrack', {
|
|
|
get() {
|
|
|
return this._ontrack;
|
|
|
},
|
|
|
set(f) {
|
|
|
if (this._ontrack) {
|
|
|
this.removeEventListener('track', this._ontrack);
|
|
|
}
|
|
|
this.addEventListener('track', this._ontrack = f);
|
|
|
},
|
|
|
enumerable: true,
|
|
|
configurable: true
|
|
|
});
|
|
|
const origSetRemoteDescription =
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription =
|
|
|
function setRemoteDescription() {
|
|
|
if (!this._ontrackpoly) {
|
|
|
this._ontrackpoly = (e) => {
|
|
|
// onaddstream does not fire when a track is added to an existing
|
|
|
// stream. But stream.onaddtrack is implemented so we use that.
|
|
|
e.stream.addEventListener('addtrack', te => {
|
|
|
let receiver;
|
|
|
if (window.RTCPeerConnection.prototype.getReceivers) {
|
|
|
receiver = this.getReceivers()
|
|
|
.find(r => r.track && r.track.id === te.track.id);
|
|
|
} else {
|
|
|
receiver = {track: te.track};
|
|
|
}
|
|
|
|
|
|
const event = new Event('track');
|
|
|
event.track = te.track;
|
|
|
event.receiver = receiver;
|
|
|
event.transceiver = {receiver};
|
|
|
event.streams = [e.stream];
|
|
|
this.dispatchEvent(event);
|
|
|
});
|
|
|
e.stream.getTracks().forEach(track => {
|
|
|
let receiver;
|
|
|
if (window.RTCPeerConnection.prototype.getReceivers) {
|
|
|
receiver = this.getReceivers()
|
|
|
.find(r => r.track && r.track.id === track.id);
|
|
|
} else {
|
|
|
receiver = {track};
|
|
|
}
|
|
|
const event = new Event('track');
|
|
|
event.track = track;
|
|
|
event.receiver = receiver;
|
|
|
event.transceiver = {receiver};
|
|
|
event.streams = [e.stream];
|
|
|
this.dispatchEvent(event);
|
|
|
});
|
|
|
};
|
|
|
this.addEventListener('addstream', this._ontrackpoly);
|
|
|
}
|
|
|
return origSetRemoteDescription.apply(this, arguments);
|
|
|
};
|
|
|
} else {
|
|
|
// even if RTCRtpTransceiver is in window, it is only used and
|
|
|
// emitted in unified-plan. Unfortunately this means we need
|
|
|
// to unconditionally wrap the event.
|
|
|
_utils_js__WEBPACK_IMPORTED_MODULE_0__.wrapPeerConnectionEvent(window, 'track', e => {
|
|
|
if (!e.transceiver) {
|
|
|
Object.defineProperty(e, 'transceiver',
|
|
|
{value: {receiver: e.receiver}});
|
|
|
}
|
|
|
return e;
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimGetSendersWithDtmf(window) {
|
|
|
// Overrides addTrack/removeTrack, depends on shimAddTrackRemoveTrack.
|
|
|
if (typeof window === 'object' && window.RTCPeerConnection &&
|
|
|
!('getSenders' in window.RTCPeerConnection.prototype) &&
|
|
|
'createDTMFSender' in window.RTCPeerConnection.prototype) {
|
|
|
const shimSenderWithDtmf = function(pc, track) {
|
|
|
return {
|
|
|
track,
|
|
|
get dtmf() {
|
|
|
if (this._dtmf === undefined) {
|
|
|
if (track.kind === 'audio') {
|
|
|
this._dtmf = pc.createDTMFSender(track);
|
|
|
} else {
|
|
|
this._dtmf = null;
|
|
|
}
|
|
|
}
|
|
|
return this._dtmf;
|
|
|
},
|
|
|
_pc: pc
|
|
|
};
|
|
|
};
|
|
|
|
|
|
// augment addTrack when getSenders is not available.
|
|
|
if (!window.RTCPeerConnection.prototype.getSenders) {
|
|
|
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
|
|
|
this._senders = this._senders || [];
|
|
|
return this._senders.slice(); // return a copy of the internal state.
|
|
|
};
|
|
|
const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
|
window.RTCPeerConnection.prototype.addTrack =
|
|
|
function addTrack(track, stream) {
|
|
|
let sender = origAddTrack.apply(this, arguments);
|
|
|
if (!sender) {
|
|
|
sender = shimSenderWithDtmf(this, track);
|
|
|
this._senders.push(sender);
|
|
|
}
|
|
|
return sender;
|
|
|
};
|
|
|
|
|
|
const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
|
|
|
window.RTCPeerConnection.prototype.removeTrack =
|
|
|
function removeTrack(sender) {
|
|
|
origRemoveTrack.apply(this, arguments);
|
|
|
const idx = this._senders.indexOf(sender);
|
|
|
if (idx !== -1) {
|
|
|
this._senders.splice(idx, 1);
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
const origAddStream = window.RTCPeerConnection.prototype.addStream;
|
|
|
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
|
|
|
this._senders = this._senders || [];
|
|
|
origAddStream.apply(this, [stream]);
|
|
|
stream.getTracks().forEach(track => {
|
|
|
this._senders.push(shimSenderWithDtmf(this, track));
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
|
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
|
function removeStream(stream) {
|
|
|
this._senders = this._senders || [];
|
|
|
origRemoveStream.apply(this, [stream]);
|
|
|
|
|
|
stream.getTracks().forEach(track => {
|
|
|
const sender = this._senders.find(s => s.track === track);
|
|
|
if (sender) { // remove sender
|
|
|
this._senders.splice(this._senders.indexOf(sender), 1);
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
} else if (typeof window === 'object' && window.RTCPeerConnection &&
|
|
|
'getSenders' in window.RTCPeerConnection.prototype &&
|
|
|
'createDTMFSender' in window.RTCPeerConnection.prototype &&
|
|
|
window.RTCRtpSender &&
|
|
|
!('dtmf' in window.RTCRtpSender.prototype)) {
|
|
|
const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
|
|
|
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
|
|
|
const senders = origGetSenders.apply(this, []);
|
|
|
senders.forEach(sender => sender._pc = this);
|
|
|
return senders;
|
|
|
};
|
|
|
|
|
|
Object.defineProperty(window.RTCRtpSender.prototype, 'dtmf', {
|
|
|
get() {
|
|
|
if (this._dtmf === undefined) {
|
|
|
if (this.track.kind === 'audio') {
|
|
|
this._dtmf = this._pc.createDTMFSender(this.track);
|
|
|
} else {
|
|
|
this._dtmf = null;
|
|
|
}
|
|
|
}
|
|
|
return this._dtmf;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimGetStats(window) {
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const origGetStats = window.RTCPeerConnection.prototype.getStats;
|
|
|
window.RTCPeerConnection.prototype.getStats = function getStats() {
|
|
|
const [selector, onSucc, onErr] = arguments;
|
|
|
|
|
|
// If selector is a function then we are in the old style stats so just
|
|
|
// pass back the original getStats format to avoid breaking old users.
|
|
|
if (arguments.length > 0 && typeof selector === 'function') {
|
|
|
return origGetStats.apply(this, arguments);
|
|
|
}
|
|
|
|
|
|
// When spec-style getStats is supported, return those when called with
|
|
|
// either no arguments or the selector argument is null.
|
|
|
if (origGetStats.length === 0 && (arguments.length === 0 ||
|
|
|
typeof selector !== 'function')) {
|
|
|
return origGetStats.apply(this, []);
|
|
|
}
|
|
|
|
|
|
const fixChromeStats_ = function(response) {
|
|
|
const standardReport = {};
|
|
|
const reports = response.result();
|
|
|
reports.forEach(report => {
|
|
|
const standardStats = {
|
|
|
id: report.id,
|
|
|
timestamp: report.timestamp,
|
|
|
type: {
|
|
|
localcandidate: 'local-candidate',
|
|
|
remotecandidate: 'remote-candidate'
|
|
|
}[report.type] || report.type
|
|
|
};
|
|
|
report.names().forEach(name => {
|
|
|
standardStats[name] = report.stat(name);
|
|
|
});
|
|
|
standardReport[standardStats.id] = standardStats;
|
|
|
});
|
|
|
|
|
|
return standardReport;
|
|
|
};
|
|
|
|
|
|
// shim getStats with maplike support
|
|
|
const makeMapStats = function(stats) {
|
|
|
return new Map(Object.keys(stats).map(key => [key, stats[key]]));
|
|
|
};
|
|
|
|
|
|
if (arguments.length >= 2) {
|
|
|
const successCallbackWrapper_ = function(response) {
|
|
|
onSucc(makeMapStats(fixChromeStats_(response)));
|
|
|
};
|
|
|
|
|
|
return origGetStats.apply(this, [successCallbackWrapper_,
|
|
|
selector]);
|
|
|
}
|
|
|
|
|
|
// promise-support
|
|
|
return new Promise((resolve, reject) => {
|
|
|
origGetStats.apply(this, [
|
|
|
function(response) {
|
|
|
resolve(makeMapStats(fixChromeStats_(response)));
|
|
|
}, reject]);
|
|
|
}).then(onSucc, onErr);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimSenderReceiverGetStats(window) {
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection &&
|
|
|
window.RTCRtpSender && window.RTCRtpReceiver)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// shim sender stats.
|
|
|
if (!('getStats' in window.RTCRtpSender.prototype)) {
|
|
|
const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
|
|
|
if (origGetSenders) {
|
|
|
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
|
|
|
const senders = origGetSenders.apply(this, []);
|
|
|
senders.forEach(sender => sender._pc = this);
|
|
|
return senders;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
|
if (origAddTrack) {
|
|
|
window.RTCPeerConnection.prototype.addTrack = function addTrack() {
|
|
|
const sender = origAddTrack.apply(this, arguments);
|
|
|
sender._pc = this;
|
|
|
return sender;
|
|
|
};
|
|
|
}
|
|
|
window.RTCRtpSender.prototype.getStats = function getStats() {
|
|
|
const sender = this;
|
|
|
return this._pc.getStats().then(result =>
|
|
|
/* Note: this will include stats of all senders that
|
|
|
* send a track with the same id as sender.track as
|
|
|
* it is not possible to identify the RTCRtpSender.
|
|
|
*/
|
|
|
_utils_js__WEBPACK_IMPORTED_MODULE_0__.filterStats(result, sender.track, true));
|
|
|
};
|
|
|
}
|
|
|
|
|
|
// shim receiver stats.
|
|
|
if (!('getStats' in window.RTCRtpReceiver.prototype)) {
|
|
|
const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
|
|
|
if (origGetReceivers) {
|
|
|
window.RTCPeerConnection.prototype.getReceivers =
|
|
|
function getReceivers() {
|
|
|
const receivers = origGetReceivers.apply(this, []);
|
|
|
receivers.forEach(receiver => receiver._pc = this);
|
|
|
return receivers;
|
|
|
};
|
|
|
}
|
|
|
_utils_js__WEBPACK_IMPORTED_MODULE_0__.wrapPeerConnectionEvent(window, 'track', e => {
|
|
|
e.receiver._pc = e.srcElement;
|
|
|
return e;
|
|
|
});
|
|
|
window.RTCRtpReceiver.prototype.getStats = function getStats() {
|
|
|
const receiver = this;
|
|
|
return this._pc.getStats().then(result =>
|
|
|
_utils_js__WEBPACK_IMPORTED_MODULE_0__.filterStats(result, receiver.track, false));
|
|
|
};
|
|
|
}
|
|
|
|
|
|
if (!('getStats' in window.RTCRtpSender.prototype &&
|
|
|
'getStats' in window.RTCRtpReceiver.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// shim RTCPeerConnection.getStats(track).
|
|
|
const origGetStats = window.RTCPeerConnection.prototype.getStats;
|
|
|
window.RTCPeerConnection.prototype.getStats = function getStats() {
|
|
|
if (arguments.length > 0 &&
|
|
|
arguments[0] instanceof window.MediaStreamTrack) {
|
|
|
const track = arguments[0];
|
|
|
let sender;
|
|
|
let receiver;
|
|
|
let err;
|
|
|
this.getSenders().forEach(s => {
|
|
|
if (s.track === track) {
|
|
|
if (sender) {
|
|
|
err = true;
|
|
|
} else {
|
|
|
sender = s;
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
this.getReceivers().forEach(r => {
|
|
|
if (r.track === track) {
|
|
|
if (receiver) {
|
|
|
err = true;
|
|
|
} else {
|
|
|
receiver = r;
|
|
|
}
|
|
|
}
|
|
|
return r.track === track;
|
|
|
});
|
|
|
if (err || (sender && receiver)) {
|
|
|
return Promise.reject(new DOMException(
|
|
|
'There are more than one sender or receiver for the track.',
|
|
|
'InvalidAccessError'));
|
|
|
} else if (sender) {
|
|
|
return sender.getStats();
|
|
|
} else if (receiver) {
|
|
|
return receiver.getStats();
|
|
|
}
|
|
|
return Promise.reject(new DOMException(
|
|
|
'There is no sender or receiver for the track.',
|
|
|
'InvalidAccessError'));
|
|
|
}
|
|
|
return origGetStats.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimAddTrackRemoveTrackWithNative(window) {
|
|
|
// shim addTrack/removeTrack with native variants in order to make
|
|
|
// the interactions with legacy getLocalStreams behave as in other browsers.
|
|
|
// Keeps a mapping stream.id => [stream, rtpsenders...]
|
|
|
window.RTCPeerConnection.prototype.getLocalStreams =
|
|
|
function getLocalStreams() {
|
|
|
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
|
|
|
return Object.keys(this._shimmedLocalStreams)
|
|
|
.map(streamId => this._shimmedLocalStreams[streamId][0]);
|
|
|
};
|
|
|
|
|
|
const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
|
window.RTCPeerConnection.prototype.addTrack =
|
|
|
function addTrack(track, stream) {
|
|
|
if (!stream) {
|
|
|
return origAddTrack.apply(this, arguments);
|
|
|
}
|
|
|
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
|
|
|
|
|
|
const sender = origAddTrack.apply(this, arguments);
|
|
|
if (!this._shimmedLocalStreams[stream.id]) {
|
|
|
this._shimmedLocalStreams[stream.id] = [stream, sender];
|
|
|
} else if (this._shimmedLocalStreams[stream.id].indexOf(sender) === -1) {
|
|
|
this._shimmedLocalStreams[stream.id].push(sender);
|
|
|
}
|
|
|
return sender;
|
|
|
};
|
|
|
|
|
|
const origAddStream = window.RTCPeerConnection.prototype.addStream;
|
|
|
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
|
|
|
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
|
|
|
|
|
|
stream.getTracks().forEach(track => {
|
|
|
const alreadyExists = this.getSenders().find(s => s.track === track);
|
|
|
if (alreadyExists) {
|
|
|
throw new DOMException('Track already exists.',
|
|
|
'InvalidAccessError');
|
|
|
}
|
|
|
});
|
|
|
const existingSenders = this.getSenders();
|
|
|
origAddStream.apply(this, arguments);
|
|
|
const newSenders = this.getSenders()
|
|
|
.filter(newSender => existingSenders.indexOf(newSender) === -1);
|
|
|
this._shimmedLocalStreams[stream.id] = [stream].concat(newSenders);
|
|
|
};
|
|
|
|
|
|
const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
|
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
|
function removeStream(stream) {
|
|
|
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
|
|
|
delete this._shimmedLocalStreams[stream.id];
|
|
|
return origRemoveStream.apply(this, arguments);
|
|
|
};
|
|
|
|
|
|
const origRemoveTrack = window.RTCPeerConnection.prototype.removeTrack;
|
|
|
window.RTCPeerConnection.prototype.removeTrack =
|
|
|
function removeTrack(sender) {
|
|
|
this._shimmedLocalStreams = this._shimmedLocalStreams || {};
|
|
|
if (sender) {
|
|
|
Object.keys(this._shimmedLocalStreams).forEach(streamId => {
|
|
|
const idx = this._shimmedLocalStreams[streamId].indexOf(sender);
|
|
|
if (idx !== -1) {
|
|
|
this._shimmedLocalStreams[streamId].splice(idx, 1);
|
|
|
}
|
|
|
if (this._shimmedLocalStreams[streamId].length === 1) {
|
|
|
delete this._shimmedLocalStreams[streamId];
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
return origRemoveTrack.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimAddTrackRemoveTrack(window, browserDetails) {
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
// shim addTrack and removeTrack.
|
|
|
if (window.RTCPeerConnection.prototype.addTrack &&
|
|
|
browserDetails.version >= 65) {
|
|
|
return shimAddTrackRemoveTrackWithNative(window);
|
|
|
}
|
|
|
|
|
|
// also shim pc.getLocalStreams when addTrack is shimmed
|
|
|
// to return the original streams.
|
|
|
const origGetLocalStreams = window.RTCPeerConnection.prototype
|
|
|
.getLocalStreams;
|
|
|
window.RTCPeerConnection.prototype.getLocalStreams =
|
|
|
function getLocalStreams() {
|
|
|
const nativeStreams = origGetLocalStreams.apply(this);
|
|
|
this._reverseStreams = this._reverseStreams || {};
|
|
|
return nativeStreams.map(stream => this._reverseStreams[stream.id]);
|
|
|
};
|
|
|
|
|
|
const origAddStream = window.RTCPeerConnection.prototype.addStream;
|
|
|
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
|
|
|
this._streams = this._streams || {};
|
|
|
this._reverseStreams = this._reverseStreams || {};
|
|
|
|
|
|
stream.getTracks().forEach(track => {
|
|
|
const alreadyExists = this.getSenders().find(s => s.track === track);
|
|
|
if (alreadyExists) {
|
|
|
throw new DOMException('Track already exists.',
|
|
|
'InvalidAccessError');
|
|
|
}
|
|
|
});
|
|
|
// Add identity mapping for consistency with addTrack.
|
|
|
// Unless this is being used with a stream from addTrack.
|
|
|
if (!this._reverseStreams[stream.id]) {
|
|
|
const newStream = new window.MediaStream(stream.getTracks());
|
|
|
this._streams[stream.id] = newStream;
|
|
|
this._reverseStreams[newStream.id] = stream;
|
|
|
stream = newStream;
|
|
|
}
|
|
|
origAddStream.apply(this, [stream]);
|
|
|
};
|
|
|
|
|
|
const origRemoveStream = window.RTCPeerConnection.prototype.removeStream;
|
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
|
function removeStream(stream) {
|
|
|
this._streams = this._streams || {};
|
|
|
this._reverseStreams = this._reverseStreams || {};
|
|
|
|
|
|
origRemoveStream.apply(this, [(this._streams[stream.id] || stream)]);
|
|
|
delete this._reverseStreams[(this._streams[stream.id] ?
|
|
|
this._streams[stream.id].id : stream.id)];
|
|
|
delete this._streams[stream.id];
|
|
|
};
|
|
|
|
|
|
window.RTCPeerConnection.prototype.addTrack =
|
|
|
function addTrack(track, stream) {
|
|
|
if (this.signalingState === 'closed') {
|
|
|
throw new DOMException(
|
|
|
'The RTCPeerConnection\'s signalingState is \'closed\'.',
|
|
|
'InvalidStateError');
|
|
|
}
|
|
|
const streams = [].slice.call(arguments, 1);
|
|
|
if (streams.length !== 1 ||
|
|
|
!streams[0].getTracks().find(t => t === track)) {
|
|
|
// this is not fully correct but all we can manage without
|
|
|
// [[associated MediaStreams]] internal slot.
|
|
|
throw new DOMException(
|
|
|
'The adapter.js addTrack polyfill only supports a single ' +
|
|
|
' stream which is associated with the specified track.',
|
|
|
'NotSupportedError');
|
|
|
}
|
|
|
|
|
|
const alreadyExists = this.getSenders().find(s => s.track === track);
|
|
|
if (alreadyExists) {
|
|
|
throw new DOMException('Track already exists.',
|
|
|
'InvalidAccessError');
|
|
|
}
|
|
|
|
|
|
this._streams = this._streams || {};
|
|
|
this._reverseStreams = this._reverseStreams || {};
|
|
|
const oldStream = this._streams[stream.id];
|
|
|
if (oldStream) {
|
|
|
// this is using odd Chrome behaviour, use with caution:
|
|
|
// https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
|
|
|
// Note: we rely on the high-level addTrack/dtmf shim to
|
|
|
// create the sender with a dtmf sender.
|
|
|
oldStream.addTrack(track);
|
|
|
|
|
|
// Trigger ONN async.
|
|
|
Promise.resolve().then(() => {
|
|
|
this.dispatchEvent(new Event('negotiationneeded'));
|
|
|
});
|
|
|
} else {
|
|
|
const newStream = new window.MediaStream([track]);
|
|
|
this._streams[stream.id] = newStream;
|
|
|
this._reverseStreams[newStream.id] = stream;
|
|
|
this.addStream(newStream);
|
|
|
}
|
|
|
return this.getSenders().find(s => s.track === track);
|
|
|
};
|
|
|
|
|
|
// replace the internal stream id with the external one and
|
|
|
// vice versa.
|
|
|
function replaceInternalStreamId(pc, description) {
|
|
|
let sdp = description.sdp;
|
|
|
Object.keys(pc._reverseStreams || []).forEach(internalId => {
|
|
|
const externalStream = pc._reverseStreams[internalId];
|
|
|
const internalStream = pc._streams[externalStream.id];
|
|
|
sdp = sdp.replace(new RegExp(internalStream.id, 'g'),
|
|
|
externalStream.id);
|
|
|
});
|
|
|
return new RTCSessionDescription({
|
|
|
type: description.type,
|
|
|
sdp
|
|
|
});
|
|
|
}
|
|
|
function replaceExternalStreamId(pc, description) {
|
|
|
let sdp = description.sdp;
|
|
|
Object.keys(pc._reverseStreams || []).forEach(internalId => {
|
|
|
const externalStream = pc._reverseStreams[internalId];
|
|
|
const internalStream = pc._streams[externalStream.id];
|
|
|
sdp = sdp.replace(new RegExp(externalStream.id, 'g'),
|
|
|
internalStream.id);
|
|
|
});
|
|
|
return new RTCSessionDescription({
|
|
|
type: description.type,
|
|
|
sdp
|
|
|
});
|
|
|
}
|
|
|
['createOffer', 'createAnswer'].forEach(function(method) {
|
|
|
const nativeMethod = window.RTCPeerConnection.prototype[method];
|
|
|
const methodObj = {[method]() {
|
|
|
const args = arguments;
|
|
|
const isLegacyCall = arguments.length &&
|
|
|
typeof arguments[0] === 'function';
|
|
|
if (isLegacyCall) {
|
|
|
return nativeMethod.apply(this, [
|
|
|
(description) => {
|
|
|
const desc = replaceInternalStreamId(this, description);
|
|
|
args[0].apply(null, [desc]);
|
|
|
},
|
|
|
(err) => {
|
|
|
if (args[1]) {
|
|
|
args[1].apply(null, err);
|
|
|
}
|
|
|
}, arguments[2]
|
|
|
]);
|
|
|
}
|
|
|
return nativeMethod.apply(this, arguments)
|
|
|
.then(description => replaceInternalStreamId(this, description));
|
|
|
}};
|
|
|
window.RTCPeerConnection.prototype[method] = methodObj[method];
|
|
|
});
|
|
|
|
|
|
const origSetLocalDescription =
|
|
|
window.RTCPeerConnection.prototype.setLocalDescription;
|
|
|
window.RTCPeerConnection.prototype.setLocalDescription =
|
|
|
function setLocalDescription() {
|
|
|
if (!arguments.length || !arguments[0].type) {
|
|
|
return origSetLocalDescription.apply(this, arguments);
|
|
|
}
|
|
|
arguments[0] = replaceExternalStreamId(this, arguments[0]);
|
|
|
return origSetLocalDescription.apply(this, arguments);
|
|
|
};
|
|
|
|
|
|
// TODO: mangle getStats: https://w3c.github.io/webrtc-stats/#dom-rtcmediastreamstats-streamidentifier
|
|
|
|
|
|
const origLocalDescription = Object.getOwnPropertyDescriptor(
|
|
|
window.RTCPeerConnection.prototype, 'localDescription');
|
|
|
Object.defineProperty(window.RTCPeerConnection.prototype,
|
|
|
'localDescription', {
|
|
|
get() {
|
|
|
const description = origLocalDescription.get.apply(this);
|
|
|
if (description.type === '') {
|
|
|
return description;
|
|
|
}
|
|
|
return replaceInternalStreamId(this, description);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
window.RTCPeerConnection.prototype.removeTrack =
|
|
|
function removeTrack(sender) {
|
|
|
if (this.signalingState === 'closed') {
|
|
|
throw new DOMException(
|
|
|
'The RTCPeerConnection\'s signalingState is \'closed\'.',
|
|
|
'InvalidStateError');
|
|
|
}
|
|
|
// We can not yet check for sender instanceof RTCRtpSender
|
|
|
// since we shim RTPSender. So we check if sender._pc is set.
|
|
|
if (!sender._pc) {
|
|
|
throw new DOMException('Argument 1 of RTCPeerConnection.removeTrack ' +
|
|
|
'does not implement interface RTCRtpSender.', 'TypeError');
|
|
|
}
|
|
|
const isLocal = sender._pc === this;
|
|
|
if (!isLocal) {
|
|
|
throw new DOMException('Sender was not created by this connection.',
|
|
|
'InvalidAccessError');
|
|
|
}
|
|
|
|
|
|
// Search for the native stream the senders track belongs to.
|
|
|
this._streams = this._streams || {};
|
|
|
let stream;
|
|
|
Object.keys(this._streams).forEach(streamid => {
|
|
|
const hasTrack = this._streams[streamid].getTracks()
|
|
|
.find(track => sender.track === track);
|
|
|
if (hasTrack) {
|
|
|
stream = this._streams[streamid];
|
|
|
}
|
|
|
});
|
|
|
|
|
|
if (stream) {
|
|
|
if (stream.getTracks().length === 1) {
|
|
|
// if this is the last track of the stream, remove the stream. This
|
|
|
// takes care of any shimmed _senders.
|
|
|
this.removeStream(this._reverseStreams[stream.id]);
|
|
|
} else {
|
|
|
// relying on the same odd chrome behaviour as above.
|
|
|
stream.removeTrack(sender.track);
|
|
|
}
|
|
|
this.dispatchEvent(new Event('negotiationneeded'));
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimPeerConnection(window, browserDetails) {
|
|
|
if (!window.RTCPeerConnection && window.webkitRTCPeerConnection) {
|
|
|
// very basic support for old versions.
|
|
|
window.RTCPeerConnection = window.webkitRTCPeerConnection;
|
|
|
}
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// shim implicit creation of RTCSessionDescription/RTCIceCandidate
|
|
|
if (browserDetails.version < 53) {
|
|
|
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
|
|
|
.forEach(function(method) {
|
|
|
const nativeMethod = window.RTCPeerConnection.prototype[method];
|
|
|
const methodObj = {[method]() {
|
|
|
arguments[0] = new ((method === 'addIceCandidate') ?
|
|
|
window.RTCIceCandidate :
|
|
|
window.RTCSessionDescription)(arguments[0]);
|
|
|
return nativeMethod.apply(this, arguments);
|
|
|
}};
|
|
|
window.RTCPeerConnection.prototype[method] = methodObj[method];
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Attempt to fix ONN in plan-b mode.
|
|
|
function fixNegotiationNeeded(window, browserDetails) {
|
|
|
_utils_js__WEBPACK_IMPORTED_MODULE_0__.wrapPeerConnectionEvent(window, 'negotiationneeded', e => {
|
|
|
const pc = e.target;
|
|
|
if (browserDetails.version < 72 || (pc.getConfiguration &&
|
|
|
pc.getConfiguration().sdpSemantics === 'plan-b')) {
|
|
|
if (pc.signalingState !== 'stable') {
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
return e;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js":
|
|
|
/*!**********************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/chrome/getdisplaymedia.js ***!
|
|
|
\**********************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimGetDisplayMedia: () => (/* binding */ shimGetDisplayMedia)
|
|
|
/* harmony export */ });
|
|
|
/*
|
|
|
* Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
function shimGetDisplayMedia(window, getSourceId) {
|
|
|
if (window.navigator.mediaDevices &&
|
|
|
'getDisplayMedia' in window.navigator.mediaDevices) {
|
|
|
return;
|
|
|
}
|
|
|
if (!(window.navigator.mediaDevices)) {
|
|
|
return;
|
|
|
}
|
|
|
// getSourceId is a function that returns a promise resolving with
|
|
|
// the sourceId of the screen/window/tab to be shared.
|
|
|
if (typeof getSourceId !== 'function') {
|
|
|
console.error('shimGetDisplayMedia: getSourceId argument is not ' +
|
|
|
'a function');
|
|
|
return;
|
|
|
}
|
|
|
window.navigator.mediaDevices.getDisplayMedia =
|
|
|
function getDisplayMedia(constraints) {
|
|
|
return getSourceId(constraints)
|
|
|
.then(sourceId => {
|
|
|
const widthSpecified = constraints.video && constraints.video.width;
|
|
|
const heightSpecified = constraints.video &&
|
|
|
constraints.video.height;
|
|
|
const frameRateSpecified = constraints.video &&
|
|
|
constraints.video.frameRate;
|
|
|
constraints.video = {
|
|
|
mandatory: {
|
|
|
chromeMediaSource: 'desktop',
|
|
|
chromeMediaSourceId: sourceId,
|
|
|
maxFrameRate: frameRateSpecified || 3
|
|
|
}
|
|
|
};
|
|
|
if (widthSpecified) {
|
|
|
constraints.video.mandatory.maxWidth = widthSpecified;
|
|
|
}
|
|
|
if (heightSpecified) {
|
|
|
constraints.video.mandatory.maxHeight = heightSpecified;
|
|
|
}
|
|
|
return window.navigator.mediaDevices.getUserMedia(constraints);
|
|
|
});
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js":
|
|
|
/*!*******************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/chrome/getusermedia.js ***!
|
|
|
\*******************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimGetUserMedia: () => (/* binding */ shimGetUserMedia)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils.js */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
const logging = _utils_js__WEBPACK_IMPORTED_MODULE_0__.log;
|
|
|
|
|
|
function shimGetUserMedia(window, browserDetails) {
|
|
|
const navigator = window && window.navigator;
|
|
|
|
|
|
if (!navigator.mediaDevices) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const constraintsToChrome_ = function(c) {
|
|
|
if (typeof c !== 'object' || c.mandatory || c.optional) {
|
|
|
return c;
|
|
|
}
|
|
|
const cc = {};
|
|
|
Object.keys(c).forEach(key => {
|
|
|
if (key === 'require' || key === 'advanced' || key === 'mediaSource') {
|
|
|
return;
|
|
|
}
|
|
|
const r = (typeof c[key] === 'object') ? c[key] : {ideal: c[key]};
|
|
|
if (r.exact !== undefined && typeof r.exact === 'number') {
|
|
|
r.min = r.max = r.exact;
|
|
|
}
|
|
|
const oldname_ = function(prefix, name) {
|
|
|
if (prefix) {
|
|
|
return prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
|
}
|
|
|
return (name === 'deviceId') ? 'sourceId' : name;
|
|
|
};
|
|
|
if (r.ideal !== undefined) {
|
|
|
cc.optional = cc.optional || [];
|
|
|
let oc = {};
|
|
|
if (typeof r.ideal === 'number') {
|
|
|
oc[oldname_('min', key)] = r.ideal;
|
|
|
cc.optional.push(oc);
|
|
|
oc = {};
|
|
|
oc[oldname_('max', key)] = r.ideal;
|
|
|
cc.optional.push(oc);
|
|
|
} else {
|
|
|
oc[oldname_('', key)] = r.ideal;
|
|
|
cc.optional.push(oc);
|
|
|
}
|
|
|
}
|
|
|
if (r.exact !== undefined && typeof r.exact !== 'number') {
|
|
|
cc.mandatory = cc.mandatory || {};
|
|
|
cc.mandatory[oldname_('', key)] = r.exact;
|
|
|
} else {
|
|
|
['min', 'max'].forEach(mix => {
|
|
|
if (r[mix] !== undefined) {
|
|
|
cc.mandatory = cc.mandatory || {};
|
|
|
cc.mandatory[oldname_(mix, key)] = r[mix];
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
if (c.advanced) {
|
|
|
cc.optional = (cc.optional || []).concat(c.advanced);
|
|
|
}
|
|
|
return cc;
|
|
|
};
|
|
|
|
|
|
const shimConstraints_ = function(constraints, func) {
|
|
|
if (browserDetails.version >= 61) {
|
|
|
return func(constraints);
|
|
|
}
|
|
|
constraints = JSON.parse(JSON.stringify(constraints));
|
|
|
if (constraints && typeof constraints.audio === 'object') {
|
|
|
const remap = function(obj, a, b) {
|
|
|
if (a in obj && !(b in obj)) {
|
|
|
obj[b] = obj[a];
|
|
|
delete obj[a];
|
|
|
}
|
|
|
};
|
|
|
constraints = JSON.parse(JSON.stringify(constraints));
|
|
|
remap(constraints.audio, 'autoGainControl', 'googAutoGainControl');
|
|
|
remap(constraints.audio, 'noiseSuppression', 'googNoiseSuppression');
|
|
|
constraints.audio = constraintsToChrome_(constraints.audio);
|
|
|
}
|
|
|
if (constraints && typeof constraints.video === 'object') {
|
|
|
// Shim facingMode for mobile & surface pro.
|
|
|
let face = constraints.video.facingMode;
|
|
|
face = face && ((typeof face === 'object') ? face : {ideal: face});
|
|
|
const getSupportedFacingModeLies = browserDetails.version < 66;
|
|
|
|
|
|
if ((face && (face.exact === 'user' || face.exact === 'environment' ||
|
|
|
face.ideal === 'user' || face.ideal === 'environment')) &&
|
|
|
!(navigator.mediaDevices.getSupportedConstraints &&
|
|
|
navigator.mediaDevices.getSupportedConstraints().facingMode &&
|
|
|
!getSupportedFacingModeLies)) {
|
|
|
delete constraints.video.facingMode;
|
|
|
let matches;
|
|
|
if (face.exact === 'environment' || face.ideal === 'environment') {
|
|
|
matches = ['back', 'rear'];
|
|
|
} else if (face.exact === 'user' || face.ideal === 'user') {
|
|
|
matches = ['front'];
|
|
|
}
|
|
|
if (matches) {
|
|
|
// Look for matches in label, or use last cam for back (typical).
|
|
|
return navigator.mediaDevices.enumerateDevices()
|
|
|
.then(devices => {
|
|
|
devices = devices.filter(d => d.kind === 'videoinput');
|
|
|
let dev = devices.find(d => matches.some(match =>
|
|
|
d.label.toLowerCase().includes(match)));
|
|
|
if (!dev && devices.length && matches.includes('back')) {
|
|
|
dev = devices[devices.length - 1]; // more likely the back cam
|
|
|
}
|
|
|
if (dev) {
|
|
|
constraints.video.deviceId = face.exact
|
|
|
? {exact: dev.deviceId}
|
|
|
: {ideal: dev.deviceId};
|
|
|
}
|
|
|
constraints.video = constraintsToChrome_(constraints.video);
|
|
|
logging('chrome: ' + JSON.stringify(constraints));
|
|
|
return func(constraints);
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
constraints.video = constraintsToChrome_(constraints.video);
|
|
|
}
|
|
|
logging('chrome: ' + JSON.stringify(constraints));
|
|
|
return func(constraints);
|
|
|
};
|
|
|
|
|
|
const shimError_ = function(e) {
|
|
|
if (browserDetails.version >= 64) {
|
|
|
return e;
|
|
|
}
|
|
|
return {
|
|
|
name: {
|
|
|
PermissionDeniedError: 'NotAllowedError',
|
|
|
PermissionDismissedError: 'NotAllowedError',
|
|
|
InvalidStateError: 'NotAllowedError',
|
|
|
DevicesNotFoundError: 'NotFoundError',
|
|
|
ConstraintNotSatisfiedError: 'OverconstrainedError',
|
|
|
TrackStartError: 'NotReadableError',
|
|
|
MediaDeviceFailedDueToShutdown: 'NotAllowedError',
|
|
|
MediaDeviceKillSwitchOn: 'NotAllowedError',
|
|
|
TabCaptureError: 'AbortError',
|
|
|
ScreenCaptureError: 'AbortError',
|
|
|
DeviceCaptureError: 'AbortError'
|
|
|
}[e.name] || e.name,
|
|
|
message: e.message,
|
|
|
constraint: e.constraint || e.constraintName,
|
|
|
toString() {
|
|
|
return this.name + (this.message && ': ') + this.message;
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
|
|
|
const getUserMedia_ = function(constraints, onSuccess, onError) {
|
|
|
shimConstraints_(constraints, c => {
|
|
|
navigator.webkitGetUserMedia(c, onSuccess, e => {
|
|
|
if (onError) {
|
|
|
onError(shimError_(e));
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
};
|
|
|
navigator.getUserMedia = getUserMedia_.bind(navigator);
|
|
|
|
|
|
// Even though Chrome 45 has navigator.mediaDevices and a getUserMedia
|
|
|
// function which returns a Promise, it does not accept spec-style
|
|
|
// constraints.
|
|
|
if (navigator.mediaDevices.getUserMedia) {
|
|
|
const origGetUserMedia = navigator.mediaDevices.getUserMedia.
|
|
|
bind(navigator.mediaDevices);
|
|
|
navigator.mediaDevices.getUserMedia = function(cs) {
|
|
|
return shimConstraints_(cs, c => origGetUserMedia(c).then(stream => {
|
|
|
if (c.audio && !stream.getAudioTracks().length ||
|
|
|
c.video && !stream.getVideoTracks().length) {
|
|
|
stream.getTracks().forEach(track => {
|
|
|
track.stop();
|
|
|
});
|
|
|
throw new DOMException('', 'NotFoundError');
|
|
|
}
|
|
|
return stream;
|
|
|
}, e => Promise.reject(shimError_(e))));
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/common_shim.js":
|
|
|
/*!***********************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/common_shim.js ***!
|
|
|
\***********************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ removeExtmapAllowMixed: () => (/* binding */ removeExtmapAllowMixed),
|
|
|
/* harmony export */ shimAddIceCandidateNullOrEmpty: () => (/* binding */ shimAddIceCandidateNullOrEmpty),
|
|
|
/* harmony export */ shimConnectionState: () => (/* binding */ shimConnectionState),
|
|
|
/* harmony export */ shimMaxMessageSize: () => (/* binding */ shimMaxMessageSize),
|
|
|
/* harmony export */ shimParameterlessSetLocalDescription: () => (/* binding */ shimParameterlessSetLocalDescription),
|
|
|
/* harmony export */ shimRTCIceCandidate: () => (/* binding */ shimRTCIceCandidate),
|
|
|
/* harmony export */ shimRTCIceCandidateRelayProtocol: () => (/* binding */ shimRTCIceCandidateRelayProtocol),
|
|
|
/* harmony export */ shimSendThrowTypeError: () => (/* binding */ shimSendThrowTypeError)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var sdp__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sdp */ "./node_modules/sdp/sdp.js");
|
|
|
/* harmony import */ var sdp__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sdp__WEBPACK_IMPORTED_MODULE_0__);
|
|
|
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./utils */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/*
|
|
|
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function shimRTCIceCandidate(window) {
|
|
|
// foundation is arbitrarily chosen as an indicator for full support for
|
|
|
// https://w3c.github.io/webrtc-pc/#rtcicecandidate-interface
|
|
|
if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'foundation' in
|
|
|
window.RTCIceCandidate.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const NativeRTCIceCandidate = window.RTCIceCandidate;
|
|
|
window.RTCIceCandidate = function RTCIceCandidate(args) {
|
|
|
// Remove the a= which shouldn't be part of the candidate string.
|
|
|
if (typeof args === 'object' && args.candidate &&
|
|
|
args.candidate.indexOf('a=') === 0) {
|
|
|
args = JSON.parse(JSON.stringify(args));
|
|
|
args.candidate = args.candidate.substring(2);
|
|
|
}
|
|
|
|
|
|
if (args.candidate && args.candidate.length) {
|
|
|
// Augment the native candidate with the parsed fields.
|
|
|
const nativeCandidate = new NativeRTCIceCandidate(args);
|
|
|
const parsedCandidate = sdp__WEBPACK_IMPORTED_MODULE_0___default().parseCandidate(args.candidate);
|
|
|
for (const key in parsedCandidate) {
|
|
|
if (!(key in nativeCandidate)) {
|
|
|
Object.defineProperty(nativeCandidate, key,
|
|
|
{value: parsedCandidate[key]});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Override serializer to not serialize the extra attributes.
|
|
|
nativeCandidate.toJSON = function toJSON() {
|
|
|
return {
|
|
|
candidate: nativeCandidate.candidate,
|
|
|
sdpMid: nativeCandidate.sdpMid,
|
|
|
sdpMLineIndex: nativeCandidate.sdpMLineIndex,
|
|
|
usernameFragment: nativeCandidate.usernameFragment,
|
|
|
};
|
|
|
};
|
|
|
return nativeCandidate;
|
|
|
}
|
|
|
return new NativeRTCIceCandidate(args);
|
|
|
};
|
|
|
window.RTCIceCandidate.prototype = NativeRTCIceCandidate.prototype;
|
|
|
|
|
|
// Hook up the augmented candidate in onicecandidate and
|
|
|
// addEventListener('icecandidate', ...)
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_1__.wrapPeerConnectionEvent(window, 'icecandidate', e => {
|
|
|
if (e.candidate) {
|
|
|
Object.defineProperty(e, 'candidate', {
|
|
|
value: new window.RTCIceCandidate(e.candidate),
|
|
|
writable: 'false'
|
|
|
});
|
|
|
}
|
|
|
return e;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function shimRTCIceCandidateRelayProtocol(window) {
|
|
|
if (!window.RTCIceCandidate || (window.RTCIceCandidate && 'relayProtocol' in
|
|
|
window.RTCIceCandidate.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Hook up the augmented candidate in onicecandidate and
|
|
|
// addEventListener('icecandidate', ...)
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_1__.wrapPeerConnectionEvent(window, 'icecandidate', e => {
|
|
|
if (e.candidate) {
|
|
|
const parsedCandidate = sdp__WEBPACK_IMPORTED_MODULE_0___default().parseCandidate(e.candidate.candidate);
|
|
|
if (parsedCandidate.type === 'relay') {
|
|
|
// This is a libwebrtc-specific mapping of local type preference
|
|
|
// to relayProtocol.
|
|
|
e.candidate.relayProtocol = {
|
|
|
0: 'tls',
|
|
|
1: 'tcp',
|
|
|
2: 'udp',
|
|
|
}[parsedCandidate.priority >> 24];
|
|
|
}
|
|
|
}
|
|
|
return e;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function shimMaxMessageSize(window, browserDetails) {
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (!('sctp' in window.RTCPeerConnection.prototype)) {
|
|
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'sctp', {
|
|
|
get() {
|
|
|
return typeof this._sctp === 'undefined' ? null : this._sctp;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
const sctpInDescription = function(description) {
|
|
|
if (!description || !description.sdp) {
|
|
|
return false;
|
|
|
}
|
|
|
const sections = sdp__WEBPACK_IMPORTED_MODULE_0___default().splitSections(description.sdp);
|
|
|
sections.shift();
|
|
|
return sections.some(mediaSection => {
|
|
|
const mLine = sdp__WEBPACK_IMPORTED_MODULE_0___default().parseMLine(mediaSection);
|
|
|
return mLine && mLine.kind === 'application'
|
|
|
&& mLine.protocol.indexOf('SCTP') !== -1;
|
|
|
});
|
|
|
};
|
|
|
|
|
|
const getRemoteFirefoxVersion = function(description) {
|
|
|
// TODO: Is there a better solution for detecting Firefox?
|
|
|
const match = description.sdp.match(/mozilla...THIS_IS_SDPARTA-(\d+)/);
|
|
|
if (match === null || match.length < 2) {
|
|
|
return -1;
|
|
|
}
|
|
|
const version = parseInt(match[1], 10);
|
|
|
// Test for NaN (yes, this is ugly)
|
|
|
return version !== version ? -1 : version;
|
|
|
};
|
|
|
|
|
|
const getCanSendMaxMessageSize = function(remoteIsFirefox) {
|
|
|
// Every implementation we know can send at least 64 KiB.
|
|
|
// Note: Although Chrome is technically able to send up to 256 KiB, the
|
|
|
// data does not reach the other peer reliably.
|
|
|
// See: https://bugs.chromium.org/p/webrtc/issues/detail?id=8419
|
|
|
let canSendMaxMessageSize = 65536;
|
|
|
if (browserDetails.browser === 'firefox') {
|
|
|
if (browserDetails.version < 57) {
|
|
|
if (remoteIsFirefox === -1) {
|
|
|
// FF < 57 will send in 16 KiB chunks using the deprecated PPID
|
|
|
// fragmentation.
|
|
|
canSendMaxMessageSize = 16384;
|
|
|
} else {
|
|
|
// However, other FF (and RAWRTC) can reassemble PPID-fragmented
|
|
|
// messages. Thus, supporting ~2 GiB when sending.
|
|
|
canSendMaxMessageSize = 2147483637;
|
|
|
}
|
|
|
} else if (browserDetails.version < 60) {
|
|
|
// Currently, all FF >= 57 will reset the remote maximum message size
|
|
|
// to the default value when a data channel is created at a later
|
|
|
// stage. :(
|
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
|
|
|
canSendMaxMessageSize =
|
|
|
browserDetails.version === 57 ? 65535 : 65536;
|
|
|
} else {
|
|
|
// FF >= 60 supports sending ~2 GiB
|
|
|
canSendMaxMessageSize = 2147483637;
|
|
|
}
|
|
|
}
|
|
|
return canSendMaxMessageSize;
|
|
|
};
|
|
|
|
|
|
const getMaxMessageSize = function(description, remoteIsFirefox) {
|
|
|
// Note: 65536 bytes is the default value from the SDP spec. Also,
|
|
|
// every implementation we know supports receiving 65536 bytes.
|
|
|
let maxMessageSize = 65536;
|
|
|
|
|
|
// FF 57 has a slightly incorrect default remote max message size, so
|
|
|
// we need to adjust it here to avoid a failure when sending.
|
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1425697
|
|
|
if (browserDetails.browser === 'firefox'
|
|
|
&& browserDetails.version === 57) {
|
|
|
maxMessageSize = 65535;
|
|
|
}
|
|
|
|
|
|
const match = sdp__WEBPACK_IMPORTED_MODULE_0___default().matchPrefix(description.sdp,
|
|
|
'a=max-message-size:');
|
|
|
if (match.length > 0) {
|
|
|
maxMessageSize = parseInt(match[0].substring(19), 10);
|
|
|
} else if (browserDetails.browser === 'firefox' &&
|
|
|
remoteIsFirefox !== -1) {
|
|
|
// If the maximum message size is not present in the remote SDP and
|
|
|
// both local and remote are Firefox, the remote peer can receive
|
|
|
// ~2 GiB.
|
|
|
maxMessageSize = 2147483637;
|
|
|
}
|
|
|
return maxMessageSize;
|
|
|
};
|
|
|
|
|
|
const origSetRemoteDescription =
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription =
|
|
|
function setRemoteDescription() {
|
|
|
this._sctp = null;
|
|
|
// Chrome decided to not expose .sctp in plan-b mode.
|
|
|
// As usual, adapter.js has to do an 'ugly worakaround'
|
|
|
// to cover up the mess.
|
|
|
if (browserDetails.browser === 'chrome' && browserDetails.version >= 76) {
|
|
|
const {sdpSemantics} = this.getConfiguration();
|
|
|
if (sdpSemantics === 'plan-b') {
|
|
|
Object.defineProperty(this, 'sctp', {
|
|
|
get() {
|
|
|
return typeof this._sctp === 'undefined' ? null : this._sctp;
|
|
|
},
|
|
|
enumerable: true,
|
|
|
configurable: true,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (sctpInDescription(arguments[0])) {
|
|
|
// Check if the remote is FF.
|
|
|
const isFirefox = getRemoteFirefoxVersion(arguments[0]);
|
|
|
|
|
|
// Get the maximum message size the local peer is capable of sending
|
|
|
const canSendMMS = getCanSendMaxMessageSize(isFirefox);
|
|
|
|
|
|
// Get the maximum message size of the remote peer.
|
|
|
const remoteMMS = getMaxMessageSize(arguments[0], isFirefox);
|
|
|
|
|
|
// Determine final maximum message size
|
|
|
let maxMessageSize;
|
|
|
if (canSendMMS === 0 && remoteMMS === 0) {
|
|
|
maxMessageSize = Number.POSITIVE_INFINITY;
|
|
|
} else if (canSendMMS === 0 || remoteMMS === 0) {
|
|
|
maxMessageSize = Math.max(canSendMMS, remoteMMS);
|
|
|
} else {
|
|
|
maxMessageSize = Math.min(canSendMMS, remoteMMS);
|
|
|
}
|
|
|
|
|
|
// Create a dummy RTCSctpTransport object and the 'maxMessageSize'
|
|
|
// attribute.
|
|
|
const sctp = {};
|
|
|
Object.defineProperty(sctp, 'maxMessageSize', {
|
|
|
get() {
|
|
|
return maxMessageSize;
|
|
|
}
|
|
|
});
|
|
|
this._sctp = sctp;
|
|
|
}
|
|
|
|
|
|
return origSetRemoteDescription.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimSendThrowTypeError(window) {
|
|
|
if (!(window.RTCPeerConnection &&
|
|
|
'createDataChannel' in window.RTCPeerConnection.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// Note: Although Firefox >= 57 has a native implementation, the maximum
|
|
|
// message size can be reset for all data channels at a later stage.
|
|
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1426831
|
|
|
|
|
|
function wrapDcSend(dc, pc) {
|
|
|
const origDataChannelSend = dc.send;
|
|
|
dc.send = function send() {
|
|
|
const data = arguments[0];
|
|
|
const length = data.length || data.size || data.byteLength;
|
|
|
if (dc.readyState === 'open' &&
|
|
|
pc.sctp && length > pc.sctp.maxMessageSize) {
|
|
|
throw new TypeError('Message too large (can send a maximum of ' +
|
|
|
pc.sctp.maxMessageSize + ' bytes)');
|
|
|
}
|
|
|
return origDataChannelSend.apply(dc, arguments);
|
|
|
};
|
|
|
}
|
|
|
const origCreateDataChannel =
|
|
|
window.RTCPeerConnection.prototype.createDataChannel;
|
|
|
window.RTCPeerConnection.prototype.createDataChannel =
|
|
|
function createDataChannel() {
|
|
|
const dataChannel = origCreateDataChannel.apply(this, arguments);
|
|
|
wrapDcSend(dataChannel, this);
|
|
|
return dataChannel;
|
|
|
};
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_1__.wrapPeerConnectionEvent(window, 'datachannel', e => {
|
|
|
wrapDcSend(e.channel, e.target);
|
|
|
return e;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
/* shims RTCConnectionState by pretending it is the same as iceConnectionState.
|
|
|
* See https://bugs.chromium.org/p/webrtc/issues/detail?id=6145#c12
|
|
|
* for why this is a valid hack in Chrome. In Firefox it is slightly incorrect
|
|
|
* since DTLS failures would be hidden. See
|
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=1265827
|
|
|
* for the Firefox tracking bug.
|
|
|
*/
|
|
|
function shimConnectionState(window) {
|
|
|
if (!window.RTCPeerConnection ||
|
|
|
'connectionState' in window.RTCPeerConnection.prototype) {
|
|
|
return;
|
|
|
}
|
|
|
const proto = window.RTCPeerConnection.prototype;
|
|
|
Object.defineProperty(proto, 'connectionState', {
|
|
|
get() {
|
|
|
return {
|
|
|
completed: 'connected',
|
|
|
checking: 'connecting'
|
|
|
}[this.iceConnectionState] || this.iceConnectionState;
|
|
|
},
|
|
|
enumerable: true,
|
|
|
configurable: true
|
|
|
});
|
|
|
Object.defineProperty(proto, 'onconnectionstatechange', {
|
|
|
get() {
|
|
|
return this._onconnectionstatechange || null;
|
|
|
},
|
|
|
set(cb) {
|
|
|
if (this._onconnectionstatechange) {
|
|
|
this.removeEventListener('connectionstatechange',
|
|
|
this._onconnectionstatechange);
|
|
|
delete this._onconnectionstatechange;
|
|
|
}
|
|
|
if (cb) {
|
|
|
this.addEventListener('connectionstatechange',
|
|
|
this._onconnectionstatechange = cb);
|
|
|
}
|
|
|
},
|
|
|
enumerable: true,
|
|
|
configurable: true
|
|
|
});
|
|
|
|
|
|
['setLocalDescription', 'setRemoteDescription'].forEach((method) => {
|
|
|
const origMethod = proto[method];
|
|
|
proto[method] = function() {
|
|
|
if (!this._connectionstatechangepoly) {
|
|
|
this._connectionstatechangepoly = e => {
|
|
|
const pc = e.target;
|
|
|
if (pc._lastConnectionState !== pc.connectionState) {
|
|
|
pc._lastConnectionState = pc.connectionState;
|
|
|
const newEvent = new Event('connectionstatechange', e);
|
|
|
pc.dispatchEvent(newEvent);
|
|
|
}
|
|
|
return e;
|
|
|
};
|
|
|
this.addEventListener('iceconnectionstatechange',
|
|
|
this._connectionstatechangepoly);
|
|
|
}
|
|
|
return origMethod.apply(this, arguments);
|
|
|
};
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function removeExtmapAllowMixed(window, browserDetails) {
|
|
|
/* remove a=extmap-allow-mixed for webrtc.org < M71 */
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
if (browserDetails.browser === 'chrome' && browserDetails.version >= 71) {
|
|
|
return;
|
|
|
}
|
|
|
if (browserDetails.browser === 'safari' && browserDetails.version >= 605) {
|
|
|
return;
|
|
|
}
|
|
|
const nativeSRD = window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription =
|
|
|
function setRemoteDescription(desc) {
|
|
|
if (desc && desc.sdp && desc.sdp.indexOf('\na=extmap-allow-mixed') !== -1) {
|
|
|
const sdp = desc.sdp.split('\n').filter((line) => {
|
|
|
return line.trim() !== 'a=extmap-allow-mixed';
|
|
|
}).join('\n');
|
|
|
// Safari enforces read-only-ness of RTCSessionDescription fields.
|
|
|
if (window.RTCSessionDescription &&
|
|
|
desc instanceof window.RTCSessionDescription) {
|
|
|
arguments[0] = new window.RTCSessionDescription({
|
|
|
type: desc.type,
|
|
|
sdp,
|
|
|
});
|
|
|
} else {
|
|
|
desc.sdp = sdp;
|
|
|
}
|
|
|
}
|
|
|
return nativeSRD.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimAddIceCandidateNullOrEmpty(window, browserDetails) {
|
|
|
// Support for addIceCandidate(null or undefined)
|
|
|
// as well as addIceCandidate({candidate: "", ...})
|
|
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=978582
|
|
|
// Note: must be called before other polyfills which change the signature.
|
|
|
if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
const nativeAddIceCandidate =
|
|
|
window.RTCPeerConnection.prototype.addIceCandidate;
|
|
|
if (!nativeAddIceCandidate || nativeAddIceCandidate.length === 0) {
|
|
|
return;
|
|
|
}
|
|
|
window.RTCPeerConnection.prototype.addIceCandidate =
|
|
|
function addIceCandidate() {
|
|
|
if (!arguments[0]) {
|
|
|
if (arguments[1]) {
|
|
|
arguments[1].apply(null);
|
|
|
}
|
|
|
return Promise.resolve();
|
|
|
}
|
|
|
// Firefox 68+ emits and processes {candidate: "", ...}, ignore
|
|
|
// in older versions.
|
|
|
// Native support for ignoring exists for Chrome M77+.
|
|
|
// Safari ignores as well, exact version unknown but works in the same
|
|
|
// version that also ignores addIceCandidate(null).
|
|
|
if (((browserDetails.browser === 'chrome' && browserDetails.version < 78)
|
|
|
|| (browserDetails.browser === 'firefox'
|
|
|
&& browserDetails.version < 68)
|
|
|
|| (browserDetails.browser === 'safari'))
|
|
|
&& arguments[0] && arguments[0].candidate === '') {
|
|
|
return Promise.resolve();
|
|
|
}
|
|
|
return nativeAddIceCandidate.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
// Note: Make sure to call this ahead of APIs that modify
|
|
|
// setLocalDescription.length
|
|
|
function shimParameterlessSetLocalDescription(window, browserDetails) {
|
|
|
if (!(window.RTCPeerConnection && window.RTCPeerConnection.prototype)) {
|
|
|
return;
|
|
|
}
|
|
|
const nativeSetLocalDescription =
|
|
|
window.RTCPeerConnection.prototype.setLocalDescription;
|
|
|
if (!nativeSetLocalDescription || nativeSetLocalDescription.length === 0) {
|
|
|
return;
|
|
|
}
|
|
|
window.RTCPeerConnection.prototype.setLocalDescription =
|
|
|
function setLocalDescription() {
|
|
|
let desc = arguments[0] || {};
|
|
|
if (typeof desc !== 'object' || (desc.type && desc.sdp)) {
|
|
|
return nativeSetLocalDescription.apply(this, arguments);
|
|
|
}
|
|
|
// The remaining steps should technically happen when SLD comes off the
|
|
|
// RTCPeerConnection's operations chain (not ahead of going on it), but
|
|
|
// this is too difficult to shim. Instead, this shim only covers the
|
|
|
// common case where the operations chain is empty. This is imperfect, but
|
|
|
// should cover many cases. Rationale: Even if we can't reduce the glare
|
|
|
// window to zero on imperfect implementations, there's value in tapping
|
|
|
// into the perfect negotiation pattern that several browsers support.
|
|
|
desc = {type: desc.type, sdp: desc.sdp};
|
|
|
if (!desc.type) {
|
|
|
switch (this.signalingState) {
|
|
|
case 'stable':
|
|
|
case 'have-local-offer':
|
|
|
case 'have-remote-pranswer':
|
|
|
desc.type = 'offer';
|
|
|
break;
|
|
|
default:
|
|
|
desc.type = 'answer';
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
if (desc.sdp || (desc.type !== 'offer' && desc.type !== 'answer')) {
|
|
|
return nativeSetLocalDescription.apply(this, [desc]);
|
|
|
}
|
|
|
const func = desc.type === 'offer' ? this.createOffer : this.createAnswer;
|
|
|
return func.apply(this)
|
|
|
.then(d => nativeSetLocalDescription.apply(this, [d]));
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js":
|
|
|
/*!********************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/firefox/firefox_shim.js ***!
|
|
|
\********************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimAddTransceiver: () => (/* binding */ shimAddTransceiver),
|
|
|
/* harmony export */ shimCreateAnswer: () => (/* binding */ shimCreateAnswer),
|
|
|
/* harmony export */ shimCreateOffer: () => (/* binding */ shimCreateOffer),
|
|
|
/* harmony export */ shimGetDisplayMedia: () => (/* reexport safe */ _getdisplaymedia__WEBPACK_IMPORTED_MODULE_2__.shimGetDisplayMedia),
|
|
|
/* harmony export */ shimGetParameters: () => (/* binding */ shimGetParameters),
|
|
|
/* harmony export */ shimGetUserMedia: () => (/* reexport safe */ _getusermedia__WEBPACK_IMPORTED_MODULE_1__.shimGetUserMedia),
|
|
|
/* harmony export */ shimOnTrack: () => (/* binding */ shimOnTrack),
|
|
|
/* harmony export */ shimPeerConnection: () => (/* binding */ shimPeerConnection),
|
|
|
/* harmony export */ shimRTCDataChannel: () => (/* binding */ shimRTCDataChannel),
|
|
|
/* harmony export */ shimReceiverGetStats: () => (/* binding */ shimReceiverGetStats),
|
|
|
/* harmony export */ shimRemoveStream: () => (/* binding */ shimRemoveStream),
|
|
|
/* harmony export */ shimSenderGetStats: () => (/* binding */ shimSenderGetStats)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/* harmony import */ var _getusermedia__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./getusermedia */ "./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js");
|
|
|
/* harmony import */ var _getdisplaymedia__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./getdisplaymedia */ "./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function shimOnTrack(window) {
|
|
|
if (typeof window === 'object' && window.RTCTrackEvent &&
|
|
|
('receiver' in window.RTCTrackEvent.prototype) &&
|
|
|
!('transceiver' in window.RTCTrackEvent.prototype)) {
|
|
|
Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
|
|
|
get() {
|
|
|
return {receiver: this.receiver};
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimPeerConnection(window, browserDetails) {
|
|
|
if (typeof window !== 'object' ||
|
|
|
!(window.RTCPeerConnection || window.mozRTCPeerConnection)) {
|
|
|
return; // probably media.peerconnection.enabled=false in about:config
|
|
|
}
|
|
|
if (!window.RTCPeerConnection && window.mozRTCPeerConnection) {
|
|
|
// very basic support for old versions.
|
|
|
window.RTCPeerConnection = window.mozRTCPeerConnection;
|
|
|
}
|
|
|
|
|
|
if (browserDetails.version < 53) {
|
|
|
// shim away need for obsolete RTCIceCandidate/RTCSessionDescription.
|
|
|
['setLocalDescription', 'setRemoteDescription', 'addIceCandidate']
|
|
|
.forEach(function(method) {
|
|
|
const nativeMethod = window.RTCPeerConnection.prototype[method];
|
|
|
const methodObj = {[method]() {
|
|
|
arguments[0] = new ((method === 'addIceCandidate') ?
|
|
|
window.RTCIceCandidate :
|
|
|
window.RTCSessionDescription)(arguments[0]);
|
|
|
return nativeMethod.apply(this, arguments);
|
|
|
}};
|
|
|
window.RTCPeerConnection.prototype[method] = methodObj[method];
|
|
|
});
|
|
|
}
|
|
|
|
|
|
const modernStatsTypes = {
|
|
|
inboundrtp: 'inbound-rtp',
|
|
|
outboundrtp: 'outbound-rtp',
|
|
|
candidatepair: 'candidate-pair',
|
|
|
localcandidate: 'local-candidate',
|
|
|
remotecandidate: 'remote-candidate'
|
|
|
};
|
|
|
|
|
|
const nativeGetStats = window.RTCPeerConnection.prototype.getStats;
|
|
|
window.RTCPeerConnection.prototype.getStats = function getStats() {
|
|
|
const [selector, onSucc, onErr] = arguments;
|
|
|
return nativeGetStats.apply(this, [selector || null])
|
|
|
.then(stats => {
|
|
|
if (browserDetails.version < 53 && !onSucc) {
|
|
|
// Shim only promise getStats with spec-hyphens in type names
|
|
|
// Leave callback version alone; misc old uses of forEach before Map
|
|
|
try {
|
|
|
stats.forEach(stat => {
|
|
|
stat.type = modernStatsTypes[stat.type] || stat.type;
|
|
|
});
|
|
|
} catch (e) {
|
|
|
if (e.name !== 'TypeError') {
|
|
|
throw e;
|
|
|
}
|
|
|
// Avoid TypeError: "type" is read-only, in old versions. 34-43ish
|
|
|
stats.forEach((stat, i) => {
|
|
|
stats.set(i, Object.assign({}, stat, {
|
|
|
type: modernStatsTypes[stat.type] || stat.type
|
|
|
}));
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
return stats;
|
|
|
})
|
|
|
.then(onSucc, onErr);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimSenderGetStats(window) {
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection &&
|
|
|
window.RTCRtpSender)) {
|
|
|
return;
|
|
|
}
|
|
|
if (window.RTCRtpSender && 'getStats' in window.RTCRtpSender.prototype) {
|
|
|
return;
|
|
|
}
|
|
|
const origGetSenders = window.RTCPeerConnection.prototype.getSenders;
|
|
|
if (origGetSenders) {
|
|
|
window.RTCPeerConnection.prototype.getSenders = function getSenders() {
|
|
|
const senders = origGetSenders.apply(this, []);
|
|
|
senders.forEach(sender => sender._pc = this);
|
|
|
return senders;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
const origAddTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
|
if (origAddTrack) {
|
|
|
window.RTCPeerConnection.prototype.addTrack = function addTrack() {
|
|
|
const sender = origAddTrack.apply(this, arguments);
|
|
|
sender._pc = this;
|
|
|
return sender;
|
|
|
};
|
|
|
}
|
|
|
window.RTCRtpSender.prototype.getStats = function getStats() {
|
|
|
return this.track ? this._pc.getStats(this.track) :
|
|
|
Promise.resolve(new Map());
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimReceiverGetStats(window) {
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection &&
|
|
|
window.RTCRtpSender)) {
|
|
|
return;
|
|
|
}
|
|
|
if (window.RTCRtpSender && 'getStats' in window.RTCRtpReceiver.prototype) {
|
|
|
return;
|
|
|
}
|
|
|
const origGetReceivers = window.RTCPeerConnection.prototype.getReceivers;
|
|
|
if (origGetReceivers) {
|
|
|
window.RTCPeerConnection.prototype.getReceivers = function getReceivers() {
|
|
|
const receivers = origGetReceivers.apply(this, []);
|
|
|
receivers.forEach(receiver => receiver._pc = this);
|
|
|
return receivers;
|
|
|
};
|
|
|
}
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_0__.wrapPeerConnectionEvent(window, 'track', e => {
|
|
|
e.receiver._pc = e.srcElement;
|
|
|
return e;
|
|
|
});
|
|
|
window.RTCRtpReceiver.prototype.getStats = function getStats() {
|
|
|
return this._pc.getStats(this.track);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimRemoveStream(window) {
|
|
|
if (!window.RTCPeerConnection ||
|
|
|
'removeStream' in window.RTCPeerConnection.prototype) {
|
|
|
return;
|
|
|
}
|
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
|
function removeStream(stream) {
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_0__.deprecated('removeStream', 'removeTrack');
|
|
|
this.getSenders().forEach(sender => {
|
|
|
if (sender.track && stream.getTracks().includes(sender.track)) {
|
|
|
this.removeTrack(sender);
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimRTCDataChannel(window) {
|
|
|
// rename DataChannel to RTCDataChannel (native fix in FF60):
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1173851
|
|
|
if (window.DataChannel && !window.RTCDataChannel) {
|
|
|
window.RTCDataChannel = window.DataChannel;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimAddTransceiver(window) {
|
|
|
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
|
|
|
// Firefox ignores the init sendEncodings options passed to addTransceiver
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection)) {
|
|
|
return;
|
|
|
}
|
|
|
const origAddTransceiver = window.RTCPeerConnection.prototype.addTransceiver;
|
|
|
if (origAddTransceiver) {
|
|
|
window.RTCPeerConnection.prototype.addTransceiver =
|
|
|
function addTransceiver() {
|
|
|
this.setParametersPromises = [];
|
|
|
// WebIDL input coercion and validation
|
|
|
let sendEncodings = arguments[1] && arguments[1].sendEncodings;
|
|
|
if (sendEncodings === undefined) {
|
|
|
sendEncodings = [];
|
|
|
}
|
|
|
sendEncodings = [...sendEncodings];
|
|
|
const shouldPerformCheck = sendEncodings.length > 0;
|
|
|
if (shouldPerformCheck) {
|
|
|
// If sendEncodings params are provided, validate grammar
|
|
|
sendEncodings.forEach((encodingParam) => {
|
|
|
if ('rid' in encodingParam) {
|
|
|
const ridRegex = /^[a-z0-9]{0,16}$/i;
|
|
|
if (!ridRegex.test(encodingParam.rid)) {
|
|
|
throw new TypeError('Invalid RID value provided.');
|
|
|
}
|
|
|
}
|
|
|
if ('scaleResolutionDownBy' in encodingParam) {
|
|
|
if (!(parseFloat(encodingParam.scaleResolutionDownBy) >= 1.0)) {
|
|
|
throw new RangeError('scale_resolution_down_by must be >= 1.0');
|
|
|
}
|
|
|
}
|
|
|
if ('maxFramerate' in encodingParam) {
|
|
|
if (!(parseFloat(encodingParam.maxFramerate) >= 0)) {
|
|
|
throw new RangeError('max_framerate must be >= 0.0');
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
const transceiver = origAddTransceiver.apply(this, arguments);
|
|
|
if (shouldPerformCheck) {
|
|
|
// Check if the init options were applied. If not we do this in an
|
|
|
// asynchronous way and save the promise reference in a global object.
|
|
|
// This is an ugly hack, but at the same time is way more robust than
|
|
|
// checking the sender parameters before and after the createOffer
|
|
|
// Also note that after the createoffer we are not 100% sure that
|
|
|
// the params were asynchronously applied so we might miss the
|
|
|
// opportunity to recreate offer.
|
|
|
const {sender} = transceiver;
|
|
|
const params = sender.getParameters();
|
|
|
if (!('encodings' in params) ||
|
|
|
// Avoid being fooled by patched getParameters() below.
|
|
|
(params.encodings.length === 1 &&
|
|
|
Object.keys(params.encodings[0]).length === 0)) {
|
|
|
params.encodings = sendEncodings;
|
|
|
sender.sendEncodings = sendEncodings;
|
|
|
this.setParametersPromises.push(sender.setParameters(params)
|
|
|
.then(() => {
|
|
|
delete sender.sendEncodings;
|
|
|
}).catch(() => {
|
|
|
delete sender.sendEncodings;
|
|
|
})
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
return transceiver;
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimGetParameters(window) {
|
|
|
if (!(typeof window === 'object' && window.RTCRtpSender)) {
|
|
|
return;
|
|
|
}
|
|
|
const origGetParameters = window.RTCRtpSender.prototype.getParameters;
|
|
|
if (origGetParameters) {
|
|
|
window.RTCRtpSender.prototype.getParameters =
|
|
|
function getParameters() {
|
|
|
const params = origGetParameters.apply(this, arguments);
|
|
|
if (!('encodings' in params)) {
|
|
|
params.encodings = [].concat(this.sendEncodings || [{}]);
|
|
|
}
|
|
|
return params;
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimCreateOffer(window) {
|
|
|
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
|
|
|
// Firefox ignores the init sendEncodings options passed to addTransceiver
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection)) {
|
|
|
return;
|
|
|
}
|
|
|
const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
|
|
|
window.RTCPeerConnection.prototype.createOffer = function createOffer() {
|
|
|
if (this.setParametersPromises && this.setParametersPromises.length) {
|
|
|
return Promise.all(this.setParametersPromises)
|
|
|
.then(() => {
|
|
|
return origCreateOffer.apply(this, arguments);
|
|
|
})
|
|
|
.finally(() => {
|
|
|
this.setParametersPromises = [];
|
|
|
});
|
|
|
}
|
|
|
return origCreateOffer.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimCreateAnswer(window) {
|
|
|
// https://github.com/webrtcHacks/adapter/issues/998#issuecomment-516921647
|
|
|
// Firefox ignores the init sendEncodings options passed to addTransceiver
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
|
|
|
if (!(typeof window === 'object' && window.RTCPeerConnection)) {
|
|
|
return;
|
|
|
}
|
|
|
const origCreateAnswer = window.RTCPeerConnection.prototype.createAnswer;
|
|
|
window.RTCPeerConnection.prototype.createAnswer = function createAnswer() {
|
|
|
if (this.setParametersPromises && this.setParametersPromises.length) {
|
|
|
return Promise.all(this.setParametersPromises)
|
|
|
.then(() => {
|
|
|
return origCreateAnswer.apply(this, arguments);
|
|
|
})
|
|
|
.finally(() => {
|
|
|
this.setParametersPromises = [];
|
|
|
});
|
|
|
}
|
|
|
return origCreateAnswer.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js":
|
|
|
/*!***********************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/firefox/getdisplaymedia.js ***!
|
|
|
\***********************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimGetDisplayMedia: () => (/* binding */ shimGetDisplayMedia)
|
|
|
/* harmony export */ });
|
|
|
/*
|
|
|
* Copyright (c) 2018 The adapter.js project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
function shimGetDisplayMedia(window, preferredMediaSource) {
|
|
|
if (window.navigator.mediaDevices &&
|
|
|
'getDisplayMedia' in window.navigator.mediaDevices) {
|
|
|
return;
|
|
|
}
|
|
|
if (!(window.navigator.mediaDevices)) {
|
|
|
return;
|
|
|
}
|
|
|
window.navigator.mediaDevices.getDisplayMedia =
|
|
|
function getDisplayMedia(constraints) {
|
|
|
if (!(constraints && constraints.video)) {
|
|
|
const err = new DOMException('getDisplayMedia without video ' +
|
|
|
'constraints is undefined');
|
|
|
err.name = 'NotFoundError';
|
|
|
// from https://heycam.github.io/webidl/#idl-DOMException-error-names
|
|
|
err.code = 8;
|
|
|
return Promise.reject(err);
|
|
|
}
|
|
|
if (constraints.video === true) {
|
|
|
constraints.video = {mediaSource: preferredMediaSource};
|
|
|
} else {
|
|
|
constraints.video.mediaSource = preferredMediaSource;
|
|
|
}
|
|
|
return window.navigator.mediaDevices.getUserMedia(constraints);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js":
|
|
|
/*!********************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/firefox/getusermedia.js ***!
|
|
|
\********************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimGetUserMedia: () => (/* binding */ shimGetUserMedia)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function shimGetUserMedia(window, browserDetails) {
|
|
|
const navigator = window && window.navigator;
|
|
|
const MediaStreamTrack = window && window.MediaStreamTrack;
|
|
|
|
|
|
navigator.getUserMedia = function(constraints, onSuccess, onError) {
|
|
|
// Replace Firefox 44+'s deprecation warning with unprefixed version.
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_0__.deprecated('navigator.getUserMedia',
|
|
|
'navigator.mediaDevices.getUserMedia');
|
|
|
navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
|
|
|
};
|
|
|
|
|
|
if (!(browserDetails.version > 55 &&
|
|
|
'autoGainControl' in navigator.mediaDevices.getSupportedConstraints())) {
|
|
|
const remap = function(obj, a, b) {
|
|
|
if (a in obj && !(b in obj)) {
|
|
|
obj[b] = obj[a];
|
|
|
delete obj[a];
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const nativeGetUserMedia = navigator.mediaDevices.getUserMedia.
|
|
|
bind(navigator.mediaDevices);
|
|
|
navigator.mediaDevices.getUserMedia = function(c) {
|
|
|
if (typeof c === 'object' && typeof c.audio === 'object') {
|
|
|
c = JSON.parse(JSON.stringify(c));
|
|
|
remap(c.audio, 'autoGainControl', 'mozAutoGainControl');
|
|
|
remap(c.audio, 'noiseSuppression', 'mozNoiseSuppression');
|
|
|
}
|
|
|
return nativeGetUserMedia(c);
|
|
|
};
|
|
|
|
|
|
if (MediaStreamTrack && MediaStreamTrack.prototype.getSettings) {
|
|
|
const nativeGetSettings = MediaStreamTrack.prototype.getSettings;
|
|
|
MediaStreamTrack.prototype.getSettings = function() {
|
|
|
const obj = nativeGetSettings.apply(this, arguments);
|
|
|
remap(obj, 'mozAutoGainControl', 'autoGainControl');
|
|
|
remap(obj, 'mozNoiseSuppression', 'noiseSuppression');
|
|
|
return obj;
|
|
|
};
|
|
|
}
|
|
|
|
|
|
if (MediaStreamTrack && MediaStreamTrack.prototype.applyConstraints) {
|
|
|
const nativeApplyConstraints =
|
|
|
MediaStreamTrack.prototype.applyConstraints;
|
|
|
MediaStreamTrack.prototype.applyConstraints = function(c) {
|
|
|
if (this.kind === 'audio' && typeof c === 'object') {
|
|
|
c = JSON.parse(JSON.stringify(c));
|
|
|
remap(c, 'autoGainControl', 'mozAutoGainControl');
|
|
|
remap(c, 'noiseSuppression', 'mozNoiseSuppression');
|
|
|
}
|
|
|
return nativeApplyConstraints.apply(this, [c]);
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/safari/safari_shim.js":
|
|
|
/*!******************************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/safari/safari_shim.js ***!
|
|
|
\******************************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ shimAudioContext: () => (/* binding */ shimAudioContext),
|
|
|
/* harmony export */ shimCallbacksAPI: () => (/* binding */ shimCallbacksAPI),
|
|
|
/* harmony export */ shimConstraints: () => (/* binding */ shimConstraints),
|
|
|
/* harmony export */ shimCreateOfferLegacy: () => (/* binding */ shimCreateOfferLegacy),
|
|
|
/* harmony export */ shimGetUserMedia: () => (/* binding */ shimGetUserMedia),
|
|
|
/* harmony export */ shimLocalStreamsAPI: () => (/* binding */ shimLocalStreamsAPI),
|
|
|
/* harmony export */ shimRTCIceServerUrls: () => (/* binding */ shimRTCIceServerUrls),
|
|
|
/* harmony export */ shimRemoteStreamsAPI: () => (/* binding */ shimRemoteStreamsAPI),
|
|
|
/* harmony export */ shimTrackEventTransceiver: () => (/* binding */ shimTrackEventTransceiver)
|
|
|
/* harmony export */ });
|
|
|
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../utils */ "./node_modules/webrtc-adapter/src/js/utils.js");
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
function shimLocalStreamsAPI(window) {
|
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
if (!('getLocalStreams' in window.RTCPeerConnection.prototype)) {
|
|
|
window.RTCPeerConnection.prototype.getLocalStreams =
|
|
|
function getLocalStreams() {
|
|
|
if (!this._localStreams) {
|
|
|
this._localStreams = [];
|
|
|
}
|
|
|
return this._localStreams;
|
|
|
};
|
|
|
}
|
|
|
if (!('addStream' in window.RTCPeerConnection.prototype)) {
|
|
|
const _addTrack = window.RTCPeerConnection.prototype.addTrack;
|
|
|
window.RTCPeerConnection.prototype.addStream = function addStream(stream) {
|
|
|
if (!this._localStreams) {
|
|
|
this._localStreams = [];
|
|
|
}
|
|
|
if (!this._localStreams.includes(stream)) {
|
|
|
this._localStreams.push(stream);
|
|
|
}
|
|
|
// Try to emulate Chrome's behaviour of adding in audio-video order.
|
|
|
// Safari orders by track id.
|
|
|
stream.getAudioTracks().forEach(track => _addTrack.call(this, track,
|
|
|
stream));
|
|
|
stream.getVideoTracks().forEach(track => _addTrack.call(this, track,
|
|
|
stream));
|
|
|
};
|
|
|
|
|
|
window.RTCPeerConnection.prototype.addTrack =
|
|
|
function addTrack(track, ...streams) {
|
|
|
if (streams) {
|
|
|
streams.forEach((stream) => {
|
|
|
if (!this._localStreams) {
|
|
|
this._localStreams = [stream];
|
|
|
} else if (!this._localStreams.includes(stream)) {
|
|
|
this._localStreams.push(stream);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
return _addTrack.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
if (!('removeStream' in window.RTCPeerConnection.prototype)) {
|
|
|
window.RTCPeerConnection.prototype.removeStream =
|
|
|
function removeStream(stream) {
|
|
|
if (!this._localStreams) {
|
|
|
this._localStreams = [];
|
|
|
}
|
|
|
const index = this._localStreams.indexOf(stream);
|
|
|
if (index === -1) {
|
|
|
return;
|
|
|
}
|
|
|
this._localStreams.splice(index, 1);
|
|
|
const tracks = stream.getTracks();
|
|
|
this.getSenders().forEach(sender => {
|
|
|
if (tracks.includes(sender.track)) {
|
|
|
this.removeTrack(sender);
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimRemoteStreamsAPI(window) {
|
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
if (!('getRemoteStreams' in window.RTCPeerConnection.prototype)) {
|
|
|
window.RTCPeerConnection.prototype.getRemoteStreams =
|
|
|
function getRemoteStreams() {
|
|
|
return this._remoteStreams ? this._remoteStreams : [];
|
|
|
};
|
|
|
}
|
|
|
if (!('onaddstream' in window.RTCPeerConnection.prototype)) {
|
|
|
Object.defineProperty(window.RTCPeerConnection.prototype, 'onaddstream', {
|
|
|
get() {
|
|
|
return this._onaddstream;
|
|
|
},
|
|
|
set(f) {
|
|
|
if (this._onaddstream) {
|
|
|
this.removeEventListener('addstream', this._onaddstream);
|
|
|
this.removeEventListener('track', this._onaddstreampoly);
|
|
|
}
|
|
|
this.addEventListener('addstream', this._onaddstream = f);
|
|
|
this.addEventListener('track', this._onaddstreampoly = (e) => {
|
|
|
e.streams.forEach(stream => {
|
|
|
if (!this._remoteStreams) {
|
|
|
this._remoteStreams = [];
|
|
|
}
|
|
|
if (this._remoteStreams.includes(stream)) {
|
|
|
return;
|
|
|
}
|
|
|
this._remoteStreams.push(stream);
|
|
|
const event = new Event('addstream');
|
|
|
event.stream = stream;
|
|
|
this.dispatchEvent(event);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
const origSetRemoteDescription =
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription;
|
|
|
window.RTCPeerConnection.prototype.setRemoteDescription =
|
|
|
function setRemoteDescription() {
|
|
|
const pc = this;
|
|
|
if (!this._onaddstreampoly) {
|
|
|
this.addEventListener('track', this._onaddstreampoly = function(e) {
|
|
|
e.streams.forEach(stream => {
|
|
|
if (!pc._remoteStreams) {
|
|
|
pc._remoteStreams = [];
|
|
|
}
|
|
|
if (pc._remoteStreams.indexOf(stream) >= 0) {
|
|
|
return;
|
|
|
}
|
|
|
pc._remoteStreams.push(stream);
|
|
|
const event = new Event('addstream');
|
|
|
event.stream = stream;
|
|
|
pc.dispatchEvent(event);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
return origSetRemoteDescription.apply(pc, arguments);
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimCallbacksAPI(window) {
|
|
|
if (typeof window !== 'object' || !window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
const prototype = window.RTCPeerConnection.prototype;
|
|
|
const origCreateOffer = prototype.createOffer;
|
|
|
const origCreateAnswer = prototype.createAnswer;
|
|
|
const setLocalDescription = prototype.setLocalDescription;
|
|
|
const setRemoteDescription = prototype.setRemoteDescription;
|
|
|
const addIceCandidate = prototype.addIceCandidate;
|
|
|
|
|
|
prototype.createOffer =
|
|
|
function createOffer(successCallback, failureCallback) {
|
|
|
const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
|
|
|
const promise = origCreateOffer.apply(this, [options]);
|
|
|
if (!failureCallback) {
|
|
|
return promise;
|
|
|
}
|
|
|
promise.then(successCallback, failureCallback);
|
|
|
return Promise.resolve();
|
|
|
};
|
|
|
|
|
|
prototype.createAnswer =
|
|
|
function createAnswer(successCallback, failureCallback) {
|
|
|
const options = (arguments.length >= 2) ? arguments[2] : arguments[0];
|
|
|
const promise = origCreateAnswer.apply(this, [options]);
|
|
|
if (!failureCallback) {
|
|
|
return promise;
|
|
|
}
|
|
|
promise.then(successCallback, failureCallback);
|
|
|
return Promise.resolve();
|
|
|
};
|
|
|
|
|
|
let withCallback = function(description, successCallback, failureCallback) {
|
|
|
const promise = setLocalDescription.apply(this, [description]);
|
|
|
if (!failureCallback) {
|
|
|
return promise;
|
|
|
}
|
|
|
promise.then(successCallback, failureCallback);
|
|
|
return Promise.resolve();
|
|
|
};
|
|
|
prototype.setLocalDescription = withCallback;
|
|
|
|
|
|
withCallback = function(description, successCallback, failureCallback) {
|
|
|
const promise = setRemoteDescription.apply(this, [description]);
|
|
|
if (!failureCallback) {
|
|
|
return promise;
|
|
|
}
|
|
|
promise.then(successCallback, failureCallback);
|
|
|
return Promise.resolve();
|
|
|
};
|
|
|
prototype.setRemoteDescription = withCallback;
|
|
|
|
|
|
withCallback = function(candidate, successCallback, failureCallback) {
|
|
|
const promise = addIceCandidate.apply(this, [candidate]);
|
|
|
if (!failureCallback) {
|
|
|
return promise;
|
|
|
}
|
|
|
promise.then(successCallback, failureCallback);
|
|
|
return Promise.resolve();
|
|
|
};
|
|
|
prototype.addIceCandidate = withCallback;
|
|
|
}
|
|
|
|
|
|
function shimGetUserMedia(window) {
|
|
|
const navigator = window && window.navigator;
|
|
|
|
|
|
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
|
// shim not needed in Safari 12.1
|
|
|
const mediaDevices = navigator.mediaDevices;
|
|
|
const _getUserMedia = mediaDevices.getUserMedia.bind(mediaDevices);
|
|
|
navigator.mediaDevices.getUserMedia = (constraints) => {
|
|
|
return _getUserMedia(shimConstraints(constraints));
|
|
|
};
|
|
|
}
|
|
|
|
|
|
if (!navigator.getUserMedia && navigator.mediaDevices &&
|
|
|
navigator.mediaDevices.getUserMedia) {
|
|
|
navigator.getUserMedia = function getUserMedia(constraints, cb, errcb) {
|
|
|
navigator.mediaDevices.getUserMedia(constraints)
|
|
|
.then(cb, errcb);
|
|
|
}.bind(navigator);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimConstraints(constraints) {
|
|
|
if (constraints && constraints.video !== undefined) {
|
|
|
return Object.assign({},
|
|
|
constraints,
|
|
|
{video: _utils__WEBPACK_IMPORTED_MODULE_0__.compactObject(constraints.video)}
|
|
|
);
|
|
|
}
|
|
|
|
|
|
return constraints;
|
|
|
}
|
|
|
|
|
|
function shimRTCIceServerUrls(window) {
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
// migrate from non-spec RTCIceServer.url to RTCIceServer.urls
|
|
|
const OrigPeerConnection = window.RTCPeerConnection;
|
|
|
window.RTCPeerConnection =
|
|
|
function RTCPeerConnection(pcConfig, pcConstraints) {
|
|
|
if (pcConfig && pcConfig.iceServers) {
|
|
|
const newIceServers = [];
|
|
|
for (let i = 0; i < pcConfig.iceServers.length; i++) {
|
|
|
let server = pcConfig.iceServers[i];
|
|
|
if (server.urls === undefined && server.url) {
|
|
|
_utils__WEBPACK_IMPORTED_MODULE_0__.deprecated('RTCIceServer.url', 'RTCIceServer.urls');
|
|
|
server = JSON.parse(JSON.stringify(server));
|
|
|
server.urls = server.url;
|
|
|
delete server.url;
|
|
|
newIceServers.push(server);
|
|
|
} else {
|
|
|
newIceServers.push(pcConfig.iceServers[i]);
|
|
|
}
|
|
|
}
|
|
|
pcConfig.iceServers = newIceServers;
|
|
|
}
|
|
|
return new OrigPeerConnection(pcConfig, pcConstraints);
|
|
|
};
|
|
|
window.RTCPeerConnection.prototype = OrigPeerConnection.prototype;
|
|
|
// wrap static methods. Currently just generateCertificate.
|
|
|
if ('generateCertificate' in OrigPeerConnection) {
|
|
|
Object.defineProperty(window.RTCPeerConnection, 'generateCertificate', {
|
|
|
get() {
|
|
|
return OrigPeerConnection.generateCertificate;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimTrackEventTransceiver(window) {
|
|
|
// Add event.transceiver member over deprecated event.receiver
|
|
|
if (typeof window === 'object' && window.RTCTrackEvent &&
|
|
|
'receiver' in window.RTCTrackEvent.prototype &&
|
|
|
!('transceiver' in window.RTCTrackEvent.prototype)) {
|
|
|
Object.defineProperty(window.RTCTrackEvent.prototype, 'transceiver', {
|
|
|
get() {
|
|
|
return {receiver: this.receiver};
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
function shimCreateOfferLegacy(window) {
|
|
|
const origCreateOffer = window.RTCPeerConnection.prototype.createOffer;
|
|
|
window.RTCPeerConnection.prototype.createOffer =
|
|
|
function createOffer(offerOptions) {
|
|
|
if (offerOptions) {
|
|
|
if (typeof offerOptions.offerToReceiveAudio !== 'undefined') {
|
|
|
// support bit values
|
|
|
offerOptions.offerToReceiveAudio =
|
|
|
!!offerOptions.offerToReceiveAudio;
|
|
|
}
|
|
|
const audioTransceiver = this.getTransceivers().find(transceiver =>
|
|
|
transceiver.receiver.track.kind === 'audio');
|
|
|
if (offerOptions.offerToReceiveAudio === false && audioTransceiver) {
|
|
|
if (audioTransceiver.direction === 'sendrecv') {
|
|
|
if (audioTransceiver.setDirection) {
|
|
|
audioTransceiver.setDirection('sendonly');
|
|
|
} else {
|
|
|
audioTransceiver.direction = 'sendonly';
|
|
|
}
|
|
|
} else if (audioTransceiver.direction === 'recvonly') {
|
|
|
if (audioTransceiver.setDirection) {
|
|
|
audioTransceiver.setDirection('inactive');
|
|
|
} else {
|
|
|
audioTransceiver.direction = 'inactive';
|
|
|
}
|
|
|
}
|
|
|
} else if (offerOptions.offerToReceiveAudio === true &&
|
|
|
!audioTransceiver) {
|
|
|
this.addTransceiver('audio', {direction: 'recvonly'});
|
|
|
}
|
|
|
|
|
|
if (typeof offerOptions.offerToReceiveVideo !== 'undefined') {
|
|
|
// support bit values
|
|
|
offerOptions.offerToReceiveVideo =
|
|
|
!!offerOptions.offerToReceiveVideo;
|
|
|
}
|
|
|
const videoTransceiver = this.getTransceivers().find(transceiver =>
|
|
|
transceiver.receiver.track.kind === 'video');
|
|
|
if (offerOptions.offerToReceiveVideo === false && videoTransceiver) {
|
|
|
if (videoTransceiver.direction === 'sendrecv') {
|
|
|
if (videoTransceiver.setDirection) {
|
|
|
videoTransceiver.setDirection('sendonly');
|
|
|
} else {
|
|
|
videoTransceiver.direction = 'sendonly';
|
|
|
}
|
|
|
} else if (videoTransceiver.direction === 'recvonly') {
|
|
|
if (videoTransceiver.setDirection) {
|
|
|
videoTransceiver.setDirection('inactive');
|
|
|
} else {
|
|
|
videoTransceiver.direction = 'inactive';
|
|
|
}
|
|
|
}
|
|
|
} else if (offerOptions.offerToReceiveVideo === true &&
|
|
|
!videoTransceiver) {
|
|
|
this.addTransceiver('video', {direction: 'recvonly'});
|
|
|
}
|
|
|
}
|
|
|
return origCreateOffer.apply(this, arguments);
|
|
|
};
|
|
|
}
|
|
|
|
|
|
function shimAudioContext(window) {
|
|
|
if (typeof window !== 'object' || window.AudioContext) {
|
|
|
return;
|
|
|
}
|
|
|
window.AudioContext = window.webkitAudioContext;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/webrtc-adapter/src/js/utils.js":
|
|
|
/*!*****************************************************!*\
|
|
|
!*** ./node_modules/webrtc-adapter/src/js/utils.js ***!
|
|
|
\*****************************************************/
|
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
|
|
"use strict";
|
|
|
__webpack_require__.r(__webpack_exports__);
|
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
|
/* harmony export */ compactObject: () => (/* binding */ compactObject),
|
|
|
/* harmony export */ deprecated: () => (/* binding */ deprecated),
|
|
|
/* harmony export */ detectBrowser: () => (/* binding */ detectBrowser),
|
|
|
/* harmony export */ disableLog: () => (/* binding */ disableLog),
|
|
|
/* harmony export */ disableWarnings: () => (/* binding */ disableWarnings),
|
|
|
/* harmony export */ extractVersion: () => (/* binding */ extractVersion),
|
|
|
/* harmony export */ filterStats: () => (/* binding */ filterStats),
|
|
|
/* harmony export */ log: () => (/* binding */ log),
|
|
|
/* harmony export */ walkStats: () => (/* binding */ walkStats),
|
|
|
/* harmony export */ wrapPeerConnectionEvent: () => (/* binding */ wrapPeerConnectionEvent)
|
|
|
/* harmony export */ });
|
|
|
/*
|
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
|
*
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
* tree.
|
|
|
*/
|
|
|
/* eslint-env node */
|
|
|
|
|
|
|
|
|
let logDisabled_ = true;
|
|
|
let deprecationWarnings_ = true;
|
|
|
|
|
|
/**
|
|
|
* Extract browser version out of the provided user agent string.
|
|
|
*
|
|
|
* @param {!string} uastring userAgent string.
|
|
|
* @param {!string} expr Regular expression used as match criteria.
|
|
|
* @param {!number} pos position in the version string to be returned.
|
|
|
* @return {!number} browser version.
|
|
|
*/
|
|
|
function extractVersion(uastring, expr, pos) {
|
|
|
const match = uastring.match(expr);
|
|
|
return match && match.length >= pos && parseInt(match[pos], 10);
|
|
|
}
|
|
|
|
|
|
// Wraps the peerconnection event eventNameToWrap in a function
|
|
|
// which returns the modified event object (or false to prevent
|
|
|
// the event).
|
|
|
function wrapPeerConnectionEvent(window, eventNameToWrap, wrapper) {
|
|
|
if (!window.RTCPeerConnection) {
|
|
|
return;
|
|
|
}
|
|
|
const proto = window.RTCPeerConnection.prototype;
|
|
|
const nativeAddEventListener = proto.addEventListener;
|
|
|
proto.addEventListener = function(nativeEventName, cb) {
|
|
|
if (nativeEventName !== eventNameToWrap) {
|
|
|
return nativeAddEventListener.apply(this, arguments);
|
|
|
}
|
|
|
const wrappedCallback = (e) => {
|
|
|
const modifiedEvent = wrapper(e);
|
|
|
if (modifiedEvent) {
|
|
|
if (cb.handleEvent) {
|
|
|
cb.handleEvent(modifiedEvent);
|
|
|
} else {
|
|
|
cb(modifiedEvent);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
this._eventMap = this._eventMap || {};
|
|
|
if (!this._eventMap[eventNameToWrap]) {
|
|
|
this._eventMap[eventNameToWrap] = new Map();
|
|
|
}
|
|
|
this._eventMap[eventNameToWrap].set(cb, wrappedCallback);
|
|
|
return nativeAddEventListener.apply(this, [nativeEventName,
|
|
|
wrappedCallback]);
|
|
|
};
|
|
|
|
|
|
const nativeRemoveEventListener = proto.removeEventListener;
|
|
|
proto.removeEventListener = function(nativeEventName, cb) {
|
|
|
if (nativeEventName !== eventNameToWrap || !this._eventMap
|
|
|
|| !this._eventMap[eventNameToWrap]) {
|
|
|
return nativeRemoveEventListener.apply(this, arguments);
|
|
|
}
|
|
|
if (!this._eventMap[eventNameToWrap].has(cb)) {
|
|
|
return nativeRemoveEventListener.apply(this, arguments);
|
|
|
}
|
|
|
const unwrappedCb = this._eventMap[eventNameToWrap].get(cb);
|
|
|
this._eventMap[eventNameToWrap].delete(cb);
|
|
|
if (this._eventMap[eventNameToWrap].size === 0) {
|
|
|
delete this._eventMap[eventNameToWrap];
|
|
|
}
|
|
|
if (Object.keys(this._eventMap).length === 0) {
|
|
|
delete this._eventMap;
|
|
|
}
|
|
|
return nativeRemoveEventListener.apply(this, [nativeEventName,
|
|
|
unwrappedCb]);
|
|
|
};
|
|
|
|
|
|
Object.defineProperty(proto, 'on' + eventNameToWrap, {
|
|
|
get() {
|
|
|
return this['_on' + eventNameToWrap];
|
|
|
},
|
|
|
set(cb) {
|
|
|
if (this['_on' + eventNameToWrap]) {
|
|
|
this.removeEventListener(eventNameToWrap,
|
|
|
this['_on' + eventNameToWrap]);
|
|
|
delete this['_on' + eventNameToWrap];
|
|
|
}
|
|
|
if (cb) {
|
|
|
this.addEventListener(eventNameToWrap,
|
|
|
this['_on' + eventNameToWrap] = cb);
|
|
|
}
|
|
|
},
|
|
|
enumerable: true,
|
|
|
configurable: true
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function disableLog(bool) {
|
|
|
if (typeof bool !== 'boolean') {
|
|
|
return new Error('Argument type: ' + typeof bool +
|
|
|
'. Please use a boolean.');
|
|
|
}
|
|
|
logDisabled_ = bool;
|
|
|
return (bool) ? 'adapter.js logging disabled' :
|
|
|
'adapter.js logging enabled';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Disable or enable deprecation warnings
|
|
|
* @param {!boolean} bool set to true to disable warnings.
|
|
|
*/
|
|
|
function disableWarnings(bool) {
|
|
|
if (typeof bool !== 'boolean') {
|
|
|
return new Error('Argument type: ' + typeof bool +
|
|
|
'. Please use a boolean.');
|
|
|
}
|
|
|
deprecationWarnings_ = !bool;
|
|
|
return 'adapter.js deprecation warnings ' + (bool ? 'disabled' : 'enabled');
|
|
|
}
|
|
|
|
|
|
function log() {
|
|
|
if (typeof window === 'object') {
|
|
|
if (logDisabled_) {
|
|
|
return;
|
|
|
}
|
|
|
if (typeof console !== 'undefined' && typeof console.log === 'function') {
|
|
|
console.log.apply(console, arguments);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Shows a deprecation warning suggesting the modern and spec-compatible API.
|
|
|
*/
|
|
|
function deprecated(oldMethod, newMethod) {
|
|
|
if (!deprecationWarnings_) {
|
|
|
return;
|
|
|
}
|
|
|
console.warn(oldMethod + ' is deprecated, please use ' + newMethod +
|
|
|
' instead.');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Browser detector.
|
|
|
*
|
|
|
* @return {object} result containing browser and version
|
|
|
* properties.
|
|
|
*/
|
|
|
function detectBrowser(window) {
|
|
|
// Returned result object.
|
|
|
const result = {browser: null, version: null};
|
|
|
|
|
|
// Fail early if it's not a browser
|
|
|
if (typeof window === 'undefined' || !window.navigator ||
|
|
|
!window.navigator.userAgent) {
|
|
|
result.browser = 'Not a browser.';
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
const {navigator} = window;
|
|
|
|
|
|
if (navigator.mozGetUserMedia) { // Firefox.
|
|
|
result.browser = 'firefox';
|
|
|
result.version = extractVersion(navigator.userAgent,
|
|
|
/Firefox\/(\d+)\./, 1);
|
|
|
} else if (navigator.webkitGetUserMedia ||
|
|
|
(window.isSecureContext === false && window.webkitRTCPeerConnection)) {
|
|
|
// Chrome, Chromium, Webview, Opera.
|
|
|
// Version matches Chrome/WebRTC version.
|
|
|
// Chrome 74 removed webkitGetUserMedia on http as well so we need the
|
|
|
// more complicated fallback to webkitRTCPeerConnection.
|
|
|
result.browser = 'chrome';
|
|
|
result.version = extractVersion(navigator.userAgent,
|
|
|
/Chrom(e|ium)\/(\d+)\./, 2);
|
|
|
} else if (window.RTCPeerConnection &&
|
|
|
navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) { // Safari.
|
|
|
result.browser = 'safari';
|
|
|
result.version = extractVersion(navigator.userAgent,
|
|
|
/AppleWebKit\/(\d+)\./, 1);
|
|
|
result.supportsUnifiedPlan = window.RTCRtpTransceiver &&
|
|
|
'currentDirection' in window.RTCRtpTransceiver.prototype;
|
|
|
} else { // Default fallthrough: not supported.
|
|
|
result.browser = 'Not a supported browser.';
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Checks if something is an object.
|
|
|
*
|
|
|
* @param {*} val The something you want to check.
|
|
|
* @return true if val is an object, false otherwise.
|
|
|
*/
|
|
|
function isObject(val) {
|
|
|
return Object.prototype.toString.call(val) === '[object Object]';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Remove all empty objects and undefined values
|
|
|
* from a nested object -- an enhanced and vanilla version
|
|
|
* of Lodash's `compact`.
|
|
|
*/
|
|
|
function compactObject(data) {
|
|
|
if (!isObject(data)) {
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
return Object.keys(data).reduce(function(accumulator, key) {
|
|
|
const isObj = isObject(data[key]);
|
|
|
const value = isObj ? compactObject(data[key]) : data[key];
|
|
|
const isEmptyObject = isObj && !Object.keys(value).length;
|
|
|
if (value === undefined || isEmptyObject) {
|
|
|
return accumulator;
|
|
|
}
|
|
|
return Object.assign(accumulator, {[key]: value});
|
|
|
}, {});
|
|
|
}
|
|
|
|
|
|
/* iterates the stats graph recursively. */
|
|
|
function walkStats(stats, base, resultSet) {
|
|
|
if (!base || resultSet.has(base.id)) {
|
|
|
return;
|
|
|
}
|
|
|
resultSet.set(base.id, base);
|
|
|
Object.keys(base).forEach(name => {
|
|
|
if (name.endsWith('Id')) {
|
|
|
walkStats(stats, stats.get(base[name]), resultSet);
|
|
|
} else if (name.endsWith('Ids')) {
|
|
|
base[name].forEach(id => {
|
|
|
walkStats(stats, stats.get(id), resultSet);
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/* filter getStats for a sender/receiver track. */
|
|
|
function filterStats(result, track, outbound) {
|
|
|
const streamStatsType = outbound ? 'outbound-rtp' : 'inbound-rtp';
|
|
|
const filteredResult = new Map();
|
|
|
if (track === null) {
|
|
|
return filteredResult;
|
|
|
}
|
|
|
const trackStats = [];
|
|
|
result.forEach(value => {
|
|
|
if (value.type === 'track' &&
|
|
|
value.trackIdentifier === track.id) {
|
|
|
trackStats.push(value);
|
|
|
}
|
|
|
});
|
|
|
trackStats.forEach(trackStat => {
|
|
|
result.forEach(stats => {
|
|
|
if (stats.type === streamStatsType && stats.trackId === trackStat.id) {
|
|
|
walkStats(result, stats, filteredResult);
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
return filteredResult;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./src/settings/WebRtcPeer.js":
|
|
|
/*!************************************!*\
|
|
|
!*** ./src/settings/WebRtcPeer.js ***!
|
|
|
\************************************/
|
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
|
|
/*
|
|
|
* (C) Copyright 2017-2022 OpenVidu (https://openvidu.io)
|
|
|
*
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
* You may obtain a copy of the License at
|
|
|
*
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
*
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
* See the License for the specific language governing permissions and
|
|
|
* limitations under the License.
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
// taken from here:
|
|
|
// https://github.com/OpenVidu/openvidu/blob/master/openvidu-browser/src/OpenViduInternal/WebRtcPeer/WebRtcPeer.ts
|
|
|
// and monkey-patched
|
|
|
const OmUtil = __webpack_require__(/*! ../main/omutils */ "../main/omutils");
|
|
|
|
|
|
const freeice = __webpack_require__(/*! freeice */ "./node_modules/freeice/index.js");
|
|
|
|
|
|
const ExceptionEventName = {
|
|
|
/**
|
|
|
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
|
|
|
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `failed` status.
|
|
|
*
|
|
|
* This is a terminal error that won't have any kind of possible recovery. If the client is still connected to OpenVidu Server,
|
|
|
* then an automatic reconnection process of the media stream is immediately performed. If the ICE connection has broken due to
|
|
|
* a total network drop, then no automatic reconnection process will be possible.
|
|
|
*
|
|
|
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
|
|
|
*/
|
|
|
ICE_CONNECTION_FAILED: 'ICE_CONNECTION_FAILED',
|
|
|
|
|
|
/**
|
|
|
* The [ICE connection state](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceConnectionState)
|
|
|
* of an [RTCPeerConnection](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection) reached `disconnected` status.
|
|
|
*
|
|
|
* This is not a terminal error, and it is possible for the ICE connection to be reconnected. If the client is still connected to
|
|
|
* OpenVidu Server and after certain timeout the ICE connection has not reached a success or terminal status, then an automatic
|
|
|
* reconnection process of the media stream is performed. If the ICE connection has broken due to a total network drop, then no
|
|
|
* automatic reconnection process will be possible.
|
|
|
*
|
|
|
* You can customize the timeout for the reconnection attempt with property {@link OpenViduAdvancedConfiguration.iceConnectionDisconnectedExceptionTimeout},
|
|
|
* which by default is 4000 milliseconds.
|
|
|
*
|
|
|
* {@link ExceptionEvent} objects with this {@link ExceptionEvent.name} will have as {@link ExceptionEvent.origin} property a {@link Stream} object.
|
|
|
*/
|
|
|
ICE_CONNECTION_DISCONNECTED: 'ICE_CONNECTION_DISCONNECTED',
|
|
|
};
|
|
|
|
|
|
class WebRtcPeer {
|
|
|
constructor(configuration) {
|
|
|
this.remoteCandidatesQueue = [];
|
|
|
this.localCandidatesQueue = [];
|
|
|
this.iceCandidateList = [];
|
|
|
this.candidategatheringdone = false;
|
|
|
|
|
|
// Same as WebRtcPeerConfiguration but without optional fields.
|
|
|
this.configuration = {
|
|
|
...configuration,
|
|
|
iceServers: !!configuration.iceServers && configuration.iceServers.length > 0 ? configuration.iceServers : freeice(),
|
|
|
mediaStream: configuration.mediaStream !== undefined ? configuration.mediaStream : null,
|
|
|
mode: !!configuration.mode ? configuration.mode : 'sendrecv',
|
|
|
id: !!configuration.id ? configuration.id : this.generateUniqueId()
|
|
|
};
|
|
|
// prettier-ignore
|
|
|
OmUtil.log(`[WebRtcPeer] configuration:\n${JSON.stringify(this.configuration, null, 2)}`);
|
|
|
|
|
|
this.pc = new RTCPeerConnection({ iceServers: this.configuration.iceServers });
|
|
|
|
|
|
this._iceCandidateListener = (event) => {
|
|
|
if (event.candidate !== null) {
|
|
|
// `RTCPeerConnectionIceEvent.candidate` is supposed to be an RTCIceCandidate:
|
|
|
// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnectioniceevent-candidate
|
|
|
//
|
|
|
// But in practice, it is actually an RTCIceCandidateInit that can be used to
|
|
|
// obtain a proper candidate, using the RTCIceCandidate constructor:
|
|
|
// https://w3c.github.io/webrtc-pc/#dom-rtcicecandidate-constructor
|
|
|
const candidateInit = event.candidate;
|
|
|
const iceCandidate = new RTCIceCandidate(candidateInit);
|
|
|
|
|
|
this.configuration.onIceCandidate(iceCandidate);
|
|
|
if (iceCandidate.candidate !== '') {
|
|
|
this.localCandidatesQueue.push(iceCandidate);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
this.pc.addEventListener('icecandidate', this._iceCandidateListener);
|
|
|
|
|
|
this._signalingStateChangeListener = async () => {
|
|
|
if (this.pc.signalingState === 'stable') {
|
|
|
// SDP Offer/Answer finished. Add stored remote candidates.
|
|
|
while (this.iceCandidateList.length > 0) {
|
|
|
let candidate = this.iceCandidateList.shift();
|
|
|
try {
|
|
|
await this.pc.addIceCandidate(candidate);
|
|
|
} catch (error) {
|
|
|
console.error('Error when calling RTCPeerConnection#addIceCandidate for RTCPeerConnection ' + this.getId(), error);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
this.pc.addEventListener('signalingstatechange', this._signalingStateChangeListener);
|
|
|
if (this.configuration.onConnectionStateChange) {
|
|
|
this.pc.addEventListener('connectionstatechange', this.configuration.onConnectionStateChange);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
getId() {
|
|
|
return this.configuration.id;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This method frees the resources used by WebRtcPeer
|
|
|
*/
|
|
|
dispose() {
|
|
|
OmUtil.log('Disposing WebRtcPeer');
|
|
|
if (this.pc) {
|
|
|
if (this.pc.signalingState === 'closed') {
|
|
|
return;
|
|
|
}
|
|
|
this.pc.removeEventListener('icecandidate', this._iceCandidateListener);
|
|
|
this._iceCandidateListener = undefined;
|
|
|
this.pc.removeEventListener('signalingstatechange', this._signalingStateChangeListener);
|
|
|
this._signalingStateChangeListener = undefined;
|
|
|
if (this._iceConnectionStateChangeListener) {
|
|
|
this.pc.removeEventListener('iceconnectionstatechange', this._iceConnectionStateChangeListener);
|
|
|
this._iceConnectionStateChangeListener = undefined;
|
|
|
}
|
|
|
if (this.configuration.onConnectionStateChange) {
|
|
|
this.pc.removeEventListener('connectionstatechange', this.configuration.onConnectionStateChange);
|
|
|
}
|
|
|
this.configuration = {};
|
|
|
this.pc.close();
|
|
|
this.remoteCandidatesQueue = [];
|
|
|
this.localCandidatesQueue = [];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Creates an SDP offer from the local RTCPeerConnection to send to the other peer.
|
|
|
* Only if the negotiation was initiated by this peer.
|
|
|
*/
|
|
|
async createOffer() {
|
|
|
// TODO: Delete this conditional when all supported browsers are
|
|
|
// modern enough to implement the Transceiver methods.
|
|
|
if (!('addTransceiver' in this.pc)) {
|
|
|
OmUtil.error(
|
|
|
'[createOffer] Method RTCPeerConnection.addTransceiver() is NOT available; using LEGACY offerToReceive{Audio,Video}'
|
|
|
);
|
|
|
return this.createOfferLegacy();
|
|
|
} else {
|
|
|
OmUtil.log('[createOffer] Method RTCPeerConnection.addTransceiver() is available; using it');
|
|
|
}
|
|
|
|
|
|
// Spec doc: https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-addtransceiver
|
|
|
|
|
|
if (this.configuration.mode !== 'recvonly') {
|
|
|
// To send media, assume that all desired media tracks have been
|
|
|
// already added by higher level code to our MediaStream.
|
|
|
|
|
|
if (!this.configuration.mediaStream) {
|
|
|
throw new Error(
|
|
|
`[WebRtcPeer.createOffer] Direction is '${this.configuration.mode}', but no stream was configured to be sent`
|
|
|
);
|
|
|
}
|
|
|
|
|
|
for (const track of this.configuration.mediaStream.getTracks()) {
|
|
|
const tcInit = {
|
|
|
direction: this.configuration.mode,
|
|
|
streams: [this.configuration.mediaStream]
|
|
|
};
|
|
|
|
|
|
if (track.kind === 'video' && this.configuration.simulcast) {
|
|
|
// Check if the requested size is enough to ask for 3 layers.
|
|
|
const trackSettings = track.getSettings();
|
|
|
const trackConsts = track.getConstraints();
|
|
|
|
|
|
const trackWidth = typeof(trackSettings.width) === 'object' ? trackConsts.width.ideal : trackConsts.width || 0;
|
|
|
const trackHeight = typeof(trackSettings.height) === 'object' ? trackConsts.height.ideal : trackConsts.height || 0;
|
|
|
OmUtil.info(`[createOffer] Video track dimensions: ${trackWidth}x${trackHeight}`);
|
|
|
|
|
|
const trackPixels = trackWidth * trackHeight;
|
|
|
let maxLayers = 0;
|
|
|
if (trackPixels >= 960 * 540) {
|
|
|
maxLayers = 3;
|
|
|
} else if (trackPixels >= 480 * 270) {
|
|
|
maxLayers = 2;
|
|
|
} else {
|
|
|
maxLayers = 1;
|
|
|
}
|
|
|
|
|
|
tcInit.sendEncodings = [];
|
|
|
for (let l = 0; l < maxLayers; l++) {
|
|
|
const layerDiv = 2 ** (maxLayers - l - 1);
|
|
|
|
|
|
const encoding = {
|
|
|
rid: 'rdiv' + layerDiv.toString(),
|
|
|
|
|
|
// @ts-ignore -- Property missing from DOM types.
|
|
|
scalabilityMode: 'L1T1'
|
|
|
};
|
|
|
|
|
|
if (['detail', 'text'].includes(track.contentHint)) {
|
|
|
// Prioritize best resolution, for maximum picture detail.
|
|
|
encoding.scaleResolutionDownBy = 1.0;
|
|
|
|
|
|
// @ts-ignore -- Property missing from DOM types.
|
|
|
encoding.maxFramerate = Math.floor(30 / layerDiv);
|
|
|
} else {
|
|
|
encoding.scaleResolutionDownBy = layerDiv;
|
|
|
}
|
|
|
|
|
|
tcInit.sendEncodings.push(encoding);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const tc = this.pc.addTransceiver(track, tcInit);
|
|
|
|
|
|
if (track.kind === 'video') {
|
|
|
let sendParams = tc.sender.getParameters();
|
|
|
let needSetParams = false;
|
|
|
|
|
|
if (sendParams.degradationPreference && !sendParams.degradationPreference.length) {
|
|
|
// degradationPreference for video: "balanced", "maintain-framerate", "maintain-resolution".
|
|
|
// https://www.w3.org/TR/2018/CR-webrtc-20180927/#dom-rtcdegradationpreference
|
|
|
if (['detail', 'text'].includes(track.contentHint)) {
|
|
|
sendParams.degradationPreference = 'maintain-resolution';
|
|
|
} else {
|
|
|
sendParams.degradationPreference = 'balanced';
|
|
|
}
|
|
|
|
|
|
OmUtil.info(`[createOffer] Video sender Degradation Preference set: ${sendParams.degradationPreference}`);
|
|
|
|
|
|
// Firefox implements degradationPreference on each individual encoding!
|
|
|
// (set it on every element of the sendParams.encodings array)
|
|
|
|
|
|
needSetParams = true;
|
|
|
}
|
|
|
|
|
|
// Check that the simulcast encodings were applied.
|
|
|
// Firefox doesn't implement `RTCRtpTransceiverInit.sendEncodings`
|
|
|
// so the only way to enable simulcast is with `RTCRtpSender.setParameters()`.
|
|
|
//
|
|
|
// This next block can be deleted when Firefox fixes bug #1396918:
|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1396918
|
|
|
//
|
|
|
// NOTE: This is done in a way that is compatible with all browsers, to save on
|
|
|
// browser-conditional code. The idea comes from WebRTC Adapter.js:
|
|
|
// * https://github.com/webrtcHacks/adapter/issues/998
|
|
|
// * https://github.com/webrtcHacks/adapter/blob/v7.7.0/src/js/firefox/firefox_shim.js#L231-L255
|
|
|
if (this.configuration.simulcast) {
|
|
|
if (sendParams.encodings.length !== tcInit.sendEncodings.length) {
|
|
|
sendParams.encodings = tcInit.sendEncodings;
|
|
|
|
|
|
needSetParams = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (needSetParams) {
|
|
|
OmUtil.log(`[createOffer] Setting new RTCRtpSendParameters to video sender`);
|
|
|
try {
|
|
|
await tc.sender.setParameters(sendParams);
|
|
|
} catch (error) {
|
|
|
let message = `[WebRtcPeer.createOffer] Cannot set RTCRtpSendParameters to video sender`;
|
|
|
if (error instanceof Error) {
|
|
|
message += `: ${error.message}`;
|
|
|
}
|
|
|
throw new Error(message);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
// To just receive media, create new recvonly transceivers.
|
|
|
for (const kind of ['audio', 'video']) {
|
|
|
// Check if the media kind should be used.
|
|
|
if (!this.configuration.mediaConstraints[kind]) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
this.configuration.mediaStream = new MediaStream();
|
|
|
this.pc.addTransceiver(kind, {
|
|
|
direction: this.configuration.mode,
|
|
|
streams: [this.configuration.mediaStream]
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
let sdpOffer;
|
|
|
try {
|
|
|
sdpOffer = await this.pc.createOffer();
|
|
|
} catch (error) {
|
|
|
let message = `[WebRtcPeer.createOffer] Browser failed creating an SDP Offer`;
|
|
|
if (error instanceof Error) {
|
|
|
message += `: ${error.message}`;
|
|
|
}
|
|
|
throw new Error(message);
|
|
|
}
|
|
|
|
|
|
return sdpOffer;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Creates an SDP answer from the local RTCPeerConnection to send to the other peer
|
|
|
* Only if the negotiation was initiated by the other peer
|
|
|
*/
|
|
|
createAnswer() {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
// TODO: Delete this conditional when all supported browsers are
|
|
|
// modern enough to implement the Transceiver methods.
|
|
|
if ('getTransceivers' in this.pc) {
|
|
|
OmUtil.log('[createAnswer] Method RTCPeerConnection.getTransceivers() is available; using it');
|
|
|
|
|
|
// Ensure that the PeerConnection already contains one Transceiver
|
|
|
// for each kind of media.
|
|
|
// The Transceivers should have been already created internally by
|
|
|
// the PC itself, when `pc.setRemoteDescription(sdpOffer)` was called.
|
|
|
|
|
|
for (const kind of ['audio', 'video']) {
|
|
|
// Check if the media kind should be used.
|
|
|
if (!this.configuration.mediaConstraints[kind]) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
let tc = this.pc.getTransceivers().find((tc) => tc.receiver.track.kind === kind);
|
|
|
|
|
|
if (tc) {
|
|
|
// Enforce our desired direction.
|
|
|
tc.direction = this.configuration.mode;
|
|
|
} else {
|
|
|
return reject(new Error(`${kind} requested, but no transceiver was created from remote description`));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
this.pc
|
|
|
.createAnswer()
|
|
|
.then((sdpAnswer) => resolve(sdpAnswer))
|
|
|
.catch((error) => reject(error));
|
|
|
} else {
|
|
|
// TODO: Delete else branch when all supported browsers are
|
|
|
// modern enough to implement the Transceiver methods
|
|
|
|
|
|
let offerAudio,
|
|
|
offerVideo = true;
|
|
|
if (!!this.configuration.mediaConstraints) {
|
|
|
offerAudio =
|
|
|
typeof this.configuration.mediaConstraints.audio === 'boolean' ? this.configuration.mediaConstraints.audio : true;
|
|
|
offerVideo =
|
|
|
typeof this.configuration.mediaConstraints.video === 'boolean' ? this.configuration.mediaConstraints.video : true;
|
|
|
const constraints = {
|
|
|
offerToReceiveAudio: offerAudio,
|
|
|
offerToReceiveVideo: offerVideo
|
|
|
};
|
|
|
(this.pc).createAnswer(constraints)
|
|
|
.then((sdpAnswer) => resolve(sdpAnswer))
|
|
|
.catch((error) => reject(error));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// else, there is nothing to do; the legacy createAnswer() options do
|
|
|
// not offer any control over which tracks are included in the answer.
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This peer initiated negotiation. Step 1/4 of SDP offer-answer protocol
|
|
|
*/
|
|
|
processLocalOffer(offer) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
this.pc
|
|
|
.setLocalDescription(offer)
|
|
|
.then(() => {
|
|
|
const localDescription = this.pc.localDescription;
|
|
|
if (!!localDescription) {
|
|
|
OmUtil.log('Local description set', localDescription.sdp);
|
|
|
return resolve();
|
|
|
} else {
|
|
|
return reject('Local description is not defined');
|
|
|
}
|
|
|
})
|
|
|
.catch((error) => reject(error));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Other peer initiated negotiation. Step 2/4 of SDP offer-answer protocol
|
|
|
*/
|
|
|
processRemoteOffer(sdpOffer) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const offer = {
|
|
|
type: 'offer',
|
|
|
sdp: sdpOffer
|
|
|
};
|
|
|
OmUtil.log('SDP offer received, setting remote description', offer);
|
|
|
|
|
|
if (this.pc.signalingState === 'closed') {
|
|
|
return reject('RTCPeerConnection is closed when trying to set remote description');
|
|
|
}
|
|
|
this.setRemoteDescription(offer)
|
|
|
.then(() => resolve())
|
|
|
.catch((error) => reject(error));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Other peer initiated negotiation. Step 3/4 of SDP offer-answer protocol
|
|
|
*/
|
|
|
processLocalAnswer(answer) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
OmUtil.log('SDP answer created, setting local description');
|
|
|
if (this.pc.signalingState === 'closed') {
|
|
|
return reject('RTCPeerConnection is closed when trying to set local description');
|
|
|
}
|
|
|
this.pc
|
|
|
.setLocalDescription(answer)
|
|
|
.then(() => resolve())
|
|
|
.catch((error) => reject(error));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This peer initiated negotiation. Step 4/4 of SDP offer-answer protocol
|
|
|
*/
|
|
|
processRemoteAnswer(sdpAnswer) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const answer = {
|
|
|
type: 'answer',
|
|
|
sdp: sdpAnswer
|
|
|
};
|
|
|
OmUtil.log('SDP answer received, setting remote description');
|
|
|
|
|
|
if (this.pc.signalingState === 'closed') {
|
|
|
return reject('RTCPeerConnection is closed when trying to set remote description');
|
|
|
}
|
|
|
this.setRemoteDescription(answer)
|
|
|
.then(() => {
|
|
|
resolve();
|
|
|
})
|
|
|
.catch((error) => reject(error));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @hidden
|
|
|
*/
|
|
|
async setRemoteDescription(sdp) {
|
|
|
return this.pc.setRemoteDescription(sdp);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Callback function invoked when an ICE candidate is received
|
|
|
*/
|
|
|
addIceCandidate(iceCandidate) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
OmUtil.log('Remote ICE candidate received', iceCandidate);
|
|
|
this.remoteCandidatesQueue.push(iceCandidate);
|
|
|
switch (this.pc.signalingState) {
|
|
|
case 'closed':
|
|
|
reject(new Error('PeerConnection object is closed'));
|
|
|
break;
|
|
|
case 'stable':
|
|
|
if (!!this.pc.remoteDescription) {
|
|
|
this.pc
|
|
|
.addIceCandidate(iceCandidate)
|
|
|
.then(() => resolve())
|
|
|
.catch((error) => reject(error));
|
|
|
} else {
|
|
|
this.iceCandidateList.push(iceCandidate);
|
|
|
resolve();
|
|
|
}
|
|
|
break;
|
|
|
default:
|
|
|
this.iceCandidateList.push(iceCandidate);
|
|
|
resolve();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
addIceConnectionStateChangeListener(otherId) {
|
|
|
if (!this._iceConnectionStateChangeListener) {
|
|
|
this._iceConnectionStateChangeListener = () => {
|
|
|
const iceConnectionState = this.pc.iceConnectionState;
|
|
|
switch (iceConnectionState) {
|
|
|
case 'disconnected':
|
|
|
// Possible network disconnection
|
|
|
const msg1 =
|
|
|
'IceConnectionState of RTCPeerConnection ' +
|
|
|
this.configuration.id +
|
|
|
' (' +
|
|
|
otherId +
|
|
|
') change to "disconnected". Possible network disconnection';
|
|
|
console.warn(msg1);
|
|
|
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_DISCONNECTED, msg1);
|
|
|
break;
|
|
|
case 'failed':
|
|
|
const msg2 = 'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') to "failed"';
|
|
|
console.error(msg2);
|
|
|
this.configuration.onIceConnectionStateException(ExceptionEventName.ICE_CONNECTION_FAILED, msg2);
|
|
|
break;
|
|
|
case 'closed':
|
|
|
OmUtil.log(
|
|
|
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "closed"'
|
|
|
);
|
|
|
break;
|
|
|
case 'new':
|
|
|
OmUtil.log('IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "new"');
|
|
|
break;
|
|
|
case 'checking':
|
|
|
OmUtil.log(
|
|
|
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "checking"'
|
|
|
);
|
|
|
break;
|
|
|
case 'connected':
|
|
|
OmUtil.log(
|
|
|
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "connected"'
|
|
|
);
|
|
|
break;
|
|
|
case 'completed':
|
|
|
OmUtil.log(
|
|
|
'IceConnectionState of RTCPeerConnection ' + this.configuration.id + ' (' + otherId + ') change to "completed"'
|
|
|
);
|
|
|
break;
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
this.pc.addEventListener('iceconnectionstatechange', this._iceConnectionStateChangeListener);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @hidden
|
|
|
*/
|
|
|
generateUniqueId() {
|
|
|
return crypto.randomUUID();
|
|
|
}
|
|
|
|
|
|
get stream() {
|
|
|
return this.pc.getLocalStreams()[0] || this.pc.getRemoteStreams()[0];
|
|
|
}
|
|
|
|
|
|
// LEGACY code
|
|
|
deprecatedPeerConnectionTrackApi() {
|
|
|
for (const track of this.configuration.mediaStream.getTracks()) {
|
|
|
this.pc.addTrack(track, this.configuration.mediaStream);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// DEPRECATED LEGACY METHOD: Old WebRTC versions don't implement
|
|
|
// Transceivers, and instead depend on the deprecated
|
|
|
// "offerToReceiveAudio" and "offerToReceiveVideo".
|
|
|
createOfferLegacy() {
|
|
|
if (!!this.configuration.mediaStream) {
|
|
|
this.deprecatedPeerConnectionTrackApi();
|
|
|
}
|
|
|
|
|
|
const hasAudio = this.configuration.mediaConstraints.audio;
|
|
|
const hasVideo = this.configuration.mediaConstraints.video;
|
|
|
|
|
|
const options = {
|
|
|
offerToReceiveAudio: this.configuration.mode !== 'sendonly' && hasAudio,
|
|
|
offerToReceiveVideo: this.configuration.mode !== 'sendonly' && hasVideo
|
|
|
};
|
|
|
|
|
|
OmUtil.log('[createOfferLegacy] RTCPeerConnection.createOffer() options:', JSON.stringify(options));
|
|
|
|
|
|
return this.pc.createOffer(options);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class WebRtcPeerRecvonly extends WebRtcPeer {
|
|
|
constructor(configuration) {
|
|
|
configuration.mode = 'recvonly';
|
|
|
super(configuration);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
class WebRtcPeerSendonly extends WebRtcPeer {
|
|
|
constructor(configuration) {
|
|
|
configuration.mode = 'sendonly';
|
|
|
super(configuration);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
class WebRtcPeerSendrecv extends WebRtcPeer {
|
|
|
constructor(configuration) {
|
|
|
configuration.mode = 'sendrecv';
|
|
|
super(configuration);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
|
Recvonly: WebRtcPeerRecvonly,
|
|
|
Sendonly: WebRtcPeerSendonly
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./src/settings/mic-level.js":
|
|
|
/*!***********************************!*\
|
|
|
!*** ./src/settings/mic-level.js ***!
|
|
|
\***********************************/
|
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
|
|
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
|
|
|
const VideoUtil = __webpack_require__(/*! ./video-util */ "./src/settings/video-util.js");
|
|
|
const RingBuffer = __webpack_require__(/*! ./ring-buffer */ "./src/settings/ring-buffer.js");
|
|
|
|
|
|
module.exports = class MicLevel {
|
|
|
constructor() {
|
|
|
let ctx, mic, analyser
|
|
|
, cnvs, canvasCtx, WIDTH, HEIGHT, horiz
|
|
|
, vol = .0, vals = new RingBuffer(100);
|
|
|
|
|
|
this.meterStream = (stream, _cnvs, _micActivity, _error, connectAudio) => {
|
|
|
if (!stream || stream.getAudioTracks().length < 1) {
|
|
|
return;
|
|
|
}
|
|
|
try {
|
|
|
const AudioCtx = window.AudioContext || window.webkitAudioContext;
|
|
|
if (!AudioCtx) {
|
|
|
_error("AudioContext is inaccessible");
|
|
|
return;
|
|
|
}
|
|
|
ctx = new AudioCtx();
|
|
|
analyser = ctx.createAnalyser();
|
|
|
mic = ctx.createMediaStreamSource(stream);
|
|
|
mic.connect(analyser);
|
|
|
if (connectAudio) {
|
|
|
analyser.connect(ctx.destination);
|
|
|
}
|
|
|
this.meter(analyser, _cnvs, _micActivity, _error);
|
|
|
} catch (err) {
|
|
|
_error(err);
|
|
|
}
|
|
|
};
|
|
|
this.setCanvas = (_cnvs) => {
|
|
|
cnvs = _cnvs;
|
|
|
const canvas = cnvs[0];
|
|
|
canvasCtx = canvas.getContext('2d');
|
|
|
WIDTH = canvas.width;
|
|
|
HEIGHT = canvas.height;
|
|
|
horiz = cnvs.data('orientation') === 'horizontal';
|
|
|
};
|
|
|
this.meter = (_analyser, _cnvs, _micActivity, _error) => {
|
|
|
this.setCanvas(_cnvs);
|
|
|
try {
|
|
|
analyser = _analyser;
|
|
|
analyser.minDecibels = -90;
|
|
|
analyser.maxDecibels = -10;
|
|
|
analyser.fftSize = 256;
|
|
|
const color = $('body').css('--level-color')
|
|
|
, al = analyser.frequencyBinCount
|
|
|
, arr = new Uint8Array(al);
|
|
|
function update() {
|
|
|
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
|
|
|
if (!!analyser && cnvs.length > 0) {
|
|
|
if (cnvs.is(':visible')) {
|
|
|
analyser.getByteFrequencyData(arr);
|
|
|
let favg = 0.0;
|
|
|
for (let i = 0; i < al; ++i) {
|
|
|
favg += arr[i] * arr[i];
|
|
|
}
|
|
|
vol = Math.sqrt(favg / al);
|
|
|
vals.push(vol);
|
|
|
const min = vals.min();
|
|
|
_micActivity(vol > min + 5); // magic number
|
|
|
canvasCtx.fillStyle = color;
|
|
|
if (horiz) {
|
|
|
canvasCtx.fillRect(0, 0, WIDTH * vol / 100, HEIGHT);
|
|
|
} else {
|
|
|
const h = HEIGHT * vol / 100;
|
|
|
canvasCtx.fillRect(0, HEIGHT - h, WIDTH, h);
|
|
|
}
|
|
|
}
|
|
|
requestAnimationFrame(update);
|
|
|
}
|
|
|
}
|
|
|
update();
|
|
|
} catch (err) {
|
|
|
_error(err);
|
|
|
}
|
|
|
};
|
|
|
this.dispose = () => {
|
|
|
if (!!ctx) {
|
|
|
VideoUtil.cleanStream(mic.mediaStream);
|
|
|
VideoUtil.disconnect(mic);
|
|
|
VideoUtil.disconnect(ctx.destination);
|
|
|
ctx.close();
|
|
|
ctx = null;
|
|
|
}
|
|
|
if (!!analyser) {
|
|
|
VideoUtil.disconnect(analyser);
|
|
|
analyser = null;
|
|
|
}
|
|
|
};
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./src/settings/ring-buffer.js":
|
|
|
/*!*************************************!*\
|
|
|
!*** ./src/settings/ring-buffer.js ***!
|
|
|
\*************************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
|
|
|
module.exports = class RingBuffer {
|
|
|
constructor(length) {
|
|
|
const buffer = [];
|
|
|
let pos = 0;
|
|
|
|
|
|
this.get = (key) => {
|
|
|
return buffer[key];
|
|
|
};
|
|
|
this.push = (item) => {
|
|
|
buffer[pos] = item;
|
|
|
pos = (pos + 1) % length;
|
|
|
};
|
|
|
this.min = () => {
|
|
|
return Math.min.apply(Math, buffer);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./src/settings/settings.js":
|
|
|
/*!**********************************!*\
|
|
|
!*** ./src/settings/settings.js ***!
|
|
|
\**********************************/
|
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
|
|
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
|
|
|
const OmUtil = __webpack_require__(/*! ../main/omutils */ "../main/omutils");
|
|
|
const Settings = __webpack_require__(/*! ../main/settings */ "../main/settings");
|
|
|
|
|
|
const MicLevel = __webpack_require__(/*! ./mic-level */ "./src/settings/mic-level.js");
|
|
|
const VideoUtil = __webpack_require__(/*! ./video-util */ "./src/settings/video-util.js");
|
|
|
const WebRtcPeer = __webpack_require__(/*! ./WebRtcPeer */ "./src/settings/WebRtcPeer.js");
|
|
|
|
|
|
const DEV_AUDIO = 'audioinput'
|
|
|
, DEV_VIDEO = 'videoinput'
|
|
|
, MsgBase = {type: 'kurento', mode: 'test'};
|
|
|
let vs, lm, s, cam, mic, res, o, rtcPeer, timer
|
|
|
, vidScroll, vid, recBtn, playBtn, recAllowed = false
|
|
|
, level;
|
|
|
|
|
|
function _load() {
|
|
|
s = Settings.load();
|
|
|
if (!s.video) {
|
|
|
const _res = $('#video-settings .cam-resolution option:selected').data();
|
|
|
s.video = {
|
|
|
cam: 0
|
|
|
, mic: 0
|
|
|
, width: _res.width
|
|
|
, height: _res.height
|
|
|
};
|
|
|
}
|
|
|
if (!s.fixed) {
|
|
|
s.fixed = {
|
|
|
enabled: false
|
|
|
, width: 120
|
|
|
, height: 90
|
|
|
};
|
|
|
}
|
|
|
return s;
|
|
|
}
|
|
|
function _save() {
|
|
|
Settings.save(s);
|
|
|
OmUtil.sendMessage({
|
|
|
type: 'av'
|
|
|
, area: 'room'
|
|
|
, settings: s
|
|
|
});
|
|
|
}
|
|
|
function _clear(_ms) {
|
|
|
const ms = _ms || (vid && vid.length === 1 ? vid[0].srcObject : null);
|
|
|
VideoUtil.cleanStream(ms);
|
|
|
if (vid && vid.length === 1) {
|
|
|
vid[0].srcObject = null;
|
|
|
}
|
|
|
VideoUtil.cleanPeer(rtcPeer);
|
|
|
if (!!lm) {
|
|
|
lm.hide();
|
|
|
}
|
|
|
if (!!level) {
|
|
|
level.dispose();
|
|
|
level = null;
|
|
|
}
|
|
|
}
|
|
|
function _close() {
|
|
|
_clear();
|
|
|
Wicket.Event.unsubscribe('/websocket/message', _onWsMessage);
|
|
|
}
|
|
|
function _onIceCandidate(candidate) {
|
|
|
OmUtil.log('Local candidate' + JSON.stringify(candidate));
|
|
|
OmUtil.sendMessage({
|
|
|
id : 'iceCandidate'
|
|
|
, candidate: candidate
|
|
|
}, MsgBase);
|
|
|
}
|
|
|
function _init(options) {
|
|
|
o = JSON.parse(JSON.stringify(options));
|
|
|
if (!!o.infoMsg) {
|
|
|
OmUtil.alert('info', o.infoMsg, 0);
|
|
|
}
|
|
|
vs = $('#video-settings');
|
|
|
lm = vs.find('.level-meter');
|
|
|
cam = vs.find('select.cam').change(function() {
|
|
|
_readValues();
|
|
|
});
|
|
|
mic = vs.find('select.mic').change(function() {
|
|
|
_readValues();
|
|
|
});
|
|
|
res = vs.find('select.cam-resolution').change(function() {
|
|
|
_readValues();
|
|
|
});
|
|
|
vidScroll = vs.find('.vid-block .video-conainer');
|
|
|
timer = vs.find('.timer');
|
|
|
vid = vidScroll.find('video');
|
|
|
recBtn = vs.find('.rec-start')
|
|
|
.click(function() {
|
|
|
recBtn.prop('disabled', true);
|
|
|
_setEnabled(true);
|
|
|
OmUtil.sendMessage({
|
|
|
id : 'wannaRecord'
|
|
|
}, MsgBase);
|
|
|
});
|
|
|
playBtn = vs.find('.play')
|
|
|
.click(function() {
|
|
|
recBtn.prop('disabled', true);
|
|
|
_setEnabled(true);
|
|
|
OmUtil.sendMessage({
|
|
|
id : 'wannaPlay'
|
|
|
}, MsgBase);
|
|
|
});
|
|
|
vs.find('.btn-save').off().click(function() {
|
|
|
_save();
|
|
|
_close();
|
|
|
vs.modal("hide");
|
|
|
});
|
|
|
vs.find('.btn-cancel').off().click(function() {
|
|
|
_close();
|
|
|
vs.modal("hide");
|
|
|
});
|
|
|
vs.off().on('hidden.bs.modal', function () {
|
|
|
_close();
|
|
|
});
|
|
|
o.width = 300;
|
|
|
o.height = 200;
|
|
|
o.mode = 'settings';
|
|
|
o.rights = (o.rights || []).join();
|
|
|
delete o.keycode;
|
|
|
vs.find('.modal-body input, .modal-body button').prop('disabled', true);
|
|
|
const rr = vs.find('.cam-resolution').parents('.sett-row');
|
|
|
if (!o.interview) {
|
|
|
rr.show();
|
|
|
} else {
|
|
|
rr.hide();
|
|
|
}
|
|
|
_load();
|
|
|
_save(); // trigger settings update
|
|
|
}
|
|
|
function _updateRec() {
|
|
|
recBtn.prop('disabled', !recAllowed || (s.video.cam < 0 && s.video.mic < 0));
|
|
|
}
|
|
|
function _setCntsDimensions(cnts) {
|
|
|
if (VideoUtil.isSafari()) {
|
|
|
let width = s.video.width;
|
|
|
//valid widths are 320, 640, 1280
|
|
|
[320, 640, 1280].some(function(w) {
|
|
|
if (width < w + 1) {
|
|
|
width = w;
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
});
|
|
|
cnts.video.width = width < 1281 ? width : 1280;
|
|
|
} else {
|
|
|
cnts.video.width = o.interview ? 320 : s.video.width;
|
|
|
cnts.video.height = o.interview ? 260 : s.video.height;
|
|
|
}
|
|
|
}
|
|
|
//each bool OR https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
|
|
|
// min/ideal/max/exact/mandatory can also be used
|
|
|
function _constraints(sd, callback) {
|
|
|
_getDevConstraints(function(devCnts) {
|
|
|
const cnts = {
|
|
|
videoEnabled: VideoUtil.hasCam(sd)
|
|
|
, audioEnabled: VideoUtil.hasMic(sd)
|
|
|
};
|
|
|
if (devCnts.video && false === o.audioOnly && s.video.cam > -1) {
|
|
|
cnts.video = {
|
|
|
frameRate: o.camera.fps
|
|
|
};
|
|
|
_setCntsDimensions(cnts)
|
|
|
if (!!s.video.camDevice) {
|
|
|
cnts.video.deviceId = {
|
|
|
ideal: s.video.camDevice
|
|
|
};
|
|
|
} else {
|
|
|
cnts.video.facingMode = {
|
|
|
ideal: 'user'
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
cnts.video = false;
|
|
|
}
|
|
|
if (devCnts.audio && s.video.mic > -1) {
|
|
|
cnts.audio = {
|
|
|
sampleRate: o.microphone.rate
|
|
|
, echoCancellation: o.microphone.echo
|
|
|
, noiseSuppression: o.microphone.noise
|
|
|
};
|
|
|
if (!!s.video.micDevice) {
|
|
|
cnts.audio.deviceId = {
|
|
|
ideal: s.video.micDevice
|
|
|
};
|
|
|
}
|
|
|
} else {
|
|
|
cnts.audio = false;
|
|
|
}
|
|
|
callback(cnts);
|
|
|
});
|
|
|
}
|
|
|
function _readValues(msg, func) {
|
|
|
const v = cam.find('option:selected')
|
|
|
, m = mic.find('option:selected')
|
|
|
, o = res.find('option:selected').data();
|
|
|
s.video.cam = 1 * cam.val();
|
|
|
s.video.camDevice = v.data('device-id');
|
|
|
s.video.mic = 1 * mic.val();
|
|
|
s.video.micDevice = m.data('device-id');
|
|
|
s.video.width = o.width;
|
|
|
s.video.height = o.height;
|
|
|
vid.width(o.width).height(o.height);
|
|
|
vidScroll.scrollLeft(Math.max(0, s.video.width / 2 - 150))
|
|
|
.scrollTop(Math.max(0, s.video.height / 2 - 110));
|
|
|
_clear();
|
|
|
_constraints(null, function(cnts) {
|
|
|
if (cnts.video !== false || cnts.audio !== false) {
|
|
|
const options = VideoUtil.addIceServers({
|
|
|
mediaConstraints: cnts
|
|
|
, onIceCandidate: _onIceCandidate
|
|
|
}, msg);
|
|
|
navigator.mediaDevices.getUserMedia(cnts)
|
|
|
.then(stream => {
|
|
|
VideoUtil.playSrc(vid[0], stream, true);
|
|
|
options.mediaStream = stream;
|
|
|
|
|
|
rtcPeer = new WebRtcPeer.Sendonly(options);
|
|
|
if (cnts.audio) {
|
|
|
lm.show();
|
|
|
level = new MicLevel();
|
|
|
level.meterStream(stream, lm, function(){}, OmUtil.error, false);
|
|
|
} else {
|
|
|
lm.hide();
|
|
|
}
|
|
|
return rtcPeer.createOffer();
|
|
|
})
|
|
|
.then(sdpOffer => {
|
|
|
rtcPeer.processLocalOffer(sdpOffer);
|
|
|
if (typeof(func) === 'function') {
|
|
|
func(sdpOffer.sdp, cnts);
|
|
|
} else {
|
|
|
_allowRec(true);
|
|
|
}
|
|
|
}).catch(_ => OmUtil.error('Error generating the offer'));
|
|
|
}
|
|
|
if (!msg) {
|
|
|
_updateRec();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
function _allowRec(allow) {
|
|
|
recAllowed = allow;
|
|
|
_updateRec();
|
|
|
}
|
|
|
function _setLoading(el) {
|
|
|
el.find('option').remove();
|
|
|
el.append(OmUtil.tmpl('#settings-option-loading'));
|
|
|
}
|
|
|
function _setDisabled(els) {
|
|
|
els.forEach(function(el) {
|
|
|
el.find('option').remove();
|
|
|
el.append(OmUtil.tmpl('#settings-option-disabled'));
|
|
|
});
|
|
|
}
|
|
|
function _setSelectedDevice(dev, devIdx) {
|
|
|
let o = dev.find('option[value="' + devIdx + '"]');
|
|
|
if (o.length === 0 && devIdx !== -1) {
|
|
|
o = dev.find('option[value="0"]');
|
|
|
}
|
|
|
o.prop('selected', true);
|
|
|
}
|
|
|
function _getDevConstraints(callback) {
|
|
|
const devCnts = {audio: false, video: false, devices: []};
|
|
|
if (window.isSecureContext === false) {
|
|
|
OmUtil.error($('#settings-https-required').text());
|
|
|
return;
|
|
|
}
|
|
|
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
|
|
|
OmUtil.error('enumerateDevices() not supported.');
|
|
|
return;
|
|
|
}
|
|
|
navigator.mediaDevices.enumerateDevices()
|
|
|
.then(devices => devices.forEach(device => {
|
|
|
if (DEV_AUDIO === device.kind || DEV_VIDEO === device.kind) {
|
|
|
devCnts.devices.push({
|
|
|
kind: device.kind
|
|
|
, label: device.label || (device.kind + ' ' + devCnts.devices.length)
|
|
|
, deviceId: device.deviceId
|
|
|
});
|
|
|
}
|
|
|
if (DEV_AUDIO === device.kind) {
|
|
|
devCnts.audio = true;
|
|
|
} else if (DEV_VIDEO === device.kind) {
|
|
|
devCnts.video = true;
|
|
|
}
|
|
|
}))
|
|
|
.catch(() => OmUtil.error('Unable to get the list of multimedia devices'))
|
|
|
.finally(() => callback(devCnts));
|
|
|
}
|
|
|
function _initDevices() {
|
|
|
if (window.isSecureContext === false) {
|
|
|
OmUtil.error($('#settings-https-required').text());
|
|
|
return;
|
|
|
}
|
|
|
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
|
|
|
OmUtil.error('enumerateDevices() not supported.');
|
|
|
return;
|
|
|
}
|
|
|
_setLoading(cam);
|
|
|
_setLoading(mic);
|
|
|
_getDevConstraints(function(devCnts) {
|
|
|
if (!devCnts.audio && !devCnts.video) {
|
|
|
_setDisabled([cam, mic]);
|
|
|
return;
|
|
|
}
|
|
|
navigator.mediaDevices.getUserMedia(devCnts)
|
|
|
.then(stream => {
|
|
|
const devices = navigator.mediaDevices.enumerateDevices()
|
|
|
.catch(function(err) {
|
|
|
throw err;
|
|
|
})
|
|
|
.finally(() => _clear(stream));
|
|
|
return devices || devCnts.devices;
|
|
|
})
|
|
|
.catch(function() {
|
|
|
return devCnts.devices;
|
|
|
})
|
|
|
.then(devices => {
|
|
|
let cCount = 0, mCount = 0;
|
|
|
_load();
|
|
|
_setDisabled([cam, mic]);
|
|
|
devices.forEach(device => {
|
|
|
if (DEV_AUDIO === device.kind) {
|
|
|
const o = $('<option></option>').attr('value', mCount).text(device.label)
|
|
|
.data('device-id', device.deviceId);
|
|
|
mic.append(o);
|
|
|
mCount++;
|
|
|
} else if (DEV_VIDEO === device.kind) {
|
|
|
const o = $('<option></option>').attr('value', cCount).text(device.label)
|
|
|
.data('device-id', device.deviceId);
|
|
|
cam.append(o);
|
|
|
cCount++;
|
|
|
}
|
|
|
});
|
|
|
_setSelectedDevice(cam, s.video.cam);
|
|
|
_setSelectedDevice(mic, s.video.mic);
|
|
|
res.find('option').each(function() {
|
|
|
const o = $(this).data();
|
|
|
if (o.width === s.video.width && o.height === s.video.height) {
|
|
|
$(this).prop('selected', true);
|
|
|
return false;
|
|
|
}
|
|
|
});
|
|
|
_readValues();
|
|
|
})
|
|
|
.catch(function(err) {
|
|
|
_setDisabled([cam, mic]);
|
|
|
OmUtil.error(err);
|
|
|
});
|
|
|
});
|
|
|
}
|
|
|
function _open() {
|
|
|
Wicket.Event.subscribe('/websocket/message', _onWsMessage);
|
|
|
recAllowed = false;
|
|
|
timer.hide();
|
|
|
playBtn.prop('disabled', true);
|
|
|
vs.modal('show');
|
|
|
_load();
|
|
|
_initDevices();
|
|
|
}
|
|
|
function _setEnabled(enabled) {
|
|
|
playBtn.prop('disabled', enabled);
|
|
|
cam.prop('disabled', enabled);
|
|
|
mic.prop('disabled', enabled);
|
|
|
res.prop('disabled', enabled);
|
|
|
}
|
|
|
function _onStop() {
|
|
|
_updateRec();
|
|
|
_setEnabled(false);
|
|
|
}
|
|
|
function _onKMessage(m) {
|
|
|
OmUtil.info('Received message: ', m);
|
|
|
switch (m.id) {
|
|
|
case 'canRecord':
|
|
|
_readValues(m, function(_offerSdp, cnts) {
|
|
|
OmUtil.info('Invoking SDP offer callback function');
|
|
|
OmUtil.sendMessage({
|
|
|
id : 'record'
|
|
|
, sdpOffer: _offerSdp
|
|
|
, video: cnts.video !== false
|
|
|
, audio: cnts.audio !== false
|
|
|
}, MsgBase);
|
|
|
});
|
|
|
break;
|
|
|
case 'canPlay': {
|
|
|
const options = VideoUtil.addIceServers({
|
|
|
mediaConstraints: {audio: true, video: true}
|
|
|
, onIceCandidate: _onIceCandidate
|
|
|
}, m);
|
|
|
_clear();
|
|
|
rtcPeer = new WebRtcPeer.Recvonly(options);
|
|
|
rtcPeer.createOffer()
|
|
|
.then(sdpOffer => {
|
|
|
rtcPeer.processLocalOffer(sdpOffer);
|
|
|
OmUtil.sendMessage({
|
|
|
id : 'play'
|
|
|
, sdpOffer: sdpOffer.sdp
|
|
|
}, MsgBase);
|
|
|
})
|
|
|
.catch(_ => OmUtil.error('Error generating the offer'));
|
|
|
}
|
|
|
break;
|
|
|
case 'playResponse':
|
|
|
OmUtil.log('Play SDP answer received from server. Processing ...');
|
|
|
|
|
|
rtcPeer.processRemoteAnswer(m.sdpAnswer)
|
|
|
.then(() => {
|
|
|
const stream = rtcPeer.stream;
|
|
|
if (stream) {
|
|
|
VideoUtil.playSrc(vid[0], stream, false);
|
|
|
lm.show();
|
|
|
level = new MicLevel();
|
|
|
level.meterStream(stream, lm, function(){}, OmUtil.error, true);
|
|
|
};
|
|
|
})
|
|
|
.catch(error => OmUtil.error(error));
|
|
|
break;
|
|
|
case 'startResponse':
|
|
|
OmUtil.log('SDP answer received from server. Processing ...');
|
|
|
rtcPeer.processRemoteAnswer(m.sdpAnswer)
|
|
|
.catch(error => OmUtil.error(error));
|
|
|
break;
|
|
|
case 'iceCandidate':
|
|
|
rtcPeer.addIceCandidate(m.candidate)
|
|
|
.catch(error => OmUtil.error('Error adding candidate: ' + error));
|
|
|
break;
|
|
|
case 'recording':
|
|
|
timer.show().find('.time').text(m.time);
|
|
|
break;
|
|
|
case 'recStopped':
|
|
|
timer.hide();
|
|
|
_onStop();
|
|
|
break;
|
|
|
case 'playStopped':
|
|
|
_onStop();
|
|
|
_readValues();
|
|
|
break;
|
|
|
default:
|
|
|
// no-op
|
|
|
}
|
|
|
}
|
|
|
function _onWsMessage(jqEvent, msg) {
|
|
|
try {
|
|
|
if (msg instanceof Blob) {
|
|
|
return; //ping
|
|
|
}
|
|
|
const m = JSON.parse(msg);
|
|
|
if (m && 'kurento' === m.type) {
|
|
|
if ('test' === m.mode) {
|
|
|
_onKMessage(m);
|
|
|
}
|
|
|
switch (m.id) {
|
|
|
case 'error':
|
|
|
OmUtil.error(m.message);
|
|
|
break;
|
|
|
default:
|
|
|
//no-op
|
|
|
}
|
|
|
}
|
|
|
} catch (err) {
|
|
|
OmUtil.error(err);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
init: _init
|
|
|
, open: _open
|
|
|
, close: function() {
|
|
|
_close();
|
|
|
vs && vs.modal('hide');
|
|
|
}
|
|
|
, load: _load
|
|
|
, save: _save
|
|
|
, constraints: _constraints
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./src/settings/video-util.js":
|
|
|
/*!************************************!*\
|
|
|
!*** ./src/settings/video-util.js ***!
|
|
|
\************************************/
|
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
|
|
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
|
|
|
const OmUtil = __webpack_require__(/*! ../main/omutils */ "../main/omutils");
|
|
|
const Settings = __webpack_require__(/*! ../main/settings */ "../main/settings");
|
|
|
|
|
|
const UAParser = __webpack_require__(/*! ua-parser-js */ "./node_modules/ua-parser-js/src/ua-parser.js")
|
|
|
, ua = (typeof window !== 'undefined' && window.navigator) ? window.navigator.userAgent : ''
|
|
|
, parser = new UAParser(ua)
|
|
|
, browser = parser.getBrowser();
|
|
|
|
|
|
const WB_AREA_SEL = '.room-block .wb-block';
|
|
|
const WBA_WB_SEL = '.room-block .wb-block .wb-tab-content';
|
|
|
const VIDWIN_SEL = '.video.user-video';
|
|
|
const VID_SEL = '.video-container[id!=user-video]';
|
|
|
const CAM_ACTIVITY = 'VIDEO';
|
|
|
const MIC_ACTIVITY = 'AUDIO';
|
|
|
const SCREEN_ACTIVITY = 'SCREEN';
|
|
|
const REC_ACTIVITY = 'RECORD';
|
|
|
|
|
|
function _isSafari() {
|
|
|
return browser.name === 'Safari';
|
|
|
}
|
|
|
function _isChrome() {
|
|
|
return browser.name === 'Chrome' || browser.name === 'Chromium';
|
|
|
}
|
|
|
function _isEdge() {
|
|
|
return browser.name === 'Edge' && "MSGestureEvent" in window;
|
|
|
}
|
|
|
function _isEdgeChromium() {
|
|
|
return browser.name === 'Edge' && !("MSGestureEvent" in window);
|
|
|
}
|
|
|
|
|
|
function _getVid(uid) {
|
|
|
return 'video' + uid;
|
|
|
}
|
|
|
function _isSharing(sd) {
|
|
|
return !!sd && 'SCREEN' === sd.type && sd.activities.includes(SCREEN_ACTIVITY);
|
|
|
}
|
|
|
function _isRecording(sd) {
|
|
|
return !!sd && 'SCREEN' === sd.type && sd.activities.includes(REC_ACTIVITY);
|
|
|
}
|
|
|
function _hasActivity(sd, act) {
|
|
|
return !!sd && sd.activities.includes(act);
|
|
|
}
|
|
|
function _hasMic(sd) {
|
|
|
if (!sd) {
|
|
|
return true;
|
|
|
}
|
|
|
const enabled = sd.micEnabled !== false;
|
|
|
return sd.activities.includes(MIC_ACTIVITY) && enabled;
|
|
|
}
|
|
|
function _hasCam(sd) {
|
|
|
if (!sd) {
|
|
|
return true;
|
|
|
}
|
|
|
const enabled = sd.camEnabled !== false;
|
|
|
return sd.activities.includes(CAM_ACTIVITY) && enabled;
|
|
|
}
|
|
|
function _hasVideo(sd) {
|
|
|
return _hasCam(sd) || _isSharing(sd) || _isRecording(sd);
|
|
|
}
|
|
|
function _getRects(sel, excl) {
|
|
|
const list = [], elems = $(sel);
|
|
|
for (let i = 0; i < elems.length; ++i) {
|
|
|
if (excl !== $(elems[i]).attr('aria-describedby')) {
|
|
|
list.push(_getRect(elems[i]));
|
|
|
}
|
|
|
}
|
|
|
return list;
|
|
|
}
|
|
|
function _getRect(e) {
|
|
|
const win = $(e), winoff = win.offset();
|
|
|
return {left: winoff.left
|
|
|
, top: winoff.top
|
|
|
, right: winoff.left + win.width()
|
|
|
, bottom: winoff.top + win.height()};
|
|
|
}
|
|
|
function _container() {
|
|
|
const a = $(WB_AREA_SEL);
|
|
|
const c = a.find('.wb-area .tabs .wb-tab-content');
|
|
|
return c.length > 0 ? $(WBA_WB_SEL) : a;
|
|
|
}
|
|
|
function __processTopToBottom(area, rectNew, list) {
|
|
|
const offsetX = 20
|
|
|
, offsetY = 10;
|
|
|
|
|
|
let minY = area.bottom, posFound;
|
|
|
do {
|
|
|
posFound = true;
|
|
|
for (let i = 0; i < list.length; ++i) {
|
|
|
const rect = list[i];
|
|
|
minY = Math.min(minY, rect.bottom);
|
|
|
|
|
|
if (rectNew.left < rect.right && rectNew.right > rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
|
|
|
rectNew.left = rect.right + offsetX;
|
|
|
posFound = false;
|
|
|
}
|
|
|
if (rectNew.right >= area.right) {
|
|
|
rectNew.left = area.left;
|
|
|
rectNew.top = Math.max(minY, rectNew.top) + offsetY;
|
|
|
posFound = false;
|
|
|
}
|
|
|
if (rectNew.bottom >= area.bottom) {
|
|
|
rectNew.top = area.top;
|
|
|
posFound = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
} while (!posFound);
|
|
|
return {left: rectNew.left, top: rectNew.top};
|
|
|
}
|
|
|
function __processEqualsBottomToTop(area, rectNew, list) {
|
|
|
const offsetX = 20
|
|
|
, offsetY = 10;
|
|
|
|
|
|
rectNew.bottom = area.bottom;
|
|
|
let minY = area.bottom, posFound;
|
|
|
do {
|
|
|
posFound = true;
|
|
|
for (let i = 0; i < list.length; ++i) {
|
|
|
const rect = list[i];
|
|
|
minY = Math.min(minY, rect.top);
|
|
|
|
|
|
if (rectNew.left < rect.right && rectNew.right > rect.left && rectNew.top < rect.bottom && rectNew.bottom > rect.top) {
|
|
|
rectNew.left = rect.right + offsetX;
|
|
|
posFound = false;
|
|
|
}
|
|
|
if (rectNew.right >= area.right) {
|
|
|
rectNew.left = area.left;
|
|
|
rectNew.bottom = Math.min(minY, rectNew.top) - offsetY;
|
|
|
posFound = false;
|
|
|
}
|
|
|
if (rectNew.top <= area.top) {
|
|
|
rectNew.top = area.top;
|
|
|
posFound = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
} while (!posFound);
|
|
|
return {left: rectNew.left, top: rectNew.top};
|
|
|
}
|
|
|
function _getPos(list, w, h, _processor) {
|
|
|
if (Room.getOptions().interview) {
|
|
|
return {left: 0, top: 0};
|
|
|
}
|
|
|
const wba = _container()
|
|
|
, woffset = wba.offset()
|
|
|
, area = {left: woffset.left, top: woffset.top, right: woffset.left + wba.width(), bottom: woffset.top + wba.height()}
|
|
|
, rectNew = {
|
|
|
_left: area.left
|
|
|
, _top: area.top
|
|
|
, _right: area.left + w
|
|
|
, _bottom: area.top + h
|
|
|
, get left() {
|
|
|
return this._left;
|
|
|
}
|
|
|
, set left(l) {
|
|
|
this._left = l;
|
|
|
this._right = l + w;
|
|
|
}
|
|
|
, get right() {
|
|
|
return this._right;
|
|
|
}
|
|
|
, get top() {
|
|
|
return this._top;
|
|
|
}
|
|
|
, set top(t) {
|
|
|
this._top = t;
|
|
|
this._bottom = t + h;
|
|
|
}
|
|
|
, set bottom(b) {
|
|
|
this._bottom = b;
|
|
|
this._top = b - h;
|
|
|
}
|
|
|
, get bottom() {
|
|
|
return this._bottom;
|
|
|
}
|
|
|
};
|
|
|
const processor = _processor || __processTopToBottom;
|
|
|
return processor(area, rectNew, list);
|
|
|
}
|
|
|
function _arrange() {
|
|
|
const list = [];
|
|
|
$(VIDWIN_SEL).each(function() {
|
|
|
const v = $(this);
|
|
|
v.css(_getPos(list, v.width(), v.height()));
|
|
|
list.push(_getRect(v));
|
|
|
});
|
|
|
}
|
|
|
function _arrangeResize(vSettings) {
|
|
|
const list = []
|
|
|
, size = {width: 120, height: 90};
|
|
|
if (vSettings.fixed.enabled) {
|
|
|
size.width = vSettings.fixed.width;
|
|
|
size.height = vSettings.fixed.height;
|
|
|
}
|
|
|
|
|
|
function __getDialog(_v) {
|
|
|
return $(_v).find('.video-container.ui-dialog-content');
|
|
|
}
|
|
|
$(VIDWIN_SEL).toArray().sort((v1, v2) => {
|
|
|
const c1 = __getDialog(v1).data().stream()
|
|
|
, c2 = __getDialog(v2).data().stream();
|
|
|
return c2.level - c1.level || c1.user.displayName.localeCompare(c2.user.displayName);
|
|
|
}).forEach(_v => {
|
|
|
const v = $(_v);
|
|
|
__getDialog(v)
|
|
|
.dialog('option', 'width', size.width)
|
|
|
.dialog('option', 'height', size.height);
|
|
|
v.css(_getPos(list, v.width(), v.height(), __processEqualsBottomToTop));
|
|
|
list.push(_getRect(v));
|
|
|
});
|
|
|
}
|
|
|
function _cleanStream(stream) {
|
|
|
if (!!stream) {
|
|
|
stream.getTracks().forEach(track => track.stop());
|
|
|
}
|
|
|
}
|
|
|
function _cleanPeer(rtcPeer) {
|
|
|
if (!!rtcPeer) {
|
|
|
try {
|
|
|
const pc = rtcPeer.pc;
|
|
|
if (!!pc) {
|
|
|
pc.getSenders().forEach(sender => {
|
|
|
try {
|
|
|
if (sender.track) {
|
|
|
sender.track.stop();
|
|
|
}
|
|
|
} catch(e) {
|
|
|
OmUtil.log('Failed to clean sender' + e);
|
|
|
}
|
|
|
});
|
|
|
pc.getReceivers().forEach(receiver => {
|
|
|
try {
|
|
|
if (receiver.track) {
|
|
|
receiver.track.stop();
|
|
|
}
|
|
|
} catch(e) {
|
|
|
OmUtil.log('Failed to clean receiver' + e);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
rtcPeer.dispose();
|
|
|
} catch(e) {
|
|
|
//no-op
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
function _setPos(v, pos) {
|
|
|
if (v.dialog('instance')) {
|
|
|
v.dialog('widget').css(pos);
|
|
|
}
|
|
|
}
|
|
|
function _askPermission(callback) {
|
|
|
const perm = $('#ask-permission');
|
|
|
$('.sidebar').confirmation({
|
|
|
title: perm.attr('title')
|
|
|
, placement: Settings.isRtl ? 'right' : 'left'
|
|
|
, singleton: true
|
|
|
, rootSelector: '.sidebar'
|
|
|
, html: true
|
|
|
, content: perm.html()
|
|
|
, buttons: [{
|
|
|
class: 'btn btn-sm btn-warning'
|
|
|
, label: perm.data('btn-ok')
|
|
|
, value: perm.data('btn-ok')
|
|
|
, onClick: function() {
|
|
|
callback();
|
|
|
$('.sidebar').confirmation('dispose');
|
|
|
}
|
|
|
}]
|
|
|
});
|
|
|
$('.sidebar').confirmation('show');
|
|
|
}
|
|
|
function _disconnect(node) {
|
|
|
try {
|
|
|
node.disconnect(); //this one can throw
|
|
|
} catch (e) {
|
|
|
//no-op
|
|
|
}
|
|
|
}
|
|
|
function _sharingSupported() {
|
|
|
return (browser.name === 'Edge' && browser.major > 16)
|
|
|
|| (typeof(navigator.mediaDevices.getDisplayMedia) === 'function'
|
|
|
&& (browser.name === 'Firefox'
|
|
|
|| browser.name === 'Opera'
|
|
|
|| browser.name === 'Yandex'
|
|
|
|| _isSafari()
|
|
|
|| _isChrome()
|
|
|
|| _isEdgeChromium()
|
|
|
|| (browser.name === 'Mozilla' && browser.major > 4)
|
|
|
));
|
|
|
}
|
|
|
function _highlight(el, clazz, count) {
|
|
|
if (!el || el.length < 1 || el.hasClass('disabled') || count < 0) {
|
|
|
return;
|
|
|
}
|
|
|
el.addClass(clazz).delay(2000).queue(function(next) {
|
|
|
el.removeClass(clazz).delay(2000).queue(function(next1) {
|
|
|
_highlight(el, clazz, --count);
|
|
|
next1();
|
|
|
});
|
|
|
next();
|
|
|
});
|
|
|
}
|
|
|
function _playSrc(_video, _stream, mute) {
|
|
|
if (_stream && _video) {
|
|
|
_video.srcObject = _stream;
|
|
|
if (_video.paused) {
|
|
|
_video.play().then(() => _video.muted = mute).catch(err => {
|
|
|
if ('NotAllowedError' === err.name) {
|
|
|
_askPermission(() => _video.play().then(() => _video.muted = mute));
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
VIDWIN_SEL: VIDWIN_SEL
|
|
|
, VID_SEL: VID_SEL
|
|
|
, CAM_ACTIVITY: CAM_ACTIVITY
|
|
|
, MIC_ACTIVITY: MIC_ACTIVITY
|
|
|
|
|
|
, getVid: _getVid
|
|
|
, isSharing: _isSharing
|
|
|
, isRecording: _isRecording
|
|
|
, hasMic: _hasMic
|
|
|
, hasCam: _hasCam
|
|
|
, hasVideo: _hasVideo
|
|
|
, hasActivity: _hasActivity
|
|
|
, getRects: _getRects
|
|
|
, getPos: _getPos
|
|
|
, container: _container
|
|
|
, arrange: _arrange
|
|
|
, arrangeResize: _arrangeResize
|
|
|
, cleanStream: _cleanStream
|
|
|
, cleanPeer: _cleanPeer
|
|
|
, addIceServers: function(opts, m) {
|
|
|
if (m && m.iceServers && m.iceServers.length > 0) {
|
|
|
opts.iceServers = m.iceServers;
|
|
|
}
|
|
|
return opts;
|
|
|
}
|
|
|
, setPos: _setPos
|
|
|
, askPermission: _askPermission
|
|
|
, disconnect: _disconnect
|
|
|
, sharingSupported: _sharingSupported
|
|
|
, highlight: _highlight
|
|
|
, playSrc: _playSrc
|
|
|
|
|
|
, browser: browser
|
|
|
, isEdge: _isEdge
|
|
|
, isEdgeChromium: _isEdgeChromium
|
|
|
, isChrome: _isChrome
|
|
|
, isSafari: _isSafari
|
|
|
};
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "../main/omutils":
|
|
|
/*!*************************!*\
|
|
|
!*** external "OmUtil" ***!
|
|
|
\*************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
"use strict";
|
|
|
module.exports = OmUtil;
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "../main/settings":
|
|
|
/*!***************************!*\
|
|
|
!*** external "Settings" ***!
|
|
|
\***************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
"use strict";
|
|
|
module.exports = Settings;
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/freeice/stun.json":
|
|
|
/*!****************************************!*\
|
|
|
!*** ./node_modules/freeice/stun.json ***!
|
|
|
\****************************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
"use strict";
|
|
|
module.exports = JSON.parse('["stun.l.google.com:19302","stun1.l.google.com:19302","stun2.l.google.com:19302","stun3.l.google.com:19302","stun4.l.google.com:19302","stun.ekiga.net","stun.ideasip.com","stun.schlund.de","stun.stunprotocol.org:3478","stun.voiparound.com","stun.voipbuster.com","stun.voipstunt.com","stun.voxgratia.org"]');
|
|
|
|
|
|
/***/ }),
|
|
|
|
|
|
/***/ "./node_modules/freeice/turn.json":
|
|
|
/*!****************************************!*\
|
|
|
!*** ./node_modules/freeice/turn.json ***!
|
|
|
\****************************************/
|
|
|
/***/ ((module) => {
|
|
|
|
|
|
"use strict";
|
|
|
module.exports = [];
|
|
|
|
|
|
/***/ })
|
|
|
|
|
|
/******/ });
|
|
|
/************************************************************************/
|
|
|
/******/ // The module cache
|
|
|
/******/ var __webpack_module_cache__ = {};
|
|
|
/******/
|
|
|
/******/ // The require function
|
|
|
/******/ function __webpack_require__(moduleId) {
|
|
|
/******/ // Check if module is in cache
|
|
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
|
/******/ if (cachedModule !== undefined) {
|
|
|
/******/ return cachedModule.exports;
|
|
|
/******/ }
|
|
|
/******/ // Create a new module (and put it into the cache)
|
|
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
|
/******/ // no module.id needed
|
|
|
/******/ // no module.loaded needed
|
|
|
/******/ exports: {}
|
|
|
/******/ };
|
|
|
/******/
|
|
|
/******/ // Execute the module function
|
|
|
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
|
/******/
|
|
|
/******/ // Return the exports of the module
|
|
|
/******/ return module.exports;
|
|
|
/******/ }
|
|
|
/******/
|
|
|
/************************************************************************/
|
|
|
/******/ /* webpack/runtime/amd options */
|
|
|
/******/ (() => {
|
|
|
/******/ __webpack_require__.amdO = {};
|
|
|
/******/ })();
|
|
|
/******/
|
|
|
/******/ /* webpack/runtime/compat get default export */
|
|
|
/******/ (() => {
|
|
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
|
/******/ __webpack_require__.n = (module) => {
|
|
|
/******/ var getter = module && module.__esModule ?
|
|
|
/******/ () => (module['default']) :
|
|
|
/******/ () => (module);
|
|
|
/******/ __webpack_require__.d(getter, { a: getter });
|
|
|
/******/ return getter;
|
|
|
/******/ };
|
|
|
/******/ })();
|
|
|
/******/
|
|
|
/******/ /* webpack/runtime/define property getters */
|
|
|
/******/ (() => {
|
|
|
/******/ // define getter functions for harmony exports
|
|
|
/******/ __webpack_require__.d = (exports, definition) => {
|
|
|
/******/ for(var key in definition) {
|
|
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
|
/******/ }
|
|
|
/******/ }
|
|
|
/******/ };
|
|
|
/******/ })();
|
|
|
/******/
|
|
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
|
/******/ (() => {
|
|
|
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
|
/******/ })();
|
|
|
/******/
|
|
|
/******/ /* webpack/runtime/make namespace object */
|
|
|
/******/ (() => {
|
|
|
/******/ // define __esModule on exports
|
|
|
/******/ __webpack_require__.r = (exports) => {
|
|
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
/******/ }
|
|
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
/******/ };
|
|
|
/******/ })();
|
|
|
/******/
|
|
|
/************************************************************************/
|
|
|
var __webpack_exports__ = {};
|
|
|
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
|
|
|
(() => {
|
|
|
/*!*******************************!*\
|
|
|
!*** ./src/settings/index.js ***!
|
|
|
\*******************************/
|
|
|
/* Licensed under the Apache License, Version 2.0 (the "License") http://www.apache.org/licenses/LICENSE-2.0 */
|
|
|
__webpack_require__(/*! webrtc-adapter */ "./node_modules/webrtc-adapter/src/js/adapter_core.js");
|
|
|
|
|
|
if (window.hasOwnProperty('isSecureContext') === false) {
|
|
|
window.isSecureContext = window.location.protocol == 'https:' || ["localhost", "127.0.0.1"].indexOf(window.location.hostname) !== -1;
|
|
|
}
|
|
|
|
|
|
Object.assign(window, {
|
|
|
VideoUtil: __webpack_require__(/*! ./video-util */ "./src/settings/video-util.js")
|
|
|
, MicLevel: __webpack_require__(/*! ./mic-level */ "./src/settings/mic-level.js")
|
|
|
, WebRtcPeer: __webpack_require__(/*! ./WebRtcPeer */ "./src/settings/WebRtcPeer.js")
|
|
|
, VideoSettings: __webpack_require__(/*! ./settings */ "./src/settings/settings.js")
|
|
|
});
|
|
|
|
|
|
})();
|
|
|
|
|
|
/******/ })()
|
|
|
;
|
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixrQkFBa0IsT0FBTztBQUMzQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTCxHQUFHO0FBQ0g7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSw2Q0FBNkM7QUFDN0M7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0I7QUFDeEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0E7QUFDQSxNQUFNO0FBQ047QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQVk7QUFDWjtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0Esa0JBQWtCLGtCQUFrQjtBQUNwQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGtCQUFrQixrQkFBa0I7QUFDcEM7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxJQUFJLElBQTBCO0FBQzlCO0FBQ0E7Ozs7Ozs7Ozs7O0FDanlCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOzs7QUFHQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0I7QUFDbEI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBLDBCQUEwQixjQUFjO0FBQ3hDO0FBQ0E7QUFDQTtBQUNBLFNBQVM7QUFDVDtBQUNBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFD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QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxRQUFRO0FBQ1I7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7O0FBRU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1QsUUFBUTtBQUNSO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsdUJBQXVCO0FBQ3ZCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRzs7QUFFSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTzs7QUFFUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsVUFBVTtBQUNWO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLDJCQUEyQjtBQUMzQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTs7QUFFQTtBQUNPO0FBQ1AsRUFBRSw4REFBNkI7QUFDL0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDs7Ozs7Ozs7Ozs7Ozs7OztBQzdyQkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNhO0FBQ047QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNB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QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQ3JsQkE7QUFDQSxrQkFBa0IsbUJBQU8sQ0FBQyxrREFBYztBQUN4QyxtQkFBbUIsbUJBQU8sQ0FBQyxvREFBZTs7QUFFMUM7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsdUJBQXVCLFFBQVE7QUFDL0I7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG9DQUFvQztBQUNwQztBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7O0FDN0ZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7OztBQ2pCQTtBQUNBLGVBQWUsbUJBQU8sQ0FBQyx3Q0FBaUI7QUFDeEMsaUJBQWlCLG1CQUFPLENBQUMsMENBQWtCOztBQUUzQyxpQkFBaUIsbUJBQU8sQ0FBQyxnREFBYTtBQUN0QyxrQkFBa0IsbUJBQU8sQ0FBQyxrREFBYztBQUN4QyxtQkFBbUIsbUJBQU8sQ0FBQyxrREFBYzs7QUFFekM7QUFDQTtBQUNBLGNBQWM7QUFDZDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQSxFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsSUFBSTtBQUNKLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0osR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQSxVQUFVO0FBQ1Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnREFBZ0Q7QUFDaEQsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsRUFBRTtBQUNGOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxrQkFBa0I7QUFDbEI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTTtBQUNOO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU07QUFDTjtBQUNBO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0EsSUFBSTtBQUNKO0FBQ0E7QUFDQTtBQUNBLElBQUk7QUFDSixFQUFFO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FB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O0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLElBQUk7QUFDSjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNILEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBLHFCQUFxQjtBQUNyQixHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBLEVBQUU7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxJQUFJO0FBQ0o7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7Ozs7Ozs7Ozs7OztBQ25XQTs7Ozs7Ozs7Ozs7QUNBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztVQ0FBO1VBQ0E7O1VBRUE7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7VUFDQTtVQUNBO1VBQ0E7O1VBRUE7VUFDQTs7VUFFQTtVQUNBO1VBQ0E7Ozs7O1dDdEJBOzs7OztXQ0FBO1dBQ0E7V0FDQTtXQUNBO1dBQ0E7V0FDQSxpQ0FBaUMsV0FBVztXQUM1QztXQUNBOzs7OztXQ1BBO1dBQ0E7V0FDQTtXQUNBO1dBQ0EseUNBQXlDLHdDQUF3QztXQUNqRjtXQUNBO1dBQ0E7Ozs7O1dDUEE7Ozs7O1dDQUE7V0FDQTtXQUNBO1dBQ0EsdURBQXVELGlCQUFpQjtXQUN4RTtXQUNBLGdEQUFnRCxhQUFhO1dBQzdEOzs7Ozs7Ozs7O0FDTkE7QUFDQSxtQkFBTyxDQUFDLDRFQUFnQjs7QUFFeEI7QUFDQTtBQUNBOztBQUVBO0FBQ0EsWUFBWSxtQkFBTyxDQUFDLGtEQUFjO0FBQ2xDLGFBQWEsbUJBQU8sQ0FBQyxnREFBYTtBQUNsQyxlQUFlLG1CQUFPLENBQUMsa0RBQWM7QUFDckMsa0JBQWtCLG1CQUFPLENBQUMsOENBQVk7QUFDdEMsQ0FBQyIsInNvdXJjZXMiOlsid2VicGFjazovL29tLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL2ZyZWVpY2UvaW5kZXguanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvbm9ybWFsaWNlL2luZGV4LmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3NkcC9zZHAuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvdWEtcGFyc2VyLWpzL3NyYy91YS1wYXJzZXIuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2FkYXB0ZXJfY29yZS5qcyIsIndlYnBhY2s6Ly9vbS1mcm9udGVuZC8uL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvYWRhcHRlcl9mYWN0b3J5LmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3dlYnJ0Yy1hZGFwdGVyL3NyYy9qcy9jaHJvbWUvY2hyb21lX3NoaW0uanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2Nocm9tZS9nZXRkaXNwbGF5bWVkaWEuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2Nocm9tZS9nZXR1c2VybWVkaWEuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2NvbW1vbl9zaGltLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3dlYnJ0Yy1hZGFwdGVyL3NyYy9qcy9maXJlZm94L2ZpcmVmb3hfc2hpbS5qcyIsIndlYnBhY2s6Ly9vbS1mcm9udGVuZC8uL25vZGVfbW9kdWxlcy93ZWJydGMtYWRhcHRlci9zcmMvanMvZmlyZWZveC9nZXRkaXNwbGF5bWVkaWEuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL2ZpcmVmb3gvZ2V0dXNlcm1lZGlhLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vbm9kZV9tb2R1bGVzL3dlYnJ0Yy1hZGFwdGVyL3NyYy9qcy9zYWZhcmkvc2FmYXJpX3NoaW0uanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9ub2RlX21vZHVsZXMvd2VicnRjLWFkYXB0ZXIvc3JjL2pzL3V0aWxzLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vc3JjL3NldHRpbmdzL1dlYlJ0Y1BlZXIuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvLi9zcmMvc2V0dGluZ3MvbWljLWxldmVsLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vc3JjL3NldHRpbmdzL3JpbmctYnVmZmVyLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vc3JjL3NldHRpbmdzL3NldHRpbmdzLmpzIiwid2VicGFjazovL29tLWZyb250ZW5kLy4vc3JjL3NldHRpbmdzL3ZpZGVvLXV0aWwuanMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvZXh0ZXJuYWwgdmFyIFwiT21VdGlsXCIiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvZXh0ZXJuYWwgdmFyIFwiU2V0dGluZ3NcIiIsIndlYnBhY2s6Ly9vbS1mcm9udGVuZC93ZWJwYWNrL2Jvb3RzdHJhcCIsIndlYnBhY2s6Ly9vbS1mcm9udGVuZC93ZWJwYWNrL3J1bnRpbWUvYW1kIG9wdGlvbnMiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvd2VicGFjay9ydW50aW1lL2NvbXBhdCBnZXQgZGVmYXVsdCBleHBvcnQiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvd2VicGFjay9ydW50aW1lL2RlZmluZSBwcm9wZXJ0eSBnZXR0ZXJzIiwid2VicGFjazovL29tLWZyb250ZW5kL3dlYnBhY2svcnVudGltZS9oYXNPd25Qcm9wZXJ0eSBzaG9ydGhhbmQiLCJ3ZWJwYWNrOi8vb20tZnJvbnRlbmQvd2VicGFjay9ydW50aW1lL21ha2UgbmFtZXNwYWNlIG9iamVjdCIsIndlYnBhY2s6Ly9vbS1mcm9udGVuZC8uL3NyYy9zZXR0aW5ncy9pbmRleC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBqc2hpbnQgbm9kZTogdHJ1ZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG52YXIgbm9ybWFsaWNlID0gcmVxdWlyZSgnbm9ybWFsaWNlJyk7XG5cbi8qKlxuICAjIGZyZWVpY2VcblxuICBUaGUgYGZyZWVpY2VgIG1vZHVsZSBpcyBhIHNpbXBsZSB3YXkgb2YgZ2V0dGluZyByYW5kb20gU1RVTiBvciBUVVJOIHNlcnZlclxuICBmb3IgeW91ciBXZWJSVEMgYXBwbGljYXRpb24uICBUaGUgbGlzdCBvZiBzZXJ2ZXJzIChqdXN0IFNUVU4gYXQgdGhpcyBzdGFnZSlcbiAgd2VyZSBzb3VyY2VkIGZyb20gdGhpcyBbZ2lzdF0oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20venppdW5pLzM3NDE5MzMpLlxuXG4gICMjIEV4YW1wbGUgVXNlXG5cbiAgVGhlIGZvbGxvd2luZyBkZW1vbnN0cmF0ZXMgaG93IHlvdSBjYW4gdXNlIGBmcmVlaWNlYCB3aXRoXG4gIFtydGMtcXVpY2tjb25uZWN0XShodHRwczovL2dpdGh1Yi5jb20vcnRjLWlvL3J0Yy1xdWlja2Nvbm5lY3QpOlxuXG4gIDw8PCBleGFtcGxlcy9xdWlja2Nvbm5lY3QuanNcblxuICBBcyB0aGUgYGZyZWVpY2VgIG1vZHVsZSBnZW5lcmF0ZXMgaWNlIHNlcnZlcnMgaW4gYSBsaXN0IGNvbXBsaWFudCB3aXRoIHRoZVxuICBXZWJSVEMgc3BlYyB5b3Ugd2lsbCBiZSBhYmxlIHRvIHVzZSBpdCB3aXRoIHJhdyBgUlRDUGVlckNvbm5lY3Rpb25gXG4gIGNvbnN0cnVjdG9ycyBhbmQgb3RoZXIgV2ViUlRDIGxpYnJhcmllcy5cblxuICAjIyBIZXksIGRvbid0IHVzZSBteSBTVFVOL1RVUk4gc2VydmVyIVxuXG4gIElmIGZvciBzb21lIHJlYXNvbiB5b3VyIGZyZWUgU1RVTiBvciBUVVJOIHNlcnZlciBlbmRzIHVwIGluIHRoZVxuICBsaXN0IG9mIHNlcnZlcnMgKFtzdHVuXShodHRwczovL2dpdGh1Yi5jb20vRGFtb25PZWhsbWFuL2ZyZWVpY2UvYmxvYi9tYXN0ZXIvc3R1bi5qc29uKSBvclxuICBbdHVybl0oaHR0cHM6Ly9naXRodWIuY29tL0RhbW9uT2VobG1hbi9mcmVlaWNlL2Jsb2IvbWFzdGVyL3R1cm4uanNvbikpXG4gIHRoYXQgaXMgdXNlZCBpbiB0aGlzIG1vZHVsZSwgeW91IGNhbiBmZWVsXG4gIGZyZWUgdG8gb3BlbiBhbiBpc3N1ZSBvbiB0aGlzIHJlcG9zaXRvcnkgYW5kIHRob3NlIHNlcnZlcnMgd2lsbCBiZSByZW1vdmVkXG4gIHdpdGhpbiAyNCBob3VycyAob3Igc29vbmVyKS4gIFRoaXMgaXMgdGhlIHF1aWNrZXN0IGFuZCBwcm9iYWJseSB0aGUgbW9zdFxuICBwb2xpdGUgd2F5IHRvIGhhdmUgc29tZXRoaW5nIHJlbW92ZWQgKGFuZCBwcm92aWRlcyB1cyBzb21lIHZpc2liaWxpdHlcbiAgaWYgc29tZW9uZSBvcGVucyBhIHB1bGwgcmVxdWVzdCByZXF1ZXN0aW5nIHRoYXQgYSBzZXJ2ZXIgaXMgYWRkZWQpLlxuXG4gICMjIFBsZWFzZSBhZGQgbXkgc2VydmVyIVxuXG4gIElmIHlvdSBoYXZlIGEgc2VydmVyIHRoYXQgeW91IHdpc2ggdG8gYWRkIHRvIHRoZSBsaXN0LCB0aGF0J3MgYXdlc29tZSEgSSdtXG4gIHN1cmUgSSBzcGVhayBvbiBiZWhhbGYgb2YgYSB3aG9sZSBwaWxlIG9mIFdlYlJUQyBkZXZlbG9wZXJzIHdobyBzYXkgdGhhbmtzLlxuICBUbyBnZXQgaXQgaW50byB0aGUgbGlzdCwgZmVlbCBmcmVlIHRvIGVpdGhlciBvcGVuIGEgcHVsbCByZXF1ZXN0IG9yIGlmIHlvdVxuICBmaW5kIHRoYXQgcHJvY2VzcyBhIGJpdCBkYXVudGluZyB0aGVuIGp1c3QgY3JlYXRlIGFuIGlzc3VlIHJlcXVlc3RpbmdcbiAgdGhlIGFkZGl0aW9uIG9mIHRoZSBzZXJ2ZXIgKG1ha2Ugc3VyZSB5b3UgcHJvdmlkZSBhbGwgdGhlIGRldGFpbHMsIGFuZCBpZlxuICB5b3UgaGF2ZSBhIFRlcm1zIG9mIFNlcnZpY2UgdGhlbiBpbmNsdWRpbmcgdGhhdCBpbiB0aGUgUFIvaXNzdWUgd291bGQgYmVcbiAgYXdlc29tZSkuXG5cbiAgIyMgSSBrbm93IG9mIGEgZnJlZSBzZXJ2ZXIsIGNhbiBJIGFkZCBpdD9cblxuICBTdXJlLCBpZiB5b3UgZG8geW91ciBob21ld29yayBhbmQgbWFrZSBzdXJlIGl0IGlzIG9rIHRvIHVzZSAoSSdtIGN1cnJlbnRseVxuICBpbiB0aGUgcHJvY2VzcyBvZiByZXZpZXdpbmcgdGhlIHRlcm1zIG9mIHRob3NlIFNUVU4gc2VydmVycyBpbmNsdWRlZCBmcm9tXG4gIHRoZSBvcmlnaW5hbCBsaXN0KS4gIElmIGl0J3Mgb2sgdG8gZ28sIHRoZW4gcGxlYXNlIHNlZSB0aGUgcHJldmlvdXMgZW50cnlcbiAgZm9yIGhvdyB0byBhZGQgaXQuXG5cbiAgIyMgQ3VycmVudCBMaXN0IG9mIFNlcnZlcnNcblxuICAqIGN1cnJlbnQgYXMgYXQgdGhlIHRpbWUgb2YgbGFzdCBgUkVBRE1FLm1kYCBmaWxlIGdlbmVyYXRpb25cblxuICAjIyMgU1RVTlxuXG4gIDw8PCBzdHVuLmpzb25cblxuICAjIyMgVFVSTlxuXG4gIDw8PCB0dXJuLmpzb25cblxuKiovXG5cbnZhciBmcmVlaWNlID0gZnVuY3Rpb24ob3B0cykge1xuICAvLyBpZiBhIGxpc3Qgb2Ygc2VydmVycyBoYXMgYmVlbiBwcm92aWRlZCwgdGhlbiB1c2UgaXQgaW5zdGVhZCBvZiBkZWZhdWx0c1xuICB2YXIgc2VydmVycyA9IHtcbiAgICBzdHVuOiAob3B0cyB8fCB7fSkuc3R1biB8fCByZXF1aXJlKCcuL3N0dW4uanNvbicpLFxuICAgIHR1cm46IChvcHRzIHx8IHt9KS50dXJuIHx8IHJlcXVpcmUoJy4vdHVybi5qc29uJylcbiAgfTtcblxuICB2YXIgc3R1bkNvdW50ID0gKG9wdHMgfHwge30pLnN0dW5Db3VudCB8fCAyO1xuICB2YXIgdHVybkNvdW50ID0gKG9wdHMgfHwge30pLnR1cm5Db3VudCB8fCAwO1xuICB2YXIgc2VsZWN0ZWQ7XG5cbiAgZnVuY3Rpb24gZ2V0U2VydmVycyh0eXBlLCBjb3VudCkge1xuICAgIHZhciBvdXQgPSBbXTtcbiAgICB2YXIgaW5wdXQgPSBbXS5jb25jYXQoc2VydmVyc1t0eXBlXSk7XG4gICAgdmFyIGlkeDtcblxuICAgIHdoaWxlIChpbnB1dC5sZW5ndGggJiYgb3V0Lmxlbmd0aCA8IGNvdW50KSB7XG4gICAgICBpZHggPSAoTWF0aC5yYW5kb20oKSAqIGlucHV0Lmxlbmd0aCkgfCAwO1xuICAgICAgb3V0ID0gb3V0LmNvbmNhdChpbnB1dC5zcGxpY2UoaWR4LCAxKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIG91dC5tYXAoZnVuY3Rpb24odXJsKSB7XG4gICAgICAgIC8vSWYgaXQncyBhIG5vdCBhIHN0cmluZywgZG9uJ3QgdHJ5IHRvIFwibm9ybWFsaWNlXCIgaXQgb3RoZXJ3aXNlIHVzaW5nIHR5cGU6dXJsIHdpbGwgc2NyZXcgaXQgdXBcbiAgICAgICAgaWYgKCh0eXBlb2YgdXJsICE9PSAnc3RyaW5nJykgJiYgKCEgKHVybCBpbnN0YW5jZW9mIFN0cmluZykpKSB7XG4gICAgICAgICAgICByZXR1cm4gdXJsO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIG5vcm1hbGljZSh0eXBlICsgJzonICsgdXJsKTtcbiAgICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLy8gYWRkIHN0dW4gc2VydmVyc1xuICBzZWxlY3RlZCA9IFtdLmNvbmNhdChnZXRTZXJ2ZXJzKCdzdHVuJywgc3R1bkNvdW50KSk7XG5cbiAgaWYgKHR1cm5Db3VudCkge1xuICAgIHNlbGVjdGVkID0gc2VsZWN0ZWQuY29uY2F0KGdldFNlcnZlcnMoJ3R1cm4nLCB0dXJuQ291bnQpKTtcbiAgfVxuXG4gIHJldHVybiBzZWxlY3RlZDtcbn07XG5cbm1vZHVsZS5leHBvcnRzID0gZnJlZWljZTsiLCIvKipcbiAgIyBub3JtYWxpY2VcblxuICBOb3JtYWxpemUgYW4gaWNlIHNlcnZlciBjb25maWd1cmF0aW9uIG9iamVjdCAob3IgcGxhaW4gb2xkIHN0cmluZykgaW50byBhIGZvcm1hdFxuICB0aGF0IGlzIHVzYWJsZSBpbiBhbGwgYnJvd3NlcnMgc3VwcG9ydGluZyBXZWJSVEMuICBQcmltYXJpbHkgdGhpcyBtb2R1bGUgaXMgZGVzaWduZWRcbiAgdG8gaGVscCB3aXRoIHRoZSB0cmFuc2l0aW9uIG9mIHRoZSBgdXJsYCBhdHRyaWJ1dGUgb2YgdGhlIGNvbmZpZ3VyYXRpb24gb2JqZWN0IHRvXG4gIHRoZSBgdXJsc2AgYXR0cmlidXRlLlxuXG4gICMjIEV4YW1wbGUgVXNhZ2VcblxuICA8PDwgZXhhbXBsZXMvc2ltcGxlLmpzXG5cbioqL1xuXG52YXIgcHJvdG9jb2xzID0gW1xuICAnc3R1bjonLFxuICAndHVybjonXG5dO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKGlucHV0KSB7XG4gIHZhciB1cmwgPSAoaW5wdXQgfHwge30pLnVybCB8fCBpbnB1dDtcbiAgdmFyIHByb3RvY29sO1xuICB2YXIgcGFydHM7XG4gIHZhciBvdXRwdXQgPSB7fTtcblxuICAvLyBpZiB3ZSBkb24ndCBoYXZlIGEgc3RyaW5nIHVybCwgdGhlbiBhbGxvdyB0aGUgaW5wdXQgdG8gcGFzc3Rocm91Z2hcbiAgaWYgKHR5cGVvZiB1cmwgIT0gJ3N0cmluZycgJiYgKCEgKHVybCBpbnN0YW5jZW9mIFN0cmluZykpKSB7XG4gICAgcmV0dXJuIGlucHV0O1xuICB9XG5cbiAgLy8gdHJpbSB0aGUgdXJsIHN0cmluZywgYW5kIGNvbnZlcnQgdG8gYW4gYXJyYXlcbiAgdXJsID0gdXJsLnRyaW0oKTtcblxuICAvLyBpZiB0aGUgcHJvdG9jb2wgaXMgbm90IGtub3duLCB0aGVuIHBhc3N0aHJvdWdoXG4gIHByb3RvY29sID0gcHJvdG9jb2xzW3Byb3RvY29scy5pbmRleE9mKHVybC5zbGljZSgwLCA1KSldO1xuICBpZiAoISBwcm90b2NvbCkge1xuICAgIHJldHVybiBpbnB1dDtcbiAgfVxuXG4gIC8vIG5vdyBsZXQncyBhdHRhY2sgdGhlIHJlbWFpbmluZyB1cmwgcGFydHNcbiAgdXJsID0gdXJsLnNsaWNlKDUpO1xuICBwYXJ0cyA9IHVybC5zcGxpdCgnQCcpO1xuXG4gIG91dHB1dC51c2VybmFtZSA9IGlucHV0LnVzZXJuYW1lO1xuICBvdXRwdXQuY3JlZGVudGlhbCA9IGlucHV0LmNyZWRlbnRpYWw7XG4gIC8vIGlmIHdlIGhhdmUgYW4gYXV0aGVudGljYXRpb24gcGFydCwgdGhlbiBzZXQgdGhlIGNyZWRlbnRpYWxzXG4gIGlmIChwYXJ0cy5sZW5ndGggPiAxKSB7XG4gICAgdXJsID0gcGFydHNbMV07XG4gICAgcGFydHMgPSBwYXJ0c1swXS5zcGxpdCgnOicpO1xuXG4gICAgLy8gYWRkIHRoZSBvdXRwdXQgY3JlZGVudGlhbCBhbmQgdXNlcm5hbWVcbiAgICBvdXRwdXQudXNlcm5hbWUgPSBwYXJ0c1swXTtcbiAgICBvdXRwdXQuY3JlZGVudGlhbCA9IChpbnB1dCB8fCB7fSkuY3JlZGVudGlhbCB8fCBwYXJ0c1sxXSB8fCAnJztcbiAgfVxuXG4gIG91dHB1dC51cmwgPSBwcm90b2NvbCArIHVybDtcbiAgb3V0cHV0LnVybHMgPSBbIG91dHB1dC51cmwgXTtcblxuICByZXR1cm4gb3V0cHV0O1xufTtcbiIsIi8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG4vLyBTRFAgaGVscGVycy5cbmNvbnN0IFNEUFV0aWxzID0ge307XG5cbi8vIEdlbmVyYXRlIGFuIGFscGhhbnVtZXJpYyBpZGVudGlmaWVyIGZvciBjbmFtZSBvciBtaWRzLlxuLy8gVE9ETzogdXNlIFVVSURzIGluc3RlYWQ/IGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL2plZC85ODI4ODNcblNEUFV0aWxzLmdlbmVyYXRlSWRlbnRpZmllciA9IGZ1bmN0aW9uKCkge1xuICByZXR1cm4gTWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyaW5nKDIsIDEyKTtcbn07XG5cbi8vIFRoZSBSVENQIENOQU1FIHVzZWQgYnkgYWxsIHBlZXJjb25uZWN0aW9ucyBmcm9tIHRoZSBzYW1lIEpTLlxuU0RQVXRpbHMubG9jYWxDTmFtZSA9IFNEUFV0aWxzLmdlbmVyYXRlSWRlbnRpZmllcigpO1xuXG4vLyBTcGxpdHMgU0RQIGludG8gbGluZXMsIGRlYWxpbmcgd2l0aCBib3RoIENSTEYgYW5kIExGLlxuU0RQVXRpbHMuc3BsaXRMaW5lcyA9IGZ1bmN0aW9uKGJsb2IpIHtcbiAgcmV0dXJuIGJsb2IudHJpbSgpLnNwbGl0KCdcXG4nKS5tYXAobGluZSA9PiBsaW5lLnRyaW0oKSk7XG59O1xuLy8gU3BsaXRzIFNEUCBpbnRvIHNlc3Npb25wYXJ0IGFuZCBtZWRpYXNlY3Rpb25zLiBFbnN1cmVzIENSTEYuXG5TRFBVdGlscy5zcGxpdFNlY3Rpb25zID0gZnVuY3Rpb24oYmxvYikge1xuICBjb25zdCBwYXJ0cyA9IGJsb2Iuc3BsaXQoJ1xcbm09Jyk7XG4gIHJldHVybiBwYXJ0cy5tYXAoKHBhcnQsIGluZGV4KSA9PiAoaW5kZXggPiAwID9cbiAgICAnbT0nICsgcGFydCA6IHBhcnQpLnRyaW0oKSArICdcXHJcXG4nKTtcbn07XG5cbi8vIFJldHVybnMgdGhlIHNlc3Npb24gZGVzY3JpcHRpb24uXG5TRFBVdGlscy5nZXREZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKGJsb2IpIHtcbiAgY29uc3Qgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGJsb2IpO1xuICByZXR1cm4gc2VjdGlvbnMgJiYgc2VjdGlvbnNbMF07XG59O1xuXG4vLyBSZXR1cm5zIHRoZSBpbmRpdmlkdWFsIG1lZGlhIHNlY3Rpb25zLlxuU0RQVXRpbHMuZ2V0TWVkaWFTZWN0aW9ucyA9IGZ1bmN0aW9uKGJsb2IpIHtcbiAgY29uc3Qgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGJsb2IpO1xuICBzZWN0aW9ucy5zaGlmdCgpO1xuICByZXR1cm4gc2VjdGlvbnM7XG59O1xuXG4vLyBSZXR1cm5zIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCBhIGNlcnRhaW4gcHJlZml4LlxuU0RQVXRpbHMubWF0Y2hQcmVmaXggPSBmdW5jdGlvbihibG9iLCBwcmVmaXgpIHtcbiAgcmV0dXJuIFNEUFV0aWxzLnNwbGl0TGluZXMoYmxvYikuZmlsdGVyKGxpbmUgPT4gbGluZS5pbmRleE9mKHByZWZpeCkgPT09IDApO1xufTtcblxuLy8gUGFyc2VzIGFuIElDRSBjYW5kaWRhdGUgbGluZS4gU2FtcGxlIGlucHV0OlxuLy8gY2FuZGlkYXRlOjcwMjc4NjM1MCAyIHVkcCA0MTgxOTkwMiA4LjguOC44IDYwNzY5IHR5cCByZWxheSByYWRkciA4LjguOC44XG4vLyBycG9ydCA1NTk5NlwiXG4vLyBJbnB1dCBjYW4gYmUgcHJlZml4ZWQgd2l0aCBhPS5cblNEUFV0aWxzLnBhcnNlQ2FuZGlkYXRlID0gZnVuY3Rpb24obGluZSkge1xuICBsZXQgcGFydHM7XG4gIC8vIFBhcnNlIGJvdGggdmFyaWFudHMuXG4gIGlmIChsaW5lLmluZGV4T2YoJ2E9Y2FuZGlkYXRlOicpID09PSAwKSB7XG4gICAgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMikuc3BsaXQoJyAnKTtcbiAgfSBlbHNlIHtcbiAgICBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDEwKS5zcGxpdCgnICcpO1xuICB9XG5cbiAgY29uc3QgY2FuZGlkYXRlID0ge1xuICAgIGZvdW5kYXRpb246IHBhcnRzWzBdLFxuICAgIGNvbXBvbmVudDogezE6ICdydHAnLCAyOiAncnRjcCd9W3BhcnRzWzFdXSB8fCBwYXJ0c1sxXSxcbiAgICBwcm90b2NvbDogcGFydHNbMl0udG9Mb3dlckNhc2UoKSxcbiAgICBwcmlvcml0eTogcGFyc2VJbnQocGFydHNbM10sIDEwKSxcbiAgICBpcDogcGFydHNbNF0sXG4gICAgYWRkcmVzczogcGFydHNbNF0sIC8vIGFkZHJlc3MgaXMgYW4gYWxpYXMgZm9yIGlwLlxuICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzVdLCAxMCksXG4gICAgLy8gc2tpcCBwYXJ0c1s2XSA9PSAndHlwJ1xuICAgIHR5cGU6IHBhcnRzWzddLFxuICB9O1xuXG4gIGZvciAobGV0IGkgPSA4OyBpIDwgcGFydHMubGVuZ3RoOyBpICs9IDIpIHtcbiAgICBzd2l0Y2ggKHBhcnRzW2ldKSB7XG4gICAgICBjYXNlICdyYWRkcic6XG4gICAgICAgIGNhbmRpZGF0ZS5yZWxhdGVkQWRkcmVzcyA9IHBhcnRzW2kgKyAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdycG9ydCc6XG4gICAgICAgIGNhbmRpZGF0ZS5yZWxhdGVkUG9ydCA9IHBhcnNlSW50KHBhcnRzW2kgKyAxXSwgMTApO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ3RjcHR5cGUnOlxuICAgICAgICBjYW5kaWRhdGUudGNwVHlwZSA9IHBhcnRzW2kgKyAxXTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd1ZnJhZyc6XG4gICAgICAgIGNhbmRpZGF0ZS51ZnJhZyA9IHBhcnRzW2kgKyAxXTsgLy8gZm9yIGJhY2t3YXJkIGNvbXBhdGliaWxpdHkuXG4gICAgICAgIGNhbmRpZGF0ZS51c2VybmFtZUZyYWdtZW50ID0gcGFydHNbaSArIDFdO1xuICAgICAgICBicmVhaztcbiAgICAgIGRlZmF1bHQ6IC8vIGV4dGVuc2lvbiBoYW5kbGluZywgaW4gcGFydGljdWxhciB1ZnJhZy4gRG9uJ3Qgb3ZlcndyaXRlLlxuICAgICAgICBpZiAoY2FuZGlkYXRlW3BhcnRzW2ldXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2FuZGlkYXRlW3BhcnRzW2ldXSA9IHBhcnRzW2kgKyAxXTtcbiAgICAgICAgfVxuICAgICAgICBicmVhaztcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGNhbmRpZGF0ZTtcbn07XG5cbi8vIFRyYW5zbGF0ZXMgYSBjYW5kaWRhdGUgb2JqZWN0IGludG8gU0RQIGNhbmRpZGF0ZSBhdHRyaWJ1dGUuXG4vLyBUaGlzIGRvZXMgbm90IGluY2x1ZGUgdGhlIGE9IHByZWZpeCFcblNEUFV0aWxzLndyaXRlQ2FuZGlkYXRlID0gZnVuY3Rpb24oY2FuZGlkYXRlKSB7XG4gIGNvbnN0IHNkcCA9IFtdO1xuICBzZHAucHVzaChjYW5kaWRhdGUuZm91bmRhdGlvbik7XG5cbiAgY29uc3QgY29tcG9uZW50ID0gY2FuZGlkYXRlLmNvbXBvbmVudDtcbiAgaWYgKGNvbXBvbmVudCA9PT0gJ3J0cCcpIHtcbiAgICBzZHAucHVzaCgxKTtcbiAgfSBlbHNlIGlmIChjb21wb25lbnQgPT09ICdydGNwJykge1xuICAgIHNkcC5wdXNoKDIpO1xuICB9IGVsc2Uge1xuICAgIHNkcC5wdXNoKGNvbXBvbmVudCk7XG4gIH1cbiAgc2RwLnB1c2goY2FuZGlkYXRlLnByb3RvY29sLnRvVXBwZXJDYXNlKCkpO1xuICBzZHAucHVzaChjYW5kaWRhdGUucHJpb3JpdHkpO1xuICBzZHAucHVzaChjYW5kaWRhdGUuYWRkcmVzcyB8fCBjYW5kaWRhdGUuaXApO1xuICBzZHAucHVzaChjYW5kaWRhdGUucG9ydCk7XG5cbiAgY29uc3QgdHlwZSA9IGNhbmRpZGF0ZS50eXBlO1xuICBzZHAucHVzaCgndHlwJyk7XG4gIHNkcC5wdXNoKHR5cGUpO1xuICBpZiAodHlwZSAhPT0gJ2hvc3QnICYmIGNhbmRpZGF0ZS5yZWxhdGVkQWRkcmVzcyAmJlxuICAgICAgY2FuZGlkYXRlLnJlbGF0ZWRQb3J0KSB7XG4gICAgc2RwLnB1c2goJ3JhZGRyJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnJlbGF0ZWRBZGRyZXNzKTtcbiAgICBzZHAucHVzaCgncnBvcnQnKTtcbiAgICBzZHAucHVzaChjYW5kaWRhdGUucmVsYXRlZFBvcnQpO1xuICB9XG4gIGlmIChjYW5kaWRhdGUudGNwVHlwZSAmJiBjYW5kaWRhdGUucHJvdG9jb2wudG9Mb3dlckNhc2UoKSA9PT0gJ3RjcCcpIHtcbiAgICBzZHAucHVzaCgndGNwdHlwZScpO1xuICAgIHNkcC5wdXNoKGNhbmRpZGF0ZS50Y3BUeXBlKTtcbiAgfVxuICBpZiAoY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgfHwgY2FuZGlkYXRlLnVmcmFnKSB7XG4gICAgc2RwLnB1c2goJ3VmcmFnJyk7XG4gICAgc2RwLnB1c2goY2FuZGlkYXRlLnVzZXJuYW1lRnJhZ21lbnQgfHwgY2FuZGlkYXRlLnVmcmFnKTtcbiAgfVxuICByZXR1cm4gJ2NhbmRpZGF0ZTonICsgc2RwLmpvaW4oJyAnKTtcbn07XG5cbi8vIFBhcnNlcyBhbiBpY2Utb3B0aW9ucyBsaW5lLCByZXR1cm5zIGFuIGFycmF5IG9mIG9wdGlvbiB0YWdzLlxuLy8gU2FtcGxlIGlucHV0OlxuLy8gYT1pY2Utb3B0aW9uczpmb28gYmFyXG5TRFBVdGlscy5wYXJzZUljZU9wdGlvbnMgPSBmdW5jdGlvbihsaW5lKSB7XG4gIHJldHVybiBsaW5lLnN1YnN0cmluZygxNCkuc3BsaXQoJyAnKTtcbn07XG5cbi8vIFBhcnNlcyBhIHJ0cG1hcCBsaW5lLCByZXR1cm5zIFJUQ1J0cENvZGRlY1BhcmFtZXRlcnMuIFNhbXBsZSBpbnB1dDpcbi8vIGE9cnRwbWFwOjExMSBvcHVzLzQ4MDAwLzJcblNEUFV0aWxzLnBhcnNlUnRwTWFwID0gZnVuY3Rpb24obGluZSkge1xuICBsZXQgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpO1xuICBjb25zdCBwYXJzZWQgPSB7XG4gICAgcGF5bG9hZFR5cGU6IHBhcnNlSW50KHBhcnRzLnNoaWZ0KCksIDEwKSwgLy8gd2FzOiBpZFxuICB9O1xuXG4gIHBhcnRzID0gcGFydHNbMF0uc3BsaXQoJy8nKTtcblxuICBwYXJzZWQubmFtZSA9IHBhcnRzWzBdO1xuICBwYXJzZWQuY2xvY2tSYXRlID0gcGFyc2VJbnQocGFydHNbMV0sIDEwKTsgLy8gd2FzOiBjbG9ja3JhdGVcbiAgcGFyc2VkLmNoYW5uZWxzID0gcGFydHMubGVuZ3RoID09PSAzID8gcGFyc2VJbnQocGFydHNbMl0sIDEwKSA6IDE7XG4gIC8vIGxlZ2FjeSBhbGlhcywgZ290IHJlbmFtZWQgYmFjayB0byBjaGFubmVscyBpbiBPUlRDLlxuICBwYXJzZWQubnVtQ2hhbm5lbHMgPSBwYXJzZWQuY2hhbm5lbHM7XG4gIHJldHVybiBwYXJzZWQ7XG59O1xuXG4vLyBHZW5lcmF0ZXMgYSBydHBtYXAgbGluZSBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvclxuLy8gUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdHBNYXAgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGNvbnN0IGNoYW5uZWxzID0gY29kZWMuY2hhbm5lbHMgfHwgY29kZWMubnVtQ2hhbm5lbHMgfHwgMTtcbiAgcmV0dXJuICdhPXJ0cG1hcDonICsgcHQgKyAnICcgKyBjb2RlYy5uYW1lICsgJy8nICsgY29kZWMuY2xvY2tSYXRlICtcbiAgICAgIChjaGFubmVscyAhPT0gMSA/ICcvJyArIGNoYW5uZWxzIDogJycpICsgJ1xcclxcbic7XG59O1xuXG4vLyBQYXJzZXMgYSBleHRtYXAgbGluZSAoaGVhZGVyZXh0ZW5zaW9uIGZyb20gUkZDIDUyODUpLiBTYW1wbGUgaW5wdXQ6XG4vLyBhPWV4dG1hcDoyIHVybjppZXRmOnBhcmFtczpydHAtaGRyZXh0OnRvZmZzZXRcbi8vIGE9ZXh0bWFwOjIvc2VuZG9ubHkgdXJuOmlldGY6cGFyYW1zOnJ0cC1oZHJleHQ6dG9mZnNldFxuU0RQVXRpbHMucGFyc2VFeHRtYXAgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHJpbmcoOSkuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBpZDogcGFyc2VJbnQocGFydHNbMF0sIDEwKSxcbiAgICBkaXJlY3Rpb246IHBhcnRzWzBdLmluZGV4T2YoJy8nKSA+IDAgPyBwYXJ0c1swXS5zcGxpdCgnLycpWzFdIDogJ3NlbmRyZWN2JyxcbiAgICB1cmk6IHBhcnRzWzFdLFxuICAgIGF0dHJpYnV0ZXM6IHBhcnRzLnNsaWNlKDIpLmpvaW4oJyAnKSxcbiAgfTtcbn07XG5cbi8vIEdlbmVyYXRlcyBhbiBleHRtYXAgbGluZSBmcm9tIFJUQ1J0cEhlYWRlckV4dGVuc2lvblBhcmFtZXRlcnMgb3Jcbi8vIFJUQ1J0cEhlYWRlckV4dGVuc2lvbi5cblNEUFV0aWxzLndyaXRlRXh0bWFwID0gZnVuY3Rpb24oaGVhZGVyRXh0ZW5zaW9uKSB7XG4gIHJldHVybiAnYT1leHRtYXA6JyArIChoZWFkZXJFeHRlbnNpb24uaWQgfHwgaGVhZGVyRXh0ZW5zaW9uLnByZWZlcnJlZElkKSArXG4gICAgICAoaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvbiAmJiBoZWFkZXJFeHRlbnNpb24uZGlyZWN0aW9uICE9PSAnc2VuZHJlY3YnXG4gICAgICAgID8gJy8nICsgaGVhZGVyRXh0ZW5zaW9uLmRpcmVjdGlvblxuICAgICAgICA6ICcnKSArXG4gICAgICAnICcgKyBoZWFkZXJFeHRlbnNpb24udXJpICtcbiAgICAgIChoZWFkZXJFeHRlbnNpb24uYXR0cmlidXRlcyA/ICcgJyArIGhlYWRlckV4dGVuc2lvbi5hdHRyaWJ1dGVzIDogJycpICtcbiAgICAgICdcXHJcXG4nO1xufTtcblxuLy8gUGFyc2VzIGEgZm10cCBsaW5lLCByZXR1cm5zIGRpY3Rpb25hcnkuIFNhbXBsZSBpbnB1dDpcbi8vIGE9Zm10cDo5NiB2YnI9b247Y25nPW9uXG4vLyBBbHNvIGRlYWxzIHdpdGggdmJyPW9uOyBjbmc9b25cblNEUFV0aWxzLnBhcnNlRm10cCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFyc2VkID0ge307XG4gIGxldCBrdjtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZyhsaW5lLmluZGV4T2YoJyAnKSArIDEpLnNwbGl0KCc7Jyk7XG4gIGZvciAobGV0IGogPSAwOyBqIDwgcGFydHMubGVuZ3RoOyBqKyspIHtcbiAgICBrdiA9IHBhcnRzW2pdLnRyaW0oKS5zcGxpdCgnPScpO1xuICAgIHBhcnNlZFtrdlswXS50cmltKCldID0ga3ZbMV07XG4gIH1cbiAgcmV0dXJuIHBhcnNlZDtcbn07XG5cbi8vIEdlbmVyYXRlcyBhIGZtdHAgbGluZSBmcm9tIFJUQ1J0cENvZGVjQ2FwYWJpbGl0eSBvciBSVENSdHBDb2RlY1BhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZUZtdHAgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZSA9ICcnO1xuICBsZXQgcHQgPSBjb2RlYy5wYXlsb2FkVHlwZTtcbiAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICBwdCA9IGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlO1xuICB9XG4gIGlmIChjb2RlYy5wYXJhbWV0ZXJzICYmIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmxlbmd0aCkge1xuICAgIGNvbnN0IHBhcmFtcyA9IFtdO1xuICAgIE9iamVjdC5rZXlzKGNvZGVjLnBhcmFtZXRlcnMpLmZvckVhY2gocGFyYW0gPT4ge1xuICAgICAgaWYgKGNvZGVjLnBhcmFtZXRlcnNbcGFyYW1dICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgcGFyYW1zLnB1c2gocGFyYW0gKyAnPScgKyBjb2RlYy5wYXJhbWV0ZXJzW3BhcmFtXSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBwYXJhbXMucHVzaChwYXJhbSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgbGluZSArPSAnYT1mbXRwOicgKyBwdCArICcgJyArIHBhcmFtcy5qb2luKCc7JykgKyAnXFxyXFxuJztcbiAgfVxuICByZXR1cm4gbGluZTtcbn07XG5cbi8vIFBhcnNlcyBhIHJ0Y3AtZmIgbGluZSwgcmV0dXJucyBSVENQUnRjcEZlZWRiYWNrIG9iamVjdC4gU2FtcGxlIGlucHV0OlxuLy8gYT1ydGNwLWZiOjk4IG5hY2sgcnBzaVxuU0RQVXRpbHMucGFyc2VSdGNwRmIgPSBmdW5jdGlvbihsaW5lKSB7XG4gIGNvbnN0IHBhcnRzID0gbGluZS5zdWJzdHJpbmcobGluZS5pbmRleE9mKCcgJykgKyAxKS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHR5cGU6IHBhcnRzLnNoaWZ0KCksXG4gICAgcGFyYW1ldGVyOiBwYXJ0cy5qb2luKCcgJyksXG4gIH07XG59O1xuXG4vLyBHZW5lcmF0ZSBhPXJ0Y3AtZmIgbGluZXMgZnJvbSBSVENSdHBDb2RlY0NhcGFiaWxpdHkgb3IgUlRDUnRwQ29kZWNQYXJhbWV0ZXJzLlxuU0RQVXRpbHMud3JpdGVSdGNwRmIgPSBmdW5jdGlvbihjb2RlYykge1xuICBsZXQgbGluZXMgPSAnJztcbiAgbGV0IHB0ID0gY29kZWMucGF5bG9hZFR5cGU7XG4gIGlmIChjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcHQgPSBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgfVxuICBpZiAoY29kZWMucnRjcEZlZWRiYWNrICYmIGNvZGVjLnJ0Y3BGZWVkYmFjay5sZW5ndGgpIHtcbiAgICAvLyBGSVhNRTogc3BlY2lhbCBoYW5kbGluZyBmb3IgdHJyLWludD9cbiAgICBjb2RlYy5ydGNwRmVlZGJhY2suZm9yRWFjaChmYiA9PiB7XG4gICAgICBsaW5lcyArPSAnYT1ydGNwLWZiOicgKyBwdCArICcgJyArIGZiLnR5cGUgK1xuICAgICAgKGZiLnBhcmFtZXRlciAmJiBmYi5wYXJhbWV0ZXIubGVuZ3RoID8gJyAnICsgZmIucGFyYW1ldGVyIDogJycpICtcbiAgICAgICAgICAnXFxyXFxuJztcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gbGluZXM7XG59O1xuXG4vLyBQYXJzZXMgYSBSRkMgNTU3NiBzc3JjIG1lZGlhIGF0dHJpYnV0ZS4gU2FtcGxlIGlucHV0OlxuLy8gYT1zc3JjOjM3MzU5Mjg1NTkgY25hbWU6c29tZXRoaW5nXG5TRFBVdGlscy5wYXJzZVNzcmNNZWRpYSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3Qgc3AgPSBsaW5lLmluZGV4T2YoJyAnKTtcbiAgY29uc3QgcGFydHMgPSB7XG4gICAgc3NyYzogcGFyc2VJbnQobGluZS5zdWJzdHJpbmcoNywgc3ApLCAxMCksXG4gIH07XG4gIGNvbnN0IGNvbG9uID0gbGluZS5pbmRleE9mKCc6Jywgc3ApO1xuICBpZiAoY29sb24gPiAtMSkge1xuICAgIHBhcnRzLmF0dHJpYnV0ZSA9IGxpbmUuc3Vic3RyaW5nKHNwICsgMSwgY29sb24pO1xuICAgIHBhcnRzLnZhbHVlID0gbGluZS5zdWJzdHJpbmcoY29sb24gKyAxKTtcbiAgfSBlbHNlIHtcbiAgICBwYXJ0cy5hdHRyaWJ1dGUgPSBsaW5lLnN1YnN0cmluZyhzcCArIDEpO1xuICB9XG4gIHJldHVybiBwYXJ0cztcbn07XG5cbi8vIFBhcnNlIGEgc3NyYy1ncm91cCBsaW5lIChzZWUgUkZDIDU1NzYpLiBTYW1wbGUgaW5wdXQ6XG4vLyBhPXNzcmMtZ3JvdXA6c2VtYW50aWNzIDEyIDM0XG5TRFBVdGlscy5wYXJzZVNzcmNHcm91cCA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxMykuc3BsaXQoJyAnKTtcbiAgcmV0dXJuIHtcbiAgICBzZW1hbnRpY3M6IHBhcnRzLnNoaWZ0KCksXG4gICAgc3NyY3M6IHBhcnRzLm1hcChzc3JjID0+IHBhcnNlSW50KHNzcmMsIDEwKSksXG4gIH07XG59O1xuXG4vLyBFeHRyYWN0cyB0aGUgTUlEIChSRkMgNTg4OCkgZnJvbSBhIG1lZGlhIHNlY3Rpb24uXG4vLyBSZXR1cm5zIHRoZSBNSUQgb3IgdW5kZWZpbmVkIGlmIG5vIG1pZCBsaW5lIHdhcyBmb3VuZC5cblNEUFV0aWxzLmdldE1pZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBtaWQgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1pZDonKVswXTtcbiAgaWYgKG1pZCkge1xuICAgIHJldHVybiBtaWQuc3Vic3RyaW5nKDYpO1xuICB9XG59O1xuXG4vLyBQYXJzZXMgYSBmaW5nZXJwcmludCBsaW5lIGZvciBEVExTLVNSVFAuXG5TRFBVdGlscy5wYXJzZUZpbmdlcnByaW50ID0gZnVuY3Rpb24obGluZSkge1xuICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDE0KS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIGFsZ29yaXRobTogcGFydHNbMF0udG9Mb3dlckNhc2UoKSwgLy8gYWxnb3JpdGhtIGlzIGNhc2Utc2Vuc2l0aXZlIGluIEVkZ2UuXG4gICAgdmFsdWU6IHBhcnRzWzFdLnRvVXBwZXJDYXNlKCksIC8vIHRoZSBkZWZpbml0aW9uIGlzIHVwcGVyLWNhc2UgaW4gUkZDIDQ1NzIuXG4gIH07XG59O1xuXG4vLyBFeHRyYWN0cyBEVExTIHBhcmFtZXRlcnMgZnJvbSBTRFAgbWVkaWEgc2VjdGlvbiBvciBzZXNzaW9ucGFydC5cbi8vIEZJWE1FOiBmb3IgY29uc2lzdGVuY3kgd2l0aCBvdGhlciBmdW5jdGlvbnMgdGhpcyBzaG91bGQgb25seVxuLy8gICBnZXQgdGhlIGZpbmdlcnByaW50IGxpbmUgYXMgaW5wdXQuIFNlZSBhbHNvIGdldEljZVBhcmFtZXRlcnMuXG5TRFBVdGlscy5nZXREdGxzUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1maW5nZXJwcmludDonKTtcbiAgLy8gTm90ZTogYT1zZXR1cCBsaW5lIGlzIGlnbm9yZWQgc2luY2Ugd2UgdXNlIHRoZSAnYXV0bycgcm9sZSBpbiBFZGdlLlxuICByZXR1cm4ge1xuICAgIHJvbGU6ICdhdXRvJyxcbiAgICBmaW5nZXJwcmludHM6IGxpbmVzLm1hcChTRFBVdGlscy5wYXJzZUZpbmdlcnByaW50KSxcbiAgfTtcbn07XG5cbi8vIFNlcmlhbGl6ZXMgRFRMUyBwYXJhbWV0ZXJzIHRvIFNEUC5cblNEUFV0aWxzLndyaXRlRHRsc1BhcmFtZXRlcnMgPSBmdW5jdGlvbihwYXJhbXMsIHNldHVwVHlwZSkge1xuICBsZXQgc2RwID0gJ2E9c2V0dXA6JyArIHNldHVwVHlwZSArICdcXHJcXG4nO1xuICBwYXJhbXMuZmluZ2VycHJpbnRzLmZvckVhY2goZnAgPT4ge1xuICAgIHNkcCArPSAnYT1maW5nZXJwcmludDonICsgZnAuYWxnb3JpdGhtICsgJyAnICsgZnAudmFsdWUgKyAnXFxyXFxuJztcbiAgfSk7XG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBQYXJzZXMgYT1jcnlwdG8gbGluZXMgaW50b1xuLy8gICBodHRwczovL3Jhd2dpdC5jb20vYWJvYmEvZWRnZXJ0Yy9tYXN0ZXIvbXNvcnRjLXJzNC5odG1sI2RpY3Rpb25hcnktcnRjc3J0cHNkZXNwYXJhbWV0ZXJzLW1lbWJlcnNcblNEUFV0aWxzLnBhcnNlQ3J5cHRvTGluZSA9IGZ1bmN0aW9uKGxpbmUpIHtcbiAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZyg5KS5zcGxpdCgnICcpO1xuICByZXR1cm4ge1xuICAgIHRhZzogcGFyc2VJbnQocGFydHNbMF0sIDEwKSxcbiAgICBjcnlwdG9TdWl0ZTogcGFydHNbMV0sXG4gICAga2V5UGFyYW1zOiBwYXJ0c1syXSxcbiAgICBzZXNzaW9uUGFyYW1zOiBwYXJ0cy5zbGljZSgzKSxcbiAgfTtcbn07XG5cblNEUFV0aWxzLndyaXRlQ3J5cHRvTGluZSA9IGZ1bmN0aW9uKHBhcmFtZXRlcnMpIHtcbiAgcmV0dXJuICdhPWNyeXB0bzonICsgcGFyYW1ldGVycy50YWcgKyAnICcgK1xuICAgIHBhcmFtZXRlcnMuY3J5cHRvU3VpdGUgKyAnICcgK1xuICAgICh0eXBlb2YgcGFyYW1ldGVycy5rZXlQYXJhbXMgPT09ICdvYmplY3QnXG4gICAgICA/IFNEUFV0aWxzLndyaXRlQ3J5cHRvS2V5UGFyYW1zKHBhcmFtZXRlcnMua2V5UGFyYW1zKVxuICAgICAgOiBwYXJhbWV0ZXJzLmtleVBhcmFtcykgK1xuICAgIChwYXJhbWV0ZXJzLnNlc3Npb25QYXJhbXMgPyAnICcgKyBwYXJhbWV0ZXJzLnNlc3Npb25QYXJhbXMuam9pbignICcpIDogJycpICtcbiAgICAnXFxyXFxuJztcbn07XG5cbi8vIFBhcnNlcyB0aGUgY3J5cHRvIGtleSBwYXJhbWV0ZXJzIGludG9cbi8vICAgaHR0cHM6Ly9yYXdnaXQuY29tL2Fib2JhL2VkZ2VydGMvbWFzdGVyL21zb3J0Yy1yczQuaHRtbCNydGNzcnRwa2V5cGFyYW0qXG5TRFBVdGlscy5wYXJzZUNyeXB0b0tleVBhcmFtcyA9IGZ1bmN0aW9uKGtleVBhcmFtcykge1xuICBpZiAoa2V5UGFyYW1zLmluZGV4T2YoJ2lubGluZTonKSAhPT0gMCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGNvbnN0IHBhcnRzID0ga2V5UGFyYW1zLnN1YnN0cmluZyg3KS5zcGxpdCgnfCcpO1xuICByZXR1cm4ge1xuICAgIGtleU1ldGhvZDogJ2lubGluZScsXG4gICAga2V5U2FsdDogcGFydHNbMF0sXG4gICAgbGlmZVRpbWU6IHBhcnRzWzFdLFxuICAgIG1raVZhbHVlOiBwYXJ0c1syXSA/IHBhcnRzWzJdLnNwbGl0KCc6JylbMF0gOiB1bmRlZmluZWQsXG4gICAgbWtpTGVuZ3RoOiBwYXJ0c1syXSA/IHBhcnRzWzJdLnNwbGl0KCc6JylbMV0gOiB1bmRlZmluZWQsXG4gIH07XG59O1xuXG5TRFBVdGlscy53cml0ZUNyeXB0b0tleVBhcmFtcyA9IGZ1bmN0aW9uKGtleVBhcmFtcykge1xuICByZXR1cm4ga2V5UGFyYW1zLmtleU1ldGhvZCArICc6J1xuICAgICsga2V5UGFyYW1zLmtleVNhbHQgK1xuICAgIChrZXlQYXJhbXMubGlmZVRpbWUgPyAnfCcgKyBrZXlQYXJhbXMubGlmZVRpbWUgOiAnJykgK1xuICAgIChrZXlQYXJhbXMubWtpVmFsdWUgJiYga2V5UGFyYW1zLm1raUxlbmd0aFxuICAgICAgPyAnfCcgKyBrZXlQYXJhbXMubWtpVmFsdWUgKyAnOicgKyBrZXlQYXJhbXMubWtpTGVuZ3RoXG4gICAgICA6ICcnKTtcbn07XG5cbi8vIEV4dHJhY3RzIGFsbCBTREVTIHBhcmFtZXRlcnMuXG5TRFBVdGlscy5nZXRDcnlwdG9QYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uLCBzZXNzaW9ucGFydCkge1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiArIHNlc3Npb25wYXJ0LFxuICAgICdhPWNyeXB0bzonKTtcbiAgcmV0dXJuIGxpbmVzLm1hcChTRFBVdGlscy5wYXJzZUNyeXB0b0xpbmUpO1xufTtcblxuLy8gUGFyc2VzIElDRSBpbmZvcm1hdGlvbiBmcm9tIFNEUCBtZWRpYSBzZWN0aW9uIG9yIHNlc3Npb25wYXJ0LlxuLy8gRklYTUU6IGZvciBjb25zaXN0ZW5jeSB3aXRoIG90aGVyIGZ1bmN0aW9ucyB0aGlzIHNob3VsZCBvbmx5XG4vLyAgIGdldCB0aGUgaWNlLXVmcmFnIGFuZCBpY2UtcHdkIGxpbmVzIGFzIGlucHV0LlxuU0RQVXRpbHMuZ2V0SWNlUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgY29uc3QgdWZyYWcgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24gKyBzZXNzaW9ucGFydCxcbiAgICAnYT1pY2UtdWZyYWc6JylbMF07XG4gIGNvbnN0IHB3ZCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiArIHNlc3Npb25wYXJ0LFxuICAgICdhPWljZS1wd2Q6JylbMF07XG4gIGlmICghKHVmcmFnICYmIHB3ZCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICByZXR1cm4ge1xuICAgIHVzZXJuYW1lRnJhZ21lbnQ6IHVmcmFnLnN1YnN0cmluZygxMiksXG4gICAgcGFzc3dvcmQ6IHB3ZC5zdWJzdHJpbmcoMTApLFxuICB9O1xufTtcblxuLy8gU2VyaWFsaXplcyBJQ0UgcGFyYW1ldGVycyB0byBTRFAuXG5TRFBVdGlscy53cml0ZUljZVBhcmFtZXRlcnMgPSBmdW5jdGlvbihwYXJhbXMpIHtcbiAgbGV0IHNkcCA9ICdhPWljZS11ZnJhZzonICsgcGFyYW1zLnVzZXJuYW1lRnJhZ21lbnQgKyAnXFxyXFxuJyArXG4gICAgICAnYT1pY2UtcHdkOicgKyBwYXJhbXMucGFzc3dvcmQgKyAnXFxyXFxuJztcbiAgaWYgKHBhcmFtcy5pY2VMaXRlKSB7XG4gICAgc2RwICs9ICdhPWljZS1saXRlXFxyXFxuJztcbiAgfVxuICByZXR1cm4gc2RwO1xufTtcblxuLy8gUGFyc2VzIHRoZSBTRFAgbWVkaWEgc2VjdGlvbiBhbmQgcmV0dXJucyBSVENSdHBQYXJhbWV0ZXJzLlxuU0RQVXRpbHMucGFyc2VSdHBQYXJhbWV0ZXJzID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGRlc2NyaXB0aW9uID0ge1xuICAgIGNvZGVjczogW10sXG4gICAgaGVhZGVyRXh0ZW5zaW9uczogW10sXG4gICAgZmVjTWVjaGFuaXNtczogW10sXG4gICAgcnRjcDogW10sXG4gIH07XG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICBjb25zdCBtbGluZSA9IGxpbmVzWzBdLnNwbGl0KCcgJyk7XG4gIGRlc2NyaXB0aW9uLnByb2ZpbGUgPSBtbGluZVsyXTtcbiAgZm9yIChsZXQgaSA9IDM7IGkgPCBtbGluZS5sZW5ndGg7IGkrKykgeyAvLyBmaW5kIGFsbCBjb2RlY3MgZnJvbSBtbGluZVszLi5dXG4gICAgY29uc3QgcHQgPSBtbGluZVtpXTtcbiAgICBjb25zdCBydHBtYXBsaW5lID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgoXG4gICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0cG1hcDonICsgcHQgKyAnICcpWzBdO1xuICAgIGlmIChydHBtYXBsaW5lKSB7XG4gICAgICBjb25zdCBjb2RlYyA9IFNEUFV0aWxzLnBhcnNlUnRwTWFwKHJ0cG1hcGxpbmUpO1xuICAgICAgY29uc3QgZm10cHMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChcbiAgICAgICAgbWVkaWFTZWN0aW9uLCAnYT1mbXRwOicgKyBwdCArICcgJyk7XG4gICAgICAvLyBPbmx5IHRoZSBmaXJzdCBhPWZtdHA6PHB0PiBpcyBjb25zaWRlcmVkLlxuICAgICAgY29kZWMucGFyYW1ldGVycyA9IGZtdHBzLmxlbmd0aCA/IFNEUFV0aWxzLnBhcnNlRm10cChmbXRwc1swXSkgOiB7fTtcbiAgICAgIGNvZGVjLnJ0Y3BGZWVkYmFjayA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KFxuICAgICAgICBtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtZmI6JyArIHB0ICsgJyAnKVxuICAgICAgICAubWFwKFNEUFV0aWxzLnBhcnNlUnRjcEZiKTtcbiAgICAgIGRlc2NyaXB0aW9uLmNvZGVjcy5wdXNoKGNvZGVjKTtcbiAgICAgIC8vIHBhcnNlIEZFQyBtZWNoYW5pc21zIGZyb20gcnRwbWFwIGxpbmVzLlxuICAgICAgc3dpdGNoIChjb2RlYy5uYW1lLnRvVXBwZXJDYXNlKCkpIHtcbiAgICAgICAgY2FzZSAnUkVEJzpcbiAgICAgICAgY2FzZSAnVUxQRkVDJzpcbiAgICAgICAgICBkZXNjcmlwdGlvbi5mZWNNZWNoYW5pc21zLnB1c2goY29kZWMubmFtZS50b1VwcGVyQ2FzZSgpKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDogLy8gb25seSBSRUQgYW5kIFVMUEZFQyBhcmUgcmVjb2duaXplZCBhcyBGRUMgbWVjaGFuaXNtcy5cbiAgICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1leHRtYXA6JykuZm9yRWFjaChsaW5lID0+IHtcbiAgICBkZXNjcmlwdGlvbi5oZWFkZXJFeHRlbnNpb25zLnB1c2goU0RQVXRpbHMucGFyc2VFeHRtYXAobGluZSkpO1xuICB9KTtcbiAgY29uc3Qgd2lsZGNhcmRSdGNwRmIgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXJ0Y3AtZmI6KiAnKVxuICAgIC5tYXAoU0RQVXRpbHMucGFyc2VSdGNwRmIpO1xuICBkZXNjcmlwdGlvbi5jb2RlY3MuZm9yRWFjaChjb2RlYyA9PiB7XG4gICAgd2lsZGNhcmRSdGNwRmIuZm9yRWFjaChmYj0+IHtcbiAgICAgIGNvbnN0IGR1cGxpY2F0ZSA9IGNvZGVjLnJ0Y3BGZWVkYmFjay5maW5kKGV4aXN0aW5nRmVlZGJhY2sgPT4ge1xuICAgICAgICByZXR1cm4gZXhpc3RpbmdGZWVkYmFjay50eXBlID09PSBmYi50eXBlICYmXG4gICAgICAgICAgZXhpc3RpbmdGZWVkYmFjay5wYXJhbWV0ZXIgPT09IGZiLnBhcmFtZXRlcjtcbiAgICAgIH0pO1xuICAgICAgaWYgKCFkdXBsaWNhdGUpIHtcbiAgICAgICAgY29kZWMucnRjcEZlZWRiYWNrLnB1c2goZmIpO1xuICAgICAgfVxuICAgIH0pO1xuICB9KTtcbiAgLy8gRklYTUU6IHBhcnNlIHJ0Y3AuXG4gIHJldHVybiBkZXNjcmlwdGlvbjtcbn07XG5cbi8vIEdlbmVyYXRlcyBwYXJ0cyBvZiB0aGUgU0RQIG1lZGlhIHNlY3Rpb24gZGVzY3JpYmluZyB0aGUgY2FwYWJpbGl0aWVzIC9cbi8vIHBhcmFtZXRlcnMuXG5TRFBVdGlscy53cml0ZVJ0cERlc2NyaXB0aW9uID0gZnVuY3Rpb24oa2luZCwgY2Fwcykge1xuICBsZXQgc2RwID0gJyc7XG5cbiAgLy8gQnVpbGQgdGhlIG1saW5lLlxuICBzZHAgKz0gJ209JyArIGtpbmQgKyAnICc7XG4gIHNkcCArPSBjYXBzLmNvZGVjcy5sZW5ndGggPiAwID8gJzknIDogJzAnOyAvLyByZWplY3QgaWYgbm8gY29kZWNzLlxuICBzZHAgKz0gJyAnICsgKGNhcHMucHJvZmlsZSB8fCAnVURQL1RMUy9SVFAvU0FWUEYnKSArICcgJztcbiAgc2RwICs9IGNhcHMuY29kZWNzLm1hcChjb2RlYyA9PiB7XG4gICAgaWYgKGNvZGVjLnByZWZlcnJlZFBheWxvYWRUeXBlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHJldHVybiBjb2RlYy5wcmVmZXJyZWRQYXlsb2FkVHlwZTtcbiAgICB9XG4gICAgcmV0dXJuIGNvZGVjLnBheWxvYWRUeXBlO1xuICB9KS5qb2luKCcgJykgKyAnXFxyXFxuJztcblxuICBzZHAgKz0gJ2M9SU4gSVA0IDAuMC4wLjBcXHJcXG4nO1xuICBzZHAgKz0gJ2E9cnRjcDo5IElOIElQNCAwLjAuMC4wXFxyXFxuJztcblxuICAvLyBBZGQgYT1ydHBtYXAgbGluZXMgZm9yIGVhY2ggY29kZWMuIEFsc28gZm10cCBhbmQgcnRjcC1mYi5cbiAgY2Fwcy5jb2RlY3MuZm9yRWFjaChjb2RlYyA9PiB7XG4gICAgc2RwICs9IFNEUFV0aWxzLndyaXRlUnRwTWFwKGNvZGVjKTtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVGbXRwKGNvZGVjKTtcbiAgICBzZHAgKz0gU0RQVXRpbHMud3JpdGVSdGNwRmIoY29kZWMpO1xuICB9KTtcbiAgbGV0IG1heHB0aW1lID0gMDtcbiAgY2Fwcy5jb2RlY3MuZm9yRWFjaChjb2RlYyA9PiB7XG4gICAgaWYgKGNvZGVjLm1heHB0aW1lID4gbWF4cHRpbWUpIHtcbiAgICAgIG1heHB0aW1lID0gY29kZWMubWF4cHRpbWU7XG4gICAgfVxuICB9KTtcbiAgaWYgKG1heHB0aW1lID4gMCkge1xuICAgIHNkcCArPSAnYT1tYXhwdGltZTonICsgbWF4cHRpbWUgKyAnXFxyXFxuJztcbiAgfVxuXG4gIGlmIChjYXBzLmhlYWRlckV4dGVuc2lvbnMpIHtcbiAgICBjYXBzLmhlYWRlckV4dGVuc2lvbnMuZm9yRWFjaChleHRlbnNpb24gPT4ge1xuICAgICAgc2RwICs9IFNEUFV0aWxzLndyaXRlRXh0bWFwKGV4dGVuc2lvbik7XG4gICAgfSk7XG4gIH1cbiAgLy8gRklYTUU6IHdyaXRlIGZlY01lY2hhbmlzbXMuXG4gIHJldHVybiBzZHA7XG59O1xuXG4vLyBQYXJzZXMgdGhlIFNEUCBtZWRpYSBzZWN0aW9uIGFuZCByZXR1cm5zIGFuIGFycmF5IG9mXG4vLyBSVENSdHBFbmNvZGluZ1BhcmFtZXRlcnMuXG5TRFBVdGlscy5wYXJzZVJ0cEVuY29kaW5nUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBlbmNvZGluZ1BhcmFtZXRlcnMgPSBbXTtcbiAgY29uc3QgZGVzY3JpcHRpb24gPSBTRFBVdGlscy5wYXJzZVJ0cFBhcmFtZXRlcnMobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgaGFzUmVkID0gZGVzY3JpcHRpb24uZmVjTWVjaGFuaXNtcy5pbmRleE9mKCdSRUQnKSAhPT0gLTE7XG4gIGNvbnN0IGhhc1VscGZlYyA9IGRlc2NyaXB0aW9uLmZlY01lY2hhbmlzbXMuaW5kZXhPZignVUxQRkVDJykgIT09IC0xO1xuXG4gIC8vIGZpbHRlciBhPXNzcmM6Li4uIGNuYW1lOiwgaWdub3JlIFBsYW5CLW1zaWRcbiAgY29uc3Qgc3NyY3MgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmM6JylcbiAgICAubWFwKGxpbmUgPT4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSkpXG4gICAgLmZpbHRlcihwYXJ0cyA9PiBwYXJ0cy5hdHRyaWJ1dGUgPT09ICdjbmFtZScpO1xuICBjb25zdCBwcmltYXJ5U3NyYyA9IHNzcmNzLmxlbmd0aCA+IDAgJiYgc3NyY3NbMF0uc3NyYztcbiAgbGV0IHNlY29uZGFyeVNzcmM7XG5cbiAgY29uc3QgZmxvd3MgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmMtZ3JvdXA6RklEJylcbiAgICAubWFwKGxpbmUgPT4ge1xuICAgICAgY29uc3QgcGFydHMgPSBsaW5lLnN1YnN0cmluZygxNykuc3BsaXQoJyAnKTtcbiAgICAgIHJldHVybiBwYXJ0cy5tYXAocGFydCA9PiBwYXJzZUludChwYXJ0LCAxMCkpO1xuICAgIH0pO1xuICBpZiAoZmxvd3MubGVuZ3RoID4gMCAmJiBmbG93c1swXS5sZW5ndGggPiAxICYmIGZsb3dzWzBdWzBdID09PSBwcmltYXJ5U3NyYykge1xuICAgIHNlY29uZGFyeVNzcmMgPSBmbG93c1swXVsxXTtcbiAgfVxuXG4gIGRlc2NyaXB0aW9uLmNvZGVjcy5mb3JFYWNoKGNvZGVjID0+IHtcbiAgICBpZiAoY29kZWMubmFtZS50b1VwcGVyQ2FzZSgpID09PSAnUlRYJyAmJiBjb2RlYy5wYXJhbWV0ZXJzLmFwdCkge1xuICAgICAgbGV0IGVuY1BhcmFtID0ge1xuICAgICAgICBzc3JjOiBwcmltYXJ5U3NyYyxcbiAgICAgICAgY29kZWNQYXlsb2FkVHlwZTogcGFyc2VJbnQoY29kZWMucGFyYW1ldGVycy5hcHQsIDEwKSxcbiAgICAgIH07XG4gICAgICBpZiAocHJpbWFyeVNzcmMgJiYgc2Vjb25kYXJ5U3NyYykge1xuICAgICAgICBlbmNQYXJhbS5ydHggPSB7c3NyYzogc2Vjb25kYXJ5U3NyY307XG4gICAgICB9XG4gICAgICBlbmNvZGluZ1BhcmFtZXRlcnMucHVzaChlbmNQYXJhbSk7XG4gICAgICBpZiAoaGFzUmVkKSB7XG4gICAgICAgIGVuY1BhcmFtID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShlbmNQYXJhbSkpO1xuICAgICAgICBlbmNQYXJhbS5mZWMgPSB7XG4gICAgICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgICAgICAgbWVjaGFuaXNtOiBoYXNVbHBmZWMgPyAncmVkK3VscGZlYycgOiAncmVkJyxcbiAgICAgICAgfTtcbiAgICAgICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goZW5jUGFyYW0pO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG4gIGlmIChlbmNvZGluZ1BhcmFtZXRlcnMubGVuZ3RoID09PSAwICYmIHByaW1hcnlTc3JjKSB7XG4gICAgZW5jb2RpbmdQYXJhbWV0ZXJzLnB1c2goe1xuICAgICAgc3NyYzogcHJpbWFyeVNzcmMsXG4gICAgfSk7XG4gIH1cblxuICAvLyB3ZSBzdXBwb3J0IGJvdGggYj1BUyBhbmQgYj1USUFTIGJ1dCBpbnRlcnByZXQgQVMgYXMgVElBUy5cbiAgbGV0IGJhbmR3aWR0aCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2I9Jyk7XG4gIGlmIChiYW5kd2lkdGgubGVuZ3RoKSB7XG4gICAgaWYgKGJhbmR3aWR0aFswXS5pbmRleE9mKCdiPVRJQVM6JykgPT09IDApIHtcbiAgICAgIGJhbmR3aWR0aCA9IHBhcnNlSW50KGJhbmR3aWR0aFswXS5zdWJzdHJpbmcoNyksIDEwKTtcbiAgICB9IGVsc2UgaWYgKGJhbmR3aWR0aFswXS5pbmRleE9mKCdiPUFTOicpID09PSAwKSB7XG4gICAgICAvLyB1c2UgZm9ybXVsYSBmcm9tIEpTRVAgdG8gY29udmVydCBiPUFTIHRvIFRJQVMgdmFsdWUuXG4gICAgICBiYW5kd2lkdGggPSBwYXJzZUludChiYW5kd2lkdGhbMF0uc3Vic3RyaW5nKDUpLCAxMCkgKiAxMDAwICogMC45NVxuICAgICAgICAgIC0gKDUwICogNDAgKiA4KTtcbiAgICB9IGVsc2Uge1xuICAgICAgYmFuZHdpZHRoID0gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBlbmNvZGluZ1BhcmFtZXRlcnMuZm9yRWFjaChwYXJhbXMgPT4ge1xuICAgICAgcGFyYW1zLm1heEJpdHJhdGUgPSBiYW5kd2lkdGg7XG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIGVuY29kaW5nUGFyYW1ldGVycztcbn07XG5cbi8vIHBhcnNlcyBodHRwOi8vZHJhZnQub3J0Yy5vcmcvI3J0Y3J0Y3BwYXJhbWV0ZXJzKlxuU0RQVXRpbHMucGFyc2VSdGNwUGFyYW1ldGVycyA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBydGNwUGFyYW1ldGVycyA9IHt9O1xuXG4gIC8vIEdldHMgdGhlIGZpcnN0IFNTUkMuIE5vdGUgdGhhdCB3aXRoIFJUWCB0aGVyZSBtaWdodCBiZSBtdWx0aXBsZVxuICAvLyBTU1JDcy5cbiAgY29uc3QgcmVtb3RlU3NyYyA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c3NyYzonKVxuICAgIC5tYXAobGluZSA9PiBTRFBVdGlscy5wYXJzZVNzcmNNZWRpYShsaW5lKSlcbiAgICAuZmlsdGVyKG9iaiA9PiBvYmouYXR0cmlidXRlID09PSAnY25hbWUnKVswXTtcbiAgaWYgKHJlbW90ZVNzcmMpIHtcbiAgICBydGNwUGFyYW1ldGVycy5jbmFtZSA9IHJlbW90ZVNzcmMudmFsdWU7XG4gICAgcnRjcFBhcmFtZXRlcnMuc3NyYyA9IHJlbW90ZVNzcmMuc3NyYztcbiAgfVxuXG4gIC8vIEVkZ2UgdXNlcyB0aGUgY29tcG91bmQgYXR0cmlidXRlIGluc3RlYWQgb2YgcmVkdWNlZFNpemVcbiAgLy8gY29tcG91bmQgaXMgIXJlZHVjZWRTaXplXG4gIGNvbnN0IHJzaXplID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1ydGNwLXJzaXplJyk7XG4gIHJ0Y3BQYXJhbWV0ZXJzLnJlZHVjZWRTaXplID0gcnNpemUubGVuZ3RoID4gMDtcbiAgcnRjcFBhcmFtZXRlcnMuY29tcG91bmQgPSByc2l6ZS5sZW5ndGggPT09IDA7XG5cbiAgLy8gcGFyc2VzIHRoZSBydGNwLW11eCBhdHRy0ZZidXRlLlxuICAvLyBOb3RlIHRoYXQgRWRnZSBkb2VzIG5vdCBzdXBwb3J0IHVubXV4ZWQgUlRDUC5cbiAgY29uc3QgbXV4ID0gU0RQVXRpbHMubWF0Y2hQcmVmaXgobWVkaWFTZWN0aW9uLCAnYT1ydGNwLW11eCcpO1xuICBydGNwUGFyYW1ldGVycy5tdXggPSBtdXgubGVuZ3RoID4gMDtcblxuICByZXR1cm4gcnRjcFBhcmFtZXRlcnM7XG59O1xuXG5TRFBVdGlscy53cml0ZVJ0Y3BQYXJhbWV0ZXJzID0gZnVuY3Rpb24ocnRjcFBhcmFtZXRlcnMpIHtcbiAgbGV0IHNkcCA9ICcnO1xuICBpZiAocnRjcFBhcmFtZXRlcnMucmVkdWNlZFNpemUpIHtcbiAgICBzZHAgKz0gJ2E9cnRjcC1yc2l6ZVxcclxcbic7XG4gIH1cbiAgaWYgKHJ0Y3BQYXJhbWV0ZXJzLm11eCkge1xuICAgIHNkcCArPSAnYT1ydGNwLW11eFxcclxcbic7XG4gIH1cbiAgaWYgKHJ0Y3BQYXJhbWV0ZXJzLnNzcmMgIT09IHVuZGVmaW5lZCAmJiBydGNwUGFyYW1ldGVycy5jbmFtZSkge1xuICAgIHNkcCArPSAnYT1zc3JjOicgKyBydGNwUGFyYW1ldGVycy5zc3JjICtcbiAgICAgICcgY25hbWU6JyArIHJ0Y3BQYXJhbWV0ZXJzLmNuYW1lICsgJ1xcclxcbic7XG4gIH1cbiAgcmV0dXJuIHNkcDtcbn07XG5cblxuLy8gcGFyc2VzIGVpdGhlciBhPW1zaWQ6IG9yIGE9c3NyYzouLi4gbXNpZCBsaW5lcyBhbmQgcmV0dXJuc1xuLy8gdGhlIGlkIG9mIHRoZSBNZWRpYVN0cmVhbSBhbmQgTWVkaWFTdHJlYW1UcmFjay5cblNEUFV0aWxzLnBhcnNlTXNpZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBsZXQgcGFydHM7XG4gIGNvbnN0IHNwZWMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1zaWQ6Jyk7XG4gIGlmIChzcGVjLmxlbmd0aCA9PT0gMSkge1xuICAgIHBhcnRzID0gc3BlY1swXS5zdWJzdHJpbmcoNykuc3BsaXQoJyAnKTtcbiAgICByZXR1cm4ge3N0cmVhbTogcGFydHNbMF0sIHRyYWNrOiBwYXJ0c1sxXX07XG4gIH1cbiAgY29uc3QgcGxhbkIgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNzcmM6JylcbiAgICAubWFwKGxpbmUgPT4gU0RQVXRpbHMucGFyc2VTc3JjTWVkaWEobGluZSkpXG4gICAgLmZpbHRlcihtc2lkUGFydHMgPT4gbXNpZFBhcnRzLmF0dHJpYnV0ZSA9PT0gJ21zaWQnKTtcbiAgaWYgKHBsYW5CLmxlbmd0aCA+IDApIHtcbiAgICBwYXJ0cyA9IHBsYW5CWzBdLnZhbHVlLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtzdHJlYW06IHBhcnRzWzBdLCB0cmFjazogcGFydHNbMV19O1xuICB9XG59O1xuXG4vLyBTQ1RQXG4vLyBwYXJzZXMgZHJhZnQtaWV0Zi1tbXVzaWMtc2N0cC1zZHAtMjYgZmlyc3QgYW5kIGZhbGxzIGJhY2tcbi8vIHRvIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTA1XG5TRFBVdGlscy5wYXJzZVNjdHBEZXNjcmlwdGlvbiA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBtbGluZSA9IFNEUFV0aWxzLnBhcnNlTUxpbmUobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgbWF4U2l6ZUxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPW1heC1tZXNzYWdlLXNpemU6Jyk7XG4gIGxldCBtYXhNZXNzYWdlU2l6ZTtcbiAgaWYgKG1heFNpemVMaW5lLmxlbmd0aCA+IDApIHtcbiAgICBtYXhNZXNzYWdlU2l6ZSA9IHBhcnNlSW50KG1heFNpemVMaW5lWzBdLnN1YnN0cmluZygxOSksIDEwKTtcbiAgfVxuICBpZiAoaXNOYU4obWF4TWVzc2FnZVNpemUpKSB7XG4gICAgbWF4TWVzc2FnZVNpemUgPSA2NTUzNjtcbiAgfVxuICBjb25zdCBzY3RwUG9ydCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KG1lZGlhU2VjdGlvbiwgJ2E9c2N0cC1wb3J0OicpO1xuICBpZiAoc2N0cFBvcnQubGVuZ3RoID4gMCkge1xuICAgIHJldHVybiB7XG4gICAgICBwb3J0OiBwYXJzZUludChzY3RwUG9ydFswXS5zdWJzdHJpbmcoMTIpLCAxMCksXG4gICAgICBwcm90b2NvbDogbWxpbmUuZm10LFxuICAgICAgbWF4TWVzc2FnZVNpemUsXG4gICAgfTtcbiAgfVxuICBjb25zdCBzY3RwTWFwTGluZXMgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdhPXNjdHBtYXA6Jyk7XG4gIGlmIChzY3RwTWFwTGluZXMubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IHBhcnRzID0gc2N0cE1hcExpbmVzWzBdXG4gICAgICAuc3Vic3RyaW5nKDEwKVxuICAgICAgLnNwbGl0KCcgJyk7XG4gICAgcmV0dXJuIHtcbiAgICAgIHBvcnQ6IHBhcnNlSW50KHBhcnRzWzBdLCAxMCksXG4gICAgICBwcm90b2NvbDogcGFydHNbMV0sXG4gICAgICBtYXhNZXNzYWdlU2l6ZSxcbiAgICB9O1xuICB9XG59O1xuXG4vLyBTQ1RQXG4vLyBvdXRwdXRzIHRoZSBkcmFmdC1pZXRmLW1tdXNpYy1zY3RwLXNkcC0yNiB2ZXJzaW9uIHRoYXQgYWxsIGJyb3dzZXJzXG4vLyBzdXBwb3J0IGJ5IG5vdyByZWNlaXZpbmcgaW4gdGhpcyBmb3JtYXQsIHVubGVzcyB3ZSBvcmlnaW5hbGx5IHBhcnNlZFxuLy8gYXMgdGhlIGRyYWZ0LWlldGYtbW11c2ljLXNjdHAtc2RwLTA1IGZvcm1hdCAoaW5kaWNhdGVkIGJ5IHRoZSBtLWxpbmVcbi8vIHByb3RvY29sIG9mIERUTFMvU0NUUCAtLSB3aXRob3V0IFVEUC8gb3IgVENQLylcblNEUFV0aWxzLndyaXRlU2N0cERlc2NyaXB0aW9uID0gZnVuY3Rpb24obWVkaWEsIHNjdHApIHtcbiAgbGV0IG91dHB1dCA9IFtdO1xuICBpZiAobWVkaWEucHJvdG9jb2wgIT09ICdEVExTL1NDVFAnKSB7XG4gICAgb3V0cHV0ID0gW1xuICAgICAgJ209JyArIG1lZGlhLmtpbmQgKyAnIDkgJyArIG1lZGlhLnByb3RvY29sICsgJyAnICsgc2N0cC5wcm90b2NvbCArICdcXHJcXG4nLFxuICAgICAgJ2M9SU4gSVA0IDAuMC4wLjBcXHJcXG4nLFxuICAgICAgJ2E9c2N0cC1wb3J0OicgKyBzY3RwLnBvcnQgKyAnXFxyXFxuJyxcbiAgICBdO1xuICB9IGVsc2Uge1xuICAgIG91dHB1dCA9IFtcbiAgICAgICdtPScgKyBtZWRpYS5raW5kICsgJyA5ICcgKyBtZWRpYS5wcm90b2NvbCArICcgJyArIHNjdHAucG9ydCArICdcXHJcXG4nLFxuICAgICAgJ2M9SU4gSVA0IDAuMC4wLjBcXHJcXG4nLFxuICAgICAgJ2E9c2N0cG1hcDonICsgc2N0cC5wb3J0ICsgJyAnICsgc2N0cC5wcm90b2NvbCArICcgNjU1MzVcXHJcXG4nLFxuICAgIF07XG4gIH1cbiAgaWYgKHNjdHAubWF4TWVzc2FnZVNpemUgIT09IHVuZGVmaW5lZCkge1xuICAgIG91dHB1dC5wdXNoKCdhPW1heC1tZXNzYWdlLXNpemU6JyArIHNjdHAubWF4TWVzc2FnZVNpemUgKyAnXFxyXFxuJyk7XG4gIH1cbiAgcmV0dXJuIG91dHB1dC5qb2luKCcnKTtcbn07XG5cbi8vIEdlbmVyYXRlIGEgc2Vzc2lvbiBJRCBmb3IgU0RQLlxuLy8gaHR0cHM6Ly90b29scy5pZXRmLm9yZy9odG1sL2RyYWZ0LWlldGYtcnRjd2ViLWpzZXAtMjAjc2VjdGlvbi01LjIuMVxuLy8gcmVjb21tZW5kcyB1c2luZyBhIGNyeXB0b2dyYXBoaWNhbGx5IHJhbmRvbSArdmUgNjQtYml0IHZhbHVlXG4vLyBidXQgcmlnaHQgbm93IHRoaXMgc2hvdWxkIGJlIGFjY2VwdGFibGUgYW5kIHdpdGhpbiB0aGUgcmlnaHQgcmFuZ2VcblNEUFV0aWxzLmdlbmVyYXRlU2Vzc2lvbklkID0gZnVuY3Rpb24oKSB7XG4gIHJldHVybiBNYXRoLnJhbmRvbSgpLnRvU3RyaW5nKCkuc3Vic3RyKDIsIDIyKTtcbn07XG5cbi8vIFdyaXRlIGJvaWxlciBwbGF0ZSBmb3Igc3RhcnQgb2YgU0RQXG4vLyBzZXNzSWQgYXJndW1lbnQgaXMgb3B0aW9uYWwgLSBpZiBub3Qgc3VwcGxpZWQgaXQgd2lsbFxuLy8gYmUgZ2VuZXJhdGVkIHJhbmRvbWx5XG4vLyBzZXNzVmVyc2lvbiBpcyBvcHRpb25hbCBhbmQgZGVmYXVsdHMgdG8gMlxuLy8gc2Vzc1VzZXIgaXMgb3B0aW9uYWwgYW5kIGRlZmF1bHRzIHRvICd0aGlzaXNhZGFwdGVyb3J0YydcblNEUFV0aWxzLndyaXRlU2Vzc2lvbkJvaWxlcnBsYXRlID0gZnVuY3Rpb24oc2Vzc0lkLCBzZXNzVmVyLCBzZXNzVXNlcikge1xuICBsZXQgc2Vzc2lvbklkO1xuICBjb25zdCB2ZXJzaW9uID0gc2Vzc1ZlciAhPT0gdW5kZWZpbmVkID8gc2Vzc1ZlciA6IDI7XG4gIGlmIChzZXNzSWQpIHtcbiAgICBzZXNzaW9uSWQgPSBzZXNzSWQ7XG4gIH0gZWxzZSB7XG4gICAgc2Vzc2lvbklkID0gU0RQVXRpbHMuZ2VuZXJhdGVTZXNzaW9uSWQoKTtcbiAgfVxuICBjb25zdCB1c2VyID0gc2Vzc1VzZXIgfHwgJ3RoaXNpc2FkYXB0ZXJvcnRjJztcbiAgLy8gRklYTUU6IHNlc3MtaWQgc2hvdWxkIGJlIGFuIE5UUCB0aW1lc3RhbXAuXG4gIHJldHVybiAndj0wXFxyXFxuJyArXG4gICAgICAnbz0nICsgdXNlciArICcgJyArIHNlc3Npb25JZCArICcgJyArIHZlcnNpb24gK1xuICAgICAgICAnIElOIElQNCAxMjcuMC4wLjFcXHJcXG4nICtcbiAgICAgICdzPS1cXHJcXG4nICtcbiAgICAgICd0PTAgMFxcclxcbic7XG59O1xuXG4vLyBHZXRzIHRoZSBkaXJlY3Rpb24gZnJvbSB0aGUgbWVkaWFTZWN0aW9uIG9yIHRoZSBzZXNzaW9ucGFydC5cblNEUFV0aWxzLmdldERpcmVjdGlvbiA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbiwgc2Vzc2lvbnBhcnQpIHtcbiAgLy8gTG9vayBmb3Igc2VuZHJlY3YsIHNlbmRvbmx5LCByZWN2b25seSwgaW5hY3RpdmUsIGRlZmF1bHQgdG8gc2VuZHJlY3YuXG4gIGNvbnN0IGxpbmVzID0gU0RQVXRpbHMuc3BsaXRMaW5lcyhtZWRpYVNlY3Rpb24pO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IGxpbmVzLmxlbmd0aDsgaSsrKSB7XG4gICAgc3dpdGNoIChsaW5lc1tpXSkge1xuICAgICAgY2FzZSAnYT1zZW5kcmVjdic6XG4gICAgICBjYXNlICdhPXNlbmRvbmx5JzpcbiAgICAgIGNhc2UgJ2E9cmVjdm9ubHknOlxuICAgICAgY2FzZSAnYT1pbmFjdGl2ZSc6XG4gICAgICAgIHJldHVybiBsaW5lc1tpXS5zdWJzdHJpbmcoMik7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICAvLyBGSVhNRTogV2hhdCBzaG91bGQgaGFwcGVuIGhlcmU/XG4gICAgfVxuICB9XG4gIGlmIChzZXNzaW9ucGFydCkge1xuICAgIHJldHVybiBTRFBVdGlscy5nZXREaXJlY3Rpb24oc2Vzc2lvbnBhcnQpO1xuICB9XG4gIHJldHVybiAnc2VuZHJlY3YnO1xufTtcblxuU0RQVXRpbHMuZ2V0S2luZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMobWVkaWFTZWN0aW9uKTtcbiAgY29uc3QgbWxpbmUgPSBsaW5lc1swXS5zcGxpdCgnICcpO1xuICByZXR1cm4gbWxpbmVbMF0uc3Vic3RyaW5nKDIpO1xufTtcblxuU0RQVXRpbHMuaXNSZWplY3RlZCA9IGZ1bmN0aW9uKG1lZGlhU2VjdGlvbikge1xuICByZXR1cm4gbWVkaWFTZWN0aW9uLnNwbGl0KCcgJywgMilbMV0gPT09ICcwJztcbn07XG5cblNEUFV0aWxzLnBhcnNlTUxpbmUgPSBmdW5jdGlvbihtZWRpYVNlY3Rpb24pIHtcbiAgY29uc3QgbGluZXMgPSBTRFBVdGlscy5zcGxpdExpbmVzKG1lZGlhU2VjdGlvbik7XG4gIGNvbnN0IHBhcnRzID0gbGluZXNbMF0uc3Vic3RyaW5nKDIpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAga2luZDogcGFydHNbMF0sXG4gICAgcG9ydDogcGFyc2VJbnQocGFydHNbMV0sIDEwKSxcbiAgICBwcm90b2NvbDogcGFydHNbMl0sXG4gICAgZm10OiBwYXJ0cy5zbGljZSgzKS5qb2luKCcgJyksXG4gIH07XG59O1xuXG5TRFBVdGlscy5wYXJzZU9MaW5lID0gZnVuY3Rpb24obWVkaWFTZWN0aW9uKSB7XG4gIGNvbnN0IGxpbmUgPSBTRFBVdGlscy5tYXRjaFByZWZpeChtZWRpYVNlY3Rpb24sICdvPScpWzBdO1xuICBjb25zdCBwYXJ0cyA9IGxpbmUuc3Vic3RyaW5nKDIpLnNwbGl0KCcgJyk7XG4gIHJldHVybiB7XG4gICAgdXNlcm5hbWU6IHBhcnRzWzBdLFxuICAgIHNlc3Npb25JZDogcGFydHNbMV0sXG4gICAgc2Vzc2lvblZlcnNpb246IHBhcnNlSW50KHBhcnRzWzJdLCAxMCksXG4gICAgbmV0VHlwZTogcGFydHNbM10sXG4gICAgYWRkcmVzc1R5cGU6IHBhcnRzWzRdLFxuICAgIGFkZHJlc3M6IHBhcnRzWzVdLFxuICB9O1xufTtcblxuLy8gYSB2ZXJ5IG5haXZlIGludGVycHJldGF0aW9uIG9mIGEgdmFsaWQgU0RQLlxuU0RQVXRpbHMuaXNWYWxpZFNEUCA9IGZ1bmN0aW9uKGJsb2IpIHtcbiAgaWYgKHR5cGVvZiBibG9iICE9PSAnc3RyaW5nJyB8fCBibG9iLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuICBjb25zdCBsaW5lcyA9IFNEUFV0aWxzLnNwbGl0TGluZXMoYmxvYik7XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgbGluZXMubGVuZ3RoOyBpKyspIHtcbiAgICBpZiAobGluZXNbaV0ubGVuZ3RoIDwgMiB8fCBsaW5lc1tpXS5jaGFyQXQoMSkgIT09ICc9Jykge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICAvLyBUT0RPOiBjaGVjayB0aGUgbW9kaWZpZXIgYSBiaXQgbW9yZS5cbiAgfVxuICByZXR1cm4gdHJ1ZTtcbn07XG5cbi8vIEV4cG9zZSBwdWJsaWMgbWV0aG9kcy5cbmlmICh0eXBlb2YgbW9kdWxlID09PSAnb2JqZWN0Jykge1xuICBtb2R1bGUuZXhwb3J0cyA9IFNEUFV0aWxzO1xufVxuIiwiLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4vKiBVQVBhcnNlci5qcyB2MS4wLjM3XG4gICBDb3B5cmlnaHQgwqkgMjAxMi0yMDIxIEZhaXNhbCBTYWxtYW4gPGZAZmFpc2FsbWFuLmNvbT5cbiAgIE1JVCBMaWNlbnNlICovLypcbiAgIERldGVjdCBCcm93c2VyLCBFbmdpbmUsIE9TLCBDUFUsIGFuZCBEZXZpY2UgdHlwZS9tb2RlbCBmcm9tIFVzZXItQWdlbnQgZGF0YS5cbiAgIFN1cHBvcnRzIGJyb3dzZXIgJiBub2RlLmpzIGVudmlyb25tZW50LiBcbiAgIERlbW8gICA6IGh0dHBzOi8vZmFpc2FsbWFuLmdpdGh1Yi5pby91YS1wYXJzZXItanNcbiAgIFNvdXJjZSA6IGh0dHBzOi8vZ2l0aHViLmNvbS9mYWlzYWxtYW4vdWEtcGFyc2VyLWpzICovXG4vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy9cblxuKGZ1bmN0aW9uICh3aW5kb3csIHVuZGVmaW5lZCkge1xuXG4gICAgJ3VzZSBzdHJpY3QnO1xuXG4gICAgLy8vLy8vLy8vLy8vLy9cbiAgICAvLyBDb25zdGFudHNcbiAgICAvLy8vLy8vLy8vLy8vXG5cblxuICAgIHZhciBMSUJWRVJTSU9OICA9ICcxLjAuMzcnLFxuICAgICAgICBFTVBUWSAgICAgICA9ICcnLFxuICAgICAgICBVTktOT1dOICAgICA9ICc/JyxcbiAgICAgICAgRlVOQ19UWVBFICAgPSAnZnVuY3Rpb24nLFxuICAgICAgICBVTkRFRl9UWVBFICA9ICd1bmRlZmluZWQnLFxuICAgICAgICBPQkpfVFlQRSAgICA9ICdvYmplY3QnLFxuICAgICAgICBTVFJfVFlQRSAgICA9ICdzdHJpbmcnLFxuICAgICAgICBNQUpPUiAgICAgICA9ICdtYWpvcicsXG4gICAgICAgIE1PREVMICAgICAgID0gJ21vZGVsJyxcbiAgICAgICAgTkFNRSAgICAgICAgPSAnbmFtZScsXG4gICAgICAgIFRZUEUgICAgICAgID0gJ3R5cGUnLFxuICAgICAgICBWRU5ET1IgICAgICA9ICd2ZW5kb3InLFxuICAgICAgICBWRVJTSU9OICAgICA9ICd2ZXJzaW9uJyxcbiAgICAgICAgQVJDSElURUNUVVJFPSAnYXJjaGl0ZWN0dXJlJyxcbiAgICAgICAgQ09OU09MRSAgICAgPSAnY29uc29sZScsXG4gICAgICAgIE1PQklMRSAgICAgID0gJ21vYmlsZScsXG4gICAgICAgIFRBQkxFVCAgICAgID0gJ3RhYmxldCcsXG4gICAgICAgIFNNQVJUVFYgICAgID0gJ3NtYXJ0dHYnLFxuICAgICAgICBXRUFSQUJMRSAgICA9ICd3ZWFyYWJsZScsXG4gICAgICAgIEVNQkVEREVEICAgID0gJ2VtYmVkZGVkJyxcbiAgICAgICAgVUFfTUFYX0xFTkdUSCA9IDUwMDtcblxuICAgIHZhciBBTUFaT04gID0gJ0FtYXpvbicsXG4gICAgICAgIEFQUExFICAgPSAnQXBwbGUnLFxuICAgICAgICBBU1VTICAgID0gJ0FTVVMnLFxuICAgICAgICBCTEFDS0JFUlJZID0gJ0JsYWNrQmVycnknLFxuICAgICAgICBCUk9XU0VSID0gJ0Jyb3dzZXInLFxuICAgICAgICBDSFJPTUUgID0gJ0Nocm9tZScsXG4gICAgICAgIEVER0UgICAgPSAnRWRnZScsXG4gICAgICAgIEZJUkVGT1ggPSAnRmlyZWZveCcsXG4gICAgICAgIEdPT0dMRSAgPSAnR29vZ2xlJyxcbiAgICAgICAgSFVBV0VJICA9ICdIdWF3ZWknLFxuICAgICAgICBMRyAgICAgID0gJ0xHJyxcbiAgICAgICAgTUlDUk9TT0ZUID0gJ01pY3Jvc29mdCcsXG4gICAgICAgIE1PVE9ST0xBICA9ICdNb3Rvcm9sYScsXG4gICAgICAgIE9QRVJBICAgPSAnT3BlcmEnLFxuICAgICAgICBTQU1TVU5HID0gJ1NhbXN1bmcnLFxuICAgICAgICBTSEFSUCAgID0gJ1NoYXJwJyxcbiAgICAgICAgU09OWSAgICA9ICdTb255JyxcbiAgICAgICAgWElBT01JICA9ICdYaWFvbWknLFxuICAgICAgICBaRUJSQSAgID0gJ1plYnJhJyxcbiAgICAgICAgRkFDRUJPT0sgICAgPSAnRmFjZWJvb2snLFxuICAgICAgICBDSFJPTUlVTV9PUyA9ICdDaHJvbWl1bSBPUycsXG4gICAgICAgIE1BQ19PUyAgPSAnTWFjIE9TJztcblxuICAgIC8vLy8vLy8vLy8vXG4gICAgLy8gSGVscGVyXG4gICAgLy8vLy8vLy8vL1xuXG4gICAgdmFyIGV4dGVuZCA9IGZ1bmN0aW9uIChyZWdleGVzLCBleHRlbnNpb25zKSB7XG4gICAgICAgICAgICB2YXIgbWVyZ2VkUmVnZXhlcyA9IHt9O1xuICAgICAgICAgICAgZm9yICh2YXIgaSBpbiByZWdleGVzKSB7XG4gICAgICAgICAgICAgICAgaWYgKGV4dGVuc2lvbnNbaV0gJiYgZXh0ZW5zaW9uc1tpXS5sZW5ndGggJSAyID09PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgIG1lcmdlZFJlZ2V4ZXNbaV0gPSBleHRlbnNpb25zW2ldLmNvbmNhdChyZWdleGVzW2ldKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBtZXJnZWRSZWdleGVzW2ldID0gcmVnZXhlc1tpXTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gbWVyZ2VkUmVnZXhlcztcbiAgICAgICAgfSxcbiAgICAgICAgZW51bWVyaXplID0gZnVuY3Rpb24gKGFycikge1xuICAgICAgICAgICAgdmFyIGVudW1zID0ge307XG4gICAgICAgICAgICBmb3IgKHZhciBpPTA7IGk8YXJyLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICAgICAgZW51bXNbYXJyW2ldLnRvVXBwZXJDYXNlKCldID0gYXJyW2ldO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGVudW1zO1xuICAgICAgICB9LFxuICAgICAgICBoYXMgPSBmdW5jdGlvbiAoc3RyMSwgc3RyMikge1xuICAgICAgICAgICAgcmV0dXJuIHR5cGVvZiBzdHIxID09PSBTVFJfVFlQRSA/IGxvd2VyaXplKHN0cjIpLmluZGV4T2YobG93ZXJpemUoc3RyMSkpICE9PSAtMSA6IGZhbHNlO1xuICAgICAgICB9LFxuICAgICAgICBsb3dlcml6ZSA9IGZ1bmN0aW9uIChzdHIpIHtcbiAgICAgICAgICAgIHJldHVybiBzdHIudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgfSxcbiAgICAgICAgbWFqb3JpemUgPSBmdW5jdGlvbiAodmVyc2lvbikge1xuICAgICAgICAgICAgcmV0dXJuIHR5cGVvZih2ZXJzaW9uKSA9PT0gU1RSX1RZUEUgPyB2ZXJzaW9uLnJlcGxhY2UoL1teXFxkXFwuXS9nLCBFTVBUWSkuc3BsaXQoJy4nKVswXSA6IHVuZGVmaW5lZDtcbiAgICAgICAgfSxcbiAgICAgICAgdHJpbSA9IGZ1bmN0aW9uIChzdHIsIGxlbikge1xuICAgICAgICAgICAgaWYgKHR5cGVvZihzdHIpID09PSBTVFJfVFlQRSkge1xuICAgICAgICAgICAgICAgIHN0ciA9IHN0ci5yZXBsYWNlKC9eXFxzXFxzKi8sIEVNUFRZKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdHlwZW9mKGxlbikgPT09IFVOREVGX1RZUEUgPyBzdHIgOiBzdHIuc3Vic3RyaW5nKDAsIFVBX01BWF9MRU5HVEgpO1xuICAgICAgICAgICAgfVxuICAgIH07XG5cbiAgICAvLy8vLy8vLy8vLy8vLy9cbiAgICAvLyBNYXAgaGVscGVyXG4gICAgLy8vLy8vLy8vLy8vLy9cblxuICAgIHZhciByZ3hNYXBwZXIgPSBmdW5jdGlvbiAodWEsIGFycmF5cykge1xuXG4gICAgICAgICAgICB2YXIgaSA9IDAsIGosIGssIHAsIHEsIG1hdGNoZXMsIG1hdGNoO1xuXG4gICAgICAgICAgICAvLyBsb29wIHRocm91Z2ggYWxsIHJlZ2V4ZXMgbWFwc1xuICAgICAgICAgICAgd2hpbGUgKGkgPCBhcnJheXMubGVuZ3RoICYmICFtYXRjaGVzKSB7XG5cbiAgICAgICAgICAgICAgICB2YXIgcmVnZXggPSBhcnJheXNbaV0sICAgICAgIC8vIGV2ZW4gc2VxdWVuY2UgKDAsMiw0LC4uKVxuICAgICAgICAgICAgICAgICAgICBwcm9wcyA9IGFycmF5c1tpICsgMV07ICAgLy8gb2RkIHNlcXVlbmNlICgxLDMsNSwuLilcbiAgICAgICAgICAgICAgICBqID0gayA9IDA7XG5cbiAgICAgICAgICAgICAgICAvLyB0cnkgbWF0Y2hpbmcgdWFzdHJpbmcgd2l0aCByZWdleGVzXG4gICAgICAgICAgICAgICAgd2hpbGUgKGogPCByZWdleC5sZW5ndGggJiYgIW1hdGNoZXMpIHtcblxuICAgICAgICAgICAgICAgICAgICBpZiAoIXJlZ2V4W2pdKSB7IGJyZWFrOyB9XG4gICAgICAgICAgICAgICAgICAgIG1hdGNoZXMgPSByZWdleFtqKytdLmV4ZWModWEpO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmICghIW1hdGNoZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAocCA9IDA7IHAgPCBwcm9wcy5sZW5ndGg7IHArKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdGNoID0gbWF0Y2hlc1srK2tdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHEgPSBwcm9wc1twXTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaGVjayBpZiBnaXZlbiBwcm9wZXJ0eSBpcyBhY3R1YWxseSBhcnJheVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgcSA9PT0gT0JKX1RZUEUgJiYgcS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChxLmxlbmd0aCA9PT0gMikge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHR5cGVvZiBxWzFdID09IEZVTkNfVFlQRSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFzc2lnbiBtb2RpZmllZCBtYXRjaFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcVswXV0gPSBxWzFdLmNhbGwodGhpcywgbWF0Y2gpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBhc3NpZ24gZ2l2ZW4gdmFsdWUsIGlnbm9yZSByZWdleCBtYXRjaFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcVswXV0gPSBxWzFdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKHEubGVuZ3RoID09PSAzKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBjaGVjayB3aGV0aGVyIGZ1bmN0aW9uIG9yIHJlZ2V4XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHFbMV0gPT09IEZVTkNfVFlQRSAmJiAhKHFbMV0uZXhlYyAmJiBxWzFdLnRlc3QpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY2FsbCBmdW5jdGlvbiAodXN1YWxseSBzdHJpbmcgbWFwcGVyKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcVswXV0gPSBtYXRjaCA/IHFbMV0uY2FsbCh0aGlzLCBtYXRjaCwgcVsyXSkgOiB1bmRlZmluZWQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNhbml0aXplIG1hdGNoIHVzaW5nIGdpdmVuIHJlZ2V4XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpc1txWzBdXSA9IG1hdGNoID8gbWF0Y2gucmVwbGFjZShxWzFdLCBxWzJdKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIGlmIChxLmxlbmd0aCA9PT0gNCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcVswXV0gPSBtYXRjaCA/IHFbM10uY2FsbCh0aGlzLCBtYXRjaC5yZXBsYWNlKHFbMV0sIHFbMl0pKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXNbcV0gPSBtYXRjaCA/IG1hdGNoIDogdW5kZWZpbmVkO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpICs9IDI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG5cbiAgICAgICAgc3RyTWFwcGVyID0gZnVuY3Rpb24gKHN0ciwgbWFwKSB7XG5cbiAgICAgICAgICAgIGZvciAodmFyIGkgaW4gbWFwKSB7XG4gICAgICAgICAgICAgICAgLy8gY2hlY2sgaWYgY3VycmVudCB2YWx1ZSBpcyBhcnJheVxuICAgICAgICAgICAgICAgIGlmICh0eXBlb2YgbWFwW2ldID09PSBPQkpfVFlQRSAmJiBtYXBbaV0ubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IG1hcFtpXS5sZW5ndGg7IGorKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGhhcyhtYXBbaV1bal0sIHN0cikpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gKGkgPT09IFVOS05PV04pID8gdW5kZWZpbmVkIDogaTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoaGFzKG1hcFtpXSwgc3RyKSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm4gKGkgPT09IFVOS05PV04pID8gdW5kZWZpbmVkIDogaTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gc3RyO1xuICAgIH07XG5cbiAgICAvLy8vLy8vLy8vLy8vLy9cbiAgICAvLyBTdHJpbmcgbWFwXG4gICAgLy8vLy8vLy8vLy8vLy9cblxuICAgIC8vIFNhZmFyaSA8IDMuMFxuICAgIHZhciBvbGRTYWZhcmlNYXAgPSB7XG4gICAgICAgICAgICAnMS4wJyAgIDogJy84JyxcbiAgICAgICAgICAgICcxLjInICAgOiAnLzEnLFxuICAgICAgICAgICAgJzEuMycgICA6ICcvMycsXG4gICAgICAgICAgICAnMi4wJyAgIDogJy80MTInLFxuICAgICAgICAgICAgJzIuMC4yJyA6ICcvNDE2JyxcbiAgICAgICAgICAgICcyLjAuMycgOiAnLzQxNycsXG4gICAgICAgICAgICAnMi4wLjQnIDogJy80MTknLFxuICAgICAgICAgICAgJz8nICAgICA6ICcvJ1xuICAgICAgICB9LFxuICAgICAgICB3aW5kb3dzVmVyc2lvbk1hcCA9IHtcbiAgICAgICAgICAgICdNRScgICAgICAgIDogJzQuOTAnLFxuICAgICAgICAgICAgJ05UIDMuMTEnICAgOiAnTlQzLjUxJyxcbiAgICAgICAgICAgICdOVCA0LjAnICAgIDogJ05UNC4wJyxcbiAgICAgICAgICAgICcyMDAwJyAgICAgIDogJ05UIDUuMCcsXG4gICAgICAgICAgICAnWFAnICAgICAgICA6IFsnTlQgNS4xJywgJ05UIDUuMiddLFxuICAgICAgICAgICAgJ1Zpc3RhJyAgICAgOiAnTlQgNi4wJyxcbiAgICAgICAgICAgICc3JyAgICAgICAgIDogJ05UIDYuMScsXG4gICAgICAgICAgICAnOCcgICAgICAgICA6ICdOVCA2LjInLFxuICAgICAgICAgICAgJzguMScgICAgICAgOiAnTlQgNi4zJyxcbiAgICAgICAgICAgICcxMCcgICAgICAgIDogWydOVCA2LjQnLCAnTlQgMTAuMCddLFxuICAgICAgICAgICAgJ1JUJyAgICAgICAgOiAnQVJNJ1xuICAgIH07XG5cbiAgICAvLy8vLy8vLy8vLy8vL1xuICAgIC8vIFJlZ2V4IG1hcFxuICAgIC8vLy8vLy8vLy8vLy9cblxuICAgIHZhciByZWdleGVzID0ge1xuXG4gICAgICAgIGJyb3dzZXIgOiBbW1xuXG4gICAgICAgICAgICAvXFxiKD86Y3Jtb3xjcmlvcylcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDaHJvbWUgZm9yIEFuZHJvaWQvaU9TXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdDaHJvbWUnXV0sIFtcbiAgICAgICAgICAgIC9lZGcoPzplfGlvc3xhKT9cXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTWljcm9zb2Z0IEVkZ2VcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ0VkZ2UnXV0sIFtcblxuICAgICAgICAgICAgLy8gUHJlc3RvIGJhc2VkXG4gICAgICAgICAgICAvKG9wZXJhIG1pbmkpXFwvKFstXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9wZXJhIE1pbmlcbiAgICAgICAgICAgIC8ob3BlcmEgW21vYmlsZXRhYl17Myw2fSlcXGIuK3ZlcnNpb25cXC8oWy1cXHdcXC5dKykvaSwgICAgICAgICAgICAgICAgIC8vIE9wZXJhIE1vYmkvVGFibGV0XG4gICAgICAgICAgICAvKG9wZXJhKSg/Oi4rdmVyc2lvblxcL3xbXFwvIF0rKShbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBPcGVyYVxuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG4gICAgICAgICAgICAvb3Bpb3NbXFwvIF0rKFtcXHdcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9wZXJhIG1pbmkgb24gaXBob25lID49IDguMFxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBPUEVSQSsnIE1pbmknXV0sIFtcbiAgICAgICAgICAgIC9cXGJvcHJcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9wZXJhIFdlYmtpdFxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBPUEVSQV1dLCBbXG5cbiAgICAgICAgICAgIC8vIE1peGVkXG4gICAgICAgICAgICAvXFxiYlthaV0qZCg/OnVoZHxbdWJdKlthZWtvcHJzd3hdezUsNn0pW1xcLyBdPyhbXFx3XFwuXSspL2kgICAgICAgICAgICAvLyBCYWlkdVxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnQmFpZHUnXV0sIFtcbiAgICAgICAgICAgIC8oa2luZGxlKVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS2luZGxlXG4gICAgICAgICAgICAvKGx1bmFzY2FwZXxtYXh0aG9ufG5ldGZyb250fGphc21pbmV8YmxhemVyKVtcXC8gXT8oW1xcd1xcLl0qKS9pLCAgICAgIC8vIEx1bmFzY2FwZS9NYXh0aG9uL05ldGZyb250L0phc21pbmUvQmxhemVyXG4gICAgICAgICAgICAvLyBUcmlkZW50IGJhc2VkXG4gICAgICAgICAgICAvKGF2YW50fGllbW9iaWxlfHNsaW0pXFxzPyg/OmJyb3dzZXIpP1tcXC8gXT8oW1xcd1xcLl0qKS9pLCAgICAgICAgICAgICAvLyBBdmFudC9JRU1vYmlsZS9TbGltQnJvd3NlclxuICAgICAgICAgICAgLyg/Om1zfFxcKCkoaWUpIChbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBJbnRlcm5ldCBFeHBsb3JlclxuXG4gICAgICAgICAgICAvLyBXZWJraXQvS0hUTUwgYmFzZWQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZsb2NrL1JvY2tNZWx0L01pZG9yaS9FcGlwaGFueS9TaWxrL1NreWZpcmUvQm9sdC9Jcm9uL0lyaWRpdW0vUGhhbnRvbUpTL0Jvd3Nlci9RdXBaaWxsYS9GYWxrb25cbiAgICAgICAgICAgIC8oZmxvY2t8cm9ja21lbHR8bWlkb3JpfGVwaXBoYW55fHNpbGt8c2t5ZmlyZXxib2x0fGlyb258dml2YWxkaXxpcmlkaXVtfHBoYW50b21qc3xib3dzZXJ8cXVhcmt8cXVwemlsbGF8ZmFsa29ufHJla29ucXxwdWZmaW58YnJhdmV8d2hhbGUoPyEuK25hdmVyKXxxcWJyb3dzZXJsaXRlfHFxfGR1Y2tkdWNrZ28pXFwvKFstXFx3XFwuXSspL2ksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFJla29ucS9QdWZmaW4vQnJhdmUvV2hhbGUvUVFCcm93c2VyTGl0ZS9RUSwgYWthIFNob3VRXG4gICAgICAgICAgICAvKGhleXRhcHxvdmkpYnJvd3NlclxcLyhbXFxkXFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEhleXRhcC9PdmlcbiAgICAgICAgICAgIC8od2VpYm8pX18oW1xcZFxcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBXZWlib1xuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG4gICAgICAgICAgICAvKD86XFxidWM/ID9icm93c2VyfCg/Omp1Yy4rKXVjd2ViKVtcXC8gXT8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAvLyBVQ0Jyb3dzZXJcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ1VDJytCUk9XU0VSXV0sIFtcbiAgICAgICAgICAgIC9taWNyb20uK1xcYnFiY29yZVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFdlQ2hhdCBEZXNrdG9wIGZvciBXaW5kb3dzIEJ1aWx0LWluIEJyb3dzZXJcbiAgICAgICAgICAgIC9cXGJxYmNvcmVcXC8oW1xcd1xcLl0rKS4rbWljcm9tL2ksXG4gICAgICAgICAgICAvbWljcm9tZXNzZW5nZXJcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFdlQ2hhdFxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnV2VDaGF0J11dLCBbXG4gICAgICAgICAgICAva29ucXVlcm9yXFwvKFtcXHdcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEtvbnF1ZXJvclxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnS29ucXVlcm9yJ11dLCBbXG4gICAgICAgICAgICAvdHJpZGVudC4rcnZbOiBdKFtcXHdcXC5dezEsOX0pXFxiLitsaWtlIGdlY2tvL2kgICAgICAgICAgICAgICAgICAgICAgIC8vIElFMTFcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ0lFJ11dLCBbXG4gICAgICAgICAgICAveWEoPzpzZWFyY2gpP2Jyb3dzZXJcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFlhbmRleFxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnWWFuZGV4J11dLCBbXG4gICAgICAgICAgICAvc2xicm93c2VyXFwvKFtcXHdcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNtYXJ0IExlbm92byBCcm93c2VyXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdTbWFydCBMZW5vdm8gJytCUk9XU0VSXV0sIFtcbiAgICAgICAgICAgIC8oYXZhc3R8YXZnKVxcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQXZhc3QvQVZHIFNlY3VyZSBCcm93c2VyXG4gICAgICAgICAgICBdLCBbW05BTUUsIC8oLispLywgJyQxIFNlY3VyZSAnK0JST1dTRVJdLCBWRVJTSU9OXSwgW1xuICAgICAgICAgICAgL1xcYmZvY3VzXFwvKFtcXHdcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRmlyZWZveCBGb2N1c1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBGSVJFRk9YKycgRm9jdXMnXV0sIFtcbiAgICAgICAgICAgIC9cXGJvcHRcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9wZXJhIFRvdWNoXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsIE9QRVJBKycgVG91Y2gnXV0sIFtcbiAgICAgICAgICAgIC9jb2NfY29jXFx3K1xcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENvYyBDb2MgQnJvd3NlclxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnQ29jIENvYyddXSwgW1xuICAgICAgICAgICAgL2RvbGZpblxcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEb2xwaGluXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdEb2xwaGluJ11dLCBbXG4gICAgICAgICAgICAvY29hc3RcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9wZXJhIENvYXN0XG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsIE9QRVJBKycgQ29hc3QnXV0sIFtcbiAgICAgICAgICAgIC9taXVpYnJvd3NlclxcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTUlVSSBCcm93c2VyXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdNSVVJICcrQlJPV1NFUl1dLCBbXG4gICAgICAgICAgICAvZnhpb3NcXC8oWy1cXHdcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZpcmVmb3ggZm9yIGlPU1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBGSVJFRk9YXV0sIFtcbiAgICAgICAgICAgIC9cXGJxaWh1fChxaT9obz9vP3wzNjApYnJvd3Nlci9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIDM2MFxuICAgICAgICAgICAgXSwgW1tOQU1FLCAnMzYwICcgKyBCUk9XU0VSXV0sIFtcbiAgICAgICAgICAgIC8ob2N1bHVzfHNhaWxmaXNofGh1YXdlaXx2aXZvKWJyb3dzZXJcXC8oW1xcd1xcLl0rKS9pXG4gICAgICAgICAgICBdLCBbW05BTUUsIC8oLispLywgJyQxICcgKyBCUk9XU0VSXSwgVkVSU0lPTl0sIFsgICAgICAgICAgICAgICAgICAgIC8vIE9jdWx1cy9TYWlsZmlzaC9IdWF3ZWlCcm93c2VyL1Zpdm9Ccm93c2VyXG4gICAgICAgICAgICAvc2Ftc3VuZ2Jyb3dzZXJcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNhbXN1bmcgSW50ZXJuZXRcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgU0FNU1VORyArICcgSW50ZXJuZXQnXV0sIFtcbiAgICAgICAgICAgIC8oY29tb2RvX2RyYWdvbilcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ29tb2RvIERyYWdvblxuICAgICAgICAgICAgXSwgW1tOQU1FLCAvXy9nLCAnICddLCBWRVJTSU9OXSwgW1xuICAgICAgICAgICAgL21ldGFzcltcXC8gXT8oW1xcZFxcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTb2dvdSBFeHBsb3JlclxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnU29nb3UgRXhwbG9yZXInXV0sIFtcbiAgICAgICAgICAgIC8oc29nb3UpbW9cXHcrXFwvKFtcXGRcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNvZ291IE1vYmlsZVxuICAgICAgICAgICAgXSwgW1tOQU1FLCAnU29nb3UgTW9iaWxlJ10sIFZFUlNJT05dLCBbXG4gICAgICAgICAgICAvKGVsZWN0cm9uKVxcLyhbXFx3XFwuXSspIHNhZmFyaS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEVsZWN0cm9uLWJhc2VkIEFwcFxuICAgICAgICAgICAgLyh0ZXNsYSkoPzogcXRjYXJicm93c2VyfFxcLygyMFxcZFxcZFxcLlstXFx3XFwuXSspKS9pLCAgICAgICAgICAgICAgICAgICAvLyBUZXNsYVxuICAgICAgICAgICAgL20/KHFxYnJvd3NlcnwyMzQ1RXhwbG9yZXIpW1xcLyBdPyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAvLyBRUUJyb3dzZXIvMjM0NSBCcm93c2VyXG4gICAgICAgICAgICBdLCBbTkFNRSwgVkVSU0lPTl0sIFtcbiAgICAgICAgICAgIC8obGJicm93c2VyKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTGllQmFvIEJyb3dzZXJcbiAgICAgICAgICAgIC9cXFsobGlua2VkaW4pYXBwXFxdL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBMaW5rZWRJbiBBcHAgZm9yIGlPUyAmIEFuZHJvaWRcbiAgICAgICAgICAgIF0sIFtOQU1FXSwgW1xuXG4gICAgICAgICAgICAvLyBXZWJWaWV3XG4gICAgICAgICAgICAvKCg/OmZiYW5cXC9mYmlvc3xmYl9pYWJcXC9mYjRhKSg/IS4rZmJhdil8O2ZiYXZcXC8oW1xcd1xcLl0rKTspL2kgICAgICAgLy8gRmFjZWJvb2sgQXBwIGZvciBpT1MgJiBBbmRyb2lkXG4gICAgICAgICAgICBdLCBbW05BTUUsIEZBQ0VCT09LXSwgVkVSU0lPTl0sIFtcbiAgICAgICAgICAgIC8oS2xhcm5hKVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS2xhcm5hIFNob3BwaW5nIEJyb3dzZXIgZm9yIGlPUyAmIEFuZHJvaWRcbiAgICAgICAgICAgIC8oa2FrYW8oPzp0YWxrfHN0b3J5KSlbXFwvIF0oW1xcd1xcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS2FrYW8gQXBwXG4gICAgICAgICAgICAvKG5hdmVyKVxcKC4qPyhcXGQrXFwuW1xcd1xcLl0rKS4qXFwpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE5hdmVyIEluQXBwXG4gICAgICAgICAgICAvc2FmYXJpIChsaW5lKVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExpbmUgQXBwIGZvciBpT1NcbiAgICAgICAgICAgIC9cXGIobGluZSlcXC8oW1xcd1xcLl0rKVxcL2lhYi9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBMaW5lIEFwcCBmb3IgQW5kcm9pZFxuICAgICAgICAgICAgLyhhbGlwYXkpY2xpZW50XFwvKFtcXHdcXC5dKykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBbGlwYXlcbiAgICAgICAgICAgIC8oY2hyb21pdW18aW5zdGFncmFtfHNuYXBjaGF0KVtcXC8gXShbLVxcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgLy8gQ2hyb21pdW0vSW5zdGFncmFtL1NuYXBjaGF0XG4gICAgICAgICAgICBdLCBbTkFNRSwgVkVSU0lPTl0sIFtcbiAgICAgICAgICAgIC9cXGJnc2FcXC8oW1xcd1xcLl0rKSAuKnNhZmFyaVxcLy9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHb29nbGUgU2VhcmNoIEFwcGxpYW5jZSBvbiBpT1NcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ0dTQSddXSwgW1xuICAgICAgICAgICAgL211c2ljYWxfbHkoPzouK2FwcF8/dmVyc2lvblxcL3xfKShbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAvLyBUaWtUb2tcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ1Rpa1RvayddXSwgW1xuXG4gICAgICAgICAgICAvaGVhZGxlc3NjaHJvbWUoPzpcXC8oW1xcd1xcLl0rKXwgKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIENocm9tZSBIZWFkbGVzc1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBDSFJPTUUrJyBIZWFkbGVzcyddXSwgW1xuXG4gICAgICAgICAgICAvIHd2XFwpLisoY2hyb21lKVxcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDaHJvbWUgV2ViVmlld1xuICAgICAgICAgICAgXSwgW1tOQU1FLCBDSFJPTUUrJyBXZWJWaWV3J10sIFZFUlNJT05dLCBbXG5cbiAgICAgICAgICAgIC9kcm9pZC4rIHZlcnNpb25cXC8oW1xcd1xcLl0rKVxcYi4rKD86bW9iaWxlIHNhZmFyaXxzYWZhcmkpL2kgICAgICAgICAgIC8vIEFuZHJvaWQgQnJvd3NlclxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnQW5kcm9pZCAnK0JST1dTRVJdXSwgW1xuXG4gICAgICAgICAgICAvKGNocm9tZXxvbW5pd2VifGFyb3JhfFt0aXplbm9rYV17NX0gP2Jyb3dzZXIpXFwvdj8oW1xcd1xcLl0rKS9pICAgICAgIC8vIENocm9tZS9PbW5pV2ViL0Fyb3JhL1RpemVuL05va2lhXG4gICAgICAgICAgICBdLCBbTkFNRSwgVkVSU0lPTl0sIFtcblxuICAgICAgICAgICAgL3ZlcnNpb25cXC8oW1xcd1xcLlxcLF0rKSAuKm1vYmlsZVxcL1xcdysgKHNhZmFyaSkvaSAgICAgICAgICAgICAgICAgICAgICAvLyBNb2JpbGUgU2FmYXJpXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdNb2JpbGUgU2FmYXJpJ11dLCBbXG4gICAgICAgICAgICAvdmVyc2lvblxcLyhbXFx3KFxcLnxcXCwpXSspIC4qKG1vYmlsZSA/c2FmYXJpfHNhZmFyaSkvaSAgICAgICAgICAgICAgICAvLyBTYWZhcmkgJiBTYWZhcmkgTW9iaWxlXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgTkFNRV0sIFtcbiAgICAgICAgICAgIC93ZWJraXQuKz8obW9iaWxlID9zYWZhcml8c2FmYXJpKShcXC9bXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgLy8gU2FmYXJpIDwgMy4wXG4gICAgICAgICAgICBdLCBbTkFNRSwgW1ZFUlNJT04sIHN0ck1hcHBlciwgb2xkU2FmYXJpTWFwXV0sIFtcblxuICAgICAgICAgICAgLyh3ZWJraXR8a2h0bWwpXFwvKFtcXHdcXC5dKykvaVxuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG5cbiAgICAgICAgICAgIC8vIEdlY2tvIGJhc2VkXG4gICAgICAgICAgICAvKG5hdmlnYXRvcnxuZXRzY2FwZVxcZD8pXFwvKFstXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBOZXRzY2FwZVxuICAgICAgICAgICAgXSwgW1tOQU1FLCAnTmV0c2NhcGUnXSwgVkVSU0lPTl0sIFtcbiAgICAgICAgICAgIC9tb2JpbGUgdnI7IHJ2OihbXFx3XFwuXSspXFwpLitmaXJlZm94L2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRmlyZWZveCBSZWFsaXR5XG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsIEZJUkVGT1grJyBSZWFsaXR5J11dLCBbXG4gICAgICAgICAgICAvZWtpb2hmLisoZmxvdylcXC8oW1xcd1xcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZsb3dcbiAgICAgICAgICAgIC8oc3dpZnRmb3gpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU3dpZnRmb3hcbiAgICAgICAgICAgIC8oaWNlZHJhZ29ufGljZXdlYXNlbHxjYW1pbm98Y2hpbWVyYXxmZW5uZWN8bWFlbW8gYnJvd3NlcnxtaW5pbW98Y29ua2Vyb3J8a2xhcilbXFwvIF0/KFtcXHdcXC5cXCtdKykvaSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSWNlRHJhZ29uL0ljZXdlYXNlbC9DYW1pbm8vQ2hpbWVyYS9GZW5uZWMvTWFlbW8vTWluaW1vL0Nvbmtlcm9yL0tsYXJcbiAgICAgICAgICAgIC8oc2VhbW9ua2V5fGstbWVsZW9ufGljZWNhdHxpY2VhcGV8ZmlyZWJpcmR8cGhvZW5peHxwYWxlbW9vbnxiYXNpbGlza3x3YXRlcmZveClcXC8oWy1cXHdcXC5dKykkL2ksXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZpcmVmb3gvU2VhTW9ua2V5L0stTWVsZW9uL0ljZUNhdC9JY2VBcGUvRmlyZWJpcmQvUGhvZW5peFxuICAgICAgICAgICAgLyhmaXJlZm94KVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBPdGhlciBGaXJlZm94LWJhc2VkXG4gICAgICAgICAgICAvKG1vemlsbGEpXFwvKFtcXHdcXC5dKykgLitydlxcOi4rZ2Vja29cXC9cXGQrL2ksICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE1vemlsbGFcblxuICAgICAgICAgICAgLy8gT3RoZXJcbiAgICAgICAgICAgIC8ocG9sYXJpc3xseW54fGRpbGxvfGljYWJ8ZG9yaXN8YW1heWF8dzNtfG5ldHN1cmZ8c2xlaXBuaXJ8b2JpZ298bW9zYWljfCg/OmdvfGljZXx1cClbXFwuIF0/YnJvd3NlcilbLVxcLyBdP3Y/KFtcXHdcXC5dKykvaSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUG9sYXJpcy9MeW54L0RpbGxvL2lDYWIvRG9yaXMvQW1heWEvdzNtL05ldFN1cmYvU2xlaXBuaXIvT2JpZ28vTW9zYWljL0dvL0lDRS9VUC5Ccm93c2VyXG4gICAgICAgICAgICAvKGxpbmtzKSBcXCgoW1xcd1xcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIExpbmtzXG4gICAgICAgICAgICAvcGFuYXNvbmljOyh2aWVyYSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBhbmFzb25pYyBWaWVyYVxuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG4gICAgICAgICAgICBcbiAgICAgICAgICAgIC8oY29iYWx0KVxcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQ29iYWx0XG4gICAgICAgICAgICBdLCBbTkFNRSwgW1ZFUlNJT04sIC9tYXN0ZXIufGx0cy4vLCBcIlwiXV1cbiAgICAgICAgXSxcblxuICAgICAgICBjcHUgOiBbW1xuXG4gICAgICAgICAgICAvKD86KGFtZHx4KD86KD86ODZ8NjQpWy1fXSk/fHdvd3x3aW4pNjQpWztcXCldL2kgICAgICAgICAgICAgICAgICAgICAvLyBBTUQ2NCAoeDY0KVxuICAgICAgICAgICAgXSwgW1tBUkNISVRFQ1RVUkUsICdhbWQ2NCddXSwgW1xuXG4gICAgICAgICAgICAvKGlhMzIoPz07KSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElBMzIgKHF1aWNrdGltZSlcbiAgICAgICAgICAgIF0sIFtbQVJDSElURUNUVVJFLCBsb3dlcml6ZV1dLCBbXG5cbiAgICAgICAgICAgIC8oKD86aVszNDZdfHgpODYpWztcXCldL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElBMzIgKHg4NilcbiAgICAgICAgICAgIF0sIFtbQVJDSElURUNUVVJFLCAnaWEzMiddXSwgW1xuXG4gICAgICAgICAgICAvXFxiKGFhcmNoNjR8YXJtKHY/OGU/bD98Xz82NCkpXFxiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBUk02NFxuICAgICAgICAgICAgXSwgW1tBUkNISVRFQ1RVUkUsICdhcm02NCddXSwgW1xuXG4gICAgICAgICAgICAvXFxiKGFybSg/OnZbNjddKT9odD9uP1tmbF1wPylcXGIvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQVJNSEZcbiAgICAgICAgICAgIF0sIFtbQVJDSElURUNUVVJFLCAnYXJtaGYnXV0sIFtcblxuICAgICAgICAgICAgLy8gUG9ja2V0UEMgbWlzdGFrZW5seSBpZGVudGlmaWVkIGFzIFBvd2VyUENcbiAgICAgICAgICAgIC93aW5kb3dzIChjZXxtb2JpbGUpOyBwcGM7L2lcbiAgICAgICAgICAgIF0sIFtbQVJDSElURUNUVVJFLCAnYXJtJ11dLCBbXG5cbiAgICAgICAgICAgIC8oKD86cHBjfHBvd2VycGMpKD86NjQpPykoPzogbWFjfDt8XFwpKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBvd2VyUENcbiAgICAgICAgICAgIF0sIFtbQVJDSElURUNUVVJFLCAvb3dlci8sIEVNUFRZLCBsb3dlcml6ZV1dLCBbXG5cbiAgICAgICAgICAgIC8oc3VuNFxcdylbO1xcKV0vaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTUEFSQ1xuICAgICAgICAgICAgXSwgW1tBUkNISVRFQ1RVUkUsICdzcGFyYyddXSwgW1xuXG4gICAgICAgICAgICAvKCg/OmF2cjMyfGlhNjQoPz07KSl8NjhrKD89XFwpKXxcXGJhcm0oPz12KD86WzEtN118WzUtN10xKWw/fDt8ZWFiaSl8KD89YXRtZWwgKWF2cnwoPzppcml4fG1pcHN8c3BhcmMpKD86NjQpP1xcYnxwYS1yaXNjKS9pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIElBNjQsIDY4SywgQVJNLzY0LCBBVlIvMzIsIElSSVgvNjQsIE1JUFMvNjQsIFNQQVJDLzY0LCBQQS1SSVNDXG4gICAgICAgICAgICBdLCBbW0FSQ0hJVEVDVFVSRSwgbG93ZXJpemVdXVxuICAgICAgICBdLFxuXG4gICAgICAgIGRldmljZSA6IFtbXG5cbiAgICAgICAgICAgIC8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vXG4gICAgICAgICAgICAvLyBNT0JJTEVTICYgVEFCTEVUU1xuICAgICAgICAgICAgLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gICAgICAgICAgICAvLyBTYW1zdW5nXG4gICAgICAgICAgICAvXFxiKHNjaC1pWzg5XTBcXGR8c2h3LW0zODBzfHNtLVtwdHhdXFx3ezIsNH18Z3QtW3BuXVxcZHsyLDR9fHNnaC10OFs1Nl05fG5leHVzIDEwKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIFNBTVNVTkddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIoKD86c1tjZ3BdaHxndHxzbSktXFx3K3xzY1tnLV0/W1xcZF0rYT98Z2FsYXh5IG5leHVzKS9pLFxuICAgICAgICAgICAgL3NhbXN1bmdbLSBdKFstXFx3XSspL2ksXG4gICAgICAgICAgICAvc2VjLShzZ2hcXHcrKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIFNBTVNVTkddLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLy8gQXBwbGVcbiAgICAgICAgICAgIC8oPzpcXC98XFwoKShpcCg/OmhvbmV8b2QpW1xcdywgXSopKD86XFwvfDspL2kgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlQb2QvaVBob25lXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEFQUExFXSwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAvXFwoKGlwYWQpO1stXFx3XFwpLDsgXSthcHBsZS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlQYWRcbiAgICAgICAgICAgIC9hcHBsZWNvcmVtZWRpYVxcL1tcXHdcXC5dKyBcXCgoaXBhZCkvaSxcbiAgICAgICAgICAgIC9cXGIoaXBhZClcXGRcXGQ/LFxcZFxcZD9bO1xcXV0uK2lvcy9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEFQUExFXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvKG1hY2ludG9zaCk7L2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgQVBQTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBTaGFycFxuICAgICAgICAgICAgL1xcYihzaC0/W2FsdHZ6XT9cXGRcXGRbYS1la21dPykvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBTSEFSUF0sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBIdWF3ZWlcbiAgICAgICAgICAgIC9cXGIoKD86YWdbcnNdWzIzXT98YmFoMj98c2h0P3xidHYpLWE/W2x3XVxcZHsyfSlcXGIoPyEuK2RcXC9zKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEhVQVdFSV0sIFtUWVBFLCBUQUJMRVRdXSwgW1xuICAgICAgICAgICAgLyg/Omh1YXdlaXxob25vcikoWy1cXHcgXSspWztcXCldL2ksXG4gICAgICAgICAgICAvXFxiKG5leHVzIDZwfFxcd3syLDR9ZT8tW2F0dV0/W2xuXVtcXGR4XVswMTIzNTljXVthZG5dPylcXGIoPyEuK2RcXC9zKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEhVQVdFSV0sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBYaWFvbWlcbiAgICAgICAgICAgIC9cXGIocG9jb1tcXHcgXSt8bTJcXGR7M31qXFxkXFxkW2Etel17Mn0pKD86IGJ1aXxcXCkpL2ksICAgICAgICAgICAgICAgICAgLy8gWGlhb21pIFBPQ09cbiAgICAgICAgICAgIC9cXGI7IChcXHcrKSBidWlsZFxcL2htXFwxL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFhpYW9taSBIb25nbWkgJ251bWVyaWMnIG1vZGVsc1xuICAgICAgICAgICAgL1xcYihobVstXyBdP25vdGU/W18gXT8oPzpcXGRcXHcpPykgYnVpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBYaWFvbWkgSG9uZ21pXG4gICAgICAgICAgICAvXFxiKHJlZG1pW1xcLV8gXT8oPzpub3RlfGspP1tcXHdfIF0rKSg/OiBidWl8XFwpKS9pLCAgICAgICAgICAgICAgICAgICAvLyBYaWFvbWkgUmVkbWlcbiAgICAgICAgICAgIC9vaWRbXlxcKV0rOyAobT9bMTJdWzAtMzg5XVswMV1cXHd7Myw2fVtjLXldKSggYnVpfDsgd3Z8XFwpKS9pLCAgICAgICAgLy8gWGlhb21pIFJlZG1pICdudW1lcmljJyBtb2RlbHNcbiAgICAgICAgICAgIC9cXGIobWlbLV8gXT8oPzphXFxkfG9uZXxvbmVbXyBdcGx1c3xub3RlIGx0ZXxtYXh8Y2MpP1tfIF0/KD86XFxkP1xcdz8pW18gXT8oPzpwbHVzfHNlfGxpdGUpPykoPzogYnVpfFxcKSkvaSAvLyBYaWFvbWkgTWlcbiAgICAgICAgICAgIF0sIFtbTU9ERUwsIC9fL2csICcgJ10sIFtWRU5ET1IsIFhJQU9NSV0sIFtUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgL29pZFteXFwpXSs7ICgyXFxkezR9KDI4M3xycGJmKVtjZ2xdKSggYnVpfFxcKSkvaSwgICAgICAgICAgICAgICAgICAgICAvLyBSZWRtaSBQYWRcbiAgICAgICAgICAgIC9cXGIobWlbLV8gXT8oPzpwYWQpKD86W1xcd18gXSspKSg/OiBidWl8XFwpKS9pICAgICAgICAgICAgICAgICAgICAgICAgLy8gTWkgUGFkIHRhYmxldHNcbiAgICAgICAgICAgIF0sW1tNT0RFTCwgL18vZywgJyAnXSwgW1ZFTkRPUiwgWElBT01JXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG5cbiAgICAgICAgICAgIC8vIE9QUE9cbiAgICAgICAgICAgIC87IChcXHcrKSBidWkuKyBvcHBvL2ksXG4gICAgICAgICAgICAvXFxiKGNwaFsxMl1cXGR7M318cCg/OmFmfGNbYWxdfGRcXHd8ZVthcl0pW210XVxcZDB8eDkwMDd8YTEwMW9wKVxcYi9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdPUFBPJ10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBWaXZvXG4gICAgICAgICAgICAvdml2byAoXFx3KykoPzogYnVpfFxcKSkvaSxcbiAgICAgICAgICAgIC9cXGIodlsxMl1cXGR7M31cXHc/W2F0XSkoPzogYnVpfDspL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1Zpdm8nXSwgW1RZUEUsIE1PQklMRV1dLCBbXG5cbiAgICAgICAgICAgIC8vIFJlYWxtZVxuICAgICAgICAgICAgL1xcYihybXhbMS0zXVxcZHszfSkoPzogYnVpfDt8XFwpKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdSZWFsbWUnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG5cbiAgICAgICAgICAgIC8vIE1vdG9yb2xhXG4gICAgICAgICAgICAvXFxiKG1pbGVzdG9uZXxkcm9pZCg/OlsyLTR4XXwgKD86YmlvbmljfHgyfHByb3xyYXpyKSk/Oj8oIDRnKT8pXFxiW1xcdyBdK2J1aWxkXFwvL2ksXG4gICAgICAgICAgICAvXFxibW90KD86b3JvbGEpP1stIF0oXFx3KikvaSxcbiAgICAgICAgICAgIC8oKD86bW90b1tcXHdcXChcXCkgXSt8eHRcXGR7Myw0fXxuZXh1cyA2KSg/PSBidWl8XFwpKSkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBNT1RPUk9MQV0sIFtUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgL1xcYihtejYwXFxkfHhvb21bMiBdezAsMn0pIGJ1aWxkXFwvL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgTU9UT1JPTEFdLCBbVFlQRSwgVEFCTEVUXV0sIFtcblxuICAgICAgICAgICAgLy8gTEdcbiAgICAgICAgICAgIC8oKD89bGcpP1t2bF1rXFwtP1xcZHszfSkgYnVpfCAzXFwuWy1cXHc7IF17MTB9bGc/LShbMDZjdjldezMsNH0pL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgTEddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC8obG0oPzotP2YxMDBbbnZdP3wtW1xcd1xcLl0rKSg/PSBidWl8XFwpKXxuZXh1cyBbNDVdKS9pLFxuICAgICAgICAgICAgL1xcYmxnWy1lO1xcLyBdKygoPyFicm93c2VyfG5ldGNhc3R8YW5kcm9pZCB0dilcXHcrKS9pLFxuICAgICAgICAgICAgL1xcYmxnLT8oW1xcZFxcd10rKSBidWkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBMR10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBMZW5vdm9cbiAgICAgICAgICAgIC8oaWRlYXRhYlstXFx3IF0rKS9pLFxuICAgICAgICAgICAgL2xlbm92byA/KHNbNTZdMDAwWy1cXHddK3x0YWIoPzpbXFx3IF0rKXx5dFstXFxkXFx3XXs2fXx0YlstXFxkXFx3XXs2fSkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnTGVub3ZvJ10sIFtUWVBFLCBUQUJMRVRdXSwgW1xuXG4gICAgICAgICAgICAvLyBOb2tpYVxuICAgICAgICAgICAgLyg/Om1hZW1vfG5va2lhKS4qKG45MDB8bHVtaWEgXFxkKykvaSxcbiAgICAgICAgICAgIC9ub2tpYVstXyBdPyhbLVxcd1xcLl0qKS9pXG4gICAgICAgICAgICBdLCBbW01PREVMLCAvXy9nLCAnICddLCBbVkVORE9SLCAnTm9raWEnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG5cbiAgICAgICAgICAgIC8vIEdvb2dsZVxuICAgICAgICAgICAgLyhwaXhlbCBjKVxcYi9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR29vZ2xlIFBpeGVsIENcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgR09PR0xFXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvZHJvaWQuKzsgKHBpeGVsW1xcZGF4bCBdezAsNn0pKD86IGJ1aXxcXCkpL2kgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR29vZ2xlIFBpeGVsXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEdPT0dMRV0sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBTb255XG4gICAgICAgICAgICAvZHJvaWQuKyAoYT9cXGRbMC0yXXsyfXNvfFtjLWddXFxkezR9fHNvWy1nbF1cXHcrfHhxLWFcXHdbNC03XVsxMl0pKD89IGJ1aXxcXCkuK2Nocm9tZVxcLyg/IVsxLTZdezAsMX1cXGRcXC4pKS9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIFNPTlldLCBbVFlQRSwgTU9CSUxFXV0sIFtcbiAgICAgICAgICAgIC9zb255IHRhYmxldCBbcHNdL2ksXG4gICAgICAgICAgICAvXFxiKD86c29ueSk/c2dwXFx3Kyg/OiBidWl8XFwpKS9pXG4gICAgICAgICAgICBdLCBbW01PREVMLCAnWHBlcmlhIFRhYmxldCddLCBbVkVORE9SLCBTT05ZXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG5cbiAgICAgICAgICAgIC8vIE9uZVBsdXNcbiAgICAgICAgICAgIC8gKGtiMjAwNXxpbjIwWzEyXTV8YmUyMFsxMl1bNTldKVxcYi9pLFxuICAgICAgICAgICAgLyg/Om9uZSk/KD86cGx1cyk/IChhXFxkMFxcZFxcZCkoPzogYnxcXCkpL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ09uZVBsdXMnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG5cbiAgICAgICAgICAgIC8vIEFtYXpvblxuICAgICAgICAgICAgLyhhbGV4YSl3ZWJtL2ksXG4gICAgICAgICAgICAvKGtmW2Etel17Mn13aXxhZW9bYy1yXXsyfSkoIGJ1aXxcXCkpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBLaW5kbGUgRmlyZSB3aXRob3V0IFNpbGsgLyBFY2hvIFNob3dcbiAgICAgICAgICAgIC8oa2ZbYS16XSspKCBidWl8XFwpKS4rc2lsa1xcLy9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBLaW5kbGUgRmlyZSBIRFxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBBTUFaT05dLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC8oKD86c2R8a2YpWzAzNDloaWpvcnN0dXddKykoIGJ1aXxcXCkpLitzaWxrXFwvL2kgICAgICAgICAgICAgICAgICAgICAvLyBGaXJlIFBob25lXG4gICAgICAgICAgICBdLCBbW01PREVMLCAvKC4rKS9nLCAnRmlyZSBQaG9uZSAkMSddLCBbVkVORE9SLCBBTUFaT05dLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLy8gQmxhY2tCZXJyeVxuICAgICAgICAgICAgLyhwbGF5Ym9vayk7Wy1cXHdcXCksOyBdKyhyaW0pL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEJsYWNrQmVycnkgUGxheUJvb2tcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgVkVORE9SLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIoKD86YmJbYS1mXXxzdFtodl0pMTAwLVxcZCkvaSxcbiAgICAgICAgICAgIC9cXChiYjEwOyAoXFx3KykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBCbGFja0JlcnJ5IDEwXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEJMQUNLQkVSUlldLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLy8gQXN1c1xuICAgICAgICAgICAgLyg/OlxcYnxhc3VzXykodHJhbnNmb1twcmltZSBdezQsMTB9IFxcdyt8ZWVlcGN8c2xpZGVyIFxcdyt8bmV4dXMgN3xwYWRmb25lfHAwMFtjal0pL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgQVNVU10sIFtUWVBFLCBUQUJMRVRdXSwgW1xuICAgICAgICAgICAgLyAoeltiZXNdNlswMjddWzAxMl1ba21dW2xzXXx6ZW5mb25lIFxcZFxcdz8pXFxiL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgQVNVU10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBIVENcbiAgICAgICAgICAgIC8obmV4dXMgOSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSFRDIE5leHVzIDlcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ0hUQyddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC8oaHRjKVstO18gXXsxLDJ9KFtcXHcgXSsoPz1cXCl8IGJ1aSl8XFx3KykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSFRDXG5cbiAgICAgICAgICAgIC8vIFpURVxuICAgICAgICAgICAgLyh6dGUpWy0gXShbXFx3IF0rPykoPzogYnVpfFxcL3xcXCkpL2ksXG4gICAgICAgICAgICAvKGFsY2F0ZWx8Z2Vla3NwaG9uZXxuZXhpYW58cGFuYXNvbmljKD8hKD86O3xcXC4pKXxzb255KD8hLWJyYSkpWy1fIF0/KFstXFx3XSopL2kgICAgICAgICAvLyBBbGNhdGVsL0dlZWtzUGhvbmUvTmV4aWFuL1BhbmFzb25pYy9Tb255XG4gICAgICAgICAgICBdLCBbVkVORE9SLCBbTU9ERUwsIC9fL2csICcgJ10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuXG4gICAgICAgICAgICAvLyBBY2VyXG4gICAgICAgICAgICAvZHJvaWQuKzsgKFthYl1bMS03XS0/WzAxNzhhXVxcZFxcZD8pL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ0FjZXInXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG5cbiAgICAgICAgICAgIC8vIE1laXp1XG4gICAgICAgICAgICAvZHJvaWQuKzsgKG1bMS01XSBub3RlKSBidWkvaSxcbiAgICAgICAgICAgIC9cXGJtei0oWy1cXHddezIsfSkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnTWVpenUnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAgICAgXG4gICAgICAgICAgICAvLyBVbGVmb25lXG4gICAgICAgICAgICAvOyAoKD86cG93ZXIgKT9hcm1vcig/OltcXHcgXXswLDh9KSkoPzogYnVpfFxcKSkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnVWxlZm9uZSddLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLy8gTUlYRURcbiAgICAgICAgICAgIC8oYmxhY2tiZXJyeXxiZW5xfHBhbG0oPz1cXC0pfHNvbnllcmljc3NvbnxhY2VyfGFzdXN8ZGVsbHxtZWl6dXxtb3Rvcm9sYXxwb2x5dHJvbnxpbmZpbml4fHRlY25vKVstXyBdPyhbLVxcd10qKS9pLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBCbGFja0JlcnJ5L0JlblEvUGFsbS9Tb255LUVyaWNzc29uL0FjZXIvQXN1cy9EZWxsL01laXp1L01vdG9yb2xhL1BvbHl0cm9uXG4gICAgICAgICAgICAvKGhwKSAoW1xcdyBdK1xcdykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSFAgaVBBUVxuICAgICAgICAgICAgLyhhc3VzKS0/KFxcdyspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQXN1c1xuICAgICAgICAgICAgLyhtaWNyb3NvZnQpOyAobHVtaWFbXFx3IF0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTWljcm9zb2Z0IEx1bWlhXG4gICAgICAgICAgICAvKGxlbm92bylbLV8gXT8oWy1cXHddKykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBMZW5vdm9cbiAgICAgICAgICAgIC8oam9sbGEpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSm9sbGFcbiAgICAgICAgICAgIC8ob3BwbykgPyhbXFx3IF0rKSBidWkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9QUE9cbiAgICAgICAgICAgIF0sIFtWRU5ET1IsIE1PREVMLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLyhrb2JvKVxccyhlcmVhZGVyfHRvdWNoKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS29ib1xuICAgICAgICAgICAgLyhhcmNob3MpIChnYW1lcGFkMj8pL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBcmNob3NcbiAgICAgICAgICAgIC8oaHApLisodG91Y2hwYWQoPyEuK3RhYmxldCl8dGFibGV0KS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSFAgVG91Y2hQYWRcbiAgICAgICAgICAgIC8oa2luZGxlKVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gS2luZGxlXG4gICAgICAgICAgICAvKG5vb2spW1xcdyBdK2J1aWxkXFwvKFxcdyspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE5vb2tcbiAgICAgICAgICAgIC8oZGVsbCkgKHN0cmVhW2twclxcZCBdKltcXGRrb10pL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEZWxsIFN0cmVha1xuICAgICAgICAgICAgLyhsZVstIF0rcGFuKVstIF0rKFxcd3sxLDl9KSBidWkvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTGUgUGFuIFRhYmxldHNcbiAgICAgICAgICAgIC8odHJpbml0eSlbLSBdKih0XFxkezN9KSBidWkvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFRyaW5pdHkgVGFibGV0c1xuICAgICAgICAgICAgLyhnaWdhc2V0KVstIF0rKHFcXHd7MSw5fSkgYnVpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR2lnYXNldCBUYWJsZXRzXG4gICAgICAgICAgICAvKHZvZGFmb25lKSAoW1xcdyBdKykoPzpcXCl8IGJ1aSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVm9kYWZvbmVcbiAgICAgICAgICAgIF0sIFtWRU5ET1IsIE1PREVMLCBbVFlQRSwgVEFCTEVUXV0sIFtcblxuICAgICAgICAgICAgLyhzdXJmYWNlIGR1bykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTdXJmYWNlIER1b1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBNSUNST1NPRlRdLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9kcm9pZCBbXFxkXFwuXSs7IChmcFxcZHU/KSg/OiBifFxcKSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZhaXJwaG9uZVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnRmFpcnBob25lJ10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgLyh1MzA0YWEpL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBVCZUXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdBVCZUJ10sIFtUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgL1xcYnNpZS0oXFx3KikvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNpZW1lbnNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1NpZW1lbnMnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAvXFxiKHJjdFxcdyspIGIvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUkNBIFRhYmxldHNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1JDQSddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIodmVudWVbXFxkIF17Miw3fSkgYi9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEZWxsIFZlbnVlIFRhYmxldHNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ0RlbGwnXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKHEoPzptdnx0YSlcXHcrKSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVmVyaXpvbiBUYWJsZXRcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1Zlcml6b24nXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKD86YmFybmVzWyYgXStub2JsZSB8Ym5bcnRdKShbXFx3XFwrIF0qKSBiL2kgICAgICAgICAgICAgICAgICAgICAgIC8vIEJhcm5lcyAmIE5vYmxlIFRhYmxldFxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnQmFybmVzICYgTm9ibGUnXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKHRtXFxkezN9XFx3KykgYi9pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdOdVZpc2lvbiddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIoazg4KSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFpURSBLIFNlcmllcyBUYWJsZXRcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1pURSddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIobnhcXGR7M31qKSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBaVEUgTnViaWFcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1pURSddLCBbVFlQRSwgTU9CSUxFXV0sIFtcbiAgICAgICAgICAgIC9cXGIoZ2VuXFxkezN9KSBiLis0OWgvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTd2lzcyBHRU4gTW9iaWxlXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdTd2lzcyddLCBbVFlQRSwgTU9CSUxFXV0sIFtcbiAgICAgICAgICAgIC9cXGIoenVyXFxkezN9KSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTd2lzcyBaVVIgVGFibGV0XG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdTd2lzcyddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC9cXGIoKHpla2kpP3RiLipcXGIpIGIvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBaZWtpIFRhYmxldHNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ1pla2knXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKFt5cl1cXGR7Mn0pIGIvaSxcbiAgICAgICAgICAgIC9cXGIoZHJhZ29uWy0gXSt0b3VjaCB8ZHQpKFxcd3s1fSkgYi9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBEcmFnb24gVG91Y2ggVGFibGV0XG4gICAgICAgICAgICBdLCBbW1ZFTkRPUiwgJ0RyYWdvbiBUb3VjaCddLCBNT0RFTCwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKG5zLT9cXHd7MCw5fSkgYi9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gSW5zaWduaWEgVGFibGV0c1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnSW5zaWduaWEnXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKChueGF8bmV4dCktP1xcd3swLDl9KSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTmV4dEJvb2sgVGFibGV0c1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnTmV4dEJvb2snXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKHh0cmVtZVxcXyk/KHYoMVswNDVdfDJbMDE1XXxbMzQ2OV0wfDdbMDVdKSkgYi9pICAgICAgICAgICAgICAgICAgLy8gVm9pY2UgWHRyZW1lIFBob25lc1xuICAgICAgICAgICAgXSwgW1tWRU5ET1IsICdWb2ljZSddLCBNT0RFTCwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAvXFxiKGx2dGVsXFwtKT8odjFbMTJdKSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTHZUZWwgUGhvbmVzXG4gICAgICAgICAgICBdLCBbW1ZFTkRPUiwgJ0x2VGVsJ10sIE1PREVMLCBbVFlQRSwgTU9CSUxFXV0sIFtcbiAgICAgICAgICAgIC9cXGIocGgtMSkgL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEVzc2VudGlhbCBQSC0xXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdFc3NlbnRpYWwnXSwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAvXFxiKHYoMTAwbWR8NzAwbmF8NzAxMXw5MTdnKS4qXFxiKSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRW52aXplbiBUYWJsZXRzXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsICdFbnZpemVuJ10sIFtUWVBFLCBUQUJMRVRdXSwgW1xuICAgICAgICAgICAgL1xcYih0cmlvWy1cXHdcXC4gXSspIGIvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBNYWNoU3BlZWQgVGFibGV0c1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnTWFjaFNwZWVkJ10sIFtUWVBFLCBUQUJMRVRdXSwgW1xuICAgICAgICAgICAgL1xcYnR1XygxNDkxKSBiL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUm90b3IgVGFibGV0c1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnUm90b3InXSwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvKHNoaWVsZFtcXHcgXSspIGIvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBOdmlkaWEgU2hpZWxkIFRhYmxldHNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ052aWRpYSddLCBbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC8oc3ByaW50KSAoXFx3KykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNwcmludCBQaG9uZXNcbiAgICAgICAgICAgIF0sIFtWRU5ET1IsIE1PREVMLCBbVFlQRSwgTU9CSUxFXV0sIFtcbiAgICAgICAgICAgIC8oa2luXFwuW29uZXR3XXszfSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE1pY3Jvc29mdCBLaW5cbiAgICAgICAgICAgIF0sIFtbTU9ERUwsIC9cXC4vZywgJyAnXSwgW1ZFTkRPUiwgTUlDUk9TT0ZUXSwgW1RZUEUsIE1PQklMRV1dLCBbXG4gICAgICAgICAgICAvZHJvaWQuKzsgKGNjNjY2Nj98ZXQ1WzE2XXxtY1syMzldWzIzXXg/fHZjOFswM114PylcXCkvaSAgICAgICAgICAgICAvLyBaZWJyYVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBaRUJSQV0sIFtUWVBFLCBUQUJMRVRdXSwgW1xuICAgICAgICAgICAgL2Ryb2lkLis7IChlYzMwfHBzMjB8dGNbMi04XVxcZFtreF0pXFwpL2lcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgWkVCUkFdLCBbVFlQRSwgTU9CSUxFXV0sIFtcblxuICAgICAgICAgICAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAgICAgICAgICAgLy8gU01BUlRUVlNcbiAgICAgICAgICAgIC8vLy8vLy8vLy8vLy8vLy8vLy9cblxuICAgICAgICAgICAgL3NtYXJ0LXR2Lisoc2Ftc3VuZykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBTYW1zdW5nXG4gICAgICAgICAgICBdLCBbVkVORE9SLCBbVFlQRSwgU01BUlRUVl1dLCBbXG4gICAgICAgICAgICAvaGJidHYuK21hcGxlOyhcXGQrKS9pXG4gICAgICAgICAgICBdLCBbW01PREVMLCAvXi8sICdTbWFydFRWJ10sIFtWRU5ET1IsIFNBTVNVTkddLCBbVFlQRSwgU01BUlRUVl1dLCBbXG4gICAgICAgICAgICAvKG51eDsgbmV0Y2FzdC4rc21hcnR0dnxsZyAobmV0Y2FzdFxcLnR2LTIwMVxcZHxhbmRyb2lkIHR2KSkvaSAgICAgICAgLy8gTEcgU21hcnRUVlxuICAgICAgICAgICAgXSwgW1tWRU5ET1IsIExHXSwgW1RZUEUsIFNNQVJUVFZdXSwgW1xuICAgICAgICAgICAgLyhhcHBsZSkgP3R2L2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBcHBsZSBUVlxuICAgICAgICAgICAgXSwgW1ZFTkRPUiwgW01PREVMLCBBUFBMRSsnIFRWJ10sIFtUWVBFLCBTTUFSVFRWXV0sIFtcbiAgICAgICAgICAgIC9jcmtleS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR29vZ2xlIENocm9tZWNhc3RcbiAgICAgICAgICAgIF0sIFtbTU9ERUwsIENIUk9NRSsnY2FzdCddLCBbVkVORE9SLCBHT09HTEVdLCBbVFlQRSwgU01BUlRUVl1dLCBbXG4gICAgICAgICAgICAvZHJvaWQuK2FmdChcXHcrKSggYnVpfFxcKSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRmlyZSBUVlxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBBTUFaT05dLCBbVFlQRSwgU01BUlRUVl1dLCBbXG4gICAgICAgICAgICAvXFwoZHR2W1xcKTtdLisoYXF1b3MpL2ksXG4gICAgICAgICAgICAvKGFxdW9zLXR2W1xcdyBdKylcXCkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU2hhcnBcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgU0hBUlBdLCBbVFlQRSwgU01BUlRUVl1dLFtcbiAgICAgICAgICAgIC8oYnJhdmlhW1xcdyBdKykoIGJ1aXxcXCkpL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU29ueVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBTT05ZXSwgW1RZUEUsIFNNQVJUVFZdXSwgW1xuICAgICAgICAgICAgLyhtaXR2LVxcd3s1fSkgYnVpL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gWGlhb21pXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIFhJQU9NSV0sIFtUWVBFLCBTTUFSVFRWXV0sIFtcbiAgICAgICAgICAgIC9IYmJ0di4qKHRlY2huaXNhdCkgKC4qKTsvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGVjaG5pU0FUXG4gICAgICAgICAgICBdLCBbVkVORE9SLCBNT0RFTCwgW1RZUEUsIFNNQVJUVFZdXSwgW1xuICAgICAgICAgICAgL1xcYihyb2t1KVtcXGR4XSpbXFwpXFwvXSgoPzpkdnAtKT9bXFxkXFwuXSopL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBSb2t1XG4gICAgICAgICAgICAvaGJidHZcXC9cXGQrXFwuXFxkK1xcLlxcZCsgK1xcKFtcXHdcXCsgXSo7ICooW1xcd1xcZF1bXjtdKik7KFteO10qKS9pICAgICAgICAgLy8gSGJiVFYgZGV2aWNlc1xuICAgICAgICAgICAgXSwgW1tWRU5ET1IsIHRyaW1dLCBbTU9ERUwsIHRyaW1dLCBbVFlQRSwgU01BUlRUVl1dLCBbXG4gICAgICAgICAgICAvXFxiKGFuZHJvaWQgdHZ8c21hcnRbLSBdP3R2fG9wZXJhIHR2fHR2OyBydjopXFxiL2kgICAgICAgICAgICAgICAgICAgLy8gU21hcnRUViBmcm9tIFVuaWRlbnRpZmllZCBWZW5kb3JzXG4gICAgICAgICAgICBdLCBbW1RZUEUsIFNNQVJUVFZdXSwgW1xuXG4gICAgICAgICAgICAvLy8vLy8vLy8vLy8vLy8vLy8vXG4gICAgICAgICAgICAvLyBDT05TT0xFU1xuICAgICAgICAgICAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gICAgICAgICAgICAvKG91eWEpL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE91eWFcbiAgICAgICAgICAgIC8obmludGVuZG8pIChbd2lkczN1dGNoXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTmludGVuZG9cbiAgICAgICAgICAgIF0sIFtWRU5ET1IsIE1PREVMLCBbVFlQRSwgQ09OU09MRV1dLCBbXG4gICAgICAgICAgICAvZHJvaWQuKzsgKHNoaWVsZCkgYnVpL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE52aWRpYVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCAnTnZpZGlhJ10sIFtUWVBFLCBDT05TT0xFXV0sIFtcbiAgICAgICAgICAgIC8ocGxheXN0YXRpb24gWzM0NXBvcnRhYmxldmldKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUGxheXN0YXRpb25cbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgU09OWV0sIFtUWVBFLCBDT05TT0xFXV0sIFtcbiAgICAgICAgICAgIC9cXGIoeGJveCg/OiBvbmUpPyg/ITsgeGJveCkpW1xcKTsgXS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBNaWNyb3NvZnQgWGJveFxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBNSUNST1NPRlRdLCBbVFlQRSwgQ09OU09MRV1dLCBbXG5cbiAgICAgICAgICAgIC8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgICAgICAgICAgIC8vIFdFQVJBQkxFU1xuICAgICAgICAgICAgLy8vLy8vLy8vLy8vLy8vLy8vL1xuXG4gICAgICAgICAgICAvKChwZWJibGUpKWFwcC9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBlYmJsZVxuICAgICAgICAgICAgXSwgW1ZFTkRPUiwgTU9ERUwsIFtUWVBFLCBXRUFSQUJMRV1dLCBbXG4gICAgICAgICAgICAvKHdhdGNoKSg/OiA/b3NbLFxcL118XFxkLFxcZFxcLylbXFxkXFwuXSsvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEFwcGxlIFdhdGNoXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtWRU5ET1IsIEFQUExFXSwgW1RZUEUsIFdFQVJBQkxFXV0sIFtcbiAgICAgICAgICAgIC9kcm9pZC4rOyAoZ2xhc3MpIFxcZC9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEdvb2dsZSBHbGFzc1xuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBHT09HTEVdLCBbVFlQRSwgV0VBUkFCTEVdXSwgW1xuICAgICAgICAgICAgL2Ryb2lkLis7ICh3dDYzPzB7MiwzfSlcXCkvaVxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBaRUJSQV0sIFtUWVBFLCBXRUFSQUJMRV1dLCBbXG4gICAgICAgICAgICAvKHF1ZXN0KCAyfCBwcm8pPykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE9jdWx1cyBRdWVzdFxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBGQUNFQk9PS10sIFtUWVBFLCBXRUFSQUJMRV1dLCBbXG5cbiAgICAgICAgICAgIC8vLy8vLy8vLy8vLy8vLy8vLy9cbiAgICAgICAgICAgIC8vIEVNQkVEREVEXG4gICAgICAgICAgICAvLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgICAgICAgICAgIC8odGVzbGEpKD86IHF0Y2FyYnJvd3NlcnxcXC9bLVxcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGVzbGFcbiAgICAgICAgICAgIF0sIFtWRU5ET1IsIFtUWVBFLCBFTUJFRERFRF1dLCBbXG4gICAgICAgICAgICAvKGFlb2JjKVxcYi9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBFY2hvIERvdFxuICAgICAgICAgICAgXSwgW01PREVMLCBbVkVORE9SLCBBTUFaT05dLCBbVFlQRSwgRU1CRURERURdXSwgW1xuXG4gICAgICAgICAgICAvLy8vLy8vLy8vLy8vLy8vLy8vL1xuICAgICAgICAgICAgLy8gTUlYRUQgKEdFTkVSSUMpXG4gICAgICAgICAgICAvLy8vLy8vLy8vLy8vLy8vLy8vXG5cbiAgICAgICAgICAgIC9kcm9pZCAuKz87IChbXjtdKz8pKD86IGJ1aXw7IHd2XFwpfFxcKSBhcHBsZXcpLis/IG1vYmlsZSBzYWZhcmkvaSAgICAvLyBBbmRyb2lkIFBob25lcyBmcm9tIFVuaWRlbnRpZmllZCBWZW5kb3JzXG4gICAgICAgICAgICBdLCBbTU9ERUwsIFtUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgL2Ryb2lkIC4rPzsgKFteO10rPykoPzogYnVpfFxcKSBhcHBsZXcpLis/KD8hIG1vYmlsZSkgc2FmYXJpL2kgICAgICAgLy8gQW5kcm9pZCBUYWJsZXRzIGZyb20gVW5pZGVudGlmaWVkIFZlbmRvcnNcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1RZUEUsIFRBQkxFVF1dLCBbXG4gICAgICAgICAgICAvXFxiKCh0YWJsZXR8dGFiKVs7XFwvXXxmb2N1c1xcL1xcZCg/IS4rbW9iaWxlKSkvaSAgICAgICAgICAgICAgICAgICAgICAvLyBVbmlkZW50aWZpYWJsZSBUYWJsZXRcbiAgICAgICAgICAgIF0sIFtbVFlQRSwgVEFCTEVUXV0sIFtcbiAgICAgICAgICAgIC8ocGhvbmV8bW9iaWxlKD86WztcXC9dfCBbIFxcd1xcL1xcLl0qc2FmYXJpKXxwZGEoPz0uK3dpbmRvd3MgY2UpKS9pICAgIC8vIFVuaWRlbnRpZmlhYmxlIE1vYmlsZVxuICAgICAgICAgICAgXSwgW1tUWVBFLCBNT0JJTEVdXSwgW1xuICAgICAgICAgICAgLyhhbmRyb2lkWy1cXHdcXC4gXXswLDl9KTsuK2J1aWwvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEdlbmVyaWMgQW5kcm9pZCBEZXZpY2VcbiAgICAgICAgICAgIF0sIFtNT0RFTCwgW1ZFTkRPUiwgJ0dlbmVyaWMnXV1cbiAgICAgICAgXSxcblxuICAgICAgICBlbmdpbmUgOiBbW1xuXG4gICAgICAgICAgICAvd2luZG93cy4rIGVkZ2VcXC8oW1xcd1xcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gRWRnZUhUTUxcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgRURHRSsnSFRNTCddXSwgW1xuXG4gICAgICAgICAgICAvd2Via2l0XFwvNTM3XFwuMzYuK2Nocm9tZVxcLyg/ITI3KShbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQmxpbmtcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBbTkFNRSwgJ0JsaW5rJ11dLCBbXG5cbiAgICAgICAgICAgIC8ocHJlc3RvKVxcLyhbXFx3XFwuXSspL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUHJlc3RvXG4gICAgICAgICAgICAvKHdlYmtpdHx0cmlkZW50fG5ldGZyb250fG5ldHN1cmZ8YW1heWF8bHlueHx3M218Z29hbm5hKVxcLyhbXFx3XFwuXSspL2ksIC8vIFdlYktpdC9UcmlkZW50L05ldEZyb250L05ldFN1cmYvQW1heWEvTHlueC93M20vR29hbm5hXG4gICAgICAgICAgICAvZWtpb2goZmxvdylcXC8oW1xcd1xcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEZsb3dcbiAgICAgICAgICAgIC8oa2h0bWx8dGFzbWFufGxpbmtzKVtcXC8gXVxcKD8oW1xcd1xcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEtIVE1ML1Rhc21hbi9MaW5rc1xuICAgICAgICAgICAgLyhpY2FiKVtcXC8gXShbMjNdXFwuW1xcZFxcLl0rKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gaUNhYlxuICAgICAgICAgICAgL1xcYihsaWJ3ZWIpL2lcbiAgICAgICAgICAgIF0sIFtOQU1FLCBWRVJTSU9OXSwgW1xuXG4gICAgICAgICAgICAvcnZcXDooW1xcd1xcLl17MSw5fSlcXGIuKyhnZWNrbykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHZWNrb1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIE5BTUVdXG4gICAgICAgIF0sXG5cbiAgICAgICAgb3MgOiBbW1xuXG4gICAgICAgICAgICAvLyBXaW5kb3dzXG4gICAgICAgICAgICAvbWljcm9zb2Z0ICh3aW5kb3dzKSAodmlzdGF8eHApL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFdpbmRvd3MgKGlUdW5lcylcbiAgICAgICAgICAgIF0sIFtOQU1FLCBWRVJTSU9OXSwgW1xuICAgICAgICAgICAgLyh3aW5kb3dzICg/OnBob25lKD86IG9zKT98bW9iaWxlKSlbXFwvIF0/KFtcXGRcXC5cXHcgXSopL2kgICAgICAgICAgICAgLy8gV2luZG93cyBQaG9uZVxuICAgICAgICAgICAgXSwgW05BTUUsIFtWRVJTSU9OLCBzdHJNYXBwZXIsIHdpbmRvd3NWZXJzaW9uTWFwXV0sIFtcbiAgICAgICAgICAgIC93aW5kb3dzIG50IDZcXC4yOyAoYXJtKS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBXaW5kb3dzIFJUXG4gICAgICAgICAgICAvd2luZG93c1tcXC8gXT8oW250Y2VcXGRcXC4gXStcXHcpKD8hLit4Ym94KS9pLFxuICAgICAgICAgICAgLyg/Ondpbig/PTN8OXxuKXx3aW4gOXggKShbbnRcXGRcXC5dKykvaVxuICAgICAgICAgICAgXSwgW1tWRVJTSU9OLCBzdHJNYXBwZXIsIHdpbmRvd3NWZXJzaW9uTWFwXSwgW05BTUUsICdXaW5kb3dzJ11dLCBbXG5cbiAgICAgICAgICAgIC8vIGlPUy9tYWNPU1xuICAgICAgICAgICAgL2lwW2hvbmVhZF17Miw0fVxcYig/Oi4qb3MgKFtcXHddKykgbGlrZSBtYWN8OyBvcGVyYSkvaSwgICAgICAgICAgICAgIC8vIGlPU1xuICAgICAgICAgICAgLyg/OmlvcztmYnN2XFwvfGlwaG9uZS4raW9zW1xcLyBdKShbXFxkXFwuXSspL2ksXG4gICAgICAgICAgICAvY2ZuZXR3b3JrXFwvLitkYXJ3aW4vaVxuICAgICAgICAgICAgXSwgW1tWRVJTSU9OLCAvXy9nLCAnLiddLCBbTkFNRSwgJ2lPUyddXSwgW1xuICAgICAgICAgICAgLyhtYWMgb3MgeCkgPyhbXFx3XFwuIF0qKS9pLFxuICAgICAgICAgICAgLyhtYWNpbnRvc2h8bWFjX3Bvd2VycGNcXGIpKD8hLitoYWlrdSkvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTWFjIE9TXG4gICAgICAgICAgICBdLCBbW05BTUUsIE1BQ19PU10sIFtWRVJTSU9OLCAvXy9nLCAnLiddXSwgW1xuXG4gICAgICAgICAgICAvLyBNb2JpbGUgT1Nlc1xuICAgICAgICAgICAgL2Ryb2lkIChbXFx3XFwuXSspXFxiLisoYW5kcm9pZFstIF14ODZ8aGFybW9ueW9zKS9pICAgICAgICAgICAgICAgICAgICAvLyBBbmRyb2lkLXg4Ni9IYXJtb255T1NcbiAgICAgICAgICAgIF0sIFtWRVJTSU9OLCBOQU1FXSwgWyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gQW5kcm9pZC9XZWJPUy9RTlgvQmFkYS9SSU0vTWFlbW8vTWVlR28vU2FpbGZpc2ggT1NcbiAgICAgICAgICAgIC8oYW5kcm9pZHx3ZWJvc3xxbnh8YmFkYXxyaW0gdGFibGV0IG9zfG1hZW1vfG1lZWdvfHNhaWxmaXNoKVstXFwvIF0/KFtcXHdcXC5dKikvaSxcbiAgICAgICAgICAgIC8oYmxhY2tiZXJyeSlcXHcqXFwvKFtcXHdcXC5dKikvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEJsYWNrYmVycnlcbiAgICAgICAgICAgIC8odGl6ZW58a2Fpb3MpW1xcLyBdKFtcXHdcXC5dKykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGl6ZW4vS2FpT1NcbiAgICAgICAgICAgIC9cXCgoc2VyaWVzNDApOy9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNlcmllcyA0MFxuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG4gICAgICAgICAgICAvXFwoYmIoMTApOy9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBCbGFja0JlcnJ5IDEwXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsIEJMQUNLQkVSUlldXSwgW1xuICAgICAgICAgICAgLyg/OnN5bWJpYW4gP29zfHN5bWJvc3xzNjAoPz07KXxzZXJpZXM2MClbLVxcLyBdPyhbXFx3XFwuXSopL2kgICAgICAgICAvLyBTeW1iaWFuXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsICdTeW1iaWFuJ11dLCBbXG4gICAgICAgICAgICAvbW96aWxsYVxcL1tcXGRcXC5dKyBcXCgoPzptb2JpbGV8dGFibGV0fHR2fG1vYmlsZTsgW1xcdyBdKyk7IHJ2Oi4rIGdlY2tvXFwvKFtcXHdcXC5dKykvaSAvLyBGaXJlZm94IE9TXG4gICAgICAgICAgICBdLCBbVkVSU0lPTiwgW05BTUUsIEZJUkVGT1grJyBPUyddXSwgW1xuICAgICAgICAgICAgL3dlYjBzOy4rcnQodHYpL2ksXG4gICAgICAgICAgICAvXFxiKD86aHApP3dvcyg/OmJyb3dzZXIpP1xcLyhbXFx3XFwuXSspL2kgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBXZWJPU1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnd2ViT1MnXV0sIFtcbiAgICAgICAgICAgIC93YXRjaCg/OiA/b3NbLFxcL118XFxkLFxcZFxcLykoW1xcZFxcLl0rKS9pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gd2F0Y2hPU1xuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCAnd2F0Y2hPUyddXSwgW1xuXG4gICAgICAgICAgICAvLyBHb29nbGUgQ2hyb21lY2FzdFxuICAgICAgICAgICAgL2Nya2V5XFwvKFtcXGRcXC5dKykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBHb29nbGUgQ2hyb21lY2FzdFxuICAgICAgICAgICAgXSwgW1ZFUlNJT04sIFtOQU1FLCBDSFJPTUUrJ2Nhc3QnXV0sIFtcbiAgICAgICAgICAgIC8oY3JvcykgW1xcd10rKD86XFwpfCAoW1xcd1xcLl0rKVxcYikvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDaHJvbWl1bSBPU1xuICAgICAgICAgICAgXSwgW1tOQU1FLCBDSFJPTUlVTV9PU10sIFZFUlNJT05dLFtcblxuICAgICAgICAgICAgLy8gU21hcnQgVFZzXG4gICAgICAgICAgICAvcGFuYXNvbmljOyh2aWVyYSkvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFBhbmFzb25pYyBWaWVyYVxuICAgICAgICAgICAgLyhuZXRyYW5nZSltbWgvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBOZXRyYW5nZVxuICAgICAgICAgICAgLyhuZXR0dilcXC8oXFxkK1xcLltcXHdcXC5dKykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE5ldFRWXG5cbiAgICAgICAgICAgIC8vIENvbnNvbGVcbiAgICAgICAgICAgIC8obmludGVuZG98cGxheXN0YXRpb24pIChbd2lkczM0NXBvcnRhYmxldnVjaF0rKS9pLCAgICAgICAgICAgICAgICAgLy8gTmludGVuZG8vUGxheXN0YXRpb25cbiAgICAgICAgICAgIC8oeGJveCk7ICt4Ym94IChbXlxcKTtdKykvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIE1pY3Jvc29mdCBYYm94ICgzNjAsIE9uZSwgWCwgUywgU2VyaWVzIFgsIFNlcmllcyBTKVxuXG4gICAgICAgICAgICAvLyBPdGhlclxuICAgICAgICAgICAgL1xcYihqb2xpfHBhbG0pXFxiID8oPzpvcyk/XFwvPyhbXFx3XFwuXSopL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEpvbGkvUGFsbVxuICAgICAgICAgICAgLyhtaW50KVtcXC9cXChcXCkgXT8oXFx3KikvaSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTWludFxuICAgICAgICAgICAgLyhtYWdlaWF8dmVjdG9ybGludXgpWzsgXS9pLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBNYWdlaWEvVmVjdG9yTGludXhcbiAgICAgICAgICAgIC8oW2t4bG5dP3VidW50dXxkZWJpYW58c3VzZXxvcGVuc3VzZXxnZW50b298YXJjaCg/PSBsaW51eCl8c2xhY2t3YXJlfGZlZG9yYXxtYW5kcml2YXxjZW50b3N8cGNsaW51eG9zfHJlZCA/aGF0fHplbndhbGt8bGlucHVzfHJhc3BiaWFufHBsYW4gOXxtaW5peHxyaXNjIG9zfGNvbnRpa2l8ZGVlcGlufG1hbmphcm98ZWxlbWVudGFyeSBvc3xzYWJheW9ufGxpbnNwaXJlKSg/OiBnbnVcXC9saW51eCk/KD86IGVudGVycHJpc2UpPyg/OlstIF1saW51eCk/KD86LWdudSk/Wy1cXC8gXT8oPyFjaHJvbXxwYWNrYWdlKShbLVxcd1xcLl0qKS9pLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBVYnVudHUvRGViaWFuL1NVU0UvR2VudG9vL0FyY2gvU2xhY2t3YXJlL0ZlZG9yYS9NYW5kcml2YS9DZW50T1MvUENMaW51eE9TL1JlZEhhdC9aZW53YWxrL0xpbnB1cy9SYXNwYmlhbi9QbGFuOS9NaW5peC9SSVNDT1MvQ29udGlraS9EZWVwaW4vTWFuamFyby9lbGVtZW50YXJ5L1NhYmF5b24vTGluc3BpcmVcbiAgICAgICAgICAgIC8oaHVyZHxsaW51eCkgPyhbXFx3XFwuXSopL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBIdXJkL0xpbnV4XG4gICAgICAgICAgICAvKGdudSkgPyhbXFx3XFwuXSopL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gR05VXG4gICAgICAgICAgICAvXFxiKFstZnJlbnRvcGNnaHNdezAsNX1ic2R8ZHJhZ29uZmx5KVtcXC8gXT8oPyFhbWR8W2l4MzQ2XXsxLDJ9ODYpKFtcXHdcXC5dKikvaSwgLy8gRnJlZUJTRC9OZXRCU0QvT3BlbkJTRC9QQy1CU0QvR2hvc3RCU0QvRHJhZ29uRmx5XG4gICAgICAgICAgICAvKGhhaWt1KSAoXFx3KykvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBIYWlrdVxuICAgICAgICAgICAgXSwgW05BTUUsIFZFUlNJT05dLCBbXG4gICAgICAgICAgICAvKHN1bm9zKSA/KFtcXHdcXC5cXGRdKikvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFNvbGFyaXNcbiAgICAgICAgICAgIF0sIFtbTkFNRSwgJ1NvbGFyaXMnXSwgVkVSU0lPTl0sIFtcbiAgICAgICAgICAgIC8oKD86b3Blbik/c29sYXJpcylbLVxcLyBdPyhbXFx3XFwuXSopL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gU29sYXJpc1xuICAgICAgICAgICAgLyhhaXgpICgoXFxkKSg/PVxcLnxcXCl8IClbXFx3XFwuXSkqL2ksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIEFJWFxuICAgICAgICAgICAgL1xcYihiZW9zfG9zXFwvMnxhbWlnYW9zfG1vcnBob3N8b3BlbnZtc3xmdWNoc2lhfGhwLXV4fHNlcmVuaXR5b3MpL2ksIC8vIEJlT1MvT1MyL0FtaWdhT1MvTW9ycGhPUy9PcGVuVk1TL0Z1Y2hzaWEvSFAtVVgvU2VyZW5pdHlPU1xuICAgICAgICAgICAgLyh1bml4KSA/KFtcXHdcXC5dKikvaSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIFVOSVhcbiAgICAgICAgICAgIF0sIFtOQU1FLCBWRVJTSU9OXVxuICAgICAgICBdXG4gICAgfTtcblxuICAgIC8vLy8vLy8vLy8vLy8vLy8vXG4gICAgLy8gQ29uc3RydWN0b3JcbiAgICAvLy8vLy8vLy8vLy8vLy8vXG5cbiAgICB2YXIgVUFQYXJzZXIgPSBmdW5jdGlvbiAodWEsIGV4dGVuc2lvbnMpIHtcblxuICAgICAgICBpZiAodHlwZW9mIHVhID09PSBPQkpfVFlQRSkge1xuICAgICAgICAgICAgZXh0ZW5zaW9ucyA9IHVhO1xuICAgICAgICAgICAgdWEgPSB1bmRlZmluZWQ7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoISh0aGlzIGluc3RhbmNlb2YgVUFQYXJzZXIpKSB7XG4gICAgICAgICAgICByZXR1cm4gbmV3IFVBUGFyc2VyKHVhLCBleHRlbnNpb25zKS5nZXRSZXN1bHQoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHZhciBfbmF2aWdhdG9yID0gKHR5cGVvZiB3aW5kb3cgIT09IFVOREVGX1RZUEUgJiYgd2luZG93Lm5hdmlnYXRvcikgPyB3aW5kb3cubmF2aWdhdG9yIDogdW5kZWZpbmVkO1xuICAgICAgICB2YXIgX3VhID0gdWEgfHwgKChfbmF2aWdhdG9yICYmIF9uYXZpZ2F0b3IudXNlckFnZW50KSA/IF9uYXZpZ2F0b3IudXNlckFnZW50IDogRU1QVFkpO1xuICAgICAgICB2YXIgX3VhY2ggPSAoX25hdmlnYXRvciAmJiBfbmF2aWdhdG9yLnVzZXJBZ2VudERhdGEpID8gX25hdmlnYXRvci51c2VyQWdlbnREYXRhIDogdW5kZWZpbmVkO1xuICAgICAgICB2YXIgX3JneG1hcCA9IGV4dGVuc2lvbnMgPyBleHRlbmQocmVnZXhlcywgZXh0ZW5zaW9ucykgOiByZWdleGVzO1xuICAgICAgICB2YXIgX2lzU2VsZk5hdiA9IF9uYXZpZ2F0b3IgJiYgX25hdmlnYXRvci51c2VyQWdlbnQgPT0gX3VhO1xuXG4gICAgICAgIHRoaXMuZ2V0QnJvd3NlciA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHZhciBfYnJvd3NlciA9IHt9O1xuICAgICAgICAgICAgX2Jyb3dzZXJbTkFNRV0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICBfYnJvd3NlcltWRVJTSU9OXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIHJneE1hcHBlci5jYWxsKF9icm93c2VyLCBfdWEsIF9yZ3htYXAuYnJvd3Nlcik7XG4gICAgICAgICAgICBfYnJvd3NlcltNQUpPUl0gPSBtYWpvcml6ZShfYnJvd3NlcltWRVJTSU9OXSk7XG4gICAgICAgICAgICAvLyBCcmF2ZS1zcGVjaWZpYyBkZXRlY3Rpb25cbiAgICAgICAgICAgIGlmIChfaXNTZWxmTmF2ICYmIF9uYXZpZ2F0b3IgJiYgX25hdmlnYXRvci5icmF2ZSAmJiB0eXBlb2YgX25hdmlnYXRvci5icmF2ZS5pc0JyYXZlID09IEZVTkNfVFlQRSkge1xuICAgICAgICAgICAgICAgIF9icm93c2VyW05BTUVdID0gJ0JyYXZlJztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybiBfYnJvd3NlcjtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXRDUFUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgX2NwdSA9IHt9O1xuICAgICAgICAgICAgX2NwdVtBUkNISVRFQ1RVUkVdID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgcmd4TWFwcGVyLmNhbGwoX2NwdSwgX3VhLCBfcmd4bWFwLmNwdSk7XG4gICAgICAgICAgICByZXR1cm4gX2NwdTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXREZXZpY2UgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgX2RldmljZSA9IHt9O1xuICAgICAgICAgICAgX2RldmljZVtWRU5ET1JdID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgX2RldmljZVtNT0RFTF0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICBfZGV2aWNlW1RZUEVdID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgcmd4TWFwcGVyLmNhbGwoX2RldmljZSwgX3VhLCBfcmd4bWFwLmRldmljZSk7XG4gICAgICAgICAgICBpZiAoX2lzU2VsZk5hdiAmJiAhX2RldmljZVtUWVBFXSAmJiBfdWFjaCAmJiBfdWFjaC5tb2JpbGUpIHtcbiAgICAgICAgICAgICAgICBfZGV2aWNlW1RZUEVdID0gTU9CSUxFO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgLy8gaVBhZE9TLXNwZWNpZmljIGRldGVjdGlvbjogaWRlbnRpZmllZCBhcyBNYWMsIGJ1dCBoYXMgc29tZSBpT1Mtb25seSBwcm9wZXJ0aWVzXG4gICAgICAgICAgICBpZiAoX2lzU2VsZk5hdiAmJiBfZGV2aWNlW01PREVMXSA9PSAnTWFjaW50b3NoJyAmJiBfbmF2aWdhdG9yICYmIHR5cGVvZiBfbmF2aWdhdG9yLnN0YW5kYWxvbmUgIT09IFVOREVGX1RZUEUgJiYgX25hdmlnYXRvci5tYXhUb3VjaFBvaW50cyAmJiBfbmF2aWdhdG9yLm1heFRvdWNoUG9pbnRzID4gMikge1xuICAgICAgICAgICAgICAgIF9kZXZpY2VbTU9ERUxdID0gJ2lQYWQnO1xuICAgICAgICAgICAgICAgIF9kZXZpY2VbVFlQRV0gPSBUQUJMRVQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gX2RldmljZTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXRFbmdpbmUgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICB2YXIgX2VuZ2luZSA9IHt9O1xuICAgICAgICAgICAgX2VuZ2luZVtOQU1FXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIF9lbmdpbmVbVkVSU0lPTl0gPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICByZ3hNYXBwZXIuY2FsbChfZW5naW5lLCBfdWEsIF9yZ3htYXAuZW5naW5lKTtcbiAgICAgICAgICAgIHJldHVybiBfZW5naW5lO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmdldE9TID0gZnVuY3Rpb24gKCkge1xuICAgICAgICAgICAgdmFyIF9vcyA9IHt9O1xuICAgICAgICAgICAgX29zW05BTUVdID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgX29zW1ZFUlNJT05dID0gdW5kZWZpbmVkO1xuICAgICAgICAgICAgcmd4TWFwcGVyLmNhbGwoX29zLCBfdWEsIF9yZ3htYXAub3MpO1xuICAgICAgICAgICAgaWYgKF9pc1NlbGZOYXYgJiYgIV9vc1tOQU1FXSAmJiBfdWFjaCAmJiBfdWFjaC5wbGF0Zm9ybSAhPSAnVW5rbm93bicpIHtcbiAgICAgICAgICAgICAgICBfb3NbTkFNRV0gPSBfdWFjaC5wbGF0Zm9ybSAgXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgvY2hyb21lIG9zL2ksIENIUk9NSVVNX09TKVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoL21hY29zL2ksIE1BQ19PUyk7ICAgICAgICAgICAvLyBiYWNrd2FyZCBjb21wYXRpYmlsaXR5XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICByZXR1cm4gX29zO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLmdldFJlc3VsdCA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgdWEgICAgICA6IHRoaXMuZ2V0VUEoKSxcbiAgICAgICAgICAgICAgICBicm93c2VyIDogdGhpcy5nZXRCcm93c2VyKCksXG4gICAgICAgICAgICAgICAgZW5naW5lICA6IHRoaXMuZ2V0RW5naW5lKCksXG4gICAgICAgICAgICAgICAgb3MgICAgICA6IHRoaXMuZ2V0T1MoKSxcbiAgICAgICAgICAgICAgICBkZXZpY2UgIDogdGhpcy5nZXREZXZpY2UoKSxcbiAgICAgICAgICAgICAgICBjcHUgICAgIDogdGhpcy5nZXRDUFUoKVxuICAgICAgICAgICAgfTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5nZXRVQSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICAgIHJldHVybiBfdWE7XG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuc2V0VUEgPSBmdW5jdGlvbiAodWEpIHtcbiAgICAgICAgICAgIF91YSA9ICh0eXBlb2YgdWEgPT09IFNUUl9UWVBFICYmIHVhLmxlbmd0aCA+IFVBX01BWF9MRU5HVEgpID8gdHJpbSh1YSwgVUFfTUFYX0xFTkdUSCkgOiB1YTtcbiAgICAgICAgICAgIHJldHVybiB0aGlzO1xuICAgICAgICB9O1xuICAgICAgICB0aGlzLnNldFVBKF91YSk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH07XG5cbiAgICBVQVBhcnNlci5WRVJTSU9OID0gTElCVkVSU0lPTjtcbiAgICBVQVBhcnNlci5CUk9XU0VSID0gIGVudW1lcml6ZShbTkFNRSwgVkVSU0lPTiwgTUFKT1JdKTtcbiAgICBVQVBhcnNlci5DUFUgPSBlbnVtZXJpemUoW0FSQ0hJVEVDVFVSRV0pO1xuICAgIFVBUGFyc2VyLkRFVklDRSA9IGVudW1lcml6ZShbTU9ERUwsIFZFTkRPUiwgVFlQRSwgQ09OU09MRSwgTU9CSUxFLCBTTUFSVFRWLCBUQUJMRVQsIFdFQVJBQkxFLCBFTUJFRERFRF0pO1xuICAgIFVBUGFyc2VyLkVOR0lORSA9IFVBUGFyc2VyLk9TID0gZW51bWVyaXplKFtOQU1FLCBWRVJTSU9OXSk7XG5cbiAgICAvLy8vLy8vLy8vL1xuICAgIC8vIEV4cG9ydFxuICAgIC8vLy8vLy8vLy9cblxuICAgIC8vIGNoZWNrIGpzIGVudmlyb25tZW50XG4gICAgaWYgKHR5cGVvZihleHBvcnRzKSAhPT0gVU5ERUZfVFlQRSkge1xuICAgICAgICAvLyBub2RlanMgZW52XG4gICAgICAgIGlmICh0eXBlb2YgbW9kdWxlICE9PSBVTkRFRl9UWVBFICYmIG1vZHVsZS5leHBvcnRzKSB7XG4gICAgICAgICAgICBleHBvcnRzID0gbW9kdWxlLmV4cG9ydHMgPSBVQVBhcnNlcjtcbiAgICAgICAgfVxuICAgICAgICBleHBvcnRzLlVBUGFyc2VyID0gVUFQYXJzZXI7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgLy8gcmVxdWlyZWpzIGVudiAob3B0aW9uYWwpXG4gICAgICAgIGlmICh0eXBlb2YoZGVmaW5lKSA9PT0gRlVOQ19UWVBFICYmIGRlZmluZS5hbWQpIHtcbiAgICAgICAgICAgIGRlZmluZShmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIFVBUGFyc2VyO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIHdpbmRvdyAhPT0gVU5ERUZfVFlQRSkge1xuICAgICAgICAgICAgLy8gYnJvd3NlciBlbnZcbiAgICAgICAgICAgIHdpbmRvdy5VQVBhcnNlciA9IFVBUGFyc2VyO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLy8galF1ZXJ5L1plcHRvIHNwZWNpZmljIChvcHRpb25hbClcbiAgICAvLyBOb3RlOlxuICAgIC8vICAgSW4gQU1EIGVudiB0aGUgZ2xvYmFsIHNjb3BlIHNob3VsZCBiZSBrZXB0IGNsZWFuLCBidXQgalF1ZXJ5IGlzIGFuIGV4Y2VwdGlvbi5cbiAgICAvLyAgIGpRdWVyeSBhbHdheXMgZXhwb3J0cyB0byBnbG9iYWwgc2NvcGUsIHVubGVzcyBqUXVlcnkubm9Db25mbGljdCh0cnVlKSBpcyB1c2VkLFxuICAgIC8vICAgYW5kIHdlIHNob3VsZCBjYXRjaCB0aGF0LlxuICAgIHZhciAkID0gdHlwZW9mIHdpbmRvdyAhPT0gVU5ERUZfVFlQRSAmJiAod2luZG93LmpRdWVyeSB8fCB3aW5kb3cuWmVwdG8pO1xuICAgIGlmICgkICYmICEkLnVhKSB7XG4gICAgICAgIHZhciBwYXJzZXIgPSBuZXcgVUFQYXJzZXIoKTtcbiAgICAgICAgJC51YSA9IHBhcnNlci5nZXRSZXN1bHQoKTtcbiAgICAgICAgJC51YS5nZXQgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgICByZXR1cm4gcGFyc2VyLmdldFVBKCk7XG4gICAgICAgIH07XG4gICAgICAgICQudWEuc2V0ID0gZnVuY3Rpb24gKHVhKSB7XG4gICAgICAgICAgICBwYXJzZXIuc2V0VUEodWEpO1xuICAgICAgICAgICAgdmFyIHJlc3VsdCA9IHBhcnNlci5nZXRSZXN1bHQoKTtcbiAgICAgICAgICAgIGZvciAodmFyIHByb3AgaW4gcmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgJC51YVtwcm9wXSA9IHJlc3VsdFtwcm9wXTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICB9XG5cbn0pKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnID8gd2luZG93IDogdGhpcyk7XG4iLCIvKlxuICogIENvcHlyaWdodCAoYykgMjAxNiBUaGUgV2ViUlRDIHByb2plY3QgYXV0aG9ycy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiAgVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYSBCU0Qtc3R5bGUgbGljZW5zZVxuICogIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3Qgb2YgdGhlIHNvdXJjZVxuICogIHRyZWUuXG4gKi9cbi8qIGVzbGludC1lbnYgbm9kZSAqL1xuXG4ndXNlIHN0cmljdCc7XG5cbmltcG9ydCB7YWRhcHRlckZhY3Rvcnl9IGZyb20gJy4vYWRhcHRlcl9mYWN0b3J5LmpzJztcblxuY29uc3QgYWRhcHRlciA9XG4gIGFkYXB0ZXJGYWN0b3J5KHt3aW5kb3c6IHR5cGVvZiB3aW5kb3cgPT09ICd1bmRlZmluZWQnID8gdW5kZWZpbmVkIDogd2luZG93fSk7XG5leHBvcnQgZGVmYXVsdCBhZGFwdGVyO1xuIiwiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tICcuL3V0aWxzJztcblxuLy8gQnJvd3NlciBzaGltcy5cbmltcG9ydCAqIGFzIGNocm9tZVNoaW0gZnJvbSAnLi9jaHJvbWUvY2hyb21lX3NoaW0nO1xuaW1wb3J0ICogYXMgZmlyZWZveFNoaW0gZnJvbSAnLi9maXJlZm94L2ZpcmVmb3hfc2hpbSc7XG5pbXBvcnQgKiBhcyBzYWZhcmlTaGltIGZyb20gJy4vc2FmYXJpL3NhZmFyaV9zaGltJztcbmltcG9ydCAqIGFzIGNvbW1vblNoaW0gZnJvbSAnLi9jb21tb25fc2hpbSc7XG5pbXBvcnQgKiBhcyBzZHAgZnJvbSAnc2RwJztcblxuLy8gU2hpbW1pbmcgc3RhcnRzIGhlcmUuXG5leHBvcnQgZnVuY3Rpb24gYWRhcHRlckZhY3Rvcnkoe3dpbmRvd30gPSB7fSwgb3B0aW9ucyA9IHtcbiAgc2hpbUNocm9tZTogdHJ1ZSxcbiAgc2hpbUZpcmVmb3g6IHRydWUsXG4gIHNoaW1TYWZhcmk6IHRydWUsXG59KSB7XG4gIC8vIFV0aWxzLlxuICBjb25zdCBsb2dnaW5nID0gdXRpbHMubG9nO1xuICBjb25zdCBicm93c2VyRGV0YWlscyA9IHV0aWxzLmRldGVjdEJyb3dzZXIod2luZG93KTtcblxuICBjb25zdCBhZGFwdGVyID0ge1xuICAgIGJyb3dzZXJEZXRhaWxzLFxuICAgIGNvbW1vblNoaW0sXG4gICAgZXh0cmFjdFZlcnNpb246IHV0aWxzLmV4dHJhY3RWZXJzaW9uLFxuICAgIGRpc2FibGVMb2c6IHV0aWxzLmRpc2FibGVMb2csXG4gICAgZGlzYWJsZVdhcm5pbmdzOiB1dGlscy5kaXNhYmxlV2FybmluZ3MsXG4gICAgLy8gRXhwb3NlIHNkcCBhcyBhIGNvbnZlbmllbmNlLiBGb3IgcHJvZHVjdGlvbiBhcHBzIGluY2x1ZGUgZGlyZWN0bHkuXG4gICAgc2RwLFxuICB9O1xuXG4gIC8vIFNoaW0gYnJvd3NlciBpZiBmb3VuZC5cbiAgc3dpdGNoIChicm93c2VyRGV0YWlscy5icm93c2VyKSB7XG4gICAgY2FzZSAnY2hyb21lJzpcbiAgICAgIGlmICghY2hyb21lU2hpbSB8fCAhY2hyb21lU2hpbS5zaGltUGVlckNvbm5lY3Rpb24gfHxcbiAgICAgICAgICAhb3B0aW9ucy5zaGltQ2hyb21lKSB7XG4gICAgICAgIGxvZ2dpbmcoJ0Nocm9tZSBzaGltIGlzIG5vdCBpbmNsdWRlZCBpbiB0aGlzIGFkYXB0ZXIgcmVsZWFzZS4nKTtcbiAgICAgICAgcmV0dXJuIGFkYXB0ZXI7XG4gICAgICB9XG4gICAgICBpZiAoYnJvd3NlckRldGFpbHMudmVyc2lvbiA9PT0gbnVsbCkge1xuICAgICAgICBsb2dnaW5nKCdDaHJvbWUgc2hpbSBjYW4gbm90IGRldGVybWluZSB2ZXJzaW9uLCBub3Qgc2hpbW1pbmcuJyk7XG4gICAgICAgIHJldHVybiBhZGFwdGVyO1xuICAgICAgfVxuICAgICAgbG9nZ2luZygnYWRhcHRlci5qcyBzaGltbWluZyBjaHJvbWUuJyk7XG4gICAgICAvLyBFeHBvcnQgdG8gdGhlIGFkYXB0ZXIgZ2xvYmFsIG9iamVjdCB2aXNpYmxlIGluIHRoZSBicm93c2VyLlxuICAgICAgYWRhcHRlci5icm93c2VyU2hpbSA9IGNocm9tZVNoaW07XG5cbiAgICAgIC8vIE11c3QgYmUgY2FsbGVkIGJlZm9yZSBzaGltUGVlckNvbm5lY3Rpb24uXG4gICAgICBjb21tb25TaGltLnNoaW1BZGRJY2VDYW5kaWRhdGVOdWxsT3JFbXB0eSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGNvbW1vblNoaW0uc2hpbVBhcmFtZXRlcmxlc3NTZXRMb2NhbERlc2NyaXB0aW9uKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuXG4gICAgICBjaHJvbWVTaGltLnNoaW1HZXRVc2VyTWVkaWEod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjaHJvbWVTaGltLnNoaW1NZWRpYVN0cmVhbSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGNocm9tZVNoaW0uc2hpbVBlZXJDb25uZWN0aW9uKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY2hyb21lU2hpbS5zaGltT25UcmFjayh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGNocm9tZVNoaW0uc2hpbUFkZFRyYWNrUmVtb3ZlVHJhY2sod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjaHJvbWVTaGltLnNoaW1HZXRTZW5kZXJzV2l0aER0bWYod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjaHJvbWVTaGltLnNoaW1HZXRTdGF0cyh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGNocm9tZVNoaW0uc2hpbVNlbmRlclJlY2VpdmVyR2V0U3RhdHMod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjaHJvbWVTaGltLmZpeE5lZ290aWF0aW9uTmVlZGVkKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuXG4gICAgICBjb21tb25TaGltLnNoaW1SVENJY2VDYW5kaWRhdGUod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjb21tb25TaGltLnNoaW1SVENJY2VDYW5kaWRhdGVSZWxheVByb3RvY29sKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltQ29ubmVjdGlvblN0YXRlKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltTWF4TWVzc2FnZVNpemUod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjb21tb25TaGltLnNoaW1TZW5kVGhyb3dUeXBlRXJyb3Iod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjb21tb25TaGltLnJlbW92ZUV4dG1hcEFsbG93TWl4ZWQod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdmaXJlZm94JzpcbiAgICAgIGlmICghZmlyZWZveFNoaW0gfHwgIWZpcmVmb3hTaGltLnNoaW1QZWVyQ29ubmVjdGlvbiB8fFxuICAgICAgICAgICFvcHRpb25zLnNoaW1GaXJlZm94KSB7XG4gICAgICAgIGxvZ2dpbmcoJ0ZpcmVmb3ggc2hpbSBpcyBub3QgaW5jbHVkZWQgaW4gdGhpcyBhZGFwdGVyIHJlbGVhc2UuJyk7XG4gICAgICAgIHJldHVybiBhZGFwdGVyO1xuICAgICAgfVxuICAgICAgbG9nZ2luZygnYWRhcHRlci5qcyBzaGltbWluZyBmaXJlZm94LicpO1xuICAgICAgLy8gRXhwb3J0IHRvIHRoZSBhZGFwdGVyIGdsb2JhbCBvYmplY3QgdmlzaWJsZSBpbiB0aGUgYnJvd3Nlci5cbiAgICAgIGFkYXB0ZXIuYnJvd3NlclNoaW0gPSBmaXJlZm94U2hpbTtcblxuICAgICAgLy8gTXVzdCBiZSBjYWxsZWQgYmVmb3JlIHNoaW1QZWVyQ29ubmVjdGlvbi5cbiAgICAgIGNvbW1vblNoaW0uc2hpbUFkZEljZUNhbmRpZGF0ZU51bGxPckVtcHR5KHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltUGFyYW1ldGVybGVzc1NldExvY2FsRGVzY3JpcHRpb24od2luZG93LCBicm93c2VyRGV0YWlscyk7XG5cbiAgICAgIGZpcmVmb3hTaGltLnNoaW1HZXRVc2VyTWVkaWEod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBmaXJlZm94U2hpbS5zaGltUGVlckNvbm5lY3Rpb24od2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBmaXJlZm94U2hpbS5zaGltT25UcmFjayh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGZpcmVmb3hTaGltLnNoaW1SZW1vdmVTdHJlYW0od2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBmaXJlZm94U2hpbS5zaGltU2VuZGVyR2V0U3RhdHMod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBmaXJlZm94U2hpbS5zaGltUmVjZWl2ZXJHZXRTdGF0cyh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGZpcmVmb3hTaGltLnNoaW1SVENEYXRhQ2hhbm5lbCh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGZpcmVmb3hTaGltLnNoaW1BZGRUcmFuc2NlaXZlcih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGZpcmVmb3hTaGltLnNoaW1HZXRQYXJhbWV0ZXJzKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgZmlyZWZveFNoaW0uc2hpbUNyZWF0ZU9mZmVyKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgZmlyZWZveFNoaW0uc2hpbUNyZWF0ZUFuc3dlcih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcblxuICAgICAgY29tbW9uU2hpbS5zaGltUlRDSWNlQ2FuZGlkYXRlKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltQ29ubmVjdGlvblN0YXRlKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltTWF4TWVzc2FnZVNpemUod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjb21tb25TaGltLnNoaW1TZW5kVGhyb3dUeXBlRXJyb3Iod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBicmVhaztcbiAgICBjYXNlICdzYWZhcmknOlxuICAgICAgaWYgKCFzYWZhcmlTaGltIHx8ICFvcHRpb25zLnNoaW1TYWZhcmkpIHtcbiAgICAgICAgbG9nZ2luZygnU2FmYXJpIHNoaW0gaXMgbm90IGluY2x1ZGVkIGluIHRoaXMgYWRhcHRlciByZWxlYXNlLicpO1xuICAgICAgICByZXR1cm4gYWRhcHRlcjtcbiAgICAgIH1cbiAgICAgIGxvZ2dpbmcoJ2FkYXB0ZXIuanMgc2hpbW1pbmcgc2FmYXJpLicpO1xuICAgICAgLy8gRXhwb3J0IHRvIHRoZSBhZGFwdGVyIGdsb2JhbCBvYmplY3QgdmlzaWJsZSBpbiB0aGUgYnJvd3Nlci5cbiAgICAgIGFkYXB0ZXIuYnJvd3NlclNoaW0gPSBzYWZhcmlTaGltO1xuXG4gICAgICAvLyBNdXN0IGJlIGNhbGxlZCBiZWZvcmUgc2hpbUNhbGxiYWNrQVBJLlxuICAgICAgY29tbW9uU2hpbS5zaGltQWRkSWNlQ2FuZGlkYXRlTnVsbE9yRW1wdHkod2luZG93LCBicm93c2VyRGV0YWlscyk7XG4gICAgICBjb21tb25TaGltLnNoaW1QYXJhbWV0ZXJsZXNzU2V0TG9jYWxEZXNjcmlwdGlvbih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcblxuICAgICAgc2FmYXJpU2hpbS5zaGltUlRDSWNlU2VydmVyVXJscyh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIHNhZmFyaVNoaW0uc2hpbUNyZWF0ZU9mZmVyTGVnYWN5KHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgc2FmYXJpU2hpbS5zaGltQ2FsbGJhY2tzQVBJKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgc2FmYXJpU2hpbS5zaGltTG9jYWxTdHJlYW1zQVBJKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgc2FmYXJpU2hpbS5zaGltUmVtb3RlU3RyZWFtc0FQSSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIHNhZmFyaVNoaW0uc2hpbVRyYWNrRXZlbnRUcmFuc2NlaXZlcih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIHNhZmFyaVNoaW0uc2hpbUdldFVzZXJNZWRpYSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIHNhZmFyaVNoaW0uc2hpbUF1ZGlvQ29udGV4dCh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcblxuICAgICAgY29tbW9uU2hpbS5zaGltUlRDSWNlQ2FuZGlkYXRlKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltUlRDSWNlQ2FuZGlkYXRlUmVsYXlQcm90b2NvbCh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKTtcbiAgICAgIGNvbW1vblNoaW0uc2hpbU1heE1lc3NhZ2VTaXplKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5zaGltU2VuZFRocm93VHlwZUVycm9yKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgY29tbW9uU2hpbS5yZW1vdmVFeHRtYXBBbGxvd01peGVkKHdpbmRvdywgYnJvd3NlckRldGFpbHMpO1xuICAgICAgYnJlYWs7XG4gICAgZGVmYXVsdDpcbiAgICAgIGxvZ2dpbmcoJ1Vuc3VwcG9ydGVkIGJyb3dzZXIhJyk7XG4gICAgICBicmVhaztcbiAgfVxuXG4gIHJldHVybiBhZGFwdGVyO1xufVxuIiwiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4vKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcbmltcG9ydCAqIGFzIHV0aWxzIGZyb20gJy4uL3V0aWxzLmpzJztcblxuZXhwb3J0IHtzaGltR2V0VXNlck1lZGlhfSBmcm9tICcuL2dldHVzZXJtZWRpYSc7XG5leHBvcnQge3NoaW1HZXREaXNwbGF5TWVkaWF9IGZyb20gJy4vZ2V0ZGlzcGxheW1lZGlhJztcblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1NZWRpYVN0cmVhbSh3aW5kb3cpIHtcbiAgd2luZG93Lk1lZGlhU3RyZWFtID0gd2luZG93Lk1lZGlhU3RyZWFtIHx8IHdpbmRvdy53ZWJraXRNZWRpYVN0cmVhbTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1PblRyYWNrKHdpbmRvdykge1xuICBpZiAodHlwZW9mIHdpbmRvdyA9PT0gJ29iamVjdCcgJiYgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uICYmICEoJ29udHJhY2snIGluXG4gICAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLCAnb250cmFjaycsIHtcbiAgICAgIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX29udHJhY2s7XG4gICAgICB9LFxuICAgICAgc2V0KGYpIHtcbiAgICAgICAgaWYgKHRoaXMuX29udHJhY2spIHtcbiAgICAgICAgICB0aGlzLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3RyYWNrJywgdGhpcy5fb250cmFjayk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCd0cmFjaycsIHRoaXMuX29udHJhY2sgPSBmKTtcbiAgICAgIH0sXG4gICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgY29uZmlndXJhYmxlOiB0cnVlXG4gICAgfSk7XG4gICAgY29uc3Qgb3JpZ1NldFJlbW90ZURlc2NyaXB0aW9uID1cbiAgICAgICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbjtcbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID1cbiAgICAgIGZ1bmN0aW9uIHNldFJlbW90ZURlc2NyaXB0aW9uKCkge1xuICAgICAgICBpZiAoIXRoaXMuX29udHJhY2twb2x5KSB7XG4gICAgICAgICAgdGhpcy5fb250cmFja3BvbHkgPSAoZSkgPT4ge1xuICAgICAgICAgICAgLy8gb25hZGRzdHJlYW0gZG9lcyBub3QgZmlyZSB3aGVuIGEgdHJhY2sgaXMgYWRkZWQgdG8gYW4gZXhpc3RpbmdcbiAgICAgICAgICAgIC8vIHN0cmVhbS4gQnV0IHN0cmVhbS5vbmFkZHRyYWNrIGlzIGltcGxlbWVudGVkIHNvIHdlIHVzZSB0aGF0LlxuICAgICAgICAgICAgZS5zdHJlYW0uYWRkRXZlbnRMaXN0ZW5lcignYWRkdHJhY2snLCB0ZSA9PiB7XG4gICAgICAgICAgICAgIGxldCByZWNlaXZlcjtcbiAgICAgICAgICAgICAgaWYgKHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0UmVjZWl2ZXJzKSB7XG4gICAgICAgICAgICAgICAgcmVjZWl2ZXIgPSB0aGlzLmdldFJlY2VpdmVycygpXG4gICAgICAgICAgICAgICAgICAuZmluZChyID0+IHIudHJhY2sgJiYgci50cmFjay5pZCA9PT0gdGUudHJhY2suaWQpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJlY2VpdmVyID0ge3RyYWNrOiB0ZS50cmFja307XG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgZXZlbnQudHJhY2sgPSB0ZS50cmFjaztcbiAgICAgICAgICAgICAgZXZlbnQucmVjZWl2ZXIgPSByZWNlaXZlcjtcbiAgICAgICAgICAgICAgZXZlbnQudHJhbnNjZWl2ZXIgPSB7cmVjZWl2ZXJ9O1xuICAgICAgICAgICAgICBldmVudC5zdHJlYW1zID0gW2Uuc3RyZWFtXTtcbiAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgZS5zdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB7XG4gICAgICAgICAgICAgIGxldCByZWNlaXZlcjtcbiAgICAgICAgICAgICAgaWYgKHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0UmVjZWl2ZXJzKSB7XG4gICAgICAgICAgICAgICAgcmVjZWl2ZXIgPSB0aGlzLmdldFJlY2VpdmVycygpXG4gICAgICAgICAgICAgICAgICAuZmluZChyID0+IHIudHJhY2sgJiYgci50cmFjay5pZCA9PT0gdHJhY2suaWQpO1xuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHJlY2VpdmVyID0ge3RyYWNrfTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBFdmVudCgndHJhY2snKTtcbiAgICAgICAgICAgICAgZXZlbnQudHJhY2sgPSB0cmFjaztcbiAgICAgICAgICAgICAgZXZlbnQucmVjZWl2ZXIgPSByZWNlaXZlcjtcbiAgICAgICAgICAgICAgZXZlbnQudHJhbnNjZWl2ZXIgPSB7cmVjZWl2ZXJ9O1xuICAgICAgICAgICAgICBldmVudC5zdHJlYW1zID0gW2Uuc3RyZWFtXTtcbiAgICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH07XG4gICAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCdhZGRzdHJlYW0nLCB0aGlzLl9vbnRyYWNrcG9seSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG9yaWdTZXRSZW1vdGVEZXNjcmlwdGlvbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgfTtcbiAgfSBlbHNlIHtcbiAgICAvLyBldmVuIGlmIFJUQ1J0cFRyYW5zY2VpdmVyIGlzIGluIHdpbmRvdywgaXQgaXMgb25seSB1c2VkIGFuZFxuICAgIC8vIGVtaXR0ZWQgaW4gdW5pZmllZC1wbGFuLiBVbmZvcnR1bmF0ZWx5IHRoaXMgbWVhbnMgd2UgbmVlZFxuICAgIC8vIHRvIHVuY29uZGl0aW9uYWxseSB3cmFwIHRoZSBldmVudC5cbiAgICB1dGlscy53cmFwUGVlckNvbm5lY3Rpb25FdmVudCh3aW5kb3csICd0cmFjaycsIGUgPT4ge1xuICAgICAgaWYgKCFlLnRyYW5zY2VpdmVyKSB7XG4gICAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLCAndHJhbnNjZWl2ZXInLFxuICAgICAgICAgIHt2YWx1ZToge3JlY2VpdmVyOiBlLnJlY2VpdmVyfX0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGU7XG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1HZXRTZW5kZXJzV2l0aER0bWYod2luZG93KSB7XG4gIC8vIE92ZXJyaWRlcyBhZGRUcmFjay9yZW1vdmVUcmFjaywgZGVwZW5kcyBvbiBzaGltQWRkVHJhY2tSZW1vdmVUcmFjay5cbiAgaWYgKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnICYmIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiAmJlxuICAgICAgISgnZ2V0U2VuZGVycycgaW4gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSkgJiZcbiAgICAgICdjcmVhdGVEVE1GU2VuZGVyJyBpbiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSB7XG4gICAgY29uc3Qgc2hpbVNlbmRlcldpdGhEdG1mID0gZnVuY3Rpb24ocGMsIHRyYWNrKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICB0cmFjayxcbiAgICAgICAgZ2V0IGR0bWYoKSB7XG4gICAgICAgICAgaWYgKHRoaXMuX2R0bWYgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgaWYgKHRyYWNrLmtpbmQgPT09ICdhdWRpbycpIHtcbiAgICAgICAgICAgICAgdGhpcy5fZHRtZiA9IHBjLmNyZWF0ZURUTUZTZW5kZXIodHJhY2spO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy5fZHRtZiA9IG51bGw7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiB0aGlzLl9kdG1mO1xuICAgICAgICB9LFxuICAgICAgICBfcGM6IHBjXG4gICAgICB9O1xuICAgIH07XG5cbiAgICAvLyBhdWdtZW50IGFkZFRyYWNrIHdoZW4gZ2V0U2VuZGVycyBpcyBub3QgYXZhaWxhYmxlLlxuICAgIGlmICghd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzKSB7XG4gICAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbiBnZXRTZW5kZXJzKCkge1xuICAgICAgICB0aGlzLl9zZW5kZXJzID0gdGhpcy5fc2VuZGVycyB8fCBbXTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NlbmRlcnMuc2xpY2UoKTsgLy8gcmV0dXJuIGEgY29weSBvZiB0aGUgaW50ZXJuYWwgc3RhdGUuXG4gICAgICB9O1xuICAgICAgY29uc3Qgb3JpZ0FkZFRyYWNrID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRUcmFjaztcbiAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkVHJhY2sgPVxuICAgICAgICBmdW5jdGlvbiBhZGRUcmFjayh0cmFjaywgc3RyZWFtKSB7XG4gICAgICAgICAgbGV0IHNlbmRlciA9IG9yaWdBZGRUcmFjay5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICAgIGlmICghc2VuZGVyKSB7XG4gICAgICAgICAgICBzZW5kZXIgPSBzaGltU2VuZGVyV2l0aER0bWYodGhpcywgdHJhY2spO1xuICAgICAgICAgICAgdGhpcy5fc2VuZGVycy5wdXNoKHNlbmRlcik7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBzZW5kZXI7XG4gICAgICAgIH07XG5cbiAgICAgIGNvbnN0IG9yaWdSZW1vdmVUcmFjayA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlVHJhY2s7XG4gICAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnJlbW92ZVRyYWNrID1cbiAgICAgICAgZnVuY3Rpb24gcmVtb3ZlVHJhY2soc2VuZGVyKSB7XG4gICAgICAgICAgb3JpZ1JlbW92ZVRyYWNrLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgICAgY29uc3QgaWR4ID0gdGhpcy5fc2VuZGVycy5pbmRleE9mKHNlbmRlcik7XG4gICAgICAgICAgaWYgKGlkeCAhPT0gLTEpIHtcbiAgICAgICAgICAgIHRoaXMuX3NlbmRlcnMuc3BsaWNlKGlkeCwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICB9O1xuICAgIH1cbiAgICBjb25zdCBvcmlnQWRkU3RyZWFtID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW07XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbiBhZGRTdHJlYW0oc3RyZWFtKSB7XG4gICAgICB0aGlzLl9zZW5kZXJzID0gdGhpcy5fc2VuZGVycyB8fCBbXTtcbiAgICAgIG9yaWdBZGRTdHJlYW0uYXBwbHkodGhpcywgW3N0cmVhbV0pO1xuICAgICAgc3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2godHJhY2sgPT4ge1xuICAgICAgICB0aGlzLl9zZW5kZXJzLnB1c2goc2hpbVNlbmRlcldpdGhEdG1mKHRoaXMsIHRyYWNrKSk7XG4gICAgICB9KTtcbiAgICB9O1xuXG4gICAgY29uc3Qgb3JpZ1JlbW92ZVN0cmVhbSA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtO1xuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID1cbiAgICAgIGZ1bmN0aW9uIHJlbW92ZVN0cmVhbShzdHJlYW0pIHtcbiAgICAgICAgdGhpcy5fc2VuZGVycyA9IHRoaXMuX3NlbmRlcnMgfHwgW107XG4gICAgICAgIG9yaWdSZW1vdmVTdHJlYW0uYXBwbHkodGhpcywgW3N0cmVhbV0pO1xuXG4gICAgICAgIHN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgICAgICBjb25zdCBzZW5kZXIgPSB0aGlzLl9zZW5kZXJzLmZpbmQocyA9PiBzLnRyYWNrID09PSB0cmFjayk7XG4gICAgICAgICAgaWYgKHNlbmRlcikgeyAvLyByZW1vdmUgc2VuZGVyXG4gICAgICAgICAgICB0aGlzLl9zZW5kZXJzLnNwbGljZSh0aGlzLl9zZW5kZXJzLmluZGV4T2Yoc2VuZGVyKSwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH07XG4gIH0gZWxzZSBpZiAodHlwZW9mIHdpbmRvdyA9PT0gJ29iamVjdCcgJiYgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uICYmXG4gICAgICAgICAgICAgJ2dldFNlbmRlcnMnIGluIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUgJiZcbiAgICAgICAgICAgICAnY3JlYXRlRFRNRlNlbmRlcicgaW4gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSAmJlxuICAgICAgICAgICAgIHdpbmRvdy5SVENSdHBTZW5kZXIgJiZcbiAgICAgICAgICAgICAhKCdkdG1mJyBpbiB3aW5kb3cuUlRDUnRwU2VuZGVyLnByb3RvdHlwZSkpIHtcbiAgICBjb25zdCBvcmlnR2V0U2VuZGVycyA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U2VuZGVycztcbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbiBnZXRTZW5kZXJzKCkge1xuICAgICAgY29uc3Qgc2VuZGVycyA9IG9yaWdHZXRTZW5kZXJzLmFwcGx5KHRoaXMsIFtdKTtcbiAgICAgIHNlbmRlcnMuZm9yRWFjaChzZW5kZXIgPT4gc2VuZGVyLl9wYyA9IHRoaXMpO1xuICAgICAgcmV0dXJuIHNlbmRlcnM7XG4gICAgfTtcblxuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3aW5kb3cuUlRDUnRwU2VuZGVyLnByb3RvdHlwZSwgJ2R0bWYnLCB7XG4gICAgICBnZXQoKSB7XG4gICAgICAgIGlmICh0aGlzLl9kdG1mID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBpZiAodGhpcy50cmFjay5raW5kID09PSAnYXVkaW8nKSB7XG4gICAgICAgICAgICB0aGlzLl9kdG1mID0gdGhpcy5fcGMuY3JlYXRlRFRNRlNlbmRlcih0aGlzLnRyYWNrKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fZHRtZiA9IG51bGw7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLl9kdG1mO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltR2V0U3RhdHMod2luZG93KSB7XG4gIGlmICghd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3Qgb3JpZ0dldFN0YXRzID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTdGF0cztcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTdGF0cyA9IGZ1bmN0aW9uIGdldFN0YXRzKCkge1xuICAgIGNvbnN0IFtzZWxlY3Rvciwgb25TdWNjLCBvbkVycl0gPSBhcmd1bWVudHM7XG5cbiAgICAvLyBJZiBzZWxlY3RvciBpcyBhIGZ1bmN0aW9uIHRoZW4gd2UgYXJlIGluIHRoZSBvbGQgc3R5bGUgc3RhdHMgc28ganVzdFxuICAgIC8vIHBhc3MgYmFjayB0aGUgb3JpZ2luYWwgZ2V0U3RhdHMgZm9ybWF0IHRvIGF2b2lkIGJyZWFraW5nIG9sZCB1c2Vycy5cbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDAgJiYgdHlwZW9mIHNlbGVjdG9yID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICByZXR1cm4gb3JpZ0dldFN0YXRzLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfVxuXG4gICAgLy8gV2hlbiBzcGVjLXN0eWxlIGdldFN0YXRzIGlzIHN1cHBvcnRlZCwgcmV0dXJuIHRob3NlIHdoZW4gY2FsbGVkIHdpdGhcbiAgICAvLyBlaXRoZXIgbm8gYXJndW1lbnRzIG9yIHRoZSBzZWxlY3RvciBhcmd1bWVudCBpcyBudWxsLlxuICAgIGlmIChvcmlnR2V0U3RhdHMubGVuZ3RoID09PSAwICYmIChhcmd1bWVudHMubGVuZ3RoID09PSAwIHx8XG4gICAgICAgIHR5cGVvZiBzZWxlY3RvciAhPT0gJ2Z1bmN0aW9uJykpIHtcbiAgICAgIHJldHVybiBvcmlnR2V0U3RhdHMuYXBwbHkodGhpcywgW10pO1xuICAgIH1cblxuICAgIGNvbnN0IGZpeENocm9tZVN0YXRzXyA9IGZ1bmN0aW9uKHJlc3BvbnNlKSB7XG4gICAgICBjb25zdCBzdGFuZGFyZFJlcG9ydCA9IHt9O1xuICAgICAgY29uc3QgcmVwb3J0cyA9IHJlc3BvbnNlLnJlc3VsdCgpO1xuICAgICAgcmVwb3J0cy5mb3JFYWNoKHJlcG9ydCA9PiB7XG4gICAgICAgIGNvbnN0IHN0YW5kYXJkU3RhdHMgPSB7XG4gICAgICAgICAgaWQ6IHJlcG9ydC5pZCxcbiAgICAgICAgICB0aW1lc3RhbXA6IHJlcG9ydC50aW1lc3RhbXAsXG4gICAgICAgICAgdHlwZToge1xuICAgICAgICAgICAgbG9jYWxjYW5kaWRhdGU6ICdsb2NhbC1jYW5kaWRhdGUnLFxuICAgICAgICAgICAgcmVtb3RlY2FuZGlkYXRlOiAncmVtb3RlLWNhbmRpZGF0ZSdcbiAgICAgICAgICB9W3JlcG9ydC50eXBlXSB8fCByZXBvcnQudHlwZVxuICAgICAgICB9O1xuICAgICAgICByZXBvcnQubmFtZXMoKS5mb3JFYWNoKG5hbWUgPT4ge1xuICAgICAgICAgIHN0YW5kYXJkU3RhdHNbbmFtZV0gPSByZXBvcnQuc3RhdChuYW1lKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHN0YW5kYXJkUmVwb3J0W3N0YW5kYXJkU3RhdHMuaWRdID0gc3RhbmRhcmRTdGF0cztcbiAgICAgIH0pO1xuXG4gICAgICByZXR1cm4gc3RhbmRhcmRSZXBvcnQ7XG4gICAgfTtcblxuICAgIC8vIHNoaW0gZ2V0U3RhdHMgd2l0aCBtYXBsaWtlIHN1cHBvcnRcbiAgICBjb25zdCBtYWtlTWFwU3RhdHMgPSBmdW5jdGlvbihzdGF0cykge1xuICAgICAgcmV0dXJuIG5ldyBNYXAoT2JqZWN0LmtleXMoc3RhdHMpLm1hcChrZXkgPT4gW2tleSwgc3RhdHNba2V5XV0pKTtcbiAgICB9O1xuXG4gICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPj0gMikge1xuICAgICAgY29uc3Qgc3VjY2Vzc0NhbGxiYWNrV3JhcHBlcl8gPSBmdW5jdGlvbihyZXNwb25zZSkge1xuICAgICAgICBvblN1Y2MobWFrZU1hcFN0YXRzKGZpeENocm9tZVN0YXRzXyhyZXNwb25zZSkpKTtcbiAgICAgIH07XG5cbiAgICAgIHJldHVybiBvcmlnR2V0U3RhdHMuYXBwbHkodGhpcywgW3N1Y2Nlc3NDYWxsYmFja1dyYXBwZXJfLFxuICAgICAgICBzZWxlY3Rvcl0pO1xuICAgIH1cblxuICAgIC8vIHByb21pc2Utc3VwcG9ydFxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBvcmlnR2V0U3RhdHMuYXBwbHkodGhpcywgW1xuICAgICAgICBmdW5jdGlvbihyZXNwb25zZSkge1xuICAgICAgICAgIHJlc29sdmUobWFrZU1hcFN0YXRzKGZpeENocm9tZVN0YXRzXyhyZXNwb25zZSkpKTtcbiAgICAgICAgfSwgcmVqZWN0XSk7XG4gICAgfSkudGhlbihvblN1Y2MsIG9uRXJyKTtcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1TZW5kZXJSZWNlaXZlckdldFN0YXRzKHdpbmRvdykge1xuICBpZiAoISh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JyAmJiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gJiZcbiAgICAgIHdpbmRvdy5SVENSdHBTZW5kZXIgJiYgd2luZG93LlJUQ1J0cFJlY2VpdmVyKSkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIHNoaW0gc2VuZGVyIHN0YXRzLlxuICBpZiAoISgnZ2V0U3RhdHMnIGluIHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlKSkge1xuICAgIGNvbnN0IG9yaWdHZXRTZW5kZXJzID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzO1xuICAgIGlmIChvcmlnR2V0U2VuZGVycykge1xuICAgICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzID0gZnVuY3Rpb24gZ2V0U2VuZGVycygpIHtcbiAgICAgICAgY29uc3Qgc2VuZGVycyA9IG9yaWdHZXRTZW5kZXJzLmFwcGx5KHRoaXMsIFtdKTtcbiAgICAgICAgc2VuZGVycy5mb3JFYWNoKHNlbmRlciA9PiBzZW5kZXIuX3BjID0gdGhpcyk7XG4gICAgICAgIHJldHVybiBzZW5kZXJzO1xuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBvcmlnQWRkVHJhY2sgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFRyYWNrO1xuICAgIGlmIChvcmlnQWRkVHJhY2spIHtcbiAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkVHJhY2sgPSBmdW5jdGlvbiBhZGRUcmFjaygpIHtcbiAgICAgICAgY29uc3Qgc2VuZGVyID0gb3JpZ0FkZFRyYWNrLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICAgIHNlbmRlci5fcGMgPSB0aGlzO1xuICAgICAgICByZXR1cm4gc2VuZGVyO1xuICAgICAgfTtcbiAgICB9XG4gICAgd2luZG93LlJUQ1J0cFNlbmRlci5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbiBnZXRTdGF0cygpIHtcbiAgICAgIGNvbnN0IHNlbmRlciA9IHRoaXM7XG4gICAgICByZXR1cm4gdGhpcy5fcGMuZ2V0U3RhdHMoKS50aGVuKHJlc3VsdCA9PlxuICAgICAgICAvKiBOb3RlOiB0aGlzIHdpbGwgaW5jbHVkZSBzdGF0cyBvZiBhbGwgc2VuZGVycyB0aGF0XG4gICAgICAgICAqICAgc2VuZCBhIHRyYWNrIHdpdGggdGhlIHNhbWUgaWQgYXMgc2VuZGVyLnRyYWNrIGFzXG4gICAgICAgICAqICAgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGlkZW50aWZ5IHRoZSBSVENSdHBTZW5kZXIuXG4gICAgICAgICAqL1xuICAgICAgICB1dGlscy5maWx0ZXJTdGF0cyhyZXN1bHQsIHNlbmRlci50cmFjaywgdHJ1ZSkpO1xuICAgIH07XG4gIH1cblxuICAvLyBzaGltIHJlY2VpdmVyIHN0YXRzLlxuICBpZiAoISgnZ2V0U3RhdHMnIGluIHdpbmRvdy5SVENSdHBSZWNlaXZlci5wcm90b3R5cGUpKSB7XG4gICAgY29uc3Qgb3JpZ0dldFJlY2VpdmVycyA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0UmVjZWl2ZXJzO1xuICAgIGlmIChvcmlnR2V0UmVjZWl2ZXJzKSB7XG4gICAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFJlY2VpdmVycyA9XG4gICAgICAgIGZ1bmN0aW9uIGdldFJlY2VpdmVycygpIHtcbiAgICAgICAgICBjb25zdCByZWNlaXZlcnMgPSBvcmlnR2V0UmVjZWl2ZXJzLmFwcGx5KHRoaXMsIFtdKTtcbiAgICAgICAgICByZWNlaXZlcnMuZm9yRWFjaChyZWNlaXZlciA9PiByZWNlaXZlci5fcGMgPSB0aGlzKTtcbiAgICAgICAgICByZXR1cm4gcmVjZWl2ZXJzO1xuICAgICAgICB9O1xuICAgIH1cbiAgICB1dGlscy53cmFwUGVlckNvbm5lY3Rpb25FdmVudCh3aW5kb3csICd0cmFjaycsIGUgPT4ge1xuICAgICAgZS5yZWNlaXZlci5fcGMgPSBlLnNyY0VsZW1lbnQ7XG4gICAgICByZXR1cm4gZTtcbiAgICB9KTtcbiAgICB3aW5kb3cuUlRDUnRwUmVjZWl2ZXIucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24gZ2V0U3RhdHMoKSB7XG4gICAgICBjb25zdCByZWNlaXZlciA9IHRoaXM7XG4gICAgICByZXR1cm4gdGhpcy5fcGMuZ2V0U3RhdHMoKS50aGVuKHJlc3VsdCA9PlxuICAgICAgICB1dGlscy5maWx0ZXJTdGF0cyhyZXN1bHQsIHJlY2VpdmVyLnRyYWNrLCBmYWxzZSkpO1xuICAgIH07XG4gIH1cblxuICBpZiAoISgnZ2V0U3RhdHMnIGluIHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlICYmXG4gICAgICAnZ2V0U3RhdHMnIGluIHdpbmRvdy5SVENSdHBSZWNlaXZlci5wcm90b3R5cGUpKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gc2hpbSBSVENQZWVyQ29ubmVjdGlvbi5nZXRTdGF0cyh0cmFjaykuXG4gIGNvbnN0IG9yaWdHZXRTdGF0cyA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHM7XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0U3RhdHMgPSBmdW5jdGlvbiBnZXRTdGF0cygpIHtcbiAgICBpZiAoYXJndW1lbnRzLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgYXJndW1lbnRzWzBdIGluc3RhbmNlb2Ygd2luZG93Lk1lZGlhU3RyZWFtVHJhY2spIHtcbiAgICAgIGNvbnN0IHRyYWNrID0gYXJndW1lbnRzWzBdO1xuICAgICAgbGV0IHNlbmRlcjtcbiAgICAgIGxldCByZWNlaXZlcjtcbiAgICAgIGxldCBlcnI7XG4gICAgICB0aGlzLmdldFNlbmRlcnMoKS5mb3JFYWNoKHMgPT4ge1xuICAgICAgICBpZiAocy50cmFjayA9PT0gdHJhY2spIHtcbiAgICAgICAgICBpZiAoc2VuZGVyKSB7XG4gICAgICAgICAgICBlcnIgPSB0cnVlO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzZW5kZXIgPSBzO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICB0aGlzLmdldFJlY2VpdmVycygpLmZvckVhY2gociA9PiB7XG4gICAgICAgIGlmIChyLnRyYWNrID09PSB0cmFjaykge1xuICAgICAgICAgIGlmIChyZWNlaXZlcikge1xuICAgICAgICAgICAgZXJyID0gdHJ1ZTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVjZWl2ZXIgPSByO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gci50cmFjayA9PT0gdHJhY2s7XG4gICAgICB9KTtcbiAgICAgIGlmIChlcnIgfHwgKHNlbmRlciAmJiByZWNlaXZlcikpIHtcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBET01FeGNlcHRpb24oXG4gICAgICAgICAgJ1RoZXJlIGFyZSBtb3JlIHRoYW4gb25lIHNlbmRlciBvciByZWNlaXZlciBmb3IgdGhlIHRyYWNrLicsXG4gICAgICAgICAgJ0ludmFsaWRBY2Nlc3NFcnJvcicpKTtcbiAgICAgIH0gZWxzZSBpZiAoc2VuZGVyKSB7XG4gICAgICAgIHJldHVybiBzZW5kZXIuZ2V0U3RhdHMoKTtcbiAgICAgIH0gZWxzZSBpZiAocmVjZWl2ZXIpIHtcbiAgICAgICAgcmV0dXJuIHJlY2VpdmVyLmdldFN0YXRzKCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IERPTUV4Y2VwdGlvbihcbiAgICAgICAgJ1RoZXJlIGlzIG5vIHNlbmRlciBvciByZWNlaXZlciBmb3IgdGhlIHRyYWNrLicsXG4gICAgICAgICdJbnZhbGlkQWNjZXNzRXJyb3InKSk7XG4gICAgfVxuICAgIHJldHVybiBvcmlnR2V0U3RhdHMuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1BZGRUcmFja1JlbW92ZVRyYWNrV2l0aE5hdGl2ZSh3aW5kb3cpIHtcbiAgLy8gc2hpbSBhZGRUcmFjay9yZW1vdmVUcmFjayB3aXRoIG5hdGl2ZSB2YXJpYW50cyBpbiBvcmRlciB0byBtYWtlXG4gIC8vIHRoZSBpbnRlcmFjdGlvbnMgd2l0aCBsZWdhY3kgZ2V0TG9jYWxTdHJlYW1zIGJlaGF2ZSBhcyBpbiBvdGhlciBicm93c2Vycy5cbiAgLy8gS2VlcHMgYSBtYXBwaW5nIHN0cmVhbS5pZCA9PiBbc3RyZWFtLCBydHBzZW5kZXJzLi4uXVxuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldExvY2FsU3RyZWFtcyA9XG4gICAgZnVuY3Rpb24gZ2V0TG9jYWxTdHJlYW1zKCkge1xuICAgICAgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcyA9IHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXMgfHwge307XG4gICAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcylcbiAgICAgICAgLm1hcChzdHJlYW1JZCA9PiB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbUlkXVswXSk7XG4gICAgfTtcblxuICBjb25zdCBvcmlnQWRkVHJhY2sgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFRyYWNrO1xuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFRyYWNrID1cbiAgICBmdW5jdGlvbiBhZGRUcmFjayh0cmFjaywgc3RyZWFtKSB7XG4gICAgICBpZiAoIXN0cmVhbSkge1xuICAgICAgICByZXR1cm4gb3JpZ0FkZFRyYWNrLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICB9XG4gICAgICB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zID0gdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcyB8fCB7fTtcblxuICAgICAgY29uc3Qgc2VuZGVyID0gb3JpZ0FkZFRyYWNrLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgICBpZiAoIXRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXNbc3RyZWFtLmlkXSkge1xuICAgICAgICB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbS5pZF0gPSBbc3RyZWFtLCBzZW5kZXJdO1xuICAgICAgfSBlbHNlIGlmICh0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbS5pZF0uaW5kZXhPZihzZW5kZXIpID09PSAtMSkge1xuICAgICAgICB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbS5pZF0ucHVzaChzZW5kZXIpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHNlbmRlcjtcbiAgICB9O1xuXG4gIGNvbnN0IG9yaWdBZGRTdHJlYW0gPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFN0cmVhbTtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbiBhZGRTdHJlYW0oc3RyZWFtKSB7XG4gICAgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcyA9IHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXMgfHwge307XG5cbiAgICBzdHJlYW0uZ2V0VHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiB7XG4gICAgICBjb25zdCBhbHJlYWR5RXhpc3RzID0gdGhpcy5nZXRTZW5kZXJzKCkuZmluZChzID0+IHMudHJhY2sgPT09IHRyYWNrKTtcbiAgICAgIGlmIChhbHJlYWR5RXhpc3RzKSB7XG4gICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oJ1RyYWNrIGFscmVhZHkgZXhpc3RzLicsXG4gICAgICAgICAgJ0ludmFsaWRBY2Nlc3NFcnJvcicpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIGNvbnN0IGV4aXN0aW5nU2VuZGVycyA9IHRoaXMuZ2V0U2VuZGVycygpO1xuICAgIG9yaWdBZGRTdHJlYW0uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICBjb25zdCBuZXdTZW5kZXJzID0gdGhpcy5nZXRTZW5kZXJzKClcbiAgICAgIC5maWx0ZXIobmV3U2VuZGVyID0+IGV4aXN0aW5nU2VuZGVycy5pbmRleE9mKG5ld1NlbmRlcikgPT09IC0xKTtcbiAgICB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbS5pZF0gPSBbc3RyZWFtXS5jb25jYXQobmV3U2VuZGVycyk7XG4gIH07XG5cbiAgY29uc3Qgb3JpZ1JlbW92ZVN0cmVhbSA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtO1xuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnJlbW92ZVN0cmVhbSA9XG4gICAgZnVuY3Rpb24gcmVtb3ZlU3RyZWFtKHN0cmVhbSkge1xuICAgICAgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcyA9IHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXMgfHwge307XG4gICAgICBkZWxldGUgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtc1tzdHJlYW0uaWRdO1xuICAgICAgcmV0dXJuIG9yaWdSZW1vdmVTdHJlYW0uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9O1xuXG4gIGNvbnN0IG9yaWdSZW1vdmVUcmFjayA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlVHJhY2s7XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlVHJhY2sgPVxuICAgIGZ1bmN0aW9uIHJlbW92ZVRyYWNrKHNlbmRlcikge1xuICAgICAgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtcyA9IHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXMgfHwge307XG4gICAgICBpZiAoc2VuZGVyKSB7XG4gICAgICAgIE9iamVjdC5rZXlzKHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXMpLmZvckVhY2goc3RyZWFtSWQgPT4ge1xuICAgICAgICAgIGNvbnN0IGlkeCA9IHRoaXMuX3NoaW1tZWRMb2NhbFN0cmVhbXNbc3RyZWFtSWRdLmluZGV4T2Yoc2VuZGVyKTtcbiAgICAgICAgICBpZiAoaWR4ICE9PSAtMSkge1xuICAgICAgICAgICAgdGhpcy5fc2hpbW1lZExvY2FsU3RyZWFtc1tzdHJlYW1JZF0uc3BsaWNlKGlkeCwgMSk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbUlkXS5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgIGRlbGV0ZSB0aGlzLl9zaGltbWVkTG9jYWxTdHJlYW1zW3N0cmVhbUlkXTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG9yaWdSZW1vdmVUcmFjay5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQWRkVHJhY2tSZW1vdmVUcmFjayh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKSB7XG4gIGlmICghd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIHNoaW0gYWRkVHJhY2sgYW5kIHJlbW92ZVRyYWNrLlxuICBpZiAod2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRUcmFjayAmJlxuICAgICAgYnJvd3NlckRldGFpbHMudmVyc2lvbiA+PSA2NSkge1xuICAgIHJldHVybiBzaGltQWRkVHJhY2tSZW1vdmVUcmFja1dpdGhOYXRpdmUod2luZG93KTtcbiAgfVxuXG4gIC8vIGFsc28gc2hpbSBwYy5nZXRMb2NhbFN0cmVhbXMgd2hlbiBhZGRUcmFjayBpcyBzaGltbWVkXG4gIC8vIHRvIHJldHVybiB0aGUgb3JpZ2luYWwgc3RyZWFtcy5cbiAgY29uc3Qgb3JpZ0dldExvY2FsU3RyZWFtcyA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGVcbiAgICAuZ2V0TG9jYWxTdHJlYW1zO1xuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldExvY2FsU3RyZWFtcyA9XG4gICAgZnVuY3Rpb24gZ2V0TG9jYWxTdHJlYW1zKCkge1xuICAgICAgY29uc3QgbmF0aXZlU3RyZWFtcyA9IG9yaWdHZXRMb2NhbFN0cmVhbXMuYXBwbHkodGhpcyk7XG4gICAgICB0aGlzLl9yZXZlcnNlU3RyZWFtcyA9IHRoaXMuX3JldmVyc2VTdHJlYW1zIHx8IHt9O1xuICAgICAgcmV0dXJuIG5hdGl2ZVN0cmVhbXMubWFwKHN0cmVhbSA9PiB0aGlzLl9yZXZlcnNlU3RyZWFtc1tzdHJlYW0uaWRdKTtcbiAgICB9O1xuXG4gIGNvbnN0IG9yaWdBZGRTdHJlYW0gPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFN0cmVhbTtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbiBhZGRTdHJlYW0oc3RyZWFtKSB7XG4gICAgdGhpcy5fc3RyZWFtcyA9IHRoaXMuX3N0cmVhbXMgfHwge307XG4gICAgdGhpcy5fcmV2ZXJzZVN0cmVhbXMgPSB0aGlzLl9yZXZlcnNlU3RyZWFtcyB8fCB7fTtcblxuICAgIHN0cmVhbS5nZXRUcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IHtcbiAgICAgIGNvbnN0IGFscmVhZHlFeGlzdHMgPSB0aGlzLmdldFNlbmRlcnMoKS5maW5kKHMgPT4gcy50cmFjayA9PT0gdHJhY2spO1xuICAgICAgaWYgKGFscmVhZHlFeGlzdHMpIHtcbiAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignVHJhY2sgYWxyZWFkeSBleGlzdHMuJyxcbiAgICAgICAgICAnSW52YWxpZEFjY2Vzc0Vycm9yJyk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgLy8gQWRkIGlkZW50aXR5IG1hcHBpbmcgZm9yIGNvbnNpc3RlbmN5IHdpdGggYWRkVHJhY2suXG4gICAgLy8gVW5sZXNzIHRoaXMgaXMgYmVpbmcgdXNlZCB3aXRoIGEgc3RyZWFtIGZyb20gYWRkVHJhY2suXG4gICAgaWYgKCF0aGlzLl9yZXZlcnNlU3RyZWFtc1tzdHJlYW0uaWRdKSB7XG4gICAgICBjb25zdCBuZXdTdHJlYW0gPSBuZXcgd2luZG93Lk1lZGlhU3RyZWFtKHN0cmVhbS5nZXRUcmFja3MoKSk7XG4gICAgICB0aGlzLl9zdHJlYW1zW3N0cmVhbS5pZF0gPSBuZXdTdHJlYW07XG4gICAgICB0aGlzLl9yZXZlcnNlU3RyZWFtc1tuZXdTdHJlYW0uaWRdID0gc3RyZWFtO1xuICAgICAgc3RyZWFtID0gbmV3U3RyZWFtO1xuICAgIH1cbiAgICBvcmlnQWRkU3RyZWFtLmFwcGx5KHRoaXMsIFtzdHJlYW1dKTtcbiAgfTtcblxuICBjb25zdCBvcmlnUmVtb3ZlU3RyZWFtID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5yZW1vdmVTdHJlYW07XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID1cbiAgICBmdW5jdGlvbiByZW1vdmVTdHJlYW0oc3RyZWFtKSB7XG4gICAgICB0aGlzLl9zdHJlYW1zID0gdGhpcy5fc3RyZWFtcyB8fCB7fTtcbiAgICAgIHRoaXMuX3JldmVyc2VTdHJlYW1zID0gdGhpcy5fcmV2ZXJzZVN0cmVhbXMgfHwge307XG5cbiAgICAgIG9yaWdSZW1vdmVTdHJlYW0uYXBwbHkodGhpcywgWyh0aGlzLl9zdHJlYW1zW3N0cmVhbS5pZF0gfHwgc3RyZWFtKV0pO1xuICAgICAgZGVsZXRlIHRoaXMuX3JldmVyc2VTdHJlYW1zWyh0aGlzLl9zdHJlYW1zW3N0cmVhbS5pZF0gP1xuICAgICAgICB0aGlzLl9zdHJlYW1zW3N0cmVhbS5pZF0uaWQgOiBzdHJlYW0uaWQpXTtcbiAgICAgIGRlbGV0ZSB0aGlzLl9zdHJlYW1zW3N0cmVhbS5pZF07XG4gICAgfTtcblxuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFRyYWNrID1cbiAgICBmdW5jdGlvbiBhZGRUcmFjayh0cmFjaywgc3RyZWFtKSB7XG4gICAgICBpZiAodGhpcy5zaWduYWxpbmdTdGF0ZSA9PT0gJ2Nsb3NlZCcpIHtcbiAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbihcbiAgICAgICAgICAnVGhlIFJUQ1BlZXJDb25uZWN0aW9uXFwncyBzaWduYWxpbmdTdGF0ZSBpcyBcXCdjbG9zZWRcXCcuJyxcbiAgICAgICAgICAnSW52YWxpZFN0YXRlRXJyb3InKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0cmVhbXMgPSBbXS5zbGljZS5jYWxsKGFyZ3VtZW50cywgMSk7XG4gICAgICBpZiAoc3RyZWFtcy5sZW5ndGggIT09IDEgfHxcbiAgICAgICAgICAhc3RyZWFtc1swXS5nZXRUcmFja3MoKS5maW5kKHQgPT4gdCA9PT0gdHJhY2spKSB7XG4gICAgICAgIC8vIHRoaXMgaXMgbm90IGZ1bGx5IGNvcnJlY3QgYnV0IGFsbCB3ZSBjYW4gbWFuYWdlIHdpdGhvdXRcbiAgICAgICAgLy8gW1thc3NvY2lhdGVkIE1lZGlhU3RyZWFtc11dIGludGVybmFsIHNsb3QuXG4gICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oXG4gICAgICAgICAgJ1RoZSBhZGFwdGVyLmpzIGFkZFRyYWNrIHBvbHlmaWxsIG9ubHkgc3VwcG9ydHMgYSBzaW5nbGUgJyArXG4gICAgICAgICAgJyBzdHJlYW0gd2hpY2ggaXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBzcGVjaWZpZWQgdHJhY2suJyxcbiAgICAgICAgICAnTm90U3VwcG9ydGVkRXJyb3InKTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYWxyZWFkeUV4aXN0cyA9IHRoaXMuZ2V0U2VuZGVycygpLmZpbmQocyA9PiBzLnRyYWNrID09PSB0cmFjayk7XG4gICAgICBpZiAoYWxyZWFkeUV4aXN0cykge1xuICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdUcmFjayBhbHJlYWR5IGV4aXN0cy4nLFxuICAgICAgICAgICdJbnZhbGlkQWNjZXNzRXJyb3InKTtcbiAgICAgIH1cblxuICAgICAgdGhpcy5fc3RyZWFtcyA9IHRoaXMuX3N0cmVhbXMgfHwge307XG4gICAgICB0aGlzLl9yZXZlcnNlU3RyZWFtcyA9IHRoaXMuX3JldmVyc2VTdHJlYW1zIHx8IHt9O1xuICAgICAgY29uc3Qgb2xkU3RyZWFtID0gdGhpcy5fc3RyZWFtc1tzdHJlYW0uaWRdO1xuICAgICAgaWYgKG9sZFN0cmVhbSkge1xuICAgICAgICAvLyB0aGlzIGlzIHVzaW5nIG9kZCBDaHJvbWUgYmVoYXZpb3VyLCB1c2Ugd2l0aCBjYXV0aW9uOlxuICAgICAgICAvLyBodHRwczovL2J1Z3MuY2hyb21pdW0ub3JnL3Avd2VicnRjL2lzc3Vlcy9kZXRhaWw/aWQ9NzgxNVxuICAgICAgICAvLyBOb3RlOiB3ZSByZWx5IG9uIHRoZSBoaWdoLWxldmVsIGFkZFRyYWNrL2R0bWYgc2hpbSB0b1xuICAgICAgICAvLyBjcmVhdGUgdGhlIHNlbmRlciB3aXRoIGEgZHRtZiBzZW5kZXIuXG4gICAgICAgIG9sZFN0cmVhbS5hZGRUcmFjayh0cmFjayk7XG5cbiAgICAgICAgLy8gVHJpZ2dlciBPTk4gYXN5bmMuXG4gICAgICAgIFByb21pc2UucmVzb2x2ZSgpLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHRoaXMuZGlzcGF0Y2hFdmVudChuZXcgRXZlbnQoJ25lZ290aWF0aW9ubmVlZGVkJykpO1xuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IG5ld1N0cmVhbSA9IG5ldyB3aW5kb3cuTWVkaWFTdHJlYW0oW3RyYWNrXSk7XG4gICAgICAgIHRoaXMuX3N0cmVhbXNbc3RyZWFtLmlkXSA9IG5ld1N0cmVhbTtcbiAgICAgICAgdGhpcy5fcmV2ZXJzZVN0cmVhbXNbbmV3U3RyZWFtLmlkXSA9IHN0cmVhbTtcbiAgICAgICAgdGhpcy5hZGRTdHJlYW0obmV3U3RyZWFtKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLmdldFNlbmRlcnMoKS5maW5kKHMgPT4gcy50cmFjayA9PT0gdHJhY2spO1xuICAgIH07XG5cbiAgLy8gcmVwbGFjZSB0aGUgaW50ZXJuYWwgc3RyZWFtIGlkIHdpdGggdGhlIGV4dGVybmFsIG9uZSBhbmRcbiAgLy8gdmljZSB2ZXJzYS5cbiAgZnVuY3Rpb24gcmVwbGFjZUludGVybmFsU3RyZWFtSWQocGMsIGRlc2NyaXB0aW9uKSB7XG4gICAgbGV0IHNkcCA9IGRlc2NyaXB0aW9uLnNkcDtcbiAgICBPYmplY3Qua2V5cyhwYy5fcmV2ZXJzZVN0cmVhbXMgfHwgW10pLmZvckVhY2goaW50ZXJuYWxJZCA9PiB7XG4gICAgICBjb25zdCBleHRlcm5hbFN0cmVhbSA9IHBjLl9yZXZlcnNlU3RyZWFtc1tpbnRlcm5hbElkXTtcbiAgICAgIGNvbnN0IGludGVybmFsU3RyZWFtID0gcGMuX3N0cmVhbXNbZXh0ZXJuYWxTdHJlYW0uaWRdO1xuICAgICAgc2RwID0gc2RwLnJlcGxhY2UobmV3IFJlZ0V4cChpbnRlcm5hbFN0cmVhbS5pZCwgJ2cnKSxcbiAgICAgICAgZXh0ZXJuYWxTdHJlYW0uaWQpO1xuICAgIH0pO1xuICAgIHJldHVybiBuZXcgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKHtcbiAgICAgIHR5cGU6IGRlc2NyaXB0aW9uLnR5cGUsXG4gICAgICBzZHBcbiAgICB9KTtcbiAgfVxuICBmdW5jdGlvbiByZXBsYWNlRXh0ZXJuYWxTdHJlYW1JZChwYywgZGVzY3JpcHRpb24pIHtcbiAgICBsZXQgc2RwID0gZGVzY3JpcHRpb24uc2RwO1xuICAgIE9iamVjdC5rZXlzKHBjLl9yZXZlcnNlU3RyZWFtcyB8fCBbXSkuZm9yRWFjaChpbnRlcm5hbElkID0+IHtcbiAgICAgIGNvbnN0IGV4dGVybmFsU3RyZWFtID0gcGMuX3JldmVyc2VTdHJlYW1zW2ludGVybmFsSWRdO1xuICAgICAgY29uc3QgaW50ZXJuYWxTdHJlYW0gPSBwYy5fc3RyZWFtc1tleHRlcm5hbFN0cmVhbS5pZF07XG4gICAgICBzZHAgPSBzZHAucmVwbGFjZShuZXcgUmVnRXhwKGV4dGVybmFsU3RyZWFtLmlkLCAnZycpLFxuICAgICAgICBpbnRlcm5hbFN0cmVhbS5pZCk7XG4gICAgfSk7XG4gICAgcmV0dXJuIG5ldyBSVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgdHlwZTogZGVzY3JpcHRpb24udHlwZSxcbiAgICAgIHNkcFxuICAgIH0pO1xuICB9XG4gIFsnY3JlYXRlT2ZmZXInLCAnY3JlYXRlQW5zd2VyJ10uZm9yRWFjaChmdW5jdGlvbihtZXRob2QpIHtcbiAgICBjb25zdCBuYXRpdmVNZXRob2QgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlW21ldGhvZF07XG4gICAgY29uc3QgbWV0aG9kT2JqID0ge1ttZXRob2RdKCkge1xuICAgICAgY29uc3QgYXJncyA9IGFyZ3VtZW50cztcbiAgICAgIGNvbnN0IGlzTGVnYWN5Q2FsbCA9IGFyZ3VtZW50cy5sZW5ndGggJiZcbiAgICAgICAgICB0eXBlb2YgYXJndW1lbnRzWzBdID09PSAnZnVuY3Rpb24nO1xuICAgICAgaWYgKGlzTGVnYWN5Q2FsbCkge1xuICAgICAgICByZXR1cm4gbmF0aXZlTWV0aG9kLmFwcGx5KHRoaXMsIFtcbiAgICAgICAgICAoZGVzY3JpcHRpb24pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGRlc2MgPSByZXBsYWNlSW50ZXJuYWxTdHJlYW1JZCh0aGlzLCBkZXNjcmlwdGlvbik7XG4gICAgICAgICAgICBhcmdzWzBdLmFwcGx5KG51bGwsIFtkZXNjXSk7XG4gICAgICAgICAgfSxcbiAgICAgICAgICAoZXJyKSA9PiB7XG4gICAgICAgICAgICBpZiAoYXJnc1sxXSkge1xuICAgICAgICAgICAgICBhcmdzWzFdLmFwcGx5KG51bGwsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSwgYXJndW1lbnRzWzJdXG4gICAgICAgIF0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG5hdGl2ZU1ldGhvZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpXG4gICAgICAgIC50aGVuKGRlc2NyaXB0aW9uID0+IHJlcGxhY2VJbnRlcm5hbFN0cmVhbUlkKHRoaXMsIGRlc2NyaXB0aW9uKSk7XG4gICAgfX07XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZVttZXRob2RdID0gbWV0aG9kT2JqW21ldGhvZF07XG4gIH0pO1xuXG4gIGNvbnN0IG9yaWdTZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbjtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICBmdW5jdGlvbiBzZXRMb2NhbERlc2NyaXB0aW9uKCkge1xuICAgICAgaWYgKCFhcmd1bWVudHMubGVuZ3RoIHx8ICFhcmd1bWVudHNbMF0udHlwZSkge1xuICAgICAgICByZXR1cm4gb3JpZ1NldExvY2FsRGVzY3JpcHRpb24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgIH1cbiAgICAgIGFyZ3VtZW50c1swXSA9IHJlcGxhY2VFeHRlcm5hbFN0cmVhbUlkKHRoaXMsIGFyZ3VtZW50c1swXSk7XG4gICAgICByZXR1cm4gb3JpZ1NldExvY2FsRGVzY3JpcHRpb24uYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9O1xuXG4gIC8vIFRPRE86IG1hbmdsZSBnZXRTdGF0czogaHR0cHM6Ly93M2MuZ2l0aHViLmlvL3dlYnJ0Yy1zdGF0cy8jZG9tLXJ0Y21lZGlhc3RyZWFtc3RhdHMtc3RyZWFtaWRlbnRpZmllclxuXG4gIGNvbnN0IG9yaWdMb2NhbERlc2NyaXB0aW9uID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihcbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLCAnbG9jYWxEZXNjcmlwdGlvbicpO1xuICBPYmplY3QuZGVmaW5lUHJvcGVydHkod2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSxcbiAgICAnbG9jYWxEZXNjcmlwdGlvbicsIHtcbiAgICAgIGdldCgpIHtcbiAgICAgICAgY29uc3QgZGVzY3JpcHRpb24gPSBvcmlnTG9jYWxEZXNjcmlwdGlvbi5nZXQuYXBwbHkodGhpcyk7XG4gICAgICAgIGlmIChkZXNjcmlwdGlvbi50eXBlID09PSAnJykge1xuICAgICAgICAgIHJldHVybiBkZXNjcmlwdGlvbjtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcmVwbGFjZUludGVybmFsU3RyZWFtSWQodGhpcywgZGVzY3JpcHRpb24pO1xuICAgICAgfVxuICAgIH0pO1xuXG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlVHJhY2sgPVxuICAgIGZ1bmN0aW9uIHJlbW92ZVRyYWNrKHNlbmRlcikge1xuICAgICAgaWYgKHRoaXMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oXG4gICAgICAgICAgJ1RoZSBSVENQZWVyQ29ubmVjdGlvblxcJ3Mgc2lnbmFsaW5nU3RhdGUgaXMgXFwnY2xvc2VkXFwnLicsXG4gICAgICAgICAgJ0ludmFsaWRTdGF0ZUVycm9yJyk7XG4gICAgICB9XG4gICAgICAvLyBXZSBjYW4gbm90IHlldCBjaGVjayBmb3Igc2VuZGVyIGluc3RhbmNlb2YgUlRDUnRwU2VuZGVyXG4gICAgICAvLyBzaW5jZSB3ZSBzaGltIFJUUFNlbmRlci4gU28gd2UgY2hlY2sgaWYgc2VuZGVyLl9wYyBpcyBzZXQuXG4gICAgICBpZiAoIXNlbmRlci5fcGMpIHtcbiAgICAgICAgdGhyb3cgbmV3IERPTUV4Y2VwdGlvbignQXJndW1lbnQgMSBvZiBSVENQZWVyQ29ubmVjdGlvbi5yZW1vdmVUcmFjayAnICtcbiAgICAgICAgICAgICdkb2VzIG5vdCBpbXBsZW1lbnQgaW50ZXJmYWNlIFJUQ1J0cFNlbmRlci4nLCAnVHlwZUVycm9yJyk7XG4gICAgICB9XG4gICAgICBjb25zdCBpc0xvY2FsID0gc2VuZGVyLl9wYyA9PT0gdGhpcztcbiAgICAgIGlmICghaXNMb2NhbCkge1xuICAgICAgICB0aHJvdyBuZXcgRE9NRXhjZXB0aW9uKCdTZW5kZXIgd2FzIG5vdCBjcmVhdGVkIGJ5IHRoaXMgY29ubmVjdGlvbi4nLFxuICAgICAgICAgICdJbnZhbGlkQWNjZXNzRXJyb3InKTtcbiAgICAgIH1cblxuICAgICAgLy8gU2VhcmNoIGZvciB0aGUgbmF0aXZlIHN0cmVhbSB0aGUgc2VuZGVycyB0cmFjayBiZWxvbmdzIHRvLlxuICAgICAgdGhpcy5fc3RyZWFtcyA9IHRoaXMuX3N0cmVhbXMgfHwge307XG4gICAgICBsZXQgc3RyZWFtO1xuICAgICAgT2JqZWN0LmtleXModGhpcy5fc3RyZWFtcykuZm9yRWFjaChzdHJlYW1pZCA9PiB7XG4gICAgICAgIGNvbnN0IGhhc1RyYWNrID0gdGhpcy5fc3RyZWFtc1tzdHJlYW1pZF0uZ2V0VHJhY2tzKClcbiAgICAgICAgICAuZmluZCh0cmFjayA9PiBzZW5kZXIudHJhY2sgPT09IHRyYWNrKTtcbiAgICAgICAgaWYgKGhhc1RyYWNrKSB7XG4gICAgICAgICAgc3RyZWFtID0gdGhpcy5fc3RyZWFtc1tzdHJlYW1pZF07XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBpZiAoc3RyZWFtKSB7XG4gICAgICAgIGlmIChzdHJlYW0uZ2V0VHJhY2tzKCkubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgLy8gaWYgdGhpcyBpcyB0aGUgbGFzdCB0cmFjayBvZiB0aGUgc3RyZWFtLCByZW1vdmUgdGhlIHN0cmVhbS4gVGhpc1xuICAgICAgICAgIC8vIHRha2VzIGNhcmUgb2YgYW55IHNoaW1tZWQgX3NlbmRlcnMuXG4gICAgICAgICAgdGhpcy5yZW1vdmVTdHJlYW0odGhpcy5fcmV2ZXJzZVN0cmVhbXNbc3RyZWFtLmlkXSk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gcmVseWluZyBvbiB0aGUgc2FtZSBvZGQgY2hyb21lIGJlaGF2aW91ciBhcyBhYm92ZS5cbiAgICAgICAgICBzdHJlYW0ucmVtb3ZlVHJhY2soc2VuZGVyLnRyYWNrKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmRpc3BhdGNoRXZlbnQobmV3IEV2ZW50KCduZWdvdGlhdGlvbm5lZWRlZCcpKTtcbiAgICAgIH1cbiAgICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbVBlZXJDb25uZWN0aW9uKHdpbmRvdywgYnJvd3NlckRldGFpbHMpIHtcbiAgaWYgKCF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gJiYgd2luZG93LndlYmtpdFJUQ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgLy8gdmVyeSBiYXNpYyBzdXBwb3J0IGZvciBvbGQgdmVyc2lvbnMuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uID0gd2luZG93LndlYmtpdFJUQ1BlZXJDb25uZWN0aW9uO1xuICB9XG4gIGlmICghd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gc2hpbSBpbXBsaWNpdCBjcmVhdGlvbiBvZiBSVENTZXNzaW9uRGVzY3JpcHRpb24vUlRDSWNlQ2FuZGlkYXRlXG4gIGlmIChicm93c2VyRGV0YWlscy52ZXJzaW9uIDwgNTMpIHtcbiAgICBbJ3NldExvY2FsRGVzY3JpcHRpb24nLCAnc2V0UmVtb3RlRGVzY3JpcHRpb24nLCAnYWRkSWNlQ2FuZGlkYXRlJ11cbiAgICAgIC5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgICBjb25zdCBuYXRpdmVNZXRob2QgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlW21ldGhvZF07XG4gICAgICAgIGNvbnN0IG1ldGhvZE9iaiA9IHtbbWV0aG9kXSgpIHtcbiAgICAgICAgICBhcmd1bWVudHNbMF0gPSBuZXcgKChtZXRob2QgPT09ICdhZGRJY2VDYW5kaWRhdGUnKSA/XG4gICAgICAgICAgICB3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlIDpcbiAgICAgICAgICAgIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24pKGFyZ3VtZW50c1swXSk7XG4gICAgICAgICAgcmV0dXJuIG5hdGl2ZU1ldGhvZC5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICB9fTtcbiAgICAgICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZVttZXRob2RdID0gbWV0aG9kT2JqW21ldGhvZF07XG4gICAgICB9KTtcbiAgfVxufVxuXG4vLyBBdHRlbXB0IHRvIGZpeCBPTk4gaW4gcGxhbi1iIG1vZGUuXG5leHBvcnQgZnVuY3Rpb24gZml4TmVnb3RpYXRpb25OZWVkZWQod2luZG93LCBicm93c2VyRGV0YWlscykge1xuICB1dGlscy53cmFwUGVlckNvbm5lY3Rpb25FdmVudCh3aW5kb3csICduZWdvdGlhdGlvbm5lZWRlZCcsIGUgPT4ge1xuICAgIGNvbnN0IHBjID0gZS50YXJnZXQ7XG4gICAgaWYgKGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPCA3MiB8fCAocGMuZ2V0Q29uZmlndXJhdGlvbiAmJlxuICAgICAgICBwYy5nZXRDb25maWd1cmF0aW9uKCkuc2RwU2VtYW50aWNzID09PSAncGxhbi1iJykpIHtcbiAgICAgIGlmIChwYy5zaWduYWxpbmdTdGF0ZSAhPT0gJ3N0YWJsZScpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gZTtcbiAgfSk7XG59XG4iLCIvKlxuICogIENvcHlyaWdodCAoYykgMjAxOCBUaGUgYWRhcHRlci5qcyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4vKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcbmV4cG9ydCBmdW5jdGlvbiBzaGltR2V0RGlzcGxheU1lZGlhKHdpbmRvdywgZ2V0U291cmNlSWQpIHtcbiAgaWYgKHdpbmRvdy5uYXZpZ2F0b3IubWVkaWFEZXZpY2VzICYmXG4gICAgJ2dldERpc3BsYXlNZWRpYScgaW4gd2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKCEod2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMpKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIC8vIGdldFNvdXJjZUlkIGlzIGEgZnVuY3Rpb24gdGhhdCByZXR1cm5zIGEgcHJvbWlzZSByZXNvbHZpbmcgd2l0aFxuICAvLyB0aGUgc291cmNlSWQgb2YgdGhlIHNjcmVlbi93aW5kb3cvdGFiIHRvIGJlIHNoYXJlZC5cbiAgaWYgKHR5cGVvZiBnZXRTb3VyY2VJZCAhPT0gJ2Z1bmN0aW9uJykge1xuICAgIGNvbnNvbGUuZXJyb3IoJ3NoaW1HZXREaXNwbGF5TWVkaWE6IGdldFNvdXJjZUlkIGFyZ3VtZW50IGlzIG5vdCAnICtcbiAgICAgICAgJ2EgZnVuY3Rpb24nKTtcbiAgICByZXR1cm47XG4gIH1cbiAgd2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0RGlzcGxheU1lZGlhID1cbiAgICBmdW5jdGlvbiBnZXREaXNwbGF5TWVkaWEoY29uc3RyYWludHMpIHtcbiAgICAgIHJldHVybiBnZXRTb3VyY2VJZChjb25zdHJhaW50cylcbiAgICAgICAgLnRoZW4oc291cmNlSWQgPT4ge1xuICAgICAgICAgIGNvbnN0IHdpZHRoU3BlY2lmaWVkID0gY29uc3RyYWludHMudmlkZW8gJiYgY29uc3RyYWludHMudmlkZW8ud2lkdGg7XG4gICAgICAgICAgY29uc3QgaGVpZ2h0U3BlY2lmaWVkID0gY29uc3RyYWludHMudmlkZW8gJiZcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLmhlaWdodDtcbiAgICAgICAgICBjb25zdCBmcmFtZVJhdGVTcGVjaWZpZWQgPSBjb25zdHJhaW50cy52aWRlbyAmJlxuICAgICAgICAgICAgY29uc3RyYWludHMudmlkZW8uZnJhbWVSYXRlO1xuICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0ge1xuICAgICAgICAgICAgbWFuZGF0b3J5OiB7XG4gICAgICAgICAgICAgIGNocm9tZU1lZGlhU291cmNlOiAnZGVza3RvcCcsXG4gICAgICAgICAgICAgIGNocm9tZU1lZGlhU291cmNlSWQ6IHNvdXJjZUlkLFxuICAgICAgICAgICAgICBtYXhGcmFtZVJhdGU6IGZyYW1lUmF0ZVNwZWNpZmllZCB8fCAzXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfTtcbiAgICAgICAgICBpZiAod2lkdGhTcGVjaWZpZWQpIHtcbiAgICAgICAgICAgIGNvbnN0cmFpbnRzLnZpZGVvLm1hbmRhdG9yeS5tYXhXaWR0aCA9IHdpZHRoU3BlY2lmaWVkO1xuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoaGVpZ2h0U3BlY2lmaWVkKSB7XG4gICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5tYW5kYXRvcnkubWF4SGVpZ2h0ID0gaGVpZ2h0U3BlY2lmaWVkO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gd2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzKTtcbiAgICAgICAgfSk7XG4gICAgfTtcbn1cbiIsIi8qXG4gKiAgQ29weXJpZ2h0IChjKSAyMDE2IFRoZSBXZWJSVEMgcHJvamVjdCBhdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlXG4gKiAgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBvZiB0aGUgc291cmNlXG4gKiAgdHJlZS5cbiAqL1xuLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tICcuLi91dGlscy5qcyc7XG5jb25zdCBsb2dnaW5nID0gdXRpbHMubG9nO1xuXG5leHBvcnQgZnVuY3Rpb24gc2hpbUdldFVzZXJNZWRpYSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKSB7XG4gIGNvbnN0IG5hdmlnYXRvciA9IHdpbmRvdyAmJiB3aW5kb3cubmF2aWdhdG9yO1xuXG4gIGlmICghbmF2aWdhdG9yLm1lZGlhRGV2aWNlcykge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGNvbnN0cmFpbnRzVG9DaHJvbWVfID0gZnVuY3Rpb24oYykge1xuICAgIGlmICh0eXBlb2YgYyAhPT0gJ29iamVjdCcgfHwgYy5tYW5kYXRvcnkgfHwgYy5vcHRpb25hbCkge1xuICAgICAgcmV0dXJuIGM7XG4gICAgfVxuICAgIGNvbnN0IGNjID0ge307XG4gICAgT2JqZWN0LmtleXMoYykuZm9yRWFjaChrZXkgPT4ge1xuICAgICAgaWYgKGtleSA9PT0gJ3JlcXVpcmUnIHx8IGtleSA9PT0gJ2FkdmFuY2VkJyB8fCBrZXkgPT09ICdtZWRpYVNvdXJjZScpIHtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgY29uc3QgciA9ICh0eXBlb2YgY1trZXldID09PSAnb2JqZWN0JykgPyBjW2tleV0gOiB7aWRlYWw6IGNba2V5XX07XG4gICAgICBpZiAoci5leGFjdCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiByLmV4YWN0ID09PSAnbnVtYmVyJykge1xuICAgICAgICByLm1pbiA9IHIubWF4ID0gci5leGFjdDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IG9sZG5hbWVfID0gZnVuY3Rpb24ocHJlZml4LCBuYW1lKSB7XG4gICAgICAgIGlmIChwcmVmaXgpIHtcbiAgICAgICAgICByZXR1cm4gcHJlZml4ICsgbmFtZS5jaGFyQXQoMCkudG9VcHBlckNhc2UoKSArIG5hbWUuc2xpY2UoMSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIChuYW1lID09PSAnZGV2aWNlSWQnKSA/ICdzb3VyY2VJZCcgOiBuYW1lO1xuICAgICAgfTtcbiAgICAgIGlmIChyLmlkZWFsICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY2Mub3B0aW9uYWwgPSBjYy5vcHRpb25hbCB8fCBbXTtcbiAgICAgICAgbGV0IG9jID0ge307XG4gICAgICAgIGlmICh0eXBlb2Ygci5pZGVhbCA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICBvY1tvbGRuYW1lXygnbWluJywga2V5KV0gPSByLmlkZWFsO1xuICAgICAgICAgIGNjLm9wdGlvbmFsLnB1c2gob2MpO1xuICAgICAgICAgIG9jID0ge307XG4gICAgICAgICAgb2Nbb2xkbmFtZV8oJ21heCcsIGtleSldID0gci5pZGVhbDtcbiAgICAgICAgICBjYy5vcHRpb25hbC5wdXNoKG9jKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvY1tvbGRuYW1lXygnJywga2V5KV0gPSByLmlkZWFsO1xuICAgICAgICAgIGNjLm9wdGlvbmFsLnB1c2gob2MpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoci5leGFjdCAhPT0gdW5kZWZpbmVkICYmIHR5cGVvZiByLmV4YWN0ICE9PSAnbnVtYmVyJykge1xuICAgICAgICBjYy5tYW5kYXRvcnkgPSBjYy5tYW5kYXRvcnkgfHwge307XG4gICAgICAgIGNjLm1hbmRhdG9yeVtvbGRuYW1lXygnJywga2V5KV0gPSByLmV4YWN0O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgWydtaW4nLCAnbWF4J10uZm9yRWFjaChtaXggPT4ge1xuICAgICAgICAgIGlmIChyW21peF0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgY2MubWFuZGF0b3J5ID0gY2MubWFuZGF0b3J5IHx8IHt9O1xuICAgICAgICAgICAgY2MubWFuZGF0b3J5W29sZG5hbWVfKG1peCwga2V5KV0gPSByW21peF07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgICBpZiAoYy5hZHZhbmNlZCkge1xuICAgICAgY2Mub3B0aW9uYWwgPSAoY2Mub3B0aW9uYWwgfHwgW10pLmNvbmNhdChjLmFkdmFuY2VkKTtcbiAgICB9XG4gICAgcmV0dXJuIGNjO1xuICB9O1xuXG4gIGNvbnN0IHNoaW1Db25zdHJhaW50c18gPSBmdW5jdGlvbihjb25zdHJhaW50cywgZnVuYykge1xuICAgIGlmIChicm93c2VyRGV0YWlscy52ZXJzaW9uID49IDYxKSB7XG4gICAgICByZXR1cm4gZnVuYyhjb25zdHJhaW50cyk7XG4gICAgfVxuICAgIGNvbnN0cmFpbnRzID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cykpO1xuICAgIGlmIChjb25zdHJhaW50cyAmJiB0eXBlb2YgY29uc3RyYWludHMuYXVkaW8gPT09ICdvYmplY3QnKSB7XG4gICAgICBjb25zdCByZW1hcCA9IGZ1bmN0aW9uKG9iaiwgYSwgYikge1xuICAgICAgICBpZiAoYSBpbiBvYmogJiYgIShiIGluIG9iaikpIHtcbiAgICAgICAgICBvYmpbYl0gPSBvYmpbYV07XG4gICAgICAgICAgZGVsZXRlIG9ialthXTtcbiAgICAgICAgfVxuICAgICAgfTtcbiAgICAgIGNvbnN0cmFpbnRzID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cykpO1xuICAgICAgcmVtYXAoY29uc3RyYWludHMuYXVkaW8sICdhdXRvR2FpbkNvbnRyb2wnLCAnZ29vZ0F1dG9HYWluQ29udHJvbCcpO1xuICAgICAgcmVtYXAoY29uc3RyYWludHMuYXVkaW8sICdub2lzZVN1cHByZXNzaW9uJywgJ2dvb2dOb2lzZVN1cHByZXNzaW9uJyk7XG4gICAgICBjb25zdHJhaW50cy5hdWRpbyA9IGNvbnN0cmFpbnRzVG9DaHJvbWVfKGNvbnN0cmFpbnRzLmF1ZGlvKTtcbiAgICB9XG4gICAgaWYgKGNvbnN0cmFpbnRzICYmIHR5cGVvZiBjb25zdHJhaW50cy52aWRlbyA9PT0gJ29iamVjdCcpIHtcbiAgICAgIC8vIFNoaW0gZmFjaW5nTW9kZSBmb3IgbW9iaWxlICYgc3VyZmFjZSBwcm8uXG4gICAgICBsZXQgZmFjZSA9IGNvbnN0cmFpbnRzLnZpZGVvLmZhY2luZ01vZGU7XG4gICAgICBmYWNlID0gZmFjZSAmJiAoKHR5cGVvZiBmYWNlID09PSAnb2JqZWN0JykgPyBmYWNlIDoge2lkZWFsOiBmYWNlfSk7XG4gICAgICBjb25zdCBnZXRTdXBwb3J0ZWRGYWNpbmdNb2RlTGllcyA9IGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPCA2NjtcblxuICAgICAgaWYgKChmYWNlICYmIChmYWNlLmV4YWN0ID09PSAndXNlcicgfHwgZmFjZS5leGFjdCA9PT0gJ2Vudmlyb25tZW50JyB8fFxuICAgICAgICAgICAgICAgICAgICBmYWNlLmlkZWFsID09PSAndXNlcicgfHwgZmFjZS5pZGVhbCA9PT0gJ2Vudmlyb25tZW50JykpICYmXG4gICAgICAgICAgIShuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFN1cHBvcnRlZENvbnN0cmFpbnRzICYmXG4gICAgICAgICAgICBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFN1cHBvcnRlZENvbnN0cmFpbnRzKCkuZmFjaW5nTW9kZSAmJlxuICAgICAgICAgICAgIWdldFN1cHBvcnRlZEZhY2luZ01vZGVMaWVzKSkge1xuICAgICAgICBkZWxldGUgY29uc3RyYWludHMudmlkZW8uZmFjaW5nTW9kZTtcbiAgICAgICAgbGV0IG1hdGNoZXM7XG4gICAgICAgIGlmIChmYWNlLmV4YWN0ID09PSAnZW52aXJvbm1lbnQnIHx8IGZhY2UuaWRlYWwgPT09ICdlbnZpcm9ubWVudCcpIHtcbiAgICAgICAgICBtYXRjaGVzID0gWydiYWNrJywgJ3JlYXInXTtcbiAgICAgICAgfSBlbHNlIGlmIChmYWNlLmV4YWN0ID09PSAndXNlcicgfHwgZmFjZS5pZGVhbCA9PT0gJ3VzZXInKSB7XG4gICAgICAgICAgbWF0Y2hlcyA9IFsnZnJvbnQnXTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobWF0Y2hlcykge1xuICAgICAgICAgIC8vIExvb2sgZm9yIG1hdGNoZXMgaW4gbGFiZWwsIG9yIHVzZSBsYXN0IGNhbSBmb3IgYmFjayAodHlwaWNhbCkuXG4gICAgICAgICAgcmV0dXJuIG5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcygpXG4gICAgICAgICAgICAudGhlbihkZXZpY2VzID0+IHtcbiAgICAgICAgICAgICAgZGV2aWNlcyA9IGRldmljZXMuZmlsdGVyKGQgPT4gZC5raW5kID09PSAndmlkZW9pbnB1dCcpO1xuICAgICAgICAgICAgICBsZXQgZGV2ID0gZGV2aWNlcy5maW5kKGQgPT4gbWF0Y2hlcy5zb21lKG1hdGNoID0+XG4gICAgICAgICAgICAgICAgZC5sYWJlbC50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKG1hdGNoKSkpO1xuICAgICAgICAgICAgICBpZiAoIWRldiAmJiBkZXZpY2VzLmxlbmd0aCAmJiBtYXRjaGVzLmluY2x1ZGVzKCdiYWNrJykpIHtcbiAgICAgICAgICAgICAgICBkZXYgPSBkZXZpY2VzW2RldmljZXMubGVuZ3RoIC0gMV07IC8vIG1vcmUgbGlrZWx5IHRoZSBiYWNrIGNhbVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChkZXYpIHtcbiAgICAgICAgICAgICAgICBjb25zdHJhaW50cy52aWRlby5kZXZpY2VJZCA9IGZhY2UuZXhhY3RcbiAgICAgICAgICAgICAgICAgID8ge2V4YWN0OiBkZXYuZGV2aWNlSWR9XG4gICAgICAgICAgICAgICAgICA6IHtpZGVhbDogZGV2LmRldmljZUlkfTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IGNvbnN0cmFpbnRzVG9DaHJvbWVfKGNvbnN0cmFpbnRzLnZpZGVvKTtcbiAgICAgICAgICAgICAgbG9nZ2luZygnY2hyb21lOiAnICsgSlNPTi5zdHJpbmdpZnkoY29uc3RyYWludHMpKTtcbiAgICAgICAgICAgICAgcmV0dXJuIGZ1bmMoY29uc3RyYWludHMpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGNvbnN0cmFpbnRzLnZpZGVvID0gY29uc3RyYWludHNUb0Nocm9tZV8oY29uc3RyYWludHMudmlkZW8pO1xuICAgIH1cbiAgICBsb2dnaW5nKCdjaHJvbWU6ICcgKyBKU09OLnN0cmluZ2lmeShjb25zdHJhaW50cykpO1xuICAgIHJldHVybiBmdW5jKGNvbnN0cmFpbnRzKTtcbiAgfTtcblxuICBjb25zdCBzaGltRXJyb3JfID0gZnVuY3Rpb24oZSkge1xuICAgIGlmIChicm93c2VyRGV0YWlscy52ZXJzaW9uID49IDY0KSB7XG4gICAgICByZXR1cm4gZTtcbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIG5hbWU6IHtcbiAgICAgICAgUGVybWlzc2lvbkRlbmllZEVycm9yOiAnTm90QWxsb3dlZEVycm9yJyxcbiAgICAgICAgUGVybWlzc2lvbkRpc21pc3NlZEVycm9yOiAnTm90QWxsb3dlZEVycm9yJyxcbiAgICAgICAgSW52YWxpZFN0YXRlRXJyb3I6ICdOb3RBbGxvd2VkRXJyb3InLFxuICAgICAgICBEZXZpY2VzTm90Rm91bmRFcnJvcjogJ05vdEZvdW5kRXJyb3InLFxuICAgICAgICBDb25zdHJhaW50Tm90U2F0aXNmaWVkRXJyb3I6ICdPdmVyY29uc3RyYWluZWRFcnJvcicsXG4gICAgICAgIFRyYWNrU3RhcnRFcnJvcjogJ05vdFJlYWRhYmxlRXJyb3InLFxuICAgICAgICBNZWRpYURldmljZUZhaWxlZER1ZVRvU2h1dGRvd246ICdOb3RBbGxvd2VkRXJyb3InLFxuICAgICAgICBNZWRpYURldmljZUtpbGxTd2l0Y2hPbjogJ05vdEFsbG93ZWRFcnJvcicsXG4gICAgICAgIFRhYkNhcHR1cmVFcnJvcjogJ0Fib3J0RXJyb3InLFxuICAgICAgICBTY3JlZW5DYXB0dXJlRXJyb3I6ICdBYm9ydEVycm9yJyxcbiAgICAgICAgRGV2aWNlQ2FwdHVyZUVycm9yOiAnQWJvcnRFcnJvcidcbiAgICAgIH1bZS5uYW1lXSB8fCBlLm5hbWUsXG4gICAgICBtZXNzYWdlOiBlLm1lc3NhZ2UsXG4gICAgICBjb25zdHJhaW50OiBlLmNvbnN0cmFpbnQgfHwgZS5jb25zdHJhaW50TmFtZSxcbiAgICAgIHRvU3RyaW5nKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5uYW1lICsgKHRoaXMubWVzc2FnZSAmJiAnOiAnKSArIHRoaXMubWVzc2FnZTtcbiAgICAgIH1cbiAgICB9O1xuICB9O1xuXG4gIGNvbnN0IGdldFVzZXJNZWRpYV8gPSBmdW5jdGlvbihjb25zdHJhaW50cywgb25TdWNjZXNzLCBvbkVycm9yKSB7XG4gICAgc2hpbUNvbnN0cmFpbnRzXyhjb25zdHJhaW50cywgYyA9PiB7XG4gICAgICBuYXZpZ2F0b3Iud2Via2l0R2V0VXNlck1lZGlhKGMsIG9uU3VjY2VzcywgZSA9PiB7XG4gICAgICAgIGlmIChvbkVycm9yKSB7XG4gICAgICAgICAgb25FcnJvcihzaGltRXJyb3JfKGUpKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG4gIH07XG4gIG5hdmlnYXRvci5nZXRVc2VyTWVkaWEgPSBnZXRVc2VyTWVkaWFfLmJpbmQobmF2aWdhdG9yKTtcblxuICAvLyBFdmVuIHRob3VnaCBDaHJvbWUgNDUgaGFzIG5hdmlnYXRvci5tZWRpYURldmljZXMgYW5kIGEgZ2V0VXNlck1lZGlhXG4gIC8vIGZ1bmN0aW9uIHdoaWNoIHJldHVybnMgYSBQcm9taXNlLCBpdCBkb2VzIG5vdCBhY2NlcHQgc3BlYy1zdHlsZVxuICAvLyBjb25zdHJhaW50cy5cbiAgaWYgKG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKSB7XG4gICAgY29uc3Qgb3JpZ0dldFVzZXJNZWRpYSA9IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhLlxuICAgICAgYmluZChuYXZpZ2F0b3IubWVkaWFEZXZpY2VzKTtcbiAgICBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uKGNzKSB7XG4gICAgICByZXR1cm4gc2hpbUNvbnN0cmFpbnRzXyhjcywgYyA9PiBvcmlnR2V0VXNlck1lZGlhKGMpLnRoZW4oc3RyZWFtID0+IHtcbiAgICAgICAgaWYgKGMuYXVkaW8gJiYgIXN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmxlbmd0aCB8fFxuICAgICAgICAgICAgYy52aWRlbyAmJiAhc3RyZWFtLmdldFZpZGVvVHJhY2tzKCkubGVuZ3RoKSB7XG4gICAgICAgICAgc3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2godHJhY2sgPT4ge1xuICAgICAgICAgICAgdHJhY2suc3RvcCgpO1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIHRocm93IG5ldyBET01FeGNlcHRpb24oJycsICdOb3RGb3VuZEVycm9yJyk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHN0cmVhbTtcbiAgICAgIH0sIGUgPT4gUHJvbWlzZS5yZWplY3Qoc2hpbUVycm9yXyhlKSkpKTtcbiAgICB9O1xuICB9XG59XG4iLCIvKlxuICogIENvcHlyaWdodCAoYykgMjAxNyBUaGUgV2ViUlRDIHByb2plY3QgYXV0aG9ycy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiAgVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYSBCU0Qtc3R5bGUgbGljZW5zZVxuICogIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3Qgb2YgdGhlIHNvdXJjZVxuICogIHRyZWUuXG4gKi9cbi8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG5pbXBvcnQgU0RQVXRpbHMgZnJvbSAnc2RwJztcbmltcG9ydCAqIGFzIHV0aWxzIGZyb20gJy4vdXRpbHMnO1xuXG5leHBvcnQgZnVuY3Rpb24gc2hpbVJUQ0ljZUNhbmRpZGF0ZSh3aW5kb3cpIHtcbiAgLy8gZm91bmRhdGlvbiBpcyBhcmJpdHJhcmlseSBjaG9zZW4gYXMgYW4gaW5kaWNhdG9yIGZvciBmdWxsIHN1cHBvcnQgZm9yXG4gIC8vIGh0dHBzOi8vdzNjLmdpdGh1Yi5pby93ZWJydGMtcGMvI3J0Y2ljZWNhbmRpZGF0ZS1pbnRlcmZhY2VcbiAgaWYgKCF3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlIHx8ICh3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlICYmICdmb3VuZGF0aW9uJyBpblxuICAgICAgd2luZG93LlJUQ0ljZUNhbmRpZGF0ZS5wcm90b3R5cGUpKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgTmF0aXZlUlRDSWNlQ2FuZGlkYXRlID0gd2luZG93LlJUQ0ljZUNhbmRpZGF0ZTtcbiAgd2luZG93LlJUQ0ljZUNhbmRpZGF0ZSA9IGZ1bmN0aW9uIFJUQ0ljZUNhbmRpZGF0ZShhcmdzKSB7XG4gICAgLy8gUmVtb3ZlIHRoZSBhPSB3aGljaCBzaG91bGRuJ3QgYmUgcGFydCBvZiB0aGUgY2FuZGlkYXRlIHN0cmluZy5cbiAgICBpZiAodHlwZW9mIGFyZ3MgPT09ICdvYmplY3QnICYmIGFyZ3MuY2FuZGlkYXRlICYmXG4gICAgICAgIGFyZ3MuY2FuZGlkYXRlLmluZGV4T2YoJ2E9JykgPT09IDApIHtcbiAgICAgIGFyZ3MgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGFyZ3MpKTtcbiAgICAgIGFyZ3MuY2FuZGlkYXRlID0gYXJncy5jYW5kaWRhdGUuc3Vic3RyaW5nKDIpO1xuICAgIH1cblxuICAgIGlmIChhcmdzLmNhbmRpZGF0ZSAmJiBhcmdzLmNhbmRpZGF0ZS5sZW5ndGgpIHtcbiAgICAgIC8vIEF1Z21lbnQgdGhlIG5hdGl2ZSBjYW5kaWRhdGUgd2l0aCB0aGUgcGFyc2VkIGZpZWxkcy5cbiAgICAgIGNvbnN0IG5hdGl2ZUNhbmRpZGF0ZSA9IG5ldyBOYXRpdmVSVENJY2VDYW5kaWRhdGUoYXJncyk7XG4gICAgICBjb25zdCBwYXJzZWRDYW5kaWRhdGUgPSBTRFBVdGlscy5wYXJzZUNhbmRpZGF0ZShhcmdzLmNhbmRpZGF0ZSk7XG4gICAgICBmb3IgKGNvbnN0IGtleSBpbiBwYXJzZWRDYW5kaWRhdGUpIHtcbiAgICAgICAgaWYgKCEoa2V5IGluIG5hdGl2ZUNhbmRpZGF0ZSkpIHtcbiAgICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkobmF0aXZlQ2FuZGlkYXRlLCBrZXksXG4gICAgICAgICAgICB7dmFsdWU6IHBhcnNlZENhbmRpZGF0ZVtrZXldfSk7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gT3ZlcnJpZGUgc2VyaWFsaXplciB0byBub3Qgc2VyaWFsaXplIHRoZSBleHRyYSBhdHRyaWJ1dGVzLlxuICAgICAgbmF0aXZlQ2FuZGlkYXRlLnRvSlNPTiA9IGZ1bmN0aW9uIHRvSlNPTigpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBjYW5kaWRhdGU6IG5hdGl2ZUNhbmRpZGF0ZS5jYW5kaWRhdGUsXG4gICAgICAgICAgc2RwTWlkOiBuYXRpdmVDYW5kaWRhdGUuc2RwTWlkLFxuICAgICAgICAgIHNkcE1MaW5lSW5kZXg6IG5hdGl2ZUNhbmRpZGF0ZS5zZHBNTGluZUluZGV4LFxuICAgICAgICAgIHVzZXJuYW1lRnJhZ21lbnQ6IG5hdGl2ZUNhbmRpZGF0ZS51c2VybmFtZUZyYWdtZW50LFxuICAgICAgICB9O1xuICAgICAgfTtcbiAgICAgIHJldHVybiBuYXRpdmVDYW5kaWRhdGU7XG4gICAgfVxuICAgIHJldHVybiBuZXcgTmF0aXZlUlRDSWNlQ2FuZGlkYXRlKGFyZ3MpO1xuICB9O1xuICB3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlLnByb3RvdHlwZSA9IE5hdGl2ZVJUQ0ljZUNhbmRpZGF0ZS5wcm90b3R5cGU7XG5cbiAgLy8gSG9vayB1cCB0aGUgYXVnbWVudGVkIGNhbmRpZGF0ZSBpbiBvbmljZWNhbmRpZGF0ZSBhbmRcbiAgLy8gYWRkRXZlbnRMaXN0ZW5lcignaWNlY2FuZGlkYXRlJywgLi4uKVxuICB1dGlscy53cmFwUGVlckNvbm5lY3Rpb25FdmVudCh3aW5kb3csICdpY2VjYW5kaWRhdGUnLCBlID0+IHtcbiAgICBpZiAoZS5jYW5kaWRhdGUpIHtcbiAgICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLCAnY2FuZGlkYXRlJywge1xuICAgICAgICB2YWx1ZTogbmV3IHdpbmRvdy5SVENJY2VDYW5kaWRhdGUoZS5jYW5kaWRhdGUpLFxuICAgICAgICB3cml0YWJsZTogJ2ZhbHNlJ1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBlO1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1SVENJY2VDYW5kaWRhdGVSZWxheVByb3RvY29sKHdpbmRvdykge1xuICBpZiAoIXdpbmRvdy5SVENJY2VDYW5kaWRhdGUgfHwgKHdpbmRvdy5SVENJY2VDYW5kaWRhdGUgJiYgJ3JlbGF5UHJvdG9jb2wnIGluXG4gICAgICB3aW5kb3cuUlRDSWNlQ2FuZGlkYXRlLnByb3RvdHlwZSkpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBIb29rIHVwIHRoZSBhdWdtZW50ZWQgY2FuZGlkYXRlIGluIG9uaWNlY2FuZGlkYXRlIGFuZFxuICAvLyBhZGRFdmVudExpc3RlbmVyKCdpY2VjYW5kaWRhdGUnLCAuLi4pXG4gIHV0aWxzLndyYXBQZWVyQ29ubmVjdGlvbkV2ZW50KHdpbmRvdywgJ2ljZWNhbmRpZGF0ZScsIGUgPT4ge1xuICAgIGlmIChlLmNhbmRpZGF0ZSkge1xuICAgICAgY29uc3QgcGFyc2VkQ2FuZGlkYXRlID0gU0RQVXRpbHMucGFyc2VDYW5kaWRhdGUoZS5jYW5kaWRhdGUuY2FuZGlkYXRlKTtcbiAgICAgIGlmIChwYXJzZWRDYW5kaWRhdGUudHlwZSA9PT0gJ3JlbGF5Jykge1xuICAgICAgICAvLyBUaGlzIGlzIGEgbGlid2VicnRjLXNwZWNpZmljIG1hcHBpbmcgb2YgbG9jYWwgdHlwZSBwcmVmZXJlbmNlXG4gICAgICAgIC8vIHRvIHJlbGF5UHJvdG9jb2wuXG4gICAgICAgIGUuY2FuZGlkYXRlLnJlbGF5UHJvdG9jb2wgPSB7XG4gICAgICAgICAgMDogJ3RscycsXG4gICAgICAgICAgMTogJ3RjcCcsXG4gICAgICAgICAgMjogJ3VkcCcsXG4gICAgICAgIH1bcGFyc2VkQ2FuZGlkYXRlLnByaW9yaXR5ID4+IDI0XTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGU7XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbU1heE1lc3NhZ2VTaXplKHdpbmRvdywgYnJvd3NlckRldGFpbHMpIHtcbiAgaWYgKCF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24pIHtcbiAgICByZXR1cm47XG4gIH1cblxuICBpZiAoISgnc2N0cCcgaW4gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSkpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkod2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSwgJ3NjdHAnLCB7XG4gICAgICBnZXQoKSB7XG4gICAgICAgIHJldHVybiB0eXBlb2YgdGhpcy5fc2N0cCA9PT0gJ3VuZGVmaW5lZCcgPyBudWxsIDogdGhpcy5fc2N0cDtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIGNvbnN0IHNjdHBJbkRlc2NyaXB0aW9uID0gZnVuY3Rpb24oZGVzY3JpcHRpb24pIHtcbiAgICBpZiAoIWRlc2NyaXB0aW9uIHx8ICFkZXNjcmlwdGlvbi5zZHApIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3Qgc2VjdGlvbnMgPSBTRFBVdGlscy5zcGxpdFNlY3Rpb25zKGRlc2NyaXB0aW9uLnNkcCk7XG4gICAgc2VjdGlvbnMuc2hpZnQoKTtcbiAgICByZXR1cm4gc2VjdGlvbnMuc29tZShtZWRpYVNlY3Rpb24gPT4ge1xuICAgICAgY29uc3QgbUxpbmUgPSBTRFBVdGlscy5wYXJzZU1MaW5lKG1lZGlhU2VjdGlvbik7XG4gICAgICByZXR1cm4gbUxpbmUgJiYgbUxpbmUua2luZCA9PT0gJ2FwcGxpY2F0aW9uJ1xuICAgICAgICAgICYmIG1MaW5lLnByb3RvY29sLmluZGV4T2YoJ1NDVFAnKSAhPT0gLTE7XG4gICAgfSk7XG4gIH07XG5cbiAgY29uc3QgZ2V0UmVtb3RlRmlyZWZveFZlcnNpb24gPSBmdW5jdGlvbihkZXNjcmlwdGlvbikge1xuICAgIC8vIFRPRE86IElzIHRoZXJlIGEgYmV0dGVyIHNvbHV0aW9uIGZvciBkZXRlY3RpbmcgRmlyZWZveD9cbiAgICBjb25zdCBtYXRjaCA9IGRlc2NyaXB0aW9uLnNkcC5tYXRjaCgvbW96aWxsYS4uLlRISVNfSVNfU0RQQVJUQS0oXFxkKykvKTtcbiAgICBpZiAobWF0Y2ggPT09IG51bGwgfHwgbWF0Y2gubGVuZ3RoIDwgMikge1xuICAgICAgcmV0dXJuIC0xO1xuICAgIH1cbiAgICBjb25zdCB2ZXJzaW9uID0gcGFyc2VJbnQobWF0Y2hbMV0sIDEwKTtcbiAgICAvLyBUZXN0IGZvciBOYU4gKHllcywgdGhpcyBpcyB1Z2x5KVxuICAgIHJldHVybiB2ZXJzaW9uICE9PSB2ZXJzaW9uID8gLTEgOiB2ZXJzaW9uO1xuICB9O1xuXG4gIGNvbnN0IGdldENhblNlbmRNYXhNZXNzYWdlU2l6ZSA9IGZ1bmN0aW9uKHJlbW90ZUlzRmlyZWZveCkge1xuICAgIC8vIEV2ZXJ5IGltcGxlbWVudGF0aW9uIHdlIGtub3cgY2FuIHNlbmQgYXQgbGVhc3QgNjQgS2lCLlxuICAgIC8vIE5vdGU6IEFsdGhvdWdoIENocm9tZSBpcyB0ZWNobmljYWxseSBhYmxlIHRvIHNlbmQgdXAgdG8gMjU2IEtpQiwgdGhlXG4gICAgLy8gICAgICAgZGF0YSBkb2VzIG5vdCByZWFjaCB0aGUgb3RoZXIgcGVlciByZWxpYWJseS5cbiAgICAvLyAgICAgICBTZWU6IGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD04NDE5XG4gICAgbGV0IGNhblNlbmRNYXhNZXNzYWdlU2l6ZSA9IDY1NTM2O1xuICAgIGlmIChicm93c2VyRGV0YWlscy5icm93c2VyID09PSAnZmlyZWZveCcpIHtcbiAgICAgIGlmIChicm93c2VyRGV0YWlscy52ZXJzaW9uIDwgNTcpIHtcbiAgICAgICAgaWYgKHJlbW90ZUlzRmlyZWZveCA9PT0gLTEpIHtcbiAgICAgICAgICAvLyBGRiA8IDU3IHdpbGwgc2VuZCBpbiAxNiBLaUIgY2h1bmtzIHVzaW5nIHRoZSBkZXByZWNhdGVkIFBQSURcbiAgICAgICAgICAvLyBmcmFnbWVudGF0aW9uLlxuICAgICAgICAgIGNhblNlbmRNYXhNZXNzYWdlU2l6ZSA9IDE2Mzg0O1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEhvd2V2ZXIsIG90aGVyIEZGIChhbmQgUkFXUlRDKSBjYW4gcmVhc3NlbWJsZSBQUElELWZyYWdtZW50ZWRcbiAgICAgICAgICAvLyBtZXNzYWdlcy4gVGh1cywgc3VwcG9ydGluZyB+MiBHaUIgd2hlbiBzZW5kaW5nLlxuICAgICAgICAgIGNhblNlbmRNYXhNZXNzYWdlU2l6ZSA9IDIxNDc0ODM2Mzc7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoYnJvd3NlckRldGFpbHMudmVyc2lvbiA8IDYwKSB7XG4gICAgICAgIC8vIEN1cnJlbnRseSwgYWxsIEZGID49IDU3IHdpbGwgcmVzZXQgdGhlIHJlbW90ZSBtYXhpbXVtIG1lc3NhZ2Ugc2l6ZVxuICAgICAgICAvLyB0byB0aGUgZGVmYXVsdCB2YWx1ZSB3aGVuIGEgZGF0YSBjaGFubmVsIGlzIGNyZWF0ZWQgYXQgYSBsYXRlclxuICAgICAgICAvLyBzdGFnZS4gOihcbiAgICAgICAgLy8gU2VlOiBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xNDI2ODMxXG4gICAgICAgIGNhblNlbmRNYXhNZXNzYWdlU2l6ZSA9XG4gICAgICAgICAgYnJvd3NlckRldGFpbHMudmVyc2lvbiA9PT0gNTcgPyA2NTUzNSA6IDY1NTM2O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gRkYgPj0gNjAgc3VwcG9ydHMgc2VuZGluZyB+MiBHaUJcbiAgICAgICAgY2FuU2VuZE1heE1lc3NhZ2VTaXplID0gMjE0NzQ4MzYzNztcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGNhblNlbmRNYXhNZXNzYWdlU2l6ZTtcbiAgfTtcblxuICBjb25zdCBnZXRNYXhNZXNzYWdlU2l6ZSA9IGZ1bmN0aW9uKGRlc2NyaXB0aW9uLCByZW1vdGVJc0ZpcmVmb3gpIHtcbiAgICAvLyBOb3RlOiA2NTUzNiBieXRlcyBpcyB0aGUgZGVmYXVsdCB2YWx1ZSBmcm9tIHRoZSBTRFAgc3BlYy4gQWxzbyxcbiAgICAvLyAgICAgICBldmVyeSBpbXBsZW1lbnRhdGlvbiB3ZSBrbm93IHN1cHBvcnRzIHJlY2VpdmluZyA2NTUzNiBieXRlcy5cbiAgICBsZXQgbWF4TWVzc2FnZVNpemUgPSA2NTUzNjtcblxuICAgIC8vIEZGIDU3IGhhcyBhIHNsaWdodGx5IGluY29ycmVjdCBkZWZhdWx0IHJlbW90ZSBtYXggbWVzc2FnZSBzaXplLCBzb1xuICAgIC8vIHdlIG5lZWQgdG8gYWRqdXN0IGl0IGhlcmUgdG8gYXZvaWQgYSBmYWlsdXJlIHdoZW4gc2VuZGluZy5cbiAgICAvLyBTZWU6IGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTE0MjU2OTdcbiAgICBpZiAoYnJvd3NlckRldGFpbHMuYnJvd3NlciA9PT0gJ2ZpcmVmb3gnXG4gICAgICAgICAmJiBicm93c2VyRGV0YWlscy52ZXJzaW9uID09PSA1Nykge1xuICAgICAgbWF4TWVzc2FnZVNpemUgPSA2NTUzNTtcbiAgICB9XG5cbiAgICBjb25zdCBtYXRjaCA9IFNEUFV0aWxzLm1hdGNoUHJlZml4KGRlc2NyaXB0aW9uLnNkcCxcbiAgICAgICdhPW1heC1tZXNzYWdlLXNpemU6Jyk7XG4gICAgaWYgKG1hdGNoLmxlbmd0aCA+IDApIHtcbiAgICAgIG1heE1lc3NhZ2VTaXplID0gcGFyc2VJbnQobWF0Y2hbMF0uc3Vic3RyaW5nKDE5KSwgMTApO1xuICAgIH0gZWxzZSBpZiAoYnJvd3NlckRldGFpbHMuYnJvd3NlciA9PT0gJ2ZpcmVmb3gnICYmXG4gICAgICAgICAgICAgICAgcmVtb3RlSXNGaXJlZm94ICE9PSAtMSkge1xuICAgICAgLy8gSWYgdGhlIG1heGltdW0gbWVzc2FnZSBzaXplIGlzIG5vdCBwcmVzZW50IGluIHRoZSByZW1vdGUgU0RQIGFuZFxuICAgICAgLy8gYm90aCBsb2NhbCBhbmQgcmVtb3RlIGFyZSBGaXJlZm94LCB0aGUgcmVtb3RlIHBlZXIgY2FuIHJlY2VpdmVcbiAgICAgIC8vIH4yIEdpQi5cbiAgICAgIG1heE1lc3NhZ2VTaXplID0gMjE0NzQ4MzYzNztcbiAgICB9XG4gICAgcmV0dXJuIG1heE1lc3NhZ2VTaXplO1xuICB9O1xuXG4gIGNvbnN0IG9yaWdTZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uO1xuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLnNldFJlbW90ZURlc2NyaXB0aW9uID1cbiAgICBmdW5jdGlvbiBzZXRSZW1vdGVEZXNjcmlwdGlvbigpIHtcbiAgICAgIHRoaXMuX3NjdHAgPSBudWxsO1xuICAgICAgLy8gQ2hyb21lIGRlY2lkZWQgdG8gbm90IGV4cG9zZSAuc2N0cCBpbiBwbGFuLWIgbW9kZS5cbiAgICAgIC8vIEFzIHVzdWFsLCBhZGFwdGVyLmpzIGhhcyB0byBkbyBhbiAndWdseSB3b3Jha2Fyb3VuZCdcbiAgICAgIC8vIHRvIGNvdmVyIHVwIHRoZSBtZXNzLlxuICAgICAgaWYgKGJyb3dzZXJEZXRhaWxzLmJyb3dzZXIgPT09ICdjaHJvbWUnICYmIGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPj0gNzYpIHtcbiAgICAgICAgY29uc3Qge3NkcFNlbWFudGljc30gPSB0aGlzLmdldENvbmZpZ3VyYXRpb24oKTtcbiAgICAgICAgaWYgKHNkcFNlbWFudGljcyA9PT0gJ3BsYW4tYicpIHtcbiAgICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkodGhpcywgJ3NjdHAnLCB7XG4gICAgICAgICAgICBnZXQoKSB7XG4gICAgICAgICAgICAgIHJldHVybiB0eXBlb2YgdGhpcy5fc2N0cCA9PT0gJ3VuZGVmaW5lZCcgPyBudWxsIDogdGhpcy5fc2N0cDtcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgICAgICAgICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChzY3RwSW5EZXNjcmlwdGlvbihhcmd1bWVudHNbMF0pKSB7XG4gICAgICAgIC8vIENoZWNrIGlmIHRoZSByZW1vdGUgaXMgRkYuXG4gICAgICAgIGNvbnN0IGlzRmlyZWZveCA9IGdldFJlbW90ZUZpcmVmb3hWZXJzaW9uKGFyZ3VtZW50c1swXSk7XG5cbiAgICAgICAgLy8gR2V0IHRoZSBtYXhpbXVtIG1lc3NhZ2Ugc2l6ZSB0aGUgbG9jYWwgcGVlciBpcyBjYXBhYmxlIG9mIHNlbmRpbmdcbiAgICAgICAgY29uc3QgY2FuU2VuZE1NUyA9IGdldENhblNlbmRNYXhNZXNzYWdlU2l6ZShpc0ZpcmVmb3gpO1xuXG4gICAgICAgIC8vIEdldCB0aGUgbWF4aW11bSBtZXNzYWdlIHNpemUgb2YgdGhlIHJlbW90ZSBwZWVyLlxuICAgICAgICBjb25zdCByZW1vdGVNTVMgPSBnZXRNYXhNZXNzYWdlU2l6ZShhcmd1bWVudHNbMF0sIGlzRmlyZWZveCk7XG5cbiAgICAgICAgLy8gRGV0ZXJtaW5lIGZpbmFsIG1heGltdW0gbWVzc2FnZSBzaXplXG4gICAgICAgIGxldCBtYXhNZXNzYWdlU2l6ZTtcbiAgICAgICAgaWYgKGNhblNlbmRNTVMgPT09IDAgJiYgcmVtb3RlTU1TID09PSAwKSB7XG4gICAgICAgICAgbWF4TWVzc2FnZVNpemUgPSBOdW1iZXIuUE9TSVRJVkVfSU5GSU5JVFk7XG4gICAgICAgIH0gZWxzZSBpZiAoY2FuU2VuZE1NUyA9PT0gMCB8fCByZW1vdGVNTVMgPT09IDApIHtcbiAgICAgICAgICBtYXhNZXNzYWdlU2l6ZSA9IE1hdGgubWF4KGNhblNlbmRNTVMsIHJlbW90ZU1NUyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbWF4TWVzc2FnZVNpemUgPSBNYXRoLm1pbihjYW5TZW5kTU1TLCByZW1vdGVNTVMpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQ3JlYXRlIGEgZHVtbXkgUlRDU2N0cFRyYW5zcG9ydCBvYmplY3QgYW5kIHRoZSAnbWF4TWVzc2FnZVNpemUnXG4gICAgICAgIC8vIGF0dHJpYnV0ZS5cbiAgICAgICAgY29uc3Qgc2N0cCA9IHt9O1xuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoc2N0cCwgJ21heE1lc3NhZ2VTaXplJywge1xuICAgICAgICAgIGdldCgpIHtcbiAgICAgICAgICAgIHJldHVybiBtYXhNZXNzYWdlU2l6ZTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLl9zY3RwID0gc2N0cDtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIG9yaWdTZXRSZW1vdGVEZXNjcmlwdGlvbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltU2VuZFRocm93VHlwZUVycm9yKHdpbmRvdykge1xuICBpZiAoISh3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gJiZcbiAgICAgICdjcmVhdGVEYXRhQ2hhbm5lbCcgaW4gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSkpIHtcbiAgICByZXR1cm47XG4gIH1cblxuICAvLyBOb3RlOiBBbHRob3VnaCBGaXJlZm94ID49IDU3IGhhcyBhIG5hdGl2ZSBpbXBsZW1lbnRhdGlvbiwgdGhlIG1heGltdW1cbiAgLy8gICAgICAgbWVzc2FnZSBzaXplIGNhbiBiZSByZXNldCBmb3IgYWxsIGRhdGEgY2hhbm5lbHMgYXQgYSBsYXRlciBzdGFnZS5cbiAgLy8gICAgICAgU2VlOiBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xNDI2ODMxXG5cbiAgZnVuY3Rpb24gd3JhcERjU2VuZChkYywgcGMpIHtcbiAgICBjb25zdCBvcmlnRGF0YUNoYW5uZWxTZW5kID0gZGMuc2VuZDtcbiAgICBkYy5zZW5kID0gZnVuY3Rpb24gc2VuZCgpIHtcbiAgICAgIGNvbnN0IGRhdGEgPSBhcmd1bWVudHNbMF07XG4gICAgICBjb25zdCBsZW5ndGggPSBkYXRhLmxlbmd0aCB8fCBkYXRhLnNpemUgfHwgZGF0YS5ieXRlTGVuZ3RoO1xuICAgICAgaWYgKGRjLnJlYWR5U3RhdGUgPT09ICdvcGVuJyAmJlxuICAgICAgICAgIHBjLnNjdHAgJiYgbGVuZ3RoID4gcGMuc2N0cC5tYXhNZXNzYWdlU2l6ZSkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdNZXNzYWdlIHRvbyBsYXJnZSAoY2FuIHNlbmQgYSBtYXhpbXVtIG9mICcgK1xuICAgICAgICAgIHBjLnNjdHAubWF4TWVzc2FnZVNpemUgKyAnIGJ5dGVzKScpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG9yaWdEYXRhQ2hhbm5lbFNlbmQuYXBwbHkoZGMsIGFyZ3VtZW50cyk7XG4gICAgfTtcbiAgfVxuICBjb25zdCBvcmlnQ3JlYXRlRGF0YUNoYW5uZWwgPVxuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlRGF0YUNoYW5uZWw7XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlRGF0YUNoYW5uZWwgPVxuICAgIGZ1bmN0aW9uIGNyZWF0ZURhdGFDaGFubmVsKCkge1xuICAgICAgY29uc3QgZGF0YUNoYW5uZWwgPSBvcmlnQ3JlYXRlRGF0YUNoYW5uZWwuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgIHdyYXBEY1NlbmQoZGF0YUNoYW5uZWwsIHRoaXMpO1xuICAgICAgcmV0dXJuIGRhdGFDaGFubmVsO1xuICAgIH07XG4gIHV0aWxzLndyYXBQZWVyQ29ubmVjdGlvbkV2ZW50KHdpbmRvdywgJ2RhdGFjaGFubmVsJywgZSA9PiB7XG4gICAgd3JhcERjU2VuZChlLmNoYW5uZWwsIGUudGFyZ2V0KTtcbiAgICByZXR1cm4gZTtcbiAgfSk7XG59XG5cblxuLyogc2hpbXMgUlRDQ29ubmVjdGlvblN0YXRlIGJ5IHByZXRlbmRpbmcgaXQgaXMgdGhlIHNhbWUgYXMgaWNlQ29ubmVjdGlvblN0YXRlLlxuICogU2VlIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC93ZWJydGMvaXNzdWVzL2RldGFpbD9pZD02MTQ1I2MxMlxuICogZm9yIHdoeSB0aGlzIGlzIGEgdmFsaWQgaGFjayBpbiBDaHJvbWUuIEluIEZpcmVmb3ggaXQgaXMgc2xpZ2h0bHkgaW5jb3JyZWN0XG4gKiBzaW5jZSBEVExTIGZhaWx1cmVzIHdvdWxkIGJlIGhpZGRlbi4gU2VlXG4gKiBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMjY1ODI3XG4gKiBmb3IgdGhlIEZpcmVmb3ggdHJhY2tpbmcgYnVnLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hpbUNvbm5lY3Rpb25TdGF0ZSh3aW5kb3cpIHtcbiAgaWYgKCF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gfHxcbiAgICAgICdjb25uZWN0aW9uU3RhdGUnIGluIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgcHJvdG8gPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlO1xuICBPYmplY3QuZGVmaW5lUHJvcGVydHkocHJvdG8sICdjb25uZWN0aW9uU3RhdGUnLCB7XG4gICAgZ2V0KCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgY29tcGxldGVkOiAnY29ubmVjdGVkJyxcbiAgICAgICAgY2hlY2tpbmc6ICdjb25uZWN0aW5nJ1xuICAgICAgfVt0aGlzLmljZUNvbm5lY3Rpb25TdGF0ZV0gfHwgdGhpcy5pY2VDb25uZWN0aW9uU3RhdGU7XG4gICAgfSxcbiAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICB9KTtcbiAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHByb3RvLCAnb25jb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCB7XG4gICAgZ2V0KCkge1xuICAgICAgcmV0dXJuIHRoaXMuX29uY29ubmVjdGlvbnN0YXRlY2hhbmdlIHx8IG51bGw7XG4gICAgfSxcbiAgICBzZXQoY2IpIHtcbiAgICAgIGlmICh0aGlzLl9vbmNvbm5lY3Rpb25zdGF0ZWNoYW5nZSkge1xuICAgICAgICB0aGlzLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ2Nvbm5lY3Rpb25zdGF0ZWNoYW5nZScsXG4gICAgICAgICAgdGhpcy5fb25jb25uZWN0aW9uc3RhdGVjaGFuZ2UpO1xuICAgICAgICBkZWxldGUgdGhpcy5fb25jb25uZWN0aW9uc3RhdGVjaGFuZ2U7XG4gICAgICB9XG4gICAgICBpZiAoY2IpIHtcbiAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCdjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLFxuICAgICAgICAgIHRoaXMuX29uY29ubmVjdGlvbnN0YXRlY2hhbmdlID0gY2IpO1xuICAgICAgfVxuICAgIH0sXG4gICAgZW51bWVyYWJsZTogdHJ1ZSxcbiAgICBjb25maWd1cmFibGU6IHRydWVcbiAgfSk7XG5cbiAgWydzZXRMb2NhbERlc2NyaXB0aW9uJywgJ3NldFJlbW90ZURlc2NyaXB0aW9uJ10uZm9yRWFjaCgobWV0aG9kKSA9PiB7XG4gICAgY29uc3Qgb3JpZ01ldGhvZCA9IHByb3RvW21ldGhvZF07XG4gICAgcHJvdG9bbWV0aG9kXSA9IGZ1bmN0aW9uKCkge1xuICAgICAgaWYgKCF0aGlzLl9jb25uZWN0aW9uc3RhdGVjaGFuZ2Vwb2x5KSB7XG4gICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zdGF0ZWNoYW5nZXBvbHkgPSBlID0+IHtcbiAgICAgICAgICBjb25zdCBwYyA9IGUudGFyZ2V0O1xuICAgICAgICAgIGlmIChwYy5fbGFzdENvbm5lY3Rpb25TdGF0ZSAhPT0gcGMuY29ubmVjdGlvblN0YXRlKSB7XG4gICAgICAgICAgICBwYy5fbGFzdENvbm5lY3Rpb25TdGF0ZSA9IHBjLmNvbm5lY3Rpb25TdGF0ZTtcbiAgICAgICAgICAgIGNvbnN0IG5ld0V2ZW50ID0gbmV3IEV2ZW50KCdjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCBlKTtcbiAgICAgICAgICAgIHBjLmRpc3BhdGNoRXZlbnQobmV3RXZlbnQpO1xuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm4gZTtcbiAgICAgICAgfTtcbiAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCdpY2Vjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLFxuICAgICAgICAgIHRoaXMuX2Nvbm5lY3Rpb25zdGF0ZWNoYW5nZXBvbHkpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG9yaWdNZXRob2QuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9O1xuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHJlbW92ZUV4dG1hcEFsbG93TWl4ZWQod2luZG93LCBicm93c2VyRGV0YWlscykge1xuICAvKiByZW1vdmUgYT1leHRtYXAtYWxsb3ctbWl4ZWQgZm9yIHdlYnJ0Yy5vcmcgPCBNNzEgKi9cbiAgaWYgKCF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24pIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKGJyb3dzZXJEZXRhaWxzLmJyb3dzZXIgPT09ICdjaHJvbWUnICYmIGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPj0gNzEpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKGJyb3dzZXJEZXRhaWxzLmJyb3dzZXIgPT09ICdzYWZhcmknICYmIGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPj0gNjA1KSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IG5hdGl2ZVNSRCA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0UmVtb3RlRGVzY3JpcHRpb247XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0UmVtb3RlRGVzY3JpcHRpb24gPVxuICBmdW5jdGlvbiBzZXRSZW1vdGVEZXNjcmlwdGlvbihkZXNjKSB7XG4gICAgaWYgKGRlc2MgJiYgZGVzYy5zZHAgJiYgZGVzYy5zZHAuaW5kZXhPZignXFxuYT1leHRtYXAtYWxsb3ctbWl4ZWQnKSAhPT0gLTEpIHtcbiAgICAgIGNvbnN0IHNkcCA9IGRlc2Muc2RwLnNwbGl0KCdcXG4nKS5maWx0ZXIoKGxpbmUpID0+IHtcbiAgICAgICAgcmV0dXJuIGxpbmUudHJpbSgpICE9PSAnYT1leHRtYXAtYWxsb3ctbWl4ZWQnO1xuICAgICAgfSkuam9pbignXFxuJyk7XG4gICAgICAvLyBTYWZhcmkgZW5mb3JjZXMgcmVhZC1vbmx5LW5lc3Mgb2YgUlRDU2Vzc2lvbkRlc2NyaXB0aW9uIGZpZWxkcy5cbiAgICAgIGlmICh3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uICYmXG4gICAgICAgICAgZGVzYyBpbnN0YW5jZW9mIHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24pIHtcbiAgICAgICAgYXJndW1lbnRzWzBdID0gbmV3IHdpbmRvdy5SVENTZXNzaW9uRGVzY3JpcHRpb24oe1xuICAgICAgICAgIHR5cGU6IGRlc2MudHlwZSxcbiAgICAgICAgICBzZHAsXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgZGVzYy5zZHAgPSBzZHA7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBuYXRpdmVTUkQuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1BZGRJY2VDYW5kaWRhdGVOdWxsT3JFbXB0eSh3aW5kb3csIGJyb3dzZXJEZXRhaWxzKSB7XG4gIC8vIFN1cHBvcnQgZm9yIGFkZEljZUNhbmRpZGF0ZShudWxsIG9yIHVuZGVmaW5lZClcbiAgLy8gYXMgd2VsbCBhcyBhZGRJY2VDYW5kaWRhdGUoe2NhbmRpZGF0ZTogXCJcIiwgLi4ufSlcbiAgLy8gaHR0cHM6Ly9idWdzLmNocm9taXVtLm9yZy9wL2Nocm9taXVtL2lzc3Vlcy9kZXRhaWw/aWQ9OTc4NTgyXG4gIC8vIE5vdGU6IG11c3QgYmUgY2FsbGVkIGJlZm9yZSBvdGhlciBwb2x5ZmlsbHMgd2hpY2ggY2hhbmdlIHRoZSBzaWduYXR1cmUuXG4gIGlmICghKHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiAmJiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCBuYXRpdmVBZGRJY2VDYW5kaWRhdGUgPVxuICAgICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGU7XG4gIGlmICghbmF0aXZlQWRkSWNlQ2FuZGlkYXRlIHx8IG5hdGl2ZUFkZEljZUNhbmRpZGF0ZS5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm47XG4gIH1cbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRJY2VDYW5kaWRhdGUgPVxuICAgIGZ1bmN0aW9uIGFkZEljZUNhbmRpZGF0ZSgpIHtcbiAgICAgIGlmICghYXJndW1lbnRzWzBdKSB7XG4gICAgICAgIGlmIChhcmd1bWVudHNbMV0pIHtcbiAgICAgICAgICBhcmd1bWVudHNbMV0uYXBwbHkobnVsbCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xuICAgICAgfVxuICAgICAgLy8gRmlyZWZveCA2OCsgZW1pdHMgYW5kIHByb2Nlc3NlcyB7Y2FuZGlkYXRlOiBcIlwiLCAuLi59LCBpZ25vcmVcbiAgICAgIC8vIGluIG9sZGVyIHZlcnNpb25zLlxuICAgICAgLy8gTmF0aXZlIHN1cHBvcnQgZm9yIGlnbm9yaW5nIGV4aXN0cyBmb3IgQ2hyb21lIE03NysuXG4gICAgICAvLyBTYWZhcmkgaWdub3JlcyBhcyB3ZWxsLCBleGFjdCB2ZXJzaW9uIHVua25vd24gYnV0IHdvcmtzIGluIHRoZSBzYW1lXG4gICAgICAvLyB2ZXJzaW9uIHRoYXQgYWxzbyBpZ25vcmVzIGFkZEljZUNhbmRpZGF0ZShudWxsKS5cbiAgICAgIGlmICgoKGJyb3dzZXJEZXRhaWxzLmJyb3dzZXIgPT09ICdjaHJvbWUnICYmIGJyb3dzZXJEZXRhaWxzLnZlcnNpb24gPCA3OClcbiAgICAgICAgICAgfHwgKGJyb3dzZXJEZXRhaWxzLmJyb3dzZXIgPT09ICdmaXJlZm94J1xuICAgICAgICAgICAgICAgJiYgYnJvd3NlckRldGFpbHMudmVyc2lvbiA8IDY4KVxuICAgICAgICAgICB8fCAoYnJvd3NlckRldGFpbHMuYnJvd3NlciA9PT0gJ3NhZmFyaScpKVxuICAgICAgICAgICYmIGFyZ3VtZW50c1swXSAmJiBhcmd1bWVudHNbMF0uY2FuZGlkYXRlID09PSAnJykge1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgICB9XG4gICAgICByZXR1cm4gbmF0aXZlQWRkSWNlQ2FuZGlkYXRlLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gICAgfTtcbn1cblxuLy8gTm90ZTogTWFrZSBzdXJlIHRvIGNhbGwgdGhpcyBhaGVhZCBvZiBBUElzIHRoYXQgbW9kaWZ5XG4vLyBzZXRMb2NhbERlc2NyaXB0aW9uLmxlbmd0aFxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1QYXJhbWV0ZXJsZXNzU2V0TG9jYWxEZXNjcmlwdGlvbih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKSB7XG4gIGlmICghKHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiAmJiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCBuYXRpdmVTZXRMb2NhbERlc2NyaXB0aW9uID1cbiAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbjtcbiAgaWYgKCFuYXRpdmVTZXRMb2NhbERlc2NyaXB0aW9uIHx8IG5hdGl2ZVNldExvY2FsRGVzY3JpcHRpb24ubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0TG9jYWxEZXNjcmlwdGlvbiA9XG4gICAgZnVuY3Rpb24gc2V0TG9jYWxEZXNjcmlwdGlvbigpIHtcbiAgICAgIGxldCBkZXNjID0gYXJndW1lbnRzWzBdIHx8IHt9O1xuICAgICAgaWYgKHR5cGVvZiBkZXNjICE9PSAnb2JqZWN0JyB8fCAoZGVzYy50eXBlICYmIGRlc2Muc2RwKSkge1xuICAgICAgICByZXR1cm4gbmF0aXZlU2V0TG9jYWxEZXNjcmlwdGlvbi5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgfVxuICAgICAgLy8gVGhlIHJlbWFpbmluZyBzdGVwcyBzaG91bGQgdGVjaG5pY2FsbHkgaGFwcGVuIHdoZW4gU0xEIGNvbWVzIG9mZiB0aGVcbiAgICAgIC8vIFJUQ1BlZXJDb25uZWN0aW9uJ3Mgb3BlcmF0aW9ucyBjaGFpbiAobm90IGFoZWFkIG9mIGdvaW5nIG9uIGl0KSwgYnV0XG4gICAgICAvLyB0aGlzIGlzIHRvbyBkaWZmaWN1bHQgdG8gc2hpbS4gSW5zdGVhZCwgdGhpcyBzaGltIG9ubHkgY292ZXJzIHRoZVxuICAgICAgLy8gY29tbW9uIGNhc2Ugd2hlcmUgdGhlIG9wZXJhdGlvbnMgY2hhaW4gaXMgZW1wdHkuIFRoaXMgaXMgaW1wZXJmZWN0LCBidXRcbiAgICAgIC8vIHNob3VsZCBjb3ZlciBtYW55IGNhc2VzLiBSYXRpb25hbGU6IEV2ZW4gaWYgd2UgY2FuJ3QgcmVkdWNlIHRoZSBnbGFyZVxuICAgICAgLy8gd2luZG93IHRvIHplcm8gb24gaW1wZXJmZWN0IGltcGxlbWVudGF0aW9ucywgdGhlcmUncyB2YWx1ZSBpbiB0YXBwaW5nXG4gICAgICAvLyBpbnRvIHRoZSBwZXJmZWN0IG5lZ290aWF0aW9uIHBhdHRlcm4gdGhhdCBzZXZlcmFsIGJyb3dzZXJzIHN1cHBvcnQuXG4gICAgICBkZXNjID0ge3R5cGU6IGRlc2MudHlwZSwgc2RwOiBkZXNjLnNkcH07XG4gICAgICBpZiAoIWRlc2MudHlwZSkge1xuICAgICAgICBzd2l0Y2ggKHRoaXMuc2lnbmFsaW5nU3RhdGUpIHtcbiAgICAgICAgICBjYXNlICdzdGFibGUnOlxuICAgICAgICAgIGNhc2UgJ2hhdmUtbG9jYWwtb2ZmZXInOlxuICAgICAgICAgIGNhc2UgJ2hhdmUtcmVtb3RlLXByYW5zd2VyJzpcbiAgICAgICAgICAgIGRlc2MudHlwZSA9ICdvZmZlcic7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgZGVzYy50eXBlID0gJ2Fuc3dlcic7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGRlc2Muc2RwIHx8IChkZXNjLnR5cGUgIT09ICdvZmZlcicgJiYgZGVzYy50eXBlICE9PSAnYW5zd2VyJykpIHtcbiAgICAgICAgcmV0dXJuIG5hdGl2ZVNldExvY2FsRGVzY3JpcHRpb24uYXBwbHkodGhpcywgW2Rlc2NdKTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGZ1bmMgPSBkZXNjLnR5cGUgPT09ICdvZmZlcicgPyB0aGlzLmNyZWF0ZU9mZmVyIDogdGhpcy5jcmVhdGVBbnN3ZXI7XG4gICAgICByZXR1cm4gZnVuYy5hcHBseSh0aGlzKVxuICAgICAgICAudGhlbihkID0+IG5hdGl2ZVNldExvY2FsRGVzY3JpcHRpb24uYXBwbHkodGhpcywgW2RdKSk7XG4gICAgfTtcbn1cbiIsIi8qXG4gKiAgQ29weXJpZ2h0IChjKSAyMDE2IFRoZSBXZWJSVEMgcHJvamVjdCBhdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlXG4gKiAgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBvZiB0aGUgc291cmNlXG4gKiAgdHJlZS5cbiAqL1xuLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbmltcG9ydCAqIGFzIHV0aWxzIGZyb20gJy4uL3V0aWxzJztcbmV4cG9ydCB7c2hpbUdldFVzZXJNZWRpYX0gZnJvbSAnLi9nZXR1c2VybWVkaWEnO1xuZXhwb3J0IHtzaGltR2V0RGlzcGxheU1lZGlhfSBmcm9tICcuL2dldGRpc3BsYXltZWRpYSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltT25UcmFjayh3aW5kb3cpIHtcbiAgaWYgKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnICYmIHdpbmRvdy5SVENUcmFja0V2ZW50ICYmXG4gICAgICAoJ3JlY2VpdmVyJyBpbiB3aW5kb3cuUlRDVHJhY2tFdmVudC5wcm90b3R5cGUpICYmXG4gICAgICAhKCd0cmFuc2NlaXZlcicgaW4gd2luZG93LlJUQ1RyYWNrRXZlbnQucHJvdG90eXBlKSkge1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh3aW5kb3cuUlRDVHJhY2tFdmVudC5wcm90b3R5cGUsICd0cmFuc2NlaXZlcicsIHtcbiAgICAgIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIHtyZWNlaXZlcjogdGhpcy5yZWNlaXZlcn07XG4gICAgICB9XG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1QZWVyQ29ubmVjdGlvbih3aW5kb3csIGJyb3dzZXJEZXRhaWxzKSB7XG4gIGlmICh0eXBlb2Ygd2luZG93ICE9PSAnb2JqZWN0JyB8fFxuICAgICAgISh3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gfHwgd2luZG93Lm1velJUQ1BlZXJDb25uZWN0aW9uKSkge1xuICAgIHJldHVybjsgLy8gcHJvYmFibHkgbWVkaWEucGVlcmNvbm5lY3Rpb24uZW5hYmxlZD1mYWxzZSBpbiBhYm91dDpjb25maWdcbiAgfVxuICBpZiAoIXdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiAmJiB3aW5kb3cubW96UlRDUGVlckNvbm5lY3Rpb24pIHtcbiAgICAvLyB2ZXJ5IGJhc2ljIHN1cHBvcnQgZm9yIG9sZCB2ZXJzaW9ucy5cbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gPSB3aW5kb3cubW96UlRDUGVlckNvbm5lY3Rpb247XG4gIH1cblxuICBpZiAoYnJvd3NlckRldGFpbHMudmVyc2lvbiA8IDUzKSB7XG4gICAgLy8gc2hpbSBhd2F5IG5lZWQgZm9yIG9ic29sZXRlIFJUQ0ljZUNhbmRpZGF0ZS9SVENTZXNzaW9uRGVzY3JpcHRpb24uXG4gICAgWydzZXRMb2NhbERlc2NyaXB0aW9uJywgJ3NldFJlbW90ZURlc2NyaXB0aW9uJywgJ2FkZEljZUNhbmRpZGF0ZSddXG4gICAgICAuZm9yRWFjaChmdW5jdGlvbihtZXRob2QpIHtcbiAgICAgICAgY29uc3QgbmF0aXZlTWV0aG9kID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZVttZXRob2RdO1xuICAgICAgICBjb25zdCBtZXRob2RPYmogPSB7W21ldGhvZF0oKSB7XG4gICAgICAgICAgYXJndW1lbnRzWzBdID0gbmV3ICgobWV0aG9kID09PSAnYWRkSWNlQ2FuZGlkYXRlJykgP1xuICAgICAgICAgICAgd2luZG93LlJUQ0ljZUNhbmRpZGF0ZSA6XG4gICAgICAgICAgICB3aW5kb3cuUlRDU2Vzc2lvbkRlc2NyaXB0aW9uKShhcmd1bWVudHNbMF0pO1xuICAgICAgICAgIHJldHVybiBuYXRpdmVNZXRob2QuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfX07XG4gICAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGVbbWV0aG9kXSA9IG1ldGhvZE9ialttZXRob2RdO1xuICAgICAgfSk7XG4gIH1cblxuICBjb25zdCBtb2Rlcm5TdGF0c1R5cGVzID0ge1xuICAgIGluYm91bmRydHA6ICdpbmJvdW5kLXJ0cCcsXG4gICAgb3V0Ym91bmRydHA6ICdvdXRib3VuZC1ydHAnLFxuICAgIGNhbmRpZGF0ZXBhaXI6ICdjYW5kaWRhdGUtcGFpcicsXG4gICAgbG9jYWxjYW5kaWRhdGU6ICdsb2NhbC1jYW5kaWRhdGUnLFxuICAgIHJlbW90ZWNhbmRpZGF0ZTogJ3JlbW90ZS1jYW5kaWRhdGUnXG4gIH07XG5cbiAgY29uc3QgbmF0aXZlR2V0U3RhdHMgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzO1xuICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24gZ2V0U3RhdHMoKSB7XG4gICAgY29uc3QgW3NlbGVjdG9yLCBvblN1Y2MsIG9uRXJyXSA9IGFyZ3VtZW50cztcbiAgICByZXR1cm4gbmF0aXZlR2V0U3RhdHMuYXBwbHkodGhpcywgW3NlbGVjdG9yIHx8IG51bGxdKVxuICAgICAgLnRoZW4oc3RhdHMgPT4ge1xuICAgICAgICBpZiAoYnJvd3NlckRldGFpbHMudmVyc2lvbiA8IDUzICYmICFvblN1Y2MpIHtcbiAgICAgICAgICAvLyBTaGltIG9ubHkgcHJvbWlzZSBnZXRTdGF0cyB3aXRoIHNwZWMtaHlwaGVucyBpbiB0eXBlIG5hbWVzXG4gICAgICAgICAgLy8gTGVhdmUgY2FsbGJhY2sgdmVyc2lvbiBhbG9uZTsgbWlzYyBvbGQgdXNlcyBvZiBmb3JFYWNoIGJlZm9yZSBNYXBcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgc3RhdHMuZm9yRWFjaChzdGF0ID0+IHtcbiAgICAgICAgICAgICAgc3RhdC50eXBlID0gbW9kZXJuU3RhdHNUeXBlc1tzdGF0LnR5cGVdIHx8IHN0YXQudHlwZTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGlmIChlLm5hbWUgIT09ICdUeXBlRXJyb3InKSB7XG4gICAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBBdm9pZCBUeXBlRXJyb3I6IFwidHlwZVwiIGlzIHJlYWQtb25seSwgaW4gb2xkIHZlcnNpb25zLiAzNC00M2lzaFxuICAgICAgICAgICAgc3RhdHMuZm9yRWFjaCgoc3RhdCwgaSkgPT4ge1xuICAgICAgICAgICAgICBzdGF0cy5zZXQoaSwgT2JqZWN0LmFzc2lnbih7fSwgc3RhdCwge1xuICAgICAgICAgICAgICAgIHR5cGU6IG1vZGVyblN0YXRzVHlwZXNbc3RhdC50eXBlXSB8fCBzdGF0LnR5cGVcbiAgICAgICAgICAgICAgfSkpO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzdGF0cztcbiAgICAgIH0pXG4gICAgICAudGhlbihvblN1Y2MsIG9uRXJyKTtcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1TZW5kZXJHZXRTdGF0cyh3aW5kb3cpIHtcbiAgaWYgKCEodHlwZW9mIHdpbmRvdyA9PT0gJ29iamVjdCcgJiYgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uICYmXG4gICAgICB3aW5kb3cuUlRDUnRwU2VuZGVyKSkge1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAod2luZG93LlJUQ1J0cFNlbmRlciAmJiAnZ2V0U3RhdHMnIGluIHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IG9yaWdHZXRTZW5kZXJzID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRTZW5kZXJzO1xuICBpZiAob3JpZ0dldFNlbmRlcnMpIHtcbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmdldFNlbmRlcnMgPSBmdW5jdGlvbiBnZXRTZW5kZXJzKCkge1xuICAgICAgY29uc3Qgc2VuZGVycyA9IG9yaWdHZXRTZW5kZXJzLmFwcGx5KHRoaXMsIFtdKTtcbiAgICAgIHNlbmRlcnMuZm9yRWFjaChzZW5kZXIgPT4gc2VuZGVyLl9wYyA9IHRoaXMpO1xuICAgICAgcmV0dXJuIHNlbmRlcnM7XG4gICAgfTtcbiAgfVxuXG4gIGNvbnN0IG9yaWdBZGRUcmFjayA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkVHJhY2s7XG4gIGlmIChvcmlnQWRkVHJhY2spIHtcbiAgICB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmFkZFRyYWNrID0gZnVuY3Rpb24gYWRkVHJhY2soKSB7XG4gICAgICBjb25zdCBzZW5kZXIgPSBvcmlnQWRkVHJhY2suYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgIHNlbmRlci5fcGMgPSB0aGlzO1xuICAgICAgcmV0dXJuIHNlbmRlcjtcbiAgICB9O1xuICB9XG4gIHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24gZ2V0U3RhdHMoKSB7XG4gICAgcmV0dXJuIHRoaXMudHJhY2sgPyB0aGlzLl9wYy5nZXRTdGF0cyh0aGlzLnRyYWNrKSA6XG4gICAgICBQcm9taXNlLnJlc29sdmUobmV3IE1hcCgpKTtcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1SZWNlaXZlckdldFN0YXRzKHdpbmRvdykge1xuICBpZiAoISh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JyAmJiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gJiZcbiAgICAgIHdpbmRvdy5SVENSdHBTZW5kZXIpKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmICh3aW5kb3cuUlRDUnRwU2VuZGVyICYmICdnZXRTdGF0cycgaW4gd2luZG93LlJUQ1J0cFJlY2VpdmVyLnByb3RvdHlwZSkge1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCBvcmlnR2V0UmVjZWl2ZXJzID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRSZWNlaXZlcnM7XG4gIGlmIChvcmlnR2V0UmVjZWl2ZXJzKSB7XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRSZWNlaXZlcnMgPSBmdW5jdGlvbiBnZXRSZWNlaXZlcnMoKSB7XG4gICAgICBjb25zdCByZWNlaXZlcnMgPSBvcmlnR2V0UmVjZWl2ZXJzLmFwcGx5KHRoaXMsIFtdKTtcbiAgICAgIHJlY2VpdmVycy5mb3JFYWNoKHJlY2VpdmVyID0+IHJlY2VpdmVyLl9wYyA9IHRoaXMpO1xuICAgICAgcmV0dXJuIHJlY2VpdmVycztcbiAgICB9O1xuICB9XG4gIHV0aWxzLndyYXBQZWVyQ29ubmVjdGlvbkV2ZW50KHdpbmRvdywgJ3RyYWNrJywgZSA9PiB7XG4gICAgZS5yZWNlaXZlci5fcGMgPSBlLnNyY0VsZW1lbnQ7XG4gICAgcmV0dXJuIGU7XG4gIH0pO1xuICB3aW5kb3cuUlRDUnRwUmVjZWl2ZXIucHJvdG90eXBlLmdldFN0YXRzID0gZnVuY3Rpb24gZ2V0U3RhdHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BjLmdldFN0YXRzKHRoaXMudHJhY2spO1xuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbVJlbW92ZVN0cmVhbSh3aW5kb3cpIHtcbiAgaWYgKCF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gfHxcbiAgICAgICdyZW1vdmVTdHJlYW0nIGluIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5yZW1vdmVTdHJlYW0gPVxuICAgIGZ1bmN0aW9uIHJlbW92ZVN0cmVhbShzdHJlYW0pIHtcbiAgICAgIHV0aWxzLmRlcHJlY2F0ZWQoJ3JlbW92ZVN0cmVhbScsICdyZW1vdmVUcmFjaycpO1xuICAgICAgdGhpcy5nZXRTZW5kZXJzKCkuZm9yRWFjaChzZW5kZXIgPT4ge1xuICAgICAgICBpZiAoc2VuZGVyLnRyYWNrICYmIHN0cmVhbS5nZXRUcmFja3MoKS5pbmNsdWRlcyhzZW5kZXIudHJhY2spKSB7XG4gICAgICAgICAgdGhpcy5yZW1vdmVUcmFjayhzZW5kZXIpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbVJUQ0RhdGFDaGFubmVsKHdpbmRvdykge1xuICAvLyByZW5hbWUgRGF0YUNoYW5uZWwgdG8gUlRDRGF0YUNoYW5uZWwgKG5hdGl2ZSBmaXggaW4gRkY2MCk6XG4gIC8vIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTExNzM4NTFcbiAgaWYgKHdpbmRvdy5EYXRhQ2hhbm5lbCAmJiAhd2luZG93LlJUQ0RhdGFDaGFubmVsKSB7XG4gICAgd2luZG93LlJUQ0RhdGFDaGFubmVsID0gd2luZG93LkRhdGFDaGFubmVsO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQWRkVHJhbnNjZWl2ZXIod2luZG93KSB7XG4gIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS93ZWJydGNIYWNrcy9hZGFwdGVyL2lzc3Vlcy85OTgjaXNzdWVjb21tZW50LTUxNjkyMTY0N1xuICAvLyBGaXJlZm94IGlnbm9yZXMgdGhlIGluaXQgc2VuZEVuY29kaW5ncyBvcHRpb25zIHBhc3NlZCB0byBhZGRUcmFuc2NlaXZlclxuICAvLyBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMzk2OTE4XG4gIGlmICghKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnICYmIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbikpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3Qgb3JpZ0FkZFRyYW5zY2VpdmVyID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRUcmFuc2NlaXZlcjtcbiAgaWYgKG9yaWdBZGRUcmFuc2NlaXZlcikge1xuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkVHJhbnNjZWl2ZXIgPVxuICAgICAgZnVuY3Rpb24gYWRkVHJhbnNjZWl2ZXIoKSB7XG4gICAgICAgIHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzID0gW107XG4gICAgICAgIC8vIFdlYklETCBpbnB1dCBjb2VyY2lvbiBhbmQgdmFsaWRhdGlvblxuICAgICAgICBsZXQgc2VuZEVuY29kaW5ncyA9IGFyZ3VtZW50c1sxXSAmJiBhcmd1bWVudHNbMV0uc2VuZEVuY29kaW5ncztcbiAgICAgICAgaWYgKHNlbmRFbmNvZGluZ3MgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHNlbmRFbmNvZGluZ3MgPSBbXTtcbiAgICAgICAgfVxuICAgICAgICBzZW5kRW5jb2RpbmdzID0gWy4uLnNlbmRFbmNvZGluZ3NdO1xuICAgICAgICBjb25zdCBzaG91bGRQZXJmb3JtQ2hlY2sgPSBzZW5kRW5jb2RpbmdzLmxlbmd0aCA+IDA7XG4gICAgICAgIGlmIChzaG91bGRQZXJmb3JtQ2hlY2spIHtcbiAgICAgICAgICAvLyBJZiBzZW5kRW5jb2RpbmdzIHBhcmFtcyBhcmUgcHJvdmlkZWQsIHZhbGlkYXRlIGdyYW1tYXJcbiAgICAgICAgICBzZW5kRW5jb2RpbmdzLmZvckVhY2goKGVuY29kaW5nUGFyYW0pID0+IHtcbiAgICAgICAgICAgIGlmICgncmlkJyBpbiBlbmNvZGluZ1BhcmFtKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHJpZFJlZ2V4ID0gL15bYS16MC05XXswLDE2fSQvaTtcbiAgICAgICAgICAgICAgaWYgKCFyaWRSZWdleC50ZXN0KGVuY29kaW5nUGFyYW0ucmlkKSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0ludmFsaWQgUklEIHZhbHVlIHByb3ZpZGVkLicpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoJ3NjYWxlUmVzb2x1dGlvbkRvd25CeScgaW4gZW5jb2RpbmdQYXJhbSkge1xuICAgICAgICAgICAgICBpZiAoIShwYXJzZUZsb2F0KGVuY29kaW5nUGFyYW0uc2NhbGVSZXNvbHV0aW9uRG93bkJ5KSA+PSAxLjApKSB7XG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ3NjYWxlX3Jlc29sdXRpb25fZG93bl9ieSBtdXN0IGJlID49IDEuMCcpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAoJ21heEZyYW1lcmF0ZScgaW4gZW5jb2RpbmdQYXJhbSkge1xuICAgICAgICAgICAgICBpZiAoIShwYXJzZUZsb2F0KGVuY29kaW5nUGFyYW0ubWF4RnJhbWVyYXRlKSA+PSAwKSkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdtYXhfZnJhbWVyYXRlIG11c3QgYmUgPj0gMC4wJyk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB0cmFuc2NlaXZlciA9IG9yaWdBZGRUcmFuc2NlaXZlci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICBpZiAoc2hvdWxkUGVyZm9ybUNoZWNrKSB7XG4gICAgICAgICAgLy8gQ2hlY2sgaWYgdGhlIGluaXQgb3B0aW9ucyB3ZXJlIGFwcGxpZWQuIElmIG5vdCB3ZSBkbyB0aGlzIGluIGFuXG4gICAgICAgICAgLy8gYXN5bmNocm9ub3VzIHdheSBhbmQgc2F2ZSB0aGUgcHJvbWlzZSByZWZlcmVuY2UgaW4gYSBnbG9iYWwgb2JqZWN0LlxuICAgICAgICAgIC8vIFRoaXMgaXMgYW4gdWdseSBoYWNrLCBidXQgYXQgdGhlIHNhbWUgdGltZSBpcyB3YXkgbW9yZSByb2J1c3QgdGhhblxuICAgICAgICAgIC8vIGNoZWNraW5nIHRoZSBzZW5kZXIgcGFyYW1ldGVycyBiZWZvcmUgYW5kIGFmdGVyIHRoZSBjcmVhdGVPZmZlclxuICAgICAgICAgIC8vIEFsc28gbm90ZSB0aGF0IGFmdGVyIHRoZSBjcmVhdGVvZmZlciB3ZSBhcmUgbm90IDEwMCUgc3VyZSB0aGF0XG4gICAgICAgICAgLy8gdGhlIHBhcmFtcyB3ZXJlIGFzeW5jaHJvbm91c2x5IGFwcGxpZWQgc28gd2UgbWlnaHQgbWlzcyB0aGVcbiAgICAgICAgICAvLyBvcHBvcnR1bml0eSB0byByZWNyZWF0ZSBvZmZlci5cbiAgICAgICAgICBjb25zdCB7c2VuZGVyfSA9IHRyYW5zY2VpdmVyO1xuICAgICAgICAgIGNvbnN0IHBhcmFtcyA9IHNlbmRlci5nZXRQYXJhbWV0ZXJzKCk7XG4gICAgICAgICAgaWYgKCEoJ2VuY29kaW5ncycgaW4gcGFyYW1zKSB8fFxuICAgICAgICAgICAgICAvLyBBdm9pZCBiZWluZyBmb29sZWQgYnkgcGF0Y2hlZCBnZXRQYXJhbWV0ZXJzKCkgYmVsb3cuXG4gICAgICAgICAgICAgIChwYXJhbXMuZW5jb2RpbmdzLmxlbmd0aCA9PT0gMSAmJlxuICAgICAgICAgICAgICAgT2JqZWN0LmtleXMocGFyYW1zLmVuY29kaW5nc1swXSkubGVuZ3RoID09PSAwKSkge1xuICAgICAgICAgICAgcGFyYW1zLmVuY29kaW5ncyA9IHNlbmRFbmNvZGluZ3M7XG4gICAgICAgICAgICBzZW5kZXIuc2VuZEVuY29kaW5ncyA9IHNlbmRFbmNvZGluZ3M7XG4gICAgICAgICAgICB0aGlzLnNldFBhcmFtZXRlcnNQcm9taXNlcy5wdXNoKHNlbmRlci5zZXRQYXJhbWV0ZXJzKHBhcmFtcylcbiAgICAgICAgICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSBzZW5kZXIuc2VuZEVuY29kaW5ncztcbiAgICAgICAgICAgICAgfSkuY2F0Y2goKCkgPT4ge1xuICAgICAgICAgICAgICAgIGRlbGV0ZSBzZW5kZXIuc2VuZEVuY29kaW5ncztcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0cmFuc2NlaXZlcjtcbiAgICAgIH07XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1HZXRQYXJhbWV0ZXJzKHdpbmRvdykge1xuICBpZiAoISh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JyAmJiB3aW5kb3cuUlRDUnRwU2VuZGVyKSkge1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zdCBvcmlnR2V0UGFyYW1ldGVycyA9IHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlLmdldFBhcmFtZXRlcnM7XG4gIGlmIChvcmlnR2V0UGFyYW1ldGVycykge1xuICAgIHdpbmRvdy5SVENSdHBTZW5kZXIucHJvdG90eXBlLmdldFBhcmFtZXRlcnMgPVxuICAgICAgZnVuY3Rpb24gZ2V0UGFyYW1ldGVycygpIHtcbiAgICAgICAgY29uc3QgcGFyYW1zID0gb3JpZ0dldFBhcmFtZXRlcnMuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgaWYgKCEoJ2VuY29kaW5ncycgaW4gcGFyYW1zKSkge1xuICAgICAgICAgIHBhcmFtcy5lbmNvZGluZ3MgPSBbXS5jb25jYXQodGhpcy5zZW5kRW5jb2RpbmdzIHx8IFt7fV0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBwYXJhbXM7XG4gICAgICB9O1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQ3JlYXRlT2ZmZXIod2luZG93KSB7XG4gIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS93ZWJydGNIYWNrcy9hZGFwdGVyL2lzc3Vlcy85OTgjaXNzdWVjb21tZW50LTUxNjkyMTY0N1xuICAvLyBGaXJlZm94IGlnbm9yZXMgdGhlIGluaXQgc2VuZEVuY29kaW5ncyBvcHRpb25zIHBhc3NlZCB0byBhZGRUcmFuc2NlaXZlclxuICAvLyBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMzk2OTE4XG4gIGlmICghKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnICYmIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbikpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3Qgb3JpZ0NyZWF0ZU9mZmVyID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlcjtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVPZmZlciA9IGZ1bmN0aW9uIGNyZWF0ZU9mZmVyKCkge1xuICAgIGlmICh0aGlzLnNldFBhcmFtZXRlcnNQcm9taXNlcyAmJiB0aGlzLnNldFBhcmFtZXRlcnNQcm9taXNlcy5sZW5ndGgpIHtcbiAgICAgIHJldHVybiBQcm9taXNlLmFsbCh0aGlzLnNldFBhcmFtZXRlcnNQcm9taXNlcylcbiAgICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIHJldHVybiBvcmlnQ3JlYXRlT2ZmZXIuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfSlcbiAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzID0gW107XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gb3JpZ0NyZWF0ZU9mZmVyLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQ3JlYXRlQW5zd2VyKHdpbmRvdykge1xuICAvLyBodHRwczovL2dpdGh1Yi5jb20vd2VicnRjSGFja3MvYWRhcHRlci9pc3N1ZXMvOTk4I2lzc3VlY29tbWVudC01MTY5MjE2NDdcbiAgLy8gRmlyZWZveCBpZ25vcmVzIHRoZSBpbml0IHNlbmRFbmNvZGluZ3Mgb3B0aW9ucyBwYXNzZWQgdG8gYWRkVHJhbnNjZWl2ZXJcbiAgLy8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTM5NjkxOFxuICBpZiAoISh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0JyAmJiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24pKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IG9yaWdDcmVhdGVBbnN3ZXIgPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlLmNyZWF0ZUFuc3dlcjtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5jcmVhdGVBbnN3ZXIgPSBmdW5jdGlvbiBjcmVhdGVBbnN3ZXIoKSB7XG4gICAgaWYgKHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzICYmIHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzLmxlbmd0aCkge1xuICAgICAgcmV0dXJuIFByb21pc2UuYWxsKHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzKVxuICAgICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgICAgcmV0dXJuIG9yaWdDcmVhdGVBbnN3ZXIuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICAgICAgfSlcbiAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgIHRoaXMuc2V0UGFyYW1ldGVyc1Byb21pc2VzID0gW107XG4gICAgICAgIH0pO1xuICAgIH1cbiAgICByZXR1cm4gb3JpZ0NyZWF0ZUFuc3dlci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICB9O1xufVxuIiwiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTggVGhlIGFkYXB0ZXIuanMgcHJvamVjdCBhdXRob3JzLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqICBVc2Ugb2YgdGhpcyBzb3VyY2UgY29kZSBpcyBnb3Zlcm5lZCBieSBhIEJTRC1zdHlsZSBsaWNlbnNlXG4gKiAgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIExJQ0VOU0UgZmlsZSBpbiB0aGUgcm9vdCBvZiB0aGUgc291cmNlXG4gKiAgdHJlZS5cbiAqL1xuLyogZXNsaW50LWVudiBub2RlICovXG4ndXNlIHN0cmljdCc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltR2V0RGlzcGxheU1lZGlhKHdpbmRvdywgcHJlZmVycmVkTWVkaWFTb3VyY2UpIHtcbiAgaWYgKHdpbmRvdy5uYXZpZ2F0b3IubWVkaWFEZXZpY2VzICYmXG4gICAgJ2dldERpc3BsYXlNZWRpYScgaW4gd2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKCEod2luZG93Lm5hdmlnYXRvci5tZWRpYURldmljZXMpKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIHdpbmRvdy5uYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldERpc3BsYXlNZWRpYSA9XG4gICAgZnVuY3Rpb24gZ2V0RGlzcGxheU1lZGlhKGNvbnN0cmFpbnRzKSB7XG4gICAgICBpZiAoIShjb25zdHJhaW50cyAmJiBjb25zdHJhaW50cy52aWRlbykpIHtcbiAgICAgICAgY29uc3QgZXJyID0gbmV3IERPTUV4Y2VwdGlvbignZ2V0RGlzcGxheU1lZGlhIHdpdGhvdXQgdmlkZW8gJyArXG4gICAgICAgICAgICAnY29uc3RyYWludHMgaXMgdW5kZWZpbmVkJyk7XG4gICAgICAgIGVyci5uYW1lID0gJ05vdEZvdW5kRXJyb3InO1xuICAgICAgICAvLyBmcm9tIGh0dHBzOi8vaGV5Y2FtLmdpdGh1Yi5pby93ZWJpZGwvI2lkbC1ET01FeGNlcHRpb24tZXJyb3ItbmFtZXNcbiAgICAgICAgZXJyLmNvZGUgPSA4O1xuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZXJyKTtcbiAgICAgIH1cbiAgICAgIGlmIChjb25zdHJhaW50cy52aWRlbyA9PT0gdHJ1ZSkge1xuICAgICAgICBjb25zdHJhaW50cy52aWRlbyA9IHttZWRpYVNvdXJjZTogcHJlZmVycmVkTWVkaWFTb3VyY2V9O1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3RyYWludHMudmlkZW8ubWVkaWFTb3VyY2UgPSBwcmVmZXJyZWRNZWRpYVNvdXJjZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB3aW5kb3cubmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5nZXRVc2VyTWVkaWEoY29uc3RyYWludHMpO1xuICAgIH07XG59XG4iLCIvKlxuICogIENvcHlyaWdodCAoYykgMjAxNiBUaGUgV2ViUlRDIHByb2plY3QgYXV0aG9ycy4gQWxsIFJpZ2h0cyBSZXNlcnZlZC5cbiAqXG4gKiAgVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYSBCU0Qtc3R5bGUgbGljZW5zZVxuICogIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgaW4gdGhlIHJvb3Qgb2YgdGhlIHNvdXJjZVxuICogIHRyZWUuXG4gKi9cbi8qIGVzbGludC1lbnYgbm9kZSAqL1xuJ3VzZSBzdHJpY3QnO1xuXG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tICcuLi91dGlscyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltR2V0VXNlck1lZGlhKHdpbmRvdywgYnJvd3NlckRldGFpbHMpIHtcbiAgY29uc3QgbmF2aWdhdG9yID0gd2luZG93ICYmIHdpbmRvdy5uYXZpZ2F0b3I7XG4gIGNvbnN0IE1lZGlhU3RyZWFtVHJhY2sgPSB3aW5kb3cgJiYgd2luZG93Lk1lZGlhU3RyZWFtVHJhY2s7XG5cbiAgbmF2aWdhdG9yLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uKGNvbnN0cmFpbnRzLCBvblN1Y2Nlc3MsIG9uRXJyb3IpIHtcbiAgICAvLyBSZXBsYWNlIEZpcmVmb3ggNDQrJ3MgZGVwcmVjYXRpb24gd2FybmluZyB3aXRoIHVucHJlZml4ZWQgdmVyc2lvbi5cbiAgICB1dGlscy5kZXByZWNhdGVkKCduYXZpZ2F0b3IuZ2V0VXNlck1lZGlhJyxcbiAgICAgICduYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYScpO1xuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKGNvbnN0cmFpbnRzKS50aGVuKG9uU3VjY2Vzcywgb25FcnJvcik7XG4gIH07XG5cbiAgaWYgKCEoYnJvd3NlckRldGFpbHMudmVyc2lvbiA+IDU1ICYmXG4gICAgICAnYXV0b0dhaW5Db250cm9sJyBpbiBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFN1cHBvcnRlZENvbnN0cmFpbnRzKCkpKSB7XG4gICAgY29uc3QgcmVtYXAgPSBmdW5jdGlvbihvYmosIGEsIGIpIHtcbiAgICAgIGlmIChhIGluIG9iaiAmJiAhKGIgaW4gb2JqKSkge1xuICAgICAgICBvYmpbYl0gPSBvYmpbYV07XG4gICAgICAgIGRlbGV0ZSBvYmpbYV07XG4gICAgICB9XG4gICAgfTtcblxuICAgIGNvbnN0IG5hdGl2ZUdldFVzZXJNZWRpYSA9IG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhLlxuICAgICAgYmluZChuYXZpZ2F0b3IubWVkaWFEZXZpY2VzKTtcbiAgICBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uKGMpIHtcbiAgICAgIGlmICh0eXBlb2YgYyA9PT0gJ29iamVjdCcgJiYgdHlwZW9mIGMuYXVkaW8gPT09ICdvYmplY3QnKSB7XG4gICAgICAgIGMgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KGMpKTtcbiAgICAgICAgcmVtYXAoYy5hdWRpbywgJ2F1dG9HYWluQ29udHJvbCcsICdtb3pBdXRvR2FpbkNvbnRyb2wnKTtcbiAgICAgICAgcmVtYXAoYy5hdWRpbywgJ25vaXNlU3VwcHJlc3Npb24nLCAnbW96Tm9pc2VTdXBwcmVzc2lvbicpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIG5hdGl2ZUdldFVzZXJNZWRpYShjKTtcbiAgICB9O1xuXG4gICAgaWYgKE1lZGlhU3RyZWFtVHJhY2sgJiYgTWVkaWFTdHJlYW1UcmFjay5wcm90b3R5cGUuZ2V0U2V0dGluZ3MpIHtcbiAgICAgIGNvbnN0IG5hdGl2ZUdldFNldHRpbmdzID0gTWVkaWFTdHJlYW1UcmFjay5wcm90b3R5cGUuZ2V0U2V0dGluZ3M7XG4gICAgICBNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZS5nZXRTZXR0aW5ncyA9IGZ1bmN0aW9uKCkge1xuICAgICAgICBjb25zdCBvYmogPSBuYXRpdmVHZXRTZXR0aW5ncy5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgICByZW1hcChvYmosICdtb3pBdXRvR2FpbkNvbnRyb2wnLCAnYXV0b0dhaW5Db250cm9sJyk7XG4gICAgICAgIHJlbWFwKG9iaiwgJ21vek5vaXNlU3VwcHJlc3Npb24nLCAnbm9pc2VTdXBwcmVzc2lvbicpO1xuICAgICAgICByZXR1cm4gb2JqO1xuICAgICAgfTtcbiAgICB9XG5cbiAgICBpZiAoTWVkaWFTdHJlYW1UcmFjayAmJiBNZWRpYVN0cmVhbVRyYWNrLnByb3RvdHlwZS5hcHBseUNvbnN0cmFpbnRzKSB7XG4gICAgICBjb25zdCBuYXRpdmVBcHBseUNvbnN0cmFpbnRzID1cbiAgICAgICAgTWVkaWFTdHJlYW1UcmFjay5wcm90b3R5cGUuYXBwbHlDb25zdHJhaW50cztcbiAgICAgIE1lZGlhU3RyZWFtVHJhY2sucHJvdG90eXBlLmFwcGx5Q29uc3RyYWludHMgPSBmdW5jdGlvbihjKSB7XG4gICAgICAgIGlmICh0aGlzLmtpbmQgPT09ICdhdWRpbycgJiYgdHlwZW9mIGMgPT09ICdvYmplY3QnKSB7XG4gICAgICAgICAgYyA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoYykpO1xuICAgICAgICAgIHJlbWFwKGMsICdhdXRvR2FpbkNvbnRyb2wnLCAnbW96QXV0b0dhaW5Db250cm9sJyk7XG4gICAgICAgICAgcmVtYXAoYywgJ25vaXNlU3VwcHJlc3Npb24nLCAnbW96Tm9pc2VTdXBwcmVzc2lvbicpO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuYXRpdmVBcHBseUNvbnN0cmFpbnRzLmFwcGx5KHRoaXMsIFtjXSk7XG4gICAgICB9O1xuICAgIH1cbiAgfVxufVxuIiwiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4ndXNlIHN0cmljdCc7XG5pbXBvcnQgKiBhcyB1dGlscyBmcm9tICcuLi91dGlscyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltTG9jYWxTdHJlYW1zQVBJKHdpbmRvdykge1xuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ29iamVjdCcgfHwgIXdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbikge1xuICAgIHJldHVybjtcbiAgfVxuICBpZiAoISgnZ2V0TG9jYWxTdHJlYW1zJyBpbiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuZ2V0TG9jYWxTdHJlYW1zID1cbiAgICAgIGZ1bmN0aW9uIGdldExvY2FsU3RyZWFtcygpIHtcbiAgICAgICAgaWYgKCF0aGlzLl9sb2NhbFN0cmVhbXMpIHtcbiAgICAgICAgICB0aGlzLl9sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdGhpcy5fbG9jYWxTdHJlYW1zO1xuICAgICAgfTtcbiAgfVxuICBpZiAoISgnYWRkU3RyZWFtJyBpbiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIGNvbnN0IF9hZGRUcmFjayA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuYWRkVHJhY2s7XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRTdHJlYW0gPSBmdW5jdGlvbiBhZGRTdHJlYW0oc3RyZWFtKSB7XG4gICAgICBpZiAoIXRoaXMuX2xvY2FsU3RyZWFtcykge1xuICAgICAgICB0aGlzLl9sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgIH1cbiAgICAgIGlmICghdGhpcy5fbG9jYWxTdHJlYW1zLmluY2x1ZGVzKHN0cmVhbSkpIHtcbiAgICAgICAgdGhpcy5fbG9jYWxTdHJlYW1zLnB1c2goc3RyZWFtKTtcbiAgICAgIH1cbiAgICAgIC8vIFRyeSB0byBlbXVsYXRlIENocm9tZSdzIGJlaGF2aW91ciBvZiBhZGRpbmcgaW4gYXVkaW8tdmlkZW8gb3JkZXIuXG4gICAgICAvLyBTYWZhcmkgb3JkZXJzIGJ5IHRyYWNrIGlkLlxuICAgICAgc3RyZWFtLmdldEF1ZGlvVHJhY2tzKCkuZm9yRWFjaCh0cmFjayA9PiBfYWRkVHJhY2suY2FsbCh0aGlzLCB0cmFjayxcbiAgICAgICAgc3RyZWFtKSk7XG4gICAgICBzdHJlYW0uZ2V0VmlkZW9UcmFja3MoKS5mb3JFYWNoKHRyYWNrID0+IF9hZGRUcmFjay5jYWxsKHRoaXMsIHRyYWNrLFxuICAgICAgICBzdHJlYW0pKTtcbiAgICB9O1xuXG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5hZGRUcmFjayA9XG4gICAgICBmdW5jdGlvbiBhZGRUcmFjayh0cmFjaywgLi4uc3RyZWFtcykge1xuICAgICAgICBpZiAoc3RyZWFtcykge1xuICAgICAgICAgIHN0cmVhbXMuZm9yRWFjaCgoc3RyZWFtKSA9PiB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuX2xvY2FsU3RyZWFtcykge1xuICAgICAgICAgICAgICB0aGlzLl9sb2NhbFN0cmVhbXMgPSBbc3RyZWFtXTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoIXRoaXMuX2xvY2FsU3RyZWFtcy5pbmNsdWRlcyhzdHJlYW0pKSB7XG4gICAgICAgICAgICAgIHRoaXMuX2xvY2FsU3RyZWFtcy5wdXNoKHN0cmVhbSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIF9hZGRUcmFjay5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgICAgfTtcbiAgfVxuICBpZiAoISgncmVtb3ZlU3RyZWFtJyBpbiB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24ucHJvdG90eXBlKSkge1xuICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUucmVtb3ZlU3RyZWFtID1cbiAgICAgIGZ1bmN0aW9uIHJlbW92ZVN0cmVhbShzdHJlYW0pIHtcbiAgICAgICAgaWYgKCF0aGlzLl9sb2NhbFN0cmVhbXMpIHtcbiAgICAgICAgICB0aGlzLl9sb2NhbFN0cmVhbXMgPSBbXTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpbmRleCA9IHRoaXMuX2xvY2FsU3RyZWFtcy5pbmRleE9mKHN0cmVhbSk7XG4gICAgICAgIGlmIChpbmRleCA9PT0gLTEpIHtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fbG9jYWxTdHJlYW1zLnNwbGljZShpbmRleCwgMSk7XG4gICAgICAgIGNvbnN0IHRyYWNrcyA9IHN0cmVhbS5nZXRUcmFja3MoKTtcbiAgICAgICAgdGhpcy5nZXRTZW5kZXJzKCkuZm9yRWFjaChzZW5kZXIgPT4ge1xuICAgICAgICAgIGlmICh0cmFja3MuaW5jbHVkZXMoc2VuZGVyLnRyYWNrKSkge1xuICAgICAgICAgICAgdGhpcy5yZW1vdmVUcmFjayhzZW5kZXIpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9O1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltUmVtb3RlU3RyZWFtc0FQSSh3aW5kb3cpIHtcbiAgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICdvYmplY3QnIHx8ICF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24pIHtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKCEoJ2dldFJlbW90ZVN0cmVhbXMnIGluIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUpKSB7XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5nZXRSZW1vdGVTdHJlYW1zID1cbiAgICAgIGZ1bmN0aW9uIGdldFJlbW90ZVN0cmVhbXMoKSB7XG4gICAgICAgIHJldHVybiB0aGlzLl9yZW1vdGVTdHJlYW1zID8gdGhpcy5fcmVtb3RlU3RyZWFtcyA6IFtdO1xuICAgICAgfTtcbiAgfVxuICBpZiAoISgnb25hZGRzdHJlYW0nIGluIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUpKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUsICdvbmFkZHN0cmVhbScsIHtcbiAgICAgIGdldCgpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX29uYWRkc3RyZWFtO1xuICAgICAgfSxcbiAgICAgIHNldChmKSB7XG4gICAgICAgIGlmICh0aGlzLl9vbmFkZHN0cmVhbSkge1xuICAgICAgICAgIHRoaXMucmVtb3ZlRXZlbnRMaXN0ZW5lcignYWRkc3RyZWFtJywgdGhpcy5fb25hZGRzdHJlYW0pO1xuICAgICAgICAgIHRoaXMucmVtb3ZlRXZlbnRMaXN0ZW5lcigndHJhY2snLCB0aGlzLl9vbmFkZHN0cmVhbXBvbHkpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuYWRkRXZlbnRMaXN0ZW5lcignYWRkc3RyZWFtJywgdGhpcy5fb25hZGRzdHJlYW0gPSBmKTtcbiAgICAgICAgdGhpcy5hZGRFdmVudExpc3RlbmVyKCd0cmFjaycsIHRoaXMuX29uYWRkc3RyZWFtcG9seSA9IChlKSA9PiB7XG4gICAgICAgICAgZS5zdHJlYW1zLmZvckVhY2goc3RyZWFtID0+IHtcbiAgICAgICAgICAgIGlmICghdGhpcy5fcmVtb3RlU3RyZWFtcykge1xuICAgICAgICAgICAgICB0aGlzLl9yZW1vdGVTdHJlYW1zID0gW107XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAodGhpcy5fcmVtb3RlU3RyZWFtcy5pbmNsdWRlcyhzdHJlYW0pKSB7XG4gICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuX3JlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgY29uc3QgZXZlbnQgPSBuZXcgRXZlbnQoJ2FkZHN0cmVhbScpO1xuICAgICAgICAgICAgZXZlbnQuc3RyZWFtID0gc3RyZWFtO1xuICAgICAgICAgICAgdGhpcy5kaXNwYXRjaEV2ZW50KGV2ZW50KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgY29uc3Qgb3JpZ1NldFJlbW90ZURlc2NyaXB0aW9uID1cbiAgICAgIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuc2V0UmVtb3RlRGVzY3JpcHRpb247XG4gICAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9XG4gICAgICBmdW5jdGlvbiBzZXRSZW1vdGVEZXNjcmlwdGlvbigpIHtcbiAgICAgICAgY29uc3QgcGMgPSB0aGlzO1xuICAgICAgICBpZiAoIXRoaXMuX29uYWRkc3RyZWFtcG9seSkge1xuICAgICAgICAgIHRoaXMuYWRkRXZlbnRMaXN0ZW5lcigndHJhY2snLCB0aGlzLl9vbmFkZHN0cmVhbXBvbHkgPSBmdW5jdGlvbihlKSB7XG4gICAgICAgICAgICBlLnN0cmVhbXMuZm9yRWFjaChzdHJlYW0gPT4ge1xuICAgICAgICAgICAgICBpZiAoIXBjLl9yZW1vdGVTdHJlYW1zKSB7XG4gICAgICAgICAgICAgICAgcGMuX3JlbW90ZVN0cmVhbXMgPSBbXTtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpZiAocGMuX3JlbW90ZVN0cmVhbXMuaW5kZXhPZihzdHJlYW0pID49IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcGMuX3JlbW90ZVN0cmVhbXMucHVzaChzdHJlYW0pO1xuICAgICAgICAgICAgICBjb25zdCBldmVudCA9IG5ldyBFdmVudCgnYWRkc3RyZWFtJyk7XG4gICAgICAgICAgICAgIGV2ZW50LnN0cmVhbSA9IHN0cmVhbTtcbiAgICAgICAgICAgICAgcGMuZGlzcGF0Y2hFdmVudChldmVudCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gb3JpZ1NldFJlbW90ZURlc2NyaXB0aW9uLmFwcGx5KHBjLCBhcmd1bWVudHMpO1xuICAgICAgfTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbUNhbGxiYWNrc0FQSSh3aW5kb3cpIHtcbiAgaWYgKHR5cGVvZiB3aW5kb3cgIT09ICdvYmplY3QnIHx8ICF3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24pIHtcbiAgICByZXR1cm47XG4gIH1cbiAgY29uc3QgcHJvdG90eXBlID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZTtcbiAgY29uc3Qgb3JpZ0NyZWF0ZU9mZmVyID0gcHJvdG90eXBlLmNyZWF0ZU9mZmVyO1xuICBjb25zdCBvcmlnQ3JlYXRlQW5zd2VyID0gcHJvdG90eXBlLmNyZWF0ZUFuc3dlcjtcbiAgY29uc3Qgc2V0TG9jYWxEZXNjcmlwdGlvbiA9IHByb3RvdHlwZS5zZXRMb2NhbERlc2NyaXB0aW9uO1xuICBjb25zdCBzZXRSZW1vdGVEZXNjcmlwdGlvbiA9IHByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbjtcbiAgY29uc3QgYWRkSWNlQ2FuZGlkYXRlID0gcHJvdG90eXBlLmFkZEljZUNhbmRpZGF0ZTtcblxuICBwcm90b3R5cGUuY3JlYXRlT2ZmZXIgPVxuICAgIGZ1bmN0aW9uIGNyZWF0ZU9mZmVyKHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgICBjb25zdCBvcHRpb25zID0gKGFyZ3VtZW50cy5sZW5ndGggPj0gMikgPyBhcmd1bWVudHNbMl0gOiBhcmd1bWVudHNbMF07XG4gICAgICBjb25zdCBwcm9taXNlID0gb3JpZ0NyZWF0ZU9mZmVyLmFwcGx5KHRoaXMsIFtvcHRpb25zXSk7XG4gICAgICBpZiAoIWZhaWx1cmVDYWxsYmFjaykge1xuICAgICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICAgIH1cbiAgICAgIHByb21pc2UudGhlbihzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjayk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICBwcm90b3R5cGUuY3JlYXRlQW5zd2VyID1cbiAgICBmdW5jdGlvbiBjcmVhdGVBbnN3ZXIoc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICAgIGNvbnN0IG9wdGlvbnMgPSAoYXJndW1lbnRzLmxlbmd0aCA+PSAyKSA/IGFyZ3VtZW50c1syXSA6IGFyZ3VtZW50c1swXTtcbiAgICAgIGNvbnN0IHByb21pc2UgPSBvcmlnQ3JlYXRlQW5zd2VyLmFwcGx5KHRoaXMsIFtvcHRpb25zXSk7XG4gICAgICBpZiAoIWZhaWx1cmVDYWxsYmFjaykge1xuICAgICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICAgIH1cbiAgICAgIHByb21pc2UudGhlbihzdWNjZXNzQ2FsbGJhY2ssIGZhaWx1cmVDYWxsYmFjayk7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfTtcblxuICBsZXQgd2l0aENhbGxiYWNrID0gZnVuY3Rpb24oZGVzY3JpcHRpb24sIHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgY29uc3QgcHJvbWlzZSA9IHNldExvY2FsRGVzY3JpcHRpb24uYXBwbHkodGhpcywgW2Rlc2NyaXB0aW9uXSk7XG4gICAgaWYgKCFmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICAgIHJldHVybiBwcm9taXNlO1xuICAgIH1cbiAgICBwcm9taXNlLnRoZW4oc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spO1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgfTtcbiAgcHJvdG90eXBlLnNldExvY2FsRGVzY3JpcHRpb24gPSB3aXRoQ2FsbGJhY2s7XG5cbiAgd2l0aENhbGxiYWNrID0gZnVuY3Rpb24oZGVzY3JpcHRpb24sIHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgY29uc3QgcHJvbWlzZSA9IHNldFJlbW90ZURlc2NyaXB0aW9uLmFwcGx5KHRoaXMsIFtkZXNjcmlwdGlvbl0pO1xuICAgIGlmICghZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgICByZXR1cm4gcHJvbWlzZTtcbiAgICB9XG4gICAgcHJvbWlzZS50aGVuKHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKTtcbiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gIH07XG4gIHByb3RvdHlwZS5zZXRSZW1vdGVEZXNjcmlwdGlvbiA9IHdpdGhDYWxsYmFjaztcblxuICB3aXRoQ2FsbGJhY2sgPSBmdW5jdGlvbihjYW5kaWRhdGUsIHN1Y2Nlc3NDYWxsYmFjaywgZmFpbHVyZUNhbGxiYWNrKSB7XG4gICAgY29uc3QgcHJvbWlzZSA9IGFkZEljZUNhbmRpZGF0ZS5hcHBseSh0aGlzLCBbY2FuZGlkYXRlXSk7XG4gICAgaWYgKCFmYWlsdXJlQ2FsbGJhY2spIHtcbiAgICAgIHJldHVybiBwcm9taXNlO1xuICAgIH1cbiAgICBwcm9taXNlLnRoZW4oc3VjY2Vzc0NhbGxiYWNrLCBmYWlsdXJlQ2FsbGJhY2spO1xuICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcbiAgfTtcbiAgcHJvdG90eXBlLmFkZEljZUNhbmRpZGF0ZSA9IHdpdGhDYWxsYmFjaztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1HZXRVc2VyTWVkaWEod2luZG93KSB7XG4gIGNvbnN0IG5hdmlnYXRvciA9IHdpbmRvdyAmJiB3aW5kb3cubmF2aWdhdG9yO1xuXG4gIGlmIChuYXZpZ2F0b3IubWVkaWFEZXZpY2VzICYmIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKSB7XG4gICAgLy8gc2hpbSBub3QgbmVlZGVkIGluIFNhZmFyaSAxMi4xXG4gICAgY29uc3QgbWVkaWFEZXZpY2VzID0gbmF2aWdhdG9yLm1lZGlhRGV2aWNlcztcbiAgICBjb25zdCBfZ2V0VXNlck1lZGlhID0gbWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYS5iaW5kKG1lZGlhRGV2aWNlcyk7XG4gICAgbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5nZXRVc2VyTWVkaWEgPSAoY29uc3RyYWludHMpID0+IHtcbiAgICAgIHJldHVybiBfZ2V0VXNlck1lZGlhKHNoaW1Db25zdHJhaW50cyhjb25zdHJhaW50cykpO1xuICAgIH07XG4gIH1cblxuICBpZiAoIW5hdmlnYXRvci5nZXRVc2VyTWVkaWEgJiYgbmF2aWdhdG9yLm1lZGlhRGV2aWNlcyAmJlxuICAgIG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKSB7XG4gICAgbmF2aWdhdG9yLmdldFVzZXJNZWRpYSA9IGZ1bmN0aW9uIGdldFVzZXJNZWRpYShjb25zdHJhaW50cywgY2IsIGVycmNiKSB7XG4gICAgICBuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYShjb25zdHJhaW50cylcbiAgICAgICAgLnRoZW4oY2IsIGVycmNiKTtcbiAgICB9LmJpbmQobmF2aWdhdG9yKTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbUNvbnN0cmFpbnRzKGNvbnN0cmFpbnRzKSB7XG4gIGlmIChjb25zdHJhaW50cyAmJiBjb25zdHJhaW50cy52aWRlbyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oe30sXG4gICAgICBjb25zdHJhaW50cyxcbiAgICAgIHt2aWRlbzogdXRpbHMuY29tcGFjdE9iamVjdChjb25zdHJhaW50cy52aWRlbyl9XG4gICAgKTtcbiAgfVxuXG4gIHJldHVybiBjb25zdHJhaW50cztcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHNoaW1SVENJY2VTZXJ2ZXJVcmxzKHdpbmRvdykge1xuICBpZiAoIXdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbikge1xuICAgIHJldHVybjtcbiAgfVxuICAvLyBtaWdyYXRlIGZyb20gbm9uLXNwZWMgUlRDSWNlU2VydmVyLnVybCB0byBSVENJY2VTZXJ2ZXIudXJsc1xuICBjb25zdCBPcmlnUGVlckNvbm5lY3Rpb24gPSB3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb247XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiA9XG4gICAgZnVuY3Rpb24gUlRDUGVlckNvbm5lY3Rpb24ocGNDb25maWcsIHBjQ29uc3RyYWludHMpIHtcbiAgICAgIGlmIChwY0NvbmZpZyAmJiBwY0NvbmZpZy5pY2VTZXJ2ZXJzKSB7XG4gICAgICAgIGNvbnN0IG5ld0ljZVNlcnZlcnMgPSBbXTtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBwY0NvbmZpZy5pY2VTZXJ2ZXJzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgbGV0IHNlcnZlciA9IHBjQ29uZmlnLmljZVNlcnZlcnNbaV07XG4gICAgICAgICAgaWYgKHNlcnZlci51cmxzID09PSB1bmRlZmluZWQgJiYgc2VydmVyLnVybCkge1xuICAgICAgICAgICAgdXRpbHMuZGVwcmVjYXRlZCgnUlRDSWNlU2VydmVyLnVybCcsICdSVENJY2VTZXJ2ZXIudXJscycpO1xuICAgICAgICAgICAgc2VydmVyID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShzZXJ2ZXIpKTtcbiAgICAgICAgICAgIHNlcnZlci51cmxzID0gc2VydmVyLnVybDtcbiAgICAgICAgICAgIGRlbGV0ZSBzZXJ2ZXIudXJsO1xuICAgICAgICAgICAgbmV3SWNlU2VydmVycy5wdXNoKHNlcnZlcik7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIG5ld0ljZVNlcnZlcnMucHVzaChwY0NvbmZpZy5pY2VTZXJ2ZXJzW2ldKTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcGNDb25maWcuaWNlU2VydmVycyA9IG5ld0ljZVNlcnZlcnM7XG4gICAgICB9XG4gICAgICByZXR1cm4gbmV3IE9yaWdQZWVyQ29ubmVjdGlvbihwY0NvbmZpZywgcGNDb25zdHJhaW50cyk7XG4gICAgfTtcbiAgd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZSA9IE9yaWdQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGU7XG4gIC8vIHdyYXAgc3RhdGljIG1ldGhvZHMuIEN1cnJlbnRseSBqdXN0IGdlbmVyYXRlQ2VydGlmaWNhdGUuXG4gIGlmICgnZ2VuZXJhdGVDZXJ0aWZpY2F0ZScgaW4gT3JpZ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbiwgJ2dlbmVyYXRlQ2VydGlmaWNhdGUnLCB7XG4gICAgICBnZXQoKSB7XG4gICAgICAgIHJldHVybiBPcmlnUGVlckNvbm5lY3Rpb24uZ2VuZXJhdGVDZXJ0aWZpY2F0ZTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hpbVRyYWNrRXZlbnRUcmFuc2NlaXZlcih3aW5kb3cpIHtcbiAgLy8gQWRkIGV2ZW50LnRyYW5zY2VpdmVyIG1lbWJlciBvdmVyIGRlcHJlY2F0ZWQgZXZlbnQucmVjZWl2ZXJcbiAgaWYgKHR5cGVvZiB3aW5kb3cgPT09ICdvYmplY3QnICYmIHdpbmRvdy5SVENUcmFja0V2ZW50ICYmXG4gICAgICAncmVjZWl2ZXInIGluIHdpbmRvdy5SVENUcmFja0V2ZW50LnByb3RvdHlwZSAmJlxuICAgICAgISgndHJhbnNjZWl2ZXInIGluIHdpbmRvdy5SVENUcmFja0V2ZW50LnByb3RvdHlwZSkpIHtcbiAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkod2luZG93LlJUQ1RyYWNrRXZlbnQucHJvdG90eXBlLCAndHJhbnNjZWl2ZXInLCB7XG4gICAgICBnZXQoKSB7XG4gICAgICAgIHJldHVybiB7cmVjZWl2ZXI6IHRoaXMucmVjZWl2ZXJ9O1xuICAgICAgfVxuICAgIH0pO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQ3JlYXRlT2ZmZXJMZWdhY3kod2luZG93KSB7XG4gIGNvbnN0IG9yaWdDcmVhdGVPZmZlciA9IHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXI7XG4gIHdpbmRvdy5SVENQZWVyQ29ubmVjdGlvbi5wcm90b3R5cGUuY3JlYXRlT2ZmZXIgPVxuICAgIGZ1bmN0aW9uIGNyZWF0ZU9mZmVyKG9mZmVyT3B0aW9ucykge1xuICAgICAgaWYgKG9mZmVyT3B0aW9ucykge1xuICAgICAgICBpZiAodHlwZW9mIG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZUF1ZGlvICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgIC8vIHN1cHBvcnQgYml0IHZhbHVlc1xuICAgICAgICAgIG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZUF1ZGlvID1cbiAgICAgICAgICAgICEhb2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW87XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXVkaW9UcmFuc2NlaXZlciA9IHRoaXMuZ2V0VHJhbnNjZWl2ZXJzKCkuZmluZCh0cmFuc2NlaXZlciA9PlxuICAgICAgICAgIHRyYW5zY2VpdmVyLnJlY2VpdmVyLnRyYWNrLmtpbmQgPT09ICdhdWRpbycpO1xuICAgICAgICBpZiAob2ZmZXJPcHRpb25zLm9mZmVyVG9SZWNlaXZlQXVkaW8gPT09IGZhbHNlICYmIGF1ZGlvVHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICBpZiAoYXVkaW9UcmFuc2NlaXZlci5kaXJlY3Rpb24gPT09ICdzZW5kcmVjdicpIHtcbiAgICAgICAgICAgIGlmIChhdWRpb1RyYW5zY2VpdmVyLnNldERpcmVjdGlvbikge1xuICAgICAgICAgICAgICBhdWRpb1RyYW5zY2VpdmVyLnNldERpcmVjdGlvbignc2VuZG9ubHknKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGF1ZGlvVHJhbnNjZWl2ZXIuZGlyZWN0aW9uID0gJ3NlbmRvbmx5JztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGF1ZGlvVHJhbnNjZWl2ZXIuZGlyZWN0aW9uID09PSAncmVjdm9ubHknKSB7XG4gICAgICAgICAgICBpZiAoYXVkaW9UcmFuc2NlaXZlci5zZXREaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgYXVkaW9UcmFuc2NlaXZlci5zZXREaXJlY3Rpb24oJ2luYWN0aXZlJyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBhdWRpb1RyYW5zY2VpdmVyLmRpcmVjdGlvbiA9ICdpbmFjdGl2ZSc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYgKG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZUF1ZGlvID09PSB0cnVlICYmXG4gICAgICAgICAgICAhYXVkaW9UcmFuc2NlaXZlcikge1xuICAgICAgICAgIHRoaXMuYWRkVHJhbnNjZWl2ZXIoJ2F1ZGlvJywge2RpcmVjdGlvbjogJ3JlY3Zvbmx5J30pO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHR5cGVvZiBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVWaWRlbyAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgICAvLyBzdXBwb3J0IGJpdCB2YWx1ZXNcbiAgICAgICAgICBvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVWaWRlbyA9XG4gICAgICAgICAgICAhIW9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHZpZGVvVHJhbnNjZWl2ZXIgPSB0aGlzLmdldFRyYW5zY2VpdmVycygpLmZpbmQodHJhbnNjZWl2ZXIgPT5cbiAgICAgICAgICB0cmFuc2NlaXZlci5yZWNlaXZlci50cmFjay5raW5kID09PSAndmlkZW8nKTtcbiAgICAgICAgaWYgKG9mZmVyT3B0aW9ucy5vZmZlclRvUmVjZWl2ZVZpZGVvID09PSBmYWxzZSAmJiB2aWRlb1RyYW5zY2VpdmVyKSB7XG4gICAgICAgICAgaWYgKHZpZGVvVHJhbnNjZWl2ZXIuZGlyZWN0aW9uID09PSAnc2VuZHJlY3YnKSB7XG4gICAgICAgICAgICBpZiAodmlkZW9UcmFuc2NlaXZlci5zZXREaXJlY3Rpb24pIHtcbiAgICAgICAgICAgICAgdmlkZW9UcmFuc2NlaXZlci5zZXREaXJlY3Rpb24oJ3NlbmRvbmx5Jyk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICB2aWRlb1RyYW5zY2VpdmVyLmRpcmVjdGlvbiA9ICdzZW5kb25seSc7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmICh2aWRlb1RyYW5zY2VpdmVyLmRpcmVjdGlvbiA9PT0gJ3JlY3Zvbmx5Jykge1xuICAgICAgICAgICAgaWYgKHZpZGVvVHJhbnNjZWl2ZXIuc2V0RGlyZWN0aW9uKSB7XG4gICAgICAgICAgICAgIHZpZGVvVHJhbnNjZWl2ZXIuc2V0RGlyZWN0aW9uKCdpbmFjdGl2ZScpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdmlkZW9UcmFuc2NlaXZlci5kaXJlY3Rpb24gPSAnaW5hY3RpdmUnO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmIChvZmZlck9wdGlvbnMub2ZmZXJUb1JlY2VpdmVWaWRlbyA9PT0gdHJ1ZSAmJlxuICAgICAgICAgICAgIXZpZGVvVHJhbnNjZWl2ZXIpIHtcbiAgICAgICAgICB0aGlzLmFkZFRyYW5zY2VpdmVyKCd2aWRlbycsIHtkaXJlY3Rpb246ICdyZWN2b25seSd9KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG9yaWdDcmVhdGVPZmZlci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBzaGltQXVkaW9Db250ZXh0KHdpbmRvdykge1xuICBpZiAodHlwZW9mIHdpbmRvdyAhPT0gJ29iamVjdCcgfHwgd2luZG93LkF1ZGlvQ29udGV4dCkge1xuICAgIHJldHVybjtcbiAgfVxuICB3aW5kb3cuQXVkaW9Db250ZXh0ID0gd2luZG93LndlYmtpdEF1ZGlvQ29udGV4dDtcbn1cblxuIiwiLypcbiAqICBDb3B5cmlnaHQgKGMpIDIwMTYgVGhlIFdlYlJUQyBwcm9qZWN0IGF1dGhvcnMuIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogIFVzZSBvZiB0aGlzIHNvdXJjZSBjb2RlIGlzIGdvdmVybmVkIGJ5IGEgQlNELXN0eWxlIGxpY2Vuc2VcbiAqICB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgTElDRU5TRSBmaWxlIGluIHRoZSByb290IG9mIHRoZSBzb3VyY2VcbiAqICB0cmVlLlxuICovXG4vKiBlc2xpbnQtZW52IG5vZGUgKi9cbid1c2Ugc3RyaWN0JztcblxubGV0IGxvZ0Rpc2FibGVkXyA9IHRydWU7XG5sZXQgZGVwcmVjYXRpb25XYXJuaW5nc18gPSB0cnVlO1xuXG4vKipcbiAqIEV4dHJhY3QgYnJvd3NlciB2ZXJzaW9uIG91dCBvZiB0aGUgcHJvdmlkZWQgdXNlciBhZ2VudCBzdHJpbmcuXG4gKlxuICogQHBhcmFtIHshc3RyaW5nfSB1YXN0cmluZyB1c2VyQWdlbnQgc3RyaW5nLlxuICogQHBhcmFtIHshc3RyaW5nfSBleHByIFJlZ3VsYXIgZXhwcmVzc2lvbiB1c2VkIGFzIG1hdGNoIGNyaXRlcmlhLlxuICogQHBhcmFtIHshbnVtYmVyfSBwb3MgcG9zaXRpb24gaW4gdGhlIHZlcnNpb24gc3RyaW5nIHRvIGJlIHJldHVybmVkLlxuICogQHJldHVybiB7IW51bWJlcn0gYnJvd3NlciB2ZXJzaW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdFZlcnNpb24odWFzdHJpbmcsIGV4cHIsIHBvcykge1xuICBjb25zdCBtYXRjaCA9IHVhc3RyaW5nLm1hdGNoKGV4cHIpO1xuICByZXR1cm4gbWF0Y2ggJiYgbWF0Y2gubGVuZ3RoID49IHBvcyAmJiBwYXJzZUludChtYXRjaFtwb3NdLCAxMCk7XG59XG5cbi8vIFdyYXBzIHRoZSBwZWVyY29ubmVjdGlvbiBldmVudCBldmVudE5hbWVUb1dyYXAgaW4gYSBmdW5jdGlvblxuLy8gd2hpY2ggcmV0dXJucyB0aGUgbW9kaWZpZWQgZXZlbnQgb2JqZWN0IChvciBmYWxzZSB0byBwcmV2ZW50XG4vLyB0aGUgZXZlbnQpLlxuZXhwb3J0IGZ1bmN0aW9uIHdyYXBQZWVyQ29ubmVjdGlvbkV2ZW50KHdpbmRvdywgZXZlbnROYW1lVG9XcmFwLCB3cmFwcGVyKSB7XG4gIGlmICghd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHByb3RvID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uLnByb3RvdHlwZTtcbiAgY29uc3QgbmF0aXZlQWRkRXZlbnRMaXN0ZW5lciA9IHByb3RvLmFkZEV2ZW50TGlzdGVuZXI7XG4gIHByb3RvLmFkZEV2ZW50TGlzdGVuZXIgPSBmdW5jdGlvbihuYXRpdmVFdmVudE5hbWUsIGNiKSB7XG4gICAgaWYgKG5hdGl2ZUV2ZW50TmFtZSAhPT0gZXZlbnROYW1lVG9XcmFwKSB7XG4gICAgICByZXR1cm4gbmF0aXZlQWRkRXZlbnRMaXN0ZW5lci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cbiAgICBjb25zdCB3cmFwcGVkQ2FsbGJhY2sgPSAoZSkgPT4ge1xuICAgICAgY29uc3QgbW9kaWZpZWRFdmVudCA9IHdyYXBwZXIoZSk7XG4gICAgICBpZiAobW9kaWZpZWRFdmVudCkge1xuICAgICAgICBpZiAoY2IuaGFuZGxlRXZlbnQpIHtcbiAgICAgICAgICBjYi5oYW5kbGVFdmVudChtb2RpZmllZEV2ZW50KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjYihtb2RpZmllZEV2ZW50KTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5fZXZlbnRNYXAgPSB0aGlzLl9ldmVudE1hcCB8fCB7fTtcbiAgICBpZiAoIXRoaXMuX2V2ZW50TWFwW2V2ZW50TmFtZVRvV3JhcF0pIHtcbiAgICAgIHRoaXMuX2V2ZW50TWFwW2V2ZW50TmFtZVRvV3JhcF0gPSBuZXcgTWFwKCk7XG4gICAgfVxuICAgIHRoaXMuX2V2ZW50TWFwW2V2ZW50TmFtZVRvV3JhcF0uc2V0KGNiLCB3cmFwcGVkQ2FsbGJhY2spO1xuICAgIHJldHVybiBuYXRpdmVBZGRFdmVudExpc3RlbmVyLmFwcGx5KHRoaXMsIFtuYXRpdmVFdmVudE5hbWUsXG4gICAgICB3cmFwcGVkQ2FsbGJhY2tdKTtcbiAgfTtcblxuICBjb25zdCBuYXRpdmVSZW1vdmVFdmVudExpc3RlbmVyID0gcHJvdG8ucmVtb3ZlRXZlbnRMaXN0ZW5lcjtcbiAgcHJvdG8ucmVtb3ZlRXZlbnRMaXN0ZW5lciA9IGZ1bmN0aW9uKG5hdGl2ZUV2ZW50TmFtZSwgY2IpIHtcbiAgICBpZiAobmF0aXZlRXZlbnROYW1lICE9PSBldmVudE5hbWVUb1dyYXAgfHwgIXRoaXMuX2V2ZW50TWFwXG4gICAgICAgIHx8ICF0aGlzLl9ldmVudE1hcFtldmVudE5hbWVUb1dyYXBdKSB7XG4gICAgICByZXR1cm4gbmF0aXZlUmVtb3ZlRXZlbnRMaXN0ZW5lci5hcHBseSh0aGlzLCBhcmd1bWVudHMpO1xuICAgIH1cbiAgICBpZiAoIXRoaXMuX2V2ZW50TWFwW2V2ZW50TmFtZVRvV3JhcF0uaGFzKGNiKSkge1xuICAgICAgcmV0dXJuIG5hdGl2ZVJlbW92ZUV2ZW50TGlzdGVuZXIuYXBwbHkodGhpcywgYXJndW1lbnRzKTtcbiAgICB9XG4gICAgY29uc3QgdW53cmFwcGVkQ2IgPSB0aGlzLl9ldmVudE1hcFtldmVudE5hbWVUb1dyYXBdLmdldChjYik7XG4gICAgdGhpcy5fZXZlbnRNYXBbZXZlbnROYW1lVG9XcmFwXS5kZWxldGUoY2IpO1xuICAgIGlmICh0aGlzLl9ldmVudE1hcFtldmVudE5hbWVUb1dyYXBdLnNpemUgPT09IDApIHtcbiAgICAgIGRlbGV0ZSB0aGlzLl9ldmVudE1hcFtldmVudE5hbWVUb1dyYXBdO1xuICAgIH1cbiAgICBpZiAoT2JqZWN0LmtleXModGhpcy5fZXZlbnRNYXApLmxlbmd0aCA9PT0gMCkge1xuICAgICAgZGVsZXRlIHRoaXMuX2V2ZW50TWFwO1xuICAgIH1cbiAgICByZXR1cm4gbmF0aXZlUmVtb3ZlRXZlbnRMaXN0ZW5lci5hcHBseSh0aGlzLCBbbmF0aXZlRXZlbnROYW1lLFxuICAgICAgdW53cmFwcGVkQ2JdKTtcbiAgfTtcblxuICBPYmplY3QuZGVmaW5lUHJvcGVydHkocHJvdG8sICdvbicgKyBldmVudE5hbWVUb1dyYXAsIHtcbiAgICBnZXQoKSB7XG4gICAgICByZXR1cm4gdGhpc1snX29uJyArIGV2ZW50TmFtZVRvV3JhcF07XG4gICAgfSxcbiAgICBzZXQoY2IpIHtcbiAgICAgIGlmICh0aGlzWydfb24nICsgZXZlbnROYW1lVG9XcmFwXSkge1xuICAgICAgICB0aGlzLnJlbW92ZUV2ZW50TGlzdGVuZXIoZXZlbnROYW1lVG9XcmFwLFxuICAgICAgICAgIHRoaXNbJ19vbicgKyBldmVudE5hbWVUb1dyYXBdKTtcbiAgICAgICAgZGVsZXRlIHRoaXNbJ19vbicgKyBldmVudE5hbWVUb1dyYXBdO1xuICAgICAgfVxuICAgICAgaWYgKGNiKSB7XG4gICAgICAgIHRoaXMuYWRkRXZlbnRMaXN0ZW5lcihldmVudE5hbWVUb1dyYXAsXG4gICAgICAgICAgdGhpc1snX29uJyArIGV2ZW50TmFtZVRvV3JhcF0gPSBjYik7XG4gICAgICB9XG4gICAgfSxcbiAgICBlbnVtZXJhYmxlOiB0cnVlLFxuICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICB9KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGRpc2FibGVMb2coYm9vbCkge1xuICBpZiAodHlwZW9mIGJvb2wgIT09ICdib29sZWFuJykge1xuICAgIHJldHVybiBuZXcgRXJyb3IoJ0FyZ3VtZW50IHR5cGU6ICcgKyB0eXBlb2YgYm9vbCArXG4gICAgICAgICcuIFBsZWFzZSB1c2UgYSBib29sZWFuLicpO1xuICB9XG4gIGxvZ0Rpc2FibGVkXyA9IGJvb2w7XG4gIHJldHVybiAoYm9vbCkgPyAnYWRhcHRlci5qcyBsb2dnaW5nIGRpc2FibGVkJyA6XG4gICAgJ2FkYXB0ZXIuanMgbG9nZ2luZyBlbmFibGVkJztcbn1cblxuLyoqXG4gKiBEaXNhYmxlIG9yIGVuYWJsZSBkZXByZWNhdGlvbiB3YXJuaW5nc1xuICogQHBhcmFtIHshYm9vbGVhbn0gYm9vbCBzZXQgdG8gdHJ1ZSB0byBkaXNhYmxlIHdhcm5pbmdzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZGlzYWJsZVdhcm5pbmdzKGJvb2wpIHtcbiAgaWYgKHR5cGVvZiBib29sICE9PSAnYm9vbGVhbicpIHtcbiAgICByZXR1cm4gbmV3IEVycm9yKCdBcmd1bWVudCB0eXBlOiAnICsgdHlwZW9mIGJvb2wgK1xuICAgICAgICAnLiBQbGVhc2UgdXNlIGEgYm9vbGVhbi4nKTtcbiAgfVxuICBkZXByZWNhdGlvbldhcm5pbmdzXyA9ICFib29sO1xuICByZXR1cm4gJ2FkYXB0ZXIuanMgZGVwcmVjYXRpb24gd2FybmluZ3MgJyArIChib29sID8gJ2Rpc2FibGVkJyA6ICdlbmFibGVkJyk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2coKSB7XG4gIGlmICh0eXBlb2Ygd2luZG93ID09PSAnb2JqZWN0Jykge1xuICAgIGlmIChsb2dEaXNhYmxlZF8pIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgaWYgKHR5cGVvZiBjb25zb2xlICE9PSAndW5kZWZpbmVkJyAmJiB0eXBlb2YgY29uc29sZS5sb2cgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgIGNvbnNvbGUubG9nLmFwcGx5KGNvbnNvbGUsIGFyZ3VtZW50cyk7XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogU2hvd3MgYSBkZXByZWNhdGlvbiB3YXJuaW5nIHN1Z2dlc3RpbmcgdGhlIG1vZGVybiBhbmQgc3BlYy1jb21wYXRpYmxlIEFQSS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRlcHJlY2F0ZWQob2xkTWV0aG9kLCBuZXdNZXRob2QpIHtcbiAgaWYgKCFkZXByZWNhdGlvbldhcm5pbmdzXykge1xuICAgIHJldHVybjtcbiAgfVxuICBjb25zb2xlLndhcm4ob2xkTWV0aG9kICsgJyBpcyBkZXByZWNhdGVkLCBwbGVhc2UgdXNlICcgKyBuZXdNZXRob2QgK1xuICAgICAgJyBpbnN0ZWFkLicpO1xufVxuXG4vKipcbiAqIEJyb3dzZXIgZGV0ZWN0b3IuXG4gKlxuICogQHJldHVybiB7b2JqZWN0fSByZXN1bHQgY29udGFpbmluZyBicm93c2VyIGFuZCB2ZXJzaW9uXG4gKiAgICAgcHJvcGVydGllcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGRldGVjdEJyb3dzZXIod2luZG93KSB7XG4gIC8vIFJldHVybmVkIHJlc3VsdCBvYmplY3QuXG4gIGNvbnN0IHJlc3VsdCA9IHticm93c2VyOiBudWxsLCB2ZXJzaW9uOiBudWxsfTtcblxuICAvLyBGYWlsIGVhcmx5IGlmIGl0J3Mgbm90IGEgYnJvd3NlclxuICBpZiAodHlwZW9mIHdpbmRvdyA9PT0gJ3VuZGVmaW5lZCcgfHwgIXdpbmRvdy5uYXZpZ2F0b3IgfHxcbiAgICAgICF3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudCkge1xuICAgIHJlc3VsdC5icm93c2VyID0gJ05vdCBhIGJyb3dzZXIuJztcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgY29uc3Qge25hdmlnYXRvcn0gPSB3aW5kb3c7XG5cbiAgaWYgKG5hdmlnYXRvci5tb3pHZXRVc2VyTWVkaWEpIHsgLy8gRmlyZWZveC5cbiAgICByZXN1bHQuYnJvd3NlciA9ICdmaXJlZm94JztcbiAgICByZXN1bHQudmVyc2lvbiA9IGV4dHJhY3RWZXJzaW9uKG5hdmlnYXRvci51c2VyQWdlbnQsXG4gICAgICAvRmlyZWZveFxcLyhcXGQrKVxcLi8sIDEpO1xuICB9IGVsc2UgaWYgKG5hdmlnYXRvci53ZWJraXRHZXRVc2VyTWVkaWEgfHxcbiAgICAgICh3aW5kb3cuaXNTZWN1cmVDb250ZXh0ID09PSBmYWxzZSAmJiB3aW5kb3cud2Via2l0UlRDUGVlckNvbm5lY3Rpb24pKSB7XG4gICAgLy8gQ2hyb21lLCBDaHJvbWl1bSwgV2VidmlldywgT3BlcmEuXG4gICAgLy8gVmVyc2lvbiBtYXRjaGVzIENocm9tZS9XZWJSVEMgdmVyc2lvbi5cbiAgICAvLyBDaHJvbWUgNzQgcmVtb3ZlZCB3ZWJraXRHZXRVc2VyTWVkaWEgb24gaHR0cCBhcyB3ZWxsIHNvIHdlIG5lZWQgdGhlXG4gICAgLy8gbW9yZSBjb21wbGljYXRlZCBmYWxsYmFjayB0byB3ZWJraXRSVENQZWVyQ29ubmVjdGlvbi5cbiAgICByZXN1bHQuYnJvd3NlciA9ICdjaHJvbWUnO1xuICAgIHJlc3VsdC52ZXJzaW9uID0gZXh0cmFjdFZlcnNpb24obmF2aWdhdG9yLnVzZXJBZ2VudCxcbiAgICAgIC9DaHJvbShlfGl1bSlcXC8oXFxkKylcXC4vLCAyKTtcbiAgfSBlbHNlIGlmICh3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gJiZcbiAgICAgIG5hdmlnYXRvci51c2VyQWdlbnQubWF0Y2goL0FwcGxlV2ViS2l0XFwvKFxcZCspXFwuLykpIHsgLy8gU2FmYXJpLlxuICAgIHJlc3VsdC5icm93c2VyID0gJ3NhZmFyaSc7XG4gICAgcmVzdWx0LnZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihuYXZpZ2F0b3IudXNlckFnZW50LFxuICAgICAgL0FwcGxlV2ViS2l0XFwvKFxcZCspXFwuLywgMSk7XG4gICAgcmVzdWx0LnN1cHBvcnRzVW5pZmllZFBsYW4gPSB3aW5kb3cuUlRDUnRwVHJhbnNjZWl2ZXIgJiZcbiAgICAgICAgJ2N1cnJlbnREaXJlY3Rpb24nIGluIHdpbmRvdy5SVENSdHBUcmFuc2NlaXZlci5wcm90b3R5cGU7XG4gIH0gZWxzZSB7IC8vIERlZmF1bHQgZmFsbHRocm91Z2g6IG5vdCBzdXBwb3J0ZWQuXG4gICAgcmVzdWx0LmJyb3dzZXIgPSAnTm90IGEgc3VwcG9ydGVkIGJyb3dzZXIuJztcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgc29tZXRoaW5nIGlzIGFuIG9iamVjdC5cbiAqXG4gKiBAcGFyYW0geyp9IHZhbCBUaGUgc29tZXRoaW5nIHlvdSB3YW50IHRvIGNoZWNrLlxuICogQHJldHVybiB0cnVlIGlmIHZhbCBpcyBhbiBvYmplY3QsIGZhbHNlIG90aGVyd2lzZS5cbiAqL1xuZnVuY3Rpb24gaXNPYmplY3QodmFsKSB7XG4gIHJldHVybiBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodmFsKSA9PT0gJ1tvYmplY3QgT2JqZWN0XSc7XG59XG5cbi8qKlxuICogUmVtb3ZlIGFsbCBlbXB0eSBvYmplY3RzIGFuZCB1bmRlZmluZWQgdmFsdWVzXG4gKiBmcm9tIGEgbmVzdGVkIG9iamVjdCAtLSBhbiBlbmhhbmNlZCBhbmQgdmFuaWxsYSB2ZXJzaW9uXG4gKiBvZiBMb2Rhc2gncyBgY29tcGFjdGAuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21wYWN0T2JqZWN0KGRhdGEpIHtcbiAgaWYgKCFpc09iamVjdChkYXRhKSkge1xuICAgIHJldHVybiBkYXRhO1xuICB9XG5cbiAgcmV0dXJuIE9iamVjdC5rZXlzKGRhdGEpLnJlZHVjZShmdW5jdGlvbihhY2N1bXVsYXRvciwga2V5KSB7XG4gICAgY29uc3QgaXNPYmogPSBpc09iamVjdChkYXRhW2tleV0pO1xuICAgIGNvbnN0IHZhbHVlID0gaXNPYmogPyBjb21wYWN0T2JqZWN0KGRhdGFba2V5XSkgOiBkYXRhW2tleV07XG4gICAgY29uc3QgaXNFbXB0eU9iamVjdCA9IGlzT2JqICYmICFPYmplY3Qua2V5cyh2YWx1ZSkubGVuZ3RoO1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IGlzRW1wdHlPYmplY3QpIHtcbiAgICAgIHJldHVybiBhY2N1bXVsYXRvcjtcbiAgICB9XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oYWNjdW11bGF0b3IsIHtba2V5XTogdmFsdWV9KTtcbiAgfSwge30pO1xufVxuXG4vKiBpdGVyYXRlcyB0aGUgc3RhdHMgZ3JhcGggcmVjdXJzaXZlbHkuICovXG5leHBvcnQgZnVuY3Rpb24gd2Fsa1N0YXRzKHN0YXRzLCBiYXNlLCByZXN1bHRTZXQpIHtcbiAgaWYgKCFiYXNlIHx8IHJlc3VsdFNldC5oYXMoYmFzZS5pZCkpIHtcbiAgICByZXR1cm47XG4gIH1cbiAgcmVzdWx0U2V0LnNldChiYXNlLmlkLCBiYXNlKTtcbiAgT2JqZWN0LmtleXMoYmFzZSkuZm9yRWFjaChuYW1lID0+IHtcbiAgICBpZiAobmFtZS5lbmRzV2l0aCgnSWQnKSkge1xuICAgICAgd2Fsa1N0YXRzKHN0YXRzLCBzdGF0cy5nZXQoYmFzZVtuYW1lXSksIHJlc3VsdFNldCk7XG4gICAgfSBlbHNlIGlmIChuYW1lLmVuZHNXaXRoKCdJZHMnKSkge1xuICAgICAgYmFzZVtuYW1lXS5mb3JFYWNoKGlkID0+IHtcbiAgICAgICAgd2Fsa1N0YXRzKHN0YXRzLCBzdGF0cy5nZXQoaWQpLCByZXN1bHRTZXQpO1xuICAgICAgfSk7XG4gICAgfVxuICB9KTtcbn1cblxuLyogZmlsdGVyIGdldFN0YXRzIGZvciBhIHNlbmRlci9yZWNlaXZlciB0cmFjay4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaWx0ZXJTdGF0cyhyZXN1bHQsIHRyYWNrLCBvdXRib3VuZCkge1xuICBjb25zdCBzdHJlYW1TdGF0c1R5cGUgPSBvdXRib3VuZCA/ICdvdXRib3VuZC1ydHAnIDogJ2luYm91bmQtcnRwJztcbiAgY29uc3QgZmlsdGVyZWRSZXN1bHQgPSBuZXcgTWFwKCk7XG4gIGlmICh0cmFjayA9PT0gbnVsbCkge1xuICAgIHJldHVybiBmaWx0ZXJlZFJlc3VsdDtcbiAgfVxuICBjb25zdCB0cmFja1N0YXRzID0gW107XG4gIHJlc3VsdC5mb3JFYWNoKHZhbHVlID0+IHtcbiAgICBpZiAodmFsdWUudHlwZSA9PT0gJ3RyYWNrJyAmJlxuICAgICAgICB2YWx1ZS50cmFja0lkZW50aWZpZXIgPT09IHRyYWNrLmlkKSB7XG4gICAgICB0cmFja1N0YXRzLnB1c2godmFsdWUpO1xuICAgIH1cbiAgfSk7XG4gIHRyYWNrU3RhdHMuZm9yRWFjaCh0cmFja1N0YXQgPT4ge1xuICAgIHJlc3VsdC5mb3JFYWNoKHN0YXRzID0+IHtcbiAgICAgIGlmIChzdGF0cy50eXBlID09PSBzdHJlYW1TdGF0c1R5cGUgJiYgc3RhdHMudHJhY2tJZCA9PT0gdHJhY2tTdGF0LmlkKSB7XG4gICAgICAgIHdhbGtTdGF0cyhyZXN1bHQsIHN0YXRzLCBmaWx0ZXJlZFJlc3VsdCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xuICByZXR1cm4gZmlsdGVyZWRSZXN1bHQ7XG59XG5cbiIsIi8qXG4gKiAoQykgQ29weXJpZ2h0IDIwMTctMjAyMiBPcGVuVmlkdSAoaHR0cHM6Ly9vcGVudmlkdS5pbylcbiAqXG4gKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpO1xuICogeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLlxuICogWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG4gKiBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG4gKiBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cbiAqIFNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbiAqIGxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuICpcbiAqL1xuXG4vLyB0YWtlbiBmcm9tIGhlcmU6XG4vLyBodHRwczovL2dpdGh1Yi5jb20vT3BlblZpZHUvb3BlbnZpZHUvYmxvYi9tYXN0ZXIvb3BlbnZpZHUtYnJvd3Nlci9zcmMvT3BlblZpZHVJbnRlcm5hbC9XZWJSdGNQZWVyL1dlYlJ0Y1BlZXIudHNcbi8vIGFuZCBtb25rZXktcGF0Y2hlZFxuY29uc3QgT21VdGlsID0gcmVxdWlyZSgnLi4vbWFpbi9vbXV0aWxzJyk7XG5cbmNvbnN0IGZyZWVpY2UgPSByZXF1aXJlKCdmcmVlaWNlJyk7XG5cbmNvbnN0IEV4Y2VwdGlvbkV2ZW50TmFtZSA9IHtcblx0LyoqXG5cdCAqIFRoZSBbSUNFIGNvbm5lY3Rpb24gc3RhdGVdKGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9SVENQZWVyQ29ubmVjdGlvbi9pY2VDb25uZWN0aW9uU3RhdGUpXG5cdCAqIG9mIGFuIFtSVENQZWVyQ29ubmVjdGlvbl0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1JUQ1BlZXJDb25uZWN0aW9uKSByZWFjaGVkIGBmYWlsZWRgIHN0YXR1cy5cblx0ICpcblx0ICogVGhpcyBpcyBhIHRlcm1pbmFsIGVycm9yIHRoYXQgd29uJ3QgaGF2ZSBhbnkga2luZCBvZiBwb3NzaWJsZSByZWNvdmVyeS4gSWYgdGhlIGNsaWVudCBpcyBzdGlsbCBjb25uZWN0ZWQgdG8gT3BlblZpZHUgU2VydmVyLFxuXHQgKiB0aGVuIGFuIGF1dG9tYXRpYyByZWNvbm5lY3Rpb24gcHJvY2VzcyBvZiB0aGUgbWVkaWEgc3RyZWFtIGlzIGltbWVkaWF0ZWx5IHBlcmZvcm1lZC4gSWYgdGhlIElDRSBjb25uZWN0aW9uIGhhcyBicm9rZW4gZHVlIHRvXG5cdCAqIGEgdG90YWwgbmV0d29yayBkcm9wLCB0aGVuIG5vIGF1dG9tYXRpYyByZWNvbm5lY3Rpb24gcHJvY2VzcyB3aWxsIGJlIHBvc3NpYmxlLlxuXHQgKlxuXHQgKiB7QGxpbmsgRXhjZXB0aW9uRXZlbnR9IG9iamVjdHMgd2l0aCB0aGlzIHtAbGluayBFeGNlcHRpb25FdmVudC5uYW1lfSB3aWxsIGhhdmUgYXMge0BsaW5rIEV4Y2VwdGlvbkV2ZW50Lm9yaWdpbn0gcHJvcGVydHkgYSB7QGxpbmsgU3RyZWFtfSBvYmplY3QuXG5cdCAqL1xuXHQgSUNFX0NPTk5FQ1RJT05fRkFJTEVEOiAnSUNFX0NPTk5FQ1RJT05fRkFJTEVEJyxcblxuXHQvKipcblx0ICogVGhlIFtJQ0UgY29ubmVjdGlvbiBzdGF0ZV0oaHR0cHM6Ly9kZXZlbG9wZXIubW96aWxsYS5vcmcvZW4tVVMvZG9jcy9XZWIvQVBJL1JUQ1BlZXJDb25uZWN0aW9uL2ljZUNvbm5lY3Rpb25TdGF0ZSlcblx0ICogb2YgYW4gW1JUQ1BlZXJDb25uZWN0aW9uXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9BUEkvUlRDUGVlckNvbm5lY3Rpb24pIHJlYWNoZWQgYGRpc2Nvbm5lY3RlZGAgc3RhdHVzLlxuXHQgKlxuXHQgKiBUaGlzIGlzIG5vdCBhIHRlcm1pbmFsIGVycm9yLCBhbmQgaXQgaXMgcG9zc2libGUgZm9yIHRoZSBJQ0UgY29ubmVjdGlvbiB0byBiZSByZWNvbm5lY3RlZC4gSWYgdGhlIGNsaWVudCBpcyBzdGlsbCBjb25uZWN0ZWQgdG9cblx0ICogT3BlblZpZHUgU2VydmVyIGFuZCBhZnRlciBjZXJ0YWluIHRpbWVvdXQgdGhlIElDRSBjb25uZWN0aW9uIGhhcyBub3QgcmVhY2hlZCBhIHN1Y2Nlc3Mgb3IgdGVybWluYWwgc3RhdHVzLCB0aGVuIGFuIGF1dG9tYXRpY1xuXHQgKiByZWNvbm5lY3Rpb24gcHJvY2VzcyBvZiB0aGUgbWVkaWEgc3RyZWFtIGlzIHBlcmZvcm1lZC4gSWYgdGhlIElDRSBjb25uZWN0aW9uIGhhcyBicm9rZW4gZHVlIHRvIGEgdG90YWwgbmV0d29yayBkcm9wLCB0aGVuIG5vXG5cdCAqIGF1dG9tYXRpYyByZWNvbm5lY3Rpb24gcHJvY2VzcyB3aWxsIGJlIHBvc3NpYmxlLlxuXHQgKlxuXHQgKiBZb3UgY2FuIGN1c3RvbWl6ZSB0aGUgdGltZW91dCBmb3IgdGhlIHJlY29ubmVjdGlvbiBhdHRlbXB0IHdpdGggcHJvcGVydHkge0BsaW5rIE9wZW5WaWR1QWR2YW5jZWRDb25maWd1cmF0aW9uLmljZUNvbm5lY3Rpb25EaXNjb25uZWN0ZWRFeGNlcHRpb25UaW1lb3V0fSxcblx0ICogd2hpY2ggYnkgZGVmYXVsdCBpcyA0MDAwIG1pbGxpc2Vjb25kcy5cblx0ICpcblx0ICoge0BsaW5rIEV4Y2VwdGlvbkV2ZW50fSBvYmplY3RzIHdpdGggdGhpcyB7QGxpbmsgRXhjZXB0aW9uRXZlbnQubmFtZX0gd2lsbCBoYXZlIGFzIHtAbGluayBFeGNlcHRpb25FdmVudC5vcmlnaW59IHByb3BlcnR5IGEge0BsaW5rIFN0cmVhbX0gb2JqZWN0LlxuXHQgKi9cblx0IElDRV9DT05ORUNUSU9OX0RJU0NPTk5FQ1RFRDogJ0lDRV9DT05ORUNUSU9OX0RJU0NPTk5FQ1RFRCcsXG59O1xuXG5jbGFzcyBXZWJSdGNQZWVyIHtcblx0Y29uc3RydWN0b3IoY29uZmlndXJhdGlvbikge1xuXHRcdHRoaXMucmVtb3RlQ2FuZGlkYXRlc1F1ZXVlID0gW107XG5cdFx0dGhpcy5sb2NhbENhbmRpZGF0ZXNRdWV1ZSA9IFtdO1xuXHRcdHRoaXMuaWNlQ2FuZGlkYXRlTGlzdCA9IFtdO1xuXHRcdHRoaXMuY2FuZGlkYXRlZ2F0aGVyaW5nZG9uZSA9IGZhbHNlO1xuXG5cdFx0Ly8gU2FtZSBhcyBXZWJSdGNQZWVyQ29uZmlndXJhdGlvbiBidXQgd2l0aG91dCBvcHRpb25hbCBmaWVsZHMuXG5cdFx0dGhpcy5jb25maWd1cmF0aW9uID0ge1xuXHRcdFx0Li4uY29uZmlndXJhdGlvbixcblx0XHRcdGljZVNlcnZlcnM6ICEhY29uZmlndXJhdGlvbi5pY2VTZXJ2ZXJzICYmIGNvbmZpZ3VyYXRpb24uaWNlU2VydmVycy5sZW5ndGggPiAwID8gY29uZmlndXJhdGlvbi5pY2VTZXJ2ZXJzIDogZnJlZWljZSgpLFxuXHRcdFx0bWVkaWFTdHJlYW06IGNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW0gIT09IHVuZGVmaW5lZCA/IGNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW0gOiBudWxsLFxuXHRcdFx0bW9kZTogISFjb25maWd1cmF0aW9uLm1vZGUgPyBjb25maWd1cmF0aW9uLm1vZGUgOiAnc2VuZHJlY3YnLFxuXHRcdFx0aWQ6ICEhY29uZmlndXJhdGlvbi5pZCA/IGNvbmZpZ3VyYXRpb24uaWQgOiB0aGlzLmdlbmVyYXRlVW5pcXVlSWQoKVxuXHRcdH07XG5cdFx0Ly8gcHJldHRpZXItaWdub3JlXG5cdFx0T21VdGlsLmxvZyhgW1dlYlJ0Y1BlZXJdIGNvbmZpZ3VyYXRpb246XFxuJHtKU09OLnN0cmluZ2lmeSh0aGlzLmNvbmZpZ3VyYXRpb24sIG51bGwsIDIpfWApO1xuXG5cdFx0dGhpcy5wYyA9IG5ldyBSVENQZWVyQ29ubmVjdGlvbih7IGljZVNlcnZlcnM6IHRoaXMuY29uZmlndXJhdGlvbi5pY2VTZXJ2ZXJzIH0pO1xuXG5cdFx0dGhpcy5faWNlQ2FuZGlkYXRlTGlzdGVuZXIgPSAoZXZlbnQpID0+IHtcblx0XHRcdGlmIChldmVudC5jYW5kaWRhdGUgIT09IG51bGwpIHtcblx0XHRcdFx0Ly8gYFJUQ1BlZXJDb25uZWN0aW9uSWNlRXZlbnQuY2FuZGlkYXRlYCBpcyBzdXBwb3NlZCB0byBiZSBhbiBSVENJY2VDYW5kaWRhdGU6XG5cdFx0XHRcdC8vIGh0dHBzOi8vdzNjLmdpdGh1Yi5pby93ZWJydGMtcGMvI2RvbS1ydGNwZWVyY29ubmVjdGlvbmljZWV2ZW50LWNhbmRpZGF0ZVxuXHRcdFx0XHQvL1xuXHRcdFx0XHQvLyBCdXQgaW4gcHJhY3RpY2UsIGl0IGlzIGFjdHVhbGx5IGFuIFJUQ0ljZUNhbmRpZGF0ZUluaXQgdGhhdCBjYW4gYmUgdXNlZCB0b1xuXHRcdFx0XHQvLyBvYnRhaW4gYSBwcm9wZXIgY2FuZGlkYXRlLCB1c2luZyB0aGUgUlRDSWNlQ2FuZGlkYXRlIGNvbnN0cnVjdG9yOlxuXHRcdFx0XHQvLyBodHRwczovL3czYy5naXRodWIuaW8vd2VicnRjLXBjLyNkb20tcnRjaWNlY2FuZGlkYXRlLWNvbnN0cnVjdG9yXG5cdFx0XHRcdGNvbnN0IGNhbmRpZGF0ZUluaXQgPSBldmVudC5jYW5kaWRhdGU7XG5cdFx0XHRcdGNvbnN0IGljZUNhbmRpZGF0ZSA9IG5ldyBSVENJY2VDYW5kaWRhdGUoY2FuZGlkYXRlSW5pdCk7XG5cblx0XHRcdFx0dGhpcy5jb25maWd1cmF0aW9uLm9uSWNlQ2FuZGlkYXRlKGljZUNhbmRpZGF0ZSk7XG5cdFx0XHRcdGlmIChpY2VDYW5kaWRhdGUuY2FuZGlkYXRlICE9PSAnJykge1xuXHRcdFx0XHRcdHRoaXMubG9jYWxDYW5kaWRhdGVzUXVldWUucHVzaChpY2VDYW5kaWRhdGUpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fTtcblx0XHR0aGlzLnBjLmFkZEV2ZW50TGlzdGVuZXIoJ2ljZWNhbmRpZGF0ZScsIHRoaXMuX2ljZUNhbmRpZGF0ZUxpc3RlbmVyKTtcblxuXHRcdHRoaXMuX3NpZ25hbGluZ1N0YXRlQ2hhbmdlTGlzdGVuZXIgPSBhc3luYyAoKSA9PiB7XG5cdFx0XHRpZiAodGhpcy5wYy5zaWduYWxpbmdTdGF0ZSA9PT0gJ3N0YWJsZScpIHtcblx0XHRcdFx0Ly8gU0RQIE9mZmVyL0Fuc3dlciBmaW5pc2hlZC4gQWRkIHN0b3JlZCByZW1vdGUgY2FuZGlkYXRlcy5cblx0XHRcdFx0d2hpbGUgKHRoaXMuaWNlQ2FuZGlkYXRlTGlzdC5sZW5ndGggPiAwKSB7XG5cdFx0XHRcdFx0bGV0IGNhbmRpZGF0ZSA9IHRoaXMuaWNlQ2FuZGlkYXRlTGlzdC5zaGlmdCgpO1xuXHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRhd2FpdCB0aGlzLnBjLmFkZEljZUNhbmRpZGF0ZShjYW5kaWRhdGUpO1xuXHRcdFx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdFx0XHRjb25zb2xlLmVycm9yKCdFcnJvciB3aGVuIGNhbGxpbmcgUlRDUGVlckNvbm5lY3Rpb24jYWRkSWNlQ2FuZGlkYXRlIGZvciBSVENQZWVyQ29ubmVjdGlvbiAnICsgdGhpcy5nZXRJZCgpLCBlcnJvcik7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fTtcblx0XHR0aGlzLnBjLmFkZEV2ZW50TGlzdGVuZXIoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJywgdGhpcy5fc2lnbmFsaW5nU3RhdGVDaGFuZ2VMaXN0ZW5lcik7XG5cdFx0aWYgKHRoaXMuY29uZmlndXJhdGlvbi5vbkNvbm5lY3Rpb25TdGF0ZUNoYW5nZSkge1xuXHRcdFx0dGhpcy5wYy5hZGRFdmVudExpc3RlbmVyKCdjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCB0aGlzLmNvbmZpZ3VyYXRpb24ub25Db25uZWN0aW9uU3RhdGVDaGFuZ2UpO1xuXHRcdH1cblx0fVxuXG5cdGdldElkKCkge1xuXHRcdHJldHVybiB0aGlzLmNvbmZpZ3VyYXRpb24uaWQ7XG5cdH1cblxuXHQvKipcblx0ICogVGhpcyBtZXRob2QgZnJlZXMgdGhlIHJlc291cmNlcyB1c2VkIGJ5IFdlYlJ0Y1BlZXJcblx0ICovXG5cdGRpc3Bvc2UoKSB7XG5cdFx0T21VdGlsLmxvZygnRGlzcG9zaW5nIFdlYlJ0Y1BlZXInKTtcblx0XHRpZiAodGhpcy5wYykge1xuXHRcdFx0aWYgKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSB7XG5cdFx0XHRcdHJldHVybjtcblx0XHRcdH1cblx0XHRcdHRoaXMucGMucmVtb3ZlRXZlbnRMaXN0ZW5lcignaWNlY2FuZGlkYXRlJywgdGhpcy5faWNlQ2FuZGlkYXRlTGlzdGVuZXIpO1xuXHRcdFx0dGhpcy5faWNlQ2FuZGlkYXRlTGlzdGVuZXIgPSB1bmRlZmluZWQ7XG5cdFx0XHR0aGlzLnBjLnJlbW92ZUV2ZW50TGlzdGVuZXIoJ3NpZ25hbGluZ3N0YXRlY2hhbmdlJywgdGhpcy5fc2lnbmFsaW5nU3RhdGVDaGFuZ2VMaXN0ZW5lcik7XG5cdFx0XHR0aGlzLl9zaWduYWxpbmdTdGF0ZUNoYW5nZUxpc3RlbmVyID0gdW5kZWZpbmVkO1xuXHRcdFx0aWYgKHRoaXMuX2ljZUNvbm5lY3Rpb25TdGF0ZUNoYW5nZUxpc3RlbmVyKSB7XG5cdFx0XHRcdHRoaXMucGMucmVtb3ZlRXZlbnRMaXN0ZW5lcignaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJywgdGhpcy5faWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlTGlzdGVuZXIpO1xuXHRcdFx0XHR0aGlzLl9pY2VDb25uZWN0aW9uU3RhdGVDaGFuZ2VMaXN0ZW5lciA9IHVuZGVmaW5lZDtcblx0XHRcdH1cblx0XHRcdGlmICh0aGlzLmNvbmZpZ3VyYXRpb24ub25Db25uZWN0aW9uU3RhdGVDaGFuZ2UpIHtcblx0XHRcdFx0dGhpcy5wYy5yZW1vdmVFdmVudExpc3RlbmVyKCdjb25uZWN0aW9uc3RhdGVjaGFuZ2UnLCB0aGlzLmNvbmZpZ3VyYXRpb24ub25Db25uZWN0aW9uU3RhdGVDaGFuZ2UpO1xuXHRcdFx0fVxuXHRcdFx0dGhpcy5jb25maWd1cmF0aW9uID0ge307XG5cdFx0XHR0aGlzLnBjLmNsb3NlKCk7XG5cdFx0XHR0aGlzLnJlbW90ZUNhbmRpZGF0ZXNRdWV1ZSA9IFtdO1xuXHRcdFx0dGhpcy5sb2NhbENhbmRpZGF0ZXNRdWV1ZSA9IFtdO1xuXHRcdH1cblx0fVxuXG5cdC8qKlxuXHQgKiBDcmVhdGVzIGFuIFNEUCBvZmZlciBmcm9tIHRoZSBsb2NhbCBSVENQZWVyQ29ubmVjdGlvbiB0byBzZW5kIHRvIHRoZSBvdGhlciBwZWVyLlxuXHQgKiBPbmx5IGlmIHRoZSBuZWdvdGlhdGlvbiB3YXMgaW5pdGlhdGVkIGJ5IHRoaXMgcGVlci5cblx0ICovXG5cdGFzeW5jIGNyZWF0ZU9mZmVyKCkge1xuXHRcdC8vIFRPRE86IERlbGV0ZSB0aGlzIGNvbmRpdGlvbmFsIHdoZW4gYWxsIHN1cHBvcnRlZCBicm93c2VycyBhcmVcblx0XHQvLyBtb2Rlcm4gZW5vdWdoIHRvIGltcGxlbWVudCB0aGUgVHJhbnNjZWl2ZXIgbWV0aG9kcy5cblx0XHRpZiAoISgnYWRkVHJhbnNjZWl2ZXInIGluIHRoaXMucGMpKSB7XG5cdFx0XHRPbVV0aWwuZXJyb3IoXG5cdFx0XHRcdCdbY3JlYXRlT2ZmZXJdIE1ldGhvZCBSVENQZWVyQ29ubmVjdGlvbi5hZGRUcmFuc2NlaXZlcigpIGlzIE5PVCBhdmFpbGFibGU7IHVzaW5nIExFR0FDWSBvZmZlclRvUmVjZWl2ZXtBdWRpbyxWaWRlb30nXG5cdFx0XHQpO1xuXHRcdFx0cmV0dXJuIHRoaXMuY3JlYXRlT2ZmZXJMZWdhY3koKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0T21VdGlsLmxvZygnW2NyZWF0ZU9mZmVyXSBNZXRob2QgUlRDUGVlckNvbm5lY3Rpb24uYWRkVHJhbnNjZWl2ZXIoKSBpcyBhdmFpbGFibGU7IHVzaW5nIGl0Jyk7XG5cdFx0fVxuXG5cdFx0Ly8gU3BlYyBkb2M6IGh0dHBzOi8vdzNjLmdpdGh1Yi5pby93ZWJydGMtcGMvI2RvbS1ydGNwZWVyY29ubmVjdGlvbi1hZGR0cmFuc2NlaXZlclxuXG5cdFx0aWYgKHRoaXMuY29uZmlndXJhdGlvbi5tb2RlICE9PSAncmVjdm9ubHknKSB7XG5cdFx0XHQvLyBUbyBzZW5kIG1lZGlhLCBhc3N1bWUgdGhhdCBhbGwgZGVzaXJlZCBtZWRpYSB0cmFja3MgaGF2ZSBiZWVuXG5cdFx0XHQvLyBhbHJlYWR5IGFkZGVkIGJ5IGhpZ2hlciBsZXZlbCBjb2RlIHRvIG91ciBNZWRpYVN0cmVhbS5cblxuXHRcdFx0aWYgKCF0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW0pIHtcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKFxuXHRcdFx0XHRcdGBbV2ViUnRjUGVlci5jcmVhdGVPZmZlcl0gRGlyZWN0aW9uIGlzICcke3RoaXMuY29uZmlndXJhdGlvbi5tb2RlfScsIGJ1dCBubyBzdHJlYW0gd2FzIGNvbmZpZ3VyZWQgdG8gYmUgc2VudGBcblx0XHRcdFx0KTtcblx0XHRcdH1cblxuXHRcdFx0Zm9yIChjb25zdCB0cmFjayBvZiB0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW0uZ2V0VHJhY2tzKCkpIHtcblx0XHRcdFx0Y29uc3QgdGNJbml0ID0ge1xuXHRcdFx0XHRcdGRpcmVjdGlvbjogdGhpcy5jb25maWd1cmF0aW9uLm1vZGUsXG5cdFx0XHRcdFx0c3RyZWFtczogW3RoaXMuY29uZmlndXJhdGlvbi5tZWRpYVN0cmVhbV1cblx0XHRcdFx0fTtcblxuXHRcdFx0XHRpZiAodHJhY2sua2luZCA9PT0gJ3ZpZGVvJyAmJiB0aGlzLmNvbmZpZ3VyYXRpb24uc2ltdWxjYXN0KSB7XG5cdFx0XHRcdFx0Ly8gQ2hlY2sgaWYgdGhlIHJlcXVlc3RlZCBzaXplIGlzIGVub3VnaCB0byBhc2sgZm9yIDMgbGF5ZXJzLlxuXHRcdFx0XHRcdGNvbnN0IHRyYWNrU2V0dGluZ3MgPSB0cmFjay5nZXRTZXR0aW5ncygpO1xuXHRcdFx0XHRcdGNvbnN0IHRyYWNrQ29uc3RzID0gdHJhY2suZ2V0Q29uc3RyYWludHMoKTtcblxuXHRcdFx0XHRcdGNvbnN0IHRyYWNrV2lkdGggPSB0eXBlb2YodHJhY2tTZXR0aW5ncy53aWR0aCkgPT09ICdvYmplY3QnID8gdHJhY2tDb25zdHMud2lkdGguaWRlYWwgOiB0cmFja0NvbnN0cy53aWR0aCB8fCAwO1xuXHRcdFx0XHRcdGNvbnN0IHRyYWNrSGVpZ2h0ID0gdHlwZW9mKHRyYWNrU2V0dGluZ3MuaGVpZ2h0KSA9PT0gJ29iamVjdCcgPyB0cmFja0NvbnN0cy5oZWlnaHQuaWRlYWwgOiB0cmFja0NvbnN0cy5oZWlnaHQgfHwgMDtcblx0XHRcdFx0XHRPbVV0aWwuaW5mbyhgW2NyZWF0ZU9mZmVyXSBWaWRlbyB0cmFjayBkaW1lbnNpb25zOiAke3RyYWNrV2lkdGh9eCR7dHJhY2tIZWlnaHR9YCk7XG5cblx0XHRcdFx0XHRjb25zdCB0cmFja1BpeGVscyA9IHRyYWNrV2lkdGggKiB0cmFja0hlaWdodDtcblx0XHRcdFx0XHRsZXQgbWF4TGF5ZXJzID0gMDtcblx0XHRcdFx0XHRpZiAodHJhY2tQaXhlbHMgPj0gOTYwICogNTQwKSB7XG5cdFx0XHRcdFx0XHRtYXhMYXllcnMgPSAzO1xuXHRcdFx0XHRcdH0gZWxzZSBpZiAodHJhY2tQaXhlbHMgPj0gNDgwICogMjcwKSB7XG5cdFx0XHRcdFx0XHRtYXhMYXllcnMgPSAyO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRtYXhMYXllcnMgPSAxO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdHRjSW5pdC5zZW5kRW5jb2RpbmdzID0gW107XG5cdFx0XHRcdFx0Zm9yIChsZXQgbCA9IDA7IGwgPCBtYXhMYXllcnM7IGwrKykge1xuXHRcdFx0XHRcdFx0Y29uc3QgbGF5ZXJEaXYgPSAyICoqIChtYXhMYXllcnMgLSBsIC0gMSk7XG5cblx0XHRcdFx0XHRcdGNvbnN0IGVuY29kaW5nID0ge1xuXHRcdFx0XHRcdFx0XHRyaWQ6ICdyZGl2JyArIGxheWVyRGl2LnRvU3RyaW5nKCksXG5cblx0XHRcdFx0XHRcdFx0Ly8gQHRzLWlnbm9yZSAtLSBQcm9wZXJ0eSBtaXNzaW5nIGZyb20gRE9NIHR5cGVzLlxuXHRcdFx0XHRcdFx0XHRzY2FsYWJpbGl0eU1vZGU6ICdMMVQxJ1xuXHRcdFx0XHRcdFx0fTtcblxuXHRcdFx0XHRcdFx0aWYgKFsnZGV0YWlsJywgJ3RleHQnXS5pbmNsdWRlcyh0cmFjay5jb250ZW50SGludCkpIHtcblx0XHRcdFx0XHRcdFx0Ly8gUHJpb3JpdGl6ZSBiZXN0IHJlc29sdXRpb24sIGZvciBtYXhpbXVtIHBpY3R1cmUgZGV0YWlsLlxuXHRcdFx0XHRcdFx0XHRlbmNvZGluZy5zY2FsZVJlc29sdXRpb25Eb3duQnkgPSAxLjA7XG5cblx0XHRcdFx0XHRcdFx0Ly8gQHRzLWlnbm9yZSAtLSBQcm9wZXJ0eSBtaXNzaW5nIGZyb20gRE9NIHR5cGVzLlxuXHRcdFx0XHRcdFx0XHRlbmNvZGluZy5tYXhGcmFtZXJhdGUgPSBNYXRoLmZsb29yKDMwIC8gbGF5ZXJEaXYpO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0ZW5jb2Rpbmcuc2NhbGVSZXNvbHV0aW9uRG93bkJ5ID0gbGF5ZXJEaXY7XG5cdFx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRcdHRjSW5pdC5zZW5kRW5jb2RpbmdzLnB1c2goZW5jb2RpbmcpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGNvbnN0IHRjID0gdGhpcy5wYy5hZGRUcmFuc2NlaXZlcih0cmFjaywgdGNJbml0KTtcblxuXHRcdFx0XHRpZiAodHJhY2sua2luZCA9PT0gJ3ZpZGVvJykge1xuXHRcdFx0XHRcdGxldCBzZW5kUGFyYW1zID0gdGMuc2VuZGVyLmdldFBhcmFtZXRlcnMoKTtcblx0XHRcdFx0XHRsZXQgbmVlZFNldFBhcmFtcyA9IGZhbHNlO1xuXG5cdFx0XHRcdFx0aWYgKHNlbmRQYXJhbXMuZGVncmFkYXRpb25QcmVmZXJlbmNlICYmICFzZW5kUGFyYW1zLmRlZ3JhZGF0aW9uUHJlZmVyZW5jZS5sZW5ndGgpIHtcblx0XHRcdFx0XHRcdC8vIGRlZ3JhZGF0aW9uUHJlZmVyZW5jZSBmb3IgdmlkZW86IFwiYmFsYW5jZWRcIiwgXCJtYWludGFpbi1mcmFtZXJhdGVcIiwgXCJtYWludGFpbi1yZXNvbHV0aW9uXCIuXG5cdFx0XHRcdFx0XHQvLyBodHRwczovL3d3dy53My5vcmcvVFIvMjAxOC9DUi13ZWJydGMtMjAxODA5MjcvI2RvbS1ydGNkZWdyYWRhdGlvbnByZWZlcmVuY2Vcblx0XHRcdFx0XHRcdGlmIChbJ2RldGFpbCcsICd0ZXh0J10uaW5jbHVkZXModHJhY2suY29udGVudEhpbnQpKSB7XG5cdFx0XHRcdFx0XHRcdHNlbmRQYXJhbXMuZGVncmFkYXRpb25QcmVmZXJlbmNlID0gJ21haW50YWluLXJlc29sdXRpb24nO1xuXHRcdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdFx0c2VuZFBhcmFtcy5kZWdyYWRhdGlvblByZWZlcmVuY2UgPSAnYmFsYW5jZWQnO1xuXHRcdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0XHRPbVV0aWwuaW5mbyhgW2NyZWF0ZU9mZmVyXSBWaWRlbyBzZW5kZXIgRGVncmFkYXRpb24gUHJlZmVyZW5jZSBzZXQ6ICR7c2VuZFBhcmFtcy5kZWdyYWRhdGlvblByZWZlcmVuY2V9YCk7XG5cblx0XHRcdFx0XHRcdC8vIEZpcmVmb3ggaW1wbGVtZW50cyBkZWdyYWRhdGlvblByZWZlcmVuY2Ugb24gZWFjaCBpbmRpdmlkdWFsIGVuY29kaW5nIVxuXHRcdFx0XHRcdFx0Ly8gKHNldCBpdCBvbiBldmVyeSBlbGVtZW50IG9mIHRoZSBzZW5kUGFyYW1zLmVuY29kaW5ncyBhcnJheSlcblxuXHRcdFx0XHRcdFx0bmVlZFNldFBhcmFtcyA9IHRydWU7XG5cdFx0XHRcdFx0fVxuXG5cdFx0XHRcdFx0Ly8gQ2hlY2sgdGhhdCB0aGUgc2ltdWxjYXN0IGVuY29kaW5ncyB3ZXJlIGFwcGxpZWQuXG5cdFx0XHRcdFx0Ly8gRmlyZWZveCBkb2Vzbid0IGltcGxlbWVudCBgUlRDUnRwVHJhbnNjZWl2ZXJJbml0LnNlbmRFbmNvZGluZ3NgXG5cdFx0XHRcdFx0Ly8gc28gdGhlIG9ubHkgd2F5IHRvIGVuYWJsZSBzaW11bGNhc3QgaXMgd2l0aCBgUlRDUnRwU2VuZGVyLnNldFBhcmFtZXRlcnMoKWAuXG5cdFx0XHRcdFx0Ly9cblx0XHRcdFx0XHQvLyBUaGlzIG5leHQgYmxvY2sgY2FuIGJlIGRlbGV0ZWQgd2hlbiBGaXJlZm94IGZpeGVzIGJ1ZyAjMTM5NjkxODpcblx0XHRcdFx0XHQvLyBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMzk2OTE4XG5cdFx0XHRcdFx0Ly9cblx0XHRcdFx0XHQvLyBOT1RFOiBUaGlzIGlzIGRvbmUgaW4gYSB3YXkgdGhhdCBpcyBjb21wYXRpYmxlIHdpdGggYWxsIGJyb3dzZXJzLCB0byBzYXZlIG9uXG5cdFx0XHRcdFx0Ly8gYnJvd3Nlci1jb25kaXRpb25hbCBjb2RlLiBUaGUgaWRlYSBjb21lcyBmcm9tIFdlYlJUQyBBZGFwdGVyLmpzOlxuXHRcdFx0XHRcdC8vICogaHR0cHM6Ly9naXRodWIuY29tL3dlYnJ0Y0hhY2tzL2FkYXB0ZXIvaXNzdWVzLzk5OFxuXHRcdFx0XHRcdC8vICogaHR0cHM6Ly9naXRodWIuY29tL3dlYnJ0Y0hhY2tzL2FkYXB0ZXIvYmxvYi92Ny43LjAvc3JjL2pzL2ZpcmVmb3gvZmlyZWZveF9zaGltLmpzI0wyMzEtTDI1NVxuXHRcdFx0XHRcdGlmICh0aGlzLmNvbmZpZ3VyYXRpb24uc2ltdWxjYXN0KSB7XG5cdFx0XHRcdFx0XHRpZiAoc2VuZFBhcmFtcy5lbmNvZGluZ3MubGVuZ3RoICE9PSB0Y0luaXQuc2VuZEVuY29kaW5ncy5sZW5ndGgpIHtcblx0XHRcdFx0XHRcdFx0c2VuZFBhcmFtcy5lbmNvZGluZ3MgPSB0Y0luaXQuc2VuZEVuY29kaW5ncztcblxuXHRcdFx0XHRcdFx0XHRuZWVkU2V0UGFyYW1zID0gdHJ1ZTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cblx0XHRcdFx0XHRpZiAobmVlZFNldFBhcmFtcykge1xuXHRcdFx0XHRcdFx0T21VdGlsLmxvZyhgW2NyZWF0ZU9mZmVyXSBTZXR0aW5nIG5ldyBSVENSdHBTZW5kUGFyYW1ldGVycyB0byB2aWRlbyBzZW5kZXJgKTtcblx0XHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRcdGF3YWl0IHRjLnNlbmRlci5zZXRQYXJhbWV0ZXJzKHNlbmRQYXJhbXMpO1xuXHRcdFx0XHRcdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdFx0XHRcdFx0bGV0IG1lc3NhZ2UgPSBgW1dlYlJ0Y1BlZXIuY3JlYXRlT2ZmZXJdIENhbm5vdCBzZXQgUlRDUnRwU2VuZFBhcmFtZXRlcnMgdG8gdmlkZW8gc2VuZGVyYDtcblx0XHRcdFx0XHRcdFx0aWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcblx0XHRcdFx0XHRcdFx0XHRtZXNzYWdlICs9IGA6ICR7ZXJyb3IubWVzc2FnZX1gO1xuXHRcdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0XHRcdHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0Ly8gVG8ganVzdCByZWNlaXZlIG1lZGlhLCBjcmVhdGUgbmV3IHJlY3Zvbmx5IHRyYW5zY2VpdmVycy5cblx0XHRcdGZvciAoY29uc3Qga2luZCBvZiBbJ2F1ZGlvJywgJ3ZpZGVvJ10pIHtcblx0XHRcdFx0Ly8gQ2hlY2sgaWYgdGhlIG1lZGlhIGtpbmQgc2hvdWxkIGJlIHVzZWQuXG5cdFx0XHRcdGlmICghdGhpcy5jb25maWd1cmF0aW9uLm1lZGlhQ29uc3RyYWludHNba2luZF0pIHtcblx0XHRcdFx0XHRjb250aW51ZTtcblx0XHRcdFx0fVxuXG5cdFx0XHRcdHRoaXMuY29uZmlndXJhdGlvbi5tZWRpYVN0cmVhbSA9IG5ldyBNZWRpYVN0cmVhbSgpO1xuXHRcdFx0XHR0aGlzLnBjLmFkZFRyYW5zY2VpdmVyKGtpbmQsIHtcblx0XHRcdFx0XHRkaXJlY3Rpb246IHRoaXMuY29uZmlndXJhdGlvbi5tb2RlLFxuXHRcdFx0XHRcdHN0cmVhbXM6IFt0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW1dXG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdGxldCBzZHBPZmZlcjtcblx0XHR0cnkge1xuXHRcdFx0c2RwT2ZmZXIgPSBhd2FpdCB0aGlzLnBjLmNyZWF0ZU9mZmVyKCk7XG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcblx0XHRcdGxldCBtZXNzYWdlID0gYFtXZWJSdGNQZWVyLmNyZWF0ZU9mZmVyXSBCcm93c2VyIGZhaWxlZCBjcmVhdGluZyBhbiBTRFAgT2ZmZXJgO1xuXHRcdFx0aWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcblx0XHRcdFx0bWVzc2FnZSArPSBgOiAke2Vycm9yLm1lc3NhZ2V9YDtcblx0XHRcdH1cblx0XHRcdHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcblx0XHR9XG5cblx0XHRyZXR1cm4gc2RwT2ZmZXI7XG5cdH1cblxuXHQvKipcblx0ICogQ3JlYXRlcyBhbiBTRFAgYW5zd2VyIGZyb20gdGhlIGxvY2FsIFJUQ1BlZXJDb25uZWN0aW9uIHRvIHNlbmQgdG8gdGhlIG90aGVyIHBlZXJcblx0ICogT25seSBpZiB0aGUgbmVnb3RpYXRpb24gd2FzIGluaXRpYXRlZCBieSB0aGUgb3RoZXIgcGVlclxuXHQgKi9cblx0Y3JlYXRlQW5zd2VyKCkge1xuXHRcdHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG5cdFx0XHQvLyBUT0RPOiBEZWxldGUgdGhpcyBjb25kaXRpb25hbCB3aGVuIGFsbCBzdXBwb3J0ZWQgYnJvd3NlcnMgYXJlXG5cdFx0XHQvLyBtb2Rlcm4gZW5vdWdoIHRvIGltcGxlbWVudCB0aGUgVHJhbnNjZWl2ZXIgbWV0aG9kcy5cblx0XHRcdGlmICgnZ2V0VHJhbnNjZWl2ZXJzJyBpbiB0aGlzLnBjKSB7XG5cdFx0XHRcdE9tVXRpbC5sb2coJ1tjcmVhdGVBbnN3ZXJdIE1ldGhvZCBSVENQZWVyQ29ubmVjdGlvbi5nZXRUcmFuc2NlaXZlcnMoKSBpcyBhdmFpbGFibGU7IHVzaW5nIGl0Jyk7XG5cblx0XHRcdFx0Ly8gRW5zdXJlIHRoYXQgdGhlIFBlZXJDb25uZWN0aW9uIGFscmVhZHkgY29udGFpbnMgb25lIFRyYW5zY2VpdmVyXG5cdFx0XHRcdC8vIGZvciBlYWNoIGtpbmQgb2YgbWVkaWEuXG5cdFx0XHRcdC8vIFRoZSBUcmFuc2NlaXZlcnMgc2hvdWxkIGhhdmUgYmVlbiBhbHJlYWR5IGNyZWF0ZWQgaW50ZXJuYWxseSBieVxuXHRcdFx0XHQvLyB0aGUgUEMgaXRzZWxmLCB3aGVuIGBwYy5zZXRSZW1vdGVEZXNjcmlwdGlvbihzZHBPZmZlcilgIHdhcyBjYWxsZWQuXG5cblx0XHRcdFx0Zm9yIChjb25zdCBraW5kIG9mIFsnYXVkaW8nLCAndmlkZW8nXSkge1xuXHRcdFx0XHRcdC8vIENoZWNrIGlmIHRoZSBtZWRpYSBraW5kIHNob3VsZCBiZSB1c2VkLlxuXHRcdFx0XHRcdGlmICghdGhpcy5jb25maWd1cmF0aW9uLm1lZGlhQ29uc3RyYWludHNba2luZF0pIHtcblx0XHRcdFx0XHRcdGNvbnRpbnVlO1xuXHRcdFx0XHRcdH1cblxuXHRcdFx0XHRcdGxldCB0YyA9IHRoaXMucGMuZ2V0VHJhbnNjZWl2ZXJzKCkuZmluZCgodGMpID0+IHRjLnJlY2VpdmVyLnRyYWNrLmtpbmQgPT09IGtpbmQpO1xuXG5cdFx0XHRcdFx0aWYgKHRjKSB7XG5cdFx0XHRcdFx0XHQvLyBFbmZvcmNlIG91ciBkZXNpcmVkIGRpcmVjdGlvbi5cblx0XHRcdFx0XHRcdHRjLmRpcmVjdGlvbiA9IHRoaXMuY29uZmlndXJhdGlvbi5tb2RlO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRyZXR1cm4gcmVqZWN0KG5ldyBFcnJvcihgJHtraW5kfSByZXF1ZXN0ZWQsIGJ1dCBubyB0cmFuc2NlaXZlciB3YXMgY3JlYXRlZCBmcm9tIHJlbW90ZSBkZXNjcmlwdGlvbmApKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblxuXHRcdFx0XHR0aGlzLnBjXG5cdFx0XHRcdFx0LmNyZWF0ZUFuc3dlcigpXG5cdFx0XHRcdFx0LnRoZW4oKHNkcEFuc3dlcikgPT4gcmVzb2x2ZShzZHBBbnN3ZXIpKVxuXHRcdFx0XHRcdC5jYXRjaCgoZXJyb3IpID0+IHJlamVjdChlcnJvcikpO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Ly8gVE9ETzogRGVsZXRlIGVsc2UgYnJhbmNoIHdoZW4gYWxsIHN1cHBvcnRlZCBicm93c2VycyBhcmVcblx0XHRcdFx0Ly8gbW9kZXJuIGVub3VnaCB0byBpbXBsZW1lbnQgdGhlIFRyYW5zY2VpdmVyIG1ldGhvZHNcblxuXHRcdFx0XHRsZXQgb2ZmZXJBdWRpbyxcblx0XHRcdFx0XHRvZmZlclZpZGVvID0gdHJ1ZTtcblx0XHRcdFx0aWYgKCEhdGhpcy5jb25maWd1cmF0aW9uLm1lZGlhQ29uc3RyYWludHMpIHtcblx0XHRcdFx0XHRvZmZlckF1ZGlvID1cblx0XHRcdFx0XHRcdHR5cGVvZiB0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFDb25zdHJhaW50cy5hdWRpbyA9PT0gJ2Jvb2xlYW4nID8gdGhpcy5jb25maWd1cmF0aW9uLm1lZGlhQ29uc3RyYWludHMuYXVkaW8gOiB0cnVlO1xuXHRcdFx0XHRcdG9mZmVyVmlkZW8gPVxuXHRcdFx0XHRcdFx0dHlwZW9mIHRoaXMuY29uZmlndXJhdGlvbi5tZWRpYUNvbnN0cmFpbnRzLnZpZGVvID09PSAnYm9vbGVhbicgPyB0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFDb25zdHJhaW50cy52aWRlbyA6IHRydWU7XG5cdFx0XHRcdFx0Y29uc3QgY29uc3RyYWludHMgPSB7XG5cdFx0XHRcdFx0XHRvZmZlclRvUmVjZWl2ZUF1ZGlvOiBvZmZlckF1ZGlvLFxuXHRcdFx0XHRcdFx0b2ZmZXJUb1JlY2VpdmVWaWRlbzogb2ZmZXJWaWRlb1xuXHRcdFx0XHRcdH07XG5cdFx0XHRcdFx0KHRoaXMucGMpLmNyZWF0ZUFuc3dlcihjb25zdHJhaW50cylcblx0XHRcdFx0XHRcdC50aGVuKChzZHBBbnN3ZXIpID0+IHJlc29sdmUoc2RwQW5zd2VyKSlcblx0XHRcdFx0XHRcdC5jYXRjaCgoZXJyb3IpID0+IHJlamVjdChlcnJvcikpO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cblx0XHRcdC8vIGVsc2UsIHRoZXJlIGlzIG5vdGhpbmcgdG8gZG87IHRoZSBsZWdhY3kgY3JlYXRlQW5zd2VyKCkgb3B0aW9ucyBkb1xuXHRcdFx0Ly8gbm90IG9mZmVyIGFueSBjb250cm9sIG92ZXIgd2hpY2ggdHJhY2tzIGFyZSBpbmNsdWRlZCBpbiB0aGUgYW5zd2VyLlxuXHRcdH0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoaXMgcGVlciBpbml0aWF0ZWQgbmVnb3RpYXRpb24uIFN0ZXAgMS80IG9mIFNEUCBvZmZlci1hbnN3ZXIgcHJvdG9jb2xcblx0ICovXG5cdHByb2Nlc3NMb2NhbE9mZmVyKG9mZmVyKSB7XG5cdFx0cmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdHRoaXMucGNcblx0XHRcdFx0LnNldExvY2FsRGVzY3JpcHRpb24ob2ZmZXIpXG5cdFx0XHRcdC50aGVuKCgpID0+IHtcblx0XHRcdFx0XHRjb25zdCBsb2NhbERlc2NyaXB0aW9uID0gdGhpcy5wYy5sb2NhbERlc2NyaXB0aW9uO1xuXHRcdFx0XHRcdGlmICghIWxvY2FsRGVzY3JpcHRpb24pIHtcblx0XHRcdFx0XHRcdE9tVXRpbC5sb2coJ0xvY2FsIGRlc2NyaXB0aW9uIHNldCcsIGxvY2FsRGVzY3JpcHRpb24uc2RwKTtcblx0XHRcdFx0XHRcdHJldHVybiByZXNvbHZlKCk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHJldHVybiByZWplY3QoJ0xvY2FsIGRlc2NyaXB0aW9uIGlzIG5vdCBkZWZpbmVkJyk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9KVxuXHRcdFx0XHQuY2F0Y2goKGVycm9yKSA9PiByZWplY3QoZXJyb3IpKTtcblx0XHR9KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBPdGhlciBwZWVyIGluaXRpYXRlZCBuZWdvdGlhdGlvbi4gU3RlcCAyLzQgb2YgU0RQIG9mZmVyLWFuc3dlciBwcm90b2NvbFxuXHQgKi9cblx0cHJvY2Vzc1JlbW90ZU9mZmVyKHNkcE9mZmVyKSB7XG5cdFx0cmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdGNvbnN0IG9mZmVyID0ge1xuXHRcdFx0XHR0eXBlOiAnb2ZmZXInLFxuXHRcdFx0XHRzZHA6IHNkcE9mZmVyXG5cdFx0XHR9O1xuXHRcdFx0T21VdGlsLmxvZygnU0RQIG9mZmVyIHJlY2VpdmVkLCBzZXR0aW5nIHJlbW90ZSBkZXNjcmlwdGlvbicsIG9mZmVyKTtcblxuXHRcdFx0aWYgKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSB7XG5cdFx0XHRcdHJldHVybiByZWplY3QoJ1JUQ1BlZXJDb25uZWN0aW9uIGlzIGNsb3NlZCB3aGVuIHRyeWluZyB0byBzZXQgcmVtb3RlIGRlc2NyaXB0aW9uJyk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLnNldFJlbW90ZURlc2NyaXB0aW9uKG9mZmVyKVxuXHRcdFx0XHQudGhlbigoKSA9PiByZXNvbHZlKCkpXG5cdFx0XHRcdC5jYXRjaCgoZXJyb3IpID0+IHJlamVjdChlcnJvcikpO1xuXHRcdH0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIE90aGVyIHBlZXIgaW5pdGlhdGVkIG5lZ290aWF0aW9uLiBTdGVwIDMvNCBvZiBTRFAgb2ZmZXItYW5zd2VyIHByb3RvY29sXG5cdCAqL1xuXHRwcm9jZXNzTG9jYWxBbnN3ZXIoYW5zd2VyKSB7XG5cdFx0cmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdE9tVXRpbC5sb2coJ1NEUCBhbnN3ZXIgY3JlYXRlZCwgc2V0dGluZyBsb2NhbCBkZXNjcmlwdGlvbicpO1xuXHRcdFx0aWYgKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSB7XG5cdFx0XHRcdHJldHVybiByZWplY3QoJ1JUQ1BlZXJDb25uZWN0aW9uIGlzIGNsb3NlZCB3aGVuIHRyeWluZyB0byBzZXQgbG9jYWwgZGVzY3JpcHRpb24nKTtcblx0XHRcdH1cblx0XHRcdHRoaXMucGNcblx0XHRcdFx0LnNldExvY2FsRGVzY3JpcHRpb24oYW5zd2VyKVxuXHRcdFx0XHQudGhlbigoKSA9PiByZXNvbHZlKCkpXG5cdFx0XHRcdC5jYXRjaCgoZXJyb3IpID0+IHJlamVjdChlcnJvcikpO1xuXHRcdH0pO1xuXHR9XG5cblx0LyoqXG5cdCAqIFRoaXMgcGVlciBpbml0aWF0ZWQgbmVnb3RpYXRpb24uIFN0ZXAgNC80IG9mIFNEUCBvZmZlci1hbnN3ZXIgcHJvdG9jb2xcblx0ICovXG5cdHByb2Nlc3NSZW1vdGVBbnN3ZXIoc2RwQW5zd2VyKSB7XG5cdFx0cmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcblx0XHRcdGNvbnN0IGFuc3dlciA9IHtcblx0XHRcdFx0dHlwZTogJ2Fuc3dlcicsXG5cdFx0XHRcdHNkcDogc2RwQW5zd2VyXG5cdFx0XHR9O1xuXHRcdFx0T21VdGlsLmxvZygnU0RQIGFuc3dlciByZWNlaXZlZCwgc2V0dGluZyByZW1vdGUgZGVzY3JpcHRpb24nKTtcblxuXHRcdFx0aWYgKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUgPT09ICdjbG9zZWQnKSB7XG5cdFx0XHRcdHJldHVybiByZWplY3QoJ1JUQ1BlZXJDb25uZWN0aW9uIGlzIGNsb3NlZCB3aGVuIHRyeWluZyB0byBzZXQgcmVtb3RlIGRlc2NyaXB0aW9uJyk7XG5cdFx0XHR9XG5cdFx0XHR0aGlzLnNldFJlbW90ZURlc2NyaXB0aW9uKGFuc3dlcilcblx0XHRcdFx0LnRoZW4oKCkgPT4ge1xuXHRcdFx0XHRcdHJlc29sdmUoKTtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmNhdGNoKChlcnJvcikgPT4gcmVqZWN0KGVycm9yKSk7XG5cdFx0fSk7XG5cdH1cblxuXHQvKipcblx0ICogQGhpZGRlblxuXHQgKi9cblx0YXN5bmMgc2V0UmVtb3RlRGVzY3JpcHRpb24oc2RwKSB7XG5cdFx0cmV0dXJuIHRoaXMucGMuc2V0UmVtb3RlRGVzY3JpcHRpb24oc2RwKTtcblx0fVxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBmdW5jdGlvbiBpbnZva2VkIHdoZW4gYW4gSUNFIGNhbmRpZGF0ZSBpcyByZWNlaXZlZFxuXHQgKi9cblx0YWRkSWNlQ2FuZGlkYXRlKGljZUNhbmRpZGF0ZSkge1xuXHRcdHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG5cdFx0XHRPbVV0aWwubG9nKCdSZW1vdGUgSUNFIGNhbmRpZGF0ZSByZWNlaXZlZCcsIGljZUNhbmRpZGF0ZSk7XG5cdFx0XHR0aGlzLnJlbW90ZUNhbmRpZGF0ZXNRdWV1ZS5wdXNoKGljZUNhbmRpZGF0ZSk7XG5cdFx0XHRzd2l0Y2ggKHRoaXMucGMuc2lnbmFsaW5nU3RhdGUpIHtcblx0XHRcdFx0Y2FzZSAnY2xvc2VkJzpcblx0XHRcdFx0XHRyZWplY3QobmV3IEVycm9yKCdQZWVyQ29ubmVjdGlvbiBvYmplY3QgaXMgY2xvc2VkJykpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRjYXNlICdzdGFibGUnOlxuXHRcdFx0XHRcdGlmICghIXRoaXMucGMucmVtb3RlRGVzY3JpcHRpb24pIHtcblx0XHRcdFx0XHRcdHRoaXMucGNcblx0XHRcdFx0XHRcdFx0LmFkZEljZUNhbmRpZGF0ZShpY2VDYW5kaWRhdGUpXG5cdFx0XHRcdFx0XHRcdC50aGVuKCgpID0+IHJlc29sdmUoKSlcblx0XHRcdFx0XHRcdFx0LmNhdGNoKChlcnJvcikgPT4gcmVqZWN0KGVycm9yKSk7XG5cdFx0XHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0XHRcdHRoaXMuaWNlQ2FuZGlkYXRlTGlzdC5wdXNoKGljZUNhbmRpZGF0ZSk7XG5cdFx0XHRcdFx0XHRyZXNvbHZlKCk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdHRoaXMuaWNlQ2FuZGlkYXRlTGlzdC5wdXNoKGljZUNhbmRpZGF0ZSk7XG5cdFx0XHRcdFx0cmVzb2x2ZSgpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHR9XG5cblx0YWRkSWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlTGlzdGVuZXIob3RoZXJJZCkge1xuXHRcdGlmICghdGhpcy5faWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlTGlzdGVuZXIpIHtcblx0XHRcdHRoaXMuX2ljZUNvbm5lY3Rpb25TdGF0ZUNoYW5nZUxpc3RlbmVyID0gKCkgPT4ge1xuXHRcdFx0XHRjb25zdCBpY2VDb25uZWN0aW9uU3RhdGUgPSB0aGlzLnBjLmljZUNvbm5lY3Rpb25TdGF0ZTtcblx0XHRcdFx0c3dpdGNoIChpY2VDb25uZWN0aW9uU3RhdGUpIHtcblx0XHRcdFx0XHRjYXNlICdkaXNjb25uZWN0ZWQnOlxuXHRcdFx0XHRcdFx0Ly8gUG9zc2libGUgbmV0d29yayBkaXNjb25uZWN0aW9uXG5cdFx0XHRcdFx0XHRjb25zdCBtc2cxID1cblx0XHRcdFx0XHRcdFx0J0ljZUNvbm5lY3Rpb25TdGF0ZSBvZiBSVENQZWVyQ29ubmVjdGlvbiAnICtcblx0XHRcdFx0XHRcdFx0dGhpcy5jb25maWd1cmF0aW9uLmlkICtcblx0XHRcdFx0XHRcdFx0JyAoJyArXG5cdFx0XHRcdFx0XHRcdG90aGVySWQgK1xuXHRcdFx0XHRcdFx0XHQnKSBjaGFuZ2UgdG8gXCJkaXNjb25uZWN0ZWRcIi4gUG9zc2libGUgbmV0d29yayBkaXNjb25uZWN0aW9uJztcblx0XHRcdFx0XHRcdGNvbnNvbGUud2Fybihtc2cxKTtcblx0XHRcdFx0XHRcdHRoaXMuY29uZmlndXJhdGlvbi5vbkljZUNvbm5lY3Rpb25TdGF0ZUV4Y2VwdGlvbihFeGNlcHRpb25FdmVudE5hbWUuSUNFX0NPTk5FQ1RJT05fRElTQ09OTkVDVEVELCBtc2cxKTtcblx0XHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRcdGNhc2UgJ2ZhaWxlZCc6XG5cdFx0XHRcdFx0XHRjb25zdCBtc2cyID0gJ0ljZUNvbm5lY3Rpb25TdGF0ZSBvZiBSVENQZWVyQ29ubmVjdGlvbiAnICsgdGhpcy5jb25maWd1cmF0aW9uLmlkICsgJyAoJyArIG90aGVySWQgKyAnKSB0byBcImZhaWxlZFwiJztcblx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IobXNnMik7XG5cdFx0XHRcdFx0XHR0aGlzLmNvbmZpZ3VyYXRpb24ub25JY2VDb25uZWN0aW9uU3RhdGVFeGNlcHRpb24oRXhjZXB0aW9uRXZlbnROYW1lLklDRV9DT05ORUNUSU9OX0ZBSUxFRCwgbXNnMik7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlICdjbG9zZWQnOlxuXHRcdFx0XHRcdFx0T21VdGlsLmxvZyhcblx0XHRcdFx0XHRcdFx0J0ljZUNvbm5lY3Rpb25TdGF0ZSBvZiBSVENQZWVyQ29ubmVjdGlvbiAnICsgdGhpcy5jb25maWd1cmF0aW9uLmlkICsgJyAoJyArIG90aGVySWQgKyAnKSBjaGFuZ2UgdG8gXCJjbG9zZWRcIidcblx0XHRcdFx0XHRcdCk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlICduZXcnOlxuXHRcdFx0XHRcdFx0T21VdGlsLmxvZygnSWNlQ29ubmVjdGlvblN0YXRlIG9mIFJUQ1BlZXJDb25uZWN0aW9uICcgKyB0aGlzLmNvbmZpZ3VyYXRpb24uaWQgKyAnICgnICsgb3RoZXJJZCArICcpIGNoYW5nZSB0byBcIm5ld1wiJyk7XG5cdFx0XHRcdFx0XHRicmVhaztcblx0XHRcdFx0XHRjYXNlICdjaGVja2luZyc6XG5cdFx0XHRcdFx0XHRPbVV0aWwubG9nKFxuXHRcdFx0XHRcdFx0XHQnSWNlQ29ubmVjdGlvblN0YXRlIG9mIFJUQ1BlZXJDb25uZWN0aW9uICcgKyB0aGlzLmNvbmZpZ3VyYXRpb24uaWQgKyAnICgnICsgb3RoZXJJZCArICcpIGNoYW5nZSB0byBcImNoZWNraW5nXCInXG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSAnY29ubmVjdGVkJzpcblx0XHRcdFx0XHRcdE9tVXRpbC5sb2coXG5cdFx0XHRcdFx0XHRcdCdJY2VDb25uZWN0aW9uU3RhdGUgb2YgUlRDUGVlckNvbm5lY3Rpb24gJyArIHRoaXMuY29uZmlndXJhdGlvbi5pZCArICcgKCcgKyBvdGhlcklkICsgJykgY2hhbmdlIHRvIFwiY29ubmVjdGVkXCInXG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdFx0Y2FzZSAnY29tcGxldGVkJzpcblx0XHRcdFx0XHRcdE9tVXRpbC5sb2coXG5cdFx0XHRcdFx0XHRcdCdJY2VDb25uZWN0aW9uU3RhdGUgb2YgUlRDUGVlckNvbm5lY3Rpb24gJyArIHRoaXMuY29uZmlndXJhdGlvbi5pZCArICcgKCcgKyBvdGhlcklkICsgJykgY2hhbmdlIHRvIFwiY29tcGxldGVkXCInXG5cdFx0XHRcdFx0XHQpO1xuXHRcdFx0XHRcdFx0YnJlYWs7XG5cdFx0XHRcdH1cblx0XHRcdH07XG5cdFx0fVxuXHRcdHRoaXMucGMuYWRkRXZlbnRMaXN0ZW5lcignaWNlY29ubmVjdGlvbnN0YXRlY2hhbmdlJywgdGhpcy5faWNlQ29ubmVjdGlvblN0YXRlQ2hhbmdlTGlzdGVuZXIpO1xuXHR9XG5cblx0LyoqXG5cdCAqIEBoaWRkZW5cblx0ICovXG5cdGdlbmVyYXRlVW5pcXVlSWQoKSB7XG5cdFx0cmV0dXJuIGNyeXB0by5yYW5kb21VVUlEKCk7XG5cdH1cblxuXHRnZXQgc3RyZWFtKCkge1xuXHRcdHJldHVybiB0aGlzLnBjLmdldExvY2FsU3RyZWFtcygpWzBdIHx8IHRoaXMucGMuZ2V0UmVtb3RlU3RyZWFtcygpWzBdO1xuXHR9XG5cblx0Ly8gTEVHQUNZIGNvZGVcblx0ZGVwcmVjYXRlZFBlZXJDb25uZWN0aW9uVHJhY2tBcGkoKSB7XG5cdFx0Zm9yIChjb25zdCB0cmFjayBvZiB0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFTdHJlYW0uZ2V0VHJhY2tzKCkpIHtcblx0XHRcdHRoaXMucGMuYWRkVHJhY2sodHJhY2ssIHRoaXMuY29uZmlndXJhdGlvbi5tZWRpYVN0cmVhbSk7XG5cdFx0fVxuXHR9XG5cblx0Ly8gREVQUkVDQVRFRCBMRUdBQ1kgTUVUSE9EOiBPbGQgV2ViUlRDIHZlcnNpb25zIGRvbid0IGltcGxlbWVudFxuXHQvLyBUcmFuc2NlaXZlcnMsIGFuZCBpbnN0ZWFkIGRlcGVuZCBvbiB0aGUgZGVwcmVjYXRlZFxuXHQvLyBcIm9mZmVyVG9SZWNlaXZlQXVkaW9cIiBhbmQgXCJvZmZlclRvUmVjZWl2ZVZpZGVvXCIuXG5cdGNyZWF0ZU9mZmVyTGVnYWN5KCkge1xuXHRcdGlmICghIXRoaXMuY29uZmlndXJhdGlvbi5tZWRpYVN0cmVhbSkge1xuXHRcdFx0dGhpcy5kZXByZWNhdGVkUGVlckNvbm5lY3Rpb25UcmFja0FwaSgpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGhhc0F1ZGlvID0gdGhpcy5jb25maWd1cmF0aW9uLm1lZGlhQ29uc3RyYWludHMuYXVkaW87XG5cdFx0Y29uc3QgaGFzVmlkZW8gPSB0aGlzLmNvbmZpZ3VyYXRpb24ubWVkaWFDb25zdHJhaW50cy52aWRlbztcblxuXHRcdGNvbnN0IG9wdGlvbnMgPSB7XG5cdFx0XHRvZmZlclRvUmVjZWl2ZUF1ZGlvOiB0aGlzLmNvbmZpZ3VyYXRpb24ubW9kZSAhPT0gJ3NlbmRvbmx5JyAmJiBoYXNBdWRpbyxcblx0XHRcdG9mZmVyVG9SZWNlaXZlVmlkZW86IHRoaXMuY29uZmlndXJhdGlvbi5tb2RlICE9PSAnc2VuZG9ubHknICYmIGhhc1ZpZGVvXG5cdFx0fTtcblxuXHRcdE9tVXRpbC5sb2coJ1tjcmVhdGVPZmZlckxlZ2FjeV0gUlRDUGVlckNvbm5lY3Rpb24uY3JlYXRlT2ZmZXIoKSBvcHRpb25zOicsIEpTT04uc3RyaW5naWZ5KG9wdGlvbnMpKTtcblxuXHRcdHJldHVybiB0aGlzLnBjLmNyZWF0ZU9mZmVyKG9wdGlvbnMpO1xuXHR9XG59XG5cbmNsYXNzIFdlYlJ0Y1BlZXJSZWN2b25seSBleHRlbmRzIFdlYlJ0Y1BlZXIge1xuXHRjb25zdHJ1Y3Rvcihjb25maWd1cmF0aW9uKSB7XG5cdFx0Y29uZmlndXJhdGlvbi5tb2RlID0gJ3JlY3Zvbmx5Jztcblx0XHRzdXBlcihjb25maWd1cmF0aW9uKTtcblx0fVxufTtcblxuY2xhc3MgV2ViUnRjUGVlclNlbmRvbmx5IGV4dGVuZHMgV2ViUnRjUGVlciB7XG5cdGNvbnN0cnVjdG9yKGNvbmZpZ3VyYXRpb24pIHtcblx0XHRjb25maWd1cmF0aW9uLm1vZGUgPSAnc2VuZG9ubHknO1xuXHRcdHN1cGVyKGNvbmZpZ3VyYXRpb24pO1xuXHR9XG59O1xuXG5jbGFzcyBXZWJSdGNQZWVyU2VuZHJlY3YgZXh0ZW5kcyBXZWJSdGNQZWVyIHtcblx0Y29uc3RydWN0b3IoY29uZmlndXJhdGlvbikge1xuXHRcdGNvbmZpZ3VyYXRpb24ubW9kZSA9ICdzZW5kcmVjdic7XG5cdFx0c3VwZXIoY29uZmlndXJhdGlvbik7XG5cdH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuXHRSZWN2b25seTogV2ViUnRjUGVlclJlY3Zvbmx5LFxuXHRTZW5kb25seTogV2ViUnRjUGVlclNlbmRvbmx5XG59O1xuIiwiLyogTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKSBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAgKi9cbmNvbnN0IFZpZGVvVXRpbCA9IHJlcXVpcmUoJy4vdmlkZW8tdXRpbCcpO1xuY29uc3QgUmluZ0J1ZmZlciA9IHJlcXVpcmUoJy4vcmluZy1idWZmZXInKTtcblxubW9kdWxlLmV4cG9ydHMgPSBjbGFzcyBNaWNMZXZlbCB7XG5cdGNvbnN0cnVjdG9yKCkge1xuXHRcdGxldCBjdHgsIG1pYywgYW5hbHlzZXJcblx0XHRcdCwgY252cywgY2FudmFzQ3R4LCBXSURUSCwgSEVJR0hULCBob3JpelxuXHRcdFx0LCB2b2wgPSAuMCwgdmFscyA9IG5ldyBSaW5nQnVmZmVyKDEwMCk7XG5cblx0XHR0aGlzLm1ldGVyU3RyZWFtID0gKHN0cmVhbSwgX2NudnMsIF9taWNBY3Rpdml0eSwgX2Vycm9yLCBjb25uZWN0QXVkaW8pID0+IHtcblx0XHRcdGlmICghc3RyZWFtIHx8IHN0cmVhbS5nZXRBdWRpb1RyYWNrcygpLmxlbmd0aCA8IDEpIHtcblx0XHRcdFx0cmV0dXJuO1xuXHRcdFx0fVxuXHRcdFx0dHJ5IHtcblx0XHRcdFx0Y29uc3QgQXVkaW9DdHggPSB3aW5kb3cuQXVkaW9Db250ZXh0IHx8IHdpbmRvdy53ZWJraXRBdWRpb0NvbnRleHQ7XG5cdFx0XHRcdGlmICghQXVkaW9DdHgpIHtcblx0XHRcdFx0XHRfZXJyb3IoXCJBdWRpb0NvbnRleHQgaXMgaW5hY2Nlc3NpYmxlXCIpO1xuXHRcdFx0XHRcdHJldHVybjtcblx0XHRcdFx0fVxuXHRcdFx0XHRjdHggPSBuZXcgQXVkaW9DdHgoKTtcblx0XHRcdFx0YW5hbHlzZXIgPSBjdHguY3JlYXRlQW5hbHlzZXIoKTtcblx0XHRcdFx0bWljID0gY3R4LmNyZWF0ZU1lZGlhU3RyZWFtU291cmNlKHN0cmVhbSk7XG5cdFx0XHRcdG1pYy5jb25uZWN0KGFuYWx5c2VyKTtcblx0XHRcdFx0aWYgKGNvbm5lY3RBdWRpbykge1xuXHRcdFx0XHRcdGFuYWx5c2VyLmNvbm5lY3QoY3R4LmRlc3RpbmF0aW9uKTtcblx0XHRcdFx0fVxuXHRcdFx0XHR0aGlzLm1ldGVyKGFuYWx5c2VyLCBfY252cywgX21pY0FjdGl2aXR5LCBfZXJyb3IpO1xuXHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdF9lcnJvcihlcnIpO1xuXHRcdFx0fVxuXHRcdH07XG5cdFx0dGhpcy5zZXRDYW52YXMgPSAoX2NudnMpID0+IHtcblx0XHRcdGNudnMgPSBfY252cztcblx0XHRcdGNvbnN0IGNhbnZhcyA9IGNudnNbMF07XG5cdFx0XHRjYW52YXNDdHggPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcblx0XHRcdFdJRFRIID0gY2FudmFzLndpZHRoO1xuXHRcdFx0SEVJR0hUID0gY2FudmFzLmhlaWdodDtcblx0XHRcdGhvcml6ID0gY252cy5kYXRhKCdvcmllbnRhdGlvbicpID09PSAnaG9yaXpvbnRhbCc7XG5cdFx0fTtcblx0XHR0aGlzLm1ldGVyID0gKF9hbmFseXNlciwgX2NudnMsIF9taWNBY3Rpdml0eSwgX2Vycm9yKSA9PiB7XG5cdFx0XHR0aGlzLnNldENhbnZhcyhfY252cyk7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRhbmFseXNlciA9IF9hbmFseXNlcjtcblx0XHRcdFx0YW5hbHlzZXIubWluRGVjaWJlbHMgPSAtOTA7XG5cdFx0XHRcdGFuYWx5c2VyLm1heERlY2liZWxzID0gLTEwO1xuXHRcdFx0XHRhbmFseXNlci5mZnRTaXplID0gMjU2O1xuXHRcdFx0XHRjb25zdCBjb2xvciA9ICQoJ2JvZHknKS5jc3MoJy0tbGV2ZWwtY29sb3InKVxuXHRcdFx0XHRcdCwgYWwgPSBhbmFseXNlci5mcmVxdWVuY3lCaW5Db3VudFxuXHRcdFx0XHRcdCwgYXJyID0gbmV3IFVpbnQ4QXJyYXkoYWwpO1xuXHRcdFx0XHRmdW5jdGlvbiB1cGRhdGUoKSB7XG5cdFx0XHRcdFx0Y2FudmFzQ3R4LmNsZWFyUmVjdCgwLCAwLCBXSURUSCwgSEVJR0hUKTtcblx0XHRcdFx0XHRpZiAoISFhbmFseXNlciAmJiBjbnZzLmxlbmd0aCA+IDApIHtcblx0XHRcdFx0XHRcdGlmIChjbnZzLmlzKCc6dmlzaWJsZScpKSB7XG5cdFx0XHRcdFx0XHRcdGFuYWx5c2VyLmdldEJ5dGVGcmVxdWVuY3lEYXRhKGFycik7XG5cdFx0XHRcdFx0XHRcdGxldCBmYXZnID0gMC4wO1xuXHRcdFx0XHRcdFx0XHRmb3IgKGxldCBpID0gMDsgaSA8IGFsOyArK2kpIHtcblx0XHRcdFx0XHRcdFx0XHRmYXZnICs9IGFycltpXSAqIGFycltpXTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0XHR2b2wgPSBNYXRoLnNxcnQoZmF2ZyAvIGFsKTtcblx0XHRcdFx0XHRcdFx0dmFscy5wdXNoKHZvbCk7XG5cdFx0XHRcdFx0XHRcdGNvbnN0IG1pbiA9IHZhbHMubWluKCk7XG5cdFx0XHRcdFx0XHRcdF9taWNBY3Rpdml0eSh2b2wgPiBtaW4gKyA1KTsgLy8gbWFnaWMgbnVtYmVyXG5cdFx0XHRcdFx0XHRcdGNhbnZhc0N0eC5maWxsU3R5bGUgPSBjb2xvcjtcblx0XHRcdFx0XHRcdFx0aWYgKGhvcml6KSB7XG5cdFx0XHRcdFx0XHRcdFx0Y2FudmFzQ3R4LmZpbGxSZWN0KDAsIDAsIFdJRFRIICogdm9sIC8gMTAwLCBIRUlHSFQpO1xuXHRcdFx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0XHRcdGNvbnN0IGggPSBIRUlHSFQgKiB2b2wgLyAxMDA7XG5cdFx0XHRcdFx0XHRcdFx0Y2FudmFzQ3R4LmZpbGxSZWN0KDAsIEhFSUdIVCAtIGgsIFdJRFRILCBoKTtcblx0XHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdFx0cmVxdWVzdEFuaW1hdGlvbkZyYW1lKHVwZGF0ZSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cdFx0XHRcdHVwZGF0ZSgpO1xuXHRcdFx0fSBjYXRjaCAoZXJyKSB7XG5cdFx0XHRcdF9lcnJvcihlcnIpO1xuXHRcdFx0fVxuXHRcdH07XG5cdFx0dGhpcy5kaXNwb3NlID0gKCkgPT4ge1xuXHRcdFx0aWYgKCEhY3R4KSB7XG5cdFx0XHRcdFZpZGVvVXRpbC5jbGVhblN0cmVhbShtaWMubWVkaWFTdHJlYW0pO1xuXHRcdFx0XHRWaWRlb1V0aWwuZGlzY29ubmVjdChtaWMpO1xuXHRcdFx0XHRWaWRlb1V0aWwuZGlzY29ubmVjdChjdHguZGVzdGluYXRpb24pO1xuXHRcdFx0XHRjdHguY2xvc2UoKTtcblx0XHRcdFx0Y3R4ID0gbnVsbDtcblx0XHRcdH1cblx0XHRcdGlmICghIWFuYWx5c2VyKSB7XG5cdFx0XHRcdFZpZGVvVXRpbC5kaXNjb25uZWN0KGFuYWx5c2VyKTtcblx0XHRcdFx0YW5hbHlzZXIgPSBudWxsO1xuXHRcdFx0fVxuXHRcdH07XG5cdH1cbn07XG4iLCIvKiBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wICh0aGUgXCJMaWNlbnNlXCIpIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMCAqL1xubW9kdWxlLmV4cG9ydHMgPSBjbGFzcyBSaW5nQnVmZmVyIHtcblx0Y29uc3RydWN0b3IobGVuZ3RoKSB7XG5cdFx0Y29uc3QgYnVmZmVyID0gW107XG5cdFx0bGV0IHBvcyA9IDA7XG5cblx0XHR0aGlzLmdldCA9IChrZXkpID0+IHtcblx0XHRcdHJldHVybiBidWZmZXJba2V5XTtcblx0XHR9O1xuXHRcdHRoaXMucHVzaCA9IChpdGVtKSA9PiB7XG5cdFx0XHRidWZmZXJbcG9zXSA9IGl0ZW07XG5cdFx0XHRwb3MgPSAocG9zICsgMSkgJSBsZW5ndGg7XG5cdFx0fTtcblx0XHR0aGlzLm1pbiA9ICgpID0+IHtcblx0XHRcdHJldHVybiBNYXRoLm1pbi5hcHBseShNYXRoLCBidWZmZXIpO1xuXHRcdH1cblx0fVxufTtcbiIsIi8qIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wICovXG5jb25zdCBPbVV0aWwgPSByZXF1aXJlKCcuLi9tYWluL29tdXRpbHMnKTtcbmNvbnN0IFNldHRpbmdzID0gcmVxdWlyZSgnLi4vbWFpbi9zZXR0aW5ncycpO1xuXG5jb25zdCBNaWNMZXZlbCA9IHJlcXVpcmUoJy4vbWljLWxldmVsJyk7XG5jb25zdCBWaWRlb1V0aWwgPSByZXF1aXJlKCcuL3ZpZGVvLXV0aWwnKTtcbmNvbnN0IFdlYlJ0Y1BlZXIgPSByZXF1aXJlKCcuL1dlYlJ0Y1BlZXInKTtcblxuY29uc3QgREVWX0FVRElPID0gJ2F1ZGlvaW5wdXQnXG5cdCwgREVWX1ZJREVPID0gJ3ZpZGVvaW5wdXQnXG5cdCwgTXNnQmFzZSA9IHt0eXBlOiAna3VyZW50bycsIG1vZGU6ICd0ZXN0J307XG5sZXQgdnMsIGxtLCBzLCBjYW0sIG1pYywgcmVzLCBvLCBydGNQZWVyLCB0aW1lclxuXHQsIHZpZFNjcm9sbCwgdmlkLCByZWNCdG4sIHBsYXlCdG4sIHJlY0FsbG93ZWQgPSBmYWxzZVxuXHQsIGxldmVsO1xuXG5mdW5jdGlvbiBfbG9hZCgpIHtcblx0cyA9IFNldHRpbmdzLmxvYWQoKTtcblx0aWYgKCFzLnZpZGVvKSB7XG5cdFx0Y29uc3QgX3JlcyA9ICQoJyN2aWRlby1zZXR0aW5ncyAuY2FtLXJlc29sdXRpb24gb3B0aW9uOnNlbGVjdGVkJykuZGF0YSgpO1xuXHRcdHMudmlkZW8gPSB7XG5cdFx0XHRjYW06IDBcblx0XHRcdCwgbWljOiAwXG5cdFx0XHQsIHdpZHRoOiBfcmVzLndpZHRoXG5cdFx0XHQsIGhlaWdodDogX3Jlcy5oZWlnaHRcblx0XHR9O1xuXHR9XG5cdGlmICghcy5maXhlZCkge1xuXHRcdHMuZml4ZWQgPSB7XG5cdFx0XHRlbmFibGVkOiBmYWxzZVxuXHRcdFx0LCB3aWR0aDogMTIwXG5cdFx0XHQsIGhlaWdodDogOTBcblx0XHR9O1xuXHR9XG5cdHJldHVybiBzO1xufVxuZnVuY3Rpb24gX3NhdmUoKSB7XG5cdFNldHRpbmdzLnNhdmUocyk7XG5cdE9tVXRpbC5zZW5kTWVzc2FnZSh7XG5cdFx0dHlwZTogJ2F2J1xuXHRcdCwgYXJlYTogJ3Jvb20nXG5cdFx0LCBzZXR0aW5nczogc1xuXHR9KTtcbn1cbmZ1bmN0aW9uIF9jbGVhcihfbXMpIHtcblx0Y29uc3QgbXMgPSBfbXMgfHwgKHZpZCAmJiB2aWQubGVuZ3RoID09PSAxID8gdmlkWzBdLnNyY09iamVjdCA6IG51bGwpO1xuXHRWaWRlb1V0aWwuY2xlYW5TdHJlYW0obXMpO1xuXHRpZiAodmlkICYmIHZpZC5sZW5ndGggPT09IDEpIHtcblx0XHR2aWRbMF0uc3JjT2JqZWN0ID0gbnVsbDtcblx0fVxuXHRWaWRlb1V0aWwuY2xlYW5QZWVyKHJ0Y1BlZXIpO1xuXHRpZiAoISFsbSkge1xuXHRcdGxtLmhpZGUoKTtcblx0fVxuXHRpZiAoISFsZXZlbCkge1xuXHRcdGxldmVsLmRpc3Bvc2UoKTtcblx0XHRsZXZlbCA9IG51bGw7XG5cdH1cbn1cbmZ1bmN0aW9uIF9jbG9zZSgpIHtcblx0X2NsZWFyKCk7XG5cdFdpY2tldC5FdmVudC51bnN1YnNjcmliZSgnL3dlYnNvY2tldC9tZXNzYWdlJywgX29uV3NNZXNzYWdlKTtcbn1cbmZ1bmN0aW9uIF9vbkljZUNhbmRpZGF0ZShjYW5kaWRhdGUpIHtcblx0T21VdGlsLmxvZygnTG9jYWwgY2FuZGlkYXRlJyArIEpTT04uc3RyaW5naWZ5KGNhbmRpZGF0ZSkpO1xuXHRPbVV0aWwuc2VuZE1lc3NhZ2Uoe1xuXHRcdGlkIDogJ2ljZUNhbmRpZGF0ZSdcblx0XHQsIGNhbmRpZGF0ZTogY2FuZGlkYXRlXG5cdH0sIE1zZ0Jhc2UpO1xufVxuZnVuY3Rpb24gX2luaXQob3B0aW9ucykge1xuXHRvID0gSlNPTi5wYXJzZShKU09OLnN0cmluZ2lmeShvcHRpb25zKSk7XG5cdGlmICghIW8uaW5mb01zZykge1xuXHRcdE9tVXRpbC5hbGVydCgnaW5mbycsIG8uaW5mb01zZywgMCk7XG5cdH1cblx0dnMgPSAkKCcjdmlkZW8tc2V0dGluZ3MnKTtcblx0bG0gPSB2cy5maW5kKCcubGV2ZWwtbWV0ZXInKTtcblx0Y2FtID0gdnMuZmluZCgnc2VsZWN0LmNhbScpLmNoYW5nZShmdW5jdGlvbigpIHtcblx0XHRfcmVhZFZhbHVlcygpO1xuXHR9KTtcblx0bWljID0gdnMuZmluZCgnc2VsZWN0Lm1pYycpLmNoYW5nZShmdW5jdGlvbigpIHtcblx0XHRfcmVhZFZhbHVlcygpO1xuXHR9KTtcblx0cmVzID0gdnMuZmluZCgnc2VsZWN0LmNhbS1yZXNvbHV0aW9uJykuY2hhbmdlKGZ1bmN0aW9uKCkge1xuXHRcdF9yZWFkVmFsdWVzKCk7XG5cdH0pO1xuXHR2aWRTY3JvbGwgPSB2cy5maW5kKCcudmlkLWJsb2NrIC52aWRlby1jb25haW5lcicpO1xuXHR0aW1lciA9IHZzLmZpbmQoJy50aW1lcicpO1xuXHR2aWQgPSB2aWRTY3JvbGwuZmluZCgndmlkZW8nKTtcblx0cmVjQnRuID0gdnMuZmluZCgnLnJlYy1zdGFydCcpXG5cdFx0LmNsaWNrKGZ1bmN0aW9uKCkge1xuXHRcdFx0cmVjQnRuLnByb3AoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHRfc2V0RW5hYmxlZCh0cnVlKTtcblx0XHRcdE9tVXRpbC5zZW5kTWVzc2FnZSh7XG5cdFx0XHRcdGlkIDogJ3dhbm5hUmVjb3JkJ1xuXHRcdFx0fSwgTXNnQmFzZSk7XG5cdFx0fSk7XG5cdHBsYXlCdG4gPSB2cy5maW5kKCcucGxheScpXG5cdFx0LmNsaWNrKGZ1bmN0aW9uKCkge1xuXHRcdFx0cmVjQnRuLnByb3AoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHRfc2V0RW5hYmxlZCh0cnVlKTtcblx0XHRcdE9tVXRpbC5zZW5kTWVzc2FnZSh7XG5cdFx0XHRcdGlkIDogJ3dhbm5hUGxheSdcblx0XHRcdH0sIE1zZ0Jhc2UpO1xuXHRcdH0pO1xuXHR2cy5maW5kKCcuYnRuLXNhdmUnKS5vZmYoKS5jbGljayhmdW5jdGlvbigpIHtcblx0XHRfc2F2ZSgpO1xuXHRcdF9jbG9zZSgpO1xuXHRcdHZzLm1vZGFsKFwiaGlkZVwiKTtcblx0fSk7XG5cdHZzLmZpbmQoJy5idG4tY2FuY2VsJykub2ZmKCkuY2xpY2soZnVuY3Rpb24oKSB7XG5cdFx0X2Nsb3NlKCk7XG5cdFx0dnMubW9kYWwoXCJoaWRlXCIpO1xuXHR9KTtcblx0dnMub2ZmKCkub24oJ2hpZGRlbi5icy5tb2RhbCcsIGZ1bmN0aW9uICgpIHtcblx0XHRfY2xvc2UoKTtcblx0fSk7XG5cdG8ud2lkdGggPSAzMDA7XG5cdG8uaGVpZ2h0ID0gMjAwO1xuXHRvLm1vZGUgPSAnc2V0dGluZ3MnO1xuXHRvLnJpZ2h0cyA9IChvLnJpZ2h0cyB8fCBbXSkuam9pbigpO1xuXHRkZWxldGUgby5rZXljb2RlO1xuXHR2cy5maW5kKCcubW9kYWwtYm9keSBpbnB1dCwgLm1vZGFsLWJvZHkgYnV0dG9uJykucHJvcCgnZGlzYWJsZWQnLCB0cnVlKTtcblx0Y29uc3QgcnIgPSB2cy5maW5kKCcuY2FtLXJlc29sdXRpb24nKS5wYXJlbnRzKCcuc2V0dC1yb3cnKTtcblx0aWYgKCFvLmludGVydmlldykge1xuXHRcdHJyLnNob3coKTtcblx0fSBlbHNlIHtcblx0XHRyci5oaWRlKCk7XG5cdH1cblx0X2xvYWQoKTtcblx0X3NhdmUoKTsgLy8gdHJpZ2dlciBzZXR0aW5ncyB1cGRhdGVcbn1cbmZ1bmN0aW9uIF91cGRhdGVSZWMoKSB7XG5cdHJlY0J0bi5wcm9wKCdkaXNhYmxlZCcsICFyZWNBbGxvd2VkIHx8IChzLnZpZGVvLmNhbSA8IDAgJiYgcy52aWRlby5taWMgPCAwKSk7XG59XG5mdW5jdGlvbiBfc2V0Q250c0RpbWVuc2lvbnMoY250cykge1xuXHRpZiAoVmlkZW9VdGlsLmlzU2FmYXJpKCkpIHtcblx0XHRsZXQgd2lkdGggPSBzLnZpZGVvLndpZHRoO1xuXHRcdC8vdmFsaWQgd2lkdGhzIGFyZSAzMjAsIDY0MCwgMTI4MFxuXHRcdFszMjAsIDY0MCwgMTI4MF0uc29tZShmdW5jdGlvbih3KSB7XG5cdFx0XHRpZiAod2lkdGggPCB3ICsgMSkge1xuXHRcdFx0XHR3aWR0aCA9IHc7XG5cdFx0XHRcdHJldHVybiB0cnVlO1xuXHRcdFx0fVxuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH0pO1xuXHRcdGNudHMudmlkZW8ud2lkdGggPSB3aWR0aCA8IDEyODEgPyB3aWR0aCA6IDEyODA7XG5cdH0gZWxzZSB7XG5cdFx0Y250cy52aWRlby53aWR0aCA9IG8uaW50ZXJ2aWV3ID8gMzIwIDogcy52aWRlby53aWR0aDtcblx0XHRjbnRzLnZpZGVvLmhlaWdodCA9IG8uaW50ZXJ2aWV3ID8gMjYwIDogcy52aWRlby5oZWlnaHQ7XG5cdH1cbn1cbi8vZWFjaCBib29sIE9SIGh0dHBzOi8vZGV2ZWxvcGVyLm1vemlsbGEub3JnL2VuLVVTL2RvY3MvV2ViL0FQSS9NZWRpYVRyYWNrQ29uc3RyYWludHNcbi8vIG1pbi9pZGVhbC9tYXgvZXhhY3QvbWFuZGF0b3J5IGNhbiBhbHNvIGJlIHVzZWRcbmZ1bmN0aW9uIF9jb25zdHJhaW50cyhzZCwgY2FsbGJhY2spIHtcblx0X2dldERldkNvbnN0cmFpbnRzKGZ1bmN0aW9uKGRldkNudHMpIHtcblx0XHRjb25zdCBjbnRzID0ge1xuXHRcdFx0dmlkZW9FbmFibGVkOiBWaWRlb1V0aWwuaGFzQ2FtKHNkKVxuXHRcdFx0LCBhdWRpb0VuYWJsZWQ6IFZpZGVvVXRpbC5oYXNNaWMoc2QpXG5cdFx0fTtcblx0XHRpZiAoZGV2Q250cy52aWRlbyAmJiBmYWxzZSA9PT0gby5hdWRpb09ubHkgJiYgcy52aWRlby5jYW0gPiAtMSkge1xuXHRcdFx0Y250cy52aWRlbyA9IHtcblx0XHRcdFx0ZnJhbWVSYXRlOiBvLmNhbWVyYS5mcHNcblx0XHRcdH07XG5cdFx0XHRfc2V0Q250c0RpbWVuc2lvbnMoY250cylcblx0XHRcdGlmICghIXMudmlkZW8uY2FtRGV2aWNlKSB7XG5cdFx0XHRcdGNudHMudmlkZW8uZGV2aWNlSWQgPSB7XG5cdFx0XHRcdFx0aWRlYWw6IHMudmlkZW8uY2FtRGV2aWNlXG5cdFx0XHRcdH07XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjbnRzLnZpZGVvLmZhY2luZ01vZGUgPSB7XG5cdFx0XHRcdFx0aWRlYWw6ICd1c2VyJ1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNudHMudmlkZW8gPSBmYWxzZTtcblx0XHR9XG5cdFx0aWYgKGRldkNudHMuYXVkaW8gJiYgcy52aWRlby5taWMgPiAtMSkge1xuXHRcdFx0Y250cy5hdWRpbyA9IHtcblx0XHRcdFx0c2FtcGxlUmF0ZTogby5taWNyb3Bob25lLnJhdGVcblx0XHRcdFx0LCBlY2hvQ2FuY2VsbGF0aW9uOiBvLm1pY3JvcGhvbmUuZWNob1xuXHRcdFx0XHQsIG5vaXNlU3VwcHJlc3Npb246IG8ubWljcm9waG9uZS5ub2lzZVxuXHRcdFx0fTtcblx0XHRcdGlmICghIXMudmlkZW8ubWljRGV2aWNlKSB7XG5cdFx0XHRcdGNudHMuYXVkaW8uZGV2aWNlSWQgPSB7XG5cdFx0XHRcdFx0aWRlYWw6IHMudmlkZW8ubWljRGV2aWNlXG5cdFx0XHRcdH07XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNudHMuYXVkaW8gPSBmYWxzZTtcblx0XHR9XG5cdFx0Y2FsbGJhY2soY250cyk7XG5cdH0pO1xufVxuZnVuY3Rpb24gX3JlYWRWYWx1ZXMobXNnLCBmdW5jKSB7XG5cdGNvbnN0IHYgPSBjYW0uZmluZCgnb3B0aW9uOnNlbGVjdGVkJylcblx0XHQsIG0gPSBtaWMuZmluZCgnb3B0aW9uOnNlbGVjdGVkJylcblx0XHQsIG8gPSByZXMuZmluZCgnb3B0aW9uOnNlbGVjdGVkJykuZGF0YSgpO1xuXHRzLnZpZGVvLmNhbSA9IDEgKiBjYW0udmFsKCk7XG5cdHMudmlkZW8uY2FtRGV2aWNlID0gdi5kYXRhKCdkZXZpY2UtaWQnKTtcblx0cy52aWRlby5taWMgPSAxICogbWljLnZhbCgpO1xuXHRzLnZpZGVvLm1pY0RldmljZSA9IG0uZGF0YSgnZGV2aWNlLWlkJyk7XG5cdHMudmlkZW8ud2lkdGggPSBvLndpZHRoO1xuXHRzLnZpZGVvLmhlaWdodCA9IG8uaGVpZ2h0O1xuXHR2aWQud2lkdGgoby53aWR0aCkuaGVpZ2h0KG8uaGVpZ2h0KTtcblx0dmlkU2Nyb2xsLnNjcm9sbExlZnQoTWF0aC5tYXgoMCwgcy52aWRlby53aWR0aCAvIDIgLSAxNTApKVxuXHRcdC5zY3JvbGxUb3AoTWF0aC5tYXgoMCwgcy52aWRlby5oZWlnaHQgLyAyIC0gMTEwKSk7XG5cdF9jbGVhcigpO1xuXHRfY29uc3RyYWludHMobnVsbCwgZnVuY3Rpb24oY250cykge1xuXHRcdGlmIChjbnRzLnZpZGVvICE9PSBmYWxzZSB8fCBjbnRzLmF1ZGlvICE9PSBmYWxzZSkge1xuXHRcdFx0Y29uc3Qgb3B0aW9ucyA9IFZpZGVvVXRpbC5hZGRJY2VTZXJ2ZXJzKHtcblx0XHRcdFx0bWVkaWFDb25zdHJhaW50czogY250c1xuXHRcdFx0XHQsIG9uSWNlQ2FuZGlkYXRlOiBfb25JY2VDYW5kaWRhdGVcblx0XHRcdH0sIG1zZyk7XG5cdFx0XHRuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldFVzZXJNZWRpYShjbnRzKVxuXHRcdFx0XHQudGhlbihzdHJlYW0gPT4ge1xuXHRcdFx0XHRcdFZpZGVvVXRpbC5wbGF5U3JjKHZpZFswXSwgc3RyZWFtLCB0cnVlKTtcblx0XHRcdFx0XHRvcHRpb25zLm1lZGlhU3RyZWFtID0gc3RyZWFtO1xuXG5cdFx0XHRcdFx0cnRjUGVlciA9IG5ldyBXZWJSdGNQZWVyLlNlbmRvbmx5KG9wdGlvbnMpO1xuXHRcdFx0XHRcdGlmIChjbnRzLmF1ZGlvKSB7XG5cdFx0XHRcdFx0XHRsbS5zaG93KCk7XG5cdFx0XHRcdFx0XHRsZXZlbCA9IG5ldyBNaWNMZXZlbCgpO1xuXHRcdFx0XHRcdFx0bGV2ZWwubWV0ZXJTdHJlYW0oc3RyZWFtLCBsbSwgZnVuY3Rpb24oKXt9LCBPbVV0aWwuZXJyb3IsIGZhbHNlKTtcblx0XHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdFx0bG0uaGlkZSgpO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0XHRyZXR1cm4gcnRjUGVlci5jcmVhdGVPZmZlcigpO1xuXHRcdFx0XHR9KVxuXHRcdFx0XHQudGhlbihzZHBPZmZlciA9PiB7XG5cdFx0XHRcdFx0cnRjUGVlci5wcm9jZXNzTG9jYWxPZmZlcihzZHBPZmZlcik7XG5cdFx0XHRcdFx0aWYgKHR5cGVvZihmdW5jKSA9PT0gJ2Z1bmN0aW9uJykge1xuXHRcdFx0XHRcdFx0ZnVuYyhzZHBPZmZlci5zZHAsIGNudHMpO1xuXHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRfYWxsb3dSZWModHJ1ZSk7XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9KS5jYXRjaChfID0+IE9tVXRpbC5lcnJvcignRXJyb3IgZ2VuZXJhdGluZyB0aGUgb2ZmZXInKSk7XG5cdFx0fVxuXHRcdGlmICghbXNnKSB7XG5cdFx0XHRfdXBkYXRlUmVjKCk7XG5cdFx0fVxuXHR9KTtcbn1cblxuZnVuY3Rpb24gX2FsbG93UmVjKGFsbG93KSB7XG5cdHJlY0FsbG93ZWQgPSBhbGxvdztcblx0X3VwZGF0ZVJlYygpO1xufVxuZnVuY3Rpb24gX3NldExvYWRpbmcoZWwpIHtcblx0ZWwuZmluZCgnb3B0aW9uJykucmVtb3ZlKCk7XG5cdGVsLmFwcGVuZChPbVV0aWwudG1wbCgnI3NldHRpbmdzLW9wdGlvbi1sb2FkaW5nJykpO1xufVxuZnVuY3Rpb24gX3NldERpc2FibGVkKGVscykge1xuXHRlbHMuZm9yRWFjaChmdW5jdGlvbihlbCkge1xuXHRcdGVsLmZpbmQoJ29wdGlvbicpLnJlbW92ZSgpO1xuXHRcdGVsLmFwcGVuZChPbVV0aWwudG1wbCgnI3NldHRpbmdzLW9wdGlvbi1kaXNhYmxlZCcpKTtcblx0fSk7XG59XG5mdW5jdGlvbiBfc2V0U2VsZWN0ZWREZXZpY2UoZGV2LCBkZXZJZHgpIHtcblx0bGV0IG8gPSBkZXYuZmluZCgnb3B0aW9uW3ZhbHVlPVwiJyArIGRldklkeCArICdcIl0nKTtcblx0aWYgKG8ubGVuZ3RoID09PSAwICYmIGRldklkeCAhPT0gLTEpIHtcblx0XHRvID0gZGV2LmZpbmQoJ29wdGlvblt2YWx1ZT1cIjBcIl0nKTtcblx0fVxuXHRvLnByb3AoJ3NlbGVjdGVkJywgdHJ1ZSk7XG59XG5mdW5jdGlvbiBfZ2V0RGV2Q29uc3RyYWludHMoY2FsbGJhY2spIHtcblx0Y29uc3QgZGV2Q250cyA9IHthdWRpbzogZmFsc2UsIHZpZGVvOiBmYWxzZSwgZGV2aWNlczogW119O1xuXHRpZiAod2luZG93LmlzU2VjdXJlQ29udGV4dCA9PT0gZmFsc2UpIHtcblx0XHRPbVV0aWwuZXJyb3IoJCgnI3NldHRpbmdzLWh0dHBzLXJlcXVpcmVkJykudGV4dCgpKTtcblx0XHRyZXR1cm47XG5cdH1cblx0aWYgKCFuYXZpZ2F0b3IubWVkaWFEZXZpY2VzIHx8ICFuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmVudW1lcmF0ZURldmljZXMpIHtcblx0XHRPbVV0aWwuZXJyb3IoJ2VudW1lcmF0ZURldmljZXMoKSBub3Qgc3VwcG9ydGVkLicpO1xuXHRcdHJldHVybjtcblx0fVxuXHRuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmVudW1lcmF0ZURldmljZXMoKVxuXHRcdC50aGVuKGRldmljZXMgPT4gZGV2aWNlcy5mb3JFYWNoKGRldmljZSA9PiB7XG5cdFx0XHRcdGlmIChERVZfQVVESU8gPT09IGRldmljZS5raW5kIHx8IERFVl9WSURFTyA9PT0gZGV2aWNlLmtpbmQpIHtcblx0XHRcdFx0XHRkZXZDbnRzLmRldmljZXMucHVzaCh7XG5cdFx0XHRcdFx0XHRraW5kOiBkZXZpY2Uua2luZFxuXHRcdFx0XHRcdFx0LCBsYWJlbDogZGV2aWNlLmxhYmVsIHx8IChkZXZpY2Uua2luZCArICcgJyArIGRldkNudHMuZGV2aWNlcy5sZW5ndGgpXG5cdFx0XHRcdFx0XHQsIGRldmljZUlkOiBkZXZpY2UuZGV2aWNlSWRcblx0XHRcdFx0XHR9KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRpZiAoREVWX0FVRElPID09PSBkZXZpY2Uua2luZCkge1xuXHRcdFx0XHRcdGRldkNudHMuYXVkaW8gPSB0cnVlO1xuXHRcdFx0XHR9IGVsc2UgaWYgKERFVl9WSURFTyA9PT0gZGV2aWNlLmtpbmQpIHtcblx0XHRcdFx0XHRkZXZDbnRzLnZpZGVvID0gdHJ1ZTtcblx0XHRcdFx0fVxuXHRcdFx0fSkpXG5cdFx0LmNhdGNoKCgpID0+IE9tVXRpbC5lcnJvcignVW5hYmxlIHRvIGdldCB0aGUgbGlzdCBvZiBtdWx0aW1lZGlhIGRldmljZXMnKSlcblx0XHQuZmluYWxseSgoKSA9PiBjYWxsYmFjayhkZXZDbnRzKSk7XG59XG5mdW5jdGlvbiBfaW5pdERldmljZXMoKSB7XG5cdGlmICh3aW5kb3cuaXNTZWN1cmVDb250ZXh0ID09PSBmYWxzZSkge1xuXHRcdE9tVXRpbC5lcnJvcigkKCcjc2V0dGluZ3MtaHR0cHMtcmVxdWlyZWQnKS50ZXh0KCkpO1xuXHRcdHJldHVybjtcblx0fVxuXHRpZiAoIW5hdmlnYXRvci5tZWRpYURldmljZXMgfHwgIW5hdmlnYXRvci5tZWRpYURldmljZXMuZW51bWVyYXRlRGV2aWNlcykge1xuXHRcdE9tVXRpbC5lcnJvcignZW51bWVyYXRlRGV2aWNlcygpIG5vdCBzdXBwb3J0ZWQuJyk7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdF9zZXRMb2FkaW5nKGNhbSk7XG5cdF9zZXRMb2FkaW5nKG1pYyk7XG5cdF9nZXREZXZDb25zdHJhaW50cyhmdW5jdGlvbihkZXZDbnRzKSB7XG5cdFx0aWYgKCFkZXZDbnRzLmF1ZGlvICYmICFkZXZDbnRzLnZpZGVvKSB7XG5cdFx0XHRfc2V0RGlzYWJsZWQoW2NhbSwgbWljXSk7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdG5hdmlnYXRvci5tZWRpYURldmljZXMuZ2V0VXNlck1lZGlhKGRldkNudHMpXG5cdFx0XHQudGhlbihzdHJlYW0gPT4ge1xuXHRcdFx0XHRjb25zdCBkZXZpY2VzID0gbmF2aWdhdG9yLm1lZGlhRGV2aWNlcy5lbnVtZXJhdGVEZXZpY2VzKClcblx0XHRcdFx0XHQuY2F0Y2goZnVuY3Rpb24oZXJyKSB7XG5cdFx0XHRcdFx0XHR0aHJvdyBlcnI7XG5cdFx0XHRcdFx0fSlcblx0XHRcdFx0XHQuZmluYWxseSgoKSA9PiBfY2xlYXIoc3RyZWFtKSk7XG5cdFx0XHRcdHJldHVybiBkZXZpY2VzIHx8IGRldkNudHMuZGV2aWNlcztcblx0XHRcdH0pXG5cdFx0XHQuY2F0Y2goZnVuY3Rpb24oKSB7XG5cdFx0XHRcdHJldHVybiBkZXZDbnRzLmRldmljZXM7XG5cdFx0XHR9KVxuXHRcdFx0LnRoZW4oZGV2aWNlcyA9PiB7XG5cdFx0XHRcdGxldCBjQ291bnQgPSAwLCBtQ291bnQgPSAwO1xuXHRcdFx0XHRfbG9hZCgpO1xuXHRcdFx0XHRfc2V0RGlzYWJsZWQoW2NhbSwgbWljXSk7XG5cdFx0XHRcdGRldmljZXMuZm9yRWFjaChkZXZpY2UgPT4ge1xuXHRcdFx0XHRcdGlmIChERVZfQVVESU8gPT09IGRldmljZS5raW5kKSB7XG5cdFx0XHRcdFx0XHRjb25zdCBvID0gJCgnPG9wdGlvbj48L29wdGlvbj4nKS5hdHRyKCd2YWx1ZScsIG1Db3VudCkudGV4dChkZXZpY2UubGFiZWwpXG5cdFx0XHRcdFx0XHRcdC5kYXRhKCdkZXZpY2UtaWQnLCBkZXZpY2UuZGV2aWNlSWQpO1xuXHRcdFx0XHRcdFx0bWljLmFwcGVuZChvKTtcblx0XHRcdFx0XHRcdG1Db3VudCsrO1xuXHRcdFx0XHRcdH0gZWxzZSBpZiAoREVWX1ZJREVPID09PSBkZXZpY2Uua2luZCkge1xuXHRcdFx0XHRcdFx0Y29uc3QgbyA9ICQoJzxvcHRpb24+PC9vcHRpb24+JykuYXR0cigndmFsdWUnLCBjQ291bnQpLnRleHQoZGV2aWNlLmxhYmVsKVxuXHRcdFx0XHRcdFx0XHQuZGF0YSgnZGV2aWNlLWlkJywgZGV2aWNlLmRldmljZUlkKTtcblx0XHRcdFx0XHRcdGNhbS5hcHBlbmQobyk7XG5cdFx0XHRcdFx0XHRjQ291bnQrKztcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRfc2V0U2VsZWN0ZWREZXZpY2UoY2FtLCBzLnZpZGVvLmNhbSk7XG5cdFx0XHRcdF9zZXRTZWxlY3RlZERldmljZShtaWMsIHMudmlkZW8ubWljKTtcblx0XHRcdFx0cmVzLmZpbmQoJ29wdGlvbicpLmVhY2goZnVuY3Rpb24oKSB7XG5cdFx0XHRcdFx0Y29uc3QgbyA9ICQodGhpcykuZGF0YSgpO1xuXHRcdFx0XHRcdGlmIChvLndpZHRoID09PSBzLnZpZGVvLndpZHRoICYmIG8uaGVpZ2h0ID09PSBzLnZpZGVvLmhlaWdodCkge1xuXHRcdFx0XHRcdFx0JCh0aGlzKS5wcm9wKCdzZWxlY3RlZCcsIHRydWUpO1xuXHRcdFx0XHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fSk7XG5cdFx0XHRcdF9yZWFkVmFsdWVzKCk7XG5cdFx0XHR9KVxuXHRcdFx0LmNhdGNoKGZ1bmN0aW9uKGVycikge1xuXHRcdFx0XHRfc2V0RGlzYWJsZWQoW2NhbSwgbWljXSk7XG5cdFx0XHRcdE9tVXRpbC5lcnJvcihlcnIpO1xuXHRcdFx0fSk7XG5cdH0pO1xufVxuZnVuY3Rpb24gX29wZW4oKSB7XG5cdFdpY2tldC5FdmVudC5zdWJzY3JpYmUoJy93ZWJzb2NrZXQvbWVzc2FnZScsIF9vbldzTWVzc2FnZSk7XG5cdHJlY0FsbG93ZWQgPSBmYWxzZTtcblx0dGltZXIuaGlkZSgpO1xuXHRwbGF5QnRuLnByb3AoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdHZzLm1vZGFsKCdzaG93Jyk7XG5cdF9sb2FkKCk7XG5cdF9pbml0RGV2aWNlcygpO1xufVxuZnVuY3Rpb24gX3NldEVuYWJsZWQoZW5hYmxlZCkge1xuXHRwbGF5QnRuLnByb3AoJ2Rpc2FibGVkJywgZW5hYmxlZCk7XG5cdGNhbS5wcm9wKCdkaXNhYmxlZCcsIGVuYWJsZWQpO1xuXHRtaWMucHJvcCgnZGlzYWJsZWQnLCBlbmFibGVkKTtcblx0cmVzLnByb3AoJ2Rpc2FibGVkJywgZW5hYmxlZCk7XG59XG5mdW5jdGlvbiBfb25TdG9wKCkge1xuXHRfdXBkYXRlUmVjKCk7XG5cdF9zZXRFbmFibGVkKGZhbHNlKTtcbn1cbmZ1bmN0aW9uIF9vbktNZXNzYWdlKG0pIHtcblx0T21VdGlsLmluZm8oJ1JlY2VpdmVkIG1lc3NhZ2U6ICcsIG0pO1xuXHRzd2l0Y2ggKG0uaWQpIHtcblx0XHRjYXNlICdjYW5SZWNvcmQnOlxuXHRcdFx0X3JlYWRWYWx1ZXMobSwgZnVuY3Rpb24oX29mZmVyU2RwLCBjbnRzKSB7XG5cdFx0XHRcdE9tVXRpbC5pbmZvKCdJbnZva2luZyBTRFAgb2ZmZXIgY2FsbGJhY2sgZnVuY3Rpb24nKTtcblx0XHRcdFx0T21VdGlsLnNlbmRNZXNzYWdlKHtcblx0XHRcdFx0XHRpZCA6ICdyZWNvcmQnXG5cdFx0XHRcdFx0LCBzZHBPZmZlcjogX29mZmVyU2RwXG5cdFx0XHRcdFx0LCB2aWRlbzogY250cy52aWRlbyAhPT0gZmFsc2Vcblx0XHRcdFx0XHQsIGF1ZGlvOiBjbnRzLmF1ZGlvICE9PSBmYWxzZVxuXHRcdFx0XHR9LCBNc2dCYXNlKTtcblx0XHRcdH0pO1xuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSAnY2FuUGxheSc6IHtcblx0XHRcdGNvbnN0IG9wdGlvbnMgPSBWaWRlb1V0aWwuYWRkSWNlU2VydmVycyh7XG5cdFx0XHRcdG1lZGlhQ29uc3RyYWludHM6IHthdWRpbzogdHJ1ZSwgdmlkZW86IHRydWV9XG5cdFx0XHRcdCwgb25JY2VDYW5kaWRhdGU6IF9vbkljZUNhbmRpZGF0ZVxuXHRcdFx0fSwgbSk7XG5cdFx0XHRfY2xlYXIoKTtcblx0XHRcdHJ0Y1BlZXIgPSBuZXcgV2ViUnRjUGVlci5SZWN2b25seShvcHRpb25zKTtcblx0XHRcdHJ0Y1BlZXIuY3JlYXRlT2ZmZXIoKVxuXHRcdFx0XHQudGhlbihzZHBPZmZlciA9PiB7XG5cdFx0XHRcdFx0cnRjUGVlci5wcm9jZXNzTG9jYWxPZmZlcihzZHBPZmZlcik7XG5cdFx0XHRcdFx0T21VdGlsLnNlbmRNZXNzYWdlKHtcblx0XHRcdFx0XHRcdGlkIDogJ3BsYXknXG5cdFx0XHRcdFx0XHQsIHNkcE9mZmVyOiBzZHBPZmZlci5zZHBcblx0XHRcdFx0XHR9LCBNc2dCYXNlKTtcblx0XHRcdFx0fSlcblx0XHRcdFx0LmNhdGNoKF8gPT4gT21VdGlsLmVycm9yKCdFcnJvciBnZW5lcmF0aW5nIHRoZSBvZmZlcicpKTtcblx0XHRcdH1cblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJ3BsYXlSZXNwb25zZSc6XG5cdFx0XHRPbVV0aWwubG9nKCdQbGF5IFNEUCBhbnN3ZXIgcmVjZWl2ZWQgZnJvbSBzZXJ2ZXIuIFByb2Nlc3NpbmcgLi4uJyk7XG5cblx0XHRcdHJ0Y1BlZXIucHJvY2Vzc1JlbW90ZUFuc3dlcihtLnNkcEFuc3dlcilcblx0XHRcdFx0LnRoZW4oKCkgPT4ge1xuXHRcdFx0XHRcdGNvbnN0IHN0cmVhbSA9IHJ0Y1BlZXIuc3RyZWFtO1xuXHRcdFx0XHRcdGlmIChzdHJlYW0pIHtcblx0XHRcdFx0XHRcdFZpZGVvVXRpbC5wbGF5U3JjKHZpZFswXSwgc3RyZWFtLCBmYWxzZSk7XG5cdFx0XHRcdFx0XHRsbS5zaG93KCk7XG5cdFx0XHRcdFx0XHRsZXZlbCA9IG5ldyBNaWNMZXZlbCgpO1xuXHRcdFx0XHRcdFx0bGV2ZWwubWV0ZXJTdHJlYW0oc3RyZWFtLCBsbSwgZnVuY3Rpb24oKXt9LCBPbVV0aWwuZXJyb3IsIHRydWUpO1xuXHRcdFx0XHRcdH07XG5cdFx0XHRcdH0pXG5cdFx0XHRcdC5jYXRjaChlcnJvciA9PiBPbVV0aWwuZXJyb3IoZXJyb3IpKTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJ3N0YXJ0UmVzcG9uc2UnOlxuXHRcdFx0T21VdGlsLmxvZygnU0RQIGFuc3dlciByZWNlaXZlZCBmcm9tIHNlcnZlci4gUHJvY2Vzc2luZyAuLi4nKTtcblx0XHRcdHJ0Y1BlZXIucHJvY2Vzc1JlbW90ZUFuc3dlcihtLnNkcEFuc3dlcilcblx0XHRcdFx0LmNhdGNoKGVycm9yID0+IE9tVXRpbC5lcnJvcihlcnJvcikpO1xuXHRcdFx0YnJlYWs7XG5cdFx0Y2FzZSAnaWNlQ2FuZGlkYXRlJzpcblx0XHRcdHJ0Y1BlZXIuYWRkSWNlQ2FuZGlkYXRlKG0uY2FuZGlkYXRlKVxuXHRcdFx0XHQuY2F0Y2goZXJyb3IgPT4gT21VdGlsLmVycm9yKCdFcnJvciBhZGRpbmcgY2FuZGlkYXRlOiAnICsgZXJyb3IpKTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJ3JlY29yZGluZyc6XG5cdFx0XHR0aW1lci5zaG93KCkuZmluZCgnLnRpbWUnKS50ZXh0KG0udGltZSk7XG5cdFx0XHRicmVhaztcblx0XHRjYXNlICdyZWNTdG9wcGVkJzpcblx0XHRcdHRpbWVyLmhpZGUoKTtcblx0XHRcdF9vblN0b3AoKTtcblx0XHRcdGJyZWFrO1xuXHRcdGNhc2UgJ3BsYXlTdG9wcGVkJzpcblx0XHRcdF9vblN0b3AoKTtcblx0XHRcdF9yZWFkVmFsdWVzKCk7XG5cdFx0XHRicmVhaztcblx0XHRkZWZhdWx0OlxuXHRcdFx0Ly8gbm8tb3Bcblx0fVxufVxuZnVuY3Rpb24gX29uV3NNZXNzYWdlKGpxRXZlbnQsIG1zZykge1xuXHR0cnkge1xuXHRcdGlmIChtc2cgaW5zdGFuY2VvZiBCbG9iKSB7XG5cdFx0XHRyZXR1cm47IC8vcGluZ1xuXHRcdH1cblx0XHRjb25zdCBtID0gSlNPTi5wYXJzZShtc2cpO1xuXHRcdGlmIChtICYmICdrdXJlbnRvJyA9PT0gbS50eXBlKSB7XG5cdFx0XHRpZiAoJ3Rlc3QnID09PSBtLm1vZGUpIHtcblx0XHRcdFx0X29uS01lc3NhZ2UobSk7XG5cdFx0XHR9XG5cdFx0XHRzd2l0Y2ggKG0uaWQpIHtcblx0XHRcdFx0Y2FzZSAnZXJyb3InOlxuXHRcdFx0XHRcdE9tVXRpbC5lcnJvcihtLm1lc3NhZ2UpO1xuXHRcdFx0XHRcdGJyZWFrO1xuXHRcdFx0XHRkZWZhdWx0OlxuXHRcdFx0XHRcdC8vbm8tb3Bcblx0XHRcdH1cblx0XHR9XG5cdH0gY2F0Y2ggKGVycikge1xuXHRcdE9tVXRpbC5lcnJvcihlcnIpO1xuXHR9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuXHRpbml0OiBfaW5pdFxuXHQsIG9wZW46IF9vcGVuXG5cdCwgY2xvc2U6IGZ1bmN0aW9uKCkge1xuXHRcdF9jbG9zZSgpO1xuXHRcdHZzICYmIHZzLm1vZGFsKCdoaWRlJyk7XG5cdH1cblx0LCBsb2FkOiBfbG9hZFxuXHQsIHNhdmU6IF9zYXZlXG5cdCwgY29uc3RyYWludHM6IF9jb25zdHJhaW50c1xufTtcbiIsIi8qIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wICovXG5jb25zdCBPbVV0aWwgPSByZXF1aXJlKCcuLi9tYWluL29tdXRpbHMnKTtcbmNvbnN0IFNldHRpbmdzID0gcmVxdWlyZSgnLi4vbWFpbi9zZXR0aW5ncycpO1xuXG5jb25zdCBVQVBhcnNlciA9IHJlcXVpcmUoJ3VhLXBhcnNlci1qcycpXG5cdCwgdWEgPSAodHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcgJiYgd2luZG93Lm5hdmlnYXRvcikgPyB3aW5kb3cubmF2aWdhdG9yLnVzZXJBZ2VudCA6ICcnXG5cdCwgcGFyc2VyID0gbmV3IFVBUGFyc2VyKHVhKVxuXHQsIGJyb3dzZXIgPSBwYXJzZXIuZ2V0QnJvd3NlcigpO1xuXG5jb25zdCBXQl9BUkVBX1NFTCA9ICcucm9vbS1ibG9jayAud2ItYmxvY2snO1xuY29uc3QgV0JBX1dCX1NFTCA9ICcucm9vbS1ibG9jayAud2ItYmxvY2sgLndiLXRhYi1jb250ZW50JztcbmNvbnN0IFZJRFdJTl9TRUwgPSAnLnZpZGVvLnVzZXItdmlkZW8nO1xuY29uc3QgVklEX1NFTCA9ICcudmlkZW8tY29udGFpbmVyW2lkIT11c2VyLXZpZGVvXSc7XG5jb25zdCBDQU1fQUNUSVZJVFkgPSAnVklERU8nO1xuY29uc3QgTUlDX0FDVElWSVRZID0gJ0FVRElPJztcbmNvbnN0IFNDUkVFTl9BQ1RJVklUWSA9ICdTQ1JFRU4nO1xuY29uc3QgUkVDX0FDVElWSVRZID0gJ1JFQ09SRCc7XG5cbmZ1bmN0aW9uIF9pc1NhZmFyaSgpIHtcblx0cmV0dXJuIGJyb3dzZXIubmFtZSA9PT0gJ1NhZmFyaSc7XG59XG5mdW5jdGlvbiBfaXNDaHJvbWUoKSB7XG5cdHJldHVybiBicm93c2VyLm5hbWUgPT09ICdDaHJvbWUnIHx8IGJyb3dzZXIubmFtZSA9PT0gJ0Nocm9taXVtJztcbn1cbmZ1bmN0aW9uIF9pc0VkZ2UoKSB7XG5cdHJldHVybiBicm93c2VyLm5hbWUgPT09ICdFZGdlJyAmJiBcIk1TR2VzdHVyZUV2ZW50XCIgaW4gd2luZG93O1xufVxuZnVuY3Rpb24gX2lzRWRnZUNocm9taXVtKCkge1xuXHRyZXR1cm4gYnJvd3Nlci5uYW1lID09PSAnRWRnZScgJiYgIShcIk1TR2VzdHVyZUV2ZW50XCIgaW4gd2luZG93KTtcbn1cblxuZnVuY3Rpb24gX2dldFZpZCh1aWQpIHtcblx0cmV0dXJuICd2aWRlbycgKyB1aWQ7XG59XG5mdW5jdGlvbiBfaXNTaGFyaW5nKHNkKSB7XG5cdHJldHVybiAhIXNkICYmICdTQ1JFRU4nID09PSBzZC50eXBlICYmIHNkLmFjdGl2aXRpZXMuaW5jbHVkZXMoU0NSRUVOX0FDVElWSVRZKTtcbn1cbmZ1bmN0aW9uIF9pc1JlY29yZGluZyhzZCkge1xuXHRyZXR1cm4gISFzZCAmJiAnU0NSRUVOJyA9PT0gc2QudHlwZSAmJiBzZC5hY3Rpdml0aWVzLmluY2x1ZGVzKFJFQ19BQ1RJVklUWSk7XG59XG5mdW5jdGlvbiBfaGFzQWN0aXZpdHkoc2QsIGFjdCkge1xuXHRyZXR1cm4gISFzZCAmJiBzZC5hY3Rpdml0aWVzLmluY2x1ZGVzKGFjdCk7XG59XG5mdW5jdGlvbiBfaGFzTWljKHNkKSB7XG5cdGlmICghc2QpIHtcblx0XHRyZXR1cm4gdHJ1ZTtcblx0fVxuXHRjb25zdCBlbmFibGVkID0gc2QubWljRW5hYmxlZCAhPT0gZmFsc2U7XG5cdHJldHVybiBzZC5hY3Rpdml0aWVzLmluY2x1ZGVzKE1JQ19BQ1RJVklUWSkgJiYgZW5hYmxlZDtcbn1cbmZ1bmN0aW9uIF9oYXNDYW0oc2QpIHtcblx0aWYgKCFzZCkge1xuXHRcdHJldHVybiB0cnVlO1xuXHR9XG5cdGNvbnN0IGVuYWJsZWQgPSBzZC5jYW1FbmFibGVkICE9PSBmYWxzZTtcblx0cmV0dXJuIHNkLmFjdGl2aXRpZXMuaW5jbHVkZXMoQ0FNX0FDVElWSVRZKSAmJiBlbmFibGVkO1xufVxuZnVuY3Rpb24gX2hhc1ZpZGVvKHNkKSB7XG5cdHJldHVybiBfaGFzQ2FtKHNkKSB8fCBfaXNTaGFyaW5nKHNkKSB8fCBfaXNSZWNvcmRpbmcoc2QpO1xufVxuZnVuY3Rpb24gX2dldFJlY3RzKHNlbCwgZXhjbCkge1xuXHRjb25zdCBsaXN0ID0gW10sIGVsZW1zID0gJChzZWwpO1xuXHRmb3IgKGxldCBpID0gMDsgaSA8IGVsZW1zLmxlbmd0aDsgKytpKSB7XG5cdFx0aWYgKGV4Y2wgIT09ICQoZWxlbXNbaV0pLmF0dHIoJ2FyaWEtZGVzY3JpYmVkYnknKSkge1xuXHRcdFx0bGlzdC5wdXNoKF9nZXRSZWN0KGVsZW1zW2ldKSk7XG5cdFx0fVxuXHR9XG5cdHJldHVybiBsaXN0O1xufVxuZnVuY3Rpb24gX2dldFJlY3QoZSkge1xuXHRjb25zdCB3aW4gPSAkKGUpLCB3aW5vZmYgPSB3aW4ub2Zmc2V0KCk7XG5cdHJldHVybiB7bGVmdDogd2lub2ZmLmxlZnRcblx0XHQsIHRvcDogd2lub2ZmLnRvcFxuXHRcdCwgcmlnaHQ6IHdpbm9mZi5sZWZ0ICsgd2luLndpZHRoKClcblx0XHQsIGJvdHRvbTogd2lub2ZmLnRvcCArIHdpbi5oZWlnaHQoKX07XG59XG5mdW5jdGlvbiBfY29udGFpbmVyKCkge1xuXHRjb25zdCBhID0gJChXQl9BUkVBX1NFTCk7XG5cdGNvbnN0IGMgPSBhLmZpbmQoJy53Yi1hcmVhIC50YWJzIC53Yi10YWItY29udGVudCcpO1xuXHRyZXR1cm4gYy5sZW5ndGggPiAwID8gJChXQkFfV0JfU0VMKSA6IGE7XG59XG5mdW5jdGlvbiBfX3Byb2Nlc3NUb3BUb0JvdHRvbShhcmVhLCByZWN0TmV3LCBsaXN0KSB7XG5cdGNvbnN0IG9mZnNldFggPSAyMFxuXHRcdCwgb2Zmc2V0WSA9IDEwO1xuXG5cdGxldCBtaW5ZID0gYXJlYS5ib3R0b20sIHBvc0ZvdW5kO1xuXHRkbyB7XG5cdFx0cG9zRm91bmQgPSB0cnVlO1xuXHRcdGZvciAobGV0IGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7ICsraSkge1xuXHRcdFx0Y29uc3QgcmVjdCA9IGxpc3RbaV07XG5cdFx0XHRtaW5ZID0gTWF0aC5taW4obWluWSwgcmVjdC5ib3R0b20pO1xuXG5cdFx0XHRpZiAocmVjdE5ldy5sZWZ0IDwgcmVjdC5yaWdodCAmJiByZWN0TmV3LnJpZ2h0ID4gcmVjdC5sZWZ0ICYmIHJlY3ROZXcudG9wIDwgcmVjdC5ib3R0b20gJiYgcmVjdE5ldy5ib3R0b20gPiByZWN0LnRvcCkge1xuXHRcdFx0XHRyZWN0TmV3LmxlZnQgPSByZWN0LnJpZ2h0ICsgb2Zmc2V0WDtcblx0XHRcdFx0cG9zRm91bmQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHRcdGlmIChyZWN0TmV3LnJpZ2h0ID49IGFyZWEucmlnaHQpIHtcblx0XHRcdFx0cmVjdE5ldy5sZWZ0ID0gYXJlYS5sZWZ0O1xuXHRcdFx0XHRyZWN0TmV3LnRvcCA9IE1hdGgubWF4KG1pblksIHJlY3ROZXcudG9wKSArIG9mZnNldFk7XG5cdFx0XHRcdHBvc0ZvdW5kID0gZmFsc2U7XG5cdFx0XHR9XG5cdFx0XHRpZiAocmVjdE5ldy5ib3R0b20gPj0gYXJlYS5ib3R0b20pIHtcblx0XHRcdFx0cmVjdE5ldy50b3AgPSBhcmVhLnRvcDtcblx0XHRcdFx0cG9zRm91bmQgPSB0cnVlO1xuXHRcdFx0XHRicmVhaztcblx0XHRcdH1cblx0XHR9XG5cdH0gd2hpbGUgKCFwb3NGb3VuZCk7XG5cdHJldHVybiB7bGVmdDogcmVjdE5ldy5sZWZ0LCB0b3A6IHJlY3ROZXcudG9wfTtcbn1cbmZ1bmN0aW9uIF9fcHJvY2Vzc0VxdWFsc0JvdHRvbVRvVG9wKGFyZWEsIHJlY3ROZXcsIGxpc3QpIHtcblx0Y29uc3Qgb2Zmc2V0WCA9IDIwXG5cdFx0LCBvZmZzZXRZID0gMTA7XG5cblx0cmVjdE5ldy5ib3R0b20gPSBhcmVhLmJvdHRvbTtcblx0bGV0IG1pblkgPSBhcmVhLmJvdHRvbSwgcG9zRm91bmQ7XG5cdGRvIHtcblx0XHRwb3NGb3VuZCA9IHRydWU7XG5cdFx0Zm9yIChsZXQgaSA9IDA7IGkgPCBsaXN0Lmxlbmd0aDsgKytpKSB7XG5cdFx0XHRjb25zdCByZWN0ID0gbGlzdFtpXTtcblx0XHRcdG1pblkgPSBNYXRoLm1pbihtaW5ZLCByZWN0LnRvcCk7XG5cblx0XHRcdGlmIChyZWN0TmV3LmxlZnQgPCByZWN0LnJpZ2h0ICYmIHJlY3ROZXcucmlnaHQgPiByZWN0LmxlZnQgJiYgcmVjdE5ldy50b3AgPCByZWN0LmJvdHRvbSAmJiByZWN0TmV3LmJvdHRvbSA+IHJlY3QudG9wKSB7XG5cdFx0XHRcdHJlY3ROZXcubGVmdCA9IHJlY3QucmlnaHQgKyBvZmZzZXRYO1xuXHRcdFx0XHRwb3NGb3VuZCA9IGZhbHNlO1xuXHRcdFx0fVxuXHRcdFx0aWYgKHJlY3ROZXcucmlnaHQgPj0gYXJlYS5yaWdodCkge1xuXHRcdFx0XHRyZWN0TmV3LmxlZnQgPSBhcmVhLmxlZnQ7XG5cdFx0XHRcdHJlY3ROZXcuYm90dG9tID0gTWF0aC5taW4obWluWSwgcmVjdE5ldy50b3ApIC0gb2Zmc2V0WTtcblx0XHRcdFx0cG9zRm91bmQgPSBmYWxzZTtcblx0XHRcdH1cblx0XHRcdGlmIChyZWN0TmV3LnRvcCA8PSBhcmVhLnRvcCkge1xuXHRcdFx0XHRyZWN0TmV3LnRvcCA9IGFyZWEudG9wO1xuXHRcdFx0XHRwb3NGb3VuZCA9IHRydWU7XG5cdFx0XHRcdGJyZWFrO1xuXHRcdFx0fVxuXHRcdH1cblx0fSB3aGlsZSAoIXBvc0ZvdW5kKTtcblx0cmV0dXJuIHtsZWZ0OiByZWN0TmV3LmxlZnQsIHRvcDogcmVjdE5ldy50b3B9O1xufVxuZnVuY3Rpb24gX2dldFBvcyhsaXN0LCB3LCBoLCBfcHJvY2Vzc29yKSB7XG5cdGlmIChSb29tLmdldE9wdGlvbnMoKS5pbnRlcnZpZXcpIHtcblx0XHRyZXR1cm4ge2xlZnQ6IDAsIHRvcDogMH07XG5cdH1cblx0Y29uc3Qgd2JhID0gX2NvbnRhaW5lcigpXG5cdFx0LCB3b2Zmc2V0ID0gd2JhLm9mZnNldCgpXG5cdFx0LCBhcmVhID0ge2xlZnQ6IHdvZmZzZXQubGVmdCwgdG9wOiB3b2Zmc2V0LnRvcCwgcmlnaHQ6IHdvZmZzZXQubGVmdCArIHdiYS53aWR0aCgpLCBib3R0b206IHdvZmZzZXQudG9wICsgd2JhLmhlaWdodCgpfVxuXHRcdCwgcmVjdE5ldyA9IHtcblx0XHRcdF9sZWZ0OiBhcmVhLmxlZnRcblx0XHRcdCwgX3RvcDogYXJlYS50b3Bcblx0XHRcdCwgX3JpZ2h0OiBhcmVhLmxlZnQgKyB3XG5cdFx0XHQsIF9ib3R0b206IGFyZWEudG9wICsgaFxuXHRcdFx0LCBnZXQgbGVmdCgpIHtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX2xlZnQ7XG5cdFx0XHR9XG5cdFx0XHQsIHNldCBsZWZ0KGwpIHtcblx0XHRcdFx0dGhpcy5fbGVmdCA9IGw7XG5cdFx0XHRcdHRoaXMuX3JpZ2h0ID0gbCArIHc7XG5cdFx0XHR9XG5cdFx0XHQsIGdldCByaWdodCgpIHtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX3JpZ2h0O1xuXHRcdFx0fVxuXHRcdFx0LCBnZXQgdG9wKCkge1xuXHRcdFx0XHRyZXR1cm4gdGhpcy5fdG9wO1xuXHRcdFx0fVxuXHRcdFx0LCBzZXQgdG9wKHQpIHtcblx0XHRcdFx0dGhpcy5fdG9wID0gdDtcblx0XHRcdFx0dGhpcy5fYm90dG9tID0gdCArIGg7XG5cdFx0XHR9XG5cdFx0XHQsIHNldCBib3R0b20oYikge1xuXHRcdFx0XHR0aGlzLl9ib3R0b20gPSBiO1xuXHRcdFx0XHR0aGlzLl90b3AgPSBiIC0gaDtcblx0XHRcdH1cblx0XHRcdCwgZ2V0IGJvdHRvbSgpIHtcblx0XHRcdFx0cmV0dXJuIHRoaXMuX2JvdHRvbTtcblx0XHRcdH1cblx0XHR9O1xuXHRjb25zdCBwcm9jZXNzb3IgPSBfcHJvY2Vzc29yIHx8IF9fcHJvY2Vzc1RvcFRvQm90dG9tO1xuXHRyZXR1cm4gcHJvY2Vzc29yKGFyZWEsIHJlY3ROZXcsIGxpc3QpO1xufVxuZnVuY3Rpb24gX2FycmFuZ2UoKSB7XG5cdGNvbnN0IGxpc3QgPSBbXTtcblx0JChWSURXSU5fU0VMKS5lYWNoKGZ1bmN0aW9uKCkge1xuXHRcdGNvbnN0IHYgPSAkKHRoaXMpO1xuXHRcdHYuY3NzKF9nZXRQb3MobGlzdCwgdi53aWR0aCgpLCB2LmhlaWdodCgpKSk7XG5cdFx0bGlzdC5wdXNoKF9nZXRSZWN0KHYpKTtcblx0fSk7XG59XG5mdW5jdGlvbiBfYXJyYW5nZVJlc2l6ZSh2U2V0dGluZ3MpIHtcblx0Y29uc3QgbGlzdCA9IFtdXG5cdFx0LCBzaXplID0ge3dpZHRoOiAxMjAsIGhlaWdodDogOTB9O1xuXHRpZiAodlNldHRpbmdzLmZpeGVkLmVuYWJsZWQpIHtcblx0XHRzaXplLndpZHRoID0gdlNldHRpbmdzLmZpeGVkLndpZHRoO1xuXHRcdHNpemUuaGVpZ2h0ID0gdlNldHRpbmdzLmZpeGVkLmhlaWdodDtcblx0fVxuXG5cdGZ1bmN0aW9uIF9fZ2V0RGlhbG9nKF92KSB7XG5cdFx0cmV0dXJuICQoX3YpLmZpbmQoJy52aWRlby1jb250YWluZXIudWktZGlhbG9nLWNvbnRlbnQnKTtcblx0fVxuXHQkKFZJRFdJTl9TRUwpLnRvQXJyYXkoKS5zb3J0KCh2MSwgdjIpID0+IHtcblx0XHRjb25zdCBjMSA9IF9fZ2V0RGlhbG9nKHYxKS5kYXRhKCkuc3RyZWFtKClcblx0XHRcdCwgYzIgPSBfX2dldERpYWxvZyh2MikuZGF0YSgpLnN0cmVhbSgpO1xuXHRcdHJldHVybiBjMi5sZXZlbCAtIGMxLmxldmVsIHx8IGMxLnVzZXIuZGlzcGxheU5hbWUubG9jYWxlQ29tcGFyZShjMi51c2VyLmRpc3BsYXlOYW1lKTtcblx0fSkuZm9yRWFjaChfdiA9PiB7XG5cdFx0Y29uc3QgdiA9ICQoX3YpO1xuXHRcdF9fZ2V0RGlhbG9nKHYpXG5cdFx0XHQuZGlhbG9nKCdvcHRpb24nLCAnd2lkdGgnLCBzaXplLndpZHRoKVxuXHRcdFx0LmRpYWxvZygnb3B0aW9uJywgJ2hlaWdodCcsIHNpemUuaGVpZ2h0KTtcblx0XHR2LmNzcyhfZ2V0UG9zKGxpc3QsIHYud2lkdGgoKSwgdi5oZWlnaHQoKSwgX19wcm9jZXNzRXF1YWxzQm90dG9tVG9Ub3ApKTtcblx0XHRsaXN0LnB1c2goX2dldFJlY3QodikpO1xuXHR9KTtcbn1cbmZ1bmN0aW9uIF9jbGVhblN0cmVhbShzdHJlYW0pIHtcblx0aWYgKCEhc3RyZWFtKSB7XG5cdFx0c3RyZWFtLmdldFRyYWNrcygpLmZvckVhY2godHJhY2sgPT4gdHJhY2suc3RvcCgpKTtcblx0fVxufVxuZnVuY3Rpb24gX2NsZWFuUGVlcihydGNQZWVyKSB7XG5cdGlmICghIXJ0Y1BlZXIpIHtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcGMgPSBydGNQZWVyLnBjO1xuXHRcdFx0aWYgKCEhcGMpIHtcblx0XHRcdFx0cGMuZ2V0U2VuZGVycygpLmZvckVhY2goc2VuZGVyID0+IHtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0aWYgKHNlbmRlci50cmFjaykge1xuXHRcdFx0XHRcdFx0XHRzZW5kZXIudHJhY2suc3RvcCgpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gY2F0Y2goZSkge1xuXHRcdFx0XHRcdFx0T21VdGlsLmxvZygnRmFpbGVkIHRvIGNsZWFuIHNlbmRlcicgKyBlKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pO1xuXHRcdFx0XHRwYy5nZXRSZWNlaXZlcnMoKS5mb3JFYWNoKHJlY2VpdmVyID0+IHtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0aWYgKHJlY2VpdmVyLnRyYWNrKSB7XG5cdFx0XHRcdFx0XHRcdHJlY2VpdmVyLnRyYWNrLnN0b3AoKTtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9IGNhdGNoKGUpIHtcblx0XHRcdFx0XHRcdE9tVXRpbC5sb2coJ0ZhaWxlZCB0byBjbGVhbiByZWNlaXZlcicgKyBlKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH0pO1xuXHRcdFx0fVxuXHRcdFx0cnRjUGVlci5kaXNwb3NlKCk7XG5cdFx0fSBjYXRjaChlKSB7XG5cdFx0XHQvL25vLW9wXG5cdFx0fVxuXHR9XG59XG5mdW5jdGlvbiBfc2V0UG9zKHYsIHBvcykge1xuXHRpZiAodi5kaWFsb2coJ2luc3RhbmNlJykpIHtcblx0XHR2LmRpYWxvZygnd2lkZ2V0JykuY3NzKHBvcyk7XG5cdH1cbn1cbmZ1bmN0aW9uIF9hc2tQZXJtaXNzaW9uKGNhbGxiYWNrKSB7XG5cdGNvbnN0IHBlcm0gPSAkKCcjYXNrLXBlcm1pc3Npb24nKTtcblx0JCgnLnNpZGViYXInKS5jb25maXJtYXRpb24oe1xuXHRcdHRpdGxlOiBwZXJtLmF0dHIoJ3RpdGxlJylcblx0XHQsIHBsYWNlbWVudDogU2V0dGluZ3MuaXNSdGwgPyAncmlnaHQnIDogJ2xlZnQnXG5cdFx0LCBzaW5nbGV0b246IHRydWVcblx0XHQsIHJvb3RTZWxlY3RvcjogJy5zaWRlYmFyJ1xuXHRcdCwgaHRtbDogdHJ1ZVxuXHRcdCwgY29udGVudDogcGVybS5odG1sKClcblx0XHQsIGJ1dHRvbnM6IFt7XG5cdFx0XHRjbGFzczogJ2J0biBidG4tc20gYnRuLXdhcm5pbmcnXG5cdFx0XHQsIGxhYmVsOiBwZXJtLmRhdGEoJ2J0bi1vaycpXG5cdFx0XHQsIHZhbHVlOiBwZXJtLmRhdGEoJ2J0bi1vaycpXG5cdFx0XHQsIG9uQ2xpY2s6IGZ1bmN0aW9uKCkge1xuXHRcdFx0XHRjYWxsYmFjaygpO1xuXHRcdFx0XHQkKCcuc2lkZWJhcicpLmNvbmZpcm1hdGlvbignZGlzcG9zZScpO1xuXHRcdFx0fVxuXHRcdH1dXG5cdH0pO1xuXHQkKCcuc2lkZWJhcicpLmNvbmZpcm1hdGlvbignc2hvdycpO1xufVxuZnVuY3Rpb24gX2Rpc2Nvbm5lY3Qobm9kZSkge1xuXHR0cnkge1xuXHRcdG5vZGUuZGlzY29ubmVjdCgpOyAvL3RoaXMgb25lIGNhbiB0aHJvd1xuXHR9IGNhdGNoIChlKSB7XG5cdFx0Ly9uby1vcFxuXHR9XG59XG5mdW5jdGlvbiBfc2hhcmluZ1N1cHBvcnRlZCgpIHtcblx0cmV0dXJuIChicm93c2VyLm5hbWUgPT09ICdFZGdlJyAmJiBicm93c2VyLm1ham9yID4gMTYpXG5cdFx0fHwgKHR5cGVvZihuYXZpZ2F0b3IubWVkaWFEZXZpY2VzLmdldERpc3BsYXlNZWRpYSkgPT09ICdmdW5jdGlvbidcblx0XHRcdCYmIChicm93c2VyLm5hbWUgPT09ICdGaXJlZm94J1xuXHRcdFx0XHR8fCBicm93c2VyLm5hbWUgPT09ICdPcGVyYSdcblx0XHRcdFx0fHwgYnJvd3Nlci5uYW1lID09PSAnWWFuZGV4J1xuXHRcdFx0XHR8fCBfaXNTYWZhcmkoKVxuXHRcdFx0XHR8fCBfaXNDaHJvbWUoKVxuXHRcdFx0XHR8fCBfaXNFZGdlQ2hyb21pdW0oKVxuXHRcdFx0XHR8fCAoYnJvd3Nlci5uYW1lID09PSAnTW96aWxsYScgJiYgYnJvd3Nlci5tYWpvciA+IDQpXG5cdFx0XHQpKTtcbn1cbmZ1bmN0aW9uIF9oaWdobGlnaHQoZWwsIGNsYXp6LCBjb3VudCkge1xuXHRpZiAoIWVsIHx8IGVsLmxlbmd0aCA8IDEgfHwgZWwuaGFzQ2xhc3MoJ2Rpc2FibGVkJykgfHwgY291bnQgPCAwKSB7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdGVsLmFkZENsYXNzKGNsYXp6KS5kZWxheSgyMDAwKS5xdWV1ZShmdW5jdGlvbihuZXh0KSB7XG5cdFx0ZWwucmVtb3ZlQ2xhc3MoY2xhenopLmRlbGF5KDIwMDApLnF1ZXVlKGZ1bmN0aW9uKG5leHQxKSB7XG5cdFx0XHRfaGlnaGxpZ2h0KGVsLCBjbGF6eiwgLS1jb3VudCk7XG5cdFx0XHRuZXh0MSgpO1xuXHRcdH0pO1xuXHRcdG5leHQoKTtcblx0fSk7XG59XG5mdW5jdGlvbiBfcGxheVNyYyhfdmlkZW8sIF9zdHJlYW0sIG11dGUpIHtcblx0aWYgKF9zdHJlYW0gJiYgX3ZpZGVvKSB7XG5cdFx0X3ZpZGVvLnNyY09iamVjdCA9IF9zdHJlYW07XG5cdFx0aWYgKF92aWRlby5wYXVzZWQpIHtcblx0XHRcdF92aWRlby5wbGF5KCkudGhlbigoKSA9PiBfdmlkZW8ubXV0ZWQgPSBtdXRlKS5jYXRjaChlcnIgPT4ge1xuXHRcdFx0XHRpZiAoJ05vdEFsbG93ZWRFcnJvcicgPT09IGVyci5uYW1lKSB7XG5cdFx0XHRcdFx0X2Fza1Blcm1pc3Npb24oKCkgPT4gX3ZpZGVvLnBsYXkoKS50aGVuKCgpID0+IF92aWRlby5tdXRlZCA9IG11dGUpKTtcblx0XHRcdFx0fVxuXHRcdFx0fSk7XG5cdFx0fVxuXHR9XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuXHRWSURXSU5fU0VMOiBWSURXSU5fU0VMXG5cdCwgVklEX1NFTDogVklEX1NFTFxuXHQsIENBTV9BQ1RJVklUWTogQ0FNX0FDVElWSVRZXG5cdCwgTUlDX0FDVElWSVRZOiBNSUNfQUNUSVZJVFlcblxuXHQsIGdldFZpZDogX2dldFZpZFxuXHQsIGlzU2hhcmluZzogX2lzU2hhcmluZ1xuXHQsIGlzUmVjb3JkaW5nOiBfaXNSZWNvcmRpbmdcblx0LCBoYXNNaWM6IF9oYXNNaWNcblx0LCBoYXNDYW06IF9oYXNDYW1cblx0LCBoYXNWaWRlbzogX2hhc1ZpZGVvXG5cdCwgaGFzQWN0aXZpdHk6IF9oYXNBY3Rpdml0eVxuXHQsIGdldFJlY3RzOiBfZ2V0UmVjdHNcblx0LCBnZXRQb3M6IF9nZXRQb3Ncblx0LCBjb250YWluZXI6IF9jb250YWluZXJcblx0LCBhcnJhbmdlOiBfYXJyYW5nZVxuXHQsIGFycmFuZ2VSZXNpemU6IF9hcnJhbmdlUmVzaXplXG5cdCwgY2xlYW5TdHJlYW06IF9jbGVhblN0cmVhbVxuXHQsIGNsZWFuUGVlcjogX2NsZWFuUGVlclxuXHQsIGFkZEljZVNlcnZlcnM6IGZ1bmN0aW9uKG9wdHMsIG0pIHtcblx0XHRpZiAobSAmJiBtLmljZVNlcnZlcnMgJiYgbS5pY2VTZXJ2ZXJzLmxlbmd0aCA+IDApIHtcblx0XHRcdG9wdHMuaWNlU2VydmVycyA9IG0uaWNlU2VydmVycztcblx0XHR9XG5cdFx0cmV0dXJuIG9wdHM7XG5cdH1cblx0LCBzZXRQb3M6IF9zZXRQb3Ncblx0LCBhc2tQZXJtaXNzaW9uOiBfYXNrUGVybWlzc2lvblxuXHQsIGRpc2Nvbm5lY3Q6IF9kaXNjb25uZWN0XG5cdCwgc2hhcmluZ1N1cHBvcnRlZDogX3NoYXJpbmdTdXBwb3J0ZWRcblx0LCBoaWdobGlnaHQ6IF9oaWdobGlnaHRcblx0LCBwbGF5U3JjOiBfcGxheVNyY1xuXG5cdCwgYnJvd3NlcjogYnJvd3NlclxuXHQsIGlzRWRnZTogX2lzRWRnZVxuXHQsIGlzRWRnZUNocm9taXVtOiBfaXNFZGdlQ2hyb21pdW1cblx0LCBpc0Nocm9tZTogX2lzQ2hyb21lXG5cdCwgaXNTYWZhcmk6IF9pc1NhZmFyaVxufTtcbiIsIm1vZHVsZS5leHBvcnRzID0gT21VdGlsOyIsIm1vZHVsZS5leHBvcnRzID0gU2V0dGluZ3M7IiwiLy8gVGhlIG1vZHVsZSBjYWNoZVxudmFyIF9fd2VicGFja19tb2R1bGVfY2FjaGVfXyA9IHt9O1xuXG4vLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcblx0dmFyIGNhY2hlZE1vZHVsZSA9IF9fd2VicGFja19tb2R1bGVfY2FjaGVfX1ttb2R1bGVJZF07XG5cdGlmIChjYWNoZWRNb2R1bGUgIT09IHVuZGVmaW5lZCkge1xuXHRcdHJldHVybiBjYWNoZWRNb2R1bGUuZXhwb3J0cztcblx0fVxuXHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuXHR2YXIgbW9kdWxlID0gX193ZWJwYWNrX21vZHVsZV9jYWNoZV9fW21vZHVsZUlkXSA9IHtcblx0XHQvLyBubyBtb2R1bGUuaWQgbmVlZGVkXG5cdFx0Ly8gbm8gbW9kdWxlLmxvYWRlZCBuZWVkZWRcblx0XHRleHBvcnRzOiB7fVxuXHR9O1xuXG5cdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuXHRfX3dlYnBhY2tfbW9kdWxlc19fW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuXHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuXHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG59XG5cbiIsIl9fd2VicGFja19yZXF1aXJlX18uYW1kTyA9IHt9OyIsIi8vIGdldERlZmF1bHRFeHBvcnQgZnVuY3Rpb24gZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBub24taGFybW9ueSBtb2R1bGVzXG5fX3dlYnBhY2tfcmVxdWlyZV9fLm4gPSAobW9kdWxlKSA9PiB7XG5cdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuXHRcdCgpID0+IChtb2R1bGVbJ2RlZmF1bHQnXSkgOlxuXHRcdCgpID0+IChtb2R1bGUpO1xuXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQoZ2V0dGVyLCB7IGE6IGdldHRlciB9KTtcblx0cmV0dXJuIGdldHRlcjtcbn07IiwiLy8gZGVmaW5lIGdldHRlciBmdW5jdGlvbnMgZm9yIGhhcm1vbnkgZXhwb3J0c1xuX193ZWJwYWNrX3JlcXVpcmVfXy5kID0gKGV4cG9ydHMsIGRlZmluaXRpb24pID0+IHtcblx0Zm9yKHZhciBrZXkgaW4gZGVmaW5pdGlvbikge1xuXHRcdGlmKF9fd2VicGFja19yZXF1aXJlX18ubyhkZWZpbml0aW9uLCBrZXkpICYmICFfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZXhwb3J0cywga2V5KSkge1xuXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIGtleSwgeyBlbnVtZXJhYmxlOiB0cnVlLCBnZXQ6IGRlZmluaXRpb25ba2V5XSB9KTtcblx0XHR9XG5cdH1cbn07IiwiX193ZWJwYWNrX3JlcXVpcmVfXy5vID0gKG9iaiwgcHJvcCkgPT4gKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChvYmosIHByb3ApKSIsIi8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uciA9IChleHBvcnRzKSA9PiB7XG5cdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuXHR9XG5cdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG59OyIsIi8qIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIikgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wICovXG5yZXF1aXJlKCd3ZWJydGMtYWRhcHRlcicpO1xuXG5pZiAod2luZG93Lmhhc093blByb3BlcnR5KCdpc1NlY3VyZUNvbnRleHQnKSA9PT0gZmFsc2UpIHtcblx0d2luZG93LmlzU2VjdXJlQ29udGV4dCA9IHdpbmRvdy5sb2NhdGlvbi5wcm90b2NvbCA9PSAnaHR0cHM6JyB8fCBbXCJsb2NhbGhvc3RcIiwgXCIxMjcuMC4wLjFcIl0uaW5kZXhPZih3aW5kb3cubG9jYXRpb24uaG9zdG5hbWUpICE9PSAtMTtcbn1cblxuT2JqZWN0LmFzc2lnbih3aW5kb3csIHtcblx0VmlkZW9VdGlsOiByZXF1aXJlKCcuL3ZpZGVvLXV0aWwnKVxuXHQsIE1pY0xldmVsOiByZXF1aXJlKCcuL21pYy1sZXZlbCcpXG5cdCwgV2ViUnRjUGVlcjogcmVxdWlyZSgnLi9XZWJSdGNQZWVyJylcblx0LCBWaWRlb1NldHRpbmdzOiByZXF1aXJlKCcuL3NldHRpbmdzJylcbn0pO1xuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9
|